From 7e28f9fed8a519d1c4e2950c7e6b6e97898d498a Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Wed, 13 Sep 2023 19:08:50 +0300 Subject: [PATCH 0001/1148] dsound: Get rid of DSOUND_capturers and related lock. Ever since dcaeb6b4fdf8 ("dsound: Allow multiple buffers to capture from the same device.") it's not used for anything. (cherry picked from commit 0b7b6d10506aa4c97f6e25ef7e3577cdd5221f3f) CW-Bug-Id: #22502 --- dlls/dsound/capture.c | 12 ------------ dlls/dsound/dsound_main.c | 11 ----------- dlls/dsound/dsound_private.h | 2 -- 3 files changed, 25 deletions(-) diff --git a/dlls/dsound/capture.c b/dlls/dsound/capture.c index 6ddae8286cd..a2b866f29c3 100644 --- a/dlls/dsound/capture.c +++ b/dlls/dsound/capture.c @@ -859,10 +859,6 @@ static ULONG DirectSoundCaptureDevice_Release( if (!ref) { TRACE("deleting object\n"); - EnterCriticalSection(&DSOUND_capturers_lock); - list_remove(&device->entry); - LeaveCriticalSection(&DSOUND_capturers_lock); - if (device->capture_buffer) IDirectSoundCaptureBufferImpl_Release(&device->capture_buffer->IDirectSoundCaptureBuffer8_iface); @@ -1038,12 +1034,9 @@ static HRESULT DirectSoundCaptureDevice_Initialize( if(FAILED(hr)) return hr; - EnterCriticalSection(&DSOUND_capturers_lock); - hr = DirectSoundCaptureDevice_Create(&device); if (hr != DS_OK) { WARN("DirectSoundCaptureDevice_Create failed\n"); - LeaveCriticalSection(&DSOUND_capturers_lock); return hr; } @@ -1061,7 +1054,6 @@ static HRESULT DirectSoundCaptureDevice_Initialize( device->lock.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&device->lock); HeapFree(GetProcessHeap(), 0, device); - LeaveCriticalSection(&DSOUND_capturers_lock); return DSERR_NODRIVER; } @@ -1074,12 +1066,8 @@ static HRESULT DirectSoundCaptureDevice_Initialize( } IAudioClient_Release(client); - list_add_tail(&DSOUND_capturers, &device->entry); - *ppDevice = device; - LeaveCriticalSection(&DSOUND_capturers_lock); - return S_OK; } diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c index 69cbec72ea3..3347f0243ab 100644 --- a/dlls/dsound/dsound_main.c +++ b/dlls/dsound/dsound_main.c @@ -76,16 +76,6 @@ static CRITICAL_SECTION_DEBUG DSOUND_renderers_lock_debug = }; CRITICAL_SECTION DSOUND_renderers_lock = { &DSOUND_renderers_lock_debug, -1, 0, 0, 0, 0 }; -struct list DSOUND_capturers = LIST_INIT(DSOUND_capturers); -CRITICAL_SECTION DSOUND_capturers_lock; -static CRITICAL_SECTION_DEBUG DSOUND_capturers_lock_debug = -{ - 0, 0, &DSOUND_capturers_lock, - { &DSOUND_capturers_lock_debug.ProcessLocksList, &DSOUND_capturers_lock_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": DSOUND_capturers_lock") } -}; -CRITICAL_SECTION DSOUND_capturers_lock = { &DSOUND_capturers_lock_debug, -1, 0, 0, 0, 0 }; - GUID DSOUND_renderer_guids[MAXWAVEDRIVERS]; GUID DSOUND_capture_guids[MAXWAVEDRIVERS]; @@ -782,7 +772,6 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved) case DLL_PROCESS_DETACH: if (lpvReserved) break; DeleteCriticalSection(&DSOUND_renderers_lock); - DeleteCriticalSection(&DSOUND_capturers_lock); break; } return TRUE; diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h index c39d9ec40ea..09fa219ecca 100644 --- a/dlls/dsound/dsound_private.h +++ b/dlls/dsound/dsound_private.h @@ -252,8 +252,6 @@ HRESULT IDirectSoundCaptureImpl_Create(IUnknown *outer_unk, REFIID riid, void ** #define STATE_STOPPING 3 extern CRITICAL_SECTION DSOUND_renderers_lock DECLSPEC_HIDDEN; -extern CRITICAL_SECTION DSOUND_capturers_lock DECLSPEC_HIDDEN; -extern struct list DSOUND_capturers DECLSPEC_HIDDEN; extern struct list DSOUND_renderers DECLSPEC_HIDDEN; extern GUID DSOUND_renderer_guids[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; From 39b8fe1a353b715c1d580fe0334257846a744005 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Fri, 15 Sep 2023 00:16:47 +0300 Subject: [PATCH 0002/1148] dsound: Get rid of the global device GUID arrays. They are not used for anything anymore and just impose limit of 10 devices of a given type (capturer / renderer) without doing bound checking. (cherry picked from commit e1f0318ec4fe83fdc86407303d55f4ee5ba3d2e7) CW-Bug-Id: #22502 --- dlls/dsound/dsound_main.c | 31 +++++++++++-------------------- dlls/dsound/dsound_private.h | 5 +---- dlls/dsound/propset.c | 12 ++++-------- 3 files changed, 16 insertions(+), 32 deletions(-) diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c index 3347f0243ab..d18733294d0 100644 --- a/dlls/dsound/dsound_main.c +++ b/dlls/dsound/dsound_main.c @@ -76,9 +76,6 @@ static CRITICAL_SECTION_DEBUG DSOUND_renderers_lock_debug = }; CRITICAL_SECTION DSOUND_renderers_lock = { &DSOUND_renderers_lock_debug, -1, 0, 0, 0, 0 }; -GUID DSOUND_renderer_guids[MAXWAVEDRIVERS]; -GUID DSOUND_capture_guids[MAXWAVEDRIVERS]; - const WCHAR wine_vxd_drv[] = L"winemm.vxd"; /* All default settings, you most likely don't want to touch these, see wiki on UsefulRegistryKeys */ @@ -391,13 +388,13 @@ HRESULT get_mmdevice(EDataFlow flow, const GUID *tgt, IMMDevice **device) return DSERR_INVALIDPARAM; } -static BOOL send_device(IMMDevice *device, GUID *guid, - LPDSENUMCALLBACKW cb, void *user) +static BOOL send_device(IMMDevice *device, LPDSENUMCALLBACKW cb, void *user) { IPropertyStore *ps; PROPVARIANT pv; BOOL keep_going; HRESULT hr; + GUID guid; PropVariantInit(&pv); @@ -407,7 +404,7 @@ static BOOL send_device(IMMDevice *device, GUID *guid, return TRUE; } - hr = get_mmdevice_guid(device, ps, guid); + hr = get_mmdevice_guid(device, ps, &guid); if(FAILED(hr)){ IPropertyStore_Release(ps); return TRUE; @@ -421,10 +418,10 @@ static BOOL send_device(IMMDevice *device, GUID *guid, return TRUE; } - TRACE("Calling back with %s (%s)\n", wine_dbgstr_guid(guid), + TRACE("Calling back with %s (%s)\n", wine_dbgstr_guid(&guid), wine_dbgstr_w(pv.pwszVal)); - keep_going = cb(guid, pv.pwszVal, wine_vxd_drv, user); + keep_going = cb(&guid, pv.pwszVal, wine_vxd_drv, user); PropVariantClear(&pv); IPropertyStore_Release(ps); @@ -434,13 +431,12 @@ static BOOL send_device(IMMDevice *device, GUID *guid, /* S_FALSE means the callback returned FALSE at some point * S_OK means the callback always returned TRUE */ -HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids, - LPDSENUMCALLBACKW cb, void *user) +HRESULT enumerate_mmdevices(EDataFlow flow, LPDSENUMCALLBACKW cb, void *user) { IMMDeviceEnumerator *devenum; IMMDeviceCollection *coll; IMMDevice *defdev = NULL; - UINT count, i, n; + UINT count, i; BOOL keep_going; HRESULT hr, init_hr; @@ -479,10 +475,8 @@ HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids, eMultimedia, &defdev); if(FAILED(hr)){ defdev = NULL; - n = 0; }else{ - keep_going = send_device(defdev, &guids[0], cb, user); - n = 1; + keep_going = send_device(defdev, cb, user); } } @@ -496,8 +490,7 @@ HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids, } if(device != defdev){ - keep_going = send_device(device, &guids[n], cb, user); - ++n; + keep_going = send_device(device, cb, user); } IMMDevice_Release(device); @@ -540,8 +533,7 @@ HRESULT WINAPI DirectSoundEnumerateW( setup_dsound_options(); - hr = enumerate_mmdevices(eRender, DSOUND_renderer_guids, - lpDSEnumCallback, lpContext); + hr = enumerate_mmdevices(eRender, lpDSEnumCallback, lpContext); return SUCCEEDED(hr) ? DS_OK : hr; } @@ -604,8 +596,7 @@ DirectSoundCaptureEnumerateW( setup_dsound_options(); - hr = enumerate_mmdevices(eCapture, DSOUND_capture_guids, - lpDSEnumCallback, lpContext); + hr = enumerate_mmdevices(eCapture, lpDSEnumCallback, lpContext); return SUCCEEDED(hr) ? DS_OK : hr; } diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h index 09fa219ecca..081a56b2ee7 100644 --- a/dlls/dsound/dsound_private.h +++ b/dlls/dsound/dsound_private.h @@ -254,8 +254,6 @@ HRESULT IDirectSoundCaptureImpl_Create(IUnknown *outer_unk, REFIID riid, void ** extern CRITICAL_SECTION DSOUND_renderers_lock DECLSPEC_HIDDEN; extern struct list DSOUND_renderers DECLSPEC_HIDDEN; -extern GUID DSOUND_renderer_guids[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; -extern GUID DSOUND_capture_guids[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; extern const WCHAR wine_vxd_drv[] DECLSPEC_HIDDEN; @@ -265,8 +263,7 @@ HRESULT get_mmdevice(EDataFlow flow, const GUID *tgt, IMMDevice **device) DECLSP BOOL DSOUND_check_supported(IAudioClient *client, DWORD rate, DWORD depth, WORD channels) DECLSPEC_HIDDEN; -HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids, - LPDSENUMCALLBACKW cb, void *user) DECLSPEC_HIDDEN; +HRESULT enumerate_mmdevices(EDataFlow flow, LPDSENUMCALLBACKW cb, void *user) DECLSPEC_HIDDEN; static inline WCHAR *strdupW( const WCHAR *str ) { diff --git a/dlls/dsound/propset.c b/dlls/dsound/propset.c index f42cfbf4163..e72757bdb4b 100644 --- a/dlls/dsound/propset.c +++ b/dlls/dsound/propset.c @@ -136,11 +136,9 @@ static HRESULT DSPROPERTY_WaveDeviceMappingW( search.found_guid = &ppd->DeviceId; if (ppd->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_RENDER) - hr = enumerate_mmdevices(eRender, DSOUND_renderer_guids, - search_callback, &search); + hr = enumerate_mmdevices(eRender, search_callback, &search); else if (ppd->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE) - hr = enumerate_mmdevices(eCapture, DSOUND_capture_guids, - search_callback, &search); + hr = enumerate_mmdevices(eCapture, search_callback, &search); else return DSERR_INVALIDPARAM; @@ -318,12 +316,10 @@ static HRESULT DSPROPERTY_EnumerateW( return E_PROP_ID_UNSUPPORTED; } - hr = enumerate_mmdevices(eRender, DSOUND_renderer_guids, - enum_callback, ppd); + hr = enumerate_mmdevices(eRender, enum_callback, ppd); if(hr == S_OK) - hr = enumerate_mmdevices(eCapture, DSOUND_capture_guids, - enum_callback, ppd); + hr = enumerate_mmdevices(eCapture, enum_callback, ppd); return SUCCEEDED(hr) ? DS_OK : hr; } From 0392d2d99d4842519a6790dac0c00cc62bbc551a Mon Sep 17 00:00:00 2001 From: Mohamad Al-Jaf Date: Thu, 2 Feb 2023 22:06:57 -0500 Subject: [PATCH 0003/1148] cfgmgr32: Implement CM_MapCrToWin32Err. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53781 (cherry picked from commit 4fab9ff230f497499da223ade9bbe3633a657f05) Link: https://github.com/ValveSoftware/Proton/issues/6510#issuecomment-1546121761 Helps with Hogwarts Legacy and Unreal Engine 5.1 on ANV. --- dlls/cfgmgr32/Makefile.in | 3 ++ dlls/cfgmgr32/cfgmgr32.spec | 1 + dlls/cfgmgr32/main.c | 58 +++++++++++++++++++++++++++++++++++++ include/cfgmgr32.h | 1 + 4 files changed, 63 insertions(+) create mode 100644 dlls/cfgmgr32/main.c diff --git a/dlls/cfgmgr32/Makefile.in b/dlls/cfgmgr32/Makefile.in index 10621fa5dc7..f05b3176aa3 100644 --- a/dlls/cfgmgr32/Makefile.in +++ b/dlls/cfgmgr32/Makefile.in @@ -1,3 +1,6 @@ MODULE = cfgmgr32.dll IMPORTLIB = cfgmgr32 IMPORTS = setupapi + +C_SRCS = \ + main.c diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index 69ec784de68..3b4f6106618 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -126,6 +126,7 @@ @ stdcall CM_Locate_DevNodeW(ptr wstr long) setupapi.CM_Locate_DevNodeW @ stdcall CM_Locate_DevNode_ExA(ptr str long long) setupapi.CM_Locate_DevNode_ExA @ stdcall CM_Locate_DevNode_ExW(ptr wstr long long) setupapi.CM_Locate_DevNode_ExW +@ stdcall CM_MapCrToWin32Err(long long) @ stub CM_Merge_Range_List @ stub CM_Modify_Res_Des @ stub CM_Modify_Res_Des_Ex diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c new file mode 100644 index 00000000000..fee3c42a5c4 --- /dev/null +++ b/dlls/cfgmgr32/main.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 Mohamad Al-Jaf + * + * 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 "wine/debug.h" +#include "cfgmgr32.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/*********************************************************************** + * CM_MapCrToWin32Err (cfgmgr32.@) + */ +DWORD WINAPI CM_MapCrToWin32Err( CONFIGRET code, DWORD default_error ) +{ + TRACE( "code: %#lx, default_error: %ld\n", code, default_error ); + + switch (code) + { + case CR_SUCCESS: return ERROR_SUCCESS; + case CR_OUT_OF_MEMORY: return ERROR_NOT_ENOUGH_MEMORY; + case CR_INVALID_POINTER: return ERROR_INVALID_USER_BUFFER; + case CR_INVALID_FLAG: return ERROR_INVALID_FLAGS; + case CR_INVALID_DEVNODE: + case CR_INVALID_DEVICE_ID: + case CR_INVALID_MACHINENAME: + case CR_INVALID_PROPERTY: + case CR_INVALID_REFERENCE_STRING: return ERROR_INVALID_DATA; + case CR_NO_SUCH_DEVNODE: + case CR_NO_SUCH_VALUE: + case CR_NO_SUCH_DEVICE_INTERFACE: return ERROR_NOT_FOUND; + case CR_ALREADY_SUCH_DEVNODE: return ERROR_ALREADY_EXISTS; + case CR_BUFFER_SMALL: return ERROR_INSUFFICIENT_BUFFER; + case CR_NO_REGISTRY_HANDLE: return ERROR_INVALID_HANDLE; + case CR_REGISTRY_ERROR: return ERROR_REGISTRY_CORRUPT; + case CR_NO_SUCH_REGISTRY_KEY: return ERROR_FILE_NOT_FOUND; + case CR_REMOTE_COMM_FAILURE: + case CR_MACHINE_UNAVAILABLE: + case CR_NO_CM_SERVICES: return ERROR_SERVICE_NOT_ACTIVE; + case CR_ACCESS_DENIED: return ERROR_ACCESS_DENIED; + case CR_CALL_NOT_IMPLEMENTED: return ERROR_CALL_NOT_IMPLEMENTED; + } + + return default_error; +} diff --git a/include/cfgmgr32.h b/include/cfgmgr32.h index bff32fe1c08..04f1f80b174 100644 --- a/include/cfgmgr32.h +++ b/include/cfgmgr32.h @@ -246,6 +246,7 @@ CMAPI WORD WINAPI CM_Get_Version(void); CMAPI CONFIGRET WINAPI CM_Locate_DevNodeA(PDEVINST,DEVINSTID_A,ULONG); CMAPI CONFIGRET WINAPI CM_Locate_DevNodeW(PDEVINST,DEVINSTID_W,ULONG); #define CM_Locate_DevNode WINELIB_NAME_AW(CM_Locate_DevNode) +CMAPI DWORD WINAPI CM_MapCrToWin32Err(CONFIGRET,DWORD); CMAPI CONFIGRET WINAPI CM_Open_DevNode_Key(DEVINST dnDevInst, REGSAM access, ULONG ulHardwareProfile, REGDISPOSITION disposition, PHKEY phkDevice, ULONG ulFlags); CMAPI CONFIGRET WINAPI CM_Request_Device_EjectA(DEVINST dev, PPNP_VETO_TYPE type, LPSTR name, ULONG length, ULONG flags); From caf2f5368d294707d912339b9466e20c0ec1972e Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Thu, 21 Sep 2023 23:34:50 +0300 Subject: [PATCH 0004/1148] fixup! winegstreamer: Set n-threads for dav1d to 1. --- dlls/winegstreamer/wg_parser.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index d12c4c2dad1..681ef4abfa7 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -588,10 +588,16 @@ static void no_more_pads_cb(GstElement *element, gpointer user) static void deep_element_added_cb(GstBin *self, GstBin *sub_bin, GstElement *element, gpointer user) { - GstElementFactory *factory = gst_element_get_factory(element); - const char *name = gst_element_factory_get_longname(factory); + GstElementFactory *factory = NULL; + const char *name = NULL; - if (strstr(name, "Dav1d")) + if (element) + factory = gst_element_get_factory(element); + + if (factory) + name = gst_element_factory_get_longname(factory); + + if (name && strstr(name, "Dav1d")) { #if defined(__x86_64__) GST_DEBUG("%s found, setting n-threads to 4.", name); From 838a3bb7e45e43495eae1af707c228061ea88632 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 0005/1148] 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 d5f2dce70ec961066467e93e25adf0eef978dc56 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 0006/1148] 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 2905cce13c034b8a8672d874a9e80a09bf110eca 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 0007/1148] 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 f6b13077ad5a9c0a4696e159f64aa40d04fb1d9b 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 0008/1148] 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 984963b127f42cb02763c82987a9b66ec298c4cc 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 0009/1148] 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 e0aca0f9120517cc8f37d612fa1a6866245130f5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 4 May 2023 13:33:48 -0600 Subject: [PATCH 0010/1148] 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 9dc11f74fb2..3e4790249f8 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 175aad72a09b62c4fa9720cf6c21faf6bcec0a3a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 10 May 2023 13:17:26 -0600 Subject: [PATCH 0011/1148] 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 5e52b3165542d3244c693c408a631db2efafebf3 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 0012/1148] 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 46d07b8e449a7fc582903e4ed5fa733d55eb7c16 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 0013/1148] 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 c2e86d27f0732bfe9ca1b6dbcf8e87733586dba3 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 0014/1148] 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 0015/1148] 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 8a21a4227ab188804e7c9304378984c59ef2e86b Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 12 Dec 2022 18:45:17 +0100 Subject: [PATCH 0016/1148] 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 30fa9bd18ed277224701a379a82013dad4733624 Mon Sep 17 00:00:00 2001 From: Akihiro Sagawa Date: Fri, 20 Jan 2023 23:51:36 +0900 Subject: [PATCH 0017/1148] 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 88a35e4f9185349ced5aa0e28c4153b7143277d2 Mon Sep 17 00:00:00 2001 From: Akihiro Sagawa Date: Fri, 20 Jan 2023 23:55:59 +0900 Subject: [PATCH 0018/1148] 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 d6dcaf3bf9825e52d2571108d7053e0059764718 Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Tue, 20 Dec 2022 15:33:56 +0100 Subject: [PATCH 0019/1148] 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 c226aef6d38e30d8c8c67642e78d1aee3dfab8cf Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Wed, 21 Dec 2022 16:11:31 +0100 Subject: [PATCH 0020/1148] 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 2fc7f518465c8cb4a13ee81b18e3c2e793bdc1e5 Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Wed, 21 Dec 2022 18:31:41 +0100 Subject: [PATCH 0021/1148] 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 cc96dcdce98546098735b47bb1d63654345d6d23 Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Wed, 21 Dec 2022 18:48:29 +0100 Subject: [PATCH 0022/1148] 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 892398a20bfb2dfaef79a174172a9b1ab6701102 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:43:08 -0600 Subject: [PATCH 0023/1148] 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 72c715ce905839b3f2e1ebbbb7263eeac79f426f Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:43:45 -0600 Subject: [PATCH 0024/1148] 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 6d5ca7209f6e028fe74c2955cc447cfcbb861b1b Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:44:36 -0600 Subject: [PATCH 0025/1148] 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 c6791a57116402a4d27152dc154802236423fa34 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:45:08 -0600 Subject: [PATCH 0026/1148] 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 9b20c74b4c3ece840712d381bb7b9083cfcd8357 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:46:06 -0600 Subject: [PATCH 0027/1148] 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 7a96a27c32e1a296424036be574ddeb20dbdb664 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:47:17 -0600 Subject: [PATCH 0028/1148] 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 d99e3c737c94e2c50097f93ba44eaa4fa4e71bb4 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:47:56 -0600 Subject: [PATCH 0029/1148] 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 0d55150c94c7baedf7bdbf9bfea55e7b6a2118ec Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:48:36 -0600 Subject: [PATCH 0030/1148] 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 297d2835541cdca9b731e8362314dc2ebcab72ea Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:48:46 -0600 Subject: [PATCH 0031/1148] 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 4be192a6850f4473f87c6ff97a380d3b04a30e0a Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:49:07 -0600 Subject: [PATCH 0032/1148] 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 3dae9dd2f3c262cabfe0630044e3a5630e63f067 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 27 Jan 2023 10:23:56 +0100 Subject: [PATCH 0033/1148] 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 18941b1ec32a6f5b85d39c75c719b491b5eb549b Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:50:38 -0600 Subject: [PATCH 0034/1148] 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 88338caa1a5d8edaf96b434f5c7bd15f87f829b1 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 18 Jan 2023 19:53:09 -0600 Subject: [PATCH 0035/1148] 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 799c0ef37f292ee847870c4ceccbb9a4937a8343 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 0036/1148] 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 84f7bba87242c60e10ecac3a004b0ae791f3f8cb 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 0037/1148] 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 ab2e7396eb09201feb53290588d0555ff7a88e01 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Sat, 11 Feb 2023 12:52:28 -0700 Subject: [PATCH 0038/1148] 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 a64d7750615dcdd5d1e837225e18b560eb54d2bd Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Mon, 13 Feb 2023 18:34:44 +0100 Subject: [PATCH 0039/1148] 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 2521625180b..300a4c983e8 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1189,7 +1189,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 ffb23f7d7511fe1090b59df323c0759edb134c53 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Tue, 28 Feb 2023 09:49:36 -0700 Subject: [PATCH 0040/1148] 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 300a4c983e8..ea2faa95828 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3349,8 +3349,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 4a7f30553dc1d3dd4d337f2bf014c0c76a13f840 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 0041/1148] 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 100df2f395a..92dc784035d 100644 --- a/configure.ac +++ b/configure.ac @@ -1196,13 +1196,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 d67b56bd54345e8e6eda56337a02207db4b09b72 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 0042/1148] 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 2ac7de07ffb1165abdd7269d31d807eb8b2109f6 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 0043/1148] 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 b87d6151a2f..8ebe10b5a0a 100644 --- a/po/ar.po +++ b/po/ar.po @@ -4117,11 +4117,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 91d7d256182..9bb71695e12 100644 --- a/po/ast.po +++ b/po/ast.po @@ -3988,11 +3988,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 47e12a893e8..70219b75174 100644 --- a/po/bg.po +++ b/po/bg.po @@ -4115,11 +4115,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 90ee5673f7a..1b66417b24d 100644 --- a/po/ca.po +++ b/po/ca.po @@ -4089,11 +4089,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 25a4f1b75e3..704550231b3 100644 --- a/po/cs.po +++ b/po/cs.po @@ -4058,11 +4058,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 c7ee34b8ff9..3fc57acec52 100644 --- a/po/da.po +++ b/po/da.po @@ -4154,11 +4154,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 1ed7acd9d05..7a14c2e5a59 100644 --- a/po/de.po +++ b/po/de.po @@ -4077,11 +4077,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 c5c8307e2cb..11180a1b639 100644 --- a/po/el.po +++ b/po/el.po @@ -4017,11 +4017,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 a9ad0a9cb0a..246a8bb86f2 100644 --- a/po/en.po +++ b/po/en.po @@ -4068,11 +4068,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 ab15878a97b..ad7b5b9773c 100644 --- a/po/en_US.po +++ b/po/en_US.po @@ -4068,11 +4068,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 050cd952e48..ad39dac7839 100644 --- a/po/eo.po +++ b/po/eo.po @@ -4024,11 +4024,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 c19a7daabc1..5bc81f068b8 100644 --- a/po/es.po +++ b/po/es.po @@ -4094,11 +4094,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 40491717d00..2eafe19dd2e 100644 --- a/po/fa.po +++ b/po/fa.po @@ -4044,11 +4044,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 51f6dc4736e..905eb06a8cc 100644 --- a/po/fi.po +++ b/po/fi.po @@ -4061,11 +4061,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 b3d2ea080c0..1c48ddc57a6 100644 --- a/po/fr.po +++ b/po/fr.po @@ -4086,11 +4086,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 2e716c65960..82df9538b43 100644 --- a/po/he.po +++ b/po/he.po @@ -4108,11 +4108,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 08201ec785b..8ba49545d06 100644 --- a/po/hi.po +++ b/po/hi.po @@ -3973,11 +3973,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 cab03b4c47b..c9f87d7dc66 100644 --- a/po/hr.po +++ b/po/hr.po @@ -4122,11 +4122,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 6b70faab001..05920d1c8e6 100644 --- a/po/hu.po +++ b/po/hu.po @@ -4172,11 +4172,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 d2bf050bd81..d0663b4d1d7 100644 --- a/po/it.po +++ b/po/it.po @@ -4180,11 +4180,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 ee177c0aaa8..318f1a9029d 100644 --- a/po/ja.po +++ b/po/ja.po @@ -4060,11 +4060,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 fde0ddc1d3b..a86e129750c 100644 --- a/po/ko.po +++ b/po/ko.po @@ -4049,11 +4049,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 2fe2909a0d6..8ede4161894 100644 --- a/po/lt.po +++ b/po/lt.po @@ -4071,11 +4071,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 29b316c025c..8df38715288 100644 --- a/po/ml.po +++ b/po/ml.po @@ -3975,11 +3975,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 ec59d534991..f7ddeff7cae 100644 --- a/po/nb_NO.po +++ b/po/nb_NO.po @@ -4092,11 +4092,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 b571b48a26b..92d601aaf92 100644 --- a/po/nl.po +++ b/po/nl.po @@ -4081,11 +4081,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 4efb1620ccb..3d8ffa37e1d 100644 --- a/po/or.po +++ b/po/or.po @@ -3973,11 +3973,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 fb970b7c963..8177d578666 100644 --- a/po/pa.po +++ b/po/pa.po @@ -3973,11 +3973,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 723493afaa2..dcb4daaa0bd 100644 --- a/po/pl.po +++ b/po/pl.po @@ -4093,11 +4093,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 d6821db3689..7593a85f8f3 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -4089,11 +4089,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 833b8b23ade..be1ddf75488 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -4140,11 +4140,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 412e3dc6c5e..ffd9d51fc0e 100644 --- a/po/rm.po +++ b/po/rm.po @@ -4002,11 +4002,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 6c82bb5c603..3947ea81074 100644 --- a/po/ro.po +++ b/po/ro.po @@ -4095,11 +4095,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 cb918603b72..b7649c75b93 100644 --- a/po/ru.po +++ b/po/ru.po @@ -4096,11 +4096,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 bb39137afd5..2dea09241dc 100644 --- a/po/si.po +++ b/po/si.po @@ -4025,11 +4025,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 c018db3e215..9caab53bd61 100644 --- a/po/sk.po +++ b/po/sk.po @@ -4062,11 +4062,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 02638b23811..49fceead951 100644 --- a/po/sl.po +++ b/po/sl.po @@ -4174,11 +4174,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 63e76fa78f5..344a6396134 100644 --- a/po/sr_RS@cyrillic.po +++ b/po/sr_RS@cyrillic.po @@ -4150,11 +4150,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 e0977ce9ac3..4d5e65cb529 100644 --- a/po/sr_RS@latin.po +++ b/po/sr_RS@latin.po @@ -4235,11 +4235,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 5f4397962bf..f02e762af01 100644 --- a/po/sv.po +++ b/po/sv.po @@ -4119,11 +4119,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 2d1c803e1ad..02563f2b63d 100644 --- a/po/ta.po +++ b/po/ta.po @@ -3938,11 +3938,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 c0c7e8116a0..6128822c44c 100644 --- a/po/te.po +++ b/po/te.po @@ -3973,11 +3973,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 8f563d508b7..9d99df54f41 100644 --- a/po/th.po +++ b/po/th.po @@ -4039,11 +4039,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 28cee4524cd..73ee1e5eb83 100644 --- a/po/tr.po +++ b/po/tr.po @@ -4087,11 +4087,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 2d941b5248f..de0466f9971 100644 --- a/po/uk.po +++ b/po/uk.po @@ -4089,11 +4089,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 13d79d4eeca..dab8e39300b 100644 --- a/po/wa.po +++ b/po/wa.po @@ -4039,11 +4039,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 881b048e33b..dd9701759d8 100644 --- a/po/wine.pot +++ b/po/wine.pot @@ -3927,11 +3927,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 f226c3b1ced..bad68026ffa 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -4014,11 +4014,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 2a93c59445a..a54460961b3 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -4022,11 +4022,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 d5ad8fac2724a8750d87807d0dfda88e40ddfb82 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 0044/1148] 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 ad5f3d0127543ede21bcad46cab99ea8f740a134 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 0045/1148] 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 315eff7fb74574d272fa06b6b5a85f52d84b3c3f 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 0046/1148] 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 2ecc317b509..a6c9fcd76cc 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2850,6 +2850,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()) @@ -2866,8 +2867,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 6cfcc0b6aef98f3505b52cf9a76192ef552ad774 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 0047/1148] 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 a6b7348dcee057a12b32a4f1a81f1f8fed5b75d3 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 0048/1148] 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 9790042267818bc9758864b81daf89b76d56c194 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 0049/1148] 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 7f48fda8bcdb7bcd2bfa945c739147ed9a991756 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 0050/1148] 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 a80687ecefe6c9d0af4bcf5c22e1384d63a48b51 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 0051/1148] 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 89edbf2a222beb95734b50733fd6464636d84472 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 0052/1148] 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 694841c908a9c4cfab9b007e7addf725d74453ce 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 0053/1148] 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 1c148d1b2b09f515aa0efeff208e6cdc8a55abca 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 0054/1148] 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 7cd3a386dca8065898eda345eeea66ad722ed7fc 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 0055/1148] 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 75de2ebb2964b52f527bc16a4fe95a13e496418d 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 0056/1148] 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 6c44aa8515e6351f4b3a9318bfc81fa19ea5fbb5 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 0057/1148] 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 46fd9c285d989c577b8fa7c719d0f8c66609db00 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 0058/1148] 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 a5e9cc80753737bc651436bf46e65de530bdf476 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 0059/1148] 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 216b37c9742ca3319c83421b264836081e87e642 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 0060/1148] 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 237b3dd34e7457167f03a157c13b52202d7ed589 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 0061/1148] 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 5d5b9d3c02edcb5d502f4bdf5ad34521f1a91784 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 0062/1148] 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 21410f6babc8a93338766c75ef5d21f8cea3152c 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 0063/1148] 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 b6bff02fc0d57532788d427c62d8dfffa15a919b 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 0064/1148] 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 7a7c29b7fea9a5ee60f6f5bf38ff3c0aea0d883a 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 0065/1148] 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 ae9dfddf8edac7a63467196c7113dc161b6556f0 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 0066/1148] 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 dee6237d591696e8d02c4040d4591a2e9648f475 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 0067/1148] 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 a28883361f4a8a87e5f1aa06f88aec2c2a230261 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 0068/1148] 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 513e2b27f9d3d0d8c8fcb058fc7765e34f5c6376 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 0069/1148] 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 333ee1b5cf5fdbcbaff8c687d6acb12253c9c834 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 0070/1148] 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 a6bc55f2484ddccdb20d9e871fdc4f60fdcbb11e 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 0071/1148] 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 5d831b85789c9b2dacf8372ec36a55443f4dbbd9 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 0072/1148] 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 0e33e8bae2d5cd47eb384df33e0846b3221233a1 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 0073/1148] 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 0c452c2feddf3351431b69c4fde06dc0b47aa95f 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 0074/1148] 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 0d007d0f0851d1d0df80a6684baf2339d56a5de6 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 0075/1148] 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 3c593757991b9d54b3394900d9cf0ae557c34b0c 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 0076/1148] 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 1addefa61f1bcf2989213a6bd1b4eda7e25d4d16 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 0077/1148] 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 5aa040f785d2062453beaf531e0b2107beafcff7 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 0078/1148] 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 9a6d64ad01372bdb8d44ef84186224edd265ace6 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 0079/1148] 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 e5b76fe0e47e0e1710c4eda34b0692df8d4f3f16 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 0080/1148] 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 ae5c3eea67de2a5169dc171d966befc684f56ae9 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 0081/1148] 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 7f2a3d8099c91f67b1dba3251de62b7b9cee14ae 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 0082/1148] 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 04c54f5fd7ce3700f291b0caffdfc2155170fe57 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 0083/1148] 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 f10f62cae1024713eb1ae13106de44822284c02d 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 0084/1148] 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 394ba901005eb2cdc395a3a023435f54ad138c51 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 0085/1148] 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 5ee688e51067057185410a8441dacc735c90663d 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 0086/1148] 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 0414c6b770595e94f344fc05be5f421941087ba9 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 0087/1148] 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 934b6e3983b81e89dcd441153418872b13f4456a 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 0088/1148] 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 d3998a25fac6b1fa436c8611ff77cc427b76b7bc 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 0089/1148] 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 1dba0fcd91ff7c54b9d9f8982b23103986592505 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 0090/1148] 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 599b9d0337e811d878b9a391c5bb1ba226e13ad6 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 0091/1148] 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 e5ddfc45ca9496d581b7524fbb53b56dc49641b3 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 0092/1148] 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 cdea5fd42fd171ab7343f8ff39a148f995b3716a 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 0093/1148] 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 cb0442be9f9d9b1e8931daf496262d03fe4ba7cb 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 0094/1148] 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 918856989fa7571da1248ff20f372ce9f140227d 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 0095/1148] 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 7fae9b9dfc1732bf896ecd1b0a9bcea93fa0b7b4 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 0096/1148] 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 1c8fec7a11f6df1dbba9921054ebe337bccd1ec6 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 0097/1148] 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 24be4a3cbb6874713117f8fa5db00210b7728f32 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 0098/1148] 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 ad0f0d89d70a4ef0cbbe12c14ddb1ecf73d6e115 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Mon, 27 Feb 2023 16:10:15 +0800 Subject: [PATCH 0099/1148] 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 53e9b2fbc6a6628154c1600f4be9a3f0edb2cbda Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Tue, 28 Feb 2023 19:22:54 +0200 Subject: [PATCH 0100/1148] 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 8deaed9864315400a0d1a59b35801a6de809f47a 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 0101/1148] 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 41e9730b9f6d91880b3991562ca819c212ad0a8a 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 0102/1148] 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 abbbcce8ba4ed33eb704420e7717742abd0c52ae 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 0103/1148] 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 e6cfc86f399732ac392a2bb0581d4b08f30c9b91 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 0104/1148] 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 3f612ab4100d4a8604c378fba5ad4371c3136db1 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 0105/1148] 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 0a88345ce74bdd4ea977867404cf8cc9e6ee9f36 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 0106/1148] 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 64ae120c0152ee4791ba8916f381d9e21cd12990 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 0107/1148] 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 1d205abc94f114770c5a66feb8b6f88d509fc64e 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 0108/1148] 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 aeeb6e14a67d2d2145ffb623212cfa5e98f90305 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 0109/1148] 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 bbfa02ebc84d613602ce33faf29e7efd2b54ed86 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 0110/1148] 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 fac285db3d094fafeb8d0255a83c79e26efd985c 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 0111/1148] 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 dda32c1bad91bbf36312b27e364d7f01cc352483 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 0112/1148] 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 c462141c367aa0d6e92f55bb0a459bd93d8f4fb9 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 0113/1148] 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 c6a05aeb218cf64c77f775eedf543e5d7038d5ab 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 0114/1148] 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 1a20365a20628afb03a4aebbbc4c06aed26b1fbe 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 0115/1148] 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 d3e4064a4bd64d15b759c68deb8172b43f3893e2 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 0116/1148] 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 26faadf4a4ae73f4de7961d5f6c6a8baa88a9cd6 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 0117/1148] 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 28e5a63436187d2d3143606394ffdd3229324aa9 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 0118/1148] 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 25690c381360c3ac96830494d5d7000a5a85b016 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 0119/1148] 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 1531479380f678be9795cf25c7b7fc17073a8156 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 0120/1148] 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 e6b2e65ba6f055b2f9b41dea8533408d1d413346 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 0121/1148] 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 a5681cb04928251050ddb4f1c4f6caf1261f8e25 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 0122/1148] 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 05b42ecd89905567a4114bf2d0533c81b421e405 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 0123/1148] 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 fc45800d973bfc886c38d94a2a6da580242e786b 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 0124/1148] 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 3f5198c1285de2ec8ae53da7d5316f0701a82d77 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 0125/1148] 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 7512a5514845ac685efa1c6ec3117a5035de3392 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 0126/1148] 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 311bef7dab9f51c06367eaafc1b905213fa769f2 Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Tue, 21 Mar 2023 04:18:26 +0100 Subject: [PATCH 0127/1148] 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 38f1a0b222d0aa8b147774a440eadaf7f4f630f2 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Mar 2023 13:44:57 +0100 Subject: [PATCH 0128/1148] 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 b53411f7525c60e2e29e7e42a5324ffa02356755 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 0129/1148] 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 7c6f448967feb2eb89ad989f5547c37beebbf9f3 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 0130/1148] 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 587cf862d6f92deabe6dfd1f6b7c2ee188bb8a1f 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 0131/1148] 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 16a5fb1c65c8b2d0763083fb00598a6f1ba1e528 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 0132/1148] 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 5869102d7766542bae12b454d87f18a4943ca0ff 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 0133/1148] 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 eec33f982ff590e1fced2c3d5618843dbead34d4 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 0134/1148] 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 7cd00e2ba80d1a1f485cd38acc8cc2985842ac37 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 0135/1148] 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 f48c5abceb922fed3959829690578489176b78ee 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 0136/1148] 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 a14d783e22870ea4316f085dc89c3ffe4aff38ba 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 0137/1148] 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 50d3bcf578d5132534f258d6205da1b62e504f51 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 0138/1148] 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 fc35326f86a9eb820062ebb80a032c47da1ba994 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 0139/1148] 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 fe910657b03f091d28a79e0b850e85dbe92d3951 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 0140/1148] 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 0a95dfdacb10659647b6f8a85a5a12fb1adf43d9 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 0141/1148] 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 9916a18da201000076014a432cf818ef251d2f04 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 0142/1148] 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 ca03c7d9cfea9bbc8130957d9d64daeda1ee6b34 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 0143/1148] 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 42a89983bb91e38cd6a5d5defae871e2b6542d6a 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 0144/1148] 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 47c7d8c76f2a9f46c36997cdb28820a59d22a4e4 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 0145/1148] 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 8edd50b83df56689bfe604540f1ddfcd59118369 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 0146/1148] 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 0626d1c7bb2ded6c7ac8e0526d46347c4b467ba6 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 0147/1148] 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 dccf894f376a7253e9358e1b65e4f67a774728bd 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 0148/1148] 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 c04b99dc65fa60dc6b688836b9d35f3a72ce71cd 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 0149/1148] 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 72dcab9f50335fe778562e9ac4e2fb30b0f6d52d 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 0150/1148] 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 14003c5dc8de6c01fbc751c3000a02b517adff12 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 0151/1148] 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 88b103ebe811fd71ff644ea98dd9641f277c2a59 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 0152/1148] 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 9dfaca970275f023a759415c1c89ac8c50f21e3c 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 0153/1148] 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 c1fd4d18816a80dc0500d508e72acaf6ff13d8d2 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 0154/1148] 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 9c037232ef1462080c3166864932bccdef68b6ab 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 0155/1148] 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 abb3f74f2bd09f5cec585cb9607171bad4969e71 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 0156/1148] 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 885d9f5c90a763b16d47fe0a8b9a16d2f0306db1 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 0157/1148] 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 10c5d11ba6f9078ce6ef4af609d3b558ec95eb1e 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 0158/1148] 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 e337d9491e14bbdec68faee9ef0a5479f0288777 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 0159/1148] 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 0d7a332ce471c935fb8925c3bc27ee9b49195277 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 0160/1148] 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 6ac81d2fba3dde3748b39a947f523a17d32b3a13 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 0161/1148] 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 73e0d3bc251f70c5bb279f4bc49383e16b4ac23f 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 0162/1148] 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 dd30bf93abccc18afcfb265e856889362a28718a 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 0163/1148] 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 b896c3c43af813ded46c74bbe46dd68311bbe66c 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 0164/1148] 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 da1916e67f4991a30cdbc9c2b596eb86074c5eca 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 0165/1148] 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 2ba363313855123d5bb31a529d5b3584d1edb447 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 0166/1148] 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 7d1260ba2a17ef35a2b9bbb4a9f60c0d9d0b158a 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 0167/1148] 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 21d6ddafc23a3c545af4c52ad5c3af260c111ab9 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 0168/1148] 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 bae2a23262954f0078ba9cad31b0a5d2318af2d3 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 0169/1148] 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 3ecf2c066209b382568f56a80b22c313ea0f1f7c 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 0170/1148] 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 d9f35d78cf77f0042d3392c628b9b8eeb9daf953 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 0171/1148] 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 5d7c1cc6246d89650c042f063b274eac109fdee3 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 0172/1148] 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 a15058a03cbeaa84c3319e1c0b5596c0b4ec2f5c 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 0173/1148] 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 ea2faa95828..f1b706b3721 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2771,7 +2771,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 32030851bf6ec2fdb2de4a0be0c00d61bf630cb5 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 0174/1148] 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 fcad4ec5968c15b1b848d270255c88966cfd6592 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 0175/1148] 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 783fa9376e2c69d1a7be1a506788dc15dab08a82 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 0176/1148] 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 3adad4db67fd05a0ae594a107e3ca3c4235b928c 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 0177/1148] 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 b19c329d2b381ef0091f21dda8a75ef2c5cf55ba 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 0178/1148] 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 66b7a9c5fb2fcbcacd144ba07e67a2c489ff4cf3 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 0179/1148] 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 b69e54fd23c20578846555dad14aa0f01d80c991 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 0180/1148] 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 9b725cc89a5687f3d66de37a3660a4588407be21 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 0181/1148] 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 c4943d3144f0e1155ec244ffb7d52cda1e0642b9 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 0182/1148] 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 61c0de7bb232a61cd92311caeac19b5f655021c9 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 0183/1148] 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 3aff333603ddc8e4f855aab303602bbe534f94ca 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 0184/1148] 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 80c9cf5586553f8310bde53868cbf3b05bb0aa31 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 0185/1148] 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 9dbc6dd2103d3fcf8b2accc11356315804e2ece5 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 0186/1148] 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 ea05b46c7a175cdc1ad5e4dc588ad391980ff128 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 0187/1148] 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 c5b8b6c00a0f1d140548ffa59ac90d1400ffc9d9 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 0188/1148] 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 25376c9939cfb1559065588d1c475c5ac71aef55 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 0189/1148] 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 794a1e7c196354d92ef99b1c16ea739105acf0cc 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 0190/1148] 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 375cb52fa89cb7eab3838b2c9d191d6c46143927 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 0191/1148] 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 e4f7bf205ea875e23f2ce8f0c8d363f6413e6a74 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 0192/1148] 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 1ee65acb4d15c7d9f08e78e313c5a0d29b3f9543 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 0193/1148] 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 c95474ef0670bc1de4049f859bc65ee984da50c4 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 0194/1148] 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 ee851f489f3c79161bc0df8e7f33328551bce4a0 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 0195/1148] 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 cf7e51c54d333164f61bebacd0e708b45abe15e1 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 0196/1148] 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 476f7a7e741a4b118f0c9a1db7b6dd3ba562efaf 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 0197/1148] 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 6194e6c804495c80a7b54e6c6e53496c113cefc6 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 0198/1148] 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 edff6100de575cdb8e527cc5aee0f62f4486d88a 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 0199/1148] 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 5b100ffa387986e6ad4037369d12e230d2977eda 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 0200/1148] 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 b70b3a0933e5e1bc97c3e4a54915cd65a7431a2b 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 0201/1148] 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 f1b706b3721..05d8977bc34 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2756,28 +2756,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 97a67c5739d118467bae46095dbd4548f5058c18 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 0202/1148] 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 9a9c0da1f959b04dd34f50f01705a1be0b5e523b 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 0203/1148] 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 e0b84f4b41ba1af050b1da68149e4b9955cb814d 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 0204/1148] 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 de2eef4b153cd11a178b5365e0c04628efd6293e 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 0205/1148] 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 ff8c0c4e85d81571dfade9eba8cdacfdc490b884 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 0206/1148] 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 27719b1e42c2ea939a7ef316fa66410036576e1e 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 0207/1148] 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 fa3036d48dc13b3eaa581c2b1bed9e1d75250c40 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 0208/1148] 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 26e7d67b9b3bee56ec763eb645496d8b6ddddbd3 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 0209/1148] 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 a37eecafa50530e9b202e3931722dc88fd12c05c 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 0210/1148] 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 0029a3454ae68b184acf8766fe10766592a1f65b 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 0211/1148] 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 74b97c796dce8d92e00f79f35b38c22416bffa80 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 0212/1148] 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 dbd0f02625c7f3114e2f046154cd9ff3f94a9fa2 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 0213/1148] 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 b95b20a4768dc519a505c002d06cd887169af122 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 0214/1148] 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 65020d639b40025fb19777da1b42f1c7ecc19a2b 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 0215/1148] 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 b9f3a4452f113f896cace04ddde25280f9505012 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 0216/1148] 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 3b803cd3dfe0cc51f0c8f65a90105f6308d3592e 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 0217/1148] 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 662583d7bea9174ebbcd25a6e143ce8ece88a61d 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 0218/1148] 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 3774dccde4ff039a0d5fadeda4270eab886deae8 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 0219/1148] 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 b92fafb9d74f35f876153dceb98516d5dc2918f1 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 0220/1148] 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 ace83d75fd18fe1a607222515701b5ed00a2c120 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 0221/1148] 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 98e21180b345e9fde623c77d869e1b426a983bb3 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 0222/1148] 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 1411abb9474a498366b117f350959ee47074ce24 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 0223/1148] 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 e567982ab509ae48b04d6779a394ecbb2f09338f 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 0224/1148] 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 d8c9dcbf06c75755354b760ac1e5349f3e6543cd 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 0225/1148] 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 0c953a9f4c6bd3b1a71475a136eea1c6e811d3ab 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 0226/1148] 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 840c5cb8ddf6413a38d0fc5bf932766d66518f33 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 0227/1148] 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 2a616e4972ab8d8bb33528eaddfb97c954f7c430 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 0228/1148] 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 d6586cb841ef3e16360c05aaf911f96cfc2ffae5 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 0229/1148] 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 9471eb46da94472a104e0784179f60d3854a5aed 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 0230/1148] 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 df8e654167f0a88f45c3ccc7acce27cfebb867b5 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 0231/1148] 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 e67e27ef4c61768195a5cbd09afe09ebfa3efe37 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 0232/1148] 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 5e380fa6e1628b6f93213217b004c30ab9cc6515 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 0233/1148] 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 294224b411ff31d57567d95486722edacf3ca024 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 0234/1148] 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 1d755733efdd74d1290f3106e0454df4380c9a6b 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 0235/1148] 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 72d663272bb86fceabfc871a83c6f1a422862fe1 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 0236/1148] 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 490509f86ed72e0342729c09375dd8c0e5b1596c Mon Sep 17 00:00:00 2001 From: Byeongsik Jeon Date: Mon, 17 Apr 2023 06:28:41 +0900 Subject: [PATCH 0237/1148] 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 251ce39e57c7db47c34af7027bb7e5911262b632 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 0238/1148] 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 7e387a96666233c181a058cb74d477a30844bced Mon Sep 17 00:00:00 2001 From: Byeong-Sik Jeon Date: Mon, 17 Apr 2023 10:10:28 +0200 Subject: [PATCH 0239/1148] 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 13c8b541adae915cb10745b086f623fb44d3aca5 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 0240/1148] 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 3e4ef1e71ac66b2c0285a2f233abf0d301214291 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 0241/1148] 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 5a2b82a4768ae92eaab3c44466c5de7f0a1b6642 Mon Sep 17 00:00:00 2001 From: Michael Stefaniuc Date: Wed, 19 Apr 2023 19:28:53 +0200 Subject: [PATCH 0242/1148] 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 1baab3764b5780649dba0594bc24a592b93351cf 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 0243/1148] 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 6cb9c3d3c1b3f7213c7beef8d56903d9d6288a5e 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 0244/1148] 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 21d363f0a3ced32df64e6d5accabbea46221a83c 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 0245/1148] 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 bcf505f6a93bb2079c687bd431fac1c6dc352419 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 0246/1148] 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 4c3578a9a3380db9f74d67f89e897825b031d457 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 0247/1148] 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 fabe5063d1e1ae5b8078266c2a924abfb705e064 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 0248/1148] 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 a752dcfcf3252d0be739736863269ae45f790d17 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 0249/1148] 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 ce6a9c3dc825d2484e9c83611f7df6c5039eb902 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 0250/1148] 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 bd92485c5dfac8c8f725c3d90c313dd7df7169d1 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 0251/1148] 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 ee1539cc6fb1ba4d82b687b63d83aa0207345d0e 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 0252/1148] 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 75daed168c040b3da5a39e4a7f0cf6732bea81f0 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 0253/1148] 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 be00f4eb1a4eb1d957375046fb34dad8f88e54d3 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 0254/1148] 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 c3004eea478a5fa09748126de770d033dc644da0 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 0255/1148] 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 46856180d8baae1a9b26b6587bf937c55340fe93 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 0256/1148] 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 91389302c6c6ca2144d3800dd2f137eab1cd3003 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 0257/1148] 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 429fecc0875de1e12da36e40f042d27121f6a33d 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 0258/1148] 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 b68e428629e4b42564cdf9bcf76a45279a254b40 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 0259/1148] 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 1a19ed9d7367a54b72284ed464f5d1b9e05a7e71 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 0260/1148] 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 309135fcea663e18e06c7ec42a38755b39367ec8 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 0261/1148] 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 52fe3b182c3c96630335972deca69737415c356d 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 0262/1148] 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 147cd413e7ab2fa3fc08cf7585d33928b491bf5b 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 0263/1148] 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 1eefd7e328d07c662a4cbcc98ecbb1ccd9f070fe 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 0264/1148] 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 27ff9fd8e74f8b008d4fac5d862298aca97c70d5 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 0265/1148] 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 c62ec817af4f8bb7add90e10e993d37fb5c86015 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 0266/1148] 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 9440fd1e124aad44499f82e4b6a9e96f6fd82981 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 0267/1148] 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 73afc0b7860f1bcba34d960b5f865012e2537b53 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 0268/1148] 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 d319e9b3ef9a52022450f435f1f3f9c3151b076d 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 0269/1148] 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 a011c697640891ecc1607805b0c6fa0e22313761 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 0270/1148] 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 b855d7aa31814522864153448c3ddd04519e4229 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 0271/1148] 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 f2fa286ad4debd0fdda9ccb9f456490cf0f2a639 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 0272/1148] 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 4ebb25f951002e70668807094f1ad007df43e2b9 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 0273/1148] 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 e7007ea85c001998a063c59ae5cfc408f6374a85 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 0274/1148] 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 96bedd7acab..bd7916da900 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 6300552b77ecede3c952aa5f9bf2317bba7fd88a 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 0275/1148] 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 e69df13398074a692797e70407e07bf2ab85c84b 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 0276/1148] 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 62d492e732806bddf9c0c5fdfa4b4fb93160a712 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 0277/1148] 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 efc05ecd23ddba071b2cac1f1557dc57183a4e42 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 0278/1148] 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 0349c88ce08fc247b66a374e5e8966c27288bcc4 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 0279/1148] 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 bd7916da900..03d164c05b9 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 cdf62585e0412de1f2eeb14b6976992913ac481f 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 0280/1148] 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 7c8ca68b683a73e491880f9428f797ecea320af3 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 0281/1148] 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 03d164c05b9..e66585384e2 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 ca7bce1ab9aa4af2ea0b54d3aafca01491d959fc 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 0282/1148] 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 0ee19914ed8bb408e3c769398d7e252aea9d0a05 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 0283/1148] 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 0f0c5dc7c868516852a25e8d654a5c152502a794 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 0284/1148] 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 d178979a139c7927fab4eccd31548fb6d15feaea 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 0285/1148] 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 f7cea8312f9641456dcefdf9aebf2aac20b6343f 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 0286/1148] 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 21d5b511fdf6af775d01c12b5924a4a025045ef8 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 0287/1148] 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 fa58369f3b3fada36e2795f4a0792e299e61a496 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 0288/1148] 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 131f0c935a286f08d2e7123bc0d0d011f0f6af78 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 0289/1148] 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 3bb2be7d0181a9da2036631afcb8d5032434306e 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 0290/1148] 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 45d9ce0828ddd32bd890b6fe384a283209e5b4b1 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 0291/1148] 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 2450f98519b6ac50240ed7159ec3921af8612146 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 0292/1148] 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 556f341dda5f9f69ce748ecf442eea9980db9138 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 0293/1148] 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 0b66ed2321ba383c3d50728a73660d985226401a 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 0294/1148] 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 14f13ad6c5b291c4248e6da698c8fb1ea6916b4b 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 0295/1148] 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 f24ae7a323c0ea45831fdfd50f409cd076dc4eb0 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 0296/1148] 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 f075bb0e6182592b5da3ed1d04cbdbfad7d66200 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 0297/1148] 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 97022d691edd597f853d0fd73d11e146d69e2b5f 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 0298/1148] 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 312651a49f436e6dd9c9775d9e22a4b45077afe1 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 0299/1148] 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 7d002d068045825562a2eff102e6d7c1cbae1fdc 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 0300/1148] 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 51ad184b5848d7c3c70e036a9d95da835dd51b1e 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 0301/1148] 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 1ba8b5cca4eb58384d41881fecc449642f50dd85 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 0302/1148] 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 9a423b9aba33de3648fa31ed5af73f3b6a4b96d0 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 0303/1148] 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 d78c3adb6af186cc80a28a7e477f61c93d656fd5 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 0304/1148] 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 fc4430a39049238a6528bf359a878668c43de50b 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 0305/1148] 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 bbf40ec29c4a2304389e86c39bd236e39bc8f9ed Mon Sep 17 00:00:00 2001 From: Jinoh Kang Date: Sat, 20 May 2023 23:34:23 +0900 Subject: [PATCH 0306/1148] 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 4458bd9aafff2fa1c7defb28f5bdccc6be5eec1a Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Mon, 7 Dec 2020 09:31:52 +0100 Subject: [PATCH 0307/1148] 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 0308/1148] 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 40543bb5213625a61b7086f5212aab50822cb26c Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 8 Jun 2021 16:01:54 +0300 Subject: [PATCH 0309/1148] 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 36d90914e67cd29bd8e14f2918cfd8df1bb93034 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 0310/1148] 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 05d8977bc34..2e297ed3e05 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -446,11 +446,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 feca43e71a9445087ffdd8547ca484cc75a7b23f Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 7 Aug 2023 21:49:05 +0300 Subject: [PATCH 0311/1148] 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 e6914a83167358c191beed955032d6ed678fd459 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 7 Aug 2023 21:49:19 +0300 Subject: [PATCH 0312/1148] 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 81bb94a17d2c1a39a44ae3bb2874e9eb8085498b 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 0313/1148] 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 33c22b48bce23079956a9d9d99fb551c45c324d4 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 0314/1148] 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 0499f7f15721c8956624dbf5ce2fd53c2e27a857 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 0315/1148] 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 097cd35c88764ff09790b5b2038c153ad7f4170f 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 0316/1148] 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 ec61a48c51fab39ce0dc256568a21c4c633c3841 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 0317/1148] 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 a90a0fb81f5603c6a1a5b25853d2c14d5ab32409 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 0318/1148] 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 3b2e9d35ed55e67615fd0b3ef66356f6836767d4 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 0319/1148] 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 272ce8f80d45c548df08dd9e8f95a7aefef4899e 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 0320/1148] 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 63898ad4325b6717bcddf6e4a57284b9ece628fe 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 0321/1148] 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 623d7f0b4bdb9f9be67234ed250b4b7ce861f870 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 0322/1148] 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 52775088d4bf9cb88db6c1d9817902d83a6eabce 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 0323/1148] 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 b8fdbba718b0224bd889e68cd038248bd537d8d7 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 0324/1148] 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 4d475e5037e42cbd5037555247058c72da0c3be3 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 0325/1148] 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 605bf6ec04c854e4ef67c1e655adc8f8a61484af 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 0326/1148] 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 08da46e553e8b1c418848700bde86725a9e4034b 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 0327/1148] 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 10822f6258fcd7103043b5c0ae81c076876e6659 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 0328/1148] 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 bbe60680b4d60e2244073fa4a9155572e0a8ef1c 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 0329/1148] 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 1912f053b64aaff4918d2265ba81c78409a97e10 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 0330/1148] 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 1adb330371bc8521e1f4f1b9928c5828e7222426 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 0331/1148] 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 bb0581dc880fd8753f0f2d76c47ed74a17836716 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 0332/1148] 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 25a17fef0f7c94b253f4cd648e28557146410ee3 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 0333/1148] 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 2623db0c7792409d0bac2b7606af157312906c12 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 0334/1148] 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 92930bd970b688906299b97b7967f19c49102b21 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 0335/1148] 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 ff45cc75d6629953cdc51e28ec7613555a1a94ce 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 0336/1148] 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 d79f3df4dc57e59874e1c914301792434d94599a 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 0337/1148] 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 01b1a9b445731f4131bf0d60bf8ab71c0bd290cc 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 0338/1148] 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 b1dfc48d0b0dc7a4e2a938f4c8e488216291557b 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 0339/1148] 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 9337cb91d95f9a3f176b39b48495d33051c20e4c 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 0340/1148] 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 7e2bd01a68919829f39941489ad7b73fa71e9266 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 0341/1148] 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 8e9fec431e2dd63bbda51c1f24441555bf6c94e1 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 0342/1148] 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 6444e4ef57cefe55afd635bee4647da1a0a04eee 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 0343/1148] 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 681ef4abfa7..f2b2ece4adc 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1918,4 +1918,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 8f76b3c2ab5328955ffdb0fb3fef2f0b23c004f5 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 0344/1148] 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 f2b2ece4adc..a5e1716f5e8 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1461,8 +1461,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; @@ -1493,7 +1491,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 7aeed26aa65ea9f062a89fa3b18f15cfb798ac80 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 0345/1148] 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 e71be53594fa4e40b9785db7a24be119a1ef1b42 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 0346/1148] 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 a5e1716f5e8..5b72579da31 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1919,4 +1919,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 13d4eb394e16f4a8446ea1c6cda10be2e190a458 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 0347/1148] 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 00cd1043737681e325e67c4cd100272d87748d31 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 0348/1148] 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 b47bed2d2d6f99e384393ca207c8cd21508eb883 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 0349/1148] 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 dc0f784c345c47cfb909047229c5e907f6d48e22 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 0350/1148] 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 070346f66820366d315196e21c3a980173deeab2 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 0351/1148] 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 62f698530973affb23bbced90e2a4a2647c9abc0 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 0352/1148] 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 537c41232c7ecfd2b8b77e3c550b811f6290b1c7 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 0353/1148] 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 2c7f7848f25fed97028317268eba4da82bb45c54 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 0354/1148] 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 d6691716678625596c0b0b3a2ee0834127d58f90 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 0355/1148] 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 0c08afb7425e2703ccd23b2b9e5879eddaf3c71e 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 0356/1148] 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 5b72579da31..b3d0e6d2716 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1919,5 +1919,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 5fcfc7a5f94f2fe933920117ebbc3128b0047a8d 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 0357/1148] 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 3e3566b359de898e4d6a719b34dcc5a3d064540e 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 0358/1148] 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 d4ec6c6d4a012b157fc5e5e032b0f6ca1041697c 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 0359/1148] 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 a28ab7853c3aa80d3ab1c5a68b6e37314e5914af 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 0360/1148] 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 7ea898f89acd7f413c571de92e9c53cb129d34cb 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 0361/1148] 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 4a438875a8612949e8b4e96da1a0b17ce284051c 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 0362/1148] 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 b3d0e6d2716..105b25006fb 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1921,4 +1921,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 e3c9763c86955af9955154a7e8c9753b0b575e47 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 0363/1148] 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 105b25006fb..002142a6beb 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1922,4 +1922,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 f2e8d2f8604431f060bab7580f3596431d8e83ee 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 0364/1148] 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 e960a00b37e998ddb64d97433c7962c227d9ba63 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 0365/1148] 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 24b84e4175244ae428e9dd0e3760144dfd03ce48 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 0366/1148] 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 bdf9ae79bd0a7bfe42a8d0a00ad99fc9922315f0 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 0367/1148] 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 571a6d1780144f19e20323bce9088843b4682ee2 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 0368/1148] 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 918385b3e8f37627aeeba01c28cec867856ed890 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 0369/1148] 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 c43c139dea4e6b4a6de767a8adf5c5ebeafefc38 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 0370/1148] 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 002142a6beb..0407676e63a 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; @@ -1327,6 +1328,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; } @@ -1687,6 +1706,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; } @@ -1829,6 +1849,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; @@ -1842,6 +1863,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); @@ -1864,6 +1891,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 7c9eec349998505bdd55203e698fe17ecfa4b5f0 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 0371/1148] 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 f64062bb6e4091a978c982061011c3499e2f6224 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 0372/1148] 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 9f6b46adfbaa547452eb40ce4973693921a301c1 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 0373/1148] 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 2b268f523473164dda9e1c4670dea91c11e05adc Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 0374/1148] 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 426362bda0f7fd3af2b37e18f2d56a754ec8a6ea Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 0375/1148] 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 a0997101ebba9785db861f4f5dff04af85aebc35 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 0376/1148] 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 551113bffc5abd1e1e3e50bd4b8804d0d2f8e0f2 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 0377/1148] 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 0407676e63a..eb928059a7a 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 fa2abde2d4413e27c047479c386c47d129c08112 Mon Sep 17 00:00:00 2001 From: Anton Baskanov Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 0378/1148] 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 004e83effc089e7a544601fdc756888696650a26 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 0379/1148] 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 8c294ba01a5f058fd5c9491c2384aa63c887b362 Mon Sep 17 00:00:00 2001 From: Anton Baskanov Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 0380/1148] 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 2ec6a43f709fa4c326e04326b6896bd3a9d0ebc9 Mon Sep 17 00:00:00 2001 From: Anton Baskanov Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 0381/1148] 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 2e9c512af49d20a55698e71af763d9ccbc707e1b Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 1 Feb 2023 01:34:12 -0500 Subject: [PATCH 0382/1148] ir50_32: Add stub dll. (cherry picked from commit 91c1c05f5b0d224b664ae5fd6c0bc93db4eab872) CW-Bug-Id: #21175 --- configure.ac | 1 + dlls/ir50_32/Makefile.in | 7 ++ dlls/ir50_32/ir50.c | 180 ++++++++++++++++++++++++++++++++++++ dlls/ir50_32/ir50_32.rc | 29 ++++++ dlls/ir50_32/ir50_32.spec | 1 + dlls/ir50_32/ir50_private.h | 34 +++++++ po/ar.po | 12 +++ po/ast.po | 8 ++ po/bg.po | 10 ++ po/ca.po | 12 +++ po/cs.po | 12 +++ po/da.po | 12 +++ po/de.po | 12 +++ po/el.po | 8 ++ po/en.po | 8 ++ po/en_US.po | 8 ++ po/eo.po | 10 ++ po/es.po | 12 +++ po/fa.po | 8 ++ po/fi.po | 12 +++ po/fr.po | 12 +++ po/he.po | 12 +++ po/hi.po | 8 ++ po/hr.po | 12 +++ po/hu.po | 12 +++ po/it.po | 12 +++ po/ja.po | 12 +++ po/ko.po | 12 +++ po/lt.po | 12 +++ po/ml.po | 8 ++ po/nb_NO.po | 12 +++ po/nl.po | 12 +++ po/or.po | 8 ++ po/pa.po | 8 ++ po/pl.po | 12 +++ po/pt_BR.po | 12 +++ po/pt_PT.po | 12 +++ po/rm.po | 9 ++ po/ro.po | 12 +++ po/ru.po | 12 +++ po/si.po | 12 +++ po/sk.po | 10 ++ po/sl.po | 12 +++ po/sr_RS@cyrillic.po | 11 +++ po/sr_RS@latin.po | 12 +++ po/sv.po | 12 +++ po/ta.po | 10 ++ po/te.po | 8 ++ po/th.po | 8 ++ po/tr.po | 12 +++ po/uk.po | 12 +++ po/wa.po | 9 ++ po/wine.pot | 8 ++ po/zh_CN.po | 12 +++ po/zh_TW.po | 12 +++ 55 files changed, 777 insertions(+) create mode 100644 dlls/ir50_32/Makefile.in create mode 100644 dlls/ir50_32/ir50.c create mode 100644 dlls/ir50_32/ir50_32.rc create mode 100644 dlls/ir50_32/ir50_32.spec create mode 100644 dlls/ir50_32/ir50_private.h diff --git a/configure.ac b/configure.ac index 92dc784035d..e4b85800b4b 100644 --- a/configure.ac +++ b/configure.ac @@ -2703,6 +2703,7 @@ WINE_CONFIG_MAKEFILE(dlls/inseng) WINE_CONFIG_MAKEFILE(dlls/iphlpapi) WINE_CONFIG_MAKEFILE(dlls/iphlpapi/tests) WINE_CONFIG_MAKEFILE(dlls/iprop) +WINE_CONFIG_MAKEFILE(dlls/ir50_32) WINE_CONFIG_MAKEFILE(dlls/irprops.cpl) WINE_CONFIG_MAKEFILE(dlls/itircl) WINE_CONFIG_MAKEFILE(dlls/itss) diff --git a/dlls/ir50_32/Makefile.in b/dlls/ir50_32/Makefile.in new file mode 100644 index 00000000000..7bac0ccc9ca --- /dev/null +++ b/dlls/ir50_32/Makefile.in @@ -0,0 +1,7 @@ +MODULE = ir50_32.dll +IMPORTS = user32 + +C_SRCS = \ + ir50.c + +RC_SRCS = ir50_32.rc diff --git a/dlls/ir50_32/ir50.c b/dlls/ir50_32/ir50.c new file mode 100644 index 00000000000..3019301742e --- /dev/null +++ b/dlls/ir50_32/ir50.c @@ -0,0 +1,180 @@ +/* + * Intel Indeo 5 Video Decoder + * 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 +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "commdlg.h" +#include "vfw.h" +#include "ir50_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(ir50_32); + +static HINSTANCE IR50_32_hModule; + +#define IV50_MAGIC mmioFOURCC('I','V','5','0') + + +static LRESULT +IV50_Open( const ICINFO *icinfo ) +{ + FIXME("DRV_OPEN %p\n", icinfo); + return 0; +} + +static LRESULT +IV50_DecompressQuery( LPBITMAPINFO in, LPBITMAPINFO out ) +{ + FIXME("ICM_DECOMPRESS_QUERY %p %p\n", in, out); + return ICERR_UNSUPPORTED; +} + +static LRESULT +IV50_DecompressGetFormat( LPBITMAPINFO in, LPBITMAPINFO out ) +{ + FIXME("ICM_DECOMPRESS_GETFORMAT %p %p\n", in, out); + return ICERR_UNSUPPORTED; +} + +static LRESULT IV50_DecompressBegin( IMFTransform *decoder, LPBITMAPINFO in, LPBITMAPINFO out ) +{ + FIXME("ICM_DECOMPRESS_BEGIN %p %p %p\n", decoder, in, out); + return ICERR_UNSUPPORTED; +} + +static LRESULT IV50_Decompress( IMFTransform *decoder, ICDECOMPRESS *icd, DWORD size ) +{ + FIXME("ICM_DECOMPRESS %p %p %lu\n", decoder, icd, size); + return ICERR_UNSUPPORTED; +} + +static LRESULT IV50_GetInfo( ICINFO *icinfo, DWORD dwSize ) +{ + FIXME("ICM_GETINFO %p %lu\n", icinfo, dwSize); + return ICERR_UNSUPPORTED; +} + +/*********************************************************************** + * DriverProc (IR50_32.@) + */ +LRESULT WINAPI IV50_DriverProc( DWORD_PTR dwDriverId, HDRVR hdrvr, UINT msg, + LPARAM lParam1, LPARAM lParam2 ) +{ + IMFTransform *decoder = (IMFTransform *) dwDriverId; + LRESULT r = ICERR_UNSUPPORTED; + + TRACE("%Id %p %04x %08Ix %08Ix\n", dwDriverId, hdrvr, msg, lParam1, lParam2); + + switch( msg ) + { + case DRV_LOAD: + TRACE("DRV_LOAD\n"); + r = 1; + break; + + case DRV_OPEN: + r = IV50_Open((ICINFO *)lParam2); + break; + + case DRV_CLOSE: + FIXME("DRV_CLOSE\n"); + break; + + case DRV_ENABLE: + case DRV_DISABLE: + case DRV_FREE: + break; + + case ICM_GETINFO: + r = IV50_GetInfo( (ICINFO *) lParam1, (DWORD) lParam2 ); + break; + + case ICM_DECOMPRESS_QUERY: + r = IV50_DecompressQuery( (LPBITMAPINFO) lParam1, (LPBITMAPINFO) lParam2 ); + break; + + case ICM_DECOMPRESS_GET_FORMAT: + r = IV50_DecompressGetFormat( (LPBITMAPINFO) lParam1, (LPBITMAPINFO) lParam2 ); + break; + + case ICM_DECOMPRESS_GET_PALETTE: + FIXME("ICM_DECOMPRESS_GET_PALETTE\n"); + break; + + case ICM_DECOMPRESS: + r = IV50_Decompress( decoder, (ICDECOMPRESS *) lParam1, (DWORD) lParam2 ); + break; + + case ICM_DECOMPRESS_BEGIN: + r = IV50_DecompressBegin( decoder, (LPBITMAPINFO) lParam1, (LPBITMAPINFO) lParam2 ); + break; + + case ICM_DECOMPRESS_END: + r = ICERR_UNSUPPORTED; + break; + + case ICM_DECOMPRESSEX_QUERY: + FIXME("ICM_DECOMPRESSEX_QUERY\n"); + break; + + case ICM_DECOMPRESSEX: + FIXME("ICM_DECOMPRESSEX\n"); + break; + + case ICM_COMPRESS_QUERY: + r = ICERR_BADFORMAT; + /* fall through */ + case ICM_COMPRESS_GET_FORMAT: + case ICM_COMPRESS_END: + case ICM_COMPRESS: + FIXME("compression not implemented\n"); + break; + + case ICM_CONFIGURE: + break; + + default: + FIXME("Unknown message: %04x %Id %Id\n", msg, lParam1, lParam2); + } + + return r; +} + +/*********************************************************************** + * DllMain + */ +BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) +{ + TRACE("(%p,%lu,%p)\n", hModule, dwReason, lpReserved); + + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hModule); + IR50_32_hModule = hModule; + break; + } + return TRUE; +} diff --git a/dlls/ir50_32/ir50_32.rc b/dlls/ir50_32/ir50_32.rc new file mode 100644 index 00000000000..fd98f76fbf6 --- /dev/null +++ b/dlls/ir50_32/ir50_32.rc @@ -0,0 +1,29 @@ +/* + * 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 "ir50_private.h" + +#pragma makedep po + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE +{ + IDS_NAME "Indeo5" + IDS_DESCRIPTION "Indeo Video Interactive version 5 video codec" +} diff --git a/dlls/ir50_32/ir50_32.spec b/dlls/ir50_32/ir50_32.spec new file mode 100644 index 00000000000..e5c54ef9c56 --- /dev/null +++ b/dlls/ir50_32/ir50_32.spec @@ -0,0 +1 @@ +@ stdcall -private DriverProc(long long long long long) IV50_DriverProc diff --git a/dlls/ir50_32/ir50_private.h b/dlls/ir50_32/ir50_private.h new file mode 100644 index 00000000000..c022a8196d2 --- /dev/null +++ b/dlls/ir50_32/ir50_private.h @@ -0,0 +1,34 @@ +/* + * 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 + */ + +#ifndef __IR50_PRIVATE_H +#define __IR50_PRIVATE_H + +#include + +#define COBJMACROS +#include "mfapi.h" +#include "mferror.h" +#include "mfobjects.h" +#include "mfidl.h" +#include "mftransform.h" + +#define IDS_NAME 100 +#define IDS_DESCRIPTION 101 + +#endif /* __IR50_PRIVATE_H */ diff --git a/po/ar.po b/po/ar.po index 8ebe10b5a0a..5fdad68ed12 100644 --- a/po/ar.po +++ b/po/ar.po @@ -3732,6 +3732,18 @@ msgstr "زائد" msgid "High" msgstr "عالي" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "الفهرس" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "ترميز واين المرئي الأول" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "مقابض اللعب" diff --git a/po/ast.po b/po/ast.po index 9bb71695e12..359de815a1a 100644 --- a/po/ast.po +++ b/po/ast.po @@ -3626,6 +3626,14 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Joysticks" diff --git a/po/bg.po b/po/bg.po index 70219b75174..cac408aef44 100644 --- a/po/bg.po +++ b/po/bg.po @@ -3744,6 +3744,16 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +msgid "Indeo5" +msgstr "&Съдържание" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine MS-RLE видео кодек" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/ca.po b/po/ca.po index 1b66417b24d..00cb01b7ce2 100644 --- a/po/ca.po +++ b/po/ca.po @@ -3724,6 +3724,18 @@ msgstr "Elevat" msgid "High" msgstr "Alt" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Índex" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Còdec de vídeo Wine Video 1" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Palanques de control" diff --git a/po/cs.po b/po/cs.po index 704550231b3..b2c53f1d99a 100644 --- a/po/cs.po +++ b/po/cs.po @@ -3681,6 +3681,18 @@ msgstr "Zvýšená" msgid "High" msgstr "Vysoká" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Rejstřík" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Videokodek Wine Video 1" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Pákové ovladače" diff --git a/po/da.po b/po/da.po index 3fc57acec52..53aa5e9ac72 100644 --- a/po/da.po +++ b/po/da.po @@ -3763,6 +3763,18 @@ msgstr "Øget" msgid "High" msgstr "&Høj" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Indeks" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine Video 1 videokodeks" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/de.po b/po/de.po index 7a14c2e5a59..e970e9175df 100644 --- a/po/de.po +++ b/po/de.po @@ -3714,6 +3714,18 @@ msgstr "Erhöht" msgid "High" msgstr "Hoch" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Index" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine-Video-1-Videocodec" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Joysticks" diff --git a/po/el.po b/po/el.po index 11180a1b639..f4ca06d96fb 100644 --- a/po/el.po +++ b/po/el.po @@ -3661,6 +3661,14 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/en.po b/po/en.po index 246a8bb86f2..a5425b52486 100644 --- a/po/en.po +++ b/po/en.po @@ -3707,6 +3707,14 @@ msgstr "Increased" msgid "High" msgstr "High" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "Indeo5" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Indeo Video Interactive version 5 video codec" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Joysticks" diff --git a/po/en_US.po b/po/en_US.po index ad7b5b9773c..0735be69271 100644 --- a/po/en_US.po +++ b/po/en_US.po @@ -3707,6 +3707,14 @@ msgstr "Increased" msgid "High" msgstr "High" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "Indeo5" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Indeo Video Interactive version 5 video codec" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Joysticks" diff --git a/po/eo.po b/po/eo.po index ad39dac7839..f2ff3e010a1 100644 --- a/po/eo.po +++ b/po/eo.po @@ -3649,6 +3649,16 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Indekso" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/es.po b/po/es.po index 5bc81f068b8..4f363add9c4 100644 --- a/po/es.po +++ b/po/es.po @@ -3727,6 +3727,18 @@ msgstr "Aumentada" msgid "High" msgstr "Alta" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Índice" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Códec de vídeo Wine Video 1" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Comando de juego" diff --git a/po/fa.po b/po/fa.po index 2eafe19dd2e..2ea2a88163a 100644 --- a/po/fa.po +++ b/po/fa.po @@ -3692,6 +3692,14 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/fi.po b/po/fi.po index 905eb06a8cc..dc90507d60e 100644 --- a/po/fi.po +++ b/po/fi.po @@ -3701,6 +3701,18 @@ msgstr "Korotettu" msgid "High" msgstr "Korkea" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Sisällys" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Winen Video 1 -videokoodekki" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Joystickit" diff --git a/po/fr.po b/po/fr.po index 1c48ddc57a6..b00b7235c3e 100644 --- a/po/fr.po +++ b/po/fr.po @@ -3732,6 +3732,18 @@ msgstr "Augmentée" msgid "High" msgstr "Haute" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Index" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Codec vidéo Wine Video 1" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Joysticks" diff --git a/po/he.po b/po/he.po index 82df9538b43..58dcefcebd4 100644 --- a/po/he.po +++ b/po/he.po @@ -3727,6 +3727,18 @@ msgstr "מוגברת" msgid "High" msgstr "גבוהה" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "מפתח" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "מקודד הווידאו Video 1 של Wine" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/hi.po b/po/hi.po index 8ba49545d06..7663549c7a2 100644 --- a/po/hi.po +++ b/po/hi.po @@ -3625,6 +3625,14 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/hr.po b/po/hr.po index c9f87d7dc66..f04c70940fa 100644 --- a/po/hr.po +++ b/po/hr.po @@ -3737,6 +3737,18 @@ msgstr "Povećane" msgid "High" msgstr "Visoke" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Indeks" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine Video 1 video codec" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Joystici" diff --git a/po/hu.po b/po/hu.po index 05920d1c8e6..24c0eeecd35 100644 --- a/po/hu.po +++ b/po/hu.po @@ -3779,6 +3779,18 @@ msgstr "Megnövelt" msgid "High" msgstr "Magas" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "&Témakörök" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine Video 1 video kodek" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/it.po b/po/it.po index d0663b4d1d7..0449017ac23 100644 --- a/po/it.po +++ b/po/it.po @@ -3787,6 +3787,18 @@ msgstr "Aumentato" msgid "High" msgstr "Alta" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Indice" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Codec Video Wine Video 1" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/ja.po b/po/ja.po index 318f1a9029d..43bf855ddcd 100644 --- a/po/ja.po +++ b/po/ja.po @@ -3699,6 +3699,18 @@ msgstr "中高" msgid "High" msgstr "高" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "索引" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine ビデオ 1 ビデオコーデック" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "ジョイスティック" diff --git a/po/ko.po b/po/ko.po index a86e129750c..c1272917c81 100644 --- a/po/ko.po +++ b/po/ko.po @@ -3689,6 +3689,18 @@ msgstr "증가" msgid "High" msgstr "높음" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "인덱스" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine 비디오 1 비디오 코덱" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "조이스틱" diff --git a/po/lt.po b/po/lt.po index 8ede4161894..e4f347061d6 100644 --- a/po/lt.po +++ b/po/lt.po @@ -3710,6 +3710,18 @@ msgstr "Padidintos" msgid "High" msgstr "Aukštos" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Indeksas" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "„Wine“ Video 1 vaizdo kodekas" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Vairasvirtės" diff --git a/po/ml.po b/po/ml.po index 8df38715288..b612d8ef96e 100644 --- a/po/ml.po +++ b/po/ml.po @@ -3627,6 +3627,14 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/nb_NO.po b/po/nb_NO.po index f7ddeff7cae..da72e8d86f8 100644 --- a/po/nb_NO.po +++ b/po/nb_NO.po @@ -3715,6 +3715,18 @@ msgstr "Økt" msgid "High" msgstr "Høy" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Innhold" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine Video 1 videokodeks" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Styrespaker" diff --git a/po/nl.po b/po/nl.po index 92d601aaf92..b6df43c1006 100644 --- a/po/nl.po +++ b/po/nl.po @@ -3720,6 +3720,18 @@ msgstr "Verhoogd" msgid "High" msgstr "Hoog" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Index" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine Video 1 video codec" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Joysticks" diff --git a/po/or.po b/po/or.po index 3d8ffa37e1d..f380aee4c32 100644 --- a/po/or.po +++ b/po/or.po @@ -3625,6 +3625,14 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/pa.po b/po/pa.po index 8177d578666..02417daffe3 100644 --- a/po/pa.po +++ b/po/pa.po @@ -3625,6 +3625,14 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/pl.po b/po/pl.po index dcb4daaa0bd..9689ee339e0 100644 --- a/po/pl.po +++ b/po/pl.po @@ -3726,6 +3726,18 @@ msgstr "Wysoki" msgid "High" msgstr "Najwyższy" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Indeks" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Kodek Wine Video 1" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Joysticki" diff --git a/po/pt_BR.po b/po/pt_BR.po index 7593a85f8f3..15079a024e1 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -3722,6 +3722,18 @@ msgstr "Elevada" msgid "High" msgstr "Alta" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Índice" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Codec de vídeo Wine Video 1" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Controles" diff --git a/po/pt_PT.po b/po/pt_PT.po index be1ddf75488..55807dedfdd 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -3763,6 +3763,18 @@ msgstr "Aumentada" msgid "High" msgstr "Alta" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Índice" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "codec video Wine Video 1" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/rm.po b/po/rm.po index ffd9d51fc0e..eb130bb02c2 100644 --- a/po/rm.po +++ b/po/rm.po @@ -3653,6 +3653,15 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +msgid "Indeo5" +msgstr "&Cuntgn�" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/ro.po b/po/ro.po index 3947ea81074..d042fde175b 100644 --- a/po/ro.po +++ b/po/ro.po @@ -3720,6 +3720,18 @@ msgstr "Mărit" msgid "High" msgstr "Înalt" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Index" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Codecul video Wine Video 1" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Joystick-uri" diff --git a/po/ru.po b/po/ru.po index b7649c75b93..483e925f005 100644 --- a/po/ru.po +++ b/po/ru.po @@ -3725,6 +3725,18 @@ msgstr "Повышенный" msgid "High" msgstr "Высокий" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Указатель" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Видео кодек Wine Video 1" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Джойстики" diff --git a/po/si.po b/po/si.po index 2dea09241dc..395e7c3aa23 100644 --- a/po/si.po +++ b/po/si.po @@ -3652,6 +3652,18 @@ msgstr "" msgid "High" msgstr "වැඩි" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "දර්ශකය" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine වීඩියෝ 1 වීඩියෝ කොඩෙක්" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "නියාමක යටි" diff --git a/po/sk.po b/po/sk.po index 9caab53bd61..5a74fd54a99 100644 --- a/po/sk.po +++ b/po/sk.po @@ -3694,6 +3694,16 @@ msgstr "Zvýšené" msgid "High" msgstr "Vysoké" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Obsah" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/sl.po b/po/sl.po index 49fceead951..f2a06472681 100644 --- a/po/sl.po +++ b/po/sl.po @@ -3781,6 +3781,18 @@ msgstr "Povečano" msgid "High" msgstr "Visoka" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Kazalo" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine Video 1 video kodek" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/sr_RS@cyrillic.po b/po/sr_RS@cyrillic.po index 344a6396134..3f6bf0232b1 100644 --- a/po/sr_RS@cyrillic.po +++ b/po/sr_RS@cyrillic.po @@ -3762,6 +3762,17 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +msgid "Indeo5" +msgstr "&Попис" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine Video 1 видео кодек" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/sr_RS@latin.po b/po/sr_RS@latin.po index 4d5e65cb529..ea6b3aecae5 100644 --- a/po/sr_RS@latin.po +++ b/po/sr_RS@latin.po @@ -3846,6 +3846,18 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Index" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine Video 1 video kodek" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/sv.po b/po/sv.po index f02e762af01..c4ed81856f5 100644 --- a/po/sv.po +++ b/po/sv.po @@ -3742,6 +3742,18 @@ msgstr "Ökad" msgid "High" msgstr "Hög" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Index" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine Video 1 videokodek" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Joysticks" diff --git a/po/ta.po b/po/ta.po index 02563f2b63d..656f7a07b3a 100644 --- a/po/ta.po +++ b/po/ta.po @@ -3589,6 +3589,16 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine வீடியோ 1 வீடியோ கோடெக்" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/te.po b/po/te.po index 6128822c44c..8b69b08e201 100644 --- a/po/te.po +++ b/po/te.po @@ -3625,6 +3625,14 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/th.po b/po/th.po index 9d99df54f41..41de65e1d68 100644 --- a/po/th.po +++ b/po/th.po @@ -3680,6 +3680,14 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/tr.po b/po/tr.po index 73ee1e5eb83..0a6e29835f6 100644 --- a/po/tr.po +++ b/po/tr.po @@ -3716,6 +3716,18 @@ msgstr "Arttırılmış" msgid "High" msgstr "Yüksek" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "İçindekiler" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine Video 1 video çözücü" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Oyun Kolları" diff --git a/po/uk.po b/po/uk.po index de0466f9971..57307a18199 100644 --- a/po/uk.po +++ b/po/uk.po @@ -3713,6 +3713,18 @@ msgstr "Збільшені" msgid "High" msgstr "Високі" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "Вказівник" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Відео кодек Wine Video 1" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "Джойстик" diff --git a/po/wa.po b/po/wa.po index dab8e39300b..891cf5fb6e3 100644 --- a/po/wa.po +++ b/po/wa.po @@ -3688,6 +3688,15 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +msgid "Indeo5" +msgstr "Å&dvins" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/wine.pot b/po/wine.pot index dd9701759d8..5241318e3bf 100644 --- a/po/wine.pot +++ b/po/wine.pot @@ -3580,6 +3580,14 @@ msgstr "" msgid "High" msgstr "" +#: dlls/ir50_32/ir50_32.rc:28 +msgid "Indeo5" +msgstr "" + +#: dlls/ir50_32/ir50_32.rc:29 +msgid "Indeo Video Interactive version 5 video codec" +msgstr "" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index bad68026ffa..022dd3369a4 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -3657,6 +3657,18 @@ msgstr "较高" msgid "High" msgstr "高" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "索引" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine Video 1 视频编解码器" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "操纵杆" diff --git a/po/zh_TW.po b/po/zh_TW.po index a54460961b3..3d888ac2790 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -3663,6 +3663,18 @@ msgstr "稍高" msgid "High" msgstr "高" +#: dlls/ir50_32/ir50_32.rc:28 +#, fuzzy +#| msgid "Index" +msgid "Indeo5" +msgstr "索引" + +#: dlls/ir50_32/ir50_32.rc:29 +#, fuzzy +#| msgid "Wine Video 1 video codec" +msgid "Indeo Video Interactive version 5 video codec" +msgstr "Wine 視訊 1 視訊編碼解碼器" + #: dlls/joy.cpl/joy.rc:37 msgid "Joysticks" msgstr "搖桿" From db73e15eb595d80a4d0cba2a8cc173f327089855 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 1 Feb 2023 01:37:09 -0500 Subject: [PATCH 0383/1148] ir50_32: Implement IV50_GetInfo. (cherry picked from commit 05eef506149ca2892b5727d8a5a7eb9e71638b08) CW-Bug-Id: #21175 --- dlls/ir50_32/ir50.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/dlls/ir50_32/ir50.c b/dlls/ir50_32/ir50.c index 3019301742e..0e1c9b4d2e4 100644 --- a/dlls/ir50_32/ir50.c +++ b/dlls/ir50_32/ir50.c @@ -72,8 +72,23 @@ static LRESULT IV50_Decompress( IMFTransform *decoder, ICDECOMPRESS *icd, DWORD static LRESULT IV50_GetInfo( ICINFO *icinfo, DWORD dwSize ) { - FIXME("ICM_GETINFO %p %lu\n", icinfo, dwSize); - return ICERR_UNSUPPORTED; + TRACE("ICM_GETINFO %p %lu\n", icinfo, dwSize); + + if ( !icinfo ) return sizeof(ICINFO); + if ( dwSize < sizeof(ICINFO) ) return 0; + + icinfo->dwSize = sizeof(ICINFO); + icinfo->fccType = ICTYPE_VIDEO; + icinfo->fccHandler = IV50_MAGIC; + icinfo->dwFlags = 0; + icinfo->dwVersion = ICVERSION; + icinfo->dwVersionICM = ICVERSION; + + LoadStringW( IR50_32_hModule, IDS_NAME, icinfo->szName, ARRAY_SIZE(icinfo->szName) ); + LoadStringW( IR50_32_hModule, IDS_DESCRIPTION, icinfo->szDescription, ARRAY_SIZE(icinfo->szDescription) ); + /* msvfw32 will fill icinfo->szDriver for us */ + + return sizeof(ICINFO); } /*********************************************************************** From 2447ef9ba843e3e9adadcb486b7708626fbdc7e7 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 1 Feb 2023 01:38:30 -0500 Subject: [PATCH 0384/1148] ir50_32: Implement IV50_DecompressQuery. (cherry picked from commit b979f3b8aa9cc395a6dfc314fc4f48af4ef215c4) CW-Bug-Id: #21175 --- dlls/ir50_32/ir50.c | 47 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/dlls/ir50_32/ir50.c b/dlls/ir50_32/ir50.c index 0e1c9b4d2e4..92e4c8a49be 100644 --- a/dlls/ir50_32/ir50.c +++ b/dlls/ir50_32/ir50.c @@ -47,8 +47,51 @@ IV50_Open( const ICINFO *icinfo ) static LRESULT IV50_DecompressQuery( LPBITMAPINFO in, LPBITMAPINFO out ) { - FIXME("ICM_DECOMPRESS_QUERY %p %p\n", in, out); - return ICERR_UNSUPPORTED; + TRACE("ICM_DECOMPRESS_QUERY %p %p\n", in, out); + + TRACE("in->planes = %d\n", in->bmiHeader.biPlanes); + TRACE("in->bpp = %d\n", in->bmiHeader.biBitCount); + TRACE("in->height = %ld\n", in->bmiHeader.biHeight); + TRACE("in->width = %ld\n", in->bmiHeader.biWidth); + TRACE("in->compr = %#lx\n", in->bmiHeader.biCompression); + + if ( in->bmiHeader.biCompression != IV50_MAGIC ) + { + TRACE("can't do %#lx compression\n", in->bmiHeader.biCompression); + return ICERR_BADFORMAT; + } + + /* output must be same dimensions as input */ + if ( out ) + { + TRACE("out->planes = %d\n", out->bmiHeader.biPlanes); + TRACE("out->bpp = %d\n", out->bmiHeader.biBitCount); + TRACE("out->height = %ld\n", out->bmiHeader.biHeight); + TRACE("out->width = %ld\n", out->bmiHeader.biWidth); + TRACE("out->compr = %#lx\n", out->bmiHeader.biCompression); + + if ( out->bmiHeader.biCompression != BI_RGB ) + { + TRACE("incompatible compression requested\n"); + return ICERR_BADFORMAT; + } + + if ( out->bmiHeader.biBitCount != 32 && out->bmiHeader.biBitCount != 16 ) + { + TRACE("incompatible depth requested\n"); + return ICERR_BADFORMAT; + } + + if ( in->bmiHeader.biPlanes != out->bmiHeader.biPlanes || + in->bmiHeader.biHeight != abs(out->bmiHeader.biHeight) || + in->bmiHeader.biWidth != out->bmiHeader.biWidth ) + { + TRACE("incompatible output dimensions requested\n"); + return ICERR_BADFORMAT; + } + } + + return ICERR_OK; } static LRESULT From f5756fbcfe18fb6de75d00798d34ff4eb404901c Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 1 Feb 2023 01:42:13 -0500 Subject: [PATCH 0385/1148] ir50_32: Implement IV50_DecompressGetFormat. (cherry picked from commit e8cfcbb2f4d4cd04e3ea77979214c4fccf503c92) CW-Bug-Id: #21175 --- dlls/ir50_32/ir50.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/dlls/ir50_32/ir50.c b/dlls/ir50_32/ir50.c index 92e4c8a49be..7e494d50761 100644 --- a/dlls/ir50_32/ir50.c +++ b/dlls/ir50_32/ir50.c @@ -97,8 +97,28 @@ IV50_DecompressQuery( LPBITMAPINFO in, LPBITMAPINFO out ) static LRESULT IV50_DecompressGetFormat( LPBITMAPINFO in, LPBITMAPINFO out ) { - FIXME("ICM_DECOMPRESS_GETFORMAT %p %p\n", in, out); - return ICERR_UNSUPPORTED; + DWORD size; + + TRACE("ICM_DECOMPRESS_GETFORMAT %p %p\n", in, out); + + if ( !in ) + return ICERR_BADPARAM; + + if ( in->bmiHeader.biCompression != IV50_MAGIC ) + return ICERR_BADFORMAT; + + size = in->bmiHeader.biSize; + if ( out ) + { + memcpy( out, in, size ); + out->bmiHeader.biHeight = abs(in->bmiHeader.biHeight); + out->bmiHeader.biCompression = BI_RGB; + out->bmiHeader.biBitCount = 32; + out->bmiHeader.biSizeImage = out->bmiHeader.biWidth * out->bmiHeader.biHeight * 4; + return ICERR_OK; + } + + return size; } static LRESULT IV50_DecompressBegin( IMFTransform *decoder, LPBITMAPINFO in, LPBITMAPINFO out ) From d40d215370d03b8621e5b13512f7a1f6f3187932 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 3 Feb 2023 02:16:34 -0500 Subject: [PATCH 0386/1148] ir50_32: Implement open and close. (cherry picked from commit 3dd09ed8f9ecf99439a372b6d0be076cef8aaef3) CW-Bug-Id: #21175 --- dlls/ir50_32/Makefile.in | 3 ++- dlls/ir50_32/ir50.c | 19 ++++++++++++++++--- dlls/ir50_32/ir50_private.h | 2 ++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/dlls/ir50_32/Makefile.in b/dlls/ir50_32/Makefile.in index 7bac0ccc9ca..2db9c8076c9 100644 --- a/dlls/ir50_32/Makefile.in +++ b/dlls/ir50_32/Makefile.in @@ -1,5 +1,6 @@ MODULE = ir50_32.dll -IMPORTS = user32 +IMPORTS = user32 mfplat mfuuid +DELAYIMPORTS = winegstreamer C_SRCS = \ ir50.c diff --git a/dlls/ir50_32/ir50.c b/dlls/ir50_32/ir50.c index 7e494d50761..428108f09ba 100644 --- a/dlls/ir50_32/ir50.c +++ b/dlls/ir50_32/ir50.c @@ -35,13 +35,23 @@ WINE_DEFAULT_DEBUG_CHANNEL(ir50_32); static HINSTANCE IR50_32_hModule; #define IV50_MAGIC mmioFOURCC('I','V','5','0') +#define compare_fourcc(fcc1, fcc2) (((fcc1)^(fcc2))&~0x20202020) static LRESULT IV50_Open( const ICINFO *icinfo ) { - FIXME("DRV_OPEN %p\n", icinfo); - return 0; + IMFTransform *decoder = NULL; + + TRACE("DRV_OPEN %p\n", icinfo); + + if ( icinfo && compare_fourcc( icinfo->fccType, ICTYPE_VIDEO ) ) + return 0; + + if ( FAILED(winegstreamer_create_video_decoder( &decoder )) ) + return 0; + + return (LRESULT)decoder; } static LRESULT @@ -177,7 +187,10 @@ LRESULT WINAPI IV50_DriverProc( DWORD_PTR dwDriverId, HDRVR hdrvr, UINT msg, break; case DRV_CLOSE: - FIXME("DRV_CLOSE\n"); + TRACE("DRV_CLOSE\n"); + if ( decoder ) + IMFTransform_Release( decoder ); + r = 1; break; case DRV_ENABLE: diff --git a/dlls/ir50_32/ir50_private.h b/dlls/ir50_32/ir50_private.h index c022a8196d2..c0b96bc17e4 100644 --- a/dlls/ir50_32/ir50_private.h +++ b/dlls/ir50_32/ir50_private.h @@ -31,4 +31,6 @@ #define IDS_NAME 100 #define IDS_DESCRIPTION 101 +HRESULT WINAPI winegstreamer_create_video_decoder(IMFTransform **out); + #endif /* __IR50_PRIVATE_H */ From 139cf070df68466d91d87d77f4753aadf17628bc Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 8 Feb 2023 02:04:02 -0500 Subject: [PATCH 0387/1148] ir50_32: Implement decompression. (cherry picked from commit 4eac23121d17e5b5cca61516429398aac1ed8e5a) CW-Bug-Id: #21175 --- dlls/ir50_32/ir50.c | 149 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 145 insertions(+), 4 deletions(-) diff --git a/dlls/ir50_32/ir50.c b/dlls/ir50_32/ir50.c index 428108f09ba..87807aba25f 100644 --- a/dlls/ir50_32/ir50.c +++ b/dlls/ir50_32/ir50.c @@ -26,6 +26,7 @@ #include "winuser.h" #include "commdlg.h" #include "vfw.h" +#include "initguid.h" #include "ir50_private.h" #include "wine/debug.h" @@ -37,6 +38,14 @@ static HINSTANCE IR50_32_hModule; #define IV50_MAGIC mmioFOURCC('I','V','5','0') #define compare_fourcc(fcc1, fcc2) (((fcc1)^(fcc2))&~0x20202020) +DEFINE_MEDIATYPE_GUID(MFVideoFormat_IV50, MAKEFOURCC('I','V','5','0')); + +static inline UINT64 +make_uint64( UINT32 high, UINT32 low ) +{ + return ((UINT64)high << 32) | low; +} + static LRESULT IV50_Open( const ICINFO *icinfo ) @@ -133,14 +142,146 @@ IV50_DecompressGetFormat( LPBITMAPINFO in, LPBITMAPINFO out ) static LRESULT IV50_DecompressBegin( IMFTransform *decoder, LPBITMAPINFO in, LPBITMAPINFO out ) { - FIXME("ICM_DECOMPRESS_BEGIN %p %p %p\n", decoder, in, out); - return ICERR_UNSUPPORTED; + IMFMediaType *input_type, *output_type; + const GUID *output_subtype; + LRESULT r = ICERR_INTERNAL; + + TRACE("ICM_DECOMPRESS_BEGIN %p %p %p\n", decoder, in, out); + + if ( !decoder ) + return ICERR_BADPARAM; + + if ( out->bmiHeader.biBitCount == 32 ) + output_subtype = &MFVideoFormat_RGB32; + else if ( out->bmiHeader.biBitCount == 16 ) + output_subtype = &MFVideoFormat_RGB555; + else + return ICERR_BADFORMAT; + + if ( FAILED(MFCreateMediaType( &input_type )) ) + return ICERR_INTERNAL; + + if ( FAILED(MFCreateMediaType( &output_type )) ) + { + IMFMediaType_Release( input_type ); + return ICERR_INTERNAL; + } + + if ( FAILED(IMFMediaType_SetGUID( input_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video )) || + FAILED(IMFMediaType_SetGUID( input_type, &MF_MT_SUBTYPE, &MFVideoFormat_IV50 )) ) + goto done; + if ( FAILED(IMFMediaType_SetUINT64( + input_type, &MF_MT_FRAME_SIZE, + make_uint64( in->bmiHeader.biWidth, in->bmiHeader.biHeight ) )) ) + goto done; + + if ( FAILED(IMFMediaType_SetGUID( output_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video )) || + FAILED(IMFMediaType_SetGUID( output_type, &MF_MT_SUBTYPE, output_subtype )) ) + goto done; + if ( FAILED(IMFMediaType_SetUINT64( + output_type, &MF_MT_FRAME_SIZE, + make_uint64( out->bmiHeader.biWidth, abs(out->bmiHeader.biHeight) ) )) ) + goto done; + + if ( FAILED(IMFTransform_SetInputType( decoder, 0, input_type, 0 )) || + FAILED(IMFTransform_SetOutputType( decoder, 0, output_type, 0 )) ) + goto done; + + r = ICERR_OK; + +done: + IMFMediaType_Release( input_type ); + IMFMediaType_Release( output_type ); + return r; } static LRESULT IV50_Decompress( IMFTransform *decoder, ICDECOMPRESS *icd, DWORD size ) { - FIXME("ICM_DECOMPRESS %p %p %lu\n", decoder, icd, size); - return ICERR_UNSUPPORTED; + IMFSample *in_sample = NULL, *out_sample = NULL; + IMFMediaBuffer *in_buf = NULL, *out_buf = NULL; + MFT_OUTPUT_DATA_BUFFER mft_buf; + DWORD mft_status; + BYTE *data; + HRESULT hr; + LRESULT r = ICERR_INTERNAL; + + TRACE("ICM_DECOMPRESS %p %p %lu\n", decoder, icd, size); + + if ( FAILED(MFCreateSample( &in_sample )) ) + return ICERR_INTERNAL; + + if ( FAILED(MFCreateMemoryBuffer( icd->lpbiInput->biSizeImage, &in_buf )) ) + goto done; + + if ( FAILED(IMFSample_AddBuffer( in_sample, in_buf )) ) + goto done; + + if ( FAILED(MFCreateSample( &out_sample )) ) + goto done; + + if ( FAILED(MFCreateMemoryBuffer( icd->lpbiOutput->biSizeImage, &out_buf )) ) + goto done; + + if ( FAILED(IMFSample_AddBuffer( out_sample, out_buf )) ) + goto done; + + if ( FAILED(IMFMediaBuffer_Lock( in_buf, &data, NULL, NULL ))) + goto done; + + memcpy( data, icd->lpInput, icd->lpbiInput->biSizeImage ); + + if ( FAILED(IMFMediaBuffer_Unlock( in_buf )) ) + goto done; + + if ( FAILED(IMFMediaBuffer_SetCurrentLength( in_buf, icd->lpbiInput->biSizeImage )) ) + goto done; + + if ( FAILED(IMFTransform_ProcessInput( decoder, 0, in_sample, 0 )) ) + goto done; + + memset( &mft_buf, 0, sizeof(mft_buf) ); + mft_buf.pSample = out_sample; + + hr = IMFTransform_ProcessOutput( decoder, 0, 1, &mft_buf, &mft_status ); + if ( SUCCEEDED(hr) && (mft_status & MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE) ) + hr = IMFTransform_ProcessOutput( decoder, 0, 1, &mft_buf, &mft_status ); + + if ( SUCCEEDED(hr) ) + { + 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); + + if ( FAILED(IMFMediaBuffer_Lock( out_buf, &data, NULL, NULL ))) + goto done; + + MFCopyImage( output_start, out_stride, data, data_stride, width, height ); + + IMFMediaBuffer_Unlock( out_buf ); + r = ICERR_OK; + } + else if ( hr == MF_E_TRANSFORM_NEED_MORE_INPUT ) + { + TRACE("no output received.\n"); + r = ICERR_OK; + } + +done: + if ( in_buf ) + IMFMediaBuffer_Release( in_buf ); + if ( in_sample ) + IMFSample_Release( in_sample ); + if ( out_buf ) + IMFMediaBuffer_Release( out_buf ); + if ( out_sample ) + IMFSample_Release( out_sample ); + + return r; } static LRESULT IV50_GetInfo( ICINFO *icinfo, DWORD dwSize ) From aca44a75dab444ca6e5b2f125749211bd52dc00d Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 8 Feb 2023 02:46:23 -0500 Subject: [PATCH 0388/1148] wine.inf: Enable ir50_32 video codec. (cherry picked from commit d83e03f638094cbac14307b0eabc9b71025dbaae) CW-Bug-Id: #21175 --- loader/wine.inf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 83397bbb9b9..6273413d92e 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2339,7 +2339,7 @@ system.ini, drivers32,,"msacm.msgsm610=msgsm32.acm" system.ini, drivers32,,"vidc.mrle=msrle32.dll" system.ini, drivers32,,"vidc.msvc=msvidc32.dll" system.ini, drivers32,,"vidc.cvid=iccvid.dll" -system.ini, drivers32,,"; vidc.IV50=ir50_32.dll" +system.ini, drivers32,,"vidc.IV50=ir50_32.dll" system.ini, drivers32,,"; vidc.IV31=ir32_32.dll" system.ini, drivers32,,"; vidc.IV32=ir32_32.dll" From 00d13dd964afc220421b8a332b25ebcdccc1f3a1 Mon Sep 17 00:00:00 2001 From: Anton Baskanov Date: Fri, 2 Jun 2023 16:37:42 +0200 Subject: [PATCH 0389/1148] 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 05d56819a573455d4b3caebbe3630d7b0b7681a1 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 0390/1148] 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 95eac4a71601e405976e85d1297730d3dea24b69 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 0391/1148] 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 ff374a7e986a2dc6a219764008a1b93376a0a315 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 0392/1148] 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 a03b70be62e5ae5db9f0483292e0c83d95b63c1b 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 0393/1148] 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 56578eb660c786ce14bd2deb7d2eb6180d068c41 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 0394/1148] 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 e5c41d3ef82ce53eedf02bf8cb3cb0a02504a86d 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 0395/1148] 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 4742e916306e52c3b4cfba2ad53fe5582765d1c6 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 0396/1148] 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 c9550922b08cf0de236f8f83586ab3edac24a2bf 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 0397/1148] 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 eb928059a7a..ee612a4f74f 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1918,7 +1918,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 cb797cba7abb15246ba8b24791c26ec45b57fbae 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 0398/1148] 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 1ee413c5d9f6b97b390e966378ea8314d960ef4b 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 0399/1148] 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 9785379c71333de1e9d620e283a1a6129045b0f9 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 0400/1148] 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 27439540977cfb79b9c2f7152c35a6ac21fc0613 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 0401/1148] 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 cd35124a4203ce5fce5bb11a887d98ec47a97122 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 31 Mar 2023 19:16:59 -0600 Subject: [PATCH 0402/1148] 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 270f32f77c79148bc73cd873373ee88fd0e3cdca Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 31 Mar 2023 20:08:19 -0600 Subject: [PATCH 0403/1148] 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 12475199ed86d8ec104505da73758a6f244292b3 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 4 Apr 2023 18:14:48 -0600 Subject: [PATCH 0404/1148] 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 14a4c867955906dae5df3b5932928b3fa2c06ab8 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 0405/1148] 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 e0a96cafa3ec31e50953e08c1b95c5e5b168ce4f 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 0406/1148] 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 af8b197a3f15bb5ba3d13be6a6307bc446a52e00 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 0407/1148] 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 d424fd953fcf40de472104b0153fca42c1e9ed48 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 28 Feb 2023 12:07:55 -0600 Subject: [PATCH 0408/1148] 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 2db7f53fccfce9af8c6817a46b47f258655f6a84 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 0409/1148] 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 ee612a4f74f..eb928059a7a 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1918,6 +1918,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 b8e9d6363aa48227625c8cb2d96c6d894a55a5c5 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 0410/1148] 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 eb928059a7a..959a2141f2c 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1919,6 +1919,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 94659261ac3a6c558e57bc483c92cefa3d05fed0 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 0411/1148] 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 96963fce1fb75ced79fc75f8499e9fdda13aa2eb 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 0412/1148] 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 8086903fa7a1215452cf8a12a673e025af72dc41 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 0413/1148] 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 34fc7820c6dc1908a0d4286807087e2d934719bf Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:18:25 -0600 Subject: [PATCH 0414/1148] 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 99a326a950b1f4bc8eb2b8fb49cbf7facb1ac367 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:47:47 -0600 Subject: [PATCH 0415/1148] 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 4b9875341a29e13653f5cd0e8f34f20f49d80e6f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:48:48 -0600 Subject: [PATCH 0416/1148] 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 e27e2d4a42e3d30bf361f30ab8c6b836b5f1d3e8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Jan 2023 14:50:38 -0600 Subject: [PATCH 0417/1148] 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 b164066f09f52a50f4a9bbceb9921b835972674a Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 2 Jun 2023 17:30:13 +0800 Subject: [PATCH 0418/1148] 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 8075bbf8ece5d6319469acdf4496b840cc737adb 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 0419/1148] 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 eb2aaad42904deb56f7e684fa605cdfff8bb32b9 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 0420/1148] 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 40bc3433d5aa684fbd2b248d974279200b31cd0e 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 0421/1148] 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 e31e5ed296cc2c1a225cfc76d08e4d8cc9524094 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 0422/1148] 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 1aa605825f503654da06bb92c2a525f6f774f559 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 0423/1148] 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 bbfd22bf7bf795d8c33e0c97d77c543b3171dd66 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 0424/1148] 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 1b579cb4ddfefe3291cd07cb52300ff9db27398e 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 0425/1148] 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 07c090fc91951385410ebc9e7cff941f34dec106 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 0426/1148] 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 bdc73a41952cc1d757673506e54b2b19d2190931 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 0427/1148] 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 8e278ca53fa69b475177d489b737824008b5c23f 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 0428/1148] 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 09fb11c2fad66f05d7bd37d12de0612d2d62caa9 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 0429/1148] 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 2e297ed3e05..3bbe4e20cf5 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3722,8 +3722,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 bd1ea9ad197b5e1a4eef13fbfc5034430e10c372 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 0430/1148] 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 c41d03291fd89aefa40d18c6288a394148d8ef50 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 0431/1148] 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 bc6666b04d2ab9220971ced4a08bceeeb7d0d5f6 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 0432/1148] 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 60cc101c5fe3be981cd3ef314c36fe5b8803f36e 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 0433/1148] 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 bd700ab56d44343c142879312f39c201f5469905 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 0434/1148] 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 85aa45465e6cd38477b03d25ed3bde90e383e9ec 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 0435/1148] 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 125397c2408e1d0458bbc7fb4be56401944207f6 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 0436/1148] 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 81cc4f2d6b49b88f60104147c5b68eb229be5a0c Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Tue, 28 Feb 2023 09:49:36 -0700 Subject: [PATCH 0437/1148] 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 3bbe4e20cf5..849b7fe0596 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2501,7 +2501,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) { @@ -2512,7 +2512,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 76a573efbb5cb6dd294739fcb5ad86cc1e4543ab 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 0438/1148] 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 5229412d7e40aadad01649a3b3475ebdc8591dee 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 0439/1148] 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 fdb37f132b376835c3863425681b4af8ab619ddc 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 0440/1148] 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 849b7fe0596..ef9521b2457 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2302,9 +2302,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; @@ -2344,7 +2344,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 e66585384e2..d5bbb5f9676 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 beae7a40de21de2f9153728a7463922dcd9ef894 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 0441/1148] 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 4c1cae8d4770e8da136a8d44c157c3798cd1783f 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 0442/1148] 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 58736bd24041645efdfda06f52083096863254e6 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 0443/1148] 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 b791410c37ee23302a260f36b03b8b2808aee26f 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 0444/1148] 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 1733c8fb3046efcdd8b644965025cc386c4666b0 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 0445/1148] 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 d5bbb5f9676..f320805c752 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 82ed8cf94e677fabd901d10f14da9bb60b2a59e5 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 0446/1148] 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 7cfdbeb5c8889d57252e24d8f3590b4db39f830f 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 0447/1148] 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 ef9521b2457..38a55c47a08 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2285,13 +2285,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 ); @@ -2323,7 +2323,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 ) { @@ -2338,11 +2341,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 29e352706e40060adcf3fbeeb44b2f475bd3cff7 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 0448/1148] 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 470a69254a2c9e5aa45667f4b31fb909d944c247 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 0449/1148] 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 2c422270e0b08fd52d25356e516b4dd2a3afae03 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 0450/1148] 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 56d68719c06bab9123c5b640832913a364ea718f 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 0451/1148] 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 ff0c9129733785256845e9f41dcf6e1fe1cfa97d 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 0452/1148] 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 5c8a462913baee7e5d525cbd8d50b66224d26197 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 0453/1148] 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 39f088c58c19782c6b5a1e4a8bff33bcdb4700b0 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 0454/1148] 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 bea39fb432bc968130a6ddd85ecf7f2b459f044f 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 0455/1148] 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 9b4c20eeba16dadb6f202cade2c66ec2b52924a8 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 0456/1148] 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 a03d45a1fcf9cd503780e462fae84bde986d8c65 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 0457/1148] 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 75bdd3e0305a7077168ae1cab7def9961782f468 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 0458/1148] 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 f6da04964e9f04cfedbc8e92fca9537454ebab9d 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 0459/1148] 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 ec6c171ff7a48f796403282fc298cbb8692792ec 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 0460/1148] 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 a6c9fcd76cc..38562fe47f4 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2726,6 +2726,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 29bb956ed545fc90095d48e8b43eb1e3ee80e3db 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 0461/1148] 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 75cc6a47ebe6d8e9e28e5ccf5d5c5bbb79ea2c26 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 0462/1148] 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 38a55c47a08..8edf5829add 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3264,7 +3264,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 f899a617c88dc67d574c287147810a443ec7dd55 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 0463/1148] 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 0e881d71544fb4923bcfd95027d9734aced06f28 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 0464/1148] 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 f320805c752..dd7d3eed2ad 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 8284c4dd8b9d6b319a7ba397d2d5e62b4bfb1de7 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 0465/1148] 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 a6376ddf0f444db2dcaf32590c4ecdd1d807080f 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 0466/1148] 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 f7ce546515dd07a55fb670e68b9d9fbcc9f60295 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 0467/1148] 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 a400dfc634d774929945c2e01d0c7e6572357507 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 0468/1148] 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 bbc4cf0a8c6269cf7933ca1f8ded0cbc5c4cfe78 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 0469/1148] 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 ab572eda14a87efd2d9d8203261062cad314f15a 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 0470/1148] 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 803e889dcdd5e0569eaf04e4570ede4819fb8b3c 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 0471/1148] 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 53dbf5a2c09cddced47a0ec121a8d9f627a1d78c 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 0472/1148] 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 dd8df863956840ad9fb48eaada9bc55f0e40cd1c 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 0473/1148] 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 8edf5829add..0e7821e8c1a 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_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 89e5d3cdd2607b144d087ca7e05a22f3777d2cd6 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 0474/1148] 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 38562fe47f4..52f1293a388 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -4127,13 +4127,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[] = @@ -4142,6 +4176,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 ); @@ -4201,6 +4236,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 26f467a616dea057bb79837f95dcf1b31af95c95 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 0475/1148] 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 a30f20f38a32fdcd495522a8feeb47fa5f2a4cf3 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 0476/1148] 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 016d341fd06d0438beb468015ba7339a6113d7df 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 0477/1148] 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 f873b820a08aeff12cea227815105ca04c2a6d5f 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 0478/1148] 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 52f1293a388..13a11581e79 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2732,6 +2732,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; @@ -4270,6 +4272,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 0e7821e8c1a..1408f1c1ab2 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_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 b98007d95e10070bbcb1838ba53fdd68cd6af249 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 0479/1148] 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 49f0fbb0b1d567fbc2cb489f7ac1ba824a18b644 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 0480/1148] 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 5aaca49a7c7086c617c627bcaa1c0a8fa1eb6779 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 0481/1148] 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 2a84b18ed09b81628959ad7507bf4044d9b7d0ce 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 0482/1148] 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 dd7d3eed2ad..60c3779727b 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 47a636df29f30ddcc0912248950fc83fe27ebe77 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 0483/1148] 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 59c9d7a03cc5c0cad60b4d0e32979bc85e0b5392 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 0484/1148] 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 42cda654d5d7a3527ea510f9f19f2b2eb0f38f7c 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 0485/1148] 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 1408f1c1ab2..4121b558ca9 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2079,8 +2079,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 ); } @@ -3711,27 +3709,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 1b1962296714a34aab1d9d7bd8d173304d4bc02c 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 0486/1148] 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 820543f39bc753aa36f796a9a4caf5ad9f3e930d 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 0487/1148] 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 8fa77ae791dd581fa9383a9f2c241fb4c4f184a0 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 0488/1148] 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 238d9059e46b8cbd4f25c4ce1cff026f8cfb72a5 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 0489/1148] 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 2da5518bb0a084a54efc9e4f2d247afc426bd4b8 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 0490/1148] 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 0895c6e1940cd6d204b7f91cbb0679f7ca3d9b39 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 0491/1148] 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 cc8a695ba0771c0246f63391a6ec3b9c0f81d8ce 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 0492/1148] 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 13a11581e79..42febfa487c 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2733,7 +2733,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 c6f1169a5eded98d719a2fbf6a4991c1ec9132ec 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 0493/1148] 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 42febfa487c..aa6aca43c7b 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2733,7 +2733,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 d99d16874446b6cca4b5902b992c2fbde51d9c81 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 0494/1148] 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 e5a4f85bb731524c31c297a4a7ae76b55516a98d 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 0495/1148] 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 0960e8a04a821d0af9a2e8be830be577aa82a44a 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 0496/1148] 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 c875bfb338be5f5c5f08c3d692c7fbfb7a0af867 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 0497/1148] 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 1ebe436112c340d923236dd7a9f81dc6641af9f9 Mon Sep 17 00:00:00 2001 From: Alexey Prokhin Date: Wed, 17 Oct 2018 19:55:27 +0300 Subject: [PATCH 0498/1148] 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 8fca1060ad4a12a1e8ab7acd9bc97e847b315c45 Mon Sep 17 00:00:00 2001 From: Alexey Prokhin Date: Sat, 20 Oct 2018 18:07:12 +0300 Subject: [PATCH 0499/1148] 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 039488fc9b621d7124445f025644ceb98a0407b2 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 0500/1148] 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 6a72d8ad772978a5027aa7b85f6ab73571ae5b59 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 0501/1148] 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 14644fa2c51b5f2d8a381cac334e0b7f3a9234e3 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 0502/1148] 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 74428e24c163430141f71ddea1a04680b495235b 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 0503/1148] 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 c8d77ecf32952cb5660c0dfcd4eaa2340eb87787 Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Mon, 15 Mar 2021 12:01:25 -0500 Subject: [PATCH 0504/1148] 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 1a1f4c1e17c37f8a30b3e04201c7866ad8c1f73d Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 8 Jun 2021 16:01:54 +0300 Subject: [PATCH 0505/1148] 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 baf88676a64e18c86af336a933224a60ea76ecd9 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 0506/1148] 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 aa6aca43c7b..6630250b667 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2075,7 +2075,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 50bf308956f045a6d65f97191cea89583c3b4fb0 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 0507/1148] 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 ae5199f37323a3daafa335edb797de332cb3872e 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 0508/1148] 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 8a2a544f43349ff7a301cb3579b7b6fb71a9d183 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 0509/1148] 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 7fdf3744ebad9d350ff246d76be5ef91c9e5cbe3 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 0510/1148] 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 adc3987b031..f2abaf8b5d5 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 5e5d20b8364..bdf7325f669 100644 --- a/dlls/windows.media.speech/synthesizer.c +++ b/dlls/windows.media.speech/synthesizer.c @@ -929,7 +929,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); } @@ -939,10 +939,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); } @@ -952,7 +952,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 217b56f56d7b8e1a62b0b252739d015eb9057a43 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 0511/1148] 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 f234dc9d643..767f559a1fd 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1886,9 +1886,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); @@ -1900,40 +1898,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 65726d883c6f8a21f235b025815aa9b40b34a4c6 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 0512/1148] 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 767f559a1fd..f425e32f170 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1885,6 +1885,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); @@ -1916,7 +1925,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 42641bb38e3e6b04b855801067a9f27b184a6818 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 0513/1148] 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 f425e32f170..c8bbf8d9d54 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1743,6 +1743,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; @@ -1841,6 +1842,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, @@ -1881,6 +1887,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. */ @@ -1891,9 +1902,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); @@ -1941,6 +1963,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 4a7e7e353c8e7c0870f483ffda0c3be89c72e536 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 0514/1148] 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 c8bbf8d9d54..9f150d68433 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" @@ -1865,6 +1866,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); @@ -1908,6 +1914,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); @@ -1947,7 +1964,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"); @@ -1968,6 +1985,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 79d158df1bada2e266246b4e646af9bcfa9b753e 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 0515/1148] 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 f2abaf8b5d5..d03fe0e773e 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 9f150d68433..9ef3f56c3d5 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1868,8 +1868,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); @@ -1987,8 +1987,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 9456eff5f1f6553cd6064f2a6fff096fa2a79ef0 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 0516/1148] 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 9ef3f56c3d5..adfdf690ee2 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1911,8 +1911,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); @@ -1968,7 +1968,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); @@ -1990,6 +1990,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 733d357baf29132e2a53c49c5e0c685a9003d734 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 0517/1148] 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 adfdf690ee2..b767fd90b11 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1922,11 +1922,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 c0fe26cdef844395f919750a8d504054abe7f207 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 0518/1148] 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 e399d34441f950d3038dd6270d9461f2c1f83f56 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 0519/1148] 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 b767fd90b11..fd013a1016e 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1845,8 +1845,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); @@ -1895,8 +1895,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. @@ -1910,9 +1910,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); @@ -1930,8 +1930,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); @@ -1982,8 +1982,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); @@ -2003,9 +2003,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); @@ -2014,8 +2014,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); @@ -2024,10 +2024,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 03fba3bb5cecfcbb28a5b42e7a1c8f8456f3e980 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 0520/1148] 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 c909a8e111b8ad289d5cf7fa9bf37f01447de129 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 0521/1148] 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 e4b85800b4b..c58fced1539 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]) @@ -488,7 +489,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() @@ -1793,6 +1795,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 2778fd4b80e6b80a352e1fea9e01721bca2b0bee 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 0522/1148] 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 d03fe0e773e..4b6a8327d6e 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 432a14f6b7f261fe35cbcfd89d7ad8f2314eaf2b 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 0523/1148] 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 fd013a1016e..558b82ceca2 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 @@ -1091,7 +1090,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 4e42f3745435cb960788192f9383f51de5b86f5c 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 0524/1148] 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 558b82ceca2..f941edee0df 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1316,7 +1316,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) { @@ -1535,7 +1535,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: @@ -1775,12 +1775,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 18f29b9643c497eee13b67c2c904767d68128ac4 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 0525/1148] 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 4b6a8327d6e..60d09c9f7d1 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 d7330b30cb5126b5510a2bcbbb89bde0b97a99b7 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 0526/1148] 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 a7076e9b1d99e66acec228726be5907697ef5354 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 0527/1148] 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 2ae20fcba7070af79281db6465c653950fcd6e90 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 0528/1148] 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 257d6e5a657e898d99b82f20ad342dfe83f68e72 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 0529/1148] 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 d1ce6febc43907701d48a5cae8e630e6e1bc33f1 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 0530/1148] 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 8ccbf1747bfa396e2209aff16cc77192995fbad4 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 0531/1148] 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 75f4455db28e48b7c0ac3a12f270ff3b1fad7d58 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 0532/1148] 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 c656ad9eb04e7808052faad5fa1774d33eaf58d4 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 0533/1148] 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 f941edee0df..1b9198ac10a 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; } @@ -1723,7 +1739,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; @@ -1897,6 +1913,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 4bcf98229506c8a705528d037f123b53239fca68 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 0534/1148] 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 377a34110d47ddb0b1bb028d29a2dc7757265894 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 0535/1148] 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 84ab296a4214223ffc00fab1cab488baecad0c15 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 0536/1148] 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 8aa0257cf34d55a5d82be7671581bd94197e84cb Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Thu, 9 Mar 2023 11:28:41 -0300 Subject: [PATCH 0537/1148] 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 03a6aa2b49c2f92199c4aa61a59ebeebaccf461f Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Thu, 9 Mar 2023 11:50:30 -0300 Subject: [PATCH 0538/1148] 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 fb4b9e437aaf5caf84767b913bf6bd5991fd6c55 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Fri, 7 Apr 2023 14:07:26 -0300 Subject: [PATCH 0539/1148] 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 f08ca377b21da906e0f778c4cf87be4931da086b Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Tue, 2 May 2023 18:32:18 -0300 Subject: [PATCH 0540/1148] 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 153911dbb44f49c8200392ea0246ac3adc5bce2e Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Fri, 9 Jun 2023 16:59:27 -0300 Subject: [PATCH 0541/1148] 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 0e26373ad406238b7e831f6cd68dfd399ca4d414 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sat, 22 Jul 2023 14:47:13 -0400 Subject: [PATCH 0542/1148] 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 3519eb67fa2f397d3f605664ceb8bc99308cea5b Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 25 Apr 2023 19:15:59 -0400 Subject: [PATCH 0543/1148] 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 07995c5c07bb900351df31dc3817843e88d7718b Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 25 Apr 2023 19:17:50 -0400 Subject: [PATCH 0544/1148] 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 0aed394c5bcf57c5552b3ce5a9d4d75cae80cb05 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 00:11:29 -0400 Subject: [PATCH 0545/1148] 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 213dd4193e4e275d2e7797aef76e1afe53bbcdb9 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 12:34:13 -0400 Subject: [PATCH 0546/1148] 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 dbeddd320096f0e36e479f61f7efbd87062b2dcd Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 00:17:23 -0400 Subject: [PATCH 0547/1148] 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 6c6b5f461432718a613d4f8d25d262f45af031b7 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 00:23:35 -0400 Subject: [PATCH 0548/1148] 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 6f155a952deb7d869b64ac69d09c6a3ca83c72cd Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 00:25:03 -0400 Subject: [PATCH 0549/1148] 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 c2571261a1fe95bf63891c9968766c076beaa83c Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 May 2023 00:32:24 -0400 Subject: [PATCH 0550/1148] 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 65e7107f55f722bc4266b5a4716a4d727d869ef4 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 25 May 2023 23:35:28 -0400 Subject: [PATCH 0551/1148] 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 c681bd5d7af54dc674d97ed301e1a6e0e20fbbc8 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 26 May 2023 00:09:45 -0400 Subject: [PATCH 0552/1148] 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 11d0946d24be67631256221eb2cddf6714da9dc5 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 26 May 2023 00:35:48 -0400 Subject: [PATCH 0553/1148] 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 46ca883784d66faaebf8b4a3549669904b891434 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 16 May 2023 18:55:31 -0400 Subject: [PATCH 0554/1148] 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 b3a4aca6969ee6e80a4f36f0e6be22097613e981 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 26 May 2023 23:56:47 -0400 Subject: [PATCH 0555/1148] 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 dd5f017fb99aab2f90a2706b992bb8e47746f179 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Mon, 15 May 2023 23:06:47 -0400 Subject: [PATCH 0556/1148] 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 3a45cccd7c7df0bd54a300ebdc044094004c0b0a Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 26 May 2023 14:31:51 -0400 Subject: [PATCH 0557/1148] 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 3f5a7e77e4ea1172e439da454b03f55401014ce1 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sat, 27 May 2023 16:26:34 -0400 Subject: [PATCH 0558/1148] 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 45e67347ecbfa874249411a13155465b540bd89a Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sat, 27 May 2023 17:20:03 -0400 Subject: [PATCH 0559/1148] 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 c94fe4be0c5e54f3d6133134397b01e7556b19e1 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 30 May 2023 23:29:22 -0400 Subject: [PATCH 0560/1148] 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 fae56426a41d7a992426304c398df2041ebd8666 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 30 May 2023 23:58:32 -0400 Subject: [PATCH 0561/1148] 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 390f14f4a08d54bc20666a85b582dd3286e04574 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 31 May 2023 00:09:50 -0400 Subject: [PATCH 0562/1148] 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 666fdd0b9443cc9bca696c18d15b925af93e09fb Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 20 Jun 2023 23:15:28 -0400 Subject: [PATCH 0563/1148] 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 64c8de642d21a7d235c43463d74ddfe72834fa6d Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 21 Jun 2023 01:29:55 -0400 Subject: [PATCH 0564/1148] 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 3b872e1c4b5281163d6b29b2f6eb01bd6924c888 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 21 Jun 2023 01:58:58 -0400 Subject: [PATCH 0565/1148] 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 d1826d55a9b640fce7c6e7f34513f00ca5d877e5 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 21 Jun 2023 02:11:02 -0400 Subject: [PATCH 0566/1148] 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 89a5c4a59b6c1571ae508160047c6d9c57197f94 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 13 Jul 2023 19:15:24 -0400 Subject: [PATCH 0567/1148] 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 2d1269796aeb7d4bb59b85828ae7cbef5e547d0f Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 13 Jul 2023 19:20:13 -0400 Subject: [PATCH 0568/1148] 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 001d9f7cc58a0fd2b3c496164e72ded062c9b238 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 14 Jul 2023 14:10:22 -0400 Subject: [PATCH 0569/1148] 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 e76d838319fe6c5edb36237714025a31b9234d69 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 14 Jul 2023 14:17:26 -0400 Subject: [PATCH 0570/1148] 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 4de68838395c643460c292ab8fd533b12fa0ff67 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 28 Jun 2023 02:10:48 -0400 Subject: [PATCH 0571/1148] 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 9ac3fb004ca6232d2f9da5b8e9170bfb2114e428 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 12:46:04 -0400 Subject: [PATCH 0572/1148] 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 397e8895a0687caab19bfea1fffa12ba44350054 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 12:47:00 -0400 Subject: [PATCH 0573/1148] 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 fdfcfa1d50e3f0631307ddef92813450e10b7a34 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 12:57:49 -0400 Subject: [PATCH 0574/1148] 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 a1174b3a36c7bd279b4ffd8d98c45686d6c6e38f Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 13:49:09 -0400 Subject: [PATCH 0575/1148] 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 654a310ee1cb2beb9994cc0597da55e158c4b595 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 13:17:43 -0400 Subject: [PATCH 0576/1148] 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 b9ae99cb92012d6c0767ef447760c66fc5b2d3ea Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 13:19:18 -0400 Subject: [PATCH 0577/1148] 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 64e941e550d351d47318639aa232d0352d2124dc Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 19 Jul 2023 13:28:21 -0400 Subject: [PATCH 0578/1148] 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 31bfb6d0863e0c5e4b2826ae81534aa27520b2a3 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 13 Jul 2023 19:51:16 -0400 Subject: [PATCH 0579/1148] 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 d7e11654d02106ebec7aa740ca0eca14705a6755 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 13 Jul 2023 19:51:36 -0400 Subject: [PATCH 0580/1148] 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 dee87f150b698615c784a32789daacb9b486a9f1 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 20 Jul 2023 17:24:56 -0400 Subject: [PATCH 0581/1148] 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 8ab41f7499fed3007f2dfbcaf587a2e232768077 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 20 Jul 2023 17:27:12 -0400 Subject: [PATCH 0582/1148] 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 de8d12348661327cd0cb7d237ca878b98e86671b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 24 Jul 2023 19:38:56 -0600 Subject: [PATCH 0583/1148] nsiproxy.sys: Detect wireless interface type on Linux. CW-Bug-Id: #22496 --- dlls/nsiproxy.sys/ndis.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/nsiproxy.sys/ndis.c b/dlls/nsiproxy.sys/ndis.c index 73c047586a9..9c4fe80e865 100644 --- a/dlls/nsiproxy.sys/ndis.c +++ b/dlls/nsiproxy.sys/ndis.c @@ -200,7 +200,6 @@ static NTSTATUS if_get_physical( const char *name, UINT *type, IF_PHYSICAL_ADDRE } #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 bf5bf63f314851fddde758b198ef0548868de641 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 0584/1148] 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 c305ca8b036626663f6564bc7425d5ab5ffc6827 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 0585/1148] 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 bd86248c88860f70281935549f549d177c0ff22f 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 0586/1148] 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 d9d4f27bc3ae540240a595c5686e38ae06e37ef6 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Aug 2023 13:41:42 -0400 Subject: [PATCH 0587/1148] 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 050699729bff10f29e60786bef8d054ab5e91004 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Aug 2023 13:43:13 -0400 Subject: [PATCH 0588/1148] 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 e2e806a50f417d8e4d63622ff6f2d3f8671c95a5 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Aug 2023 13:44:04 -0400 Subject: [PATCH 0589/1148] 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 fa64f49ad69e297e446ae81b89efb9119802bbae Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Aug 2023 13:51:17 -0400 Subject: [PATCH 0590/1148] 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 905dd28173c3a98cbe444926ac9d0101a3e889a0 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Aug 2023 13:57:55 -0400 Subject: [PATCH 0591/1148] 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 74300abe0b1a276391f5d64d3fafc667fc3938c9 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Mon, 14 Aug 2023 16:21:07 -0300 Subject: [PATCH 0592/1148] 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 768bd5aa15561a9ee10e47d916db685a564a0ada Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Mon, 31 Jul 2023 11:44:35 -0300 Subject: [PATCH 0593/1148] 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 61bf462cbcc698e0ec792e2cd810c58131a67733 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Wed, 9 Aug 2023 19:43:42 -0300 Subject: [PATCH 0594/1148] 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 533c0651555b5673e4aae0613248b757cd9b37df Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Thu, 3 Aug 2023 12:23:17 -0300 Subject: [PATCH 0595/1148] 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 e2d344ee6a4791a24fbaa183765ff6ec8e604a8b 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 0596/1148] 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 e9c575406ea5870be811accdb8780bebb0671f0a 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 0597/1148] 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 b539dbc9e1131bfca5085f8a8347753b05e496ef 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 0598/1148] 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 77b5eb9eced4b8fb34cde3b9e26ed1a4369ba790 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 0599/1148] 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 237e63746092e6c0c7ac833149bd60737592ae6a 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 0600/1148] 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 1a625230efd5a9775182601ac27d4a93839ec39a 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 0601/1148] HACK: winegstreamer: Do not report live latency for some games. CW-Bug-Id: #22581 --- dlls/winegstreamer/h264_decoder.c | 6 ++++++ 1 file changed, 6 insertions(+) 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; From c33fa31361e91350e0cd13f7a5f97666830ea3e6 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 0602/1148] 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 92460954a2700c78648bbc6bfbe9aa273c54ef21 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Aug 2023 12:52:48 -0600 Subject: [PATCH 0603/1148] 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 79da11e94637b3587a49dd3f0524b5e94aaa0a56 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 31 Aug 2023 09:25:29 +0200 Subject: [PATCH 0604/1148] ntdll/tests: Add test for Win10 OutputDebugStringW evolution. Signed-off-by: Eric Pouech (cherry picked from commit b7004335c5f4509ecd2518aa8ed3d0e7db2a653e) --- dlls/ntdll/tests/exception.c | 155 +++++++++++++++++++++++++++++++---- 1 file changed, 138 insertions(+), 17 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 98624d3b7ac..2155e47b9ab 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -76,6 +76,7 @@ static BOOL (WINAPI *pInitializeContext2)(void *buffer, DWORD context_flags static void * (WINAPI *pLocateXStateFeature)(CONTEXT *context, DWORD feature_id, DWORD *length); static BOOL (WINAPI *pSetXStateFeaturesMask)(CONTEXT *context, DWORD64 feature_mask); static BOOL (WINAPI *pGetXStateFeaturesMask)(CONTEXT *context, DWORD64 *feature_mask); +static BOOL (WINAPI *pWaitForDebugEventEx)(DEBUG_EVENT *, DWORD); #define RTL_UNLOAD_EVENT_TRACE_NUMBER 64 @@ -8445,25 +8446,43 @@ static void test_debug_service(DWORD numexc) } #endif /* defined(__i386__) || defined(__x86_64__) */ -static DWORD outputdebugstring_exceptions; +static DWORD outputdebugstring_exceptions_ansi; +static DWORD outputdebugstring_exceptions_unicode; static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) { PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; trace("vect. handler %08lx addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress); - ok(rec->ExceptionCode == DBG_PRINTEXCEPTION_C, "ExceptionCode is %08lx instead of %08lx\n", - rec->ExceptionCode, DBG_PRINTEXCEPTION_C); - ok(rec->NumberParameters == 2, "ExceptionParameters is %ld instead of 2\n", rec->NumberParameters); - ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]); - ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"), - "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]); + switch (rec->ExceptionCode) + { + case DBG_PRINTEXCEPTION_C: + ok(rec->NumberParameters == 2, "ExceptionParameters is %ld instead of 2\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]); + ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"), + "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]); + outputdebugstring_exceptions_ansi++; + break; + case DBG_PRINTEXCEPTION_WIDE_C: + ok(outputdebugstring_exceptions_ansi == 0, "Unicode exception should come first\n"); + ok(rec->NumberParameters == 4, "ExceptionParameters is %ld instead of 4\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]); + ok(!wcscmp((WCHAR *)rec->ExceptionInformation[1], L"Hello World"), + "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]); + ok(rec->ExceptionInformation[2] == 12, "ExceptionInformation[2] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[2]); + ok(!strcmp((char *)rec->ExceptionInformation[3], "Hello World"), + "ExceptionInformation[3] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[3]); + outputdebugstring_exceptions_unicode++; + break; + default: + ok(0, "ExceptionCode is %08lx unexpected\n", rec->ExceptionCode); + break; + } - outputdebugstring_exceptions++; return EXCEPTION_CONTINUE_SEARCH; } -static void test_outputdebugstring(DWORD numexc, BOOL todo) +static void test_outputdebugstring(BOOL unicode, DWORD numexc_ansi, BOOL todo_ansi, DWORD numexc_unicode) { PVOID vectored_handler; @@ -8476,12 +8495,111 @@ static void test_outputdebugstring(DWORD numexc, BOOL todo) vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_vectored_handler); ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); - outputdebugstring_exceptions = 0; - OutputDebugStringA("Hello World"); + outputdebugstring_exceptions_ansi = outputdebugstring_exceptions_unicode = 0; + + if (unicode) + OutputDebugStringW(L"Hello World"); + else + OutputDebugStringA("Hello World"); + + todo_wine_if(todo_ansi) + ok(outputdebugstring_exceptions_ansi == numexc_ansi, + "OutputDebugString%c generated %ld ansi exceptions, expected %ld\n", + unicode ? 'W' : 'A', outputdebugstring_exceptions_ansi, numexc_ansi); + todo_wine_if(unicode) + ok(outputdebugstring_exceptions_unicode == numexc_unicode, + "OutputDebugString%c generated %lu unicode exceptions, expected %ld\n", + unicode ? 'W' : 'A', outputdebugstring_exceptions_unicode, numexc_unicode); + + pRtlRemoveVectoredExceptionHandler(vectored_handler); +} + +static DWORD outputdebugstring_exceptions_newmodel_order; +static DWORD outputdebugstring_newmodel_return; + +static LONG CALLBACK outputdebugstring_new_model_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + trace("vect. handler %08lx addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress); + + switch (rec->ExceptionCode) + { + case DBG_PRINTEXCEPTION_C: + ok(rec->NumberParameters == 2, "ExceptionParameters is %ld instead of 2\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]); + ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"), + "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]); + outputdebugstring_exceptions_newmodel_order = + (outputdebugstring_exceptions_newmodel_order << 8) | 'A'; + break; + case DBG_PRINTEXCEPTION_WIDE_C: + ok(rec->NumberParameters == 4, "ExceptionParameters is %ld instead of 4\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]); + ok(!wcscmp((WCHAR *)rec->ExceptionInformation[1], L"Hello World"), + "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]); + ok(rec->ExceptionInformation[2] == 12, "ExceptionInformation[2] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[2]); + ok(!strcmp((char *)rec->ExceptionInformation[3], "Hello World"), + "ExceptionInformation[3] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[3]); + outputdebugstring_exceptions_newmodel_order = + (outputdebugstring_exceptions_newmodel_order << 8) | 'W'; + break; + default: + ok(0, "ExceptionCode is %08lx unexpected\n", rec->ExceptionCode); + break; + } + + return outputdebugstring_newmodel_return; +} + +static void test_outputdebugstring_newmodel(void) +{ + PVOID vectored_handler; + struct + { + /* input */ + BOOL unicode; + DWORD ret_code; + /* expected output */ + DWORD exceptions_order; + } + tests[] = + { + {FALSE, EXCEPTION_CONTINUE_EXECUTION, 'A'}, + {FALSE, EXCEPTION_CONTINUE_SEARCH, 'A'}, + {TRUE, EXCEPTION_CONTINUE_EXECUTION, 'W'}, + {TRUE, EXCEPTION_CONTINUE_SEARCH, ('W' << 8) | 'A'}, + }; + int i; + + if (!pWaitForDebugEventEx) + { + skip("Unsupported new unicode debug string model\n"); + return; + } + if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler) + { + skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n"); + return; + } + + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_new_model_vectored_handler); + ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + outputdebugstring_exceptions_newmodel_order = 0; + outputdebugstring_newmodel_return = tests[i].ret_code; - todo_wine_if(todo) - ok(outputdebugstring_exceptions == numexc, "OutputDebugStringA generated %ld exceptions, expected %ld\n", - outputdebugstring_exceptions, numexc); + if (tests[i].unicode) + OutputDebugStringW(L"Hello World"); + else + OutputDebugStringA("Hello World"); + + ok(outputdebugstring_exceptions_newmodel_order == tests[i].exceptions_order, + "OutputDebugString%c/%u generated exceptions %04lxs, expected %04lx\n", + tests[i].unicode ? 'W' : 'A', i, + outputdebugstring_exceptions_newmodel_order, tests[i].exceptions_order); + } pRtlRemoveVectoredExceptionHandler(vectored_handler); } @@ -10907,6 +11025,7 @@ START_TEST(exception) X(LocateXStateFeature); X(SetXStateFeaturesMask); X(GetXStateFeaturesMask); + X(WaitForDebugEventEx); #undef X if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler) @@ -10955,9 +11074,9 @@ START_TEST(exception) else skip( "RtlRaiseException not found\n" ); #endif test_stage = 3; - test_outputdebugstring(0, FALSE); + test_outputdebugstring(FALSE, 0, FALSE, 0); test_stage = 4; - test_outputdebugstring(2, TRUE); /* is this a Windows bug? */ + test_outputdebugstring(FALSE, 2, TRUE, 0); /* is 2 a Windows bug? */ test_stage = 5; test_ripevent(0); test_stage = 6; @@ -11071,7 +11190,9 @@ START_TEST(exception) test_debugger(DBG_EXCEPTION_HANDLED); test_debugger(DBG_CONTINUE); test_thread_context(); - test_outputdebugstring(1, FALSE); + test_outputdebugstring(FALSE, 1, FALSE, 0); + test_outputdebugstring(TRUE, 1, FALSE, 1); + test_outputdebugstring_newmodel(); test_ripevent(1); test_fastfail(); test_breakpoint(1); From 4a3825c73ffbc19f300e985330514a4aae6b3878 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 31 Aug 2023 09:25:29 +0200 Subject: [PATCH 0605/1148] ntdll/tests: Introduce enumeration to handle stages in test_debugger(). It'll be easier to add stages. Signed-off-by: Eric Pouech (cherry picked from commit 33db5b78bc7357251172411c93bb13d4359e2f1a) --- dlls/ntdll/tests/exception.c | 211 +++++++++++++++++++---------------- 1 file changed, 116 insertions(+), 95 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 2155e47b9ab..8b45bcd096d 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -190,14 +190,34 @@ static VOID (WINAPI *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, static int (CDECL *p_setjmp)(_JUMP_BUFFER*); #endif +enum debugger_stages +{ + STAGE_RTLRAISE_NOT_HANDLED = 1, + STAGE_RTLRAISE_HANDLE_LAST_CHANCE, + STAGE_OUTPUTDEBUGSTRINGA_CONTINUE, + STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED, + STAGE_RIPEVENT_CONTINUE, + STAGE_RIPEVENT_NOT_HANDLED, + STAGE_SERVICE_CONTINUE, + STAGE_SERVICE_NOT_HANDLED, + STAGE_BREAKPOINT_CONTINUE, + STAGE_BREAKPOINT_NOT_HANDLED, + STAGE_EXCEPTION_INVHANDLE_CONTINUE, + STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED, + STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED, + STAGE_XSTATE, + STAGE_XSTATE_LEGACY_SSE, + STAGE_SEGMENTS, +}; + static int my_argc; static char** my_argv; static BOOL is_wow64; static BOOL have_vectored_api; -static int test_stage; +static enum debugger_stages test_stage; #if defined(__i386__) || defined(__x86_64__) -static void test_debugger_xstate(HANDLE thread, CONTEXT *ctx, int stage) +static void test_debugger_xstate(HANDLE thread, CONTEXT *ctx, enum debugger_stages stage) { char context_buffer[sizeof(CONTEXT) + sizeof(CONTEXT_EX) + sizeof(XSTATE) + 63]; CONTEXT_EX *c_ex; @@ -212,7 +232,7 @@ static void test_debugger_xstate(HANDLE thread, CONTEXT *ctx, int stage) if (!pRtlGetEnabledExtendedFeatures || !pRtlGetEnabledExtendedFeatures(1 << XSTATE_AVX)) return; - if (stage == 14) + if (stage == STAGE_XSTATE) return; length = sizeof(context_buffer); @@ -525,7 +545,7 @@ static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTR /* give the debugger a chance to examine the state a second time */ /* without the exception handler changing Eip */ - if (test_stage == 2) + if (test_stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) return ExceptionContinueSearch; /* Eip in context is decreased by 1 @@ -1110,7 +1130,7 @@ static void test_debugger(DWORD cont_status) else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { CONTEXT ctx; - int stage; + enum debugger_stages stage; counter++; status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address, @@ -1146,7 +1166,7 @@ static void test_debugger(DWORD cont_status) } else { - if (stage == 1) + if (stage == STAGE_RTLRAISE_NOT_HANDLED) { ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at %lx instead of %p\n", ctx.Eip, (char *)code_mem_address + 0xb); @@ -1157,7 +1177,7 @@ static void test_debugger(DWORD cont_status) /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 2) + else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) { if (de.u.Exception.dwFirstChance) { @@ -1193,25 +1213,25 @@ static void test_debugger(DWORD cont_status) /* here we handle exception */ } } - else if (stage == 7 || stage == 8) + else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); ok((char *)ctx.Eip == (char *)code_mem_address + 0x1d, "expected Eip = %p, got 0x%lx\n", (char *)code_mem_address + 0x1d, ctx.Eip); - if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 9 || stage == 10) + else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); ok((char *)ctx.Eip == (char *)code_mem_address + 2, "expected Eip = %p, got 0x%lx\n", (char *)code_mem_address + 2, ctx.Eip); - if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 11 || stage == 12) + else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE, "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode, @@ -1219,18 +1239,18 @@ static void test_debugger(DWORD cont_status) ok(de.u.Exception.ExceptionRecord.NumberParameters == 0, "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); - if (stage == 12) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 13) + else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 14 || stage == 15) + else if (stage == STAGE_XSTATE || stage == STAGE_XSTATE_LEGACY_SSE) { test_debugger_xstate(pi.hThread, &ctx, stage); } - else if (stage == 16) + else if (stage == STAGE_SEGMENTS) { USHORT ss; __asm__( "movw %%ss,%0" : "=r" (ss) ); @@ -1258,7 +1278,7 @@ static void test_debugger(DWORD cont_status) } else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { - int stage; + enum debugger_stages stage; char buffer[64]; status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, @@ -1274,22 +1294,22 @@ static void test_debugger(DWORD cont_status) de.u.DebugString.nDebugStringLength, &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == 3 || stage == 4) + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer); - if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } else if (de.dwDebugEventCode == RIP_EVENT) { - int stage; + enum debugger_stages stage; status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == 5 || stage == 6) + if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED) { ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n", de.u.RipInfo.dwError, 0x11223344); @@ -1299,7 +1319,7 @@ static void test_debugger(DWORD cont_status) else ok(FALSE, "unexpected stage %x\n", stage); - if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus); @@ -3653,7 +3673,7 @@ static void rtlraiseexception_handler_( EXCEPTION_RECORD *rec, EXCEPTION_REGISTR /* give the debugger a chance to examine the state a second time */ /* without the exception handler changing pc */ - if (test_stage == 2) + if (test_stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) return; /* pc in context is decreased by 1 @@ -3667,7 +3687,7 @@ static LONG CALLBACK rtlraiseexception_unhandled_handler(EXCEPTION_POINTERS *Exc PCONTEXT context = ExceptionInfo->ContextRecord; PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; rtlraiseexception_handler_(rec, NULL, context, NULL, TRUE); - if (test_stage == 2) return EXCEPTION_CONTINUE_SEARCH; + if (test_stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_EXECUTION; } @@ -3675,7 +3695,7 @@ static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTR CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) { rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE); - if (test_stage == 2) return ExceptionContinueSearch; + if (test_stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) return ExceptionContinueSearch; return ExceptionContinueExecution; } @@ -3741,7 +3761,7 @@ static void run_rtlraiseexception_test(DWORD exceptioncode) todo_wine ok( !rtlraiseexception_handler_called, "Frame handler called\n" ); - todo_wine_if (test_stage != 2) + todo_wine_if (test_stage != STAGE_RTLRAISE_HANDLE_LAST_CHANCE) ok( rtlraiseexception_unhandled_handler_called, "UnhandledExceptionFilter wasn't called\n" ); if (have_vectored_api) @@ -3818,7 +3838,7 @@ static void test_debugger(DWORD cont_status) else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { CONTEXT ctx; - int stage; + enum debugger_stages stage; counter++; status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address, @@ -3855,7 +3875,7 @@ static void test_debugger(DWORD cont_status) } else { - if (stage == 1) + if (stage == STAGE_RTLRAISE_NOT_HANDLED) { ok((char *)ctx.Rip == (char *)code_mem_address + 0x10, "Rip at %p instead of %p\n", (char *)ctx.Rip, (char *)code_mem_address + 0x10); @@ -3866,7 +3886,7 @@ static void test_debugger(DWORD cont_status) /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 2) + else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) { if (de.u.Exception.dwFirstChance) { @@ -3894,23 +3914,23 @@ static void test_debugger(DWORD cont_status) /* here we handle exception */ } } - else if (stage == 7 || stage == 8) + else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); ok((char *)ctx.Rip == (char *)code_mem_address + 0x30, "expected Rip = %p, got %p\n", (char *)code_mem_address + 0x30, (char *)ctx.Rip); - if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 9 || stage == 10) + else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); ok((char *)ctx.Rip == (char *)code_mem_address + 2, "expected Rip = %p, got %p\n", (char *)code_mem_address + 2, (char *)ctx.Rip); - if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 11 || stage == 12) + else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE, "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode, @@ -3918,18 +3938,18 @@ static void test_debugger(DWORD cont_status) ok(de.u.Exception.ExceptionRecord.NumberParameters == 0, "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); - if (stage == 12) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 13) + else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 14 || stage == 15) + else if (stage == STAGE_XSTATE || stage == STAGE_XSTATE_LEGACY_SSE) { test_debugger_xstate(pi.hThread, &ctx, stage); } - else if (stage == 16) + else if (stage == STAGE_SEGMENTS) { USHORT ss; __asm__( "movw %%ss,%0" : "=r" (ss) ); @@ -3961,7 +3981,7 @@ static void test_debugger(DWORD cont_status) } else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { - int stage; + enum debugger_stages stage; char buffer[64]; status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, @@ -3977,22 +3997,22 @@ static void test_debugger(DWORD cont_status) de.u.DebugString.nDebugStringLength, &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == 3 || stage == 4) + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer); - if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } else if (de.dwDebugEventCode == RIP_EVENT) { - int stage; + enum debugger_stages stage; status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == 5 || stage == 6) + if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED) { ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n", de.u.RipInfo.dwError, 0x11223344); @@ -4002,7 +4022,7 @@ static void test_debugger(DWORD cont_status) else ok(FALSE, "unexpected stage %x\n", stage); - if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus); @@ -6636,7 +6656,7 @@ static void test_debugger(DWORD cont_status) else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { CONTEXT ctx; - int stage; + enum debugger_stages stage; counter++; status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address, @@ -6673,7 +6693,7 @@ static void test_debugger(DWORD cont_status) } else { - if (stage == 1) + if (stage == STAGE_RTLRAISE_NOT_HANDLED) { ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at %lx instead of %p\n", ctx.Pc, (char *)code_mem_address + 0xb); @@ -6684,7 +6704,7 @@ static void test_debugger(DWORD cont_status) /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 2) + else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) { if (de.u.Exception.dwFirstChance) { @@ -6713,23 +6733,23 @@ static void test_debugger(DWORD cont_status) /* here we handle exception */ } } - else if (stage == 7 || stage == 8) + else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); ok((char *)ctx.Pc == (char *)code_mem_address + 0x1d, "expected Pc = %p, got 0x%lx\n", (char *)code_mem_address + 0x1d, ctx.Pc); - if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 9 || stage == 10) + else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); ok((char *)ctx.Pc == (char *)code_mem_address + 3, "expected Pc = %p, got 0x%lx\n", (char *)code_mem_address + 3, ctx.Pc); - if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 11 || stage == 12) + else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE, "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode, @@ -6737,9 +6757,9 @@ static void test_debugger(DWORD cont_status) ok(de.u.Exception.ExceptionRecord.NumberParameters == 0, "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); - if (stage == 12) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 13) + else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); continuestatus = DBG_EXCEPTION_NOT_HANDLED; @@ -6753,7 +6773,7 @@ static void test_debugger(DWORD cont_status) } else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { - int stage; + enum debugger_stages stage; char buffer[64]; status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, @@ -6769,22 +6789,22 @@ static void test_debugger(DWORD cont_status) de.u.DebugString.nDebugStringLength, &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == 3 || stage == 4) + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer); - if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } else if (de.dwDebugEventCode == RIP_EVENT) { - int stage; + enum debugger_stages stage; status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == 5 || stage == 6) + if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED) { ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n", de.u.RipInfo.dwError, 0x11223344); @@ -6794,7 +6814,7 @@ static void test_debugger(DWORD cont_status) else ok(FALSE, "unexpected stage %x\n", stage); - if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus); @@ -7891,7 +7911,7 @@ static void test_debugger(DWORD cont_status) else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { CONTEXT ctx; - int stage; + enum debugger_stages stage; counter++; status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address, @@ -7928,7 +7948,7 @@ static void test_debugger(DWORD cont_status) } else { - if (stage == 1) + if (stage == STAGE_RTLRAISE_NOT_HANDLED) { ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at %p instead of %p\n", (char *)ctx.Pc, (char *)code_mem_address + 0xb); @@ -7939,7 +7959,7 @@ static void test_debugger(DWORD cont_status) /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 2) + else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) { if (de.u.Exception.dwFirstChance) { @@ -7968,23 +7988,23 @@ static void test_debugger(DWORD cont_status) /* here we handle exception */ } } - else if (stage == 7 || stage == 8) + else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); ok((char *)ctx.Pc == (char *)code_mem_address + 0x1d, "expected Pc = %p, got %p\n", (char *)code_mem_address + 0x1d, (char *)ctx.Pc); - if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 9 || stage == 10) + else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); ok((char *)ctx.Pc == (char *)code_mem_address + 4, "expected Pc = %p, got %p\n", (char *)code_mem_address + 4, (char *)ctx.Pc); - if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 11 || stage == 12) + else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE, "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode, @@ -7992,9 +8012,9 @@ static void test_debugger(DWORD cont_status) ok(de.u.Exception.ExceptionRecord.NumberParameters == 0, "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); - if (stage == 12) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } - else if (stage == 13) + else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); continuestatus = DBG_EXCEPTION_NOT_HANDLED; @@ -8008,7 +8028,7 @@ static void test_debugger(DWORD cont_status) } else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { - int stage; + enum debugger_stages stage; char buffer[128]; status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, @@ -8024,23 +8044,23 @@ static void test_debugger(DWORD cont_status) de.u.DebugString.nDebugStringLength, &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == 3 || stage == 4) + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ ok(strstr(buffer, "SHIMVIEW") || !strncmp(buffer, "RTL:", 4), "unexpected stage %x, got debug string event '%s'\n", stage, buffer); - if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } else if (de.dwDebugEventCode == RIP_EVENT) { - int stage; + enum debugger_stages stage; status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == 5 || stage == 6) + if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED) { ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n", de.u.RipInfo.dwError, 0x11223344); @@ -8050,7 +8070,7 @@ static void test_debugger(DWORD cont_status) else ok(FALSE, "unexpected stage %x\n", stage); - if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus); @@ -8854,12 +8874,12 @@ static void test_debuggee_xstate(void) func(); for (i = 0; i < 4; ++i) - ok(data[i] == (test_stage == 14 ? i + 1 : 0x28282828), + ok(data[i] == (test_stage == STAGE_XSTATE ? i + 1 : 0x28282828), "Got unexpected data %#x, test_stage %u, i %u.\n", data[i], test_stage, i); for ( ; i < ARRAY_SIZE(data); ++i) - ok(data[i] == (test_stage == 14 ? i + 1 : 0x48484848) - || broken(test_stage == 15 && data[i] == i + 1) /* Win7 */, + ok(data[i] == (test_stage == STAGE_XSTATE ? i + 1 : 0x48484848) + || broken(test_stage == STAGE_XSTATE_LEGACY_SSE && data[i] == i + 1) /* Win7 */, "Got unexpected data %#x, test_stage %u, i %u.\n", data[i], test_stage, i); } @@ -11062,40 +11082,41 @@ START_TEST(exception) #if defined(__i386__) || defined(__x86_64__) if (pRtlRaiseException) { - test_stage = 1; + test_stage = STAGE_RTLRAISE_NOT_HANDLED; run_rtlraiseexception_test(0x12345); run_rtlraiseexception_test(EXCEPTION_BREAKPOINT); run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE); - test_stage = 2; + test_stage = STAGE_RTLRAISE_HANDLE_LAST_CHANCE; run_rtlraiseexception_test(0x12345); run_rtlraiseexception_test(EXCEPTION_BREAKPOINT); run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE); } else skip( "RtlRaiseException not found\n" ); #endif - test_stage = 3; + + test_stage = STAGE_OUTPUTDEBUGSTRINGA_CONTINUE; test_outputdebugstring(FALSE, 0, FALSE, 0); - test_stage = 4; + test_stage = STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED; test_outputdebugstring(FALSE, 2, TRUE, 0); /* is 2 a Windows bug? */ - test_stage = 5; + test_stage = STAGE_RIPEVENT_CONTINUE; test_ripevent(0); - test_stage = 6; + test_stage = STAGE_RIPEVENT_NOT_HANDLED; test_ripevent(1); - test_stage = 7; + test_stage = STAGE_SERVICE_CONTINUE; test_debug_service(0); - test_stage = 8; + test_stage = STAGE_SERVICE_NOT_HANDLED; test_debug_service(1); - test_stage = 9; + test_stage = STAGE_BREAKPOINT_CONTINUE; test_breakpoint(0); - test_stage = 10; + test_stage = STAGE_BREAKPOINT_NOT_HANDLED; test_breakpoint(1); - test_stage = 11; + test_stage = STAGE_EXCEPTION_INVHANDLE_CONTINUE; test_closehandle(0, (HANDLE)0xdeadbeef); test_closehandle(0, (HANDLE)0x7fffffff); - test_stage = 12; + test_stage = STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED; test_closehandle(1, (HANDLE)0xdeadbeef); test_closehandle(1, (HANDLE)~(ULONG_PTR)6); - test_stage = 13; /* special cases */ + test_stage = STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED; /* special cases */ test_closehandle(0, 0); test_closehandle(0, INVALID_HANDLE_VALUE); test_closehandle(0, GetCurrentProcess()); @@ -11105,11 +11126,11 @@ START_TEST(exception) test_closehandle(0, GetCurrentThreadToken()); test_closehandle(0, GetCurrentThreadEffectiveToken()); #if defined(__i386__) || defined(__x86_64__) - test_stage = 14; + test_stage = STAGE_XSTATE; test_debuggee_xstate(); - test_stage = 15; + test_stage = STAGE_XSTATE_LEGACY_SSE; test_debuggee_xstate(); - test_stage = 16; + test_stage = STAGE_SEGMENTS; test_debuggee_segments(); #endif From cd65117ba99d4e9310f2fa9c220e445b62ef9ec9 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 31 Aug 2023 09:25:29 +0200 Subject: [PATCH 0606/1148] ntdll/tests: Add tests for debuggee and new Win10 unicode debug strings. Signed-off-by: Eric Pouech (cherry picked from commit 514f63f3b5b56f8437ae5ed85fa4a81220477093) --- dlls/ntdll/tests/exception.c | 188 +++++++++++++++++++++++++++-------- 1 file changed, 144 insertions(+), 44 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 8b45bcd096d..0d5a4b59295 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -196,6 +196,8 @@ enum debugger_stages STAGE_RTLRAISE_HANDLE_LAST_CHANCE, STAGE_OUTPUTDEBUGSTRINGA_CONTINUE, STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED, + STAGE_OUTPUTDEBUGSTRINGW_CONTINUE, + STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED, STAGE_RIPEVENT_CONTINUE, STAGE_RIPEVENT_NOT_HANDLED, STAGE_SERVICE_CONTINUE, @@ -1077,7 +1079,7 @@ static void test_exceptions(void) ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", res ); } -static void test_debugger(DWORD cont_status) +static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) { char cmdline[MAX_PATH]; PROCESS_INFORMATION pi; @@ -1097,6 +1099,12 @@ static void test_debugger(DWORD cont_status) return; } + if (with_WaitForDebugEventEx && !pWaitForDebugEventEx) + { + skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n"); + return; + } + sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage); ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); ok(ret, "could not create child process error: %lu\n", GetLastError()); @@ -1106,7 +1114,8 @@ static void test_debugger(DWORD cont_status) do { continuestatus = cont_status; - ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n"); + ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE); + ok(ret, "reading debug event\n"); ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef); ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n"); @@ -1279,27 +1288,43 @@ static void test_debugger(DWORD cont_status) else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { enum debugger_stages stage; - char buffer[64]; + char buffer[64 * sizeof(WCHAR)]; + unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char); status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n"); - ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n", - de.u.DebugString.nDebugStringLength); + if (de.u.DebugString.fUnicode) + ok(with_WaitForDebugEventEx && + (stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED), + "unexpected unicode debug string event\n"); + else + ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE, + "unexpected ansi debug string event %u %s %lx\n", + stage, with_WaitForDebugEventEx ? "with" : "without", cont_status); + + ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1, + "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength); memset(buffer, 0, sizeof(buffer)); status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer, - de.u.DebugString.nDebugStringLength, &size_read); + de.u.DebugString.nDebugStringLength * char_size, &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) - ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || + stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + { + if (de.u.DebugString.fUnicode) + ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer); + else + ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + } else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer); - if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + continuestatus = DBG_EXCEPTION_NOT_HANDLED; } else if (de.dwDebugEventCode == RIP_EVENT) { @@ -3785,7 +3810,7 @@ static void test_rtlraiseexception(void) run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE); } -static void test_debugger(DWORD cont_status) +static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) { char cmdline[MAX_PATH]; PROCESS_INFORMATION pi; @@ -3805,6 +3830,12 @@ static void test_debugger(DWORD cont_status) return; } + if (with_WaitForDebugEventEx && !pWaitForDebugEventEx) + { + skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n"); + return; + } + sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage); ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); ok(ret, "could not create child process error: %lu\n", GetLastError()); @@ -3814,7 +3845,8 @@ static void test_debugger(DWORD cont_status) do { continuestatus = cont_status; - ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n"); + ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE); + ok(ret, "reading debug event\n"); ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef); ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n"); @@ -3982,27 +4014,43 @@ static void test_debugger(DWORD cont_status) else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { enum debugger_stages stage; - char buffer[64]; + char buffer[64 * sizeof(WCHAR)]; + unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char); status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n"); - ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n", - de.u.DebugString.nDebugStringLength); + if (de.u.DebugString.fUnicode) + ok(with_WaitForDebugEventEx && + (stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED), + "unexpected unicode debug string event\n"); + else + ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE, + "unexpected ansi debug string event %u %s %lx\n", + stage, with_WaitForDebugEventEx ? "with" : "without", cont_status); + + ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1, + "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength); memset(buffer, 0, sizeof(buffer)); status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer, - de.u.DebugString.nDebugStringLength, &size_read); + de.u.DebugString.nDebugStringLength * char_size, &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) - ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || + stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + { + if (de.u.DebugString.fUnicode) + ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer); + else + ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + } else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer); - if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + continuestatus = DBG_EXCEPTION_NOT_HANDLED; } else if (de.dwDebugEventCode == RIP_EVENT) { @@ -6603,7 +6651,7 @@ static void test_thread_context(void) #undef COMPARE } -static void test_debugger(DWORD cont_status) +static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) { char cmdline[MAX_PATH]; PROCESS_INFORMATION pi; @@ -6623,6 +6671,12 @@ static void test_debugger(DWORD cont_status) return; } + if (with_WaitForDebugEventEx && !pWaitForDebugEventEx) + { + skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n"); + return; + } + sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage); ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); ok(ret, "could not create child process error: %lu\n", GetLastError()); @@ -6632,7 +6686,8 @@ static void test_debugger(DWORD cont_status) do { continuestatus = cont_status; - ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n"); + ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE); + ok(ret, "reading debug event\n"); ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef); ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n"); @@ -6774,27 +6829,43 @@ static void test_debugger(DWORD cont_status) else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { enum debugger_stages stage; - char buffer[64]; + char buffer[64 * sizeof(WCHAR)]; + unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char); status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n"); - ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n", - de.u.DebugString.nDebugStringLength); + if (de.u.DebugString.fUnicode) + ok(with_WaitForDebugEventEx && + (stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED), + "unexpected unicode debug string event\n"); + else + ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE, + "unexpected ansi debug string event %u %s %lx\n", + stage, with_WaitForDebugEventEx ? "with" : "without", cont_status); + + ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1, + "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength); memset(buffer, 0, sizeof(buffer)); status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer, - de.u.DebugString.nDebugStringLength, &size_read); + de.u.DebugString.nDebugStringLength * char_size, &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) - ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || + stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + { + if (de.u.DebugString.fUnicode) + ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer); + else + ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + } else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer); - if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + continuestatus = DBG_EXCEPTION_NOT_HANDLED; } else if (de.dwDebugEventCode == RIP_EVENT) { @@ -7858,7 +7929,7 @@ static void test_thread_context(void) #undef COMPARE } -static void test_debugger(DWORD cont_status) +static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) { char cmdline[MAX_PATH]; PROCESS_INFORMATION pi; @@ -7878,6 +7949,12 @@ static void test_debugger(DWORD cont_status) return; } + if (with_WaitForDebugEventEx && !pWaitForDebugEventEx) + { + skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n"); + return; + } + sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage); ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); ok(ret, "could not create child process error: %lu\n", GetLastError()); @@ -7887,7 +7964,8 @@ static void test_debugger(DWORD cont_status) do { continuestatus = cont_status; - ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n"); + ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE); + ok(ret, "reading debug event\n"); ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef); ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n"); @@ -8029,28 +8107,44 @@ static void test_debugger(DWORD cont_status) else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { enum debugger_stages stage; - char buffer[128]; + char buffer[128 * sizeof(WCHAR)]; + unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char); status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, sizeof(stage), &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n"); - ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n", - de.u.DebugString.nDebugStringLength); + if (de.u.DebugString.fUnicode) + ok(with_WaitForDebugEventEx && + (stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED), + "unexpected unicode debug string event\n"); + else + ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE, + "unexpected ansi debug string event %u %s %lx\n", + stage, with_WaitForDebugEventEx ? "with" : "without", cont_status); + + ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1, + "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength); memset(buffer, 0, sizeof(buffer)); status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer, - de.u.DebugString.nDebugStringLength, &size_read); + de.u.DebugString.nDebugStringLength * char_size, &size_read); ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) - ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || + stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + { + if (de.u.DebugString.fUnicode) + ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer); + else + ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + } else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ ok(strstr(buffer, "SHIMVIEW") || !strncmp(buffer, "RTL:", 4), - "unexpected stage %x, got debug string event '%s'\n", stage, buffer); + "unexpected stage %x, got debug string event '%s'\n", stage, buffer); - if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + continuestatus = DBG_EXCEPTION_NOT_HANDLED; } else if (de.dwDebugEventCode == RIP_EVENT) { @@ -8526,7 +8620,7 @@ static void test_outputdebugstring(BOOL unicode, DWORD numexc_ansi, BOOL todo_an ok(outputdebugstring_exceptions_ansi == numexc_ansi, "OutputDebugString%c generated %ld ansi exceptions, expected %ld\n", unicode ? 'W' : 'A', outputdebugstring_exceptions_ansi, numexc_ansi); - todo_wine_if(unicode) + todo_wine_if(unicode && numexc_unicode) ok(outputdebugstring_exceptions_unicode == numexc_unicode, "OutputDebugString%c generated %lu unicode exceptions, expected %ld\n", unicode ? 'W' : 'A', outputdebugstring_exceptions_unicode, numexc_unicode); @@ -11098,6 +11192,10 @@ START_TEST(exception) test_outputdebugstring(FALSE, 0, FALSE, 0); test_stage = STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED; test_outputdebugstring(FALSE, 2, TRUE, 0); /* is 2 a Windows bug? */ + test_stage = STAGE_OUTPUTDEBUGSTRINGW_CONTINUE; + test_outputdebugstring(TRUE, 0, FALSE, 0); + test_stage = STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED; + test_outputdebugstring(TRUE, 2, TRUE, 1); /* is 2 a Windows bug? */ test_stage = STAGE_RIPEVENT_CONTINUE; test_ripevent(0); test_stage = STAGE_RIPEVENT_NOT_HANDLED; @@ -11208,8 +11306,10 @@ START_TEST(exception) #endif - test_debugger(DBG_EXCEPTION_HANDLED); - test_debugger(DBG_CONTINUE); + test_debugger(DBG_EXCEPTION_HANDLED, FALSE); + test_debugger(DBG_CONTINUE, FALSE); + test_debugger(DBG_EXCEPTION_HANDLED, TRUE); + test_debugger(DBG_CONTINUE, TRUE); test_thread_context(); test_outputdebugstring(FALSE, 1, FALSE, 0); test_outputdebugstring(TRUE, 1, FALSE, 1); From c4a24ec256782546e4c431868473f3fabd0cd0b0 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 31 Aug 2023 09:25:29 +0200 Subject: [PATCH 0607/1148] kernelbase: Emit DBG_PRINTEXCEPTION_WIDE_C from OutputDebugStringW. Also modifying WaitForDebugEvent() to force resending the ansi DBG_PRINTEXCEPTION_C exception instead. Signed-off-by: Eric Pouech (cherry picked from commit 090fb2cc19f2cb8930933f1931e6ccb77d88c470) --- dlls/kernelbase/debug.c | 29 +++++++++++++++++++++++++- dlls/kernelbase/sync.c | 11 ++++++++++ dlls/ntdll/tests/exception.c | 40 +++++++++++++++++++++--------------- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c index 652f1cf8809..8af51dd7d71 100644 --- a/dlls/kernelbase/debug.c +++ b/dlls/kernelbase/debug.c @@ -262,6 +262,11 @@ void WINAPI DECLSPEC_HOTPATCH OutputDebugStringA( LPCSTR str ) } } +static LONG WINAPI debug_exception_handler_wide( EXCEPTION_POINTERS *eptr ) +{ + EXCEPTION_RECORD *rec = eptr->ExceptionRecord; + return (rec->ExceptionCode == DBG_PRINTEXCEPTION_WIDE_C) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} /*********************************************************************** * OutputDebugStringW (kernelbase.@) @@ -271,10 +276,32 @@ void WINAPI DECLSPEC_HOTPATCH OutputDebugStringW( LPCWSTR str ) UNICODE_STRING strW; STRING strA; + WARN( "%s\n", debugstr_w(str) ); + RtlInitUnicodeString( &strW, str ); if (!RtlUnicodeStringToAnsiString( &strA, &strW, TRUE )) { - OutputDebugStringA( strA.Buffer ); + BOOL exc_handled; + + __TRY + { + ULONG_PTR args[4]; + args[0] = wcslen(str) + 1; + args[1] = (ULONG_PTR)str; + args[2] = strlen(strA.Buffer) + 1; + args[3] = (ULONG_PTR)strA.Buffer; + RaiseException( DBG_PRINTEXCEPTION_WIDE_C, 0, 4, args ); + exc_handled = TRUE; + } + __EXCEPT(debug_exception_handler_wide) + { + exc_handled = FALSE; + } + __ENDTRY + + if (!exc_handled) + OutputDebugStringA( strA.Buffer ); + RtlFreeAnsiString( &strA ); } } diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c index 4c366f859c6..797e43fbedc 100644 --- a/dlls/kernelbase/sync.c +++ b/dlls/kernelbase/sync.c @@ -364,6 +364,17 @@ BOOL WINAPI DECLSPEC_HOTPATCH WaitForDebugEvent( DEBUG_EVENT *event, DWORD timeo switch (status) { case STATUS_SUCCESS: + /* continue on wide print exceptions to force resending an ANSI one. */ + if (state.NewState == DbgExceptionStateChange) + { + DBGKM_EXCEPTION *info = &state.StateInfo.Exception; + DWORD code = info->ExceptionRecord.ExceptionCode; + if (code == DBG_PRINTEXCEPTION_WIDE_C && info->ExceptionRecord.NumberParameters >= 2) + { + DbgUiContinue( &state.AppClientId, DBG_EXCEPTION_NOT_HANDLED ); + break; + } + } DbgUiConvertStateChangeStructure( &state, event ); return TRUE; case STATUS_USER_APC: diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 0d5a4b59295..0b55a93e898 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -8596,7 +8596,8 @@ static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *Exce return EXCEPTION_CONTINUE_SEARCH; } -static void test_outputdebugstring(BOOL unicode, DWORD numexc_ansi, BOOL todo_ansi, DWORD numexc_unicode) +static void test_outputdebugstring(BOOL unicode, DWORD numexc_ansi, BOOL todo_ansi, + DWORD numexc_unicode_low, DWORD numexc_unicode_high) { PVOID vectored_handler; @@ -8620,10 +8621,11 @@ static void test_outputdebugstring(BOOL unicode, DWORD numexc_ansi, BOOL todo_an ok(outputdebugstring_exceptions_ansi == numexc_ansi, "OutputDebugString%c generated %ld ansi exceptions, expected %ld\n", unicode ? 'W' : 'A', outputdebugstring_exceptions_ansi, numexc_ansi); - todo_wine_if(unicode && numexc_unicode) - ok(outputdebugstring_exceptions_unicode == numexc_unicode, - "OutputDebugString%c generated %lu unicode exceptions, expected %ld\n", - unicode ? 'W' : 'A', outputdebugstring_exceptions_unicode, numexc_unicode); + todo_wine_if(unicode && numexc_unicode_low) + ok(outputdebugstring_exceptions_unicode >= numexc_unicode_low && + outputdebugstring_exceptions_unicode <= numexc_unicode_high, + "OutputDebugString%c generated %lu unicode exceptions, expected %ld-%ld\n", + unicode ? 'W' : 'A', outputdebugstring_exceptions_unicode, numexc_unicode_low, numexc_unicode_high); pRtlRemoveVectoredExceptionHandler(vectored_handler); } @@ -8685,11 +8687,6 @@ static void test_outputdebugstring_newmodel(void) }; int i; - if (!pWaitForDebugEventEx) - { - skip("Unsupported new unicode debug string model\n"); - return; - } if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler) { skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n"); @@ -11189,13 +11186,16 @@ START_TEST(exception) #endif test_stage = STAGE_OUTPUTDEBUGSTRINGA_CONTINUE; - test_outputdebugstring(FALSE, 0, FALSE, 0); + + test_outputdebugstring(FALSE, 0, FALSE, 0, 0); test_stage = STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED; - test_outputdebugstring(FALSE, 2, TRUE, 0); /* is 2 a Windows bug? */ + test_outputdebugstring(FALSE, 2, TRUE, 0, 0); /* is 2 a Windows bug? */ test_stage = STAGE_OUTPUTDEBUGSTRINGW_CONTINUE; - test_outputdebugstring(TRUE, 0, FALSE, 0); + /* depending on value passed DebugContinue we can get the unicode exception or not */ + test_outputdebugstring(TRUE, 0, FALSE, 0, 1); test_stage = STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED; - test_outputdebugstring(TRUE, 2, TRUE, 1); /* is 2 a Windows bug? */ + /* depending on value passed DebugContinue we can get the unicode exception or not */ + test_outputdebugstring(TRUE, 2, TRUE, 0, 1); /* is 2 a Windows bug? */ test_stage = STAGE_RIPEVENT_CONTINUE; test_ripevent(0); test_stage = STAGE_RIPEVENT_NOT_HANDLED; @@ -11311,9 +11311,15 @@ START_TEST(exception) test_debugger(DBG_EXCEPTION_HANDLED, TRUE); test_debugger(DBG_CONTINUE, TRUE); test_thread_context(); - test_outputdebugstring(FALSE, 1, FALSE, 0); - test_outputdebugstring(TRUE, 1, FALSE, 1); - test_outputdebugstring_newmodel(); + test_outputdebugstring(FALSE, 1, FALSE, 0, 0); + if (pWaitForDebugEventEx) + { + test_outputdebugstring(TRUE, 1, FALSE, 1, 1); + test_outputdebugstring_newmodel(); + } + else + skip("Unsupported new unicode debug string model\n"); + test_ripevent(1); test_fastfail(); test_breakpoint(1); From fed0b4105215a54d555bb4a58eb6435a5084b3f1 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 31 Aug 2023 09:25:29 +0200 Subject: [PATCH 0608/1148] kernelbase: Implement WaitForDebugEventEx(). Signed-off-by: Eric Pouech (cherry picked from commit e09dde5c949a5650ac9be0f2ccc457552e854dee) --- dlls/kernel32/kernel32.spec | 1 + dlls/kernelbase/kernelbase.spec | 2 +- dlls/kernelbase/sync.c | 28 ++++++++++++++++++++++++++++ dlls/ntdll/process.c | 7 +++++++ dlls/ntdll/tests/exception.c | 1 - include/winbase.h | 1 + 6 files changed, 38 insertions(+), 2 deletions(-) diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index 1d6dcc11f77..c9172e8022e 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -1606,6 +1606,7 @@ @ stdcall WTSGetActiveConsoleSessionId() @ stdcall -import WaitCommEvent(long ptr ptr) @ stdcall -import WaitForDebugEvent(ptr long) +@ stdcall -import WaitForDebugEventEx(ptr long) @ stdcall -import WaitForMultipleObjects(long ptr long long) @ stdcall -import WaitForMultipleObjectsEx(long ptr long long long) @ stdcall -import WaitForSingleObject(long long) diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index c14f2a87472..9c3571260ab 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1730,7 +1730,7 @@ # @ stub WTSIsServerContainer @ stdcall WaitCommEvent(long ptr ptr) @ stdcall WaitForDebugEvent(ptr long) -# @ stub WaitForDebugEventEx +@ stdcall WaitForDebugEventEx(ptr long) # @ stub WaitForMachinePolicyForegroundProcessingInternal @ stdcall WaitForMultipleObjects(long ptr long long) @ stdcall WaitForMultipleObjectsEx(long ptr long long long) diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c index 797e43fbedc..270ce137a02 100644 --- a/dlls/kernelbase/sync.c +++ b/dlls/kernelbase/sync.c @@ -388,6 +388,34 @@ BOOL WINAPI DECLSPEC_HOTPATCH WaitForDebugEvent( DEBUG_EVENT *event, DWORD timeo } } +/****************************************************************************** + * WaitForDebugEventEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WaitForDebugEventEx( DEBUG_EVENT *event, DWORD timeout ) +{ + NTSTATUS status; + LARGE_INTEGER time; + DBGUI_WAIT_STATE_CHANGE state; + + for (;;) + { + status = DbgUiWaitStateChange( &state, get_nt_timeout( &time, timeout ) ); + switch (status) + { + case STATUS_SUCCESS: + DbgUiConvertStateChangeStructure( &state, event ); + return TRUE; + case STATUS_USER_APC: + continue; + case STATUS_TIMEOUT: + SetLastError( ERROR_SEM_TIMEOUT ); + return FALSE; + default: + return set_ntstatus( status ); + } + } +} + /*********************************************************************** * WaitOnAddress (kernelbase.@) diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c index 0b4245fdd42..dbfcd14060b 100644 --- a/dlls/ntdll/process.c +++ b/dlls/ntdll/process.c @@ -487,6 +487,13 @@ NTSTATUS WINAPI DbgUiConvertStateChangeStructure( DBGUI_WAIT_STATE_CHANGE *state event->u.DebugString.fUnicode = FALSE; event->u.DebugString.nDebugStringLength = info->ExceptionRecord.ExceptionInformation[0]; } + else if (code == DBG_PRINTEXCEPTION_WIDE_C && info->ExceptionRecord.NumberParameters >= 2) + { + event->dwDebugEventCode = OUTPUT_DEBUG_STRING_EVENT; + event->u.DebugString.lpDebugStringData = (void *)info->ExceptionRecord.ExceptionInformation[1]; + event->u.DebugString.fUnicode = TRUE; + event->u.DebugString.nDebugStringLength = info->ExceptionRecord.ExceptionInformation[0]; + } else if (code == DBG_RIPEXCEPTION && info->ExceptionRecord.NumberParameters >= 2) { event->dwDebugEventCode = RIP_EVENT; diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 0b55a93e898..af215c1c12b 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -8621,7 +8621,6 @@ static void test_outputdebugstring(BOOL unicode, DWORD numexc_ansi, BOOL todo_an ok(outputdebugstring_exceptions_ansi == numexc_ansi, "OutputDebugString%c generated %ld ansi exceptions, expected %ld\n", unicode ? 'W' : 'A', outputdebugstring_exceptions_ansi, numexc_ansi); - todo_wine_if(unicode && numexc_unicode_low) ok(outputdebugstring_exceptions_unicode >= numexc_unicode_low && outputdebugstring_exceptions_unicode <= numexc_unicode_high, "OutputDebugString%c generated %lu unicode exceptions, expected %ld-%ld\n", diff --git a/include/winbase.h b/include/winbase.h index 9859e22598d..5a2170882a1 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -2806,6 +2806,7 @@ WINBASEAPI BOOL WINAPI VirtualUnlock(LPVOID,SIZE_T); WINBASEAPI DWORD WINAPI WTSGetActiveConsoleSessionId(void); WINBASEAPI BOOL WINAPI WaitCommEvent(HANDLE,LPDWORD,LPOVERLAPPED); WINBASEAPI BOOL WINAPI WaitForDebugEvent(LPDEBUG_EVENT,DWORD); +WINBASEAPI BOOL WINAPI WaitForDebugEventEx(LPDEBUG_EVENT,DWORD); WINBASEAPI DWORD WINAPI WaitForMultipleObjects(DWORD,const HANDLE*,BOOL,DWORD); WINBASEAPI DWORD WINAPI WaitForMultipleObjectsEx(DWORD,const HANDLE*,BOOL,DWORD,BOOL); WINBASEAPI DWORD WINAPI WaitForSingleObject(HANDLE,DWORD); From 5632bb3270029acf233bafbb8e9b93778acb5f92 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 1 Sep 2023 12:56:39 -0600 Subject: [PATCH 0609/1148] server: Don't set SO_RCVBUF below default value on Unix socket. CW-Bug-Id: #22692 --- dlls/ws2_32/tests/sock.c | 59 +++++++++++++++++++++++++++++++++++++++- server/sock.c | 10 +++++-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 21c6b81028d..ae582aba406 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -161,6 +161,7 @@ static GUID WSARecvMsg_GUID = WSAID_WSARECVMSG; static SOCKET setup_server_socket(struct sockaddr_in *addr, int *len); static SOCKET setup_connector_socket(const struct sockaddr_in *addr, int len, BOOL nonblock); +static int sync_recv(SOCKET s, void *buffer, int len, DWORD flags); static void tcp_socketpair_flags(SOCKET *src, SOCKET *dst, DWORD flags) { @@ -1230,7 +1231,7 @@ static void test_set_getsockopt(void) {AF_INET6, SOCK_DGRAM, IPPROTO_IPV6, IPV6_UNICAST_HOPS, TRUE, {1, 1, 4}, {0}, FALSE}, {AF_INET6, SOCK_DGRAM, IPPROTO_IPV6, IPV6_V6ONLY, TRUE, {1, 1, 1}, {0}, TRUE}, }; - SOCKET s, s2; + SOCKET s, s2, src, dst; int i, j, err, lasterr; int timeout; LINGER lingval; @@ -1242,6 +1243,7 @@ static void test_set_getsockopt(void) int expected_err, expected_size; DWORD value, save_value; UINT64 value64; + char buffer[4096]; struct _prottest { @@ -1307,6 +1309,61 @@ static void test_set_getsockopt(void) ok( !err, "getsockopt(SO_RCVBUF) failed error: %u\n", WSAGetLastError() ); ok( value == 4096, "expected 4096, got %lu\n", value ); + value = 0; + size = sizeof(value); + err = setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&value, size); + ok( !err, "setsockopt(SO_RCVBUF) failed error: %u\n", WSAGetLastError() ); + value = 0xdeadbeef; + err = getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&value, &size); + ok( !err, "getsockopt(SO_RCVBUF) failed error: %u\n", WSAGetLastError() ); + ok( value == 0, "expected 0, got %lu\n", value ); + + /* Test non-blocking receive with too short SO_RCVBUF. */ + tcp_socketpair(&src, &dst); + set_blocking(src, FALSE); + set_blocking(dst, FALSE); + + value = 0; + size = sizeof(value); + err = setsockopt(src, SOL_SOCKET, SO_SNDBUF, (char *)&value, size); + ok( !err, "got %d, error %u.\n", err, WSAGetLastError() ); + + value = 0xdeadbeef; + err = getsockopt(dst, SOL_SOCKET, SO_RCVBUF, (char *)&value, &size); + ok( !err, "got %d, error %u.\n", err, WSAGetLastError() ); + if (value >= sizeof(buffer) * 3) + { + value = 1024; + size = sizeof(value); + err = setsockopt(dst, SOL_SOCKET, SO_RCVBUF, (char *)&value, size); + ok( !err, "got %d, error %u.\n", err, WSAGetLastError() ); + value = 0xdeadbeef; + err = getsockopt(dst, SOL_SOCKET, SO_RCVBUF, (char *)&value, &size); + ok( !err, "got %d, error %u.\n", err, WSAGetLastError() ); + ok( value == 1024, "expected 0, got %lu\n", value ); + + err = send(src, buffer, sizeof(buffer), 0); + ok(err == sizeof(buffer), "got %d\n", err); + err = send(src, buffer, sizeof(buffer), 0); + ok(err == sizeof(buffer), "got %d\n", err); + err = send(src, buffer, sizeof(buffer), 0); + ok(err == sizeof(buffer), "got %d\n", err); + + err = sync_recv(dst, buffer, sizeof(buffer), 0); + ok(err == sizeof(buffer), "got %d, error %u\n", err, WSAGetLastError()); + err = sync_recv(dst, buffer, sizeof(buffer), 0); + ok(err == sizeof(buffer), "got %d, error %u\n", err, WSAGetLastError()); + err = sync_recv(dst, buffer, sizeof(buffer), 0); + ok(err == sizeof(buffer), "got %d, error %u\n", err, WSAGetLastError()); + } + else + { + skip("Default SO_RCVBUF %lu is too small, skipping test.\n", value); + } + + closesocket(src); + closesocket(dst); + /* SO_LINGER */ for( i = 0; i < ARRAY_SIZE(linger_testvals);i++) { size = sizeof(lingval); diff --git a/server/sock.c b/server/sock.c index 16769fc2b4b..62b9ceca0a2 100644 --- a/server/sock.c +++ b/server/sock.c @@ -239,6 +239,7 @@ struct sock struct poll_req *main_poll; /* main poll */ union win_sockaddr addr; /* socket name */ int addr_len; /* socket name length */ + unsigned int default_rcvbuf; /* initial advisory recv buffer size */ unsigned int rcvbuf; /* advisory recv buffer size */ unsigned int sndbuf; /* advisory send buffer size */ unsigned int rcvtimeo; /* receive timeout in ms */ @@ -1729,6 +1730,7 @@ static struct sock *create_socket(void) sock->reset = 0; sock->reuseaddr = 0; sock->exclusiveaddruse = 0; + sock->default_rcvbuf = 0; sock->rcvbuf = 0; sock->sndbuf = 0; sock->rcvtimeo = 0; @@ -1912,7 +1914,7 @@ static int init_socket( struct sock *sock, int family, int type, int protocol ) len = sizeof(value); if (!getsockopt( sockfd, SOL_SOCKET, SO_RCVBUF, &value, &len )) - sock->rcvbuf = value; + sock->rcvbuf = sock->default_rcvbuf = value; len = sizeof(value); if (!getsockopt( sockfd, SOL_SOCKET, SO_SNDBUF, &value, &len )) @@ -2007,6 +2009,7 @@ static struct sock *accept_socket( struct sock *sock ) acceptsock->reuseaddr = sock->reuseaddr; acceptsock->exclusiveaddruse = sock->exclusiveaddruse; acceptsock->sndbuf = sock->sndbuf; + acceptsock->default_rcvbuf = sock->default_rcvbuf; acceptsock->rcvbuf = sock->rcvbuf; acceptsock->sndtimeo = sock->sndtimeo; acceptsock->rcvtimeo = sock->rcvtimeo; @@ -3086,7 +3089,7 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) case IOCTL_AFD_WINE_SET_SO_RCVBUF: { - DWORD rcvbuf; + DWORD rcvbuf, set_rcvbuf; if (get_req_data_size() < sizeof(rcvbuf)) { @@ -3094,8 +3097,9 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) return; } rcvbuf = *(DWORD *)get_req_data(); + set_rcvbuf = max( rcvbuf, sock->default_rcvbuf ); - if (!setsockopt( unix_fd, SOL_SOCKET, SO_RCVBUF, (char *)&rcvbuf, sizeof(rcvbuf) )) + if (!setsockopt( unix_fd, SOL_SOCKET, SO_RCVBUF, (char *)&set_rcvbuf, sizeof(set_rcvbuf) )) sock->rcvbuf = rcvbuf; else set_error( sock_get_ntstatus( errno ) ); From 057bdf02e4f52c38b271ef77558e0c08ab2f6ab9 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Thu, 10 Aug 2023 22:41:16 +0800 Subject: [PATCH 0610/1148] winex11.drv: Use thread display instead of gdi_display to move windows. Thread display instead of gdi_display should be used to move X11 windows. Otherwise, in sync_client_position() the thread display is used but in window_update_fshack() gdi_display is used. Since they are on a different display, their window configure requests might not be handled in the correct order. CW-Bug-Id: #22345 --- 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 4121b558ca9..4940ddc7804 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3020,7 +3020,7 @@ static void window_update_fshack( struct x11drv_win_data *data, const RECT *wind client_rect_host.right = min( max( client_rect_host.right, 1 ), 65535 ); client_rect_host.bottom = min( max( client_rect_host.bottom, 1 ), 65535 ); - XMoveResizeWindow( gdi_display, data->client_window, top_left.x, top_left.y, client_rect_host.right, client_rect_host.bottom ); + XMoveResizeWindow( data->display, data->client_window, top_left.x, top_left.y, client_rect_host.right, client_rect_host.bottom ); sync_gl_drawable( data->hwnd, !data->whole_window ); invalidate_vk_surfaces( data->hwnd ); From e8b6e4e73edcee9a4c988ea91b9a7e8e7d7dfdb5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Aug 2023 12:19:48 -0600 Subject: [PATCH 0611/1148] ntdll/tests: Add tests for RtlGetFullPathName_U and RtlDosPathNameToNtPathName_U with mask. (cherry picked from commit 9e04cd31bcfacbfbccf1f0c9525493189710e8de) CW-Bug-Id: #22684 --- dlls/ntdll/tests/path.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/ntdll/tests/path.c b/dlls/ntdll/tests/path.c index 4732f6a6aa8..64bc92a4f72 100644 --- a/dlls/ntdll/tests/path.c +++ b/dlls/ntdll/tests/path.c @@ -306,6 +306,9 @@ static void test_RtlGetFullPathName_U(void) { "c:/test../file", "c:\\test.\\file", "file", "c:\\test..\\file", "file"}, /* vista */ { "c:\\test", "c:\\test", "test"}, + { "c:\\test\\*.", "c:\\test\\*", "*"}, + { "c:\\test\\a*b.*", "c:\\test\\a*b.*", "a*b.*"}, + { "c:\\test\\a*b*.", "c:\\test\\a*b*", "a*b*"}, { "C:\\test", "C:\\test", "test"}, { "c:/", "c:\\", NULL}, { "c:.", "C:\\windows", "windows"}, @@ -440,6 +443,7 @@ static void test_RtlDosPathNameToNtPathName_U(void) tests[] = { {L"c:\\", L"\\??\\c:\\", -1}, + {L"c:\\test\\*.", L"\\??\\c:\\test\\*", 12}, {L"c:/", L"\\??\\c:\\", -1}, {L"c:/foo", L"\\??\\c:\\foo", 7}, {L"c:/foo.", L"\\??\\c:\\foo", 7}, From 80e524ce33c0762b1d3ac18c33929f592fe9afe7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Aug 2023 13:19:51 -0600 Subject: [PATCH 0612/1148] ntdll/tests: Test NtQueryDirectoryFile with wildcards. (cherry picked from commit 98c929d73e684f42c5d77044e7fa2fe27d4c5cec) CW-Bug-Id: #22684 --- dlls/ntdll/tests/directory.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/dlls/ntdll/tests/directory.c b/dlls/ntdll/tests/directory.c index a5ea7900f8d..9ff5c7a2bc2 100644 --- a/dlls/ntdll/tests/directory.c +++ b/dlls/ntdll/tests/directory.c @@ -70,6 +70,7 @@ static struct testfile_s { { 0, FILE_ATTRIBUTE_NORMAL, {0xe9,'a','.','t','m','p'}, "normal" }, { 0, FILE_ATTRIBUTE_NORMAL, {0xc9,'b','.','t','m','p'}, "normal" }, { 0, FILE_ATTRIBUTE_NORMAL, {'e','a','.','t','m','p'}, "normal" }, + { 0, FILE_ATTRIBUTE_NORMAL, {'e','a'}, "normal" }, { 0, FILE_ATTRIBUTE_DIRECTORY, {'.'}, ". directory" }, { 0, FILE_ATTRIBUTE_DIRECTORY, {'.','.'}, ".. directory" } }; @@ -237,13 +238,13 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char } ok(numfiles < max_test_dir_size, "too many loops\n"); - if (mask) + if (mask && !wcspbrk( mask->Buffer, L"*?" )) for (i = 0; i < test_dir_count; i++) ok(testfiles[i].nfound == (testfiles[i].name == mask->Buffer), "Wrong number %d of %s files found (single_entry=%d,mask=%s)\n", testfiles[i].nfound, testfiles[i].description, single_entry, wine_dbgstr_wn(mask->Buffer, mask->Length/sizeof(WCHAR) )); - else + else if (!mask) for (i = 0; i < test_dir_count; i++) ok(testfiles[i].nfound == 1, "Wrong number %d of %s files found (single_entry=%d,restart=%d)\n", testfiles[i].nfound, testfiles[i].description, single_entry, restart_flag); @@ -448,11 +449,27 @@ static void test_NtQueryDirectoryFile_classes( HANDLE handle, UNICODE_STRING *ma static void test_NtQueryDirectoryFile(void) { + static const struct + { + const WCHAR *mask; + int found[ARRAY_SIZE(testfiles)]; + BOOL todo_missing; + } + mask_tests[] = + { + {L"*.", {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1}, TRUE}, + {L"*.*", {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1}, TRUE}, + {L"*.**", {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1}, TRUE}, + {L"*", {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, + {L"**", {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, + {L"??.???", {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}}, + }; + OBJECT_ATTRIBUTES attr; UNICODE_STRING ntdirname, mask; char testdirA[MAX_PATH], buffer[MAX_PATH]; WCHAR testdirW[MAX_PATH]; - int i; + int i, j; IO_STATUS_BLOCK io; WCHAR short_name[12]; UINT data_size; @@ -494,6 +511,19 @@ static void test_NtQueryDirectoryFile(void) test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, FALSE); } + for (i = 0; i < ARRAY_SIZE(mask_tests); ++i) + { + winetest_push_context("mask %s", debugstr_w(mask_tests[i].mask)); + RtlInitUnicodeString(&mask, mask_tests[i].mask); + test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, TRUE); + for (j = 0; j < test_dir_count; j++) + { + todo_wine_if(mask_tests[i].todo_missing && !mask_tests[i].found[j]) + ok(testfiles[j].nfound == mask_tests[i].found[j], "%S, got %d.\n", testfiles[j].name, testfiles[j].nfound); + } + winetest_pop_context(); + } + /* short path passed as mask */ status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE); From 063c8a7bcd0a2473cb7def0c40bbef459a6cc731 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 31 Aug 2023 11:59:14 -0600 Subject: [PATCH 0613/1148] kernel32/tests: Add more tests for FindFirstFile with wildcards. (cherry picked from commit 6f3c76c9a576b2e3c424124aa586b8f8858426c7) CW-Bug-Id: #22684 --- dlls/kernel32/tests/file.c | 39 +++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 73ae75d52af..f2751c51e65 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -2963,38 +2963,43 @@ static void test_FindFirstFile_wildcards(void) int i; static const char* files[] = { "..a", "..a.a", ".a", ".a..a", ".a.a", ".aaa", - "a", "a..a", "a.a", "a.a.a", "aa", "aaa", "aaaa" + "a", "a..a", "a.a", "a.a.a", "aa", "aaa", "aaaa", " .a" }; static const struct { int todo; const char *pattern, *result; } tests[] = { - {0, "*.*.*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, - {0, "*.*.", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, + {0, "*.*.*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, + {0, "*.*.", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {0, ".*.*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa'"}, - {0, "*.*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, + {0, "*.*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {0, ".*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa'"}, {1, "*.", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, - {0, "*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, + {0, "*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {1, "*..*", ", '.', '..', '..a', '..a.a', '.a..a', 'a..a'"}, {1, "*..", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, {1, ".*.", ", '.', '..', '.a', '.aaa'"}, {0, "..*", ", '.', '..', '..a', '..a.a'"}, - {0, "**", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, - {0, "**.", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, - {0, "*. ", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, + {0, "**", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, + {0, "**.", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, + {0, "*. ", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {1, "* .", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, - {0, "* . ", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, - {0, "*.. ", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, + {0, "* . ", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, + {0, "*.. ", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {1, "*. .", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, {1, "* ..", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, - {0, " *..", ""}, + {1, " *..", ""}, {0, "..* ", ", '.', '..', '..a', '..a.a'"}, + {1, "a*.", ", '..a', '.a', '.aaa', 'a', 'aa', 'aaa', 'aaaa'"}, + {0, "*a ", ", '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, + + /* a.a.a not found due to short name mismatch, a.a.a -> "AA6BF5~1.A on Windows. */ + {1, "*aa*", ", '.aaa', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, {1, "<.<.<", ", '..a', '..a.a', '.a..a', '.a.a', 'a..a', 'a.a.a'"}, - {1, "<.<.", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a..a', 'a.a', 'a.a.a'"}, + {1, "<.<.", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a..a', 'a.a', 'a.a.a', ' .a'"}, {1, ".<.<", ", '..a', '..a.a', '.a..a', '.a.a'"}, - {1, "<.<", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a..a', 'a.a', 'a.a.a'"}, + {1, "<.<", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a..a', 'a.a', 'a.a.a', ' .a'"}, {1, ".<", ", '.', '..', '.a', '.aaa'"}, {1, "<.", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, {1, "<", ", '.', '..', '..a', '.a', '.aaa', 'a', 'aa', 'aaa', 'aaaa'"}, @@ -3002,8 +3007,8 @@ static void test_FindFirstFile_wildcards(void) {1, "<..", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, {1, ".<.", ", '.', '..', '.a', '.aaa'"}, {1, "..<", ", '..a'"}, - {1, "<<", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, - {1, "<<.", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa'"}, + {1, "<<", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, + {1, "<<.", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {1, "<. ", ", '.', '..', '..a', '.a', '.aaa', 'a', 'aa', 'aaa', 'aaaa'"}, {1, "< .", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, {1, "< . ", ", '.', '..', '..a', '.a', '.aaa', 'a', 'aa', 'aaa', 'aaaa'"}, @@ -3019,7 +3024,7 @@ static void test_FindFirstFile_wildcards(void) {1, "??.", ", '.', '..', 'a', 'aa'"}, {1, "??. ", ", '.', '..', 'a', 'aa'"}, {1, "???.", ", '.', '..', 'a', 'aa', 'aaa'"}, - {1, "?.??.", ", '.', '..', '.a', 'a', 'a.a'"}, + {1, "?.??.", ", '.', '..', '.a', 'a', 'a.a', ' .a'"}, {1, ">", ", '.', '..', 'a'"}, {1, ">.", ", '.', '..', 'a'"}, @@ -3027,7 +3032,7 @@ static void test_FindFirstFile_wildcards(void) {1, ">>.", ", '.', '..', 'a', 'aa'"}, {1, ">>. ", ", '.', '..', 'a', 'aa'"}, {1, ">>>.", ", '.', '..', 'a', 'aa', 'aaa'"}, - {1, ">.>>.", ", '.', '..', '.a', 'a.a'"}, + {1, ">.>>.", ", '.', '..', '.a', 'a.a', ' .a'"}, }; CreateDirectoryA("test-dir", NULL); From 7f395dbb9131e6c9b8434f3410b80772efa21df2 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Aug 2023 16:10:44 -0600 Subject: [PATCH 0614/1148] kernelbase: Duplicate file name matching code for FindNextFileW(). (cherry picked from commit 68fecd77eb103b6a6cfa53f8de559fb28b29b7b6) CW-Bug-Id: #22684 --- dlls/kernelbase/file.c | 97 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 0088bc8747b..4d94945cf4e 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -60,6 +60,7 @@ typedef struct UINT data_pos; /* current position in dir data */ UINT data_len; /* length of dir data */ UINT data_size; /* size of data buffer, or 0 when everything has been read */ + WCHAR *mask; /* mask string to match if wildcards are used */ BYTE data[1]; /* directory data */ } FIND_FIRST_INFO; @@ -1127,7 +1128,7 @@ HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_ OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK io; NTSTATUS status; - DWORD size, device = 0; + DWORD size, mask_size = 0, device = 0; TRACE( "%s %d %p %d %p %lx\n", debugstr_w(filename), level, data, search_op, filter, flags ); @@ -1189,10 +1190,15 @@ HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_ { nt_name.Length = (mask - nt_name.Buffer) * sizeof(WCHAR); has_wildcard = wcspbrk( mask, L"*?" ) != NULL; - size = has_wildcard ? 8192 : max_entry_size; + if (has_wildcard) + { + size = 8192; + mask_size = (lstrlenW( mask ) + 1) * sizeof(*mask); + } + else size = max_entry_size; } - if (!(info = HeapAlloc( GetProcessHeap(), 0, offsetof( FIND_FIRST_INFO, data[size] )))) + if (!(info = HeapAlloc( GetProcessHeap(), 0, offsetof( FIND_FIRST_INFO, data[size + mask_size] )))) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); goto error; @@ -1236,6 +1242,13 @@ HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_ info->data_size = size; info->search_op = search_op; info->level = level; + if (mask_size) + { + info->mask = (WCHAR *)(info->data + size); + memcpy( info->mask, mask, mask_size ); + mask = NULL; + } + else info->mask = NULL; if (device) { @@ -1253,7 +1266,7 @@ HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_ RtlInitUnicodeString( &mask_str, mask ); status = NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size, - FileBothDirectoryInformation, FALSE, &mask_str, TRUE ); + FileBothDirectoryInformation, FALSE, has_wildcard ? NULL : &mask_str, TRUE ); if (status) { FindClose( info ); @@ -1336,6 +1349,74 @@ BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *da } +/*********************************************************************** + * match_filename + * + * Check if the file name matches mask containing wildcards. + */ +static BOOL match_filename( const WCHAR *name, int length, const WCHAR *mask ) +{ + BOOL mismatch; + const WCHAR *name_end = name + length; + const WCHAR *mask_end = mask + lstrlenW( mask ); + const WCHAR *lastjoker = NULL; + const WCHAR *next_to_retry = NULL; + + while (name < name_end && mask < mask_end) + { + switch(*mask) + { + case '*': + mask++; + while (mask < mask_end && *mask == '*') mask++; + if (mask == mask_end) return TRUE; /* end of mask is all '*', so match */ + lastjoker = mask; + + /* skip to the next match after the joker(s) */ + while (name < name_end && towupper( *name ) != towupper( *mask )) name++; + next_to_retry = name; + break; + case '?': + case '>': + mask++; + name++; + break; + default: + mismatch = towupper( *mask ) != towupper( *name ); + + if (!mismatch) + { + mask++; + name++; + if (mask == mask_end) + { + if (name == name_end) return TRUE; + if (lastjoker) mask = lastjoker; + } + } + else /* mismatch ! */ + { + if (lastjoker) /* we had an '*', so we can try unlimitedly */ + { + mask = lastjoker; + + /* this scan sequence was a mismatch, so restart + * 1 char after the first char we checked last time */ + next_to_retry++; + name = next_to_retry; + } + else return FALSE; + } + break; + } + } + + while (mask < mask_end && (*mask == '.' || *mask == '*')) + mask++; + return (name == name_end && mask == mask_end); +} + + /****************************************************************************** * FindNextFileW (kernelbase.@) */ @@ -1395,6 +1476,14 @@ BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *da dir_info->FileName[0] == '.' && dir_info->FileName[1] == '.') continue; } + if (info->mask) + { + if (!match_filename( dir_info->FileName, dir_info->FileNameLength / sizeof(WCHAR), info->mask ) + && (!dir_info->ShortNameLength + || !match_filename( dir_info->ShortName, dir_info->ShortNameLength / sizeof(WCHAR), info->mask ))) + continue; + } + data->dwFileAttributes = dir_info->FileAttributes; data->ftCreationTime = *(FILETIME *)&dir_info->CreationTime; data->ftLastAccessTime = *(FILETIME *)&dir_info->LastAccessTime; From 776c31d4f8d975432ce2f70f2ab14918583c0113 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Aug 2023 18:36:25 -0600 Subject: [PATCH 0615/1148] kernelbase: Get unprocessed mask in FindFirstFileExW(). (cherry picked from commit a90f695f78e67b0e4cdd330c8898a72be4c7b09a) CW-Bug-Id: #22684 --- dlls/kernel32/tests/file.c | 4 ++-- dlls/kernelbase/file.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index f2751c51e65..35d568a24a1 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -3019,8 +3019,8 @@ static void test_FindFirstFile_wildcards(void) {1, "..< ", ", '..a'"}, {1, "?", ", '.', '..', 'a'"}, - {1, "?.", ", '.', '..', 'a'"}, - {1, "?. ", ", '.', '..', 'a'"}, + {0, "?.", ", '.', '..', 'a'"}, + {0, "?. ", ", '.', '..', 'a'"}, {1, "??.", ", '.', '..', 'a', 'aa'"}, {1, "??. ", ", '.', '..', 'a', 'aa'"}, {1, "???.", ", '.', '..', 'a', 'aa', 'aaa'"}, diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 4d94945cf4e..470fdd31423 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -1193,6 +1193,7 @@ HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_ if (has_wildcard) { size = 8192; + mask = PathFindFileNameW( filename ); mask_size = (lstrlenW( mask ) + 1) * sizeof(*mask); } else size = max_entry_size; @@ -1411,7 +1412,7 @@ static BOOL match_filename( const WCHAR *name, int length, const WCHAR *mask ) } } - while (mask < mask_end && (*mask == '.' || *mask == '*')) + while (mask < mask_end && (*mask == ' ' || *mask == '.' || *mask == '*')) mask++; return (name == name_end && mask == mask_end); } From 0156602dd2041a292a1613e74f47eb49be6823ba Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Aug 2023 21:07:19 -0600 Subject: [PATCH 0616/1148] kernelbase: Support masks suggesting no extension in FindNextFile(). (cherry picked from commit 9ff0569667e2eaa84ddbb99303231ecd1d6e0d99) CW-Bug-Id: #22684 --- dlls/kernel32/tests/file.c | 12 ++++++------ dlls/kernelbase/file.c | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 35d568a24a1..66be69409b0 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -2974,21 +2974,21 @@ static void test_FindFirstFile_wildcards(void) {0, ".*.*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa'"}, {0, "*.*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {0, ".*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa'"}, - {1, "*.", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, + {0, "*.", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, {0, "*", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {1, "*..*", ", '.', '..', '..a', '..a.a', '.a..a', 'a..a'"}, - {1, "*..", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, + {0, "*..", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, {1, ".*.", ", '.', '..', '.a', '.aaa'"}, {0, "..*", ", '.', '..', '..a', '..a.a'"}, {0, "**", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {0, "**.", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {0, "*. ", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, - {1, "* .", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, + {0, "* .", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, {0, "* . ", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {0, "*.. ", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, - {1, "*. .", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, - {1, "* ..", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, - {1, " *..", ""}, + {0, "*. .", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, + {0, "* ..", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, + {0, " *..", ""}, {0, "..* ", ", '.', '..', '..a', '..a.a'"}, {1, "a*.", ", '..a', '.a', '.aaa', 'a', 'aa', 'aaa', 'aaaa'"}, {0, "*a ", ", '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 470fdd31423..a497904b9d3 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -1350,6 +1350,19 @@ BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *da } +/*********************************************************************** + * name_has_ext + * + * Check if the file name has extension (skipping leading dots). + */ +static BOOL name_has_ext( const WCHAR *name, const WCHAR *name_end ) +{ + while (name != name_end && *name == '.') ++name; + while (name != name_end && *name != '.') ++name; + return name != name_end; +} + + /*********************************************************************** * match_filename * @@ -1362,6 +1375,14 @@ static BOOL match_filename( const WCHAR *name, int length, const WCHAR *mask ) const WCHAR *mask_end = mask + lstrlenW( mask ); const WCHAR *lastjoker = NULL; const WCHAR *next_to_retry = NULL; + const WCHAR *asterisk; + + if (mask != mask_end && mask_end[-1] == '.' && (asterisk = wcschr( mask, '*' )) && asterisk == wcsrchr( mask, '*' ) + && name_has_ext( name, name_end )) + { + /* Single '*' mask ending with '.' only matches files without extension. */ + return FALSE; + } while (name < name_end && mask < mask_end) { From 92a21d1b622952462e392b2f320dc9e54ed2b8d3 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 15 Aug 2023 11:22:40 -0600 Subject: [PATCH 0617/1148] Revert "mfplat/buffer: Use appropriate Map flags." This reverts commit 737c30ae820e7bcc20d8f0db0ef6a0a7f7eb31cd. CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 70 ++++---------------------------------------- 1 file changed, 5 insertions(+), 65 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 6cdc57692a0..97c1ceb5d44 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -967,27 +967,8 @@ static HRESULT dxgi_surface_buffer_create_readback_texture(struct buffer *buffer { D3D11_TEXTURE2D_DESC texture_desc; ID3D11Device *device; - UINT cpu_usage; HRESULT hr; - switch (buffer->_2d.lock_flags) - { - case MF2DBuffer_LockFlags_Read: - cpu_usage = D3D11_CPU_ACCESS_READ; - break; - - case MF2DBuffer_LockFlags_Write: - cpu_usage = D3D11_CPU_ACCESS_WRITE; - break; - - case MF2DBuffer_LockFlags_ReadWrite: - cpu_usage = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; - break; - - default: - return E_INVALIDARG; - } - if (buffer->dxgi_surface.rb_texture) return S_OK; @@ -996,7 +977,7 @@ static HRESULT dxgi_surface_buffer_create_readback_texture(struct buffer *buffer ID3D11Texture2D_GetDesc(buffer->dxgi_surface.texture, &texture_desc); texture_desc.Usage = D3D11_USAGE_STAGING; texture_desc.BindFlags = 0; - texture_desc.CPUAccessFlags = cpu_usage; + texture_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; texture_desc.MiscFlags = 0; texture_desc.MipLevels = 1; if (FAILED(hr = ID3D11Device_CreateTexture2D(device, &texture_desc, NULL, &buffer->dxgi_surface.rb_texture))) @@ -1011,30 +992,7 @@ static HRESULT dxgi_surface_buffer_map(struct buffer *buffer) { ID3D11DeviceContext *immediate_context; ID3D11Device *device; - D3D11_MAP map_type; HRESULT hr; - BOOL copy; - - switch (buffer->_2d.lock_flags) - { - case MF2DBuffer_LockFlags_Read: - map_type = D3D11_MAP_READ; - copy = TRUE; - break; - - case MF2DBuffer_LockFlags_Write: - map_type = D3D11_MAP_WRITE; - copy = FALSE; - break; - - case MF2DBuffer_LockFlags_ReadWrite: - map_type = D3D11_MAP_READ_WRITE; - copy = TRUE; - break; - - default: - return E_INVALIDARG; - } if (FAILED(hr = dxgi_surface_buffer_create_readback_texture(buffer))) return hr; @@ -1042,15 +1000,16 @@ static HRESULT dxgi_surface_buffer_map(struct buffer *buffer) ID3D11Texture2D_GetDevice(buffer->dxgi_surface.texture, &device); ID3D11Device_GetImmediateContext(device, &immediate_context); - if (copy) + if (buffer->_2d.lock_flags == MF2DBuffer_LockFlags_Read || buffer->_2d.lock_flags == MF2DBuffer_LockFlags_ReadWrite) { ID3D11DeviceContext_CopySubresourceRegion(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, 0, 0, 0, 0, (ID3D11Resource *)buffer->dxgi_surface.texture, buffer->dxgi_surface.sub_resource_idx, NULL); } + // TODO: fix _Map flags memset(&buffer->dxgi_surface.map_desc, 0, sizeof(buffer->dxgi_surface.map_desc)); if (FAILED(hr = ID3D11DeviceContext_Map(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, - 0, map_type, 0, &buffer->dxgi_surface.map_desc))) + 0, D3D11_MAP_READ_WRITE, 0, &buffer->dxgi_surface.map_desc))) { WARN("Failed to map readback texture, hr %#lx.\n", hr); } @@ -1065,32 +1024,13 @@ static void dxgi_surface_buffer_unmap(struct buffer *buffer) { ID3D11DeviceContext *immediate_context; ID3D11Device *device; - BOOL copy; - - switch (buffer->_2d.lock_flags) - { - case MF2DBuffer_LockFlags_Read: - copy = FALSE; - break; - - case MF2DBuffer_LockFlags_Write: - copy = TRUE; - break; - - case MF2DBuffer_LockFlags_ReadWrite: - copy = TRUE; - break; - - default: - copy = FALSE; - } ID3D11Texture2D_GetDevice(buffer->dxgi_surface.texture, &device); ID3D11Device_GetImmediateContext(device, &immediate_context); ID3D11DeviceContext_Unmap(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, 0); memset(&buffer->dxgi_surface.map_desc, 0, sizeof(buffer->dxgi_surface.map_desc)); - if (copy) + if (buffer->_2d.lock_flags == MF2DBuffer_LockFlags_Write || buffer->_2d.lock_flags == MF2DBuffer_LockFlags_ReadWrite) { ID3D11DeviceContext_CopySubresourceRegion(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.texture, buffer->dxgi_surface.sub_resource_idx, 0, 0, 0, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, 0, NULL); From e07ed9f6e80d712cf0429b1c04d2fe7e67940d34 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 15 Aug 2023 11:22:41 -0600 Subject: [PATCH 0618/1148] Revert "mfplat/buffer: Optimize locking a DXGI surface buffer." This reverts commit c42c848ff6fe77f9aa4c9261920642217dbf5995. CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 97c1ceb5d44..94f34b236f5 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -56,7 +56,6 @@ struct buffer unsigned int height; int pitch; unsigned int locks; - MF2DBuffer_LockFlags lock_flags; p_copy_image_func copy_image; } _2d; struct @@ -999,14 +998,9 @@ static HRESULT dxgi_surface_buffer_map(struct buffer *buffer) ID3D11Texture2D_GetDevice(buffer->dxgi_surface.texture, &device); ID3D11Device_GetImmediateContext(device, &immediate_context); + ID3D11DeviceContext_CopySubresourceRegion(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, + 0, 0, 0, 0, (ID3D11Resource *)buffer->dxgi_surface.texture, buffer->dxgi_surface.sub_resource_idx, NULL); - if (buffer->_2d.lock_flags == MF2DBuffer_LockFlags_Read || buffer->_2d.lock_flags == MF2DBuffer_LockFlags_ReadWrite) - { - ID3D11DeviceContext_CopySubresourceRegion(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, - 0, 0, 0, 0, (ID3D11Resource *)buffer->dxgi_surface.texture, buffer->dxgi_surface.sub_resource_idx, NULL); - } - - // TODO: fix _Map flags memset(&buffer->dxgi_surface.map_desc, 0, sizeof(buffer->dxgi_surface.map_desc)); if (FAILED(hr = ID3D11DeviceContext_Map(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, 0, D3D11_MAP_READ_WRITE, 0, &buffer->dxgi_surface.map_desc))) @@ -1030,11 +1024,8 @@ static void dxgi_surface_buffer_unmap(struct buffer *buffer) ID3D11DeviceContext_Unmap(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, 0); memset(&buffer->dxgi_surface.map_desc, 0, sizeof(buffer->dxgi_surface.map_desc)); - if (buffer->_2d.lock_flags == MF2DBuffer_LockFlags_Write || buffer->_2d.lock_flags == MF2DBuffer_LockFlags_ReadWrite) - { - ID3D11DeviceContext_CopySubresourceRegion(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.texture, - buffer->dxgi_surface.sub_resource_idx, 0, 0, 0, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, 0, NULL); - } + ID3D11DeviceContext_CopySubresourceRegion(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.texture, + buffer->dxgi_surface.sub_resource_idx, 0, 0, 0, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, 0, NULL); ID3D11DeviceContext_Release(immediate_context); ID3D11Device_Release(device); @@ -1062,7 +1053,6 @@ static HRESULT WINAPI dxgi_surface_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat if (SUCCEEDED(hr)) { - buffer->_2d.lock_flags = MF2DBuffer_LockFlags_ReadWrite; hr = dxgi_surface_buffer_map(buffer); if (SUCCEEDED(hr)) { @@ -1132,14 +1122,7 @@ static HRESULT dxgi_surface_buffer_lock(struct buffer *buffer, MF2DBuffer_LockFl if (buffer->_2d.linear_buffer) hr = MF_E_UNEXPECTED; else if (!buffer->_2d.locks++) - { - buffer->_2d.lock_flags = flags; hr = dxgi_surface_buffer_map(buffer); - } - else if (buffer->_2d.lock_flags != flags) - { - WARN("relocking DXGI surface buffer %p with different flags!\n", buffer); - } if (SUCCEEDED(hr)) { From 8d9f3fb0b63a0b810cfe2e2078b3b25d7fdc5106 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 15 Aug 2023 11:22:42 -0600 Subject: [PATCH 0619/1148] Revert "mfplat/buffer: Implement ContiguousCopyFrom()." This reverts commit 5cfa3a0c0d422b0e809e2fb849681a28570f7dac. CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 94f34b236f5..bdec2777c39 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -627,34 +627,9 @@ static HRESULT WINAPI memory_2d_buffer_ContiguousCopyTo(IMF2DBuffer2 *iface, BYT static HRESULT WINAPI memory_2d_buffer_ContiguousCopyFrom(IMF2DBuffer2 *iface, const BYTE *src_buffer, DWORD src_length) { - struct buffer *buffer = impl_from_IMF2DBuffer2(iface); - BYTE *dst_scanline0, *dst_buffer_start; - DWORD dst_length; - HRESULT hr, hr2; - LONG dst_pitch; - - TRACE("%p, %p, %lu.\n", iface, src_buffer, src_length); - - if (src_length != buffer->_2d.plane_size) - { - WARN("truncating contiguous copy not implemented\n"); - return E_NOTIMPL; - } - - hr = IMF2DBuffer2_Lock2DSize(iface, MF2DBuffer_LockFlags_Write, &dst_scanline0, &dst_pitch, &dst_buffer_start, &dst_length); + FIXME("%p, %p, %lu.\n", iface, src_buffer, src_length); - if (SUCCEEDED(hr)) - { - hr = copy_image(buffer, dst_scanline0, dst_pitch, src_buffer, buffer->_2d.width, buffer->_2d.width, buffer->_2d.height, buffer->max_length); - } - - hr2 = IMF2DBuffer2_Unlock2D(iface); - if (FAILED(hr2)) - WARN("Unlocking destination buffer %p failed with hr %#lx.\n", iface, hr2); - if (FAILED(hr2) && SUCCEEDED(hr)) - hr = hr2; - - return hr; + return E_NOTIMPL; } static HRESULT WINAPI memory_2d_buffer_Lock2DSize(IMF2DBuffer2 *iface, MF2DBuffer_LockFlags flags, BYTE **scanline0, From 9059288e88421ccd16284840b3d05fda6e90584c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 15 Aug 2023 11:22:43 -0600 Subject: [PATCH 0620/1148] Revert "mfplat: Implement IMF2DBuffer2::Copy2DTo()." This reverts commit 968ac2bffba1db38b9afd93bb4ef9770d1da21cf. CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 49 ++++---------------------------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index bdec2777c39..c617abf7ae0 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -652,52 +652,11 @@ static HRESULT WINAPI memory_2d_buffer_Lock2DSize(IMF2DBuffer2 *iface, MF2DBuffe return hr; } -static HRESULT WINAPI memory_2d_buffer_Copy2DTo(IMF2DBuffer2 *iface, IMF2DBuffer2 *dst_buffer) +static HRESULT WINAPI memory_2d_buffer_Copy2DTo(IMF2DBuffer2 *iface, IMF2DBuffer2 *dest_buffer) { - struct buffer *buffer = impl_from_IMF2DBuffer2(iface); - BYTE *src_scanline0, *dst_scanline0, *src_buffer_start, *dst_buffer_start; - DWORD src_length, dst_length; - LONG src_pitch, dst_pitch; - BOOL src_locked = FALSE, dst_locked = FALSE; - HRESULT hr, hr2; - - TRACE("%p, %p.\n", iface, dst_buffer); - - hr = IMF2DBuffer2_Lock2DSize(iface, MF2DBuffer_LockFlags_Read, &src_scanline0, - &src_pitch, &src_buffer_start, &src_length); - if (FAILED(hr)) - goto end; - src_locked = TRUE; + FIXME("%p, %p.\n", iface, dest_buffer); - hr = IMF2DBuffer2_Lock2DSize(dst_buffer, MF2DBuffer_LockFlags_Write, &dst_scanline0, - &dst_pitch, &dst_buffer_start, &dst_length); - if (FAILED(hr)) - goto end; - dst_locked = TRUE; - - hr = copy_image(buffer, dst_scanline0, dst_pitch, src_scanline0, src_pitch, - buffer->_2d.width, buffer->_2d.height, dst_length); - -end: - if (src_locked) - { - hr2 = IMF2DBuffer2_Unlock2D(iface); - if (FAILED(hr2)) - WARN("Unlocking source buffer %p failed with hr %#lx.\n", iface, hr2); - if (FAILED(hr2) && SUCCEEDED(hr)) - hr = hr2; - } - - if (dst_locked) - { - hr2 = IMF2DBuffer2_Unlock2D(dst_buffer); - if (FAILED(hr2)) - WARN("Unlocking destination buffer %p failed with hr %#lx.\n", dst_buffer, hr2); - if (FAILED(hr2) && SUCCEEDED(hr)) - hr = hr2; - } - - return hr; + return E_NOTIMPL; } static const IMF2DBuffer2Vtbl memory_2d_buffer_vtbl = @@ -1380,7 +1339,7 @@ static p_copy_image_func get_2d_buffer_copy_func(DWORD fourcc) return copy_image_nv12; if (fourcc == MAKEFOURCC('I','M','C','1') || fourcc == MAKEFOURCC('I','M','C','3')) return copy_image_imc1; - if (fourcc == MAKEFOURCC('I','M','C','2') || fourcc == MAKEFOURCC('I','M','C','4') || fourcc == MAKEFOURCC('Y','V','1', '2')) + if (fourcc == MAKEFOURCC('I','M','C','2') || fourcc == MAKEFOURCC('I','M','C','4')) return copy_image_imc2; return NULL; } From 691b2b726065cae48a56dd18528c28d11624b2d7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 15 Aug 2023 11:22:44 -0600 Subject: [PATCH 0621/1148] Revert "mfplat/buffer: Validate buffer size in image copying functions." This reverts commit eefc50a6d3bd6a08db1d4701442493448e6baf9d. CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index c617abf7ae0..b955a0ac8b9 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -80,9 +80,6 @@ static HRESULT copy_image(const struct buffer *buffer, BYTE *dest, LONG dest_str { HRESULT hr = S_OK; - if (width > abs(dest_stride) || abs(dest_stride) * lines > dest_size) - return E_NOT_SUFFICIENT_BUFFER; - hr = MFCopyImage(dest, dest_stride, src, src_stride, width, lines); if (SUCCEEDED(hr) && buffer->_2d.copy_image) @@ -98,27 +95,18 @@ static HRESULT copy_image(const struct buffer *buffer, BYTE *dest, LONG dest_str static HRESULT copy_image_nv12(BYTE *dest, LONG dest_stride, const BYTE *src, LONG src_stride, DWORD width, DWORD lines, DWORD dest_size) { - if (abs(dest_stride) * (lines + lines / 2) > dest_size) - return E_NOT_SUFFICIENT_BUFFER; - return MFCopyImage(dest, dest_stride, src, src_stride, width, lines / 2); } static HRESULT copy_image_imc1(BYTE *dest, LONG dest_stride, const BYTE *src, LONG src_stride, DWORD width, DWORD lines, DWORD dest_size) { - if (abs(dest_stride) * (2 * lines) > dest_size) - return E_NOT_SUFFICIENT_BUFFER; - return MFCopyImage(dest, dest_stride, src, src_stride, width / 2, lines); } static HRESULT copy_image_imc2(BYTE *dest, LONG dest_stride, const BYTE *src, LONG src_stride, DWORD width, DWORD lines, DWORD dest_size) { - if (abs(dest_stride) * 3 * lines / 2 > dest_size) - return E_NOT_SUFFICIENT_BUFFER; - return MFCopyImage(dest, dest_stride / 2, src, src_stride / 2, width / 2, lines); } From 399b5ea3f60dca2730f33e7f7b1ff0938edae4f0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 15 Aug 2023 11:22:49 -0600 Subject: [PATCH 0622/1148] Revert "mfplat/buffer: Prepare image copying functions for error checking." This reverts commit 3b621e598b0c7ce54610c5c81fcb3e9deea67e3b. CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 58 +++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index b955a0ac8b9..e3d38438b88 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -32,7 +32,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); #define ALIGN_SIZE(size, alignment) (((size) + (alignment)) & ~((alignment))) -typedef HRESULT (*p_copy_image_func)(BYTE *dest, LONG dest_stride, const BYTE *src, LONG src_stride, DWORD width, DWORD lines, DWORD dest_size); +typedef void (*p_copy_image_func)(BYTE *dest, LONG dest_stride, const BYTE *src, LONG src_stride, DWORD width, DWORD lines); struct buffer { @@ -75,39 +75,32 @@ struct buffer CRITICAL_SECTION cs; }; -static HRESULT copy_image(const struct buffer *buffer, BYTE *dest, LONG dest_stride, const BYTE *src, - LONG src_stride, DWORD width, DWORD lines, DWORD dest_size) +static void copy_image(const struct buffer *buffer, BYTE *dest, LONG dest_stride, const BYTE *src, + LONG src_stride, DWORD width, DWORD lines) { - HRESULT hr = S_OK; - - hr = MFCopyImage(dest, dest_stride, src, src_stride, width, lines); + MFCopyImage(dest, dest_stride, src, src_stride, width, lines); - if (SUCCEEDED(hr) && buffer->_2d.copy_image) + if (buffer->_2d.copy_image) { dest += dest_stride * lines; src += src_stride * lines; - hr = buffer->_2d.copy_image(dest, dest_stride, src, src_stride, width, lines, dest_size); + buffer->_2d.copy_image(dest, dest_stride, src, src_stride, width, lines); } - - return hr; } -static HRESULT copy_image_nv12(BYTE *dest, LONG dest_stride, const BYTE *src, - LONG src_stride, DWORD width, DWORD lines, DWORD dest_size) +static void copy_image_nv12(BYTE *dest, LONG dest_stride, const BYTE *src, LONG src_stride, DWORD width, DWORD lines) { - return MFCopyImage(dest, dest_stride, src, src_stride, width, lines / 2); + MFCopyImage(dest, dest_stride, src, src_stride, width, lines / 2); } -static HRESULT copy_image_imc1(BYTE *dest, LONG dest_stride, const BYTE *src, - LONG src_stride, DWORD width, DWORD lines, DWORD dest_size) +static void copy_image_imc1(BYTE *dest, LONG dest_stride, const BYTE *src, LONG src_stride, DWORD width, DWORD lines) { - return MFCopyImage(dest, dest_stride, src, src_stride, width / 2, lines); + MFCopyImage(dest, dest_stride, src, src_stride, width / 2, lines); } -static HRESULT copy_image_imc2(BYTE *dest, LONG dest_stride, const BYTE *src, - LONG src_stride, DWORD width, DWORD lines, DWORD dest_size) +static void copy_image_imc2(BYTE *dest, LONG dest_stride, const BYTE *src, LONG src_stride, DWORD width, DWORD lines) { - return MFCopyImage(dest, dest_stride / 2, src, src_stride / 2, width / 2, lines); + MFCopyImage(dest, dest_stride / 2, src, src_stride / 2, width / 2, lines); } static inline struct buffer *impl_from_IMFMediaBuffer(IMFMediaBuffer *iface) @@ -319,8 +312,8 @@ static HRESULT WINAPI memory_1d_2d_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat hr = E_OUTOFMEMORY; if (SUCCEEDED(hr)) - hr = copy_image(buffer, buffer->_2d.linear_buffer, buffer->_2d.width, buffer->data, buffer->_2d.pitch, - buffer->_2d.width, buffer->_2d.height, buffer->_2d.plane_size); + copy_image(buffer, buffer->_2d.linear_buffer, buffer->_2d.width, buffer->data, buffer->_2d.pitch, + buffer->_2d.width, buffer->_2d.height); } if (SUCCEEDED(hr)) @@ -341,7 +334,6 @@ static HRESULT WINAPI memory_1d_2d_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat static HRESULT WINAPI memory_1d_2d_buffer_Unlock(IMFMediaBuffer *iface) { struct buffer *buffer = impl_from_IMFMediaBuffer(iface); - HRESULT hr = S_OK; TRACE("%p.\n", iface); @@ -349,8 +341,8 @@ static HRESULT WINAPI memory_1d_2d_buffer_Unlock(IMFMediaBuffer *iface) if (buffer->_2d.linear_buffer && !--buffer->_2d.locks) { - hr = copy_image(buffer, buffer->data, buffer->_2d.pitch, buffer->_2d.linear_buffer, buffer->_2d.width, - buffer->_2d.width, buffer->_2d.height, buffer->max_length); + copy_image(buffer, buffer->data, buffer->_2d.pitch, buffer->_2d.linear_buffer, buffer->_2d.width, + buffer->_2d.width, buffer->_2d.height); free(buffer->_2d.linear_buffer); buffer->_2d.linear_buffer = NULL; @@ -358,7 +350,7 @@ static HRESULT WINAPI memory_1d_2d_buffer_Unlock(IMFMediaBuffer *iface) LeaveCriticalSection(&buffer->cs); - return hr; + return S_OK; } static const IMFMediaBufferVtbl memory_1d_2d_buffer_vtbl = @@ -399,8 +391,8 @@ static HRESULT WINAPI d3d9_surface_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat hr = IDirect3DSurface9_LockRect(buffer->d3d9_surface.surface, &rect, NULL, 0); if (SUCCEEDED(hr)) { - hr = copy_image(buffer, buffer->_2d.linear_buffer, buffer->_2d.width, rect.pBits, rect.Pitch, - buffer->_2d.width, buffer->_2d.height, buffer->_2d.plane_size); + copy_image(buffer, buffer->_2d.linear_buffer, buffer->_2d.width, rect.pBits, rect.Pitch, + buffer->_2d.width, buffer->_2d.height); IDirect3DSurface9_UnlockRect(buffer->d3d9_surface.surface); } } @@ -438,8 +430,8 @@ static HRESULT WINAPI d3d9_surface_buffer_Unlock(IMFMediaBuffer *iface) if (SUCCEEDED(hr = IDirect3DSurface9_LockRect(buffer->d3d9_surface.surface, &rect, NULL, 0))) { - hr = copy_image(buffer, rect.pBits, rect.Pitch, buffer->_2d.linear_buffer, buffer->_2d.width, - buffer->_2d.width, buffer->_2d.height, buffer->max_length); + copy_image(buffer, rect.pBits, rect.Pitch, buffer->_2d.linear_buffer, buffer->_2d.width, + buffer->_2d.width, buffer->_2d.height); IDirect3DSurface9_UnlockRect(buffer->d3d9_surface.surface); } @@ -978,8 +970,8 @@ static HRESULT WINAPI dxgi_surface_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat hr = dxgi_surface_buffer_map(buffer); if (SUCCEEDED(hr)) { - hr = copy_image(buffer, buffer->_2d.linear_buffer, buffer->_2d.width, buffer->dxgi_surface.map_desc.pData, - buffer->dxgi_surface.map_desc.RowPitch, buffer->_2d.width, buffer->_2d.height, buffer->_2d.plane_size); + copy_image(buffer, buffer->_2d.linear_buffer, buffer->_2d.width, buffer->dxgi_surface.map_desc.pData, + buffer->dxgi_surface.map_desc.RowPitch, buffer->_2d.width, buffer->_2d.height); } } } @@ -1012,8 +1004,8 @@ static HRESULT WINAPI dxgi_surface_buffer_Unlock(IMFMediaBuffer *iface) hr = HRESULT_FROM_WIN32(ERROR_WAS_UNLOCKED); else if (!--buffer->_2d.locks) { - hr = copy_image(buffer, buffer->dxgi_surface.map_desc.pData, buffer->dxgi_surface.map_desc.RowPitch, - buffer->_2d.linear_buffer, buffer->_2d.width, buffer->_2d.width, buffer->_2d.height, buffer->max_length); + copy_image(buffer, buffer->dxgi_surface.map_desc.pData, buffer->dxgi_surface.map_desc.RowPitch, + buffer->_2d.linear_buffer, buffer->_2d.width, buffer->_2d.width, buffer->_2d.height); dxgi_surface_buffer_unmap(buffer); free(buffer->_2d.linear_buffer); From f7598bc02d8dbbd729a7ae78e14f0507cd5629c7 Mon Sep 17 00:00:00 2001 From: Ziqing Hui Date: Wed, 21 Dec 2022 16:57:36 +0800 Subject: [PATCH 0623/1148] mfplat: Support YVYU, NV11, MEDIASUBTYPE_RGB* media types. (cherry picked from commit df26eae97dce7907cff087bde36775504fcb5c3e) CW-Bug-Id: #22084 --- dlls/mf/tests/transform.c | 4 ++-- dlls/mfplat/buffer.c | 3 +++ dlls/mfplat/mediatype.c | 11 +++++++++++ dlls/mfplat/tests/mfplat.c | 9 --------- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 36181c33d20..60ea02313ac 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -5836,8 +5836,8 @@ static void test_video_processor(void) && !IsEqualGUID(&guid, &MEDIASUBTYPE_Y42T)) { hr = MFCalculateImageSize(&guid, 16, 16, (UINT32 *)&input_info.cbSize); - todo_wine_if(IsEqualGUID(&guid, &MFVideoFormat_NV11) || IsEqualGUID(&guid, &MFVideoFormat_YVYU) - || IsEqualGUID(&guid, &MFVideoFormat_Y216) || IsEqualGUID(&guid, &MFVideoFormat_v410) + todo_wine_if(IsEqualGUID(&guid, &MFVideoFormat_Y216) + || IsEqualGUID(&guid, &MFVideoFormat_v410) || IsEqualGUID(&guid, &MFVideoFormat_Y41P)) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); } diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index e3d38438b88..a221c6d55b8 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -1357,6 +1357,7 @@ static HRESULT create_2d_buffer(DWORD width, DWORD height, DWORD fourcc, BOOL bo break; case MAKEFOURCC('I','M','C','2'): case MAKEFOURCC('I','M','C','4'): + case MAKEFOURCC('N','V','1','1'): plane_size = stride * 3 / 2 * height; break; case MAKEFOURCC('N','V','1','2'): @@ -1379,6 +1380,7 @@ static HRESULT create_2d_buffer(DWORD width, DWORD height, DWORD fourcc, BOOL bo case MAKEFOURCC('I','M','C','3'): case MAKEFOURCC('I','M','C','4'): case MAKEFOURCC('Y','V','1','2'): + case MAKEFOURCC('N','V','1','1'): row_alignment = MF_128_BYTE_ALIGNMENT; break; default: @@ -1397,6 +1399,7 @@ static HRESULT create_2d_buffer(DWORD width, DWORD height, DWORD fourcc, BOOL bo case MAKEFOURCC('Y','V','1','2'): case MAKEFOURCC('I','M','C','2'): case MAKEFOURCC('I','M','C','4'): + case MAKEFOURCC('N','V','1','1'): max_length = pitch * height * 3 / 2; break; default: diff --git a/dlls/mfplat/mediatype.c b/dlls/mfplat/mediatype.c index 2e16de96849..374024d8190 100644 --- a/dlls/mfplat/mediatype.c +++ b/dlls/mfplat/mediatype.c @@ -2713,13 +2713,20 @@ static const struct uncompressed_video_format video_formats[] = { &MFVideoFormat_IMC3, 2, 3, 0, 1 }, { &MFVideoFormat_IMC4, 1, 0, 0, 1 }, { &MFVideoFormat_IYUV, 1, 0, 0, 1 }, + { &MFVideoFormat_NV11, 1, 0, 0, 1 }, { &MFVideoFormat_NV12, 1, 0, 0, 1 }, { &MFVideoFormat_D16, 2, 3, 0, 0 }, { &MFVideoFormat_L16, 2, 3, 0, 0 }, { &MFVideoFormat_UYVY, 2, 0, 0, 1 }, { &MFVideoFormat_YUY2, 2, 0, 0, 1 }, { &MFVideoFormat_YV12, 1, 0, 0, 1 }, + { &MFVideoFormat_YVYU, 2, 0, 0, 1 }, { &MFVideoFormat_A16B16G16R16F, 8, 3, 1, 0 }, + { &MEDIASUBTYPE_RGB8, 1, 3, 1, 0 }, + { &MEDIASUBTYPE_RGB565, 2, 3, 1, 0 }, + { &MEDIASUBTYPE_RGB555, 2, 3, 1, 0 }, + { &MEDIASUBTYPE_RGB24, 3, 3, 1, 0 }, + { &MEDIASUBTYPE_RGB32, 4, 3, 1, 0 }, }; static struct uncompressed_video_format *mf_get_video_format(const GUID *subtype) @@ -2799,6 +2806,9 @@ HRESULT WINAPI MFCalculateImageSize(REFGUID subtype, UINT32 width, UINT32 height /* 2 x 2 block, interleaving UV for half the height */ *size = ((width + 1) & ~1) * height * 3 / 2; break; + case MAKEFOURCC('N','V','1','1'): + *size = ((width + 3) & ~3) * height * 3 / 2; + break; case D3DFMT_L8: case D3DFMT_L16: case D3DFMT_D16: @@ -2839,6 +2849,7 @@ HRESULT WINAPI MFGetPlaneSize(DWORD fourcc, DWORD width, DWORD height, DWORD *si case MAKEFOURCC('Y','V','1','2'): case MAKEFOURCC('I','4','2','0'): case MAKEFOURCC('I','Y','U','V'): + case MAKEFOURCC('N','V','1','1'): *size = stride * height * 3 / 2; break; default: diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 2dd3e834a0e..9969fd7213e 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -4453,7 +4453,6 @@ static void test_MFCalculateImageSize(void) IsEqualGUID(ptr->subtype, &MFVideoFormat_A2R10G10B10); hr = MFCalculateImageSize(ptr->subtype, ptr->width, ptr->height, &size); - todo_wine_if(is_MEDIASUBTYPE_RGB(ptr->subtype) || IsEqualGUID(ptr->subtype, &MFVideoFormat_NV11)) ok(hr == S_OK || (is_broken && hr == E_INVALIDARG), "%u: failed to calculate image size, hr %#lx.\n", i, hr); todo_wine_if(is_MEDIASUBTYPE_RGB(ptr->subtype) || IsEqualGUID(ptr->subtype, &MFVideoFormat_NV11) @@ -5827,9 +5826,7 @@ static void test_MFGetStrideForBitmapInfoHeader(void) for (i = 0; i < ARRAY_SIZE(stride_tests); ++i) { hr = pMFGetStrideForBitmapInfoHeader(stride_tests[i].subtype->Data1, stride_tests[i].width, &stride); - todo_wine_if(IsEqualGUID(stride_tests[i].subtype, &MFVideoFormat_NV11)) ok(hr == S_OK, "%u: failed to get stride, hr %#lx.\n", i, hr); - todo_wine_if(IsEqualGUID(stride_tests[i].subtype, &MFVideoFormat_NV11)) ok(stride == stride_tests[i].stride, "%u: format %s, unexpected stride %ld, expected %ld.\n", i, wine_dbgstr_an((char *)&stride_tests[i].subtype->Data1, 4), stride, stride_tests[i].stride); } @@ -6042,10 +6039,7 @@ static void test_MFCreate2DMediaBuffer(void) continue; hr = pMFCreate2DMediaBuffer(ptr->width, ptr->height, ptr->subtype->Data1, FALSE, &buffer); - todo_wine_if(IsEqualGUID(ptr->subtype, &MFVideoFormat_NV11)) ok(hr == S_OK, "Failed to create a buffer, hr %#lx.\n", hr); - if (hr != S_OK) - continue; hr = IMFMediaBuffer_GetMaxLength(buffer, &length); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -6188,10 +6182,7 @@ static void test_MFCreate2DMediaBuffer(void) continue; hr = pMFCreate2DMediaBuffer(ptr->width, ptr->height, ptr->subtype->Data1, FALSE, &buffer); - todo_wine_if(IsEqualGUID(ptr->subtype, &MFVideoFormat_NV11)) ok(hr == S_OK, "Failed to create a buffer, hr %#lx.\n", hr); - if (hr != S_OK) - continue; hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer, (void **)&_2dbuffer); ok(hr == S_OK, "Failed to get interface, hr %#lx.\n", hr); From 11bc79706b17e0a4cdc34d9f3bfa90c14684f910 Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Thu, 13 Apr 2023 15:47:44 +0200 Subject: [PATCH 0624/1148] mfplat: Fix locking flags usage for D3D9 buffers. (cherry picked from commit 91c0eda70972f1d6421d9dbb035b3ca50aef8d76) CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 8 ++++++-- dlls/mfplat/tests/mfplat.c | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index a221c6d55b8..df85af37126 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -56,6 +56,7 @@ struct buffer unsigned int height; int pitch; unsigned int locks; + MF2DBuffer_LockFlags lock_flags; p_copy_image_func copy_image; } _2d; struct @@ -663,12 +664,14 @@ static HRESULT d3d9_surface_buffer_lock(struct buffer *buffer, MF2DBuffer_LockFl if (buffer->_2d.linear_buffer) hr = MF_E_UNEXPECTED; else if (!buffer->_2d.locks) - { hr = IDirect3DSurface9_LockRect(buffer->d3d9_surface.surface, &buffer->d3d9_surface.rect, NULL, 0); - } + else if (buffer->_2d.lock_flags == MF2DBuffer_LockFlags_Write && flags != MF2DBuffer_LockFlags_Write) + hr = HRESULT_FROM_WIN32(ERROR_WAS_LOCKED); if (SUCCEEDED(hr)) { + if (!buffer->_2d.locks) + buffer->_2d.lock_flags = flags; buffer->_2d.locks++; *scanline0 = buffer->d3d9_surface.rect.pBits; *pitch = buffer->d3d9_surface.rect.Pitch; @@ -715,6 +718,7 @@ static HRESULT WINAPI d3d9_surface_buffer_Unlock2D(IMF2DBuffer2 *iface) { IDirect3DSurface9_UnlockRect(buffer->d3d9_surface.surface); memset(&buffer->d3d9_surface.rect, 0, sizeof(buffer->d3d9_surface.rect)); + buffer->_2d.lock_flags = 0; } } else diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 9969fd7213e..ff2d6fa15aa 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -6706,7 +6706,39 @@ static void test_MFCreateDXSurfaceBuffer(void) hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_ReadWrite, &data, &pitch, &data2, &length); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMF2DBuffer2_Unlock2D(_2dbuffer2); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Write, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer_Lock2D(_2dbuffer, &data, &pitch); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Except when originally locking for writing. */ + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Write, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Write, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_ReadWrite, &data, &pitch, &data2, &length); + ok(hr == HRESULT_FROM_WIN32(ERROR_WAS_LOCKED), "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Read, &data, &pitch, &data2, &length); + ok(hr == HRESULT_FROM_WIN32(ERROR_WAS_LOCKED), "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer_Lock2D(_2dbuffer, &data, &pitch); + ok(hr == HRESULT_FROM_WIN32(ERROR_WAS_LOCKED), "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == HRESULT_FROM_WIN32(ERROR_WAS_UNLOCKED), "Unexpected hr %#lx.\n", hr); IMF2DBuffer2_Release(_2dbuffer2); From cab64e1bde59bf3581172c8958f7f76910083401 Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Mon, 17 Apr 2023 15:24:08 +0200 Subject: [PATCH 0625/1148] mfplat: Do not mark a DXGI buffer as locked if surface mapping fails. Analogous to 44c9ea50432771258e9eed012967a16c6f132fe9. (cherry picked from commit df660e80f07fd129a9fbbd3c01c153c6a2ebb558) CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index df85af37126..1c38e03c3e1 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -1039,11 +1039,12 @@ static HRESULT dxgi_surface_buffer_lock(struct buffer *buffer, MF2DBuffer_LockFl if (buffer->_2d.linear_buffer) hr = MF_E_UNEXPECTED; - else if (!buffer->_2d.locks++) + else if (!buffer->_2d.locks) hr = dxgi_surface_buffer_map(buffer); if (SUCCEEDED(hr)) { + buffer->_2d.locks++; *scanline0 = buffer->dxgi_surface.map_desc.pData; *pitch = buffer->dxgi_surface.map_desc.RowPitch; if (buffer_start) From 125a7e6d2f397c6da0231311ad7fa0355927e1e9 Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Mon, 17 Apr 2023 15:45:38 +0200 Subject: [PATCH 0626/1148] mfplat: Fix locking flags usage for DXGI buffers. (cherry picked from commit 071eb50ff08a8a73484830dbb37509a350f099ae) CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 7 +++ dlls/mfplat/tests/mfplat.c | 115 +++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 1c38e03c3e1..c8e797e6743 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -1041,9 +1041,13 @@ static HRESULT dxgi_surface_buffer_lock(struct buffer *buffer, MF2DBuffer_LockFl hr = MF_E_UNEXPECTED; else if (!buffer->_2d.locks) hr = dxgi_surface_buffer_map(buffer); + else if (buffer->_2d.lock_flags == MF2DBuffer_LockFlags_Write && flags != MF2DBuffer_LockFlags_Write) + hr = HRESULT_FROM_WIN32(ERROR_WAS_LOCKED); if (SUCCEEDED(hr)) { + if (!buffer->_2d.locks) + buffer->_2d.lock_flags = flags; buffer->_2d.locks++; *scanline0 = buffer->dxgi_surface.map_desc.pData; *pitch = buffer->dxgi_surface.map_desc.RowPitch; @@ -1087,7 +1091,10 @@ static HRESULT WINAPI dxgi_surface_buffer_Unlock2D(IMF2DBuffer2 *iface) if (buffer->_2d.locks) { if (!--buffer->_2d.locks) + { dxgi_surface_buffer_unmap(buffer); + buffer->_2d.lock_flags = 0; + } } else hr = HRESULT_FROM_WIN32(ERROR_WAS_UNLOCKED); diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index ff2d6fa15aa..f324379c387 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -7299,7 +7299,122 @@ static void test_d3d11_surface_buffer(void) hr = IMF2DBuffer_Unlock2D(_2d_buffer); ok(hr == HRESULT_FROM_WIN32(ERROR_WAS_UNLOCKED), "Unexpected hr %#lx.\n", hr); + hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMF2DBuffer_Lock2D(_2d_buffer, &data, &pitch); + ok(hr == MF_E_UNEXPECTED, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaBuffer_Unlock(buffer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMF2DBuffer_Release(_2d_buffer); + + hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer2, (void **)&_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Lock flags are honored, so reads and writes are discarded if + * the flags are not correct. */ + put_d3d11_texture_color(texture, 0, 0, 0xcdcdcdcd); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Read, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(data == data2, "Unexpected scanline pointer.\n"); + ok(*(DWORD *)data == 0xcdcdcdcd, "Unexpected leading dword %#lx.\n", *(DWORD *)data); + memset(data, 0xab, 4); + IMF2DBuffer2_Unlock2D(_2dbuffer2); + + color = get_d3d11_texture_color(texture, 0, 0); + todo_wine + ok(color == 0xcdcdcdcd, "Unexpected leading dword %#lx.\n", color); + put_d3d11_texture_color(texture, 0, 0, 0xefefefef); + + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Write, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(*(DWORD *)data != 0xefefefef, "Unexpected leading dword.\n"); + memset(data, 0x89, 4); + IMF2DBuffer2_Unlock2D(_2dbuffer2); + + color = get_d3d11_texture_color(texture, 0, 0); + ok(color == 0x89898989, "Unexpected leading dword %#lx.\n", color); + + /* When relocking for writing, stores are committed even if they + * were issued before relocking. */ + put_d3d11_texture_color(texture, 0, 0, 0xcdcdcdcd); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Read, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + memset(data, 0xab, 4); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Write, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMF2DBuffer2_Unlock2D(_2dbuffer2); + IMF2DBuffer2_Unlock2D(_2dbuffer2); + + color = get_d3d11_texture_color(texture, 0, 0); + ok(color == 0xabababab, "Unexpected leading dword %#lx.\n", color); + + /* Flags incompatibilities. */ + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_ReadWrite, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_ReadWrite, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Read, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Write, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer_Lock2D(_2d_buffer, &data, &pitch); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Read, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Read, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_ReadWrite, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Write, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer_Lock2D(_2d_buffer, &data, &pitch); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Except when originally locking for writing. */ + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Write, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Write, &data, &pitch, &data2, &length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_ReadWrite, &data, &pitch, &data2, &length); + ok(hr == HRESULT_FROM_WIN32(ERROR_WAS_LOCKED), "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Read, &data, &pitch, &data2, &length); + ok(hr == HRESULT_FROM_WIN32(ERROR_WAS_LOCKED), "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer_Lock2D(_2d_buffer, &data, &pitch); + ok(hr == HRESULT_FROM_WIN32(ERROR_WAS_LOCKED), "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == HRESULT_FROM_WIN32(ERROR_WAS_UNLOCKED), "Unexpected hr %#lx.\n", hr); + + IMF2DBuffer2_Release(_2dbuffer2); IMFMediaBuffer_Release(buffer); /* Bottom up. */ From b88c73d801bc6c3b706c35c93ac6305a5418c6c8 Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Mon, 17 Apr 2023 15:52:17 +0200 Subject: [PATCH 0627/1148] mfplat: Only download surface data from GPU for DXGI buffers when reading. (cherry picked from commit 49b8e55b3c086754418ecec015c2e30ea72160c9) CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 14 +++++++++----- dlls/mfplat/tests/mfplat.c | 1 - 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index c8e797e6743..c0874faa23d 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -905,7 +905,7 @@ static HRESULT dxgi_surface_buffer_create_readback_texture(struct buffer *buffer return hr; } -static HRESULT dxgi_surface_buffer_map(struct buffer *buffer) +static HRESULT dxgi_surface_buffer_map(struct buffer *buffer, MF2DBuffer_LockFlags flags) { ID3D11DeviceContext *immediate_context; ID3D11Device *device; @@ -916,8 +916,12 @@ static HRESULT dxgi_surface_buffer_map(struct buffer *buffer) ID3D11Texture2D_GetDevice(buffer->dxgi_surface.texture, &device); ID3D11Device_GetImmediateContext(device, &immediate_context); - ID3D11DeviceContext_CopySubresourceRegion(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, - 0, 0, 0, 0, (ID3D11Resource *)buffer->dxgi_surface.texture, buffer->dxgi_surface.sub_resource_idx, NULL); + + if (flags == MF2DBuffer_LockFlags_Read || flags == MF2DBuffer_LockFlags_ReadWrite) + { + ID3D11DeviceContext_CopySubresourceRegion(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, + 0, 0, 0, 0, (ID3D11Resource *)buffer->dxgi_surface.texture, buffer->dxgi_surface.sub_resource_idx, NULL); + } memset(&buffer->dxgi_surface.map_desc, 0, sizeof(buffer->dxgi_surface.map_desc)); if (FAILED(hr = ID3D11DeviceContext_Map(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, @@ -971,7 +975,7 @@ static HRESULT WINAPI dxgi_surface_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat if (SUCCEEDED(hr)) { - hr = dxgi_surface_buffer_map(buffer); + hr = dxgi_surface_buffer_map(buffer, MF2DBuffer_LockFlags_ReadWrite); if (SUCCEEDED(hr)) { copy_image(buffer, buffer->_2d.linear_buffer, buffer->_2d.width, buffer->dxgi_surface.map_desc.pData, @@ -1040,7 +1044,7 @@ static HRESULT dxgi_surface_buffer_lock(struct buffer *buffer, MF2DBuffer_LockFl if (buffer->_2d.linear_buffer) hr = MF_E_UNEXPECTED; else if (!buffer->_2d.locks) - hr = dxgi_surface_buffer_map(buffer); + hr = dxgi_surface_buffer_map(buffer, flags); else if (buffer->_2d.lock_flags == MF2DBuffer_LockFlags_Write && flags != MF2DBuffer_LockFlags_Write) hr = HRESULT_FROM_WIN32(ERROR_WAS_LOCKED); diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index f324379c387..99cdd04976f 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -7330,7 +7330,6 @@ static void test_d3d11_surface_buffer(void) hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Write, &data, &pitch, &data2, &length); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(*(DWORD *)data != 0xefefefef, "Unexpected leading dword.\n"); memset(data, 0x89, 4); IMF2DBuffer2_Unlock2D(_2dbuffer2); From c2ed34573eb832c735cbe5a6366f0bcab1f2f03f Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Mon, 17 Apr 2023 16:02:10 +0200 Subject: [PATCH 0628/1148] mfplat: Only upload surface data to GPU for DXGI buffers when writing. (cherry picked from commit d073d3ea18d32d35a1d324d5567f7d743372a25c) CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 15 ++++++++++----- dlls/mfplat/tests/mfplat.c | 1 - 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index c0874faa23d..d8de06fb23a 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -936,7 +936,7 @@ static HRESULT dxgi_surface_buffer_map(struct buffer *buffer, MF2DBuffer_LockFla return hr; } -static void dxgi_surface_buffer_unmap(struct buffer *buffer) +static void dxgi_surface_buffer_unmap(struct buffer *buffer, MF2DBuffer_LockFlags flags) { ID3D11DeviceContext *immediate_context; ID3D11Device *device; @@ -946,8 +946,11 @@ static void dxgi_surface_buffer_unmap(struct buffer *buffer) ID3D11DeviceContext_Unmap(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, 0); memset(&buffer->dxgi_surface.map_desc, 0, sizeof(buffer->dxgi_surface.map_desc)); - ID3D11DeviceContext_CopySubresourceRegion(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.texture, - buffer->dxgi_surface.sub_resource_idx, 0, 0, 0, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, 0, NULL); + if (flags == MF2DBuffer_LockFlags_Write || flags == MF2DBuffer_LockFlags_ReadWrite) + { + ID3D11DeviceContext_CopySubresourceRegion(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.texture, + buffer->dxgi_surface.sub_resource_idx, 0, 0, 0, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, 0, NULL); + } ID3D11DeviceContext_Release(immediate_context); ID3D11Device_Release(device); @@ -1014,7 +1017,7 @@ static HRESULT WINAPI dxgi_surface_buffer_Unlock(IMFMediaBuffer *iface) { copy_image(buffer, buffer->dxgi_surface.map_desc.pData, buffer->dxgi_surface.map_desc.RowPitch, buffer->_2d.linear_buffer, buffer->_2d.width, buffer->_2d.width, buffer->_2d.height); - dxgi_surface_buffer_unmap(buffer); + dxgi_surface_buffer_unmap(buffer, MF2DBuffer_LockFlags_ReadWrite); free(buffer->_2d.linear_buffer); buffer->_2d.linear_buffer = NULL; @@ -1052,6 +1055,8 @@ static HRESULT dxgi_surface_buffer_lock(struct buffer *buffer, MF2DBuffer_LockFl { if (!buffer->_2d.locks) buffer->_2d.lock_flags = flags; + else + buffer->_2d.lock_flags |= flags; buffer->_2d.locks++; *scanline0 = buffer->dxgi_surface.map_desc.pData; *pitch = buffer->dxgi_surface.map_desc.RowPitch; @@ -1096,7 +1101,7 @@ static HRESULT WINAPI dxgi_surface_buffer_Unlock2D(IMF2DBuffer2 *iface) { if (!--buffer->_2d.locks) { - dxgi_surface_buffer_unmap(buffer); + dxgi_surface_buffer_unmap(buffer, buffer->_2d.lock_flags); buffer->_2d.lock_flags = 0; } } diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 99cdd04976f..507830e5843 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -7324,7 +7324,6 @@ static void test_d3d11_surface_buffer(void) IMF2DBuffer2_Unlock2D(_2dbuffer2); color = get_d3d11_texture_color(texture, 0, 0); - todo_wine ok(color == 0xcdcdcdcd, "Unexpected leading dword %#lx.\n", color); put_d3d11_texture_color(texture, 0, 0, 0xefefefef); From 6cf6a291931d190af504adcb96d61fb8f2a4e503 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 4 Apr 2023 16:07:19 -0600 Subject: [PATCH 0629/1148] mfplat: Fix returned buffer length in dxgi_surface_buffer_lock(). (cherry picked from commit 5a162f2c316a28ad45f7c0707ed16b194204227e) CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 2 +- dlls/mfplat/tests/mfplat.c | 52 +++++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index d8de06fb23a..1b9ba24a408 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -1063,7 +1063,7 @@ static HRESULT dxgi_surface_buffer_lock(struct buffer *buffer, MF2DBuffer_LockFl if (buffer_start) *buffer_start = *scanline0; if (buffer_length) - *buffer_length = buffer->dxgi_surface.map_desc.RowPitch * buffer->_2d.height; + *buffer_length = buffer->dxgi_surface.map_desc.DepthPitch; } return hr; diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 507830e5843..1307b690f4a 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -7128,6 +7128,7 @@ static ID3D12Device *create_d3d12_device(void) static void test_d3d11_surface_buffer(void) { DWORD max_length, cur_length, length, color; + BYTE *data, *data2, *buffer_start; IMFDXGIBuffer *dxgi_buffer; D3D11_TEXTURE2D_DESC desc; ID3D11Texture2D *texture; @@ -7135,7 +7136,6 @@ static void test_d3d11_surface_buffer(void) IMFMediaBuffer *buffer; ID3D11Device *device; BYTE buff[64 * 64 * 4]; - BYTE *data, *data2; LONG pitch, pitch2; UINT index, size; IUnknown *obj; @@ -7438,7 +7438,54 @@ static void test_d3d11_surface_buffer(void) ID3D11Texture2D_Release(texture); - /* Subresource index 1. */ + memset(&desc, 0, sizeof(desc)); + desc.Width = 64; + desc.Height = 64; + desc.ArraySize = 1; + desc.MipLevels = 1; + desc.Format = DXGI_FORMAT_NV12; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + + hr = ID3D11Device_CreateTexture2D(device, &desc, NULL, &texture); + if (SUCCEEDED(hr)) + { + hr = pMFCreateDXGISurfaceBuffer(&IID_ID3D11Texture2D, (IUnknown *)texture, 0, FALSE, &buffer); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer2, (void **)&_2dbuffer2); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = IMF2DBuffer2_Lock2DSize(_2dbuffer2, MF2DBuffer_LockFlags_Read, &data, &pitch, &buffer_start, &length); + ok(hr == S_OK, "got %#lx.\n", hr); + + ok(pitch >= desc.Width, "got %ld.\n", pitch); + ok(length == pitch * desc.Height * 3 / 2, "got %lu.\n", length); + + hr = IMF2DBuffer2_Unlock2D(_2dbuffer2); + ok(hr == S_OK, "got %#lx.\n", hr); + + IMF2DBuffer2_Release(_2dbuffer2); + IMFMediaBuffer_Release(buffer); + ID3D11Texture2D_Release(texture); + } + else + { + win_skip("Failed to create NV12 texture, hr %#lx, skipping test.\n", hr); + ID3D11Device_Release(device); + return; + } + + /* Subresource index 1. + * When WARP d3d11 device is used, this test leaves the device in a broken state, so it should + * be kept last. */ + memset(&desc, 0, sizeof(desc)); + desc.Width = 64; + desc.Height = 64; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + hr = ID3D11Device_CreateTexture2D(device, &desc, NULL, &texture); ok(hr == S_OK, "Failed to create a texture, hr %#lx.\n", hr); @@ -7463,7 +7510,6 @@ static void test_d3d11_surface_buffer(void) IMF2DBuffer_Release(_2d_buffer); IMFMediaBuffer_Release(buffer); - ID3D11Texture2D_Release(texture); ID3D11Device_Release(device); From 61af157199591c2b0f002675e4e7a6b7a1019071 Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Wed, 3 May 2023 16:55:46 +0200 Subject: [PATCH 0630/1148] mfplat/buffer: Use the appropriate image copy function for NV11. (cherry picked from commit 6d1fc9096d26e98e946013efa8cc9f29a2b8909d) CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 24 +++++++++++++++++------- dlls/mfplat/tests/mfplat.c | 2 ++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 1b9ba24a408..493451a7d5b 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -1336,13 +1336,23 @@ static HRESULT create_1d_buffer(DWORD max_length, DWORD alignment, IMFMediaBuffe static p_copy_image_func get_2d_buffer_copy_func(DWORD fourcc) { - if (fourcc == MAKEFOURCC('N','V','1','2')) - return copy_image_nv12; - if (fourcc == MAKEFOURCC('I','M','C','1') || fourcc == MAKEFOURCC('I','M','C','3')) - return copy_image_imc1; - if (fourcc == MAKEFOURCC('I','M','C','2') || fourcc == MAKEFOURCC('I','M','C','4')) - return copy_image_imc2; - return NULL; + switch (fourcc) + { + case MAKEFOURCC('I','M','C','1'): + case MAKEFOURCC('I','M','C','3'): + return copy_image_imc1; + + case MAKEFOURCC('I','M','C','2'): + case MAKEFOURCC('I','M','C','4'): + case MAKEFOURCC('N','V','1','1'): + return copy_image_imc2; + + case MAKEFOURCC('N','V','1','2'): + return copy_image_nv12; + + default: + return NULL; + } } static HRESULT create_2d_buffer(DWORD width, DWORD height, DWORD fourcc, BOOL bottom_up, IMFMediaBuffer **buffer) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 1307b690f4a..d4d6eaf984f 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -6088,6 +6088,7 @@ static void test_MFCreate2DMediaBuffer(void) case MAKEFOURCC('I','M','C','2'): case MAKEFOURCC('I','M','C','4'): + case MAKEFOURCC('N','V','1','1'): ok(stride * 3 / 2 * ptr->height <= length2, "Insufficient buffer space: expected at least %lu bytes, got only %lu\n", stride * 3 / 2 * ptr->height, length2); for (j = 0; j < ptr->height; j++) @@ -6138,6 +6139,7 @@ static void test_MFCreate2DMediaBuffer(void) case MAKEFOURCC('I','M','C','2'): case MAKEFOURCC('I','M','C','4'): + case MAKEFOURCC('N','V','1','1'): for (j = 0; j < ptr->height; j++) for (k = 0; k < stride / 2; k++) ok(data[j * (pitch / 2) + k] == (((j + ptr->height) % 16) << 4) + (k % 16), From 1de11aa500c815bd7ae82dd46bbf5ec553e78f3c Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Tue, 14 Jun 2022 12:39:42 +0200 Subject: [PATCH 0631/1148] mfplat/buffer: Support YV12, I420 and IYUV image formats. (cherry picked from commit 480554602458a5be721f7e650f64ec15c1e45ade) CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 13 +++++++++--- dlls/mfplat/tests/mfplat.c | 42 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 493451a7d5b..f090dd13417 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -1345,6 +1345,9 @@ static p_copy_image_func get_2d_buffer_copy_func(DWORD fourcc) case MAKEFOURCC('I','M','C','2'): case MAKEFOURCC('I','M','C','4'): case MAKEFOURCC('N','V','1','1'): + case MAKEFOURCC('Y','V','1','2'): + case MAKEFOURCC('I','4','2','0'): + case MAKEFOURCC('I','Y','U','V'): return copy_image_imc2; case MAKEFOURCC('N','V','1','2'): @@ -1389,12 +1392,12 @@ static HRESULT create_2d_buffer(DWORD width, DWORD height, DWORD fourcc, BOOL bo case MAKEFOURCC('I','M','C','2'): case MAKEFOURCC('I','M','C','4'): case MAKEFOURCC('N','V','1','1'): - plane_size = stride * 3 / 2 * height; - break; - case MAKEFOURCC('N','V','1','2'): case MAKEFOURCC('Y','V','1','2'): case MAKEFOURCC('I','4','2','0'): case MAKEFOURCC('I','Y','U','V'): + plane_size = stride * 3 / 2 * height; + break; + case MAKEFOURCC('N','V','1','2'): plane_size = stride * height * 3 / 2; break; default: @@ -1412,6 +1415,8 @@ static HRESULT create_2d_buffer(DWORD width, DWORD height, DWORD fourcc, BOOL bo case MAKEFOURCC('I','M','C','4'): case MAKEFOURCC('Y','V','1','2'): case MAKEFOURCC('N','V','1','1'): + case MAKEFOURCC('I','4','2','0'): + case MAKEFOURCC('I','Y','U','V'): row_alignment = MF_128_BYTE_ALIGNMENT; break; default: @@ -1431,6 +1436,8 @@ static HRESULT create_2d_buffer(DWORD width, DWORD height, DWORD fourcc, BOOL bo case MAKEFOURCC('I','M','C','2'): case MAKEFOURCC('I','M','C','4'): case MAKEFOURCC('N','V','1','1'): + case MAKEFOURCC('I','4','2','0'): + case MAKEFOURCC('I','Y','U','V'): max_length = pitch * height * 3 / 2; break; default: diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index d4d6eaf984f..5c50ebe3b17 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -4431,6 +4431,42 @@ image_size_tests[] = { &MFVideoFormat_NV11, 3, 2, 12, 9, 384, 8, 128 }, { &MFVideoFormat_NV11, 4, 2, 12, 0, 384, 12, 128 }, { &MFVideoFormat_NV11, 320, 240, 115200, 0, 138240, 115200, 384 }, + + { &MFVideoFormat_YV12, 1, 1, 3, 1, 192, 1, 128 }, + { &MFVideoFormat_YV12, 1, 2, 6, 3, 384, 2, 128 }, + { &MFVideoFormat_YV12, 1, 3, 9, 4, 576, 3, 128 }, + { &MFVideoFormat_YV12, 2, 1, 3, 0, 192, 3, 128 }, + { &MFVideoFormat_YV12, 2, 2, 6, 6, 384, 6, 128 }, + { &MFVideoFormat_YV12, 2, 4, 12, 0, 768, 12, 128 }, + { &MFVideoFormat_YV12, 3, 2, 12, 9, 384, 8, 128 }, + { &MFVideoFormat_YV12, 3, 5, 30, 22, 960, 20, 128 }, + { &MFVideoFormat_YV12, 4, 2, 12, 0, 384, 12, 128 }, + { &MFVideoFormat_YV12, 4, 3, 18, 0, 576, 18, 128 }, + { &MFVideoFormat_YV12, 320, 240, 115200, 0, 138240, 115200, 384 }, + + { &MFVideoFormat_I420, 1, 1, 3, 1, 192, 1, 128 }, + { &MFVideoFormat_I420, 1, 2, 6, 3, 384, 2, 128 }, + { &MFVideoFormat_I420, 1, 3, 9, 4, 576, 3, 128 }, + { &MFVideoFormat_I420, 2, 1, 3, 0, 192, 3, 128 }, + { &MFVideoFormat_I420, 2, 2, 6, 6, 384, 6, 128 }, + { &MFVideoFormat_I420, 2, 4, 12, 0, 768, 12, 128 }, + { &MFVideoFormat_I420, 3, 2, 12, 9, 384, 8, 128 }, + { &MFVideoFormat_I420, 3, 5, 30, 22, 960, 20, 128 }, + { &MFVideoFormat_I420, 4, 2, 12, 0, 384, 12, 128 }, + { &MFVideoFormat_I420, 4, 3, 18, 0, 576, 18, 128 }, + { &MFVideoFormat_I420, 320, 240, 115200, 0, 138240, 115200, 384 }, + + { &MFVideoFormat_IYUV, 1, 1, 3, 1, 192, 1, 128 }, + { &MFVideoFormat_IYUV, 1, 2, 6, 3, 384, 2, 128 }, + { &MFVideoFormat_IYUV, 1, 3, 9, 4, 576, 3, 128 }, + { &MFVideoFormat_IYUV, 2, 1, 3, 0, 192, 3, 128 }, + { &MFVideoFormat_IYUV, 2, 2, 6, 6, 384, 6, 128 }, + { &MFVideoFormat_IYUV, 2, 4, 12, 0, 768, 12, 128 }, + { &MFVideoFormat_IYUV, 3, 2, 12, 9, 384, 8, 128 }, + { &MFVideoFormat_IYUV, 3, 5, 30, 22, 960, 20, 128 }, + { &MFVideoFormat_IYUV, 4, 2, 12, 0, 384, 12, 128 }, + { &MFVideoFormat_IYUV, 4, 3, 18, 0, 576, 18, 128 }, + { &MFVideoFormat_IYUV, 320, 240, 115200, 0, 138240, 115200, 384 }, }; static void test_MFCalculateImageSize(void) @@ -6089,6 +6125,9 @@ static void test_MFCreate2DMediaBuffer(void) case MAKEFOURCC('I','M','C','2'): case MAKEFOURCC('I','M','C','4'): case MAKEFOURCC('N','V','1','1'): + case MAKEFOURCC('Y','V','1','2'): + case MAKEFOURCC('I','4','2','0'): + case MAKEFOURCC('I','Y','U','V'): ok(stride * 3 / 2 * ptr->height <= length2, "Insufficient buffer space: expected at least %lu bytes, got only %lu\n", stride * 3 / 2 * ptr->height, length2); for (j = 0; j < ptr->height; j++) @@ -6140,6 +6179,9 @@ static void test_MFCreate2DMediaBuffer(void) case MAKEFOURCC('I','M','C','2'): case MAKEFOURCC('I','M','C','4'): case MAKEFOURCC('N','V','1','1'): + case MAKEFOURCC('Y','V','1','2'): + case MAKEFOURCC('I','4','2','0'): + case MAKEFOURCC('I','Y','U','V'): for (j = 0; j < ptr->height; j++) for (k = 0; k < stride / 2; k++) ok(data[j * (pitch / 2) + k] == (((j + ptr->height) % 16) << 4) + (k % 16), From ad6f164495e7707cf773aca51d06b266bb171c04 Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Mon, 11 Jul 2022 12:54:25 +0200 Subject: [PATCH 0632/1148] mfplat/buffer: Implement IMF2DBuffer::ContiguousCopyFrom(). This is a generic implementation, which is probably fine for buffers backed by system memory. The implementation for buffers backed by GPU memory can probably be optimized. (cherry picked from commit da1885ce230ed7097b7513e7cb8abc7b7158e41e) CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 23 +++++++++++- dlls/mfplat/tests/mfplat.c | 77 +++++++++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 8 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index f090dd13417..63647c33ece 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -608,9 +608,28 @@ static HRESULT WINAPI memory_2d_buffer_ContiguousCopyTo(IMF2DBuffer2 *iface, BYT static HRESULT WINAPI memory_2d_buffer_ContiguousCopyFrom(IMF2DBuffer2 *iface, const BYTE *src_buffer, DWORD src_length) { - FIXME("%p, %p, %lu.\n", iface, src_buffer, src_length); + struct buffer *buffer = impl_from_IMF2DBuffer2(iface); + BYTE *dst_scanline0, *dst_buffer_start; + DWORD dst_length; + LONG dst_pitch; + HRESULT hr; - return E_NOTIMPL; + TRACE("%p, %p, %lu.\n", iface, src_buffer, src_length); + + if (src_length < buffer->_2d.plane_size) + return E_INVALIDARG; + + hr = IMF2DBuffer2_Lock2DSize(iface, MF2DBuffer_LockFlags_Write, &dst_scanline0, &dst_pitch, &dst_buffer_start, &dst_length); + + if (SUCCEEDED(hr)) + { + copy_image(buffer, dst_scanline0, dst_pitch, src_buffer, buffer->_2d.width, buffer->_2d.width, buffer->_2d.height); + + if (FAILED(IMF2DBuffer2_Unlock2D(iface))) + WARN("Couldn't unlock destination buffer %p, hr %#lx.\n", iface, hr); + } + + return hr; } static HRESULT WINAPI memory_2d_buffer_Lock2DSize(IMF2DBuffer2 *iface, MF2DBuffer_LockFlags flags, BYTE **scanline0, diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 5c50ebe3b17..a9ae6c18218 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -6074,29 +6074,94 @@ static void test_MFCreate2DMediaBuffer(void) if (is_MEDIASUBTYPE_RGB(ptr->subtype)) continue; + winetest_push_context("%u, %u x %u, format %s", i, ptr->width, ptr->height, wine_dbgstr_guid(ptr->subtype)); + hr = pMFCreate2DMediaBuffer(ptr->width, ptr->height, ptr->subtype->Data1, FALSE, &buffer); ok(hr == S_OK, "Failed to create a buffer, hr %#lx.\n", hr); hr = IMFMediaBuffer_GetMaxLength(buffer, &length); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(length == ptr->max_length, "%u: unexpected maximum length %lu for %u x %u, format %s.\n", - i, length, ptr->width, ptr->height, wine_dbgstr_guid(ptr->subtype)); + ok(length == ptr->max_length, "Unexpected maximum length %lu.\n", length); hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer, (void **)&_2dbuffer); ok(hr == S_OK, "Failed to get interface, hr %#lx.\n", hr); + hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer2, (void **)&_2dbuffer2); + ok(hr == S_OK, "Failed to get interface, hr %#lx.\n", hr); + hr = IMF2DBuffer_GetContiguousLength(_2dbuffer, &length); ok(hr == S_OK, "Failed to get length, hr %#lx.\n", hr); - todo_wine_if(IsEqualGUID(ptr->subtype, &MFVideoFormat_RGB24) && ptr->width % 4 == 0) - ok(length == ptr->contiguous_length, "%d: unexpected contiguous length %lu for %u x %u, format %s.\n", - i, length, ptr->width, ptr->height, wine_dbgstr_guid(ptr->subtype)); + ok(length == ptr->contiguous_length, "Unexpected contiguous length %lu.\n", length); + + data2 = malloc(ptr->contiguous_length + 16); + ok(!!data2, "Failed to allocate buffer.\n"); + + for (j = 0; j < ptr->contiguous_length + 16; j++) + data2[j] = j & 0x7f; + + hr = IMF2DBuffer2_ContiguousCopyFrom(_2dbuffer2, data2, ptr->contiguous_length - 1); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaBuffer_Lock(buffer, &data, &length2, NULL); + ok(hr == S_OK, "Failed to lock buffer, hr %#lx.\n", hr); + ok(length2 == ptr->contiguous_length, "Unexpected linear buffer length %lu.\n", length2); + + memset(data, 0xff, length2); + + hr = IMFMediaBuffer_Unlock(buffer); + ok(hr == S_OK, "Failed to unlock buffer, hr %#lx.\n", hr); + + hr = IMF2DBuffer2_ContiguousCopyFrom(_2dbuffer2, data2, ptr->contiguous_length + 16); + ok(hr == S_OK, "Failed to copy from contiguous buffer, hr %#lx.\n", hr); + + hr = IMFMediaBuffer_Lock(buffer, &data, &length2, NULL); + ok(hr == S_OK, "Failed to lock buffer, hr %#lx.\n", hr); + ok(length2 == ptr->contiguous_length, "%d: unexpected linear buffer length %lu for %u x %u, format %s.\n", + i, length2, ptr->width, ptr->height, wine_dbgstr_guid(ptr->subtype)); + + for (j = 0; j < ptr->contiguous_length; j++) + { + if (IsEqualGUID(ptr->subtype, &MFVideoFormat_IMC1) || IsEqualGUID(ptr->subtype, &MFVideoFormat_IMC3)) + { + if (j < ptr->height * ptr->pitch && j % ptr->pitch >= ptr->width) + continue; + if (j >= ptr->height * ptr->pitch && j % ptr->pitch >= ptr->width / 2) + continue; + } + if (data[j] != (j & 0x7f)) + break; + } + ok(j == ptr->contiguous_length, "Unexpected byte %02x instead of %02x at position %u.\n", data[j], j & 0x7f, j); + + memset(data, 0xff, length2); + + hr = IMFMediaBuffer_Unlock(buffer); + ok(hr == S_OK, "Failed to unlock buffer, hr %#lx.\n", hr); + + hr = IMF2DBuffer2_ContiguousCopyFrom(_2dbuffer2, data2, ptr->contiguous_length); + ok(hr == S_OK, "Failed to copy from contiguous buffer, hr %#lx.\n", hr); + + free(data2); hr = IMFMediaBuffer_Lock(buffer, &data, &length2, NULL); ok(hr == S_OK, "Failed to lock buffer, hr %#lx.\n", hr); - todo_wine_if(IsEqualGUID(ptr->subtype, &MFVideoFormat_RGB24) && ptr->width % 4 == 0) ok(length2 == ptr->contiguous_length, "%d: unexpected linear buffer length %lu for %u x %u, format %s.\n", i, length2, ptr->width, ptr->height, wine_dbgstr_guid(ptr->subtype)); + for (j = 0; j < ptr->contiguous_length; j++) + { + if (IsEqualGUID(ptr->subtype, &MFVideoFormat_IMC1) || IsEqualGUID(ptr->subtype, &MFVideoFormat_IMC3)) + { + if (j < ptr->height * ptr->pitch && j % ptr->pitch >= ptr->width) + continue; + if (j >= ptr->height * ptr->pitch && j % ptr->pitch >= ptr->width / 2) + continue; + } + if (data[j] != (j & 0x7f)) + break; + } + ok(j == ptr->contiguous_length, "Unexpected byte %02x instead of %02x at position %u.\n", data[j], j & 0x7f, j); + hr = pMFGetStrideForBitmapInfoHeader(ptr->subtype->Data1, ptr->width, &stride); ok(hr == S_OK, "Failed to get stride, hr %#lx.\n", hr); stride = abs(stride); From e09f550ed95d63753d172501327c02e38c26f586 Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Mon, 11 Jul 2022 13:17:30 +0200 Subject: [PATCH 0633/1148] mfplat/buffer: Implement IMF2DBuffer::ContiguousCopyTo(). (cherry picked from commit 885d8dfab221e1b1a9deeb09c61b69e125380984) CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 23 +++++++++++++++-- dlls/mfplat/tests/mfplat.c | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 63647c33ece..cb1b3176a08 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -601,9 +601,28 @@ static HRESULT WINAPI memory_2d_buffer_GetContiguousLength(IMF2DBuffer2 *iface, static HRESULT WINAPI memory_2d_buffer_ContiguousCopyTo(IMF2DBuffer2 *iface, BYTE *dest_buffer, DWORD dest_length) { - FIXME("%p, %p, %lu.\n", iface, dest_buffer, dest_length); + struct buffer *buffer = impl_from_IMF2DBuffer2(iface); + BYTE *src_scanline0, *src_buffer_start; + DWORD src_length; + LONG src_pitch; + HRESULT hr; - return E_NOTIMPL; + TRACE("%p, %p, %lu.\n", iface, dest_buffer, dest_length); + + if (dest_length < buffer->_2d.plane_size) + return E_INVALIDARG; + + hr = IMF2DBuffer2_Lock2DSize(iface, MF2DBuffer_LockFlags_Read, &src_scanline0, &src_pitch, &src_buffer_start, &src_length); + + if (SUCCEEDED(hr)) + { + copy_image(buffer, dest_buffer, buffer->_2d.width, src_scanline0, src_pitch, buffer->_2d.width, buffer->_2d.height); + + if (FAILED(IMF2DBuffer2_Unlock2D(iface))) + WARN("Couldn't unlock source buffer %p, hr %#lx.\n", iface, hr); + } + + return S_OK; } static HRESULT WINAPI memory_2d_buffer_ContiguousCopyFrom(IMF2DBuffer2 *iface, const BYTE *src_buffer, DWORD src_length) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index a9ae6c18218..5309016152d 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -6162,6 +6162,57 @@ static void test_MFCreate2DMediaBuffer(void) } ok(j == ptr->contiguous_length, "Unexpected byte %02x instead of %02x at position %u.\n", data[j], j & 0x7f, j); + hr = IMFMediaBuffer_Unlock(buffer); + ok(hr == S_OK, "Failed to unlock buffer, hr %#lx.\n", hr); + + hr = IMF2DBuffer2_ContiguousCopyTo(_2dbuffer2, data2, ptr->contiguous_length - 1); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + memset(data2, 0xff, ptr->contiguous_length + 16); + + hr = IMF2DBuffer2_ContiguousCopyTo(_2dbuffer2, data2, ptr->contiguous_length + 16); + ok(hr == S_OK, "Failed to copy to contiguous buffer, hr %#lx.\n", hr); + + for (j = 0; j < ptr->contiguous_length; j++) + { + if (IsEqualGUID(ptr->subtype, &MFVideoFormat_IMC1) || IsEqualGUID(ptr->subtype, &MFVideoFormat_IMC3)) + { + if (j < ptr->height * ptr->pitch && j % ptr->pitch >= ptr->width) + continue; + if (j >= ptr->height * ptr->pitch && j % ptr->pitch >= ptr->width / 2) + continue; + } + if (data2[j] != (j & 0x7f)) + break; + } + ok(j == ptr->contiguous_length, "Unexpected byte %02x instead of %02x at position %u.\n", data2[j], j & 0x7f, j); + + memset(data2, 0xff, ptr->contiguous_length + 16); + + hr = IMF2DBuffer2_ContiguousCopyTo(_2dbuffer2, data2, ptr->contiguous_length); + ok(hr == S_OK, "Failed to copy to contiguous buffer, hr %#lx.\n", hr); + + for (j = 0; j < ptr->contiguous_length; j++) + { + if (IsEqualGUID(ptr->subtype, &MFVideoFormat_IMC1) || IsEqualGUID(ptr->subtype, &MFVideoFormat_IMC3)) + { + if (j < ptr->height * ptr->pitch && j % ptr->pitch >= ptr->width) + continue; + if (j >= ptr->height * ptr->pitch && j % ptr->pitch >= ptr->width / 2) + continue; + } + if (data2[j] != (j & 0x7f)) + break; + } + ok(j == ptr->contiguous_length, "Unexpected byte %02x instead of %02x at position %u.\n", data2[j], j & 0x7f, j); + + free(data2); + + hr = IMFMediaBuffer_Lock(buffer, &data, &length2, NULL); + ok(hr == S_OK, "Failed to lock buffer, hr %#lx.\n", hr); + ok(length2 == ptr->contiguous_length, "%d: unexpected linear buffer length %lu for %u x %u, format %s.\n", + i, length2, ptr->width, ptr->height, wine_dbgstr_guid(ptr->subtype)); + hr = pMFGetStrideForBitmapInfoHeader(ptr->subtype->Data1, ptr->width, &stride); ok(hr == S_OK, "Failed to get stride, hr %#lx.\n", hr); stride = abs(stride); From 4332fbc546f3d80256656e07f3770149a9f43580 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 May 2023 19:22:04 -0600 Subject: [PATCH 0634/1148] mfplat/buffer: Use absolute pitch in memory_1d_2d_buffer_[Un]Lock(). CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index cb1b3176a08..52d05f4ef80 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -313,8 +313,14 @@ static HRESULT WINAPI memory_1d_2d_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat hr = E_OUTOFMEMORY; if (SUCCEEDED(hr)) - copy_image(buffer, buffer->_2d.linear_buffer, buffer->_2d.width, buffer->data, buffer->_2d.pitch, + { + int pitch = buffer->_2d.pitch; + + if (pitch < 0) + pitch = -pitch; + copy_image(buffer, buffer->_2d.linear_buffer, buffer->_2d.width, buffer->data, pitch, buffer->_2d.width, buffer->_2d.height); + } } if (SUCCEEDED(hr)) @@ -342,7 +348,11 @@ static HRESULT WINAPI memory_1d_2d_buffer_Unlock(IMFMediaBuffer *iface) if (buffer->_2d.linear_buffer && !--buffer->_2d.locks) { - copy_image(buffer, buffer->data, buffer->_2d.pitch, buffer->_2d.linear_buffer, buffer->_2d.width, + int pitch = buffer->_2d.pitch; + + if (pitch < 0) + pitch = -pitch; + copy_image(buffer, buffer->data, pitch, buffer->_2d.linear_buffer, buffer->_2d.width, buffer->_2d.width, buffer->_2d.height); free(buffer->_2d.linear_buffer); From db8bf7bb3820b7151e307fd637f9961bd998db98 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 May 2023 13:28:48 -0600 Subject: [PATCH 0635/1148] mfplat/tests: Add more tests for copying 2d buffers. CW-Bug-Id: #22084 --- dlls/mfplat/tests/mfplat.c | 187 +++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 5309016152d..11a8620b0ee 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -8944,6 +8944,192 @@ static void test_MFInitMediaTypeFromAMMediaType(void) IMFMediaType_Release(media_type); } +#define check_reset_data(a, b, c, d, e, f) check_reset_data_(__LINE__, a, b, c, d, e, f) +static void check_reset_data_(unsigned int line, IMF2DBuffer2 *buffer2d, const BYTE *data, BOOL bottom_up, + DWORD width, DWORD height, BOOL todo) +{ + BYTE *scanline0, *buffer_start; + DWORD length, max_length; + IMFMediaBuffer *buffer; + LONG pitch; + BYTE *lock; + HRESULT hr; + int i; + + hr = IMF2DBuffer2_QueryInterface(buffer2d, &IID_IMFMediaBuffer, (void **)&buffer); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + hr = IMF2DBuffer2_Lock2DSize(buffer2d, MF2DBuffer_LockFlags_Read, &scanline0, &pitch, &buffer_start, &length); + ok(hr == S_OK, "got hr %#lx.\n", hr); + if (bottom_up) + { + ok(pitch < 0, "got pitch %ld.\n", pitch); + ok(buffer_start == scanline0 + pitch * (LONG)(height - 1), "buffer start mismatch.\n"); + } + else + { + ok(pitch > 0, "got pitch %ld.\n", pitch); + ok(buffer_start == scanline0, "buffer start mismatch.\n"); + } + for (i = 0; i < height; ++i) + todo_wine_if(bottom_up && todo) ok_(__FILE__,line)(!memcmp(buffer_start + abs(pitch) * i, data + width * i * 4, width * 4), + "2D Data mismatch, scaline %d.\n", i); + hr = IMF2DBuffer2_Unlock2D(buffer2d); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + hr = IMFMediaBuffer_Lock(buffer, &lock, &max_length, &length); + ok(hr == S_OK, "got hr %#lx.\n", hr); + ok_(__FILE__,line)(max_length == width * height * 4, "got max_length %lu.\n", max_length); + ok_(__FILE__,line)(length == width * height * 4, "got length %lu.\n", length); + todo_wine_if(bottom_up && todo) ok_(__FILE__,line)(!memcmp(lock, data, length), "contiguous data mismatch.\n"); + memset(lock, 0xcc, length); + hr = IMFMediaBuffer_Unlock(buffer); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + IMFMediaBuffer_Release(buffer); +} + +static void test_2dbuffer_copy_(IMFMediaBuffer *buffer, BOOL bottom_up, DWORD width, DWORD height) +{ + static const unsigned int test_data[] = + { + 0x01010101, 0x01010101, + 0x02020202, 0x02020202, + }; + + BYTE data[sizeof(test_data)]; + IMFMediaBuffer *src_buffer; + DWORD length, max_length; + IMF2DBuffer2 *buffer2d; + IMFSample *sample; + BYTE *lock; + HRESULT hr; + ULONG ref; + + hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer2, (void **)&buffer2d); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + hr = MFCreateSample(&sample); + ok(hr == S_OK, "got hr %#lx.\n", hr); + hr = MFCreateMemoryBuffer(sizeof(test_data) * 2, &src_buffer); + ok(hr == S_OK, "got hr %#lx.\n", hr); + hr = IMFSample_AddBuffer(sample, src_buffer); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + hr = IMFMediaBuffer_Lock(src_buffer, &lock, &max_length, &length); + ok(hr == S_OK, "got hr %#lx.\n", hr); + ok(max_length == sizeof(test_data) * 2, "got %lu.\n", max_length); + memcpy(lock, test_data, sizeof(test_data)); + hr = IMFMediaBuffer_Unlock(src_buffer); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + hr = IMFMediaBuffer_Lock(buffer, &lock, &max_length, &length); + ok(hr == S_OK, "got hr %#lx.\n", hr); + ok(max_length == 16, "got %lu.\n", max_length); + ok(length == 16, "got %lu.\n", length); + memset(lock, 0xcc, length); + hr = IMFMediaBuffer_Unlock(buffer); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + hr = IMFMediaBuffer_SetCurrentLength(src_buffer, 1); + ok(hr == S_OK, "got hr %#lx.\n", hr); + hr = IMFSample_CopyToBuffer(sample, buffer); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + memset(data, 0xcc, sizeof(data)); + data[0] = ((BYTE *)test_data)[0]; + check_reset_data(buffer2d, data, bottom_up, width, height, FALSE); + + hr = IMF2DBuffer2_ContiguousCopyFrom(buffer2d, (BYTE *)test_data, sizeof(test_data)); + ok(hr == S_OK, "got hr %#lx.\n", hr); + hr = IMF2DBuffer2_ContiguousCopyTo(buffer2d, data, sizeof(data)); + ok(hr == S_OK, "got hr %#lx.\n", hr); + ok(!memcmp(data, test_data, sizeof(data)), "data mismatch.\n"); + + check_reset_data(buffer2d, (const BYTE *)test_data, bottom_up, width, height, TRUE); + + hr = IMFMediaBuffer_SetCurrentLength(src_buffer, sizeof(test_data) + 1); + ok(hr == S_OK, "got hr %#lx.\n", hr); + hr = IMFSample_CopyToBuffer(sample, buffer); + ok(hr == MF_E_BUFFERTOOSMALL, "got hr %#lx.\n", hr); + + hr = IMFMediaBuffer_SetCurrentLength(src_buffer, sizeof(test_data)); + ok(hr == S_OK, "got hr %#lx.\n", hr); + hr = IMFSample_CopyToBuffer(sample, buffer); + ok(hr == S_OK, "got hr %#lx.\n", hr); + + check_reset_data(buffer2d, (const BYTE *)test_data, bottom_up, width, height, FALSE); + + IMF2DBuffer2_Release(buffer2d); + ref = IMFSample_Release(sample); + ok(!ref, "got %lu.\n", ref); + ref = IMFMediaBuffer_Release(src_buffer); + ok(!ref, "got %lu.\n", ref); +} + +static void test_2dbuffer_copy(void) +{ + D3D11_TEXTURE2D_DESC desc; + ID3D11Texture2D *texture; + IMFMediaBuffer *buffer; + ID3D11Device *device; + HRESULT hr; + ULONG ref; + + if (!pMFCreate2DMediaBuffer) + { + win_skip("MFCreate2DMediaBuffer() is not available.\n"); + return; + } + + winetest_push_context("top down"); + hr = pMFCreate2DMediaBuffer(2, 2, D3DFMT_A8R8G8B8, FALSE, &buffer); + ok(hr == S_OK, "got hr %#lx.\n", hr); + test_2dbuffer_copy_(buffer, FALSE, 2, 2); + ref = IMFMediaBuffer_Release(buffer); + ok(!ref, "got %lu.\n", ref); + winetest_pop_context(); + + winetest_push_context("bottom up"); + hr = pMFCreate2DMediaBuffer(2, 2, D3DFMT_A8R8G8B8, TRUE, &buffer); + ok(hr == S_OK, "got hr %#lx.\n", hr); + test_2dbuffer_copy_(buffer, TRUE, 2, 2); + ref = IMFMediaBuffer_Release(buffer); + ok(!ref, "got %lu.\n", ref); + winetest_pop_context(); + + if (!pMFCreateDXGISurfaceBuffer) + { + win_skip("MFCreateDXGISurfaceBuffer() is not available.\n"); + return; + } + + if (!(device = create_d3d11_device())) + { + skip("Failed to create a D3D11 device, skipping tests.\n"); + return; + } + + memset(&desc, 0, sizeof(desc)); + desc.Width = 2; + desc.Height = 2; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + hr = ID3D11Device_CreateTexture2D(device, &desc, NULL, &texture); + ok(hr == S_OK, "Failed to create a texture, hr %#lx.\n", hr); + + hr = pMFCreateDXGISurfaceBuffer(&IID_ID3D11Texture2D, (IUnknown *)texture, 0, FALSE, &buffer); + ok(hr == S_OK, "Failed to create a buffer, hr %#lx.\n", hr); + test_2dbuffer_copy_(buffer, FALSE, 2, 2); + + ID3D11Texture2D_Release(texture); + ref = IMFMediaBuffer_Release(buffer); + ok(!ref, "got %lu.\n", ref); + ID3D11Device_Release(device); +} + START_TEST(mfplat) { char **argv; @@ -9027,6 +9213,7 @@ START_TEST(mfplat) test_MFCreateVideoMediaTypeFromVideoInfoHeader(); test_MFInitMediaTypeFromVideoInfoHeader(); test_MFInitMediaTypeFromAMMediaType(); + test_2dbuffer_copy(); CoUninitialize(); } From d595f45e5865e4bf4da6773375716803daadcc34 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 May 2023 19:24:23 -0600 Subject: [PATCH 0636/1148] mfplat/buffer: Do not flip in memory_2d_buffer_ContiguousCopy{From|To}(). CW-Bug-Id: #22084 --- dlls/mfplat/buffer.c | 10 ++++++++-- dlls/mfplat/tests/mfplat.c | 14 +++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 52d05f4ef80..3850efa594c 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -626,7 +626,10 @@ static HRESULT WINAPI memory_2d_buffer_ContiguousCopyTo(IMF2DBuffer2 *iface, BYT if (SUCCEEDED(hr)) { - copy_image(buffer, dest_buffer, buffer->_2d.width, src_scanline0, src_pitch, buffer->_2d.width, buffer->_2d.height); + if (src_pitch < 0) + src_pitch = -src_pitch; + copy_image(buffer, dest_buffer, buffer->_2d.width, src_buffer_start, src_pitch, + buffer->_2d.width, buffer->_2d.height); if (FAILED(IMF2DBuffer2_Unlock2D(iface))) WARN("Couldn't unlock source buffer %p, hr %#lx.\n", iface, hr); @@ -652,7 +655,10 @@ static HRESULT WINAPI memory_2d_buffer_ContiguousCopyFrom(IMF2DBuffer2 *iface, c if (SUCCEEDED(hr)) { - copy_image(buffer, dst_scanline0, dst_pitch, src_buffer, buffer->_2d.width, buffer->_2d.width, buffer->_2d.height); + if (dst_pitch < 0) + dst_pitch = -dst_pitch; + copy_image(buffer, dst_buffer_start, dst_pitch, src_buffer, buffer->_2d.width, + buffer->_2d.width, buffer->_2d.height); if (FAILED(IMF2DBuffer2_Unlock2D(iface))) WARN("Couldn't unlock destination buffer %p, hr %#lx.\n", iface, hr); diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 11a8620b0ee..7264db7f79b 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -8944,9 +8944,9 @@ static void test_MFInitMediaTypeFromAMMediaType(void) IMFMediaType_Release(media_type); } -#define check_reset_data(a, b, c, d, e, f) check_reset_data_(__LINE__, a, b, c, d, e, f) +#define check_reset_data(a, b, c, d, e) check_reset_data_(__LINE__, a, b, c, d, e) static void check_reset_data_(unsigned int line, IMF2DBuffer2 *buffer2d, const BYTE *data, BOOL bottom_up, - DWORD width, DWORD height, BOOL todo) + DWORD width, DWORD height) { BYTE *scanline0, *buffer_start; DWORD length, max_length; @@ -8972,7 +8972,7 @@ static void check_reset_data_(unsigned int line, IMF2DBuffer2 *buffer2d, const B ok(buffer_start == scanline0, "buffer start mismatch.\n"); } for (i = 0; i < height; ++i) - todo_wine_if(bottom_up && todo) ok_(__FILE__,line)(!memcmp(buffer_start + abs(pitch) * i, data + width * i * 4, width * 4), + ok_(__FILE__,line)(!memcmp(buffer_start + abs(pitch) * i, data + width * i * 4, width * 4), "2D Data mismatch, scaline %d.\n", i); hr = IMF2DBuffer2_Unlock2D(buffer2d); ok(hr == S_OK, "got hr %#lx.\n", hr); @@ -8981,7 +8981,7 @@ static void check_reset_data_(unsigned int line, IMF2DBuffer2 *buffer2d, const B ok(hr == S_OK, "got hr %#lx.\n", hr); ok_(__FILE__,line)(max_length == width * height * 4, "got max_length %lu.\n", max_length); ok_(__FILE__,line)(length == width * height * 4, "got length %lu.\n", length); - todo_wine_if(bottom_up && todo) ok_(__FILE__,line)(!memcmp(lock, data, length), "contiguous data mismatch.\n"); + ok_(__FILE__,line)(!memcmp(lock, data, length), "contiguous data mismatch.\n"); memset(lock, 0xcc, length); hr = IMFMediaBuffer_Unlock(buffer); ok(hr == S_OK, "got hr %#lx.\n", hr); @@ -9038,7 +9038,7 @@ static void test_2dbuffer_copy_(IMFMediaBuffer *buffer, BOOL bottom_up, DWORD wi memset(data, 0xcc, sizeof(data)); data[0] = ((BYTE *)test_data)[0]; - check_reset_data(buffer2d, data, bottom_up, width, height, FALSE); + check_reset_data(buffer2d, data, bottom_up, width, height); hr = IMF2DBuffer2_ContiguousCopyFrom(buffer2d, (BYTE *)test_data, sizeof(test_data)); ok(hr == S_OK, "got hr %#lx.\n", hr); @@ -9046,7 +9046,7 @@ static void test_2dbuffer_copy_(IMFMediaBuffer *buffer, BOOL bottom_up, DWORD wi ok(hr == S_OK, "got hr %#lx.\n", hr); ok(!memcmp(data, test_data, sizeof(data)), "data mismatch.\n"); - check_reset_data(buffer2d, (const BYTE *)test_data, bottom_up, width, height, TRUE); + check_reset_data(buffer2d, (const BYTE *)test_data, bottom_up, width, height); hr = IMFMediaBuffer_SetCurrentLength(src_buffer, sizeof(test_data) + 1); ok(hr == S_OK, "got hr %#lx.\n", hr); @@ -9058,7 +9058,7 @@ static void test_2dbuffer_copy_(IMFMediaBuffer *buffer, BOOL bottom_up, DWORD wi hr = IMFSample_CopyToBuffer(sample, buffer); ok(hr == S_OK, "got hr %#lx.\n", hr); - check_reset_data(buffer2d, (const BYTE *)test_data, bottom_up, width, height, FALSE); + check_reset_data(buffer2d, (const BYTE *)test_data, bottom_up, width, height); IMF2DBuffer2_Release(buffer2d); ref = IMFSample_Release(sample); From 02fb0c7cbe73397668c40407c400c2bf41ed858f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 May 2023 11:14:32 -0600 Subject: [PATCH 0637/1148] mfplat/sample: Refactor sample_CopyToBuffer(). CW-Bug-Id: #22084 --- dlls/mfplat/sample.c | 52 ++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/dlls/mfplat/sample.c b/dlls/mfplat/sample.c index 6a589e4757b..513b8bb4ca9 100644 --- a/dlls/mfplat/sample.c +++ b/dlls/mfplat/sample.c @@ -875,36 +875,42 @@ static HRESULT WINAPI sample_CopyToBuffer(IMFSample *iface, IMFMediaBuffer *buff dst_ptr = NULL; dst_length = current_length = 0; locked = SUCCEEDED(hr = IMFMediaBuffer_Lock(buffer, &dst_ptr, &dst_length, ¤t_length)); - if (locked) + if (!locked) + goto done; + + if (dst_length < total_length) { - if (dst_length < total_length) - hr = MF_E_BUFFERTOOSMALL; - else if (dst_ptr) + hr = MF_E_BUFFERTOOSMALL; + goto done; + } + + if (!dst_ptr) + goto done; + + for (i = 0; i < sample->buffer_count && SUCCEEDED(hr); ++i) + { + src_ptr = NULL; + src_max_length = current_length = 0; + + if (FAILED(hr = IMFMediaBuffer_Lock(sample->buffers[i], &src_ptr, &src_max_length, ¤t_length))) + continue; + + if (src_ptr) { - for (i = 0; i < sample->buffer_count && SUCCEEDED(hr); ++i) + if (current_length > dst_length) + hr = MF_E_BUFFERTOOSMALL; + else if (current_length) { - src_ptr = NULL; - src_max_length = current_length = 0; - if (SUCCEEDED(hr = IMFMediaBuffer_Lock(sample->buffers[i], &src_ptr, &src_max_length, ¤t_length))) - { - if (src_ptr) - { - if (current_length > dst_length) - hr = MF_E_BUFFERTOOSMALL; - else if (current_length) - { - memcpy(dst_ptr, src_ptr, current_length); - dst_length -= current_length; - dst_current_length += current_length; - dst_ptr += current_length; - } - } - IMFMediaBuffer_Unlock(sample->buffers[i]); - } + memcpy(dst_ptr, src_ptr, current_length); + dst_length -= current_length; + dst_current_length += current_length; + dst_ptr += current_length; } } + IMFMediaBuffer_Unlock(sample->buffers[i]); } +done: IMFMediaBuffer_SetCurrentLength(buffer, dst_current_length); if (locked) From f8b15fbc99aed74431a7f1e8810e37390436289f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 May 2023 12:24:46 -0600 Subject: [PATCH 0638/1148] mfplat/sample: Optimize copying to 2d buffer. CW-Bug-Id: #22084 --- dlls/mfplat/sample.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/dlls/mfplat/sample.c b/dlls/mfplat/sample.c index 513b8bb4ca9..8e489d22acd 100644 --- a/dlls/mfplat/sample.c +++ b/dlls/mfplat/sample.c @@ -852,8 +852,9 @@ static HRESULT WINAPI sample_CopyToBuffer(IMFSample *iface, IMFMediaBuffer *buff struct sample *sample = impl_from_IMFSample(iface); DWORD total_length, dst_length, dst_current_length, src_max_length, current_length; BYTE *src_ptr, *dst_ptr; - BOOL locked; - HRESULT hr; + IMF2DBuffer *buffer2d; + BOOL locked = FALSE; + HRESULT hr = E_FAIL; size_t i; TRACE("%p, %p.\n", iface, buffer); @@ -872,6 +873,25 @@ static HRESULT WINAPI sample_CopyToBuffer(IMFSample *iface, IMFMediaBuffer *buff total_length = sample_get_total_length(sample); dst_current_length = 0; + if (sample->buffer_count == 1 + && SUCCEEDED(IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer, (void **)&buffer2d))) + { + if (SUCCEEDED(IMFMediaBuffer_GetCurrentLength(sample->buffers[0], ¤t_length)) + && SUCCEEDED(IMF2DBuffer_GetContiguousLength(buffer2d, &dst_length)) + && current_length == dst_length + && SUCCEEDED(IMFMediaBuffer_Lock(sample->buffers[0], &src_ptr, &src_max_length, ¤t_length))) + { + hr = IMF2DBuffer_ContiguousCopyFrom(buffer2d, src_ptr, current_length); + IMFMediaBuffer_Unlock(sample->buffers[0]); + } + IMF2DBuffer_Release(buffer2d); + if (SUCCEEDED(hr)) + { + dst_current_length = current_length; + goto done; + } + } + dst_ptr = NULL; dst_length = current_length = 0; locked = SUCCEEDED(hr = IMFMediaBuffer_Lock(buffer, &dst_ptr, &dst_length, ¤t_length)); From 86af89ca3f2e6f578d3991f5ff9f7c762111c778 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 5 Sep 2023 12:28:19 -0600 Subject: [PATCH 0639/1148] Revert "HACK: msmpeg2vdec: Refuse to load DLL for some games." This reverts commit 7267a0e38eee69af7042292bff435b98a8e1898b. CW-Bug-Id: #22084 --- dlls/msmpeg2vdec/Makefile.in | 3 --- dlls/msmpeg2vdec/main.c | 48 ------------------------------------ 2 files changed, 51 deletions(-) delete mode 100644 dlls/msmpeg2vdec/main.c diff --git a/dlls/msmpeg2vdec/Makefile.in b/dlls/msmpeg2vdec/Makefile.in index 609c6a34f9a..d2dbf5adda0 100644 --- a/dlls/msmpeg2vdec/Makefile.in +++ b/dlls/msmpeg2vdec/Makefile.in @@ -1,4 +1 @@ MODULE = msmpeg2vdec.dll - -C_SRCS = \ - main.c diff --git a/dlls/msmpeg2vdec/main.c b/dlls/msmpeg2vdec/main.c deleted file mode 100644 index 348d3d405b4..00000000000 --- a/dlls/msmpeg2vdec/main.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 - -#include "windef.h" -#include "winbase.h" - -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(mfplat); - -BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) -{ - TRACE( "instance %p, reason %#lx, reserved %p\n", instance, reason, reserved ); - - switch (reason) - { - case DLL_PROCESS_ATTACH: - { - const char *sgi; - if ((sgi = getenv( "SteamGameId" )) && !strcmp( sgi, "1498570" )) return FALSE; - DisableThreadLibraryCalls( instance ); - break; - } - case DLL_PROCESS_DETACH: - break; - } - - return TRUE; -} From 6171b883ad0b130bdbc50a39884c364e03803a2b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 7 Sep 2023 10:03:00 -0600 Subject: [PATCH 0640/1148] wtsapi32: Improve WTSEnumerateSessionsW() stub. CW-Bug-Id: #22715 --- dlls/wtsapi32/tests/wtsapi.c | 33 +++++++++++++++++++++++++++++++++ dlls/wtsapi32/wtsapi32.c | 27 ++++++++++++++++++++------- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/dlls/wtsapi32/tests/wtsapi.c b/dlls/wtsapi32/tests/wtsapi.c index 2023a21e938..02d6bb61d90 100644 --- a/dlls/wtsapi32/tests/wtsapi.c +++ b/dlls/wtsapi32/tests/wtsapi.c @@ -305,6 +305,38 @@ static void test_WTSQueryUserToken(void) ok(GetLastError()==ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER got: %ld\n", GetLastError()); } +static void test_WTSEnumerateSessions(void) +{ + BOOL console_found = FALSE, services_found = FALSE; + WTS_SESSION_INFOW *info; + unsigned int i; + DWORD count; + BOOL bret; + + bret = WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &info, &count); + ok(bret, "got error %lu.\n", GetLastError()); + todo_wine_if(count == 1) ok(count >= 2, "got %lu.\n", count); + + for (i = 0; i < count; ++i) + { + trace("SessionId %lu, name %s, State %d.\n", info[i].SessionId, debugstr_w(info[i].pWinStationName), info[i].State); + if (!wcscmp(info[i].pWinStationName, L"Console")) + { + console_found = TRUE; + ok(info[i].State == WTSActive, "got State %d.\n", info[i].State); + } + else if (!wcscmp(info[i].pWinStationName, L"Services")) + { + services_found = TRUE; + ok(info[i].State == WTSDisconnected, "got State %d.\n", info[i].State); + } + } + ok(console_found, "Console session not found.\n"); + todo_wine ok(services_found, "Services session not found.\n"); + + WTSFreeMemory(info); +} + START_TEST (wtsapi) { pWTSEnumerateProcessesExW = (void *)GetProcAddress(GetModuleHandleA("wtsapi32"), "WTSEnumerateProcessesExW"); @@ -313,4 +345,5 @@ START_TEST (wtsapi) test_WTSEnumerateProcessesW(); test_WTSQuerySessionInformation(); test_WTSQueryUserToken(); + test_WTSEnumerateSessions(); } diff --git a/dlls/wtsapi32/wtsapi32.c b/dlls/wtsapi32/wtsapi32.c index 7de1b8124ea..0071922d866 100644 --- a/dlls/wtsapi32/wtsapi32.c +++ b/dlls/wtsapi32/wtsapi32.c @@ -309,16 +309,29 @@ BOOL WINAPI WTSEnumerateSessionsA(HANDLE hServer, DWORD Reserved, DWORD Version, /************************************************************ * WTSEnumerateEnumerateSessionsW (WTSAPI32.@) */ -BOOL WINAPI WTSEnumerateSessionsW(HANDLE hServer, DWORD Reserved, DWORD Version, - PWTS_SESSION_INFOW* ppSessionInfo, DWORD* pCount) +BOOL WINAPI WTSEnumerateSessionsW(HANDLE server, DWORD reserved, DWORD version, + PWTS_SESSION_INFOW *session_info, DWORD *count) { - FIXME("Stub %p 0x%08lx 0x%08lx %p %p\n", hServer, Reserved, Version, - ppSessionInfo, pCount); + static const WCHAR session_name[] = L"Console"; - if (!ppSessionInfo || !pCount) return FALSE; + FIXME("%p 0x%08lx 0x%08lx %p %p semi-stub.\n", server, reserved, version, session_info, count); - *pCount = 0; - *ppSessionInfo = NULL; + if (!session_info || !count) return FALSE; + + if (!(*session_info = heap_alloc(sizeof(**session_info) + sizeof(session_name)))) + { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + if (!ProcessIdToSessionId( GetCurrentProcessId(), &(*session_info)->SessionId)) + { + WTSFreeMemory(*session_info); + return FALSE; + } + *count = 1; + (*session_info)->State = WTSActive; + (*session_info)->pWinStationName = (WCHAR *)((char *)*session_info + sizeof(**session_info)); + memcpy((*session_info)->pWinStationName, session_name, sizeof(session_name)); return TRUE; } From 0f268292b29415cc46dfff9652e729c76bd54f60 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 7 Sep 2023 10:51:07 -0600 Subject: [PATCH 0641/1148] wtsapi32: Implement WTSEnumerateSessionsA() on top of WTSEnumerateSessionsW(). CW-Bug-Id: #22715 --- dlls/wtsapi32/tests/wtsapi.c | 10 ++++++- dlls/wtsapi32/wtsapi32.c | 55 ++++++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/dlls/wtsapi32/tests/wtsapi.c b/dlls/wtsapi32/tests/wtsapi.c index 02d6bb61d90..abd4825056d 100644 --- a/dlls/wtsapi32/tests/wtsapi.c +++ b/dlls/wtsapi32/tests/wtsapi.c @@ -309,14 +309,19 @@ static void test_WTSEnumerateSessions(void) { BOOL console_found = FALSE, services_found = FALSE; WTS_SESSION_INFOW *info; + WTS_SESSION_INFOA *infoA; + DWORD count, count2; unsigned int i; - DWORD count; BOOL bret; bret = WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &info, &count); ok(bret, "got error %lu.\n", GetLastError()); todo_wine_if(count == 1) ok(count >= 2, "got %lu.\n", count); + bret = WTSEnumerateSessionsA(WTS_CURRENT_SERVER_HANDLE, 0, 1, &infoA, &count2); + ok(bret, "got error %lu.\n", GetLastError()); + ok(count2 == count, "got %lu.\n", count2); + for (i = 0; i < count; ++i) { trace("SessionId %lu, name %s, State %d.\n", info[i].SessionId, debugstr_w(info[i].pWinStationName), info[i].State); @@ -324,17 +329,20 @@ static void test_WTSEnumerateSessions(void) { console_found = TRUE; ok(info[i].State == WTSActive, "got State %d.\n", info[i].State); + ok(!strcmp(infoA[i].pWinStationName, "Console"), "got %s.\n", debugstr_a(infoA[i].pWinStationName)); } else if (!wcscmp(info[i].pWinStationName, L"Services")) { services_found = TRUE; ok(info[i].State == WTSDisconnected, "got State %d.\n", info[i].State); + ok(!strcmp(infoA[i].pWinStationName, "Services"), "got %s.\n", debugstr_a(infoA[i].pWinStationName)); } } ok(console_found, "Console session not found.\n"); todo_wine ok(services_found, "Services session not found.\n"); WTSFreeMemory(info); + WTSFreeMemory(infoA); } START_TEST (wtsapi) diff --git a/dlls/wtsapi32/wtsapi32.c b/dlls/wtsapi32/wtsapi32.c index 0071922d866..6eaea8d3c6f 100644 --- a/dlls/wtsapi32/wtsapi32.c +++ b/dlls/wtsapi32/wtsapi32.c @@ -266,7 +266,6 @@ BOOL WINAPI WTSEnumerateServersW(LPWSTR pDomainName, DWORD Reserved, DWORD Versi return FALSE; } - /************************************************************ * WTSEnumerateEnumerateSessionsExW (WTSAPI32.@) */ @@ -290,19 +289,57 @@ BOOL WINAPI WTSEnumerateSessionsExA(HANDLE server, DWORD *level, DWORD filter, W /************************************************************ * WTSEnumerateEnumerateSessionsA (WTSAPI32.@) */ -BOOL WINAPI WTSEnumerateSessionsA(HANDLE hServer, DWORD Reserved, DWORD Version, - PWTS_SESSION_INFOA* ppSessionInfo, DWORD* pCount) +BOOL WINAPI WTSEnumerateSessionsA(HANDLE server, DWORD reserved, DWORD version, + PWTS_SESSION_INFOA *session_info, DWORD *count) { - static int once; + PWTS_SESSION_INFOW infoW; + DWORD size, offset; + unsigned int i; + int len; - if (!once++) FIXME("Stub %p 0x%08lx 0x%08lx %p %p\n", hServer, Reserved, Version, - ppSessionInfo, pCount); + TRACE("%p 0x%08lx 0x%08lx %p %p.\n", server, reserved, version, session_info, count); - if (!ppSessionInfo || !pCount) return FALSE; + if (!session_info || !count) return FALSE; - *pCount = 0; - *ppSessionInfo = NULL; + if (!WTSEnumerateSessionsW(server, reserved, version, &infoW, count)) return FALSE; + + size = 0; + for (i = 0; i < *count; ++i) + { + if (!(len = WideCharToMultiByte(CP_ACP, 0, infoW[i].pWinStationName, -1, NULL, 0, NULL, NULL))) + { + ERR("WideCharToMultiByte failed.\n"); + WTSFreeMemory(infoW); + return FALSE; + } + size += sizeof(**session_info) + len; + } + + if (!(*session_info = heap_alloc(size))) + { + WTSFreeMemory(infoW); + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + offset = *count * sizeof(**session_info); + for (i = 0; i < *count; ++i) + { + (*session_info)[i].State = infoW[i].State; + (*session_info)[i].SessionId = infoW[i].SessionId; + (*session_info)[i].pWinStationName = (char *)(*session_info) + offset; + len = WideCharToMultiByte(CP_ACP, 0, infoW[i].pWinStationName, -1, (*session_info)[i].pWinStationName, + size - offset, NULL, NULL); + if (!len) + { + ERR("WideCharToMultiByte failed.\n"); + WTSFreeMemory(*session_info); + WTSFreeMemory(infoW); + } + offset += len; + } + WTSFreeMemory(infoW); return TRUE; } From 9e1bfd2b9eda77032590439cbae5cd01e75361f6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 7 Sep 2023 11:06:00 -0600 Subject: [PATCH 0642/1148] wtsapi32: Handle WTSConnectState class in WTSQuerySessionInformationW(). CW-Bug-Id: #22715 --- dlls/wtsapi32/tests/wtsapi.c | 15 +++++++++++++++ dlls/wtsapi32/wtsapi32.c | 24 +++++++++++++----------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/dlls/wtsapi32/tests/wtsapi.c b/dlls/wtsapi32/tests/wtsapi.c index abd4825056d..2748f63a132 100644 --- a/dlls/wtsapi32/tests/wtsapi.c +++ b/dlls/wtsapi32/tests/wtsapi.c @@ -191,10 +191,25 @@ static void test_WTSQuerySessionInformation(void) { WCHAR *buf1, usernameW[UNLEN + 1], computernameW[MAX_COMPUTERNAME_LENGTH + 1]; char *buf2, username[UNLEN + 1], computername[MAX_COMPUTERNAME_LENGTH + 1]; + WTS_CONNECTSTATE_CLASS *state; DWORD count, tempsize; USHORT *protocol; BOOL ret; + count = 0; + ret = WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, (WCHAR **)&state, &count); + ok(ret, "got error %lu\n", GetLastError()); + ok(count == sizeof(*state), "got %lu\n", count); + ok(*state == WTSActive, "got %d.\n", *state); + WTSFreeMemory(state); + + count = 0; + ret = WTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, (char **)&state, &count); + ok(ret, "got error %lu\n", GetLastError()); + ok(count == sizeof(*state), "got %lu\n", count); + ok(*state == WTSActive, "got %d.\n", *state); + WTSFreeMemory(state); + SetLastError(0xdeadbeef); count = 0; ret = WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSUserName, NULL, &count); diff --git a/dlls/wtsapi32/wtsapi32.c b/dlls/wtsapi32/wtsapi32.c index 6eaea8d3c6f..f4072e7090b 100644 --- a/dlls/wtsapi32/wtsapi32.c +++ b/dlls/wtsapi32/wtsapi32.c @@ -468,17 +468,8 @@ BOOL WINAPI WTSQuerySessionInformationA(HANDLE server, DWORD session_id, WTS_INF return FALSE; } - if (class == WTSClientProtocolType) - { - USHORT *protocol; - - if (!(protocol = heap_alloc(sizeof(*protocol)))) return FALSE; - FIXME("returning 0 protocol type\n"); - *protocol = 0; - *buffer = (char *)protocol; - *count = sizeof(*protocol); - return TRUE; - } + if (class == WTSClientProtocolType || class == WTSConnectState) + return WTSQuerySessionInformationW(server, session_id, class, (WCHAR **)buffer, count); if (!WTSQuerySessionInformationW(server, session_id, class, &bufferW, count)) return FALSE; @@ -520,6 +511,17 @@ BOOL WINAPI WTSQuerySessionInformationW(HANDLE server, DWORD session_id, WTS_INF return FALSE; } + if (class == WTSConnectState) + { + WTS_CONNECTSTATE_CLASS *state; + + if (!(state = heap_alloc(sizeof(*state)))) return FALSE; + *state = WTSActive; + *buffer = (WCHAR *)state; + *count = sizeof(*state); + return TRUE; + } + if (class == WTSClientProtocolType) { USHORT *protocol; From a1a1cd10f183e530cb6af537f2bc2f7e8af179cc Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Thu, 7 Sep 2023 20:26:43 +0300 Subject: [PATCH 0643/1148] Revert "nsiproxy.sys: Detect wireless interface type on Linux." This reverts commit e7fdbdab8b25bf8cde5d2ad1738a5ecf45643784. This commit is already included as a cherry-pick. Upon experimental rebase only whitespace removal was left - we can remove that. --- dlls/nsiproxy.sys/ndis.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/nsiproxy.sys/ndis.c b/dlls/nsiproxy.sys/ndis.c index 9c4fe80e865..73c047586a9 100644 --- a/dlls/nsiproxy.sys/ndis.c +++ b/dlls/nsiproxy.sys/ndis.c @@ -200,6 +200,7 @@ static NTSTATUS if_get_physical( const char *name, UINT *type, IF_PHYSICAL_ADDRE } #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 dd8bf9aa59fc5921b0a6d02bdbf30a612a995d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 7 Sep 2023 19:41:46 +0200 Subject: [PATCH 0644/1148] dinput: Assume that clipping the cursor succeeds. Fixes a regression (in Deathloop) where it falls back to SetCursorPos as we now sometimes shrink the clipping rectangle. CW-Bug-Id: #21707 --- dlls/dinput/mouse.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dlls/dinput/mouse.c b/dlls/dinput/mouse.c index ec30c825733..d376546f1a9 100644 --- a/dlls/dinput/mouse.c +++ b/dlls/dinput/mouse.c @@ -306,8 +306,8 @@ static void warp_check( struct mouse *impl, BOOL force ) if (force || (impl->need_warp && (now - impl->last_warped > interval))) { - RECT rect, new_rect; POINT mapped_center; + RECT rect; impl->last_warped = now; impl->need_warp = FALSE; @@ -328,8 +328,7 @@ static void warp_check( struct mouse *impl, BOOL force ) rect.right = min( rect.right, rect.left + GetSystemMetrics( SM_CXVIRTUALSCREEN ) - 2 ); rect.bottom = min( rect.bottom, rect.top + GetSystemMetrics( SM_CYVIRTUALSCREEN ) - 2 ); TRACE("Clipping mouse to %s\n", wine_dbgstr_rect( &rect )); - ClipCursor( &rect ); - impl->clipped = GetClipCursor( &new_rect ) && EqualRect( &rect, &new_rect ); + impl->clipped = ClipCursor( &rect ); } } } From 119b578887847b9147a49d72749c947d68ef4024 Mon Sep 17 00:00:00 2001 From: Derek Lesho Date: Thu, 7 Sep 2023 14:00:34 +0200 Subject: [PATCH 0645/1148] winegstreamer: Block protonvideoconvert autoplug when not trying for it. Signed-off-by: Derek Lesho --- dlls/winegstreamer/wg_parser.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 959a2141f2c..3e5afe49bf0 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -493,6 +493,11 @@ static GstAutoplugSelectResult autoplug_select_cb(GstElement *bin, GstPad *pad, } if (!strcmp(name, "QuickTime demuxer")) parser->using_qtdemux = true; + if (!strcmp(name, "Proton video converter") && !parser->use_mediaconv) + { + GST_INFO("Skipping \"Proton video converter\"."); + return GST_AUTOPLUG_SELECT_SKIP; + } return GST_AUTOPLUG_SELECT_TRY; } From a1c074a5eec1e9a005cdfaec915dd860e84c4ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 8 Sep 2023 14:24:11 +0200 Subject: [PATCH 0646/1148] winex11: Always ignore MotionNotify event after SetCursorPos. Trying to workaround spurious Wayland mouse motion. CW-Bug-Id: #22650 --- dlls/winex11.drv/mouse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 72a5d0173e4..2f208da3645 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1674,7 +1674,7 @@ BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *xev ) input.u.mi.time = x11drv_time_to_ticks( event->time ); input.u.mi.dwExtraInfo = 0; - if (!hwnd && is_old_motion_event( event->serial )) + if (is_old_motion_event( event->serial )) { TRACE( "pos %d,%d old serial %lu, ignoring\n", event->x, event->y, event->serial ); return FALSE; @@ -1935,7 +1935,7 @@ static BOOL X11DRV_XIDeviceEvent( XIDeviceEvent *event ) TRACE( "evtype %u hwnd %p/%lx pos %f,%f detail %u flags %#x serial %lu\n", event->evtype, hwnd, event->event, event->event_x, event->event_y, event->detail, event->flags, event->serial ); - if (!hwnd && is_old_motion_event( event->serial )) + if (is_old_motion_event( event->serial )) { TRACE( "pos %f,%f old serial %lu, ignoring\n", event->event_x, event->event_y, event->serial ); return FALSE; From be37b0664feefb420ca6dce18dfc1336eebf1f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 8 Sep 2023 19:26:51 +0200 Subject: [PATCH 0647/1148] dinput: Only call SetCursorPos if clipping fails. Fixes mouse sometimes jumping around in Mechwarrior Online on KDE/Wayland. CW-Bug-Id: #22650 --- dlls/dinput/mouse.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dlls/dinput/mouse.c b/dlls/dinput/mouse.c index d376546f1a9..2d4e0a6b42c 100644 --- a/dlls/dinput/mouse.c +++ b/dlls/dinput/mouse.c @@ -313,13 +313,6 @@ static void warp_check( struct mouse *impl, BOOL force ) impl->need_warp = FALSE; if (!GetClientRect( impl->base.win, &rect )) return; MapWindowPoints( impl->base.win, 0, (POINT *)&rect, 2 ); - if (!impl->clipped) - { - mapped_center.x = (rect.left + rect.right) / 2; - mapped_center.y = (rect.top + rect.bottom) / 2; - TRACE( "Warping mouse to x %+ld, y %+ld.\n", mapped_center.x, mapped_center.y ); - SetCursorPos( mapped_center.x, mapped_center.y ); - } if (impl->base.dwCoopLevel & DISCL_EXCLUSIVE) { /* make sure we clip even if the window covers the whole screen */ @@ -330,6 +323,13 @@ static void warp_check( struct mouse *impl, BOOL force ) TRACE("Clipping mouse to %s\n", wine_dbgstr_rect( &rect )); impl->clipped = ClipCursor( &rect ); } + if (!impl->clipped) + { + mapped_center.x = (rect.left + rect.right) / 2; + mapped_center.y = (rect.top + rect.bottom) / 2; + TRACE( "Warping mouse to x %+ld, y %+ld.\n", mapped_center.x, mapped_center.y ); + SetCursorPos( mapped_center.x, mapped_center.y ); + } } } From a16948553d06d9581d2e9553610c891464f1adbe Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 8 Sep 2023 16:28:09 -0600 Subject: [PATCH 0648/1148] winex11.drv: Use offscreen windows instead of pixmaps for layered windows GL drawing. CW-Bug-Id: #22716 --- dlls/winex11.drv/opengl.c | 50 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 3cf88a7d4bc..e35748b45e8 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -1522,21 +1522,7 @@ static struct gl_drawable *create_gl_drawable( HWND hwnd, const struct wgl_pixel gl->layered_type = get_gl_layered_type( hwnd ); - if (gl->layered_type) - { - detach_client_window( hwnd, 0 ); - gl->type = DC_GL_PIXMAP_WIN; - gl->pixmap = XCreatePixmap( gdi_display, root_window, width, height, visual->depth ); - if (gl->pixmap) - { - gl->drawable = pglXCreatePixmap( gdi_display, gl->format->fbconfig, gl->pixmap, NULL ); - if (!gl->drawable) XFreePixmap( gdi_display, gl->pixmap ); - gl->pixmap_size.cx = width; - gl->pixmap_size.cy = height; - } - TRACE( "%p created pixmap drawable %lx for layered window, type %u.\n", hwnd, gl->drawable, gl->layered_type ); - } - else if (!drawable_needs_clipping( hwnd, known_child )) /* childless top-level window */ + if (!gl->layered_type && !drawable_needs_clipping( hwnd, known_child )) /* childless top-level window */ { struct x11drv_win_data *data; @@ -1566,6 +1552,7 @@ static struct gl_drawable *create_gl_drawable( HWND hwnd, const struct wgl_pixel 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 ); + if (gl->layered_type) detach_client_window( hwnd, 0 ); TRACE( "%p created child %lx drawable %lx\n", hwnd, gl->window, gl->drawable ); } #endif @@ -1690,7 +1677,7 @@ void sync_gl_drawable( HWND hwnd, BOOL known_child ) known_child = drawable_needs_clipping( hwnd, known_child ); - if (old->type == DC_GL_PIXMAP_WIN || (known_child && old->type == DC_GL_WINDOW) + if (old->layered_type || (known_child && old->type == DC_GL_WINDOW) || (!known_child && old->type != DC_GL_WINDOW) || old->layered_type != new_layered_type) { @@ -3313,6 +3300,11 @@ static BOOL glxdrv_wglShareLists(struct wgl_context *org, struct wgl_context *de return FALSE; } +static int XGetImage_handler( Display *dpy, XErrorEvent *event, void *arg ) +{ + return event->request_code == X_GetImage && event->error_code == BadMatch; +} + static void update_window_surface(struct gl_drawable *gl, HWND hwnd) { char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )]; @@ -3326,7 +3318,7 @@ static void update_window_surface(struct gl_drawable *gl, HWND hwnd) TRACE( "gl %p, hwnd %p, gl->layered_type %u.\n", gl, hwnd, gl->layered_type ); - if (gl->layered_type != DC_GL_LAYERED_ATTRIBUTES || !gl->pixmap) return; + if (gl->layered_type != DC_GL_LAYERED_ATTRIBUTES || !gl->window) return; if (!(data = get_win_data( hwnd ))) return; @@ -3347,11 +3339,18 @@ static void update_window_surface(struct gl_drawable *gl, HWND hwnd) rect.right = min( rect.right, abs( bmi->bmiHeader.biWidth )); rect.bottom = min( rect.bottom, abs( bmi->bmiHeader.biHeight )); - width = min( rect.right - rect.left, gl->pixmap_size.cx ); - height = min( rect.bottom - rect.top, gl->pixmap_size.cy ); + width = rect.right - rect.left; + height = rect.bottom - rect.top; - image = XGetImage( gdi_display, gl->pixmap, 0, 0, width, height, + TRACE( "client_rect %s, whole_rect %s bmi %dx%d, rect %s.\n", + wine_dbgstr_rect(&data->client_rect), wine_dbgstr_rect(&data->whole_rect), + (int)bmi->bmiHeader.biWidth, (int)bmi->bmiHeader.biHeight, + wine_dbgstr_rect(&rect) ); + + X11DRV_expect_error( gdi_display, XGetImage_handler, NULL ); + image = XGetImage( gdi_display, gl->window, 0, 0, width, height, AllPlanes, ZPixmap ); + if (X11DRV_check_error()) ERR( "XGetImage error.\n" ); if (!image) { TRACE( "NULL image.\n" ); @@ -3373,7 +3372,6 @@ static void update_window_surface(struct gl_drawable *gl, HWND hwnd) for (y = 0; y < height; ++y) memcpy( dst_bits + (y + rect.top) * pitch + rect.left * stride, src_bits + y * image->bytes_per_line, width * stride ); - add_bounds_rect( surface->funcs->get_bounds( surface ), &rect ); done: @@ -3398,7 +3396,7 @@ static void wglFinish(void) switch (gl->type) { case DC_GL_PIXMAP_WIN: if (!gl->layered_type) escape.drawable = gl->pixmap; break; - case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + case DC_GL_CHILD_WIN: if (!gl->layered_type) escape.drawable = gl->window; break; default: break; } sync_context(ctx); @@ -3439,7 +3437,7 @@ static void wglFlush(void) switch (gl->type) { case DC_GL_PIXMAP_WIN: if (!gl->layered_type) escape.drawable = gl->pixmap; break; - case DC_GL_CHILD_WIN: escape.drawable = gl->window; break; + case DC_GL_CHILD_WIN: if (!gl->layered_type) escape.drawable = gl->window; break; default: break; } sync_context(ctx); @@ -4893,7 +4891,7 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc ) case DC_GL_WINDOW: case DC_GL_CHILD_WIN: if (ctx) sync_context( ctx ); - if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; + if (gl->type == DC_GL_CHILD_WIN && !gl->layered_type) escape.drawable = gl->window; /* fall through */ default: if (gl->fs_hack) @@ -4908,7 +4906,7 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc ) ctx->fs_hack = FALSE; fs_hack_setup_context( ctx, gl ); } - if (escape.drawable && pglXSwapBuffersMscOML) + if ((escape.drawable || gl->layered_type) && pglXSwapBuffersMscOML) { pglFlush(); target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); @@ -4918,7 +4916,7 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc ) break; } - if (escape.drawable && pglXWaitForSbcOML) + if ((escape.drawable || gl->layered_type) && pglXWaitForSbcOML) pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc ); update_window_surface( gl, hwnd ); From cbe3bb57855747dcd546e477ae3c8039f616cb8c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Sep 2023 14:47:07 -0600 Subject: [PATCH 0649/1148] combase/tests: Add tests for implicit MTA with RoGetActivationFactory(). CW-Bug-Id: #22728 --- dlls/combase/tests/roapi.c | 198 +++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/dlls/combase/tests/roapi.c b/dlls/combase/tests/roapi.c index f10cbb4507b..3959d7435b0 100644 --- a/dlls/combase/tests/roapi.c +++ b/dlls/combase/tests/roapi.c @@ -116,12 +116,210 @@ static void test_ActivationFactories(void) RoUninitialize(); } +static APTTYPE check_thread_apttype; +static APTTYPEQUALIFIER check_thread_aptqualifier; +static HRESULT check_thread_hr; + +static DWORD WINAPI check_apartment_thread(void *dummy) +{ + check_thread_apttype = 0xdeadbeef; + check_thread_aptqualifier = 0xdeadbeef; + check_thread_hr = CoGetApartmentType(&check_thread_apttype, &check_thread_aptqualifier); + return 0; +} + +#define check_thread_apartment(a) check_thread_apartment_(__LINE__, FALSE, a) +#define check_thread_apartment_broken(a) check_thread_apartment_(__LINE__, TRUE, a) +static void check_thread_apartment_(unsigned int line, BOOL broken_fail, HRESULT expected_hr_thread) +{ + HANDLE thread; + + check_thread_hr = 0xdeadbeef; + thread = CreateThread(NULL, 0, check_apartment_thread, NULL, 0, NULL); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + ok_(__FILE__, line)(check_thread_hr == expected_hr_thread + || broken(broken_fail && expected_hr_thread == S_OK && check_thread_hr == CO_E_NOTINITIALIZED), + "got %#lx, expected %#lx.\n", check_thread_hr, expected_hr_thread); + if (SUCCEEDED(check_thread_hr)) + { + ok_(__FILE__, line)(check_thread_apttype == APTTYPE_MTA, "got %d.\n", check_thread_apttype); + ok_(__FILE__, line)(check_thread_aptqualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, "got %d.\n", check_thread_aptqualifier); + } +} + +static HANDLE mta_init_thread_init_done_event, mta_init_thread_done_event; + +static DWORD WINAPI mta_init_thread(void *dummy) +{ + HRESULT hr; + + hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + ok(hr == S_OK, "got %#lx.\n", hr); + SetEvent(mta_init_thread_init_done_event); + + WaitForSingleObject(mta_init_thread_done_event, INFINITE); + CoUninitialize(); + return 0; +} + +static DWORD WINAPI mta_init_implicit_thread(void *dummy) +{ + IActivationFactory *factory; + HSTRING str; + HRESULT hr; + + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = WindowsCreateString(L"Does.Not.Exist", ARRAY_SIZE(L"Does.Not.Exist") - 1, &str); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); + ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); + WindowsDeleteString(str); + + SetEvent(mta_init_thread_init_done_event); + WaitForSingleObject(mta_init_thread_done_event, INFINITE); + + /* No CoUninitialize(), testing cleanup on thread exit. */ + return 0; +} + +static void test_implicit_mta(void) +{ + static const struct + { + BOOL ro_init; + BOOL mta; + BOOL ro_uninit; + } + tests[] = + { + { TRUE, TRUE, TRUE }, + { TRUE, FALSE, FALSE }, + { TRUE, FALSE, TRUE }, + { FALSE, FALSE, FALSE }, + { FALSE, FALSE, TRUE }, + }; + APTTYPEQUALIFIER aptqualifier; + IActivationFactory *factory; + APTTYPE apttype; + unsigned int i; + HANDLE thread; + HSTRING str; + HRESULT hr; + + hr = WindowsCreateString(L"Does.Not.Exist", ARRAY_SIZE(L"Does.Not.Exist") - 1, &str); + ok(hr == S_OK, "got %#lx.\n", hr); + /* RoGetActivationFactory doesn't implicitly initialize COM. */ + hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); + todo_wine ok(hr == CO_E_NOTINITIALIZED, "got %#lx.\n", hr); + + check_thread_apartment(CO_E_NOTINITIALIZED); + + /* RoGetActivationFactory initializes implicit MTA. */ + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + winetest_push_context("test %u", i); + if (tests[i].ro_init) + hr = RoInitialize(tests[i].mta ? RO_INIT_MULTITHREADED : RO_INIT_SINGLETHREADED); + else + hr = CoInitializeEx(NULL, tests[i].mta ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED); + ok(hr == S_OK, "got %#lx.\n", hr); + check_thread_apartment(tests[i].mta ? S_OK : CO_E_NOTINITIALIZED); + hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); + ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); + todo_wine_if(!tests[i].mta) check_thread_apartment_broken(S_OK); /* Broken on Win8. */ + hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); + ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); + todo_wine_if(!tests[i].mta) check_thread_apartment_broken(S_OK); /* Broken on Win8. */ + if (tests[i].ro_uninit) + RoUninitialize(); + else + CoUninitialize(); + check_thread_apartment(CO_E_NOTINITIALIZED); + winetest_pop_context(); + } + + mta_init_thread_init_done_event = CreateEventW(NULL, FALSE, FALSE, NULL); + mta_init_thread_done_event = CreateEventW(NULL, FALSE, FALSE, NULL); + + /* RoGetActivationFactory references implicit MTA in a current thread + * even if implicit MTA was already initialized: check with STA init + * after RoGetActivationFactory(). */ + thread = CreateThread(NULL, 0, mta_init_thread, NULL, 0, NULL); + ok(!!thread, "failed.\n"); + WaitForSingleObject(mta_init_thread_init_done_event, INFINITE); + check_thread_apartment(S_OK); + + hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); + ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); + check_thread_apartment(S_OK); + + hr = CoGetApartmentType(&apttype, &aptqualifier); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(apttype == APTTYPE_MTA, "got %d.\n", apttype); + ok(aptqualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, "got %d.\n", aptqualifier); + + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = CoGetApartmentType(&apttype, &aptqualifier); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(apttype == APTTYPE_MAINSTA, "got %d.\n", apttype); + ok(aptqualifier == APTTYPEQUALIFIER_NONE, "got %d.\n", aptqualifier); + + SetEvent(mta_init_thread_done_event); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + todo_wine check_thread_apartment_broken(S_OK); /* Broken on Win8. */ + CoUninitialize(); + check_thread_apartment(CO_E_NOTINITIALIZED); + + /* RoGetActivationFactory references implicit MTA in a current thread + * even if implicit MTA was already initialized: check with STA init + * before RoGetActivationFactory(). */ + thread = CreateThread(NULL, 0, mta_init_thread, NULL, 0, NULL); + ok(!!thread, "failed.\n"); + WaitForSingleObject(mta_init_thread_init_done_event, INFINITE); + check_thread_apartment(S_OK); + + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); + ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); + check_thread_apartment(S_OK); + + SetEvent(mta_init_thread_done_event); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + todo_wine check_thread_apartment_broken(S_OK); /* Broken on Win8. */ + CoUninitialize(); + check_thread_apartment(CO_E_NOTINITIALIZED); + + /* Test implicit MTA apartment thread exit. */ + thread = CreateThread(NULL, 0, mta_init_implicit_thread, NULL, 0, NULL); + ok(!!thread, "failed.\n"); + WaitForSingleObject(mta_init_thread_init_done_event, INFINITE); + todo_wine check_thread_apartment_broken(S_OK); /* Broken on Win8. */ + SetEvent(mta_init_thread_done_event); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + check_thread_apartment(CO_E_NOTINITIALIZED); + + CloseHandle(mta_init_thread_init_done_event); + CloseHandle(mta_init_thread_done_event); + WindowsDeleteString(str); +} + START_TEST(roapi) { BOOL ret; load_resource(L"wine.combase.test.dll"); + test_implicit_mta(); test_ActivationFactories(); SetLastError(0xdeadbeef); From b91b97ba1063945234fdf7f349ef8f0e38fcba56 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Sep 2023 12:53:27 -0600 Subject: [PATCH 0650/1148] combase: Create implicit MTA in STA apartment in RoGetActivationFactory(). CW-Bug-Id: #22728 --- dlls/combase/apartment.c | 32 ++++++++++++++++++++++++++++++++ dlls/combase/combase.c | 3 +++ dlls/combase/combase_private.h | 2 ++ dlls/combase/roapi.c | 5 +++++ dlls/combase/tests/roapi.c | 12 ++++++------ dlls/ole32/compobj_private.h | 1 + 6 files changed, 49 insertions(+), 6 deletions(-) diff --git a/dlls/combase/apartment.c b/dlls/combase/apartment.c index eb63fea6ef8..38a58aa0e72 100644 --- a/dlls/combase/apartment.c +++ b/dlls/combase/apartment.c @@ -1159,6 +1159,11 @@ void leave_apartment(struct tlsdata *data) if (data->ole_inits) WARN( "Uninitializing apartment while Ole is still initialized\n" ); apartment_release(data->apt); + if (data->implicit_mta) + { + apartment_release(data->implicit_mta); + data->implicit_mta = NULL; + } data->apt = NULL; data->flags &= ~(OLETLS_DISABLE_OLE1DDE | OLETLS_APARTMENTTHREADED | OLETLS_MULTITHREADED); } @@ -1290,3 +1295,30 @@ void apartment_global_cleanup(void) apartment_release_dlls(); DeleteCriticalSection(&apt_cs); } + +HRESULT reference_implicit_mta_from_sta(void) +{ + struct tlsdata *data; + HRESULT hr; + struct apartment *apt, *apt_mt; + + if (FAILED(hr = com_get_tlsdata(&data))) + return hr; + if ((apt = data->apt) && (data->implicit_mta || apt->multi_threaded)) + return S_OK; + + EnterCriticalSection(&apt_cs); + if (apt && !mta) + apt_mt = mta = apartment_construct(COINIT_MULTITHREADED); + else if ((apt_mt = mta)) + apartment_addref(mta); + LeaveCriticalSection(&apt_cs); + + if (!apt_mt) + { + ERR("Apartment not initialized.\n"); + return CO_E_NOTINITIALIZED; + } + data->implicit_mta = apt_mt; + return S_OK; +} diff --git a/dlls/combase/combase.c b/dlls/combase/combase.c index 0695bb77405..16c149679df 100644 --- a/dlls/combase/combase.c +++ b/dlls/combase/combase.c @@ -410,6 +410,9 @@ static void com_cleanup_tlsdata(void) if (tlsdata->apt) apartment_release(tlsdata->apt); + if (tlsdata->implicit_mta) + apartment_release(tlsdata->implicit_mta); + if (tlsdata->errorinfo) IErrorInfo_Release(tlsdata->errorinfo); if (tlsdata->state) diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h index 19e3def0b4e..79f3aba91b0 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -92,6 +92,7 @@ struct tlsdata struct list spies; /* Spies installed with CoRegisterInitializeSpy */ DWORD spies_lock; DWORD cancelcount; + struct apartment *implicit_mta; /* mta referenced by roapi from sta thread */ }; extern HRESULT WINAPI InternalTlsAllocData(struct tlsdata **data); @@ -161,6 +162,7 @@ void apartment_release(struct apartment *apt) DECLSPEC_HIDDEN; struct apartment * apartment_get_current_or_mta(void) DECLSPEC_HIDDEN; HRESULT apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE *cookie) DECLSPEC_HIDDEN; void apartment_decrement_mta_usage(CO_MTA_USAGE_COOKIE cookie) DECLSPEC_HIDDEN; +HRESULT reference_implicit_mta_from_sta(void) DECLSPEC_HIDDEN; struct apartment * apartment_get_mta(void) DECLSPEC_HIDDEN; HRESULT apartment_get_inproc_class_object(struct apartment *apt, const struct class_reg_data *regdata, REFCLSID rclsid, REFIID riid, DWORD class_context, void **ppv) DECLSPEC_HIDDEN; diff --git a/dlls/combase/roapi.c b/dlls/combase/roapi.c index 78f35de39d4..807a9059fa7 100644 --- a/dlls/combase/roapi.c +++ b/dlls/combase/roapi.c @@ -24,6 +24,8 @@ #include "roerrorapi.h" #include "winstring.h" +#include "combase_private.h" + #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(combase); @@ -163,6 +165,9 @@ HRESULT WINAPI RoGetActivationFactory(HSTRING classid, REFIID iid, void **class_ if (!iid || !class_factory) return E_INVALIDARG; + if (FAILED(hr = reference_implicit_mta_from_sta())) + return hr; + hr = get_library_for_classid(WindowsGetStringRawBuffer(classid, NULL), &library); if (FAILED(hr)) { diff --git a/dlls/combase/tests/roapi.c b/dlls/combase/tests/roapi.c index 3959d7435b0..ef0035dae4e 100644 --- a/dlls/combase/tests/roapi.c +++ b/dlls/combase/tests/roapi.c @@ -213,7 +213,7 @@ static void test_implicit_mta(void) ok(hr == S_OK, "got %#lx.\n", hr); /* RoGetActivationFactory doesn't implicitly initialize COM. */ hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); - todo_wine ok(hr == CO_E_NOTINITIALIZED, "got %#lx.\n", hr); + ok(hr == CO_E_NOTINITIALIZED, "got %#lx.\n", hr); check_thread_apartment(CO_E_NOTINITIALIZED); @@ -229,10 +229,10 @@ static void test_implicit_mta(void) check_thread_apartment(tests[i].mta ? S_OK : CO_E_NOTINITIALIZED); hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); - todo_wine_if(!tests[i].mta) check_thread_apartment_broken(S_OK); /* Broken on Win8. */ + check_thread_apartment_broken(S_OK); /* Broken on Win8. */ hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); ok(hr == REGDB_E_CLASSNOTREG, "got %#lx.\n", hr); - todo_wine_if(!tests[i].mta) check_thread_apartment_broken(S_OK); /* Broken on Win8. */ + check_thread_apartment_broken(S_OK); /* Broken on Win8. */ if (tests[i].ro_uninit) RoUninitialize(); else @@ -272,7 +272,7 @@ static void test_implicit_mta(void) SetEvent(mta_init_thread_done_event); WaitForSingleObject(thread, INFINITE); CloseHandle(thread); - todo_wine check_thread_apartment_broken(S_OK); /* Broken on Win8. */ + check_thread_apartment_broken(S_OK); /* Broken on Win8. */ CoUninitialize(); check_thread_apartment(CO_E_NOTINITIALIZED); @@ -294,7 +294,7 @@ static void test_implicit_mta(void) SetEvent(mta_init_thread_done_event); WaitForSingleObject(thread, INFINITE); CloseHandle(thread); - todo_wine check_thread_apartment_broken(S_OK); /* Broken on Win8. */ + check_thread_apartment_broken(S_OK); /* Broken on Win8. */ CoUninitialize(); check_thread_apartment(CO_E_NOTINITIALIZED); @@ -302,7 +302,7 @@ static void test_implicit_mta(void) thread = CreateThread(NULL, 0, mta_init_implicit_thread, NULL, 0, NULL); ok(!!thread, "failed.\n"); WaitForSingleObject(mta_init_thread_init_done_event, INFINITE); - todo_wine check_thread_apartment_broken(S_OK); /* Broken on Win8. */ + check_thread_apartment_broken(S_OK); /* Broken on Win8. */ SetEvent(mta_init_thread_done_event); WaitForSingleObject(thread, INFINITE); CloseHandle(thread); diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h index 34f5a8ec485..ad6b70d7a44 100644 --- a/dlls/ole32/compobj_private.h +++ b/dlls/ole32/compobj_private.h @@ -63,6 +63,7 @@ struct oletls struct list spies; /* Spies installed with CoRegisterInitializeSpy */ DWORD spies_lock; DWORD cancelcount; + struct apartment *implicit_mta; /* mta referenced by roapi from sta thread */ }; /* Global Interface Table Functions */ From ed51426269e7fbfe32391b6d1083a72441cc13ab Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 14 Sep 2023 16:20:08 -0600 Subject: [PATCH 0651/1148] d3dx9: Handle special adjacency index value in d3dx9_mesh_OptimizeInplace(). CW-Bug-Id: #22737 --- dlls/d3dx9_36/mesh.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c index f033ac14cb7..47a0cadde67 100644 --- a/dlls/d3dx9_36/mesh.c +++ b/dlls/d3dx9_36/mesh.c @@ -1624,6 +1624,13 @@ static HRESULT remap_faces_for_attrsort(struct d3dx9_mesh *This, const DWORD *in return D3D_OK; } +static DWORD adjacency_remap(DWORD *face_remap, DWORD index) +{ + if (index == 0xffffffff) + return index; + return face_remap[index]; +} + static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in, DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out) { @@ -1778,9 +1785,10 @@ static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, for (i = 0; i < This->numfaces; i++) { DWORD old_pos = i * 3; DWORD new_pos = face_remap[i] * 3; - adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]]; - adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]]; - adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]]; + + adjacency_out[new_pos++] = adjacency_remap(face_remap, adjacency_in[old_pos++]); + adjacency_out[new_pos++] = adjacency_remap(face_remap, adjacency_in[old_pos++]); + adjacency_out[new_pos] = adjacency_remap(face_remap, adjacency_in[old_pos]); } } else { memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out)); From 2223786722e740508cf36b6e0cb9cacda50314ea Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 14 Sep 2023 16:20:36 -0600 Subject: [PATCH 0652/1148] d3dx9/tests: Add a basic test for d3dx9_mesh_OptimizeInplace(). CW-Bug-Id: #22737 --- dlls/d3dx9_36/tests/mesh.c | 117 +++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c index f44e8f9be52..c988c368382 100644 --- a/dlls/d3dx9_36/tests/mesh.c +++ b/dlls/d3dx9_36/tests/mesh.c @@ -11439,6 +11439,122 @@ static void test_load_skin_mesh_from_xof(void) DestroyWindow(hwnd); } +static void test_mesh_optimize(void) +{ +/* + * . _ . + * / \ / \ + * . _ . _ . + * \ / \ / + * . _ . + */ + static const struct + { + float c[3]; + float n[3]; + float t[2]; + } + vertices[] = + { + { {-0.5f, -1.0f, 0.0f,}, {0.0f, 0.0f, 1.0f}, {-0.5f, -1.0f} }, + { { 0.5f, -1.0f, 0.0f,}, {0.0f, 0.0f, 1.0f}, { 0.5f, -1.0f} }, + + { {-1.0f, 0.0f, 0.0f,}, {0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f} }, + { { 0.0f, 0.0f, 0.0f,}, {0.0f, 0.0f, 1.0f}, { 0.0f, 0.0f} }, + { { 1.0f, 0.0f, 0.0f,}, {0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f} }, + + { {-0.5f, 1.0f, 0.0f,}, {0.0f, 0.0f, 1.0f}, {-0.5f, 1.0f} }, + { { 0.5f, 1.0f, 0.0f,}, {0.0f, 0.0f, 1.0f}, { 0.5f, 1.0f} }, + }; + static const unsigned short indices[] = + { + 3, 0, 2, + 3, 2, 5, + 3, 5, 6, + 3, 6, 4, + 3, 4, 1, + 3, 1, 0, + }; + static const DWORD attrs[] = { 1, 2, 1, 2, 1, 2 }; + static const DWORD expected_adjacency[] = + { + 5, 0xffffffff, 1, + 0, 0xffffffff, 2, + 1, 0xffffffff, 3, + 2, 0xffffffff, 4, + 3, 0xffffffff, 5, + 4, 0xffffffff, 0, + }; + static const DWORD expected_adjacency_out[] = + { + 5, 0xffffffff, 3, + 3, 0xffffffff, 4, + 4, 0xffffffff, 5, + 0, 0xffffffff, 1, + 1, 0xffffffff, 2, + 2, 0xffffffff, 0, + }; + + DWORD adjacency[6 * 3], adjacency_out[6 * 3]; + struct test_context *test_context; + IDirect3DDevice9 *device; + ID3DXBuffer *buffer; + ID3DXMesh *mesh; + unsigned int i; + DWORD size; + HRESULT hr; + void *data; + + test_context = new_test_context(); + if (!test_context) + { + skip("Couldn't create test context\n"); + return; + } + device = test_context->device; + + hr = D3DXCreateMeshFVF(ARRAY_SIZE(attrs), ARRAY_SIZE(vertices), D3DXMESH_VB_MANAGED | D3DXMESH_IB_MANAGED, + D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1, device, &mesh); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, &data); + ok(hr == S_OK, "got %#lx.\n", hr); + memcpy(data, vertices, sizeof(vertices)); + hr = mesh->lpVtbl->UnlockVertexBuffer(mesh); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &data); + ok(hr == S_OK, "got %#lx.\n", hr); + memcpy(data, indices, sizeof(indices)); + hr = mesh->lpVtbl->UnlockIndexBuffer(mesh); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, (DWORD **)&data); + ok(hr == S_OK, "got %#lx.\n", hr); + memcpy(data, attrs, sizeof(attrs)); + hr = mesh->lpVtbl->UnlockAttributeBuffer(mesh); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = mesh->lpVtbl->GenerateAdjacency(mesh, 0.0f, adjacency); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(!memcmp(adjacency, expected_adjacency, sizeof(adjacency)), "data mismatch.\n"); + + hr = mesh->lpVtbl->OptimizeInplace(mesh, D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_DONOTSPLIT, + adjacency, adjacency_out, NULL, &buffer); + ok(hr == S_OK, "got %#lx.\n", hr); + + size = buffer->lpVtbl->GetBufferSize(buffer); + ok(size == sizeof(DWORD) * ARRAY_SIZE(vertices), "got %lu.\n", size); + data = buffer->lpVtbl->GetBufferPointer(buffer); + for (i = 0; i < ARRAY_SIZE(vertices); ++i) + ok(((DWORD *)data)[i] == i, "i %u, got %lu.\n", i, ((DWORD *)data)[i]); + ok(!memcmp(adjacency_out, expected_adjacency_out, sizeof(adjacency)), "data mismatch.\n"); + + buffer->lpVtbl->Release(buffer); + mesh->lpVtbl->Release(mesh); + free_test_context(test_context); +} + START_TEST(mesh) { D3DXBoundProbeTest(); @@ -11472,4 +11588,5 @@ START_TEST(mesh) test_compute_normals(); test_D3DXFrameFind(); test_load_skin_mesh_from_xof(); + test_mesh_optimize(); } From fba48ad5682705f1ef48f5cf33424bc7ba8e4bf8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 15 Sep 2023 15:13:10 -0600 Subject: [PATCH 0653/1148] kernelbase, winex11.drv: HACK: Add --use-angle=gl for Red Tie Runner. And override Nvidia in GL so that works on Nvidia. CW-Bug-Id: #22742 --- dlls/kernelbase/process.c | 1 + dlls/winex11.drv/opengl.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 7afd5f3a38e..3c52ef6fa3c 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -594,6 +594,7 @@ static const WCHAR *hack_append_command_line( const WCHAR *cmd ) {L"PaladinLias\\Game.exe", L" --use-gl=desktop"}, {L"EverQuest 2\\LaunchPad.exe", L" --use-gl=swiftshader"}, {L"Everquest F2P\\LaunchPad.exe", L" --use-gl=swiftshader"}, + {L"Red Tie Runner.exe", L" --use-angle=gl"}, }; unsigned int i; diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index e35748b45e8..0380aa4cc63 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -3484,7 +3484,7 @@ static const GLubyte *wglGetString(GLenum name) if ((sz = read(fd, buffer, sizeof(buffer) - 1)) > 0) { buffer[sz] = 0; - if (strstr(buffer, "\\Paradox Launcher.exe")) + if (strstr(buffer, "\\Paradox Launcher.exe") || strstr(buffer, "Red Tie Runner.exe")) { FIXME("HACK: overriding GL vendor and renderer.\n"); override_vendor = 1; From 4cb5d6f81e1dedd8de2d62a9124df1812705895d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 15 Sep 2023 15:16:10 -0600 Subject: [PATCH 0654/1148] ntdll: HACK: Enable WINE_SIMULATE_WRITECOPY for Red Tie Runner. CW-Bug-Id: #22742 --- 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 3e4790249f8..b9b6f66c52f 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2363,6 +2363,7 @@ static void hacks_init(void) || !strcmp(sgi, "1680700") /* Purgo box */ || !strcmp(sgi, "2095300") /* Breakout 13 */ || !strcmp(sgi, "2053940") /* Idol Hands 2 */ + || !strcmp(sgi, "391150") /* Red Tie Runner */ || !strcmp(sgi, "2176450"); /* Mr. Hopp's Playhouse 3 */ if (main_argc > 1 && strstr(main_argv[1], "MicrosoftEdgeUpdate.exe")) From 49c83cda5a07b450700c4fe046897b56ffc2afea Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Sun, 2 Jul 2023 22:22:42 +0200 Subject: [PATCH 0655/1148] dcomp: Add stub for DCompositionCreateDevice3. This is needed by recent chromium CW-Bug-Id: #22749 --- dlls/dcomp/dcomp.spec | 2 +- dlls/dcomp/device.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dlls/dcomp/dcomp.spec b/dlls/dcomp/dcomp.spec index 8d625cc036a..2120ce8b6f6 100644 --- a/dlls/dcomp/dcomp.spec +++ b/dlls/dcomp/dcomp.spec @@ -14,7 +14,7 @@ @ stub DCompositionAttachMouseDragToHwnd @ stub DCompositionAttachMouseWheelToHwnd @ stdcall DCompositionCreateDevice2(ptr ptr ptr) -@ stub DCompositionCreateDevice3 +@ stdcall DCompositionCreateDevice3(ptr ptr ptr) @ stdcall DCompositionCreateDevice(ptr ptr ptr) @ stub DCompositionCreateSurfaceHandle @ stub DeserializeEffectDescription diff --git a/dlls/dcomp/device.c b/dlls/dcomp/device.c index 2d0600ce50a..2744d758e91 100644 --- a/dlls/dcomp/device.c +++ b/dlls/dcomp/device.c @@ -38,3 +38,10 @@ HRESULT WINAPI DCompositionCreateDevice2(IUnknown *rendering_device, REFIID iid, return E_NOTIMPL; } + +HRESULT WINAPI DCompositionCreateDevice3(IUnknown *rendering_device, REFIID iid, void **device) +{ + FIXME("%p, %s, %p.\n", rendering_device, debugstr_guid(iid), device); + + return E_NOTIMPL; +} From 00c1e8b951428cbf39cab9c80fa2ccbb2164e8c3 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Sep 2023 19:10:05 -0600 Subject: [PATCH 0656/1148] mf/tests: Add tests for AAC decoder with different input number of channels. CW-Bug-Id: #22717 --- dlls/mf/tests/transform.c | 168 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 60ea02313ac..2ab39f33c58 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -35,6 +35,8 @@ #include "wmcodecdsp.h" #include "mediaerr.h" #include "amvideo.h" +#include "ks.h" +#include "ksmedia.h" #include "mf_test.h" @@ -2530,6 +2532,169 @@ static void test_aac_decoder_subtype(const struct attribute_desc *input_type_des CoUninitialize(); } +static void test_aac_decoder_channels(const struct attribute_desc *input_type_desc) +{ + static const struct attribute_desc expect_output_attributes[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), + {0}, + }; + static const media_type_desc expect_available_outputs[] = + { + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_Float), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32), + }, + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio), + ATTR_GUID(MF_MT_SUBTYPE, MFAudioFormat_PCM), + ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16), + }, + }; + static const UINT32 expected_mask[7] = + { + 0, + 0, + 0, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_CENTER, + KSAUDIO_SPEAKER_QUAD, + KSAUDIO_SPEAKER_QUAD | SPEAKER_FRONT_CENTER, + KSAUDIO_SPEAKER_5POINT1, + }; + + UINT32 value, num_channels, expected_chans, format_index, sample_size; + unsigned int num_channels_index = ~0u; + struct attribute_desc input_desc[64]; + IMFTransform *transform; + IMFAttributes *attrs; + IMFMediaType *type; + BOOL many_channels; + ULONG i, ret; + HRESULT hr; + + for (i = 0; i < ARRAY_SIZE(input_desc); i++) + { + input_desc[i] = input_type_desc[i]; + if (!input_desc[i].key) + break; + if (IsEqualGUID(input_desc[i].key, &MF_MT_AUDIO_NUM_CHANNELS)) + num_channels_index = i; + } + + ok(num_channels_index != ~0u, "Could not find MF_MT_AUDIO_NUM_CHANNELS.\n"); + ok(i < ARRAY_SIZE(input_desc), "Too many attributes.\n"); + + hr = CoInitialize(NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + winetest_push_context("aacdec channels"); + + if (FAILED(hr = CoCreateInstance(&CLSID_MSAACDecMFT, NULL, CLSCTX_INPROC_SERVER, + &IID_IMFTransform, (void **)&transform))) + { + win_skip("AAC decoder transform is not available.\n"); + goto failed; + } + + for (num_channels = 0; num_channels < 16; ++num_channels) + { + many_channels = num_channels > 2; + winetest_push_context("chans %u", num_channels); + input_desc[num_channels_index].value.ulVal = num_channels; + + hr = MFCreateMediaType(&type); + ok(hr == S_OK, "got %#lx.\n", hr); + init_media_type(type, input_desc, -1); + hr = IMFTransform_SetInputType(transform, 0, type, 0); + IMFMediaType_Release(type); + if (num_channels <= 6) + ok(hr == S_OK, "got %#lx.\n", hr); + else + { + todo_wine ok(hr == MF_E_INVALIDMEDIATYPE, "got %#lx.\n", hr); + winetest_pop_context(); + continue; + } + + i = -1; + while (SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, ++i, &type))) + { + winetest_push_context("out %lu", i); + ok(hr == S_OK, "got %#lx.\n", hr); + check_media_type(type, expect_output_attributes, -1); + format_index = i % 2; + sample_size = format_index ? 2 : 4; + check_media_type(type, expect_available_outputs[format_index], -1); + attrs = (IMFAttributes *)type; + + hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_NUM_CHANNELS, &value); + ok(hr == S_OK, "got %#lx.\n", hr); + if (!num_channels || i >= ARRAY_SIZE(expect_available_outputs)) + expected_chans = 2; + else + expected_chans = num_channels; + todo_wine_if(!num_channels) + ok(value == expected_chans, "got %u, expected %u.\n", value, expected_chans); + + hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &value); + ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine_if(!num_channels) + ok(value == sample_size * 44100 * expected_chans, "got %u, expected %u.\n", + value, sample_size * 44100 * expected_chans); + + hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &value); + ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine_if(!num_channels) + ok(value == sample_size * expected_chans, "got %u, expected %u.\n", value, sample_size * expected_chans); + + hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_PREFER_WAVEFORMATEX, &value); + if (many_channels && i < ARRAY_SIZE(expect_available_outputs)) + { + todo_wine ok(hr == MF_E_ATTRIBUTENOTFOUND, "got %#lx.\n", hr); + } + else + { + ok(hr == S_OK, "got %#lx.\n", hr); + ok(value == 1, "got %u.\n", value); + } + + value = 0xdeadbeef; + hr = IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, &value); + if (expected_chans <= 2) + { + ok(hr == MF_E_ATTRIBUTENOTFOUND, "got %#lx.\n", hr); + } + else + { + todo_wine { + ok(hr == S_OK, "got %#lx.\n", hr); + ok(value == expected_mask[expected_chans], "got %#x, expected %#x.\n", + value, expected_mask[expected_chans]); + } + } + + ret = IMFMediaType_Release(type); + ok(ret <= 1, "got %lu.\n", ret); + winetest_pop_context(); + } + ok(hr == MF_E_NO_MORE_TYPES, "got %#lx.\n", hr); + if (many_channels) + todo_wine ok(i == ARRAY_SIZE(expect_available_outputs) * 2, "got %lu media types.\n", i); + else + ok(i == ARRAY_SIZE(expect_available_outputs), "got %lu media types.\n", i); + winetest_pop_context(); + } + + ret = IMFTransform_Release(transform); + ok(!ret, "got %lu.\n", ret); + +failed: + winetest_pop_context(); + CoUninitialize(); +} + static void test_aac_decoder(void) { static const BYTE aac_raw_codec_data[] = {0x12, 0x08}; @@ -2558,6 +2723,9 @@ static void test_aac_decoder(void) test_aac_decoder_subtype(aac_input_type_desc); test_aac_decoder_subtype(raw_aac_input_type_desc); + + test_aac_decoder_channels(aac_input_type_desc); + test_aac_decoder_channels(raw_aac_input_type_desc); } static const BYTE wma_codec_data[10] = {0, 0x44, 0, 0, 0x17, 0, 0, 0, 0, 0}; From e538c75f8685e2c01856514d85df10171c412afc Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Sep 2023 19:22:37 -0600 Subject: [PATCH 0657/1148] winegstreamer: Handle missing or zero channel count in _GetOutputAvailableType in AAC decoder. CW-Bug-Id: #22717 --- dlls/mf/tests/transform.c | 19 ++++++++++++++++--- dlls/winegstreamer/aac_decoder.c | 6 ++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 2ab39f33c58..8a47938a51c 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -2598,6 +2598,22 @@ static void test_aac_decoder_channels(const struct attribute_desc *input_type_de goto failed; } + hr = MFCreateMediaType(&type); + ok(hr == S_OK, "got %#lx.\n", hr); + input_desc[num_channels_index].value.vt = VT_UI8; + input_desc[num_channels_index].value.ulVal = 1; + init_media_type(type, input_desc, -1); + hr = IMFTransform_SetInputType(transform, 0, type, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + IMFMediaType_Release(type); + hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &type); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IMFAttributes_GetUINT32((IMFAttributes *)type, &MF_MT_AUDIO_NUM_CHANNELS, &value); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(value == 2, "got %u.\n", value); + IMFMediaType_Release(type); + input_desc[num_channels_index].value.vt = VT_UI4; + for (num_channels = 0; num_channels < 16; ++num_channels) { many_channels = num_channels > 2; @@ -2635,18 +2651,15 @@ static void test_aac_decoder_channels(const struct attribute_desc *input_type_de expected_chans = 2; else expected_chans = num_channels; - todo_wine_if(!num_channels) ok(value == expected_chans, "got %u, expected %u.\n", value, expected_chans); hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &value); ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine_if(!num_channels) ok(value == sample_size * 44100 * expected_chans, "got %u, expected %u.\n", value, sample_size * 44100 * expected_chans); hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &value); ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine_if(!num_channels) ok(value == sample_size * expected_chans, "got %u, expected %u.\n", value, sample_size * expected_chans); hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_PREFER_WAVEFORMATEX, &value); diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/aac_decoder.c index 69c91b0d6d0..a24c29f9eed 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/aac_decoder.c @@ -291,6 +291,10 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR *type = NULL; + if (FAILED(hr = IMFMediaType_GetUINT32(decoder->input_type, &MF_MT_AUDIO_NUM_CHANNELS, &channel_count)) + || !channel_count) + channel_count = 2; + if (index >= ARRAY_SIZE(aac_decoder_output_types)) return MF_E_NO_MORE_TYPES; index = ARRAY_SIZE(aac_decoder_output_types) - index - 1; @@ -318,8 +322,6 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, sample_size))) goto done; - if (FAILED(hr = IMFMediaType_GetUINT32(decoder->input_type, &MF_MT_AUDIO_NUM_CHANNELS, &channel_count))) - goto done; if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, channel_count))) goto done; From 5ff0beadc20607d5cfe3f4ade87b5c70a1718ca6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Sep 2023 20:00:41 -0600 Subject: [PATCH 0658/1148] winegstreamer: Correct output available types attrs in AAC decoder for channel count > 2. CW-Bug-Id: #22717 --- dlls/mf/tests/transform.c | 4 +--- dlls/winegstreamer/aac_decoder.c | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 8a47938a51c..7d4522ebf23 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -2665,7 +2665,7 @@ static void test_aac_decoder_channels(const struct attribute_desc *input_type_de hr = IMFAttributes_GetUINT32(attrs, &MF_MT_AUDIO_PREFER_WAVEFORMATEX, &value); if (many_channels && i < ARRAY_SIZE(expect_available_outputs)) { - todo_wine ok(hr == MF_E_ATTRIBUTENOTFOUND, "got %#lx.\n", hr); + ok(hr == MF_E_ATTRIBUTENOTFOUND, "got %#lx.\n", hr); } else { @@ -2681,11 +2681,9 @@ static void test_aac_decoder_channels(const struct attribute_desc *input_type_de } else { - todo_wine { ok(hr == S_OK, "got %#lx.\n", hr); ok(value == expected_mask[expected_chans], "got %#x, expected %#x.\n", value, expected_mask[expected_chans]); - } } ret = IMFMediaType_Release(type); diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/aac_decoder.c index a24c29f9eed..0cb5ccf6efd 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/aac_decoder.c @@ -24,6 +24,8 @@ #include "mfobjects.h" #include "mftransform.h" #include "wmcodecdsp.h" +#include "ks.h" +#include "ksmedia.h" #include "wine/debug.h" @@ -48,6 +50,17 @@ static const GUID *const aac_decoder_output_types[] = &MFAudioFormat_Float, }; +static const UINT32 default_channel_mask[7] = +{ + 0, + 0, + 0, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_CENTER, + KSAUDIO_SPEAKER_QUAD, + KSAUDIO_SPEAKER_QUAD | SPEAKER_FRONT_CENTER, + KSAUDIO_SPEAKER_5POINT1, +}; + struct aac_decoder { IMFTransform IMFTransform_iface; @@ -295,6 +308,9 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR || !channel_count) channel_count = 2; + if (channel_count >= ARRAY_SIZE(default_channel_mask)) + return MF_E_INVALIDMEDIATYPE; + if (index >= ARRAY_SIZE(aac_decoder_output_types)) return MF_E_NO_MORE_TYPES; index = ARRAY_SIZE(aac_decoder_output_types) - index - 1; @@ -340,7 +356,10 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR goto done; if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_FIXED_SIZE_SAMPLES, 1))) goto done; - if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1))) + if (channel_count < 3 && FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1))) + goto done; + if (channel_count >= 3 && FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_CHANNEL_MASK, + default_channel_mask[channel_count]))) goto done; done: From 669fb6ad41701a66a8d36c6d8f1e9652abce2bab Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Sep 2023 20:28:15 -0600 Subject: [PATCH 0659/1148] winegstreamer: Validate maximum channel count in _SetInputType in AAC decoder. CW-Bug-Id: #22717 --- dlls/mf/tests/transform.c | 2 +- dlls/winegstreamer/aac_decoder.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 7d4522ebf23..2c7337d50cd 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -2629,7 +2629,7 @@ static void test_aac_decoder_channels(const struct attribute_desc *input_type_de ok(hr == S_OK, "got %#lx.\n", hr); else { - todo_wine ok(hr == MF_E_INVALIDMEDIATYPE, "got %#lx.\n", hr); + ok(hr == MF_E_INVALIDMEDIATYPE, "got %#lx.\n", hr); winetest_pop_context(); continue; } diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/aac_decoder.c index 0cb5ccf6efd..ab94349d9e6 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/aac_decoder.c @@ -374,6 +374,7 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM { struct aac_decoder *decoder = impl_from_IMFTransform(iface); MF_ATTRIBUTE_TYPE item_type; + UINT32 channel_count; GUID major, subtype; HRESULT hr; ULONG i; @@ -396,6 +397,10 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM if (i == ARRAY_SIZE(aac_decoder_input_types)) return MF_E_INVALIDMEDIATYPE; + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channel_count)) + && channel_count >= ARRAY_SIZE(default_channel_mask)) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &item_type)) || item_type != MF_ATTRIBUTE_UINT32) return MF_E_INVALIDMEDIATYPE; From 1a3b318a19649befeaceabaf4c0d03d08073c4e1 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Sep 2023 20:05:21 -0600 Subject: [PATCH 0660/1148] winegstreamer: Also return output with 2 channels for multichannel inputs from AAC decoder. CW-Bug-Id: #22717 --- dlls/mf/tests/transform.c | 2 +- dlls/winegstreamer/aac_decoder.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 2c7337d50cd..a0df24bab6e 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -2692,7 +2692,7 @@ static void test_aac_decoder_channels(const struct attribute_desc *input_type_de } ok(hr == MF_E_NO_MORE_TYPES, "got %#lx.\n", hr); if (many_channels) - todo_wine ok(i == ARRAY_SIZE(expect_available_outputs) * 2, "got %lu media types.\n", i); + ok(i == ARRAY_SIZE(expect_available_outputs) * 2, "got %lu media types.\n", i); else ok(i == ARRAY_SIZE(expect_available_outputs), "got %lu media types.\n", i); winetest_pop_context(); diff --git a/dlls/winegstreamer/aac_decoder.c b/dlls/winegstreamer/aac_decoder.c index ab94349d9e6..bee353a7174 100644 --- a/dlls/winegstreamer/aac_decoder.c +++ b/dlls/winegstreamer/aac_decoder.c @@ -311,6 +311,14 @@ static HRESULT WINAPI transform_GetOutputAvailableType(IMFTransform *iface, DWOR if (channel_count >= ARRAY_SIZE(default_channel_mask)) return MF_E_INVALIDMEDIATYPE; + if (channel_count > 2 && index >= ARRAY_SIZE(aac_decoder_output_types)) + { + /* If there are more than two channels in the input type GetOutputAvailableType additionally lists + * types with 2 channels. */ + index -= ARRAY_SIZE(aac_decoder_output_types); + channel_count = 2; + } + if (index >= ARRAY_SIZE(aac_decoder_output_types)) return MF_E_NO_MORE_TYPES; index = ARRAY_SIZE(aac_decoder_output_types) - index - 1; From abc049ad3b58570766397805e3e4130ea1fa0e83 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 20 Sep 2023 15:45:43 -0600 Subject: [PATCH 0661/1148] wine.inf: Set FLG_HEAP_ENABLE_FREE_CHECK for Crysis 2 Remastered. CW-Bug-Id: #22761 --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 6273413d92e..e31c426ca04 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -420,6 +420,7 @@ 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 +HKLM,%CurrentVersionNT%\Image File Execution Options\Crysis2Remastered.exe,GlobalFlag,0x00040002,0x00000020 [CurrentVersionWow64] HKLM,%CurrentVersion%,"ProgramFilesDir (x86)",,"%16426%" From 8d18881c52e7f11a3a85ac2085502a83aa191129 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 21 Sep 2023 16:23:33 -0600 Subject: [PATCH 0662/1148] ntdll: Remove entries from queue in RtlWakeAddressAll(). CW-Bug-Id: #22770 --- dlls/ntdll/sync.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 4f5ee820286..11cea3c27ae 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -954,8 +954,8 @@ NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size void WINAPI RtlWakeAddressAll( const void *addr ) { struct futex_queue *queue = get_futex_queue( addr ); + struct futex_entry *entry, *next; unsigned int count = 0, i; - struct futex_entry *entry; DWORD tids[256]; TRACE("%p\n", addr); @@ -967,10 +967,12 @@ void WINAPI RtlWakeAddressAll( const void *addr ) if (!queue->queue.next) list_init(&queue->queue); - LIST_FOR_EACH_ENTRY( entry, &queue->queue, struct futex_entry, entry ) + LIST_FOR_EACH_ENTRY_SAFE( entry, next, &queue->queue, struct futex_entry, entry ) { if (entry->addr == addr) { + entry->addr = NULL; + list_remove( &entry->entry ); /* Try to buffer wakes, so that we don't make a system call while * holding a spinlock. */ if (count < ARRAY_SIZE(tids)) From b038988a92cac82e604a199ce97ba0556fdefb8b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 21 Sep 2023 16:29:14 -0600 Subject: [PATCH 0663/1148] ntdll: Pre-check entry->addr before taking a spin lock in RtlWaitOnAddress(). CW-Bug-Id: #22770 --- dlls/ntdll/sync.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 11cea3c27ae..650226b89f4 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -936,11 +936,14 @@ NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size ret = NtWaitForAlertByThreadId( NULL, timeout ); - spin_lock( &queue->lock ); - /* We may have already been removed by a call to RtlWakeAddressSingle(). */ + /* We may have already been removed by a call to RtlWakeAddressSingle() or RtlWakeAddressAll(). */ if (entry.addr) - list_remove( &entry.entry ); - spin_unlock( &queue->lock ); + { + spin_lock( &queue->lock ); + if (entry.addr) + list_remove( &entry.entry ); + spin_unlock( &queue->lock ); + } TRACE("returning %#lx\n", ret); From 002567d321f51390653e37706cfe46b2d89e21eb Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 19 Sep 2023 15:21:25 +0200 Subject: [PATCH 0664/1148] ntdll: Allow empty application nodes in actctx. Signed-off-by: Eric Pouech (cherry picked from commit 6558611fa2d24447297cb62d168b924c33839c43) CW-Bug-ID: #20860 This patch doesn't let the game start; this patch lets the game crash a little bit further down the road. --- dlls/kernel32/tests/actctx.c | 34 ++++++++++++++++++++++++++++++++++ dlls/ntdll/actctx.c | 9 +++++++++ 2 files changed, 43 insertions(+) diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c index bec04b7f066..fe4b24cf20d 100644 --- a/dlls/kernel32/tests/actctx.c +++ b/dlls/kernel32/tests/actctx.c @@ -506,6 +506,23 @@ static const char settings_manifest3[] = " " ""; +static const char settings_manifest4[] = +"" +" " +" " +" " +" " +" true" +" " +" " +" " +" " +" " +" true" +" " +" " +""; + static const char two_dll_manifest_dll[] = "" " " @@ -3302,6 +3319,23 @@ static void test_settings(void) ok( !ret, "QueryActCtxSettingsW succeeded\n" ); ok( GetLastError() == ERROR_SXS_KEY_NOT_FOUND, "wrong error %lu\n", GetLastError() ); ReleaseActCtx(handle); + + /* lookup occurs in first non empty node */ + create_manifest_file( "manifest_settings4.manifest", settings_manifest4, -1, NULL, NULL ); + handle = test_create("manifest_settings4.manifest"); + ok( handle != INVALID_HANDLE_VALUE, "handle == INVALID_HANDLE_VALUE, error %lu\n", GetLastError() ); + DeleteFileA( "manifest_settings3.manifest" ); + SetLastError( 0xdeadbeef ); + size = 0xdead; + memset( buffer, 0xcc, sizeof(buffer) ); + ret = pQueryActCtxSettingsW( 0, handle, NULL, dpiAwareW, buffer, 80, &size ); + ok( ret, "QueryActCtxSettingsW failed\n" ); + SetLastError( 0xdeadbeef ); + size = 0xdead; + memset( buffer, 0xcc, sizeof(buffer) ); + ret = pQueryActCtxSettingsW( 0, handle, NULL, dpiAwarenessW, buffer, 80, &size ); + ok( !ret, "QueryActCtxSettingsW succeeded\n" ); + ReleaseActCtx(handle); } typedef struct diff --git a/dlls/ntdll/actctx.c b/dlls/ntdll/actctx.c index 721a2f339a5..99a5c8f9b8a 100644 --- a/dlls/ntdll/actctx.c +++ b/dlls/ntdll/actctx.c @@ -2565,6 +2565,15 @@ static void parse_application_elem( xmlbuf_t *xmlbuf, struct assembly *assembly, struct actctx_loader *acl, const struct xml_elem *parent ) { struct xml_elem elem; + struct xml_attr attr; + BOOL end = FALSE; + + while (next_xml_attr(xmlbuf, &attr, &end)) + { + if (!is_xmlns_attr( &attr )) WARN( "unknown attr %s\n", debugstr_xml_attr(&attr) ); + } + + if (end) return; while (next_xml_elem( xmlbuf, &elem, parent )) { From 1b4edd6a753d1aec6c643b5e75bb7d0dc7f0671f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 21 Oct 2020 17:19:30 +0200 Subject: [PATCH 0665/1148] makedep: Allow building modules with C++ sources. CW-Bug-Id: #22729 --- tools/makedep.c | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/tools/makedep.c b/tools/makedep.c index 3dd58428c99..12dc51cdf17 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -201,6 +201,7 @@ struct makefile int data_only; int is_win16; int is_exe; + int has_cxx; int disabled[MAX_ARCHS]; /* values generated at output time */ @@ -1185,6 +1186,7 @@ static const struct } parse_functions[] = { { ".c", parse_c_file }, + { ".cpp", parse_c_file }, { ".h", parse_c_file }, { ".inl", parse_c_file }, { ".l", parse_c_file }, @@ -1498,7 +1500,7 @@ static struct file *open_include_file( const struct makefile *make, struct incl_ return file; } - if (make->extlib) return NULL; /* ignore missing files in external libs */ + if (make->extlib || make->has_cxx) return NULL; /* ignore missing files in external libs */ fprintf( stderr, "%s:%d: error: ", pFile->included_by->file->name, pFile->included_line ); perror( pFile->name ); @@ -1551,6 +1553,13 @@ static void parse_file( struct makefile *make, struct incl_file *source, int src { struct file *file = src ? open_src_file( make, source ) : open_include_file( make, source ); + if (strendswith( source->name, ".cpp" )) + { + /* ignore missing headers or unsupported includes in .cpp files */ + source->is_external = 1; + make->has_cxx = 1; + } + if (!file) return; source->file = file; @@ -2416,9 +2425,11 @@ static const char *cmd_prefix( const char *cmd ) /******************************************************************* * output_winegcc_command */ -static void output_winegcc_command( struct makefile *make, unsigned int arch ) +static void output_winegcc_command( struct makefile *make, unsigned int arch, int is_cxx ) { - output( "\t%s%s -o $@", cmd_prefix( "CCLD" ), tools_path( make, "winegcc" )); + const char *tool = tools_path( make, "winegcc" ); + if (is_cxx) strcpy( strrchr( tool, 'w' ), "wineg++" ); + output( "\t%s%s -o $@", cmd_prefix( "CCLD" ), tool ); output_filename( "--wine-objdir ." ); if (tools_dir) { @@ -3111,7 +3122,7 @@ static void output_source_spec( struct makefile *make, struct incl_file *source, output_filename( tools_path( make, "winebuild" )); output_filename( tools_path( make, "winegcc" )); output( "\n" ); - output_winegcc_command( make, arch ); + output_winegcc_command( make, arch, 0 ); output_filename( "-s" ); output_filenames( dll_flags ); if (arch) output_filenames( get_expanded_arch_var_array( make, "EXTRADLLFLAGS", arch )); @@ -3134,9 +3145,11 @@ static void output_source_one_arch( struct makefile *make, struct incl_file *sou struct strarray defines, struct strarray *targets, unsigned int arch, int is_dll_src ) { + const int is_cxx = strendswith( source->name, ".cpp" ); const char *obj_name; if (make->disabled[arch] && !(source->file->flags & FLAG_C_IMPLIB)) return; + make->has_cxx |= is_cxx; if (arch) { @@ -3164,10 +3177,11 @@ static void output_source_one_arch( struct makefile *make, struct incl_file *sou strarray_add( &make->clean_files, obj_name ); output( "%s: %s\n", obj_dir_path( make, obj_name ), source->filename ); - output( "\t%s%s -c -o $@ %s", cmd_prefix( "CC" ), arch_make_variable( "CC", arch ), source->filename ); + if (is_cxx) output( "\t%s%s -c -o $@ %s", cmd_prefix( "CXX" ), arch_make_variable( "CXX", arch ), source->filename ); + else output( "\t%s%s -c -o $@ %s", cmd_prefix( "CC" ), arch_make_variable( "CC", arch ), source->filename ); output_filenames( defines ); if (!source->use_msvcrt) output_filenames( make->unix_cflags ); - output_filenames( make->extlib ? extra_cflags_extlib[arch] : extra_cflags[arch] ); + output_filenames( make->extlib || is_cxx ? extra_cflags_extlib[arch] : extra_cflags[arch] ); if (!arch) { if (make->sharedlib || (source->file->flags & FLAG_C_UNIX)) @@ -3188,7 +3202,8 @@ static void output_source_one_arch( struct makefile *make, struct incl_file *sou } output_filenames( cpp_flags ); - output_filename( arch_make_variable( "CFLAGS", arch )); + if (is_cxx) output_filename( arch_make_variable( "CXXFLAGS", arch )); + else output_filename( arch_make_variable( "CFLAGS", arch )); output( "\n" ); if (make->testdll && !is_dll_src && strendswith( source->name, ".c" ) && @@ -3290,7 +3305,7 @@ static void output_fake_module( struct makefile *make ) output_filename( tools_path( make, "winebuild" )); output_filename( tools_path( make, "winegcc" )); output( "\n" ); - output_winegcc_command( make, arch ); + output_winegcc_command( make, arch, 0 ); output_filename( "-Wb,--fake-module" ); if (spec_file) { @@ -3314,7 +3329,7 @@ static void output_module( struct makefile *make, unsigned int arch ) struct strarray imports = make->imports; const char *module_name; const char *debug_file; - char *spec_file = NULL; + char *tool, *spec_file = NULL; unsigned int i; if (make->disabled[arch]) return; @@ -3358,9 +3373,15 @@ static void output_module( struct makefile *make, unsigned int arch ) output_filenames_obj_dir( make, make->res_files[arch] ); output_filenames( dep_libs ); output_filename( tools_path( make, "winebuild" )); - output_filename( tools_path( make, "winegcc" )); + tool = tools_path( make, "winegcc" ); + output_filename( tool ); + if (make->has_cxx) + { + strcpy( strrchr( tool, 'w' ), "wineg++" ); + output_filename( tool ); + } output( "\n" ); - output_winegcc_command( make, arch ); + output_winegcc_command( make, arch, make->has_cxx ); if (arch) output_filename( "-Wl,--wine-builtin" ); if (spec_file) { @@ -3527,7 +3548,7 @@ static void output_test_module( struct makefile *make, unsigned int arch ) strarray_add( &make->all_targets[arch], testmodule ); strarray_add( &make->clean_files, stripped ); output( "%s:\n", obj_dir_path( make, testmodule )); - output_winegcc_command( make, arch ); + output_winegcc_command( make, arch, 0 ); output_filenames( make->extradllflags ); output_filenames_obj_dir( make, make->object_files[arch] ); output_filenames_obj_dir( make, make->res_files[arch] ); @@ -3537,7 +3558,7 @@ static void output_test_module( struct makefile *make, unsigned int arch ) output_filename( arch_make_variable( "LDFLAGS", arch )); output( "\n" ); output( "%s:\n", obj_dir_path( make, stripped )); - output_winegcc_command( make, arch ); + output_winegcc_command( make, arch, 0 ); output_filename( "-s" ); output_filename( strmake( "-Wb,-F,%s_test.exe", basemodule )); output_filenames( make->extradllflags ); @@ -4208,6 +4229,7 @@ static void load_sources( struct makefile *make ) { "SOURCES", "C_SRCS", + "CXX_SRCS", "OBJC_SRCS", "RC_SRCS", "MC_SRCS", From e093242feb88db181ea034aa2581ec325b41ab32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 20 Sep 2023 00:06:48 +0200 Subject: [PATCH 0666/1148] Revert "makedep: Assume that all modules are built with msvcrt." This reverts commit a9183c7e3bd6ee25aeab2aed6c57a58d9558b614. CW-Bug-Id: #22729 --- tools/makedep.c | 53 ++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/tools/makedep.c b/tools/makedep.c index 12dc51cdf17..9497a8a7b97 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -198,6 +198,7 @@ struct makefile const char *staticlib; const char *importlib; const char *unixlib; + int use_msvcrt; int data_only; int is_win16; int is_exe; @@ -598,17 +599,6 @@ static int is_multiarch( unsigned int arch ) } -/******************************************************************* - * is_using_msvcrt - * - * Check if the files of a makefile use msvcrt by default. - */ -static int is_using_msvcrt( struct makefile *make ) -{ - return make->module || make->testdll; -} - - /******************************************************************* * arch_module_name */ @@ -867,7 +857,7 @@ static struct incl_file *add_generated_source( struct makefile *make, const char file->basename = xstrdup( filename ? filename : name ); file->filename = obj_dir_path( make, file->basename ); file->file->flags = FLAG_GENERATED; - file->use_msvcrt = is_using_msvcrt( make ); + file->use_msvcrt = make->use_msvcrt; list_add_tail( &make->sources, &file->entry ); if (make == include_makefile) { @@ -1624,7 +1614,7 @@ static struct incl_file *add_src_file( struct makefile *make, const char *name ) memset( file, 0, sizeof(*file) ); file->name = xstrdup(name); - file->use_msvcrt = is_using_msvcrt( make ); + file->use_msvcrt = make->use_msvcrt; file->is_external = !!make->extlib; list_add_tail( &make->sources, &file->entry ); if (make == include_makefile) @@ -1822,12 +1812,13 @@ static void add_generated_sources( struct makefile *make ) unsigned int i, arch; struct incl_file *source, *next, *file, *dlldata = NULL; struct strarray objs = get_expanded_make_var_array( make, "EXTRA_OBJS" ); + int multiarch = archs.count > 1 && make->use_msvcrt; LIST_FOR_EACH_ENTRY_SAFE( source, next, &make->sources, struct incl_file, entry ) { for (arch = 0; arch < archs.count; arch++) { - if (!is_multiarch( arch )) continue; + if (!arch != !multiarch) continue; if (source->file->flags & FLAG_IDL_CLIENT) { file = add_generated_source( make, replace_extension( source->name, ".idl", "_c.c" ), NULL, arch ); @@ -1931,7 +1922,7 @@ static void add_generated_sources( struct makefile *make ) { for (arch = 0; arch < archs.count; arch++) { - if (!is_multiarch( arch )) continue; + if (!arch != !multiarch) continue; file = add_generated_source( make, "testlist.o", "testlist.c", arch ); add_dependency( file->file, "wine/test.h", INCL_NORMAL ); add_all_includes( make, file, file->file ); @@ -2185,6 +2176,7 @@ static int is_crt_module( const char *file ) */ static const char *get_default_crt( const struct makefile *make ) { + if (!make->use_msvcrt) return NULL; if (make->module && is_crt_module( make->module )) return NULL; /* don't add crt import to crt dlls */ return !make->testdll && (!make->staticlib || make->extlib) ? "ucrtbase" : "msvcrt"; } @@ -2374,7 +2366,6 @@ static struct strarray get_source_defines( struct makefile *make, struct incl_fi strarray_add( &ret, strmake( "-I%s", root_src_dir_path( "include/msvcrt" ))); for (i = 0; i < make->include_paths.count; i++) strarray_add( &ret, strmake( "-I%s", make->include_paths.str[i] )); - strarray_add( &ret, get_crt_define( make )); } strarray_addall( &ret, make->define_args ); strarray_addall( &ret, get_expanded_file_local_var( make, obj, "EXTRADEFS" )); @@ -2437,9 +2428,7 @@ static void output_winegcc_command( struct makefile *make, unsigned int arch, in output_filename( tools_path( make, "winebuild" )); } output_filenames( target_flags[arch] ); - if (arch) return; - output_filename( "-mno-cygwin" ); - output_filenames( lddll_flags ); + if (!arch) output_filenames( lddll_flags ); } @@ -2861,6 +2850,7 @@ static void output_source_idl( struct makefile *make, struct incl_file *source, if (!(source->file->flags & idl_outputs[i].flag)) continue; for (arch = 0; arch < archs.count; arch++) { + if (!make->use_msvcrt) continue; if (!is_multiarch( arch )) continue; if (make->disabled[arch]) continue; dest = strmake( "%s%s%s", arch_dirs[arch], obj, idl_outputs[i].ext ); @@ -3154,13 +3144,13 @@ static void output_source_one_arch( struct makefile *make, struct incl_file *sou if (arch) { if (source->file->flags & FLAG_C_UNIX) return; - if (!is_using_msvcrt( make ) && !make->staticlib && !(source->file->flags & FLAG_C_IMPLIB)) return; + if (!make->use_msvcrt && !make->staticlib && !(source->file->flags & FLAG_C_IMPLIB)) return; } else if (source->file->flags & FLAG_C_UNIX) { if (!*dll_ext) return; } - else if (archs.count > 1 && is_using_msvcrt( make ) && + else if (archs.count > 1 && make->use_msvcrt && !(source->file->flags & FLAG_C_IMPLIB) && (!make->staticlib || make->extlib)) return; @@ -3347,6 +3337,12 @@ static void output_module( struct makefile *make, unsigned int arch ) strarray_addall( &all_libs, add_import_libs( make, &dep_libs, default_imports, IMPORT_TYPE_DEFAULT, arch ) ); if (!arch) strarray_addall( &all_libs, libs ); + if (!make->use_msvcrt) + { + strarray_addall( &all_libs, get_expanded_make_var_array( make, "UNIX_LIBS" )); + strarray_addall( &all_libs, libs ); + } + if (delay_load_flags[arch]) { for (i = 0; i < make->delayimports.count; i++) @@ -3589,7 +3585,7 @@ static void output_test_module( struct makefile *make, unsigned int arch ) output( ": %s", obj_dir_path( make, testmodule )); if (parent) { - char *parent_module = arch_module_name( make->testdll, arch ); + char *parent_module = arch_module_name( make->testdll, parent->use_msvcrt ? arch : 0 ); output_filename( obj_dir_path( parent, parent_module )); if (parent->unixlib) output_filename( obj_dir_path( parent, parent->unixlib )); } @@ -3839,7 +3835,12 @@ static void output_sources( struct makefile *make ) } else if (make->module) { - for (arch = 0; arch < archs.count; arch++) if (is_multiarch( arch )) output_module( make, arch ); + if (!make->use_msvcrt) output_module( make, 0 ); + else + { + for (arch = 0; arch < archs.count; arch++) + if (is_multiarch( arch )) output_module( make, arch ); + } if (make->unixlib) output_unix_lib( make ); if (make->importlib) for (arch = 0; arch < archs.count; arch++) output_import_lib( make, arch ); if (make->is_exe && !make->is_win16 && *dll_ext && strendswith( make->module, ".exe" )) @@ -4282,9 +4283,13 @@ static void load_sources( struct makefile *make ) } make->is_win16 = strarray_exists( &make->extradllflags, "-m16" ); make->data_only = strarray_exists( &make->extradllflags, "-Wb,--data-only" ); + make->use_msvcrt = (make->module || make->testdll || make->is_win16) && + !strarray_exists( &make->extradllflags, "-mcygwin" ); make->is_exe = strarray_exists( &make->extradllflags, "-mconsole" ) || strarray_exists( &make->extradllflags, "-mwindows" ); + if (make->use_msvcrt) strarray_add_uniq( &make->extradllflags, "-mno-cygwin" ); + if (make->module) { /* add default install rules if nothing was specified */ @@ -4341,6 +4346,8 @@ static void load_sources( struct makefile *make ) add_generated_sources( make ); + if (make->use_msvcrt) strarray_add( &make->define_args, get_crt_define( make )); + LIST_FOR_EACH_ENTRY( file, &make->includes, struct incl_file, entry ) parse_file( make, file, 0 ); LIST_FOR_EACH_ENTRY( file, &make->sources, struct incl_file, entry ) get_dependencies( file, file ); From aabaf8d435a1e2dd0f4b080382b71da55376e4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 20 Sep 2023 00:03:48 +0200 Subject: [PATCH 0667/1148] makedep: Allow building modules from external sources. CW-Bug-Id: #22729 --- tools/makedep.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/makedep.c b/tools/makedep.c index 9497a8a7b97..77fa990b6b5 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -4414,7 +4414,7 @@ static int parse_option( const char *opt ) int main( int argc, char *argv[] ) { const char *makeflags = getenv( "MAKEFLAGS" ); - const char *target; + const char *target, *tmp; unsigned int i, j, arch; if (makeflags) parse_makeflags( makeflags ); @@ -4530,7 +4530,12 @@ int main( int argc, char *argv[] ) subdirs = get_expanded_make_var_array( top_makefile, "SUBDIRS" ); submakes = xmalloc( subdirs.count * sizeof(*submakes) ); - for (i = 0; i < subdirs.count; i++) submakes[i] = parse_makefile( subdirs.str[i] ); + for (i = 0; i < subdirs.count; i++) + { + submakes[i] = parse_makefile( subdirs.str[i] ); + if (*(tmp = subdirs.str[i]) == '/') tmp = strmake( "dlls%s", strrchr( tmp, '/' ) ); + submakes[i]->obj_dir = subdirs.str[i] = tmp; + } load_sources( top_makefile ); load_sources( include_makefile ); From d45cd219db5159bb1d3748dbbebb17d3fcda0fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 26 Sep 2023 16:34:16 +0200 Subject: [PATCH 0668/1148] makedep: Allow using an external wine object dir. CW-Bug-Id: #22729 --- tools/makedep.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tools/makedep.c b/tools/makedep.c index 77fa990b6b5..3436ee7a84f 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -143,6 +143,7 @@ static struct strarray subdirs; static struct strarray delay_import_libs; static struct strarray top_install[NB_INSTALL_RULES]; static const char *root_src_dir; +static const char *root_obj_dir; static const char *tools_dir; static const char *tools_ext; static const char *exe_ext; @@ -645,6 +646,16 @@ static char *root_src_dir_path( const char *path ) } +/******************************************************************* + * root_obj_dir_path + */ +static char *root_obj_dir_path( const char *path ) +{ + if (!root_obj_dir) return (char *)path; + return concat_paths( root_obj_dir, path ); +} + + /******************************************************************* * tools_dir_path */ @@ -2421,7 +2432,7 @@ static void output_winegcc_command( struct makefile *make, unsigned int arch, in const char *tool = tools_path( make, "winegcc" ); if (is_cxx) strcpy( strrchr( tool, 'w' ), "wineg++" ); output( "\t%s%s -o $@", cmd_prefix( "CCLD" ), tool ); - output_filename( "--wine-objdir ." ); + output_filename( strmake( "--wine-objdir %s", root_obj_dir_path( "." ) ) ); if (tools_dir) { output_filename( "--winebuild" ); @@ -4332,7 +4343,7 @@ static void load_sources( struct makefile *make ) strarray_add( &make->include_args, strmake( "-I%s", make->src_dir )); if (make->parent_dir) strarray_add( &make->include_args, strmake( "-I%s", src_dir_path( make, make->parent_dir ))); - strarray_add( &make->include_args, "-Iinclude" ); + strarray_add( &make->include_args, strmake( "-I%s", root_obj_dir_path( "include" ) ) ); if (root_src_dir) strarray_add( &make->include_args, strmake( "-I%s", root_src_dir_path( "include" ))); list_init( &make->sources ); @@ -4470,6 +4481,7 @@ int main( int argc, char *argv[] ) top_install[i] = get_expanded_make_var_array( top_makefile, strmake( "TOP_%s", install_variables[i] )); root_src_dir = get_expanded_make_variable( top_makefile, "srcdir" ); + root_obj_dir = get_expanded_make_variable( top_makefile, "objdir" ); tools_dir = get_expanded_make_variable( top_makefile, "toolsdir" ); tools_ext = get_expanded_make_variable( top_makefile, "toolsext" ); exe_ext = get_expanded_make_variable( top_makefile, "EXEEXT" ); From c1bfdf13c76761f77f07c04657a770d487a3d0e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 26 Sep 2023 16:34:50 +0200 Subject: [PATCH 0669/1148] makedep: Allow include to be missing from SUBDIRS. CW-Bug-Id: #22729 --- tools/makedep.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/makedep.c b/tools/makedep.c index 3436ee7a84f..3f3baf826df 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -1446,9 +1446,9 @@ static struct file *open_include_file( const struct makefile *make, struct incl_ if ((file = open_local_file( make, pFile->name, &pFile->filename ))) return file; /* check for global importlib (module dependency) */ - if (pFile->type == INCL_IMPORTLIB && find_importlib_module( pFile->name )) + if (pFile->type == INCL_IMPORTLIB) { - pFile->filename = pFile->name; + if (find_importlib_module( pFile->name )) pFile->filename = pFile->name; return NULL; } @@ -4549,6 +4549,8 @@ int main( int argc, char *argv[] ) submakes[i]->obj_dir = subdirs.str[i] = tmp; } + if (!include_makefile) include_makefile = parse_makefile( root_src_dir_path( "include" ) ); + load_sources( top_makefile ); load_sources( include_makefile ); for (i = 0; i < subdirs.count; i++) From 5666eb754b790ad3998a7121cded96c947ffd8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 20 Sep 2023 09:49:29 +0200 Subject: [PATCH 0670/1148] makedep: Allow modules to have a different 64bit name. CW-Bug-Id: #22729 --- tools/makedep.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/tools/makedep.c b/tools/makedep.c index 3f3baf826df..6b02b154e45 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -193,6 +193,7 @@ struct makefile const char *obj_dir; const char *parent_dir; const char *module; + const char *module_x64; const char *testdll; const char *extlib; const char *sharedlib; @@ -3291,14 +3292,17 @@ static const struct static void output_fake_module( struct makefile *make ) { unsigned int arch = 0; /* fake modules are always native */ - const char *spec_file = NULL, *name = strmake( "%s%s", arch_pe_dirs[arch], make->module ); + const char *spec_file = NULL, *name; + const char *module = make->module; if (make->disabled[arch]) return; - if (!make->is_exe) spec_file = src_dir_path( make, replace_extension( make->module, ".dll", ".spec" )); + if (make->module_x64 && !strcmp( archs.str[arch], "x86_64" )) module = make->module_x64; + if (!make->is_exe) spec_file = src_dir_path( make, replace_extension( module, ".dll", ".spec" )); + name = strmake( "%s%s", arch_pe_dirs[arch], module ); strarray_add( &make->all_targets[arch], name ); - add_install_rule( make, make->module, arch, name, strmake( "d$(dlldir)/%s", name )); + add_install_rule( make, module, arch, name, strmake( "d$(dlldir)/%s", name )); output( "%s:", obj_dir_path( make, name )); if (spec_file) output_filename( spec_file ); @@ -3328,18 +3332,19 @@ static void output_module( struct makefile *make, unsigned int arch ) struct strarray all_libs = empty_strarray; struct strarray dep_libs = empty_strarray; struct strarray imports = make->imports; - const char *module_name; + const char *module_name, *module = make->module; const char *debug_file; char *tool, *spec_file = NULL; unsigned int i; if (make->disabled[arch]) return; - if (!make->is_exe) spec_file = src_dir_path( make, replace_extension( make->module, ".dll", ".spec" )); + if (make->module_x64 && !strcmp( archs.str[arch], "x86_64" )) module = make->module_x64; + if (!make->is_exe) spec_file = src_dir_path( make, replace_extension( module, ".dll", ".spec" )); if (!make->data_only) { - module_name = arch_module_name( make->module, arch ); + module_name = arch_module_name( module, arch ); if (!strarray_exists( &make->extradllflags, "-nodefaultlibs" )) default_imports = get_default_imports( make, imports ); @@ -3363,15 +3368,15 @@ static void output_module( struct makefile *make, unsigned int arch ) } } } - else module_name = strmake( "%s%s", arch_pe_dirs[arch], make->module ); + else module_name = strmake( "%s%s", arch_pe_dirs[arch], module ); strarray_add( &make->all_targets[arch], module_name ); if (make->data_only) - add_install_rule( make, make->module, arch, module_name, - strmake( "d$(dlldir)/%s%s", arch_pe_dirs[arch], make->module )); + add_install_rule( make, module, arch, module_name, + strmake( "d$(dlldir)/%s%s", arch_pe_dirs[arch], module )); else - add_install_rule( make, make->module, arch, module_name, - strmake( "%c%s%s%s", '0' + arch, arch_install_dirs[arch], make->module, + add_install_rule( make, module, arch, module_name, + strmake( "%c%s%s%s", '0' + arch, arch_install_dirs[arch], module, arch ? "" : dll_ext )); output( "%s:", obj_dir_path( make, module_name )); @@ -4267,6 +4272,7 @@ static void load_sources( struct makefile *make ) make->parent_dir = get_expanded_make_variable( make, "PARENTSRC" ); make->module = get_expanded_make_variable( make, "MODULE" ); + make->module_x64 = get_expanded_make_variable( make, "MODULE_x64" ); make->testdll = get_expanded_make_variable( make, "TESTDLL" ); make->sharedlib = get_expanded_make_variable( make, "SHAREDLIB" ); make->staticlib = get_expanded_make_variable( make, "STATICLIB" ); @@ -4310,6 +4316,7 @@ static void load_sources( struct makefile *make ) if (make->importlib) strarray_add( &make->install[INSTALL_DEV], make->importlib ); if (make->staticlib) strarray_add( &make->install[INSTALL_DEV], make->staticlib ); else strarray_add( &make->install[INSTALL_LIB], make->module ); + if (make->module_x64) strarray_add( &make->install[INSTALL_LIB], make->module_x64 ); } } From 1f107270f441525dba97c313383a8a7211becbb0 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 25 Sep 2023 18:23:21 +0100 Subject: [PATCH 0671/1148] rtworkq: Fix leak of thread pool work items. (cherry picked from commit 34319496d83a816bc22005851cce32bd480892af) CW-Bug-Id: #22498 --- dlls/rtworkq/queue.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/dlls/rtworkq/queue.c b/dlls/rtworkq/queue.c index baf648bf771..c088d892f39 100644 --- a/dlls/rtworkq/queue.c +++ b/dlls/rtworkq/queue.c @@ -120,6 +120,13 @@ enum system_queue_index SYS_QUEUE_COUNT, }; +enum work_item_type +{ + WORK_ITEM_WORK, + WORK_ITEM_TIMER, + WORK_ITEM_WAIT, +}; + struct work_item { IUnknown IUnknown_iface; @@ -131,10 +138,11 @@ struct work_item RTWQWORKITEM_KEY key; LONG priority; DWORD flags; - TP_WORK *work_object; PTP_SIMPLE_CALLBACK finalization_callback; + enum work_item_type type; union { + TP_WORK *work_object; TP_WAIT *wait_object; TP_TIMER *timer_object; } u; @@ -389,8 +397,9 @@ static void pool_queue_submit(struct queue *queue, struct work_item *item) we need finalization callback. */ if (item->finalization_callback) IUnknown_AddRef(&item->IUnknown_iface); - item->work_object = CreateThreadpoolWork(standard_queue_worker, item, (TP_CALLBACK_ENVIRON *)&env); - SubmitThreadpoolWork(item->work_object); + item->u.work_object = CreateThreadpoolWork(standard_queue_worker, item, (TP_CALLBACK_ENVIRON *)&env); + item->type = WORK_ITEM_WORK; + SubmitThreadpoolWork(item->u.work_object); TRACE("dispatched %p.\n", item->result); } @@ -551,8 +560,18 @@ static ULONG WINAPI work_item_Release(IUnknown *iface) if (!refcount) { - if (item->work_object) - CloseThreadpoolWork(item->work_object); + switch (item->type) + { + case WORK_ITEM_WORK: + if (item->u.work_object) CloseThreadpoolWork(item->u.work_object); + break; + case WORK_ITEM_WAIT: + if (item->u.wait_object) CloseThreadpoolWait(item->u.wait_object); + break; + case WORK_ITEM_TIMER: + if (item->u.timer_object) CloseThreadpoolTimer(item->u.timer_object); + break; + } if (item->reply_result) IRtwqAsyncResult_Release(item->reply_result); IRtwqAsyncResult_Release(item->result); @@ -816,6 +835,7 @@ static HRESULT queue_submit_wait(struct queue *queue, HANDLE event, LONG priorit item->u.wait_object = CreateThreadpoolWait(callback, item, (TP_CALLBACK_ENVIRON *)&queue->envs[TP_CALLBACK_PRIORITY_NORMAL]); + item->type = WORK_ITEM_WAIT; SetThreadpoolWait(item->u.wait_object, event, NULL); TRACE("dispatched %p.\n", result); @@ -850,6 +870,7 @@ static HRESULT queue_submit_timer(struct queue *queue, IRtwqAsyncResult *result, item->u.timer_object = CreateThreadpoolTimer(callback, item, (TP_CALLBACK_ENVIRON *)&queue->envs[TP_CALLBACK_PRIORITY_NORMAL]); + item->type = WORK_ITEM_TIMER; SetThreadpoolTimer(item->u.timer_object, &filetime, period, 0); TRACE("dispatched %p.\n", result); From 87fbf3305fe733b475a1f907a730f40b4823424b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 30 May 2023 12:11:51 +0200 Subject: [PATCH 0672/1148] include: Don't align the stack for PE builds. (upstream commit 62173699c38453777c7d5638ed2e779790506b75) CW-Bug-Id: #22660 --- include/msvcrt/corecrt.h | 9 +++------ include/windef.h | 23 +++++++++-------------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/include/msvcrt/corecrt.h b/include/msvcrt/corecrt.h index aba9bc9422e..4ddea3edb1e 100644 --- a/include/msvcrt/corecrt.h +++ b/include/msvcrt/corecrt.h @@ -82,7 +82,7 @@ #define __has_attribute(x) 0 #endif -#ifndef _MSC_VER +#if !defined(_MSC_VER) && !defined(__MINGW32__) # undef __stdcall # ifdef __i386__ # ifdef __GNUC__ @@ -100,16 +100,13 @@ # else # define __stdcall __attribute__((ms_abi)) # endif -# elif defined(__arm__) && defined (__GNUC__) && !defined(__SOFTFP__) && !defined(__MINGW32__) && !defined(__CYGWIN__) +# elif defined(__arm__) && defined (__GNUC__) && !defined(__SOFTFP__) && !defined(__CYGWIN__) # define __stdcall __attribute__((pcs("aapcs-vfp"))) # elif defined(__aarch64__) && defined (__GNUC__) && __has_attribute(ms_abi) # define __stdcall __attribute__((ms_abi)) # else /* __i386__ */ # define __stdcall # endif /* __i386__ */ -#endif /* __stdcall */ - -#ifndef _MSC_VER # undef __cdecl # if defined(__i386__) && defined(__GNUC__) # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || defined(__APPLE__) @@ -120,7 +117,7 @@ # else # define __cdecl __stdcall # endif -#endif +#endif /* _MSC_VER || __MINGW32__ */ #if (defined(__x86_64__) || (defined(__aarch64__) && __has_attribute(ms_abi))) && defined (__GNUC__) # include diff --git a/include/windef.h b/include/windef.h index cb7eb1179c8..827e9f92a26 100644 --- a/include/windef.h +++ b/include/windef.h @@ -54,7 +54,7 @@ extern "C" { # endif #endif -#ifndef _MSC_VER +#if !defined(_MSC_VER) && !defined(__MINGW32__) # undef __stdcall # ifdef __i386__ # ifdef __GNUC__ @@ -72,16 +72,13 @@ extern "C" { # else # define __stdcall __attribute__((ms_abi)) # endif -# elif defined(__arm__) && defined (__GNUC__) && !defined(__SOFTFP__) && !defined(__MINGW32__) && !defined(__CYGWIN__) +# elif defined(__arm__) && defined (__GNUC__) && !defined(__SOFTFP__) && !defined(__CYGWIN__) # define __stdcall __attribute__((pcs("aapcs-vfp"))) # elif defined(__aarch64__) && defined (__GNUC__) && __has_attribute(ms_abi) # define __stdcall __attribute__((ms_abi)) # else /* __i386__ */ # define __stdcall # endif /* __i386__ */ -#endif /* __stdcall */ - -#ifndef _MSC_VER # undef __cdecl # if defined(__i386__) && defined(__GNUC__) # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || defined(__APPLE__) @@ -92,15 +89,13 @@ extern "C" { # else # define __cdecl __stdcall # endif -#endif - -#if !defined(_MSC_VER) && !defined(__fastcall) -# define __fastcall __stdcall -#endif - -#if (!defined(_MSC_VER) || !defined(__clang__)) && !defined(__thiscall) -# define __thiscall __stdcall -#endif +# ifndef __fastcall +# define __fastcall __stdcall +# endif +# ifndef __thiscall +# define __thiscall __stdcall +# endif +#endif /* _MSC_VER || __MINGW32__ */ #ifndef __ms_va_list # if (defined(__x86_64__) || (defined(__aarch64__) && __has_attribute(ms_abi))) && defined (__GNUC__) From 7f7f1f7707fa71391070a2c6d34e344c6837a77b Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 20 Sep 2023 14:29:33 +0100 Subject: [PATCH 0673/1148] gdi32: Make GetStockObject hotpatchable. Needed for ntlea. (upstream commit 74027f37311098859b1b279384e79dabb9468546) CW-Bug-Id: #22660 --- dlls/gdi32/objects.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/gdi32/objects.c b/dlls/gdi32/objects.c index 24ac5b015ea..bddc29a3007 100644 --- a/dlls/gdi32/objects.c +++ b/dlls/gdi32/objects.c @@ -418,7 +418,7 @@ HGDIOBJ WINAPI GetCurrentObject( HDC hdc, UINT type ) /*********************************************************************** * GetStockObject (GDI32.@) */ -HGDIOBJ WINAPI GetStockObject( INT obj ) +HGDIOBJ WINAPI DECLSPEC_HOTPATCH GetStockObject( INT obj ) { if (obj < 0 || obj > STOCK_LAST + 1 || obj == 9) return 0; From 1135dee9a4d5d33dc59f9532ebf7f979cd9f2937 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 20 Sep 2023 14:29:33 +0100 Subject: [PATCH 0674/1148] user32: Make CallWindowProcA hotpatchable. Needed for ntlea. (upstream commit 2508bb83633c7732bab376da2e4d9f8da2a1a00b) CW-Bug-Id: #22660 --- dlls/user32/winproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/user32/winproc.c b/dlls/user32/winproc.c index 9fd35e1d24e..83cc9533fe2 100644 --- a/dlls/user32/winproc.c +++ b/dlls/user32/winproc.c @@ -1251,7 +1251,7 @@ BOOL WINAPI User32CallSendAsyncCallback( const struct send_async_params *params, * * ECMA-234, Win32 */ -LRESULT WINAPI CallWindowProcA( WNDPROC func, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) +LRESULT WINAPI DECLSPEC_HOTPATCH CallWindowProcA( WNDPROC func, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { struct win_proc_params params; LRESULT result; From 4fb9230526fa3888f2fad3885d65acafc16715fa Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 20 Sep 2023 14:29:33 +0100 Subject: [PATCH 0675/1148] user32: Add hotpatchable wrapper for GetWindowLongA. ntlea for some reason expects GetWindowLongA to start with a "push $-2", and will try to skip over this instruction. If we don't anticipate this, it will ended up either skipping over critical instructions, or on a desync address. (upstream commit f9f9481b67f7ecddd7cb5ddd308a9338d26445e4) CW-Bug-Id: #22660 --- dlls/user32/win.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/dlls/user32/win.c b/dlls/user32/win.c index 28cf40441d9..7eaaa41e951 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -888,7 +888,28 @@ WORD WINAPI GetWindowWord( HWND hwnd, INT offset ) /********************************************************************** * GetWindowLongA (USER32.@) */ -LONG WINAPI GetWindowLongA( HWND hwnd, INT offset ) + +#ifdef __i386__ + +/* This wrapper is here to workaround a ntlea quirk. First of all, ntlea + * checks whether GetWindowLongA starts with the Win32 hotpatchable prologue, + * if it can find that, it will use a hooking strategy more difficult for us + * to deal with. Secondly, it assumes what follows the prologue is a `pushl $-2`, + * and will try to skip over this instruction when calling `GetWindowLongA`, + * (i.e. it tries to jump to `GetWindowLongA + 7`, 5 bytes for the prologue, 2 + * bytes for the `pushl`.). We have to anticipate that and make sure the result + * of doing this won't be a messed up stack, or a desynced PC. + */ +__ASM_STDCALL_FUNC( GetWindowLongA, 8, + ".byte 0x8b, 0xff, 0x55, 0x8b, 0xec\n" /* Win32 hotpatchable prologue. */ + "pushl $-2\n" + "addl $4, %esp\n" + "popl %ebp\n" + "jmp " __ASM_STDCALL("get_window_longA", 8) ) +LONG WINAPI get_window_longA( HWND hwnd, INT offset ) +#else +LONG WINAPI DECLSPEC_HOTPATCH GetWindowLongA( HWND hwnd, INT offset ) +#endif { switch (offset) { From 3a142cbedb414ceaf10986058e06230af24eb371 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 27 Sep 2023 20:17:13 -0600 Subject: [PATCH 0676/1148] ntdll: Map EDESTADDRREQ to STATUS_INVALID_CONNECTION. CW-Bug-Id: #22794 --- dlls/ntdll/unix/socket.c | 2 +- dlls/ws2_32/tests/sock.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 32a40570d14..dfbc892be05 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -170,7 +170,7 @@ static NTSTATUS sock_errno_to_status( int err ) case EWOULDBLOCK: return STATUS_DEVICE_NOT_READY; case EALREADY: return STATUS_NETWORK_BUSY; case ENOTSOCK: return STATUS_OBJECT_TYPE_MISMATCH; - case EDESTADDRREQ: return STATUS_INVALID_PARAMETER; + case EDESTADDRREQ: return STATUS_INVALID_CONNECTION; case EMSGSIZE: return STATUS_BUFFER_OVERFLOW; case EPROTONOSUPPORT: case ESOCKTNOSUPPORT: diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index ae582aba406..8dab68b8938 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -13759,7 +13759,7 @@ static void test_connect_udp(void) SetLastError(0xdeadbeef); ret = send(client, "data", 4, 0); ok(ret == -1, "got %d\n", ret); - todo_wine ok(GetLastError() == WSAENOTCONN, "got error %lu\n", GetLastError()); + ok(GetLastError() == WSAENOTCONN, "got error %lu\n", GetLastError()); SetLastError(0xdeadbeef); ret = recv(server, buffer, sizeof(buffer), 0); @@ -13807,7 +13807,7 @@ static void test_connect_udp(void) SetLastError(0xdeadbeef); ret = send(server, "data", 4, 0); ok(ret == -1, "got %d\n", ret); - todo_wine ok(GetLastError() == WSAENOTCONN, "got error %lu\n", GetLastError()); + ok(GetLastError() == WSAENOTCONN, "got error %lu\n", GetLastError()); ret = connect(client, (struct sockaddr *)&addr, sizeof(addr)); ok(!ret, "got error %lu\n", GetLastError()); From c58f5eceba1fd744d9d40ab9eafa1e26e7ef3417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 28 Sep 2023 09:06:28 +0200 Subject: [PATCH 0677/1148] makedep: Track external modules with a dedicated flag. CW-Bug-Id: #22729 --- tools/makedep.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tools/makedep.c b/tools/makedep.c index 6b02b154e45..9612eae4802 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -205,6 +205,7 @@ struct makefile int is_win16; int is_exe; int has_cxx; + int is_external; int disabled[MAX_ARCHS]; /* values generated at output time */ @@ -1502,7 +1503,7 @@ static struct file *open_include_file( const struct makefile *make, struct incl_ return file; } - if (make->extlib || make->has_cxx) return NULL; /* ignore missing files in external libs */ + if (make->extlib || make->is_external) return NULL; /* ignore missing files in external libs */ fprintf( stderr, "%s:%d: error: ", pFile->included_by->file->name, pFile->included_line ); perror( pFile->name ); @@ -1555,13 +1556,6 @@ static void parse_file( struct makefile *make, struct incl_file *source, int src { struct file *file = src ? open_src_file( make, source ) : open_include_file( make, source ); - if (strendswith( source->name, ".cpp" )) - { - /* ignore missing headers or unsupported includes in .cpp files */ - source->is_external = 1; - make->has_cxx = 1; - } - if (!file) return; source->file = file; @@ -1627,7 +1621,7 @@ static struct incl_file *add_src_file( struct makefile *make, const char *name ) memset( file, 0, sizeof(*file) ); file->name = xstrdup(name); file->use_msvcrt = make->use_msvcrt; - file->is_external = !!make->extlib; + file->is_external = !!make->extlib || make->is_external; list_add_tail( &make->sources, &file->entry ); if (make == include_makefile) { @@ -4552,7 +4546,11 @@ int main( int argc, char *argv[] ) for (i = 0; i < subdirs.count; i++) { submakes[i] = parse_makefile( subdirs.str[i] ); - if (*(tmp = subdirs.str[i]) == '/') tmp = strmake( "dlls%s", strrchr( tmp, '/' ) ); + if (*(tmp = subdirs.str[i]) == '/') + { + tmp = strmake( "dlls%s", strrchr( tmp, '/' ) ); + submakes[i]->is_external = 1; + } submakes[i]->obj_dir = subdirs.str[i] = tmp; } From 87377a5248c21a4dbc16940344817ac1bdf19661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 28 Sep 2023 09:06:55 +0200 Subject: [PATCH 0678/1148] makedep: Avoid linking external modules with -lntdll. They don't have ntdll module source so this won't be translated into ntdll.so path. External modules will have to do that elsewhere. CW-Bug-Id: #22729 --- tools/makedep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/makedep.c b/tools/makedep.c index 9612eae4802..cb85b50960c 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -2140,7 +2140,7 @@ static struct strarray add_unix_libraries( const struct makefile *make, struct s struct strarray all_libs = empty_strarray; unsigned int i, j; - if (strcmp( make->unixlib, "ntdll.so" )) strarray_add( &all_libs, "-lntdll" ); + if (strcmp( make->unixlib, "ntdll.so" ) && !make->is_external) strarray_add( &all_libs, "-lntdll" ); strarray_addall( &all_libs, get_expanded_make_var_array( make, "UNIX_LIBS" )); for (i = 0; i < all_libs.count; i++) From c7862e4f8600bf433e4c34e460eb8a257b8419ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 28 Sep 2023 09:07:45 +0200 Subject: [PATCH 0679/1148] makedep: Correctly link unixlib with C++ sources with g++. CW-Bug-Id: #22729 --- tools/makedep.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/makedep.c b/tools/makedep.c index cb85b50960c..2707e1c77bc 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -3460,7 +3460,8 @@ static void output_unix_lib( struct makefile *make ) output_filenames_obj_dir( make, make->unixobj_files ); output_filenames( unix_deps ); output( "\n" ); - output( "\t%s$(CC) -o $@", cmd_prefix( "CCLD" )); + if (make->has_cxx) output( "\t%s$(CXX) -o $@", cmd_prefix( "CCLD" )); + else output( "\t%s$(CC) -o $@", cmd_prefix( "CCLD" )); output_filenames( get_expanded_make_var_array( make, "UNIXLDFLAGS" )); output_filenames_obj_dir( make, make->unixobj_files ); output_filenames( unix_libs ); From 8acb51613e66989baecd3e4276c0455f5926309b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 28 Sep 2023 09:14:25 +0200 Subject: [PATCH 0680/1148] nsiproxy.sys: Fix compilation warnings. --- dlls/nsiproxy.sys/device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index 8acdecdb735..9a8b1a5905e 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -19,6 +19,8 @@ */ #include +#include +#include #include "ntstatus.h" #define WIN32_NO_STATUS From 5c4d869e0d87c7ae91fab3ab77eaf37504235441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 28 Sep 2023 17:36:01 +0200 Subject: [PATCH 0681/1148] fixup! win32u: Keep the clipping rectangle inside a fullscreen window. --- dlls/win32u/input.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index c3ee888557a..060ee2e23d6 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2637,6 +2637,7 @@ BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset ) */ BOOL WINAPI NtUserClipCursor( const RECT *rect ) { + static int keep_inside_window = -1; HWND foreground = NtUserGetForegroundWindow(); UINT dpi; BOOL ret; @@ -2660,8 +2661,14 @@ BOOL WINAPI NtUserClipCursor( const RECT *rect ) rect = &new_rect; } + if (keep_inside_window == -1) + { + const char *sgi = getenv( "SteamGameId" ); + keep_inside_window = sgi && !strcmp( sgi, "730830" ); /* Escape from Monkey Island */ + } + /* keep the mouse clipped inside of a fullscreen foreground window */ - if (NtUserGetWindowRect( foreground, &full_rect ) && is_window_rect_full_screen( &full_rect )) + if (keep_inside_window && 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 ) ); From fbf9cf6ebadc7d50c8fb7a130851c3658607b343 Mon Sep 17 00:00:00 2001 From: Jiajin Cui Date: Wed, 21 Jun 2023 17:53:52 +0800 Subject: [PATCH 0682/1148] shell32: Make sure wcmd has enough space to hold the string. If the length of wszApplicationName exceeds 1024, it will cause an error when writing to the subsequent stack space after exceeding the wcmd space, Wcmd needs to be modified to dynamic allocation. Signed-off-by: Jiajin Cui (cherry picked from commit eee640c93874317ab3ff62ca6afda962ac96d999) CW-Bug-Id: #22797 --- dlls/shell32/shlexec.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dlls/shell32/shlexec.c b/dlls/shell32/shlexec.c index 411d0c691a3..bc3f2ceabb4 100644 --- a/dlls/shell32/shlexec.c +++ b/dlls/shell32/shlexec.c @@ -1764,6 +1764,14 @@ static BOOL SHELL_execute( LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc ) TRACE("execute:%s,%s,%s\n", debugstr_w(wszApplicationName), debugstr_w(wszParameters), debugstr_w(wszDir)); lpFile = sei_tmp.lpFile; wcmd = wcmdBuffer; + len = lstrlenW(wszApplicationName) + 3; + if (sei_tmp.lpParameters[0]) + len += 1 + lstrlenW(wszParameters); + if (len > wcmdLen) + { + wcmd = heap_alloc(len * sizeof(WCHAR)); + wcmdLen = len; + } lstrcpyW(wcmd, wszApplicationName); if (sei_tmp.lpDirectory) { From 9b73e3df90970c7784f18da259606440ecea14e4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 28 Sep 2023 19:29:03 -0600 Subject: [PATCH 0683/1148] shell32: Avoid stack corruption with long name in SHELL_TryAppPathW(). CW-Bug-Id: #22797 --- dlls/shell32/shlexec.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dlls/shell32/shlexec.c b/dlls/shell32/shlexec.c index bc3f2ceabb4..099343334c3 100644 --- a/dlls/shell32/shlexec.c +++ b/dlls/shell32/shlexec.c @@ -428,8 +428,14 @@ static BOOL SHELL_TryAppPathW( LPCWSTR szName, LPWSTR lpResult, WCHAR **env) BOOL found = FALSE; if (env) *env = NULL; - lstrcpyW(buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"); - lstrcatW(buffer, szName); + wcscpy(buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"); + if (wcslen(buffer) + wcslen(szName) + 1 > ARRAY_SIZE(buffer)) + { + WARN("Name is too big.\n"); + return FALSE; + } + + wcscat(buffer, szName); res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp); if (res) goto end; From 3f3a500f1d8dc5099a7ef593e03f792c6b29599b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 28 Sep 2023 19:23:41 -0600 Subject: [PATCH 0684/1148] shell32: Handle long command line in execute_from_key(). CW-Bug-Id: #22797 --- dlls/shell32/shlexec.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dlls/shell32/shlexec.c b/dlls/shell32/shlexec.c index 099343334c3..2d1830ad838 100644 --- a/dlls/shell32/shlexec.c +++ b/dlls/shell32/shlexec.c @@ -965,7 +965,7 @@ static UINT_PTR execute_from_key(LPCWSTR key, LPCWSTR lpFile, WCHAR *env, LPCWST SHELL_ExecuteW32 execfunc, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out) { - WCHAR cmd[256], param[1024], ddeexec[256]; + WCHAR cmd[256], parambuffer[1024], ddeexec[256], *param = parambuffer; LONG cmdlen = sizeof(cmd), ddeexeclen = sizeof(ddeexec); UINT_PTR retval = SE_ERR_NOASSOC; DWORD resultLen; @@ -987,9 +987,14 @@ static UINT_PTR execute_from_key(LPCWSTR key, LPCWSTR lpFile, WCHAR *env, LPCWST if (cmdlen >= ARRAY_SIZE(cmd)) cmdlen = ARRAY_SIZE(cmd) - 1; cmd[cmdlen] = '\0'; - SHELL_ArgifyW(param, ARRAY_SIZE(param), cmd, lpFile, psei->lpIDList, szCommandline, &resultLen); - if (resultLen > ARRAY_SIZE(param)) - ERR("Argify buffer not large enough, truncating\n"); + SHELL_ArgifyW(param, ARRAY_SIZE(parambuffer), cmd, lpFile, psei->lpIDList, szCommandline, &resultLen); + if (resultLen > ARRAY_SIZE(parambuffer)) + { + if ((param = malloc(resultLen * sizeof(*param)))) + SHELL_ArgifyW(param, resultLen, cmd, lpFile, psei->lpIDList, szCommandline, &resultLen); + else + ERR("No memory."); + } } /* Get the parameters needed by the application @@ -1012,6 +1017,7 @@ static UINT_PTR execute_from_key(LPCWSTR key, LPCWSTR lpFile, WCHAR *env, LPCWST else WARN("Nothing appropriate found for %s\n", debugstr_w(key)); + if (param != parambuffer) free(param); return retval; } From ff75f2ab3c10d509f930e2ade063386d8186240a Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 29 Sep 2023 16:39:51 +0100 Subject: [PATCH 0685/1148] Revert "include: Don't align the stack for PE builds." This reverts commit 87fbf3305fe733b475a1f907a730f40b4823424b. We saw hangs in Phasmophobia with the commit, and then discovered this commit is not strictly necessary for ntlea, so we revert it. --- include/msvcrt/corecrt.h | 9 ++++++--- include/windef.h | 23 ++++++++++++++--------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/msvcrt/corecrt.h b/include/msvcrt/corecrt.h index 4ddea3edb1e..aba9bc9422e 100644 --- a/include/msvcrt/corecrt.h +++ b/include/msvcrt/corecrt.h @@ -82,7 +82,7 @@ #define __has_attribute(x) 0 #endif -#if !defined(_MSC_VER) && !defined(__MINGW32__) +#ifndef _MSC_VER # undef __stdcall # ifdef __i386__ # ifdef __GNUC__ @@ -100,13 +100,16 @@ # else # define __stdcall __attribute__((ms_abi)) # endif -# elif defined(__arm__) && defined (__GNUC__) && !defined(__SOFTFP__) && !defined(__CYGWIN__) +# elif defined(__arm__) && defined (__GNUC__) && !defined(__SOFTFP__) && !defined(__MINGW32__) && !defined(__CYGWIN__) # define __stdcall __attribute__((pcs("aapcs-vfp"))) # elif defined(__aarch64__) && defined (__GNUC__) && __has_attribute(ms_abi) # define __stdcall __attribute__((ms_abi)) # else /* __i386__ */ # define __stdcall # endif /* __i386__ */ +#endif /* __stdcall */ + +#ifndef _MSC_VER # undef __cdecl # if defined(__i386__) && defined(__GNUC__) # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || defined(__APPLE__) @@ -117,7 +120,7 @@ # else # define __cdecl __stdcall # endif -#endif /* _MSC_VER || __MINGW32__ */ +#endif #if (defined(__x86_64__) || (defined(__aarch64__) && __has_attribute(ms_abi))) && defined (__GNUC__) # include diff --git a/include/windef.h b/include/windef.h index 827e9f92a26..cb7eb1179c8 100644 --- a/include/windef.h +++ b/include/windef.h @@ -54,7 +54,7 @@ extern "C" { # endif #endif -#if !defined(_MSC_VER) && !defined(__MINGW32__) +#ifndef _MSC_VER # undef __stdcall # ifdef __i386__ # ifdef __GNUC__ @@ -72,13 +72,16 @@ extern "C" { # else # define __stdcall __attribute__((ms_abi)) # endif -# elif defined(__arm__) && defined (__GNUC__) && !defined(__SOFTFP__) && !defined(__CYGWIN__) +# elif defined(__arm__) && defined (__GNUC__) && !defined(__SOFTFP__) && !defined(__MINGW32__) && !defined(__CYGWIN__) # define __stdcall __attribute__((pcs("aapcs-vfp"))) # elif defined(__aarch64__) && defined (__GNUC__) && __has_attribute(ms_abi) # define __stdcall __attribute__((ms_abi)) # else /* __i386__ */ # define __stdcall # endif /* __i386__ */ +#endif /* __stdcall */ + +#ifndef _MSC_VER # undef __cdecl # if defined(__i386__) && defined(__GNUC__) # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || defined(__APPLE__) @@ -89,13 +92,15 @@ extern "C" { # else # define __cdecl __stdcall # endif -# ifndef __fastcall -# define __fastcall __stdcall -# endif -# ifndef __thiscall -# define __thiscall __stdcall -# endif -#endif /* _MSC_VER || __MINGW32__ */ +#endif + +#if !defined(_MSC_VER) && !defined(__fastcall) +# define __fastcall __stdcall +#endif + +#if (!defined(_MSC_VER) || !defined(__clang__)) && !defined(__thiscall) +# define __thiscall __stdcall +#endif #ifndef __ms_va_list # if (defined(__x86_64__) || (defined(__aarch64__) && __has_attribute(ms_abi))) && defined (__GNUC__) From ea95125cf60ec539c8a613e9a5baef3a861f6074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 7 Jun 2023 16:24:20 +0200 Subject: [PATCH 0686/1148] mf/session: Use local variables to access transform node streams. (cherry picked from commit b3e4a52fcc750f4b2900c71665ccdefe378ff342) CW-Bug-Id: #21713 --- dlls/mf/session.c | 48 +++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 25e1c474b44..a31a8110f37 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -3139,6 +3139,8 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, /* Collect returned samples for all streams. */ for (i = 0; i < node->u.transform.output_count; ++i) { + struct transform_stream *stream = &node->u.transform.outputs[i]; + if (buffers[i].pEvents) IMFCollection_Release(buffers[i].pEvents); @@ -3148,7 +3150,7 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, IMFQualityManager_NotifyProcessOutput(session->quality_manager, node->node, i, buffers[i].pSample); queued_sample = transform_create_sample(buffers[i].pSample); - list_add_tail(&node->u.transform.outputs[i].samples, &queued_sample->entry); + list_add_tail(&stream->samples, &queued_sample->entry); } if (buffers[i].pSample) @@ -3200,17 +3202,21 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop } break; case MF_TOPOLOGY_TRANSFORM_NODE: + { + struct transform_stream *input_stream = &topo_node->u.transform.inputs[input]; transform_node_pull_samples(session, topo_node); sample_entry = transform_create_sample(sample); - list_add_tail(&topo_node->u.transform.inputs[input].samples, &sample_entry->entry); + list_add_tail(&input_stream->samples, &sample_entry->entry); for (i = 0; i < topo_node->u.transform.input_count; ++i) { + struct transform_stream *stream = &topo_node->u.transform.inputs[i]; + stream_id = transform_node_get_stream_id(topo_node, FALSE, i); - LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.inputs[i].samples, - struct sample, entry) + + LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, struct sample, entry) { if (sample_entry->sample) { @@ -3223,7 +3229,7 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop } else { - transform_stream_drop_samples(&topo_node->u.transform.inputs[i]); + transform_stream_drop_samples(stream); drain = TRUE; } } @@ -3242,28 +3248,32 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop { for (i = 0; i < topo_node->u.transform.output_count; ++i) { - if ((sample_entry = transform_create_sample(NULL))) - list_add_tail(&topo_node->u.transform.outputs[i].samples, &sample_entry->entry); + struct transform_stream *stream = &topo_node->u.transform.outputs[i]; + + if ((sample_entry = transform_create_sample(NULL))) + list_add_tail(&stream->samples, &sample_entry->entry); } } /* Push down all available output. */ for (i = 0; i < topo_node->u.transform.output_count; ++i) { + struct transform_stream *stream = &topo_node->u.transform.outputs[i]; + if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input))) { WARN("Failed to get connected node for output %u.\n", i); continue; } - LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.outputs[i].samples, + LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, struct sample, entry) { - if (!topo_node->u.transform.outputs[i].requests) + if (!stream->requests) break; session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample); - topo_node->u.transform.outputs[i].requests--; + stream->requests--; transform_release_sample(sample_entry); } @@ -3272,6 +3282,7 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop } break; + } case MF_TOPOLOGY_TEE_NODE: FIXME("Unhandled downstream node type %d.\n", node_type); break; @@ -3304,20 +3315,22 @@ static void session_deliver_pending_samples(struct media_session *session, IMFTo /* Push down all available output. */ for (i = 0; i < topo_node->u.transform.output_count; ++i) { + struct transform_stream *stream = &topo_node->u.transform.outputs[i]; + if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input))) { WARN("Failed to get connected node for output %u.\n", i); continue; } - LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &topo_node->u.transform.outputs[i].samples, + LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, struct sample, entry) { - if (!topo_node->u.transform.outputs[i].requests) + if (!stream->requests) break; session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample); - topo_node->u.transform.outputs[i].requests--; + stream->requests--; transform_release_sample(sample_entry); } @@ -3353,14 +3366,16 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I WARN("Sample request failed, hr %#lx.\n", hr); break; case MF_TOPOLOGY_TRANSFORM_NODE: + { + struct transform_stream *stream = &topo_node->u.transform.outputs[output]; - if (list_empty(&topo_node->u.transform.outputs[output].samples)) + if (list_empty(&stream->samples)) { /* Forward request to upstream node. */ if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node, 0 /* FIXME */, &upstream_node, &upstream_output))) { if (SUCCEEDED(hr = session_request_sample_from_node(session, upstream_node, upstream_output))) - topo_node->u.transform.outputs[output].requests++; + stream->requests++; IMFTopologyNode_Release(upstream_node); } } @@ -3368,7 +3383,7 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I { if (SUCCEEDED(hr = IMFTopologyNode_GetOutput(node, output, &downstream_node, &downstream_input))) { - sample = LIST_ENTRY(list_head(&topo_node->u.transform.outputs[output].samples), struct sample, entry); + sample = LIST_ENTRY(list_head(&stream->samples), struct sample, entry); session_deliver_sample_to_node(session, downstream_node, downstream_input, sample->sample); transform_release_sample(sample); IMFTopologyNode_Release(downstream_node); @@ -3376,6 +3391,7 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I } break; + } case MF_TOPOLOGY_TEE_NODE: FIXME("Unhandled upstream node type %d.\n", node_type); default: From 86294623c0a0b37bf7bded04adcb7abd2722e586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 4 Jul 2023 09:30:26 +0200 Subject: [PATCH 0687/1148] mf/session: Use a helper to deliver transform node requested samples. (cherry picked from commit 46743a0546d381d65346de65c0ad7e64a2fd50f3) CW-Bug-Id: #21713 --- dlls/mf/session.c | 98 +++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 59 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index a31a8110f37..bf338dee3a2 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -3162,12 +3162,47 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, return hr; } +static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, + IMFSample *sample); + +static void transform_node_deliver_samples(struct media_session *session, struct topo_node *topo_node) +{ + IMFTopologyNode *up_node = topo_node->node, *down_node; + struct sample *sample, *next; + DWORD output, input; + HRESULT hr; + + /* Push down all available output. */ + for (output = 0; output < topo_node->u.transform.output_count; ++output) + { + struct transform_stream *stream = &topo_node->u.transform.outputs[output]; + + if (FAILED(hr = IMFTopologyNode_GetOutput(up_node, output, &down_node, &input))) + { + WARN("Failed to node %p/%lu output, hr %#lx.\n", up_node, output, hr); + continue; + } + + LIST_FOR_EACH_ENTRY_SAFE(sample, next, &stream->samples, struct sample, entry) + { + if (!stream->requests) + break; + + session_deliver_sample_to_node(session, down_node, input, sample->sample); + stream->requests--; + + transform_release_sample(sample); + } + + IMFTopologyNode_Release(down_node); + } +} + static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, IMFSample *sample) { struct sample *sample_entry, *sample_entry2; - DWORD stream_id, downstream_input; - IMFTopologyNode *downstream_node; + DWORD stream_id; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; BOOL drain = FALSE; @@ -3255,32 +3290,7 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop } } - /* Push down all available output. */ - for (i = 0; i < topo_node->u.transform.output_count; ++i) - { - struct transform_stream *stream = &topo_node->u.transform.outputs[i]; - - if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input))) - { - WARN("Failed to get connected node for output %u.\n", i); - continue; - } - - LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, - struct sample, entry) - { - if (!stream->requests) - break; - - session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample); - stream->requests--; - - transform_release_sample(sample_entry); - } - - IMFTopologyNode_Release(downstream_node); - } - + transform_node_deliver_samples(session, topo_node); break; } case MF_TOPOLOGY_TEE_NODE: @@ -3293,13 +3303,9 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop static void session_deliver_pending_samples(struct media_session *session, IMFTopologyNode *node) { - struct sample *sample_entry, *sample_entry2; - IMFTopologyNode *downstream_node; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; - DWORD downstream_input; TOPOID node_id; - unsigned int i; IMFTopologyNode_GetNodeType(node, &node_type); IMFTopologyNode_GetTopoNodeID(node, &node_id); @@ -3309,34 +3315,8 @@ static void session_deliver_pending_samples(struct media_session *session, IMFTo switch (node_type) { case MF_TOPOLOGY_TRANSFORM_NODE: - transform_node_pull_samples(session, topo_node); - - /* Push down all available output. */ - for (i = 0; i < topo_node->u.transform.output_count; ++i) - { - struct transform_stream *stream = &topo_node->u.transform.outputs[i]; - - if (FAILED(IMFTopologyNode_GetOutput(node, i, &downstream_node, &downstream_input))) - { - WARN("Failed to get connected node for output %u.\n", i); - continue; - } - - LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, - struct sample, entry) - { - if (!stream->requests) - break; - - session_deliver_sample_to_node(session, downstream_node, downstream_input, sample_entry->sample); - stream->requests--; - - transform_release_sample(sample_entry); - } - - IMFTopologyNode_Release(downstream_node); - } + transform_node_deliver_samples(session, topo_node); break; default: FIXME("Unexpected node type %u.\n", node_type); From 277b89688aed1e44cd3a39b87c722b9f968c50eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 6 Jul 2023 10:59:40 +0200 Subject: [PATCH 0688/1148] mf/session: Drain transform node input streams individually. (cherry picked from commit 6946c761bf0a043b1c777b39311addc5f66859e6) CW-Bug-Id: #21713 --- dlls/mf/session.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index bf338dee3a2..aa496494a01 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -3266,16 +3266,14 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop { transform_stream_drop_samples(stream); drain = TRUE; + + if (FAILED(hr = IMFTransform_ProcessMessage(topo_node->object.transform, + MFT_MESSAGE_COMMAND_DRAIN, stream_id))) + WARN("Drain command failed for transform, hr %#lx.\n", hr); } } } - if (drain) - { - if (FAILED(hr = IMFTransform_ProcessMessage(topo_node->object.transform, MFT_MESSAGE_COMMAND_DRAIN, 0))) - WARN("Drain command failed for transform, hr %#lx.\n", hr); - } - transform_node_pull_samples(session, topo_node); /* Remaining unprocessed input has been discarded, now queue markers for every output. */ From 4148e4f7384f107782c41ca907c80389ab618abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 8 Aug 2023 11:39:43 +0200 Subject: [PATCH 0689/1148] mf/session: Drain remaining requests in transform_node_deliver_samples. (cherry picked from commit b60db1693e91c01f8055b56cd595cb04c6c3c54c) CW-Bug-Id: #21713 --- dlls/mf/session.c | 55 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index aa496494a01..ff2f70f29aa 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -155,6 +155,7 @@ struct transform_stream struct list samples; unsigned int requests; unsigned int min_buffer_size; + BOOL draining; }; enum topo_node_flags @@ -892,7 +893,9 @@ static HRESULT session_subscribe_sources(struct media_session *session) static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *source; + struct topo_node *topo_node; HRESULT hr; + UINT i; switch (session->state) { @@ -928,6 +931,18 @@ static void session_start(struct media_session *session, const GUID *time_format } } + LIST_FOR_EACH_ENTRY(topo_node, &session->presentation.nodes, struct topo_node, entry) + { + if (topo_node->type == MF_TOPOLOGY_TRANSFORM_NODE) + { + for (i = 0; i < topo_node->u.transform.input_count; i++) + { + struct transform_stream *stream = &topo_node->u.transform.inputs[i]; + stream->draining = FALSE; + } + } + } + session->state = SESSION_STATE_STARTING_SOURCES; break; case SESSION_STATE_STARTED: @@ -3162,12 +3177,27 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, return hr; } +static BOOL transform_node_is_drained(struct topo_node *topo_node) +{ + UINT i; + + for (i = 0; i < topo_node->u.transform.input_count; i++) + { + struct transform_stream *stream = &topo_node->u.transform.inputs[i]; + if (!stream->draining) + return FALSE; + } + + return TRUE; +} + static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, IMFSample *sample); static void transform_node_deliver_samples(struct media_session *session, struct topo_node *topo_node) { IMFTopologyNode *up_node = topo_node->node, *down_node; + BOOL drained = transform_node_is_drained(topo_node); struct sample *sample, *next; DWORD output, input; HRESULT hr; @@ -3194,6 +3224,12 @@ static void transform_node_deliver_samples(struct media_session *session, struct transform_release_sample(sample); } + while (stream->requests && drained) + { + session_deliver_sample_to_node(session, down_node, input, NULL); + stream->requests--; + } + IMFTopologyNode_Release(down_node); } } @@ -3205,7 +3241,6 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop DWORD stream_id; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; - BOOL drain = FALSE; TOPOID node_id; unsigned int i; HRESULT hr; @@ -3264,30 +3299,16 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop } else { - transform_stream_drop_samples(stream); - drain = TRUE; - if (FAILED(hr = IMFTransform_ProcessMessage(topo_node->object.transform, MFT_MESSAGE_COMMAND_DRAIN, stream_id))) WARN("Drain command failed for transform, hr %#lx.\n", hr); + else + stream->draining = TRUE; } } } transform_node_pull_samples(session, topo_node); - - /* Remaining unprocessed input has been discarded, now queue markers for every output. */ - if (drain) - { - for (i = 0; i < topo_node->u.transform.output_count; ++i) - { - struct transform_stream *stream = &topo_node->u.transform.outputs[i]; - - if ((sample_entry = transform_create_sample(NULL))) - list_add_tail(&stream->samples, &sample_entry->entry); - } - } - transform_node_deliver_samples(session, topo_node); break; } From ff7ca2da72637f7917ffc7e77c2b203dc65c6f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 8 Aug 2023 11:32:44 +0200 Subject: [PATCH 0690/1148] mf/session: Use helpers to push and pop samples for transform streams. (cherry picked from commit 5d5134927652ca0fc07dd57f9aeb433a25dd3986) CW-Bug-Id: #21713 --- dlls/mf/session.c | 80 ++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index ff2f70f29aa..3dab2f3bd24 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -681,12 +681,41 @@ static void transform_release_sample(struct sample *sample) free(sample); } +static HRESULT transform_stream_push_sample(struct transform_stream *stream, IMFSample *sample) +{ + struct sample *entry; + + if (!(entry = calloc(1, sizeof(*entry)))) + return E_OUTOFMEMORY; + + entry->sample = sample; + IMFSample_AddRef(entry->sample); + + list_add_tail(&stream->samples, &entry->entry); + return S_OK; +} + +static HRESULT transform_stream_pop_sample(struct transform_stream *stream, IMFSample **sample) +{ + struct sample *entry; + struct list *ptr; + + if (!(ptr = list_head(&stream->samples))) + return E_FAIL; + + entry = LIST_ENTRY(ptr, struct sample, entry); + list_remove(&entry->entry); + *sample = entry->sample; + free(entry); + return S_OK; +} + static void transform_stream_drop_samples(struct transform_stream *stream) { - struct sample *sample, *sample2; + IMFSample *sample; - LIST_FOR_EACH_ENTRY_SAFE(sample, sample2, &stream->samples, struct sample, entry) - transform_release_sample(sample); + while (SUCCEEDED(transform_stream_pop_sample(stream, &sample))) + IMFSample_Release(sample); } static void release_topo_node(struct topo_node *node) @@ -3060,20 +3089,6 @@ static void session_set_sink_stream_state(struct media_session *session, IMFStre } } -static struct sample *transform_create_sample(IMFSample *sample) -{ - struct sample *sample_entry = calloc(1, sizeof(*sample_entry)); - - if (sample_entry) - { - sample_entry->sample = sample; - if (sample_entry->sample) - IMFSample_AddRef(sample_entry->sample); - } - - return sample_entry; -} - static HRESULT transform_get_external_output_sample(const struct media_session *session, struct topo_node *transform, unsigned int output_index, const MFT_OUTPUT_STREAM_INFO *stream_info, IMFSample **sample) { @@ -3122,7 +3137,6 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, { MFT_OUTPUT_STREAM_INFO stream_info; MFT_OUTPUT_DATA_BUFFER *buffers; - struct sample *queued_sample; HRESULT hr = E_UNEXPECTED; DWORD status = 0; unsigned int i; @@ -3163,9 +3177,8 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, { if (session->quality_manager) IMFQualityManager_NotifyProcessOutput(session->quality_manager, node->node, i, buffers[i].pSample); - - queued_sample = transform_create_sample(buffers[i].pSample); - list_add_tail(&stream->samples, &queued_sample->entry); + if (FAILED(hr = transform_stream_push_sample(stream, buffers[i].pSample))) + WARN("Failed to queue output sample, hr %#lx\n", hr); } if (buffers[i].pSample) @@ -3198,8 +3211,8 @@ static void transform_node_deliver_samples(struct media_session *session, struct { IMFTopologyNode *up_node = topo_node->node, *down_node; BOOL drained = transform_node_is_drained(topo_node); - struct sample *sample, *next; DWORD output, input; + IMFSample *sample; HRESULT hr; /* Push down all available output. */ @@ -3213,15 +3226,11 @@ static void transform_node_deliver_samples(struct media_session *session, struct continue; } - LIST_FOR_EACH_ENTRY_SAFE(sample, next, &stream->samples, struct sample, entry) + while (stream->requests && SUCCEEDED(hr = transform_stream_pop_sample(stream, &sample))) { - if (!stream->requests) - break; - - session_deliver_sample_to_node(session, down_node, input, sample->sample); + session_deliver_sample_to_node(session, down_node, input, sample); stream->requests--; - - transform_release_sample(sample); + IMFSample_Release(sample); } while (stream->requests && drained) @@ -3277,8 +3286,7 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop transform_node_pull_samples(session, topo_node); - sample_entry = transform_create_sample(sample); - list_add_tail(&input_stream->samples, &sample_entry->entry); + transform_stream_push_sample(input_stream, sample); for (i = 0; i < topo_node->u.transform.input_count; ++i) { @@ -3349,7 +3357,7 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I DWORD downstream_input, upstream_output; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; - struct sample *sample; + IMFSample *sample; TOPOID node_id; HRESULT hr; @@ -3368,7 +3376,7 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I { struct transform_stream *stream = &topo_node->u.transform.outputs[output]; - if (list_empty(&stream->samples)) + if (FAILED(hr = transform_stream_pop_sample(stream, &sample))) { /* Forward request to upstream node. */ if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node, 0 /* FIXME */, &upstream_node, &upstream_output))) @@ -3382,11 +3390,11 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I { if (SUCCEEDED(hr = IMFTopologyNode_GetOutput(node, output, &downstream_node, &downstream_input))) { - sample = LIST_ENTRY(list_head(&stream->samples), struct sample, entry); - session_deliver_sample_to_node(session, downstream_node, downstream_input, sample->sample); - transform_release_sample(sample); + session_deliver_sample_to_node(session, downstream_node, downstream_input, sample); IMFTopologyNode_Release(downstream_node); } + + IMFSample_Release(sample); } break; From eca73348c4ddd70eff68edfcdf2e8290376162b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 8 Aug 2023 11:41:13 +0200 Subject: [PATCH 0691/1148] mf/session: Push transform input samples directly to ProcessInput. (cherry picked from commit 986d82d147040a1e4e513d6a04283a71881f1279) CW-Bug-Id: #21713 --- dlls/mf/session.c | 74 +++++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 47 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 3dab2f3bd24..6770cf8bbbf 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -673,14 +673,6 @@ static void session_set_caps(struct media_session *session, DWORD caps) IMFMediaEvent_Release(event); } -static void transform_release_sample(struct sample *sample) -{ - list_remove(&sample->entry); - if (sample->sample) - IMFSample_Release(sample->sample); - free(sample); -} - static HRESULT transform_stream_push_sample(struct transform_stream *stream, IMFSample *sample) { struct sample *entry; @@ -3204,6 +3196,31 @@ static BOOL transform_node_is_drained(struct topo_node *topo_node) return TRUE; } +static HRESULT transform_node_push_sample(const struct media_session *session, struct topo_node *topo_node, + UINT input, IMFSample *sample) +{ + struct transform_stream *stream = &topo_node->u.transform.inputs[input]; + UINT id = transform_node_get_stream_id(topo_node, FALSE, input); + IMFTransform *transform = topo_node->object.transform; + HRESULT hr; + + if (sample) + { + hr = IMFTransform_ProcessInput(transform, id, sample, 0); + if (hr == MF_E_NOTACCEPTING) + hr = transform_stream_push_sample(stream, sample); + } + else + { + if (FAILED(hr = IMFTransform_ProcessMessage(transform, MFT_MESSAGE_COMMAND_DRAIN, id))) + WARN("Drain command failed for transform, hr %#lx.\n", hr); + else + stream->draining = TRUE; + } + + return hr; +} + static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, IMFSample *sample); @@ -3246,12 +3263,9 @@ static void transform_node_deliver_samples(struct media_session *session, struct static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, IMFSample *sample) { - struct sample *sample_entry, *sample_entry2; - DWORD stream_id; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; TOPOID node_id; - unsigned int i; HRESULT hr; if (session->quality_manager) @@ -3281,45 +3295,11 @@ static void session_deliver_sample_to_node(struct media_session *session, IMFTop } break; case MF_TOPOLOGY_TRANSFORM_NODE: - { - struct transform_stream *input_stream = &topo_node->u.transform.inputs[input]; - - transform_node_pull_samples(session, topo_node); - - transform_stream_push_sample(input_stream, sample); - - for (i = 0; i < topo_node->u.transform.input_count; ++i) - { - struct transform_stream *stream = &topo_node->u.transform.inputs[i]; - - stream_id = transform_node_get_stream_id(topo_node, FALSE, i); - - LIST_FOR_EACH_ENTRY_SAFE(sample_entry, sample_entry2, &stream->samples, struct sample, entry) - { - if (sample_entry->sample) - { - if ((hr = IMFTransform_ProcessInput(topo_node->object.transform, stream_id, - sample_entry->sample, 0)) == MF_E_NOTACCEPTING) - break; - if (FAILED(hr)) - WARN("Failed to process input for stream %u/%lu, hr %#lx.\n", i, stream_id, hr); - transform_release_sample(sample_entry); - } - else - { - if (FAILED(hr = IMFTransform_ProcessMessage(topo_node->object.transform, - MFT_MESSAGE_COMMAND_DRAIN, stream_id))) - WARN("Drain command failed for transform, hr %#lx.\n", hr); - else - stream->draining = TRUE; - } - } - } - + if (FAILED(hr = transform_node_push_sample(session, topo_node, input, sample))) + WARN("Failed to push or queue sample to transform, hr %#lx\n", hr); transform_node_pull_samples(session, topo_node); transform_node_deliver_samples(session, topo_node); break; - } case MF_TOPOLOGY_TEE_NODE: FIXME("Unhandled downstream node type %d.\n", node_type); break; From 605f02cd23332cdaac7da539212a4b828a64399f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 4 Jul 2023 10:15:18 +0200 Subject: [PATCH 0692/1148] mf/session: Request more samples from upstream when necessary. (cherry picked from commit 4d30ce831e7a17a8de9a19caa04bee67ae7dc8db) CW-Bug-Id: #21713 --- dlls/mf/session.c | 48 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 6770cf8bbbf..39d6c764a50 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -200,6 +200,7 @@ struct topo_node struct transform_stream *inputs; DWORD *input_map; unsigned int input_count; + unsigned int next_input; struct transform_stream *outputs; DWORD *output_map; @@ -693,7 +694,7 @@ static HRESULT transform_stream_pop_sample(struct transform_stream *stream, IMFS struct list *ptr; if (!(ptr = list_head(&stream->samples))) - return E_FAIL; + return MF_E_TRANSFORM_NEED_MORE_INPUT; entry = LIST_ENTRY(ptr, struct sample, entry); list_remove(&entry->entry); @@ -3196,6 +3197,17 @@ static BOOL transform_node_is_drained(struct topo_node *topo_node) return TRUE; } +static BOOL transform_node_has_requests(struct topo_node *topo_node) +{ + UINT i; + + for (i = 0; i < topo_node->u.transform.output_count; i++) + if (topo_node->u.transform.outputs[i].requests) + return TRUE; + + return FALSE; +} + static HRESULT transform_node_push_sample(const struct media_session *session, struct topo_node *topo_node, UINT input, IMFSample *sample) { @@ -3230,10 +3242,10 @@ static void transform_node_deliver_samples(struct media_session *session, struct BOOL drained = transform_node_is_drained(topo_node); DWORD output, input; IMFSample *sample; - HRESULT hr; + HRESULT hr = S_OK; /* Push down all available output. */ - for (output = 0; output < topo_node->u.transform.output_count; ++output) + for (output = 0; SUCCEEDED(hr) && output < topo_node->u.transform.output_count; ++output) { struct transform_stream *stream = &topo_node->u.transform.outputs[output]; @@ -3243,8 +3255,17 @@ static void transform_node_deliver_samples(struct media_session *session, struct continue; } - while (stream->requests && SUCCEEDED(hr = transform_stream_pop_sample(stream, &sample))) + while (stream->requests) { + if (FAILED(hr = transform_stream_pop_sample(stream, &sample))) + { + /* try getting more samples by calling IMFTransform_ProcessOutput */ + if (FAILED(hr = transform_node_pull_samples(session, topo_node))) + break; + if (FAILED(hr = transform_stream_pop_sample(stream, &sample))) + break; + } + session_deliver_sample_to_node(session, down_node, input, sample); stream->requests--; IMFSample_Release(sample); @@ -3258,6 +3279,25 @@ static void transform_node_deliver_samples(struct media_session *session, struct IMFTopologyNode_Release(down_node); } + + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT && transform_node_has_requests(topo_node)) + { + struct transform_stream *stream; + + input = topo_node->u.transform.next_input++ % topo_node->u.transform.input_count; + stream = &topo_node->u.transform.inputs[input]; + + if (SUCCEEDED(transform_stream_pop_sample(stream, &sample))) + session_deliver_sample_to_node(session, topo_node->node, input, sample); + else if (FAILED(hr = IMFTopologyNode_GetInput(topo_node->node, input, &up_node, &output))) + WARN("Failed to get node %p/%lu input, hr %#lx\n", topo_node->node, input, hr); + else + { + if (FAILED(hr = session_request_sample_from_node(session, up_node, output))) + WARN("Failed to request sample from upstream node %p/%lu, hr %#lx\n", up_node, output, hr); + IMFTopologyNode_Release(up_node); + } + } } static void session_deliver_sample_to_node(struct media_session *session, IMFTopologyNode *node, unsigned int input, From 72e83904bf2fc81c64ffdefd11d0ac59a23ba2c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 4 Jul 2023 10:15:18 +0200 Subject: [PATCH 0693/1148] mf/session: Increase the request count when requests are already queued. (cherry picked from commit 57bd5b10a11c9b80d0cd2ef48bcab286bceea405) CW-Bug-Id: #21713 --- dlls/mf/session.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 39d6c764a50..7a578d2c8d8 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -3373,13 +3373,13 @@ static void session_deliver_pending_samples(struct media_session *session, IMFTo static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *node, DWORD output) { - IMFTopologyNode *downstream_node, *upstream_node; - DWORD downstream_input, upstream_output; + IMFTopologyNode *down_node; struct topo_node *topo_node; MF_TOPOLOGY_TYPE node_type; + HRESULT hr = S_OK; IMFSample *sample; TOPOID node_id; - HRESULT hr; + DWORD input; IMFTopologyNode_GetNodeType(node, &node_type); IMFTopologyNode_GetTopoNodeID(node, &node_id); @@ -3396,27 +3396,29 @@ static HRESULT session_request_sample_from_node(struct media_session *session, I { struct transform_stream *stream = &topo_node->u.transform.outputs[output]; - if (FAILED(hr = transform_stream_pop_sample(stream, &sample))) + if (FAILED(hr = IMFTopologyNode_GetOutput(node, output, &down_node, &input))) { - /* Forward request to upstream node. */ - if (SUCCEEDED(hr = IMFTopologyNode_GetInput(node, 0 /* FIXME */, &upstream_node, &upstream_output))) - { - if (SUCCEEDED(hr = session_request_sample_from_node(session, upstream_node, upstream_output))) - stream->requests++; - IMFTopologyNode_Release(upstream_node); - } + WARN("Failed to node %p/%lu output, hr %#lx.\n", node, output, hr); + break; } - else - { - if (SUCCEEDED(hr = IMFTopologyNode_GetOutput(node, output, &downstream_node, &downstream_input))) - { - session_deliver_sample_to_node(session, downstream_node, downstream_input, sample); - IMFTopologyNode_Release(downstream_node); - } + if (SUCCEEDED(transform_stream_pop_sample(stream, &sample))) + { + session_deliver_sample_to_node(session, down_node, input, sample); IMFSample_Release(sample); } + else if (transform_node_has_requests(topo_node)) + { + /* there's already requests pending, just increase the counter */ + stream->requests++; + } + else + { + stream->requests++; + transform_node_deliver_samples(session, topo_node); + } + IMFTopologyNode_Release(down_node); break; } case MF_TOPOLOGY_TEE_NODE: From 7a90e28c58cc4d5fa60e7c1ff9d4aba12bdfa3d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 20 Sep 2023 09:49:29 +0200 Subject: [PATCH 0694/1148] makedep: Allow unixlibs to have a different 64bit name. CW-Bug-Id: #22729 --- tools/makedep.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tools/makedep.c b/tools/makedep.c index 2707e1c77bc..3a028287245 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -200,6 +200,7 @@ struct makefile const char *staticlib; const char *importlib; const char *unixlib; + const char *unixlib_x64; int use_msvcrt; int data_only; int is_win16; @@ -3450,13 +3451,15 @@ static void output_unix_lib( struct makefile *make ) struct strarray unix_deps = empty_strarray; struct strarray unix_libs = add_unix_libraries( make, &unix_deps ); unsigned int arch = 0; /* unix libs are always native */ + const char *unixlib = make->unixlib; if (make->disabled[arch]) return; + if (make->unixlib_x64 && !strcmp( archs.str[arch], "x86_64" )) unixlib = make->unixlib_x64; - strarray_add( &make->all_targets[arch], make->unixlib ); - add_install_rule( make, make->module, arch, make->unixlib, - strmake( "p%s%s", arch_install_dirs[arch], make->unixlib )); - output( "%s:", obj_dir_path( make, make->unixlib )); + strarray_add( &make->all_targets[arch], unixlib ); + add_install_rule( make, make->module, arch, unixlib, + strmake( "p%s%s", arch_install_dirs[arch], unixlib )); + output( "%s:", obj_dir_path( make, unixlib )); output_filenames_obj_dir( make, make->unixobj_files ); output_filenames( unix_deps ); output( "\n" ); @@ -4273,7 +4276,11 @@ static void load_sources( struct makefile *make ) make->staticlib = get_expanded_make_variable( make, "STATICLIB" ); make->importlib = get_expanded_make_variable( make, "IMPORTLIB" ); make->extlib = get_expanded_make_variable( make, "EXTLIB" ); - if (*dll_ext) make->unixlib = get_expanded_make_variable( make, "UNIXLIB" ); + if (*dll_ext) + { + make->unixlib = get_expanded_make_variable( make, "UNIXLIB" ); + make->unixlib_x64 = get_expanded_make_variable( make, "UNIXLIB_x64" ); + } make->programs = get_expanded_make_var_array( make, "PROGRAMS" ); make->scripts = get_expanded_make_var_array( make, "SCRIPTS" ); From c3e75250e8b32a995c1133aba607d3a663e91a2d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 3 Oct 2023 15:02:44 -0600 Subject: [PATCH 0695/1148] fixup! ntdll: Use kernel soft dirty flags for write watches support. CW-Bug-Id: #22809 --- dlls/ntdll/unix/virtual.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index b62968cb26d..05e91bd4f8c 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -1609,7 +1609,10 @@ static NTSTATUS create_view( struct file_view **view_ret, void *base, size_t siz } if (vprot & VPROT_WRITEWATCH && use_kernel_writewatch) + { + madvise( view->base, view->size, MADV_NOHUGEPAGE ); reset_write_watches( view->base, view->size ); + } return STATUS_SUCCESS; } @@ -2201,7 +2204,10 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, if (!set_vprot( *view_ret, base, size, vprot | VPROT_COMMITTED )) ERR("set_protection failed.\n"); if (vprot & VPROT_WRITEWATCH) + { + madvise( base, size, MADV_NOHUGEPAGE ); reset_write_watches( base, size ); + } return STATUS_SUCCESS; } TRACE("MEM_REPLACE_PLACEHOLDER view not found.\n"); From 709458a262f7c3f1194d013bc7384e6d8d96e821 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 3 Oct 2023 16:02:55 -0600 Subject: [PATCH 0696/1148] secur32: Syncrhonize access to schannel handle table. CW-Bug-Id: #22809 --- dlls/secur32/schannel.c | 47 +++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index d4aab2b4c2a..a1c641cd34e 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -71,6 +71,7 @@ static struct schan_handle *schan_handle_table; static struct schan_handle *schan_free_handles; static SIZE_T schan_handle_table_size; static SIZE_T schan_handle_count; +static SRWLOCK handle_table_lock = SRWLOCK_INIT; /* Protocols enabled, only those may be used for the connection. */ static DWORD config_enabled_protocols; @@ -81,22 +82,24 @@ static DWORD config_default_disabled_protocols; static ULONG_PTR schan_alloc_handle(void *object, enum schan_handle_type type) { struct schan_handle *handle; + ULONG_PTR index = SCHAN_INVALID_HANDLE; + AcquireSRWLockExclusive(&handle_table_lock); if (schan_free_handles) { - DWORD index = schan_free_handles - schan_handle_table; /* Use a free handle */ handle = schan_free_handles; if (handle->type != SCHAN_HANDLE_FREE) { - ERR("Handle %ld(%p) is in the free list, but has type %#x.\n", index, handle, handle->type); - return SCHAN_INVALID_HANDLE; + ERR("Handle %p is in the free list, but has type %#x.\n", handle, handle->type); + goto done; } + index = schan_free_handles - schan_handle_table; schan_free_handles = handle->object; handle->object = object; handle->type = type; - return index; + goto done; } if (!(schan_handle_count < schan_handle_table_size)) { @@ -106,7 +109,7 @@ static ULONG_PTR schan_alloc_handle(void *object, enum schan_handle_type type) if (!new_table) { ERR("Failed to grow the handle table\n"); - return SCHAN_INVALID_HANDLE; + goto done; } schan_handle_table = new_table; schan_handle_table_size = new_size; @@ -116,21 +119,30 @@ static ULONG_PTR schan_alloc_handle(void *object, enum schan_handle_type type) handle->object = object; handle->type = type; - return handle - schan_handle_table; + index = handle - schan_handle_table; + +done: + ReleaseSRWLockExclusive(&handle_table_lock); + return index; } static void *schan_free_handle(ULONG_PTR handle_idx, enum schan_handle_type type) { struct schan_handle *handle; - void *object; + void *object = NULL; if (handle_idx == SCHAN_INVALID_HANDLE) return NULL; - if (handle_idx >= schan_handle_count) return NULL; + + AcquireSRWLockExclusive(&handle_table_lock); + + if (handle_idx >= schan_handle_count) + goto done; + handle = &schan_handle_table[handle_idx]; if (handle->type != type) { ERR("Handle %Id(%p) is not of type %#x\n", handle_idx, handle, type); - return NULL; + goto done; } object = handle->object; @@ -138,23 +150,32 @@ static void *schan_free_handle(ULONG_PTR handle_idx, enum schan_handle_type type handle->type = SCHAN_HANDLE_FREE; schan_free_handles = handle; +done: + ReleaseSRWLockExclusive(&handle_table_lock); return object; } static void *schan_get_object(ULONG_PTR handle_idx, enum schan_handle_type type) { struct schan_handle *handle; + void *object = NULL; if (handle_idx == SCHAN_INVALID_HANDLE) return NULL; - if (handle_idx >= schan_handle_count) return NULL; + + AcquireSRWLockShared(&handle_table_lock); + if (handle_idx >= schan_handle_count) + goto done; handle = &schan_handle_table[handle_idx]; if (handle->type != type) { - ERR("Handle %Id(%p) is not of type %#x\n", handle_idx, handle, type); - return NULL; + ERR("Handle %Id(%p) is not of type %#x (%#x)\n", handle_idx, handle, type, handle->type); + goto done; } + object = handle->object; - return handle->object; +done: + ReleaseSRWLockShared(&handle_table_lock); + return object; } static void read_config(void) From 513d371c76b45e383a20e05a64c8c75dc52d5b0b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 3 Oct 2023 16:37:20 -0600 Subject: [PATCH 0697/1148] secur32: Get rid of schannel free handle list. CW-Bug-Id: #22809 --- dlls/secur32/schannel.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index a1c641cd34e..8129c17ad17 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -68,7 +68,6 @@ struct schan_context }; static struct schan_handle *schan_handle_table; -static struct schan_handle *schan_free_handles; static SIZE_T schan_handle_table_size; static SIZE_T schan_handle_count; static SRWLOCK handle_table_lock = SRWLOCK_INIT; @@ -82,23 +81,17 @@ static DWORD config_default_disabled_protocols; static ULONG_PTR schan_alloc_handle(void *object, enum schan_handle_type type) { struct schan_handle *handle; - ULONG_PTR index = SCHAN_INVALID_HANDLE; + ULONG_PTR index = SCHAN_INVALID_HANDLE, i; AcquireSRWLockExclusive(&handle_table_lock); - if (schan_free_handles) + for (i = 0; i < schan_handle_table_size; ++i) { - /* Use a free handle */ - handle = schan_free_handles; + handle = &schan_handle_table[i]; if (handle->type != SCHAN_HANDLE_FREE) - { - ERR("Handle %p is in the free list, but has type %#x.\n", handle, handle->type); - goto done; - } - index = schan_free_handles - schan_handle_table; - schan_free_handles = handle->object; + continue; handle->object = object; handle->type = type; - + index = i; goto done; } if (!(schan_handle_count < schan_handle_table_size)) @@ -146,9 +139,8 @@ static void *schan_free_handle(ULONG_PTR handle_idx, enum schan_handle_type type } object = handle->object; - handle->object = schan_free_handles; + handle->object = NULL; handle->type = SCHAN_HANDLE_FREE; - schan_free_handles = handle; done: ReleaseSRWLockExclusive(&handle_table_lock); From f3be587d495cb9a747ff714db44ba4e63e4ec23c Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Wed, 4 Oct 2023 16:05:30 -0300 Subject: [PATCH 0698/1148] mf: Clear end of presentation if topo_status is not invalid. Cw-Bug-Id: #22719 Cw-Bug-Id: #21809 --- dlls/mf/session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 7a578d2c8d8..0d3ed620a0c 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -2607,7 +2607,7 @@ 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); - if (session->presentation.topo_status == MF_TOPOSTATUS_READY) + if (session->presentation.topo_status != MF_TOPOSTATUS_INVALID) session_clear_end_of_presentation(session); session_stop(session); break; From 264d01417ab5cf23c922f8f86310b7bc6af7b2f5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 5 Oct 2023 08:37:31 -0600 Subject: [PATCH 0699/1148] Revert "msxml3: Implement FreeThreadedXMLHTTP60." This reverts commit 758483d719f42fad51d22f7d609bb521e8acc46c. CW-Bug-Id: #22822 --- dlls/msxml3/Makefile.in | 2 +- dlls/msxml3/factory.c | 5 - dlls/msxml3/httprequest.c | 496 +----------------------------------- dlls/msxml3/msxml_private.h | 1 - dlls/msxml3/tests/httpreq.c | 395 +--------------------------- dlls/msxml3/tests/schema.c | 6 - dlls/msxml3/uuid.c | 5 - 7 files changed, 5 insertions(+), 905 deletions(-) diff --git a/dlls/msxml3/Makefile.in b/dlls/msxml3/Makefile.in index e2d737599b1..2bf789732da 100644 --- a/dlls/msxml3/Makefile.in +++ b/dlls/msxml3/Makefile.in @@ -1,5 +1,5 @@ MODULE = msxml3.dll -IMPORTS = $(XSLT_PE_LIBS) $(XML2_PE_LIBS) uuid urlmon shlwapi oleaut32 ole32 user32 advapi32 rtworkq +IMPORTS = $(XSLT_PE_LIBS) $(XML2_PE_LIBS) uuid urlmon shlwapi oleaut32 ole32 user32 advapi32 EXTRAINCL = $(XSLT_PE_CFLAGS) $(XML2_PE_CFLAGS) C_SRCS = \ diff --git a/dlls/msxml3/factory.c b/dlls/msxml3/factory.c index 323c7b49848..243ee379712 100644 --- a/dlls/msxml3/factory.c +++ b/dlls/msxml3/factory.c @@ -279,7 +279,6 @@ static HRESULT DOMClassFactory_Create(const GUID *clsid, REFIID riid, void **ppv static ClassFactory xmldoccf = { { &ClassFactoryVtbl }, XMLDocument_create }; static ClassFactory httpreqcf = { { &ClassFactoryVtbl }, XMLHTTPRequest_create }; -static ClassFactory httpreqcf2 = { { &ClassFactoryVtbl }, XMLHTTPRequest2_create }; static ClassFactory serverhttp = { { &ClassFactoryVtbl }, ServerXMLHTTP_create }; static ClassFactory xsltemplatecf = { { &ClassFactoryVtbl }, XSLTemplate_create }; static ClassFactory mxnsmanagercf = { {&ClassFactoryVtbl }, MXNamespaceManager_create }; @@ -341,10 +340,6 @@ HRESULT WINAPI DllGetClassObject( REFCLSID rclsid, REFIID riid, void **ppv ) { cf = &httpreqcf.IClassFactory_iface; } - else if( IsEqualCLSID( rclsid, &CLSID_FreeThreadedXMLHTTP60 )) - { - cf = &httpreqcf2.IClassFactory_iface; - } else if( IsEqualCLSID( rclsid, &CLSID_ServerXMLHTTP ) || IsEqualCLSID( rclsid, &CLSID_ServerXMLHTTP30 ) || IsEqualCLSID( rclsid, &CLSID_ServerXMLHTTP40 ) || diff --git a/dlls/msxml3/httprequest.c b/dlls/msxml3/httprequest.c index 903787f9af0..cc384a380e5 100644 --- a/dlls/msxml3/httprequest.c +++ b/dlls/msxml3/httprequest.c @@ -38,12 +38,10 @@ #include "shlwapi.h" #include "msxml_dispex.h" -#include "initguid.h" -#include "rtworkq.h" #include "wine/debug.h" -WINE_DEFAULT_DEBUG_CHANNEL(xmlhttp); +WINE_DEFAULT_DEBUG_CHANNEL(msxml); static const WCHAR colspaceW[] = {':',' ',0}; static const WCHAR crlfW[] = {'\r','\n',0}; @@ -2060,468 +2058,6 @@ static const struct IServerXMLHTTPRequestVtbl ServerXMLHTTPRequestVtbl = ServerXMLHTTPRequest_setOption }; -static DWORD xhr2_work_queue; - -struct xml_http_request_2 -{ - httprequest req; - IXMLHTTPRequest3 IXMLHTTPRequest3_iface; - IRtwqAsyncCallback IRtwqAsyncCallback_iface; - IDispatch IDispatch_iface; - - IXMLHTTPRequest2Callback *callback; - IXMLHTTPRequest3Callback *callback3; - ISequentialStream *response_body; - ISequentialStream *request_body; - ULONGLONG request_body_size; -}; - -static inline struct xml_http_request_2 *impl_from_IXMLHTTPRequest3(IXMLHTTPRequest3 *iface) -{ - return CONTAINING_RECORD(iface, struct xml_http_request_2, IXMLHTTPRequest3_iface); -} - -static inline struct xml_http_request_2 *xml_http_request_2_from_IRtwqAsyncCallback(IRtwqAsyncCallback *iface) -{ - return CONTAINING_RECORD(iface, struct xml_http_request_2, IRtwqAsyncCallback_iface); -} - -static inline struct xml_http_request_2 *xml_http_request_2_from_IDispatch(IDispatch *iface) -{ - return CONTAINING_RECORD(iface, struct xml_http_request_2, IDispatch_iface); -} - -static HRESULT WINAPI xml_http_request_2_QueryInterface(IXMLHTTPRequest3 *iface, REFIID riid, void **obj) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - - TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); - - if (IsEqualGUID(riid, &IID_IXMLHTTPRequest3) || IsEqualGUID(riid, &IID_IXMLHTTPRequest2) - || IsEqualGUID(riid, &IID_IUnknown)) - { - *obj = iface; - IUnknown_AddRef((IUnknown*)*obj); - return S_OK; - } - - FIXME("Unsupported interface %s\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI xml_http_request_2_AddRef(IXMLHTTPRequest3 *iface) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - ULONG ref = InterlockedIncrement(&This->req.ref); - TRACE("(%p)->(%lu)\n", This, ref); - return ref; -} - -static ULONG WINAPI xml_http_request_2_Release(IXMLHTTPRequest3 *iface) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - ULONG ref = InterlockedDecrement(&This->req.ref); - - TRACE("(%p)->(%lu)\n", This, ref); - - if (ref == 0) - { - /* do not call httprequest_put_onreadystatechange to avoid ref cycle */ - This->req.sink = NULL; - if (This->response_body) ISequentialStream_Release(This->response_body); - if (This->request_body) ISequentialStream_Release(This->request_body); - if (This->callback3) IXMLHTTPRequest3Callback_Release(This->callback3); - if (This->callback) IXMLHTTPRequest2Callback_Release(This->callback); - heap_free(This); - RtwqShutdown(); - } - - return ref; -} - -static HRESULT WINAPI xml_http_request_2_Open(IXMLHTTPRequest3 *iface, const WCHAR *method, - const WCHAR *url, IXMLHTTPRequest2Callback *callback, - const WCHAR *username, const WCHAR *password, - const WCHAR *proxy_username, const WCHAR *proxy_password) -{ - static const WCHAR accept_encoding[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0}; - static const WCHAR empty = 0; - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - VARIANT async_v, username_v, password_v; - HRESULT hr; - - TRACE("(%p)->(%s %s %p %s %s %s %s)\n", This, debugstr_w(method), debugstr_w(url), callback, - debugstr_w(username), debugstr_w(password), debugstr_w(proxy_username), debugstr_w(proxy_password)); - - if (This->callback) IXMLHTTPRequest2Callback_Release(This->callback); - if (This->callback3) IXMLHTTPRequest3Callback_Release(This->callback3); - IXMLHTTPRequest2Callback_AddRef(callback); - This->callback = callback; - if (FAILED(IXMLHTTPRequest2Callback_QueryInterface(callback, &IID_IXMLHTTPRequest3Callback, (void **)&This->callback3))) - This->callback3 = NULL; - - if (proxy_username || proxy_password) FIXME("proxy credentials not implemented\n"); - - VariantInit(&async_v); - V_VT(&async_v) = VT_BOOL; - V_BOOL(&async_v) = FALSE; /* FIXME: TRUE needs a RTWQ_WINDOW_WORKQUEUE */ - - VariantInit(&username_v); - V_VT(&username_v) = VT_BSTR; - if (username) V_BSTR(&username_v) = SysAllocString(username); - else V_BSTR(&username_v) = SysAllocString(&empty); - - VariantInit(&password_v); - V_VT(&password_v) = VT_BSTR; - if (password) V_BSTR(&password_v) = SysAllocString(password); - else V_BSTR(&password_v) = SysAllocString(&empty); - - if (FAILED(hr = httprequest_open(&This->req, (BSTR)method, (BSTR)url, async_v, username_v, password_v))) - return hr; - return httprequest_setRequestHeader(&This->req, (BSTR)accept_encoding, (BSTR)&empty); -} - -static HRESULT WINAPI xml_http_request_2_Send(IXMLHTTPRequest3 *iface, ISequentialStream *body, ULONGLONG body_size) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - IRtwqAsyncResult *result; - HRESULT hr; - - TRACE("(%p)->(%p %s)\n", This, body, wine_dbgstr_longlong( body_size )); - - if (body_size) - { - ISequentialStream_AddRef(body); - This->request_body = body; - This->request_body_size = body_size; - } - - if (FAILED(hr = RtwqCreateAsyncResult(NULL, &This->IRtwqAsyncCallback_iface, NULL, &result))) - return hr; - // IRtwqAsyncCallback_Invoke(&This->IRtwqAsyncCallback_iface, result); - hr = RtwqPutWorkItem(xhr2_work_queue, 0, result); - if (result) IRtwqAsyncResult_Release(result); - - return hr; -} - -static HRESULT WINAPI xml_http_request_2_Abort(IXMLHTTPRequest3 *iface) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - TRACE("(%p) stub!\n", This); - return E_NOTIMPL; -} - -static HRESULT WINAPI xml_http_request_2_SetCookie(IXMLHTTPRequest3 *iface, const XHR_COOKIE *cookie, DWORD *state) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - FIXME("(%p)->(%p %p) stub!\n", This, cookie, state); - return E_NOTIMPL; -} - -static HRESULT WINAPI xml_http_request_2_SetCustomResponseStream(IXMLHTTPRequest3 *iface, ISequentialStream *stream) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - FIXME("(%p)->(%p) stub!\n", This, stream); - return E_NOTIMPL; -} - -static HRESULT WINAPI xml_http_request_2_SetProperty(IXMLHTTPRequest3 *iface, XHR_PROPERTY property, ULONGLONG value) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - FIXME("(%p)->(%#x %s) stub!\n", This, property, wine_dbgstr_longlong( value )); - return E_NOTIMPL; -} - -static HRESULT WINAPI xml_http_request_2_SetRequestHeader(IXMLHTTPRequest3 *iface, - const WCHAR *header, const WCHAR *value) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - TRACE("(%p)->(%s %s)\n", This, debugstr_w(header), debugstr_w(value)); - return httprequest_setRequestHeader(&This->req, (BSTR)header, (BSTR)value); -} - -static HRESULT WINAPI xml_http_request_2_GetAllResponseHeaders(IXMLHTTPRequest3 *iface, WCHAR **headers) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - FIXME("(%p)->(%p) stub!\n", This, headers); - return E_NOTIMPL; -} - -static HRESULT WINAPI xml_http_request_2_GetCookie(IXMLHTTPRequest3 *iface, const WCHAR *url, - const WCHAR *name, DWORD flags, - ULONG *cookies_count, XHR_COOKIE **cookies) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - FIXME("(%p)->(%s %s %#lx %p %p) stub!\n", This, debugstr_w(url), debugstr_w(name), flags, cookies_count, cookies); - return E_NOTIMPL; -} - -static HRESULT WINAPI xml_http_request_2_GetResponseHeader(IXMLHTTPRequest3 *iface, - const WCHAR *header, WCHAR **value) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - HRESULT hr; - - TRACE("(%p)->(%s %p)\n", This, debugstr_w(header), value); - - if (FAILED(hr = httprequest_getResponseHeader(&This->req, (BSTR)header, value))) - return hr; - -#define E_FILE_NOT_FOUND _HRESULT_TYPEDEF_(0x80070002) - - if (hr == S_FALSE) - { - *value = NULL; - return E_FILE_NOT_FOUND; - } - - return hr; -} - -static HRESULT WINAPI xml_http_request_3_SetClientCertificate(IXMLHTTPRequest3 *iface, DWORD count, const BYTE *hashes, const WCHAR *pin) -{ - struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - FIXME("(%p)->(%ld %p %s) stub!\n", This, count, hashes, debugstr_w(pin)); - return E_NOTIMPL; -} - -static const struct IXMLHTTPRequest3Vtbl XMLHTTPRequest3Vtbl = { - /* IUnknown methods */ - xml_http_request_2_QueryInterface, - xml_http_request_2_AddRef, - xml_http_request_2_Release, - /* IXMLHTTPRequest2 methods */ - xml_http_request_2_Open, - xml_http_request_2_Send, - xml_http_request_2_Abort, - xml_http_request_2_SetCookie, - xml_http_request_2_SetCustomResponseStream, - xml_http_request_2_SetProperty, - xml_http_request_2_SetRequestHeader, - xml_http_request_2_GetAllResponseHeaders, - xml_http_request_2_GetCookie, - xml_http_request_2_GetResponseHeader, - /* IXMLHTTPRequest3 methods */ - xml_http_request_3_SetClientCertificate, -}; - -static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_QueryInterface(IRtwqAsyncCallback *iface, REFIID riid, void **obj) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); - TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); - - if (IsEqualGUID(riid, &IID_IRtwqAsyncCallback) || IsEqualGUID(riid, &IID_IUnknown)) - { - IRtwqAsyncCallback_AddRef(iface); - *obj = iface; - return S_OK; - } - - FIXME("Unsupported interface %s\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI xml_http_request_2_IRtwqAsyncCallback_AddRef(IRtwqAsyncCallback *iface) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); - TRACE("(%p)\n", This); - return xml_http_request_2_AddRef(&This->IXMLHTTPRequest3_iface); -} - -static ULONG WINAPI xml_http_request_2_IRtwqAsyncCallback_Release(IRtwqAsyncCallback *iface) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); - TRACE("(%p)\n", This); - return xml_http_request_2_Release(&This->IXMLHTTPRequest3_iface); -} - -static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_GetParameters(IRtwqAsyncCallback *iface, - DWORD *flags, DWORD *queue) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); - - TRACE("(%p)->(%p %p)\n", This, flags, queue); - - *flags = 0; - *queue = xhr2_work_queue; - return S_OK; -} - -static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_Invoke(IRtwqAsyncCallback *iface, - IRtwqAsyncResult *result) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); - VARIANT body_v; - HRESULT hr; - ULONG read; - - TRACE("(%p)->(%p)\n", This, result); - - VariantInit(&body_v); - - if (This->request_body) - { - V_VT(&body_v) = VT_BSTR; - V_BSTR(&body_v) = CoTaskMemAlloc(This->request_body_size); - - if (FAILED(hr = ISequentialStream_Read(This->request_body, V_BSTR(&body_v), This->request_body_size, &read)) || - read < This->request_body_size) - { - ERR("Failed to allocate request body memory, hr %#lx\n", hr); - CoTaskMemFree(V_BSTR(&body_v)); - goto done; - } - - ISequentialStream_Release(This->request_body); - This->request_body = NULL; - } - - hr = httprequest_send(&This->req, body_v); - -done: - return IRtwqAsyncResult_SetStatus(result, hr); -} - -static const struct IRtwqAsyncCallbackVtbl xml_http_request_2_IRtwqAsyncCallbackVtbl = { - /* IUnknown methods */ - xml_http_request_2_IRtwqAsyncCallback_QueryInterface, - xml_http_request_2_IRtwqAsyncCallback_AddRef, - xml_http_request_2_IRtwqAsyncCallback_Release, - /* IRtwqAsyncCallback methods */ - xml_http_request_2_IRtwqAsyncCallback_GetParameters, - xml_http_request_2_IRtwqAsyncCallback_Invoke, -}; - -static HRESULT WINAPI xml_http_request_2_IDispatch_QueryInterface(IDispatch *iface, REFIID riid, void **obj) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); - TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); - - if (IsEqualGUID(riid, &IID_IDispatch) || IsEqualGUID(riid, &IID_IUnknown)) - { - IDispatch_AddRef(iface); - *obj = iface; - return S_OK; - } - - FIXME("Unsupported interface %s\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI xml_http_request_2_IDispatch_AddRef(IDispatch *iface) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); - TRACE("(%p)\n", This); - return xml_http_request_2_AddRef(&This->IXMLHTTPRequest3_iface); -} - -static ULONG WINAPI xml_http_request_2_IDispatch_Release(IDispatch *iface) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); - TRACE("(%p)\n", This); - return xml_http_request_2_Release(&This->IXMLHTTPRequest3_iface); -} - -static HRESULT WINAPI xml_http_request_2_IDispatch_GetTypeInfoCount(IDispatch *iface, UINT *value) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); - FIXME("(%p)->(%p) stub!\n", This, value); - *value = 0; - return S_OK; -} - -static HRESULT WINAPI xml_http_request_2_IDispatch_GetTypeInfo(IDispatch *iface, UINT index, - LCID lcid, ITypeInfo **value) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); - FIXME("(%p)->(%d %lu %p) stub!\n", This, index, lcid, value); - *value = NULL; - return S_OK; -} - -static HRESULT WINAPI xml_http_request_2_IDispatch_GetIDsOfNames(IDispatch *iface, REFIID riid, - OLECHAR **names, UINT names_count, - LCID lcid, DISPID *disp_ids) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); - FIXME("(%p)->(%s %p %d %lu %p) stub!\n", This, debugstr_guid(riid), names, names_count, lcid, disp_ids); - return S_OK; -} - -static HRESULT WINAPI xml_http_request_2_IDispatch_Invoke(IDispatch *iface, DISPID id, REFIID riid, - LCID lcid, WORD flags, DISPPARAMS *params, - VARIANT *result, EXCEPINFO *exception, UINT *arg_err) -{ - struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); - IXMLHTTPRequest2 *xhr2_iface = (IXMLHTTPRequest2*)&This->IXMLHTTPRequest3_iface; - HRESULT hr; - LONG status; - BSTR status_str = NULL; - - TRACE("(%p)->(%ld %s %lu %d %p %p %p %p) stub!\n", This, id, debugstr_guid(riid), lcid, flags, - params, result, exception, arg_err); - - if (This->req.state == READYSTATE_COMPLETE) - { - VARIANT body_v; - VariantInit(&body_v); - - IXMLHTTPRequest2Callback_AddRef(This->callback); - if (This->callback3) - { - IXMLHTTPRequest3Callback_AddRef(This->callback3); - IXMLHTTPRequest3Callback_OnServerCertificateReceived(This->callback3, (IXMLHTTPRequest3 *)xhr2_iface, 0, 1, NULL); - IXMLHTTPRequest3Callback_Release(This->callback3); - } - - if (FAILED(hr = httprequest_get_status(&This->req, &status)) || - FAILED(hr = httprequest_get_statusText(&This->req, &status_str))) - { - WARN("failed to get response status, error %#lx\n", hr); - IXMLHTTPRequest2Callback_OnError(This->callback, xhr2_iface, hr); - IXMLHTTPRequest2Callback_Release(This->callback); - return S_OK; - } - - IXMLHTTPRequest2Callback_OnHeadersAvailable(This->callback, xhr2_iface, status, status_str); - SysFreeString(status_str); - - if (This->response_body) ISequentialStream_Release(This->response_body); - This->response_body = NULL; - - if (FAILED(hr = httprequest_get_responseStream(&This->req, &body_v)) || - FAILED(hr = IUnknown_QueryInterface(V_UNKNOWN(&body_v), &IID_ISequentialStream, (void **)&This->response_body))) - { - WARN("failed to get response stream, error %#lx\n", hr); - IXMLHTTPRequest2Callback_OnError(This->callback, xhr2_iface, hr); - IXMLHTTPRequest2Callback_Release(This->callback); - return S_OK; - } - - IXMLHTTPRequest2Callback_OnDataAvailable(This->callback, xhr2_iface, This->response_body); - IXMLHTTPRequest2Callback_OnResponseReceived(This->callback, xhr2_iface, This->response_body); - IXMLHTTPRequest2Callback_Release(This->callback); - } - - return S_OK; -} - -static const struct IDispatchVtbl xml_http_request_2_IDispatchVtbl = { - /* IUnknown methods */ - xml_http_request_2_IDispatch_QueryInterface, - xml_http_request_2_IDispatch_AddRef, - xml_http_request_2_IDispatch_Release, - /* IDispatch methods */ - xml_http_request_2_IDispatch_GetTypeInfoCount, - xml_http_request_2_IDispatch_GetTypeInfo, - xml_http_request_2_IDispatch_GetIDsOfNames, - xml_http_request_2_IDispatch_Invoke, -}; - static void init_httprequest(httprequest *req) { req->IXMLHTTPRequest_iface.lpVtbl = &XMLHTTPRequestVtbl; @@ -2571,35 +2107,6 @@ HRESULT XMLHTTPRequest_create(void **obj) return S_OK; } -HRESULT XMLHTTPRequest2_create(void **obj) -{ - struct xml_http_request_2 *xhr2; - TRACE("(%p)\n", obj); - - if (!(xhr2 = heap_alloc(sizeof(*xhr2)))) return E_OUTOFMEMORY; - - init_httprequest(&xhr2->req); - xhr2->IXMLHTTPRequest3_iface.lpVtbl = &XMLHTTPRequest3Vtbl; - xhr2->IRtwqAsyncCallback_iface.lpVtbl = &xml_http_request_2_IRtwqAsyncCallbackVtbl; - xhr2->IDispatch_iface.lpVtbl = &xml_http_request_2_IDispatchVtbl; - - /* do not call httprequest_put_onreadystatechange to avoid ref cycle */ - xhr2->req.sink = &xhr2->IDispatch_iface; - - xhr2->callback = NULL; - xhr2->callback3 = NULL; - xhr2->request_body = NULL; - xhr2->response_body = NULL; - - /* for async http requests we need window message queue */ - RtwqStartup(); - if (!xhr2_work_queue) RtwqAllocateWorkQueue(RTWQ_MULTITHREADED_WORKQUEUE, &xhr2_work_queue); - - *obj = &xhr2->IXMLHTTPRequest3_iface; - TRACE("returning iface %p\n", *obj); - return S_OK; -} - HRESULT ServerXMLHTTP_create(void **obj) { serverhttp *req; @@ -2619,4 +2126,3 @@ HRESULT ServerXMLHTTP_create(void **obj) return S_OK; } - diff --git a/dlls/msxml3/msxml_private.h b/dlls/msxml3/msxml_private.h index f3c9edd2c70..cd8ae547c73 100644 --- a/dlls/msxml3/msxml_private.h +++ b/dlls/msxml3/msxml_private.h @@ -344,7 +344,6 @@ extern HRESULT XMLDocument_create(void**) DECLSPEC_HIDDEN; extern HRESULT SAXXMLReader_create(MSXML_VERSION, void**) DECLSPEC_HIDDEN; extern HRESULT SAXAttributes_create(MSXML_VERSION, void**) DECLSPEC_HIDDEN; extern HRESULT XMLHTTPRequest_create(void **) DECLSPEC_HIDDEN; -extern HRESULT XMLHTTPRequest2_create(void **) DECLSPEC_HIDDEN; extern HRESULT ServerXMLHTTP_create(void **) DECLSPEC_HIDDEN; extern HRESULT XSLTemplate_create(void**) DECLSPEC_HIDDEN; extern HRESULT MXWriter_create(MSXML_VERSION, void**) DECLSPEC_HIDDEN; diff --git a/dlls/msxml3/tests/httpreq.c b/dlls/msxml3/tests/httpreq.c index bac1c1bc40d..bccfbaf582a 100644 --- a/dlls/msxml3/tests/httpreq.c +++ b/dlls/msxml3/tests/httpreq.c @@ -26,9 +26,9 @@ #include #include "windows.h" + #include "msxml2.h" -#include "msxml6.h" -#include "msxml6did.h" +#include "msxml2did.h" #include "dispex.h" #include "initguid.h" @@ -1344,17 +1344,6 @@ static IXMLHttpRequest *create_xhr(void) return SUCCEEDED(hr) ? ret : NULL; } -static IXMLHTTPRequest2 *create_xhr2(void) -{ - IXMLHTTPRequest2 *ret; - HRESULT hr; - - hr = CoCreateInstance(&CLSID_FreeThreadedXMLHTTP60, NULL, CLSCTX_INPROC_SERVER, - &IID_IXMLHTTPRequest2, (void**)&ret); - - return SUCCEEDED(hr) ? ret : NULL; -} - static IServerXMLHTTPRequest *create_server_xhr(void) { IServerXMLHTTPRequest *ret; @@ -1915,388 +1904,11 @@ static void test_supporterrorinfo(void) IServerXMLHTTPRequest_Release(server_xhr); } -struct xhr3_callback -{ - IXMLHTTPRequest3Callback IXMLHTTPRequest3Callback_iface; - LONG ref; - HANDLE event; -}; - -static inline struct xhr3_callback *xhr3_callback_from_IXMLHTTPRequest3Callback(IXMLHTTPRequest3Callback *iface) -{ - return CONTAINING_RECORD(iface, struct xhr3_callback, IXMLHTTPRequest3Callback_iface); -} - -static HRESULT WINAPI xhr3_callback_QueryInterface(IXMLHTTPRequest3Callback *iface, REFIID riid, void **obj) -{ - struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); - trace("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); - - if (IsEqualGUID(riid, &IID_IXMLHTTPRequest3Callback) || IsEqualGUID(riid, &IID_IXMLHTTPRequest2Callback) || IsEqualGUID(riid, &IID_IUnknown)) - { - IXMLHTTPRequest3Callback_AddRef(iface); - *obj = iface; - return S_OK; - } - - ok(0, "unexpected interface %s\n", debugstr_guid(riid)); - return E_NOINTERFACE; -} - -static ULONG WINAPI xhr3_callback_AddRef(IXMLHTTPRequest3Callback *iface) -{ - struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); - ULONG ref = InterlockedIncrement(&This->ref); - trace("(%p)->(%u)\n", This, ref); - return ref; -} - -static ULONG WINAPI xhr3_callback_Release(IXMLHTTPRequest3Callback *iface) -{ - struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); - ULONG ref = InterlockedDecrement(&This->ref); - trace("(%p)->(%u)\n", This, ref); - if (ref == 0) HeapFree(GetProcessHeap(), 0, This); - return ref; -} - -static HRESULT WINAPI xhr3_callback_OnRedirect(IXMLHTTPRequest3Callback *iface, - IXMLHTTPRequest2 *request, const WCHAR* redirect_url) -{ - struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); - trace("(%p)->(%p %s)\n", This, request, debugstr_w(redirect_url)); - return S_OK; -} - -static HRESULT WINAPI xhr3_callback_OnHeadersAvailable(IXMLHTTPRequest3Callback *iface, - IXMLHTTPRequest2 *request, DWORD status, const WCHAR *status_str) -{ - struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); - WCHAR *header = NULL; - HRESULT hr; - - trace("(%p)->(%p %d %s)\n", This, request, status, debugstr_w(status_str)); - - header = (void *)0xdeadbeef; - hr = IXMLHTTPRequest2_GetResponseHeader(request, L"Content-Length", &header); - trace("Content-Length: %p (%s), hr %#x\n", header, debugstr_w(header), hr); - - return S_OK; -} - -static HRESULT WINAPI xhr3_callback_OnDataAvailable(IXMLHTTPRequest3Callback *iface, - IXMLHTTPRequest2 *request, ISequentialStream *response) -{ - struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); - trace("(%p)->(%p %p)\n", This, request, response); - return S_OK; -} - -static HRESULT WINAPI xhr3_callback_OnResponseReceived(IXMLHTTPRequest3Callback *iface, - IXMLHTTPRequest2 *request, ISequentialStream *response) -{ - struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); - WCHAR *header = NULL; - char *buffer = HeapAlloc( GetProcessHeap(), 0, 256 ); - ULONG read_size = 0; - HRESULT hr; - - memset(buffer, '?', 256); - buffer[255] = 0; - - trace("(%p)->(%p %p)\n", This, request, response); - - header = (void *)0xdeadbeef; - hr = IXMLHTTPRequest2_GetResponseHeader(request, L"Cache-Control", &header); - trace("Cache-Control: %p (%s), hr %#x\n", header, debugstr_w(header), hr); - - header = (void *)0xdeadbeef; - hr = IXMLHTTPRequest2_GetResponseHeader(request, L"Expires", &header); - trace("Expires: %p (%s), hr %#x\n", header, debugstr_w(header), hr); - - header = (void *)0xdeadbeef; - hr = IXMLHTTPRequest2_GetResponseHeader(request, L"Content-Type", &header); - trace("Content-Type: %p (%s), hr %#x\n", header, debugstr_w(header), hr); - - read_size = 0xdeadbeef; - hr = ISequentialStream_Read(response, buffer, 214, &read_size); - trace("Response: (%d) %s, hr %#x\n", read_size, debugstr_a(buffer), hr); - - read_size = 0xdeadbeef; - hr = ISequentialStream_Read(response, buffer, 1, &read_size); - trace("Response: (%d) %s, hr %#x\n", read_size, debugstr_a(buffer), hr); - - HeapFree( GetProcessHeap(), 0, buffer ); - SetEvent(This->event); - - return S_OK; -} - -static HRESULT WINAPI xhr3_callback_OnError(IXMLHTTPRequest3Callback *iface, - IXMLHTTPRequest2 *request, HRESULT error) -{ - struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); - trace("(%p)->(%p %#x)\n", This, request, error); - SetEvent(This->event); - return S_OK; -} - -static HRESULT WINAPI xhr3_callback_OnServerCertificateReceived(IXMLHTTPRequest3Callback *iface, - IXMLHTTPRequest3 *request, DWORD errors, DWORD chain_size, const XHR_CERT *chain) -{ - struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); - trace("(%p)->(%p %u %u %p)\n", This, request, errors, chain_size, chain); - return S_OK; -} - -static HRESULT WINAPI xhr3_callback_OnClientCertificateRequested(IXMLHTTPRequest3Callback *iface, - IXMLHTTPRequest3 *request, DWORD issuers_size, const WCHAR **issuers) -{ - struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); - trace("(%p)->(%p %u %p)\n", This, request, issuers_size, issuers); - return S_OK; -} - -static const IXMLHTTPRequest3CallbackVtbl xhr3_callback_vtbl = -{ - xhr3_callback_QueryInterface, - xhr3_callback_AddRef, - xhr3_callback_Release, - /* IXMLHTTPRequest2Callback methods */ - xhr3_callback_OnRedirect, - xhr3_callback_OnHeadersAvailable, - xhr3_callback_OnDataAvailable, - xhr3_callback_OnResponseReceived, - xhr3_callback_OnError, - /* IXMLHTTPRequest3Callback methods */ - xhr3_callback_OnServerCertificateReceived, - xhr3_callback_OnClientCertificateRequested, -}; - -static IXMLHTTPRequest2Callback* xhr3_callback_create(HANDLE event) -{ - struct xhr3_callback *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); - ok(This != NULL, "failed to allocate object\n"); - if (!This) return NULL; - - This->IXMLHTTPRequest3Callback_iface.lpVtbl = &xhr3_callback_vtbl; - This->ref = 1; - This->event = event; - - return (IXMLHTTPRequest2Callback*)&This->IXMLHTTPRequest3Callback_iface; -} - -struct xhr2_stream -{ - IStream IStream_iface; - LONG ref; - IStream *stream; -}; - -static inline struct xhr2_stream *xhr2_stream_from_IStream(IStream *iface) -{ - return CONTAINING_RECORD(iface, struct xhr2_stream, IStream_iface); -} - -static HRESULT WINAPI xhr2_stream_QueryInterface(IStream *iface, REFIID riid, void **obj) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); - - if (IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_ISequentialStream) || IsEqualGUID(riid, &IID_IUnknown)) - { - IStream_AddRef(iface); - *obj = iface; - return S_OK; - } - - ok(0, "unexpected interface %s\n", debugstr_guid(riid)); - return E_NOINTERFACE; -} - -static ULONG WINAPI xhr2_stream_AddRef(IStream *iface) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - ULONG ref = InterlockedIncrement(&This->ref); - trace("(%p)->(%u)\n", This, ref); - return ref; -} - -static ULONG WINAPI xhr2_stream_Release(IStream *iface) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - ULONG ref = InterlockedDecrement(&This->ref); - trace("(%p)->(%u)\n", This, ref); - if (ref == 0) - { - IStream_Release(This->stream); - HeapFree(GetProcessHeap(), 0, This); - } - return ref; -} - -static HRESULT WINAPI xhr2_stream_Read(IStream *iface, void *pv, ULONG cb, - ULONG *pcbRead) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead); - return IStream_Read(This->stream, pv, cb, pcbRead); -} - -static HRESULT WINAPI xhr2_stream_Write(IStream *iface, const void *pv, - ULONG cb, ULONG *pcbWritten) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->(%p %u %p)\n", This, pv, cb, pcbWritten); - return IStream_Write(This->stream, pv, cb, pcbWritten); -} - -static HRESULT WINAPI xhr2_stream_Seek(IStream *iface, LARGE_INTEGER dlibMove, - DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->(%lu, %u %p)\n", This, dlibMove.QuadPart, dwOrigin, plibNewPosition); - return IStream_Seek(This->stream, dlibMove, dwOrigin, plibNewPosition); -} - -static HRESULT WINAPI xhr2_stream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->(%lu)\n", This, libNewSize.QuadPart); - return IStream_SetSize(This->stream, libNewSize); -} - -static HRESULT WINAPI xhr2_stream_CopyTo(IStream *iface, IStream *pstm, - ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->(%p %lu %p %p)\n", This, pstm, cb.QuadPart, pcbRead, pcbWritten); - return IStream_CopyTo(This->stream, pstm, cb, pcbRead, pcbWritten); -} - -static HRESULT WINAPI xhr2_stream_Commit(IStream *iface, DWORD grfCommitFlags) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->(%#x)\n", This, grfCommitFlags); - return IStream_Commit(This->stream, grfCommitFlags); -} - -static HRESULT WINAPI xhr2_stream_Revert(IStream *iface) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->()\n", This); - return IStream_Revert(This->stream); -} - -static HRESULT WINAPI xhr2_stream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset, - ULARGE_INTEGER cb, DWORD dwLockType) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->(%lu %lu %u)\n", This, libOffset.QuadPart, cb.QuadPart, dwLockType); - return IStream_LockRegion(This->stream, libOffset, cb, dwLockType); -} - -static HRESULT WINAPI xhr2_stream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, - ULARGE_INTEGER cb, DWORD dwLockType) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->(%lu %lu %u)\n", This, libOffset.QuadPart, cb.QuadPart, dwLockType); - return IStream_UnlockRegion(This->stream, libOffset, cb, dwLockType); -} - -static HRESULT WINAPI xhr2_stream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->(%p %#x)\n", This, pstatstg, grfStatFlag); - return IStream_Stat(This->stream, pstatstg, grfStatFlag); -} - -static HRESULT WINAPI xhr2_stream_Clone(IStream *iface, IStream **ppstm) -{ - struct xhr2_stream *This = xhr2_stream_from_IStream(iface); - trace("(%p)->(%p)\n", This, ppstm); - return IStream_Clone(This->stream, ppstm); -} - -static const IStreamVtbl xhr2_stream_vtbl = -{ - xhr2_stream_QueryInterface, - xhr2_stream_AddRef, - xhr2_stream_Release, - /* IStream methods */ - xhr2_stream_Read, - xhr2_stream_Write, - xhr2_stream_Seek, - xhr2_stream_SetSize, - xhr2_stream_CopyTo, - xhr2_stream_Commit, - xhr2_stream_Revert, - xhr2_stream_LockRegion, - xhr2_stream_UnlockRegion, - xhr2_stream_Stat, - xhr2_stream_Clone -}; - -static ISequentialStream *xhr2_stream_create(void) -{ - struct xhr2_stream *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); - ok(This != NULL, "failed to allocate object\n"); - if (!This) return NULL; - - This->IStream_iface.lpVtbl = &xhr2_stream_vtbl; - This->ref = 1; - CreateStreamOnHGlobal(NULL, TRUE, &This->stream); - - return (ISequentialStream*)&This->IStream_iface; -} - -static void test_IXMLHTTPRequest2(void) -{ - IXMLHTTPRequest2 *xhr2[16]; - IXMLHTTPRequest2Callback *xhr3_callback; - ISequentialStream *stream; - HANDLE events[16]; - HRESULT hr; - int i = 0; - - if (!(xhr2[i] = create_xhr2())) - { - win_skip("IXMLHTTPRequest2 is not available\n"); - return; - } - - events[i] = CreateEventW(NULL, FALSE, FALSE, NULL); - if (!(xhr3_callback = xhr3_callback_create(events[i]))) - return; - - trace("IXMLHTTPRequest2_Open (%p)->(L\"GET\", L\"http://test.winehq.org/\", xhr3_callback, NULL, NULL, NULL, NULL)\n", GetCurrentThreadId(), xhr2[i]); - hr = IXMLHTTPRequest2_Open(xhr2[i], L"GET", L"http://test.winehq.org/", xhr3_callback, NULL, NULL, NULL, NULL); - ok(SUCCEEDED(hr), "IXMLHTTPRequest2_Send failed %#x\n", hr); - - if ((stream = xhr2_stream_create())) - { - trace("IXMLHTTPRequest2_Send (%p)->(%p 0)\n", GetCurrentThreadId(), xhr2[i], stream); - hr = IXMLHTTPRequest2_Send(xhr2[i], stream, 0); - ok(SUCCEEDED(hr), "IXMLHTTPRequest2_Send failed %#x\n", hr); - - ISequentialStream_Release(stream); - } - - IXMLHTTPRequest2Callback_Release(xhr3_callback); - i++; - - while (i--) - { - WaitForSingleObject(events[i], INFINITE); - IXMLHTTPRequest2_Release(xhr2[i]); - } -} - START_TEST(httpreq) { IXMLHttpRequest *xhr; - CoInitializeEx(NULL, COINIT_MULTITHREADED); + CoInitialize(NULL); if (!(xhr = create_xhr())) { @@ -2311,7 +1923,6 @@ START_TEST(httpreq) test_server_xhr(); test_safe_httpreq(); test_supporterrorinfo(); - test_IXMLHTTPRequest2(); CoUninitialize(); } diff --git a/dlls/msxml3/tests/schema.c b/dlls/msxml3/tests/schema.c index 2b63182e2e4..99c7d35cc10 100644 --- a/dlls/msxml3/tests/schema.c +++ b/dlls/msxml3/tests/schema.c @@ -32,16 +32,10 @@ #include "dispex.h" #include "cguid.h" -DEFINE_GUID(CLSID_FreeThreadedDOMDocument60, 0x88d96a06, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_FreeThreadedXMLHTTP60, 0x88d96a09, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_MXXMLWriter60, 0x88d96a0f, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_SAXAttributes60, 0x88d96a0e, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_SAXXMLReader60, 0x88d96a0c, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_XMLSchemaCache60, 0x88d96a07, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(IID_IXMLHTTPRequest2, 0xe5d37dc0, 0x552a, 0x4d52, 0x9c,0xc0, 0xa1,0x4d,0x54,0x6f,0xbd,0x04); -DEFINE_GUID(IID_IXMLHTTPRequest3, 0xa1c9feee, 0x0617, 0x4f23, 0x9d,0x58, 0x89,0x61,0xea,0x43,0x56,0x7c); -DEFINE_GUID(IID_IXMLHTTPRequest2Callback, 0xa44a9299, 0xe321, 0x40de, 0x88,0x66, 0x34,0x1b,0x41,0x66,0x91,0x62); -DEFINE_GUID(IID_IXMLHTTPRequest3Callback, 0xb9e57830, 0x8c6c, 0x4a6f, 0x9c,0x13, 0x47,0x77,0x2b,0xb0,0x47,0xbb); #include "wine/test.h" diff --git a/dlls/msxml3/uuid.c b/dlls/msxml3/uuid.c index 1b4f0452c5f..333d4f3d3c7 100644 --- a/dlls/msxml3/uuid.c +++ b/dlls/msxml3/uuid.c @@ -43,7 +43,6 @@ /* Cannot include msxml6 here since we will get a duplicate LIBID_MSXML2 error. */ DEFINE_GUID(CLSID_FreeThreadedDOMDocument60, 0x88d96a06, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_FreeThreadedXMLHTTP60, 0x88d96a09, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_MXNamespaceManager60, 0x88d96a11, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_MXXMLWriter60, 0x88d96a0f, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_SAXAttributes60, 0x88d96a0e, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); @@ -52,10 +51,6 @@ DEFINE_GUID(CLSID_ServerXMLHTTP60, 0x88d96a0b, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0 DEFINE_GUID(CLSID_XMLHTTP60, 0x88d96a0a, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_XMLSchemaCache60, 0x88d96a07, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_XSLTemplate60, 0x88d96a08, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(IID_IXMLHTTPRequest2, 0xe5d37dc0, 0x552a, 0x4d52, 0x9c,0xc0, 0xa1,0x4d,0x54,0x6f,0xbd,0x04); -DEFINE_GUID(IID_IXMLHTTPRequest3, 0xa1c9feee, 0x0617, 0x4f23, 0x9d,0x58, 0x89,0x61,0xea,0x43,0x56,0x7c); -DEFINE_GUID(IID_IXMLHTTPRequest2Callback, 0xa44a9299, 0xe321, 0x40de, 0x88,0x66, 0x34,0x1b,0x41,0x66,0x91,0x62); -DEFINE_GUID(IID_IXMLHTTPRequest3Callback, 0xb9e57830, 0x8c6c, 0x4a6f, 0x9c,0x13, 0x47,0x77,0x2b,0xb0,0x47,0xbb); /* * Note that because of a #define in msxml2.h, we end up initializing From e3c51122fb3a11201794d53c674afba003be206c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 5 Oct 2023 08:37:32 -0600 Subject: [PATCH 0700/1148] Revert "include: Remove interfaces already define in msxml6.idl" This reverts commit a3c9cb385e8de27d248587fe31aa529dde454514. CW-Bug-Id: #22822 --- dlls/msxml3/factory.c | 1 - dlls/msxml3/tests/saxreader.c | 1 - dlls/msxml3/tests/schema.c | 5 -- dlls/msxml3/uuid.c | 11 ---- include/msxml2.idl | 109 ++++++++++++++++++++++++++++++++++ include/msxml6.idl | 24 ++++---- 6 files changed, 121 insertions(+), 30 deletions(-) diff --git a/dlls/msxml3/factory.c b/dlls/msxml3/factory.c index 243ee379712..c2d3cd30c60 100644 --- a/dlls/msxml3/factory.c +++ b/dlls/msxml3/factory.c @@ -31,7 +31,6 @@ #include "ole2.h" #include "msxml.h" #include "msxml2.h" -#include "msxml6.h" #include "xmlparser.h" /* undef the #define in msxml2 so that we can access the v.2 version diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c index 48cfa8f5593..e123d4eba5a 100644 --- a/dlls/msxml3/tests/saxreader.c +++ b/dlls/msxml3/tests/saxreader.c @@ -29,7 +29,6 @@ #include "windows.h" #include "ole2.h" #include "msxml2.h" -#include "msxml6.h" #include "msxml2did.h" #include "ocidl.h" #include "dispex.h" diff --git a/dlls/msxml3/tests/schema.c b/dlls/msxml3/tests/schema.c index 99c7d35cc10..3d209c0e4a0 100644 --- a/dlls/msxml3/tests/schema.c +++ b/dlls/msxml3/tests/schema.c @@ -32,11 +32,6 @@ #include "dispex.h" #include "cguid.h" -DEFINE_GUID(CLSID_MXXMLWriter60, 0x88d96a0f, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_SAXAttributes60, 0x88d96a0e, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_SAXXMLReader60, 0x88d96a0c, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_XMLSchemaCache60, 0x88d96a07, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); - #include "wine/test.h" #define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) diff --git a/dlls/msxml3/uuid.c b/dlls/msxml3/uuid.c index 333d4f3d3c7..4abbe5e4763 100644 --- a/dlls/msxml3/uuid.c +++ b/dlls/msxml3/uuid.c @@ -41,17 +41,6 @@ #include "initguid.h" #include "msxml2.h" -/* Cannot include msxml6 here since we will get a duplicate LIBID_MSXML2 error. */ -DEFINE_GUID(CLSID_FreeThreadedDOMDocument60, 0x88d96a06, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_MXNamespaceManager60, 0x88d96a11, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_MXXMLWriter60, 0x88d96a0f, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_SAXAttributes60, 0x88d96a0e, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_SAXXMLReader60, 0x88d96a0c, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_ServerXMLHTTP60, 0x88d96a0b, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_XMLHTTP60, 0x88d96a0a, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_XMLSchemaCache60, 0x88d96a07, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); -DEFINE_GUID(CLSID_XSLTemplate60, 0x88d96a08, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); - /* * Note that because of a #define in msxml2.h, we end up initializing * CLSID_DOMDocument2 to be the v.3 version independent DOMDocument diff --git a/include/msxml2.idl b/include/msxml2.idl index 1d1ba7a5248..916e0e8ab3d 100644 --- a/include/msxml2.idl +++ b/include/msxml2.idl @@ -1612,6 +1612,15 @@ coclass FreeThreadedDOMDocument40 [default, source] dispinterface XMLDOMDocumentEvents; } +[ + uuid(88d96a06-f192-11d4-a65f-0040963251e5), +] +coclass FreeThreadedDOMDocument60 +{ + [default] interface IXMLDOMDocument3; + [default, source] dispinterface XMLDOMDocumentEvents; +} + [ helpstring("Free threaded XML DOM Document"), progid("Msxml2.FreeThreadedDOMDocument"), @@ -1653,6 +1662,14 @@ coclass XMLHTTP40 [default] interface IXMLHTTPRequest; } +[ + uuid(88d96a0a-f192-11d4-a65f-0040963251e5) +] +coclass XMLHTTP60 +{ + [default] interface IXMLHTTPRequest; +} + [ helpstring("XML HTTP"), progid("Msxml2.XMLHTTP"), @@ -1685,6 +1702,14 @@ coclass ServerXMLHTTP40 [default] interface IServerXMLHTTPRequest2; } +[ + uuid(88d96a0b-f192-11d4-a65f-0040963251e5) +] +coclass ServerXMLHTTP60 +{ + [default] interface IServerXMLHTTPRequest2; +} + [ helpstring("Server XML HTTP"), progid("Msxml2.ServerXMLHTTP"), @@ -1725,6 +1750,14 @@ coclass XMLSchemaCache40 [default] interface IXMLDOMSchemaCollection2; } +[ + uuid(88d96a07-f192-11d4-a65f-0040963251e5) +] +coclass XMLSchemaCache60 +{ + [default] interface IXMLDOMSchemaCollection2; +} + [ helpstring("XML Schema Cache"), progid("Msxml2.XMLSchemaCache"), @@ -1765,6 +1798,14 @@ coclass XSLTemplate40 [default] interface IXSLTemplate; } +[ + uuid(88d96a08-f192-11d4-a65f-0040963251e5) +] +coclass XSLTemplate60 +{ + [default] interface IXSLTemplate; +} + [ helpstring("XSL Template"), progid("Msxml2.XSLTemplate"), @@ -3256,6 +3297,15 @@ coclass SAXXMLReader40 interface ISAXXMLReader; } +[ + uuid(88d96a0c-f192-11d4-a65f-0040963251e5) +] +coclass SAXXMLReader60 +{ + [default] interface IVBSAXXMLReader; + interface ISAXXMLReader; +} + [ helpstring("SAX XML Reader"), progid("Msxml2.SAXXMLReader"), @@ -3330,6 +3380,26 @@ coclass MXHTMLWriter40 interface IVBSAXLexicalHandler; } +[ + uuid(88d96a10-f192-11d4-a65f-0040963251e5) +] +coclass MXHTMLWriter60 +{ + [default] interface IMXWriter; + + interface ISAXContentHandler; + interface ISAXDeclHandler; + interface ISAXDTDHandler; + interface ISAXErrorHandler; + interface ISAXLexicalHandler; + + interface IVBSAXContentHandler; + interface IVBSAXDeclHandler; + interface IVBSAXDTDHandler; + interface IVBSAXErrorHandler; + interface IVBSAXLexicalHandler; +} + [ helpstring("MXXMLWriter 3.0"), progid("Msxml2.MXXMLWriter.3.0"), @@ -3374,6 +3444,26 @@ coclass MXXMLWriter40 interface IVBSAXLexicalHandler; } +[ + uuid(88d96a0f-f192-11d4-a65f-0040963251e5) +] +coclass MXXMLWriter60 +{ + [default] interface IMXWriter; + + interface ISAXContentHandler; + interface ISAXDeclHandler; + interface ISAXDTDHandler; + interface ISAXErrorHandler; + interface ISAXLexicalHandler; + + interface IVBSAXContentHandler; + interface IVBSAXDeclHandler; + interface IVBSAXDTDHandler; + interface IVBSAXErrorHandler; + interface IVBSAXLexicalHandler; +} + [ helpstring("MXXMLWriter"), progid("Msxml2.MXXMLWriter"), @@ -3416,6 +3506,15 @@ coclass MXNamespaceManager40 interface IMXNamespaceManager; } +[ + uuid(88d96a11-f192-11d4-a65f-0040963251e5) +] +coclass MXNamespaceManager60 +{ + [default] interface IVBMXNamespaceManager; + interface IMXNamespaceManager; +} + [ helpstring("SAXAttributes 3.0"), progid("Msxml2.SAXAttributes.3.0"), @@ -3440,6 +3539,16 @@ coclass SAXAttributes40 interface ISAXAttributes; } +[ + uuid(88d96a0e-f192-11d4-a65f-0040963251e5) +] +coclass SAXAttributes60 +{ + [default] interface IMXAttributes; + interface IVBSAXAttributes; + interface ISAXAttributes; +} + [ helpstring("SAXAttributes"), progid("Msxml2.SAXAttributes"), diff --git a/include/msxml6.idl b/include/msxml6.idl index 4672ae80626..458c43e389d 100644 --- a/include/msxml6.idl +++ b/include/msxml6.idl @@ -3051,6 +3051,18 @@ coclass DOMDocument60 [default, source] dispinterface XMLDOMDocumentEvents; } +[ + helpstring("Free threaded XML DOM Document 6.0"), + progid("Msxml2.FreeThreadedDOMDocument.6.0"), + threading(both), + uuid(88d96a06-f192-11d4-a65f-0040963251e5), +] +coclass FreeThreadedDOMDocument60 +{ + [default] interface IXMLDOMDocument3; + [default, source] dispinterface XMLDOMDocumentEvents; +} + [ helpstring("SAX XML Reader 6.0"), progid("Msxml2.SAXXMLReader.6.0"), @@ -3156,18 +3168,6 @@ coclass XSLTemplate60 [default] interface IXSLTemplate; } -[ - helpstring("Free threaded XML DOM Document 6.0"), - progid("Msxml2.FreeThreadedDOMDocument.6.0"), - threading(both), - uuid(88d96a06-f192-11d4-a65f-0040963251e5), -] -coclass FreeThreadedDOMDocument60 -{ - [default] interface IXMLDOMDocument3; - [default, source] dispinterface XMLDOMDocumentEvents; -} - [ helpstring("XML HTTP 6.0"), progid("Msxml2.XMLHTTP.6.0"), From fbd4d019a3b1f98f63d62300cbd9c7f0efe01dfe Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Tue, 10 Jan 2023 16:22:14 +1100 Subject: [PATCH 0701/1148] include: Add _XHR enum values. (cherry picked from commit d05ce2e72da6e576b509076224aed866ff7275cb) CW-Bug-Id: #22822 --- include/msxml6.idl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/msxml6.idl b/include/msxml6.idl index 458c43e389d..d4a5c490243 100644 --- a/include/msxml6.idl +++ b/include/msxml6.idl @@ -256,6 +256,20 @@ typedef enum _SCHEMATYPEVARIETY } SCHEMATYPEVARIETY; cpp_quote("#endif /* __msxml_som_enums__ */") +typedef [v1_enum] enum _XHR_CRED_PROMPT +{ + XHR_CRED_PROMPT_ALL, + XHR_CRED_PROMPT_NONE, + XHR_CRED_PROMPT_PROXY +} XHR_CRED_PROMPT; + +typedef [v1_enum] enum _XHR_AUTH +{ + XHR_AUTH_ALL, + XHR_AUTH_NONE, + XHR_AUTH_PROXY +} XHR_AUTH; + typedef [v1_enum] enum _XHR_PROPERTY { XHR_PROP_NO_CRED_PROMPT, From e13978fa3e8e23b7ab3bcfbca608808c0bab4426 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Fri, 11 Sep 2020 17:55:59 +1000 Subject: [PATCH 0702/1148] include: Remove interfaces already define in msxml6.idl Signed-off-by: Alistair Leslie-Hughes CW-Bug-Id: #22822 --- dlls/msxml3/factory.c | 1 + dlls/msxml3/tests/saxreader.c | 1 + dlls/msxml3/tests/schema.c | 5 ++ dlls/msxml3/uuid.c | 11 ++++ include/msxml2.idl | 109 ---------------------------------- include/msxml6.idl | 24 ++++---- 6 files changed, 30 insertions(+), 121 deletions(-) diff --git a/dlls/msxml3/factory.c b/dlls/msxml3/factory.c index c2d3cd30c60..243ee379712 100644 --- a/dlls/msxml3/factory.c +++ b/dlls/msxml3/factory.c @@ -31,6 +31,7 @@ #include "ole2.h" #include "msxml.h" #include "msxml2.h" +#include "msxml6.h" #include "xmlparser.h" /* undef the #define in msxml2 so that we can access the v.2 version diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c index e123d4eba5a..48cfa8f5593 100644 --- a/dlls/msxml3/tests/saxreader.c +++ b/dlls/msxml3/tests/saxreader.c @@ -29,6 +29,7 @@ #include "windows.h" #include "ole2.h" #include "msxml2.h" +#include "msxml6.h" #include "msxml2did.h" #include "ocidl.h" #include "dispex.h" diff --git a/dlls/msxml3/tests/schema.c b/dlls/msxml3/tests/schema.c index 3d209c0e4a0..99c7d35cc10 100644 --- a/dlls/msxml3/tests/schema.c +++ b/dlls/msxml3/tests/schema.c @@ -32,6 +32,11 @@ #include "dispex.h" #include "cguid.h" +DEFINE_GUID(CLSID_MXXMLWriter60, 0x88d96a0f, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_SAXAttributes60, 0x88d96a0e, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_SAXXMLReader60, 0x88d96a0c, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_XMLSchemaCache60, 0x88d96a07, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); + #include "wine/test.h" #define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) diff --git a/dlls/msxml3/uuid.c b/dlls/msxml3/uuid.c index 4abbe5e4763..333d4f3d3c7 100644 --- a/dlls/msxml3/uuid.c +++ b/dlls/msxml3/uuid.c @@ -41,6 +41,17 @@ #include "initguid.h" #include "msxml2.h" +/* Cannot include msxml6 here since we will get a duplicate LIBID_MSXML2 error. */ +DEFINE_GUID(CLSID_FreeThreadedDOMDocument60, 0x88d96a06, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_MXNamespaceManager60, 0x88d96a11, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_MXXMLWriter60, 0x88d96a0f, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_SAXAttributes60, 0x88d96a0e, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_SAXXMLReader60, 0x88d96a0c, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_ServerXMLHTTP60, 0x88d96a0b, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_XMLHTTP60, 0x88d96a0a, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_XMLSchemaCache60, 0x88d96a07, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_XSLTemplate60, 0x88d96a08, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); + /* * Note that because of a #define in msxml2.h, we end up initializing * CLSID_DOMDocument2 to be the v.3 version independent DOMDocument diff --git a/include/msxml2.idl b/include/msxml2.idl index 916e0e8ab3d..1d1ba7a5248 100644 --- a/include/msxml2.idl +++ b/include/msxml2.idl @@ -1612,15 +1612,6 @@ coclass FreeThreadedDOMDocument40 [default, source] dispinterface XMLDOMDocumentEvents; } -[ - uuid(88d96a06-f192-11d4-a65f-0040963251e5), -] -coclass FreeThreadedDOMDocument60 -{ - [default] interface IXMLDOMDocument3; - [default, source] dispinterface XMLDOMDocumentEvents; -} - [ helpstring("Free threaded XML DOM Document"), progid("Msxml2.FreeThreadedDOMDocument"), @@ -1662,14 +1653,6 @@ coclass XMLHTTP40 [default] interface IXMLHTTPRequest; } -[ - uuid(88d96a0a-f192-11d4-a65f-0040963251e5) -] -coclass XMLHTTP60 -{ - [default] interface IXMLHTTPRequest; -} - [ helpstring("XML HTTP"), progid("Msxml2.XMLHTTP"), @@ -1702,14 +1685,6 @@ coclass ServerXMLHTTP40 [default] interface IServerXMLHTTPRequest2; } -[ - uuid(88d96a0b-f192-11d4-a65f-0040963251e5) -] -coclass ServerXMLHTTP60 -{ - [default] interface IServerXMLHTTPRequest2; -} - [ helpstring("Server XML HTTP"), progid("Msxml2.ServerXMLHTTP"), @@ -1750,14 +1725,6 @@ coclass XMLSchemaCache40 [default] interface IXMLDOMSchemaCollection2; } -[ - uuid(88d96a07-f192-11d4-a65f-0040963251e5) -] -coclass XMLSchemaCache60 -{ - [default] interface IXMLDOMSchemaCollection2; -} - [ helpstring("XML Schema Cache"), progid("Msxml2.XMLSchemaCache"), @@ -1798,14 +1765,6 @@ coclass XSLTemplate40 [default] interface IXSLTemplate; } -[ - uuid(88d96a08-f192-11d4-a65f-0040963251e5) -] -coclass XSLTemplate60 -{ - [default] interface IXSLTemplate; -} - [ helpstring("XSL Template"), progid("Msxml2.XSLTemplate"), @@ -3297,15 +3256,6 @@ coclass SAXXMLReader40 interface ISAXXMLReader; } -[ - uuid(88d96a0c-f192-11d4-a65f-0040963251e5) -] -coclass SAXXMLReader60 -{ - [default] interface IVBSAXXMLReader; - interface ISAXXMLReader; -} - [ helpstring("SAX XML Reader"), progid("Msxml2.SAXXMLReader"), @@ -3380,26 +3330,6 @@ coclass MXHTMLWriter40 interface IVBSAXLexicalHandler; } -[ - uuid(88d96a10-f192-11d4-a65f-0040963251e5) -] -coclass MXHTMLWriter60 -{ - [default] interface IMXWriter; - - interface ISAXContentHandler; - interface ISAXDeclHandler; - interface ISAXDTDHandler; - interface ISAXErrorHandler; - interface ISAXLexicalHandler; - - interface IVBSAXContentHandler; - interface IVBSAXDeclHandler; - interface IVBSAXDTDHandler; - interface IVBSAXErrorHandler; - interface IVBSAXLexicalHandler; -} - [ helpstring("MXXMLWriter 3.0"), progid("Msxml2.MXXMLWriter.3.0"), @@ -3444,26 +3374,6 @@ coclass MXXMLWriter40 interface IVBSAXLexicalHandler; } -[ - uuid(88d96a0f-f192-11d4-a65f-0040963251e5) -] -coclass MXXMLWriter60 -{ - [default] interface IMXWriter; - - interface ISAXContentHandler; - interface ISAXDeclHandler; - interface ISAXDTDHandler; - interface ISAXErrorHandler; - interface ISAXLexicalHandler; - - interface IVBSAXContentHandler; - interface IVBSAXDeclHandler; - interface IVBSAXDTDHandler; - interface IVBSAXErrorHandler; - interface IVBSAXLexicalHandler; -} - [ helpstring("MXXMLWriter"), progid("Msxml2.MXXMLWriter"), @@ -3506,15 +3416,6 @@ coclass MXNamespaceManager40 interface IMXNamespaceManager; } -[ - uuid(88d96a11-f192-11d4-a65f-0040963251e5) -] -coclass MXNamespaceManager60 -{ - [default] interface IVBMXNamespaceManager; - interface IMXNamespaceManager; -} - [ helpstring("SAXAttributes 3.0"), progid("Msxml2.SAXAttributes.3.0"), @@ -3539,16 +3440,6 @@ coclass SAXAttributes40 interface ISAXAttributes; } -[ - uuid(88d96a0e-f192-11d4-a65f-0040963251e5) -] -coclass SAXAttributes60 -{ - [default] interface IMXAttributes; - interface IVBSAXAttributes; - interface ISAXAttributes; -} - [ helpstring("SAXAttributes"), progid("Msxml2.SAXAttributes"), diff --git a/include/msxml6.idl b/include/msxml6.idl index d4a5c490243..7396826a1f6 100644 --- a/include/msxml6.idl +++ b/include/msxml6.idl @@ -3065,18 +3065,6 @@ coclass DOMDocument60 [default, source] dispinterface XMLDOMDocumentEvents; } -[ - helpstring("Free threaded XML DOM Document 6.0"), - progid("Msxml2.FreeThreadedDOMDocument.6.0"), - threading(both), - uuid(88d96a06-f192-11d4-a65f-0040963251e5), -] -coclass FreeThreadedDOMDocument60 -{ - [default] interface IXMLDOMDocument3; - [default, source] dispinterface XMLDOMDocumentEvents; -} - [ helpstring("SAX XML Reader 6.0"), progid("Msxml2.SAXXMLReader.6.0"), @@ -3182,6 +3170,18 @@ coclass XSLTemplate60 [default] interface IXSLTemplate; } +[ + helpstring("Free threaded XML DOM Document 6.0"), + progid("Msxml2.FreeThreadedDOMDocument.6.0"), + threading(both), + uuid(88d96a06-f192-11d4-a65f-0040963251e5), +] +coclass FreeThreadedDOMDocument60 +{ + [default] interface IXMLDOMDocument3; + [default, source] dispinterface XMLDOMDocumentEvents; +} + [ helpstring("XML HTTP 6.0"), progid("Msxml2.XMLHTTP.6.0"), From fab157c128d108beddf7295514c840c8054a5a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 8 Sep 2020 18:43:52 +0200 Subject: [PATCH 0703/1148] msxml3: Implement FreeThreadedXMLHTTP60. Update from Gijs Vermeulen CW-Bug-Id: #22822 --- dlls/msxml3/Makefile.in | 2 +- dlls/msxml3/factory.c | 5 + dlls/msxml3/httprequest.c | 495 +++++++++++++++++++++++++++++++++++- dlls/msxml3/msxml_private.h | 1 + dlls/msxml3/tests/httpreq.c | 395 +++++++++++++++++++++++++++- dlls/msxml3/tests/schema.c | 6 + dlls/msxml3/uuid.c | 5 + 7 files changed, 904 insertions(+), 5 deletions(-) diff --git a/dlls/msxml3/Makefile.in b/dlls/msxml3/Makefile.in index 2bf789732da..e2d737599b1 100644 --- a/dlls/msxml3/Makefile.in +++ b/dlls/msxml3/Makefile.in @@ -1,5 +1,5 @@ MODULE = msxml3.dll -IMPORTS = $(XSLT_PE_LIBS) $(XML2_PE_LIBS) uuid urlmon shlwapi oleaut32 ole32 user32 advapi32 +IMPORTS = $(XSLT_PE_LIBS) $(XML2_PE_LIBS) uuid urlmon shlwapi oleaut32 ole32 user32 advapi32 rtworkq EXTRAINCL = $(XSLT_PE_CFLAGS) $(XML2_PE_CFLAGS) C_SRCS = \ diff --git a/dlls/msxml3/factory.c b/dlls/msxml3/factory.c index 243ee379712..323c7b49848 100644 --- a/dlls/msxml3/factory.c +++ b/dlls/msxml3/factory.c @@ -279,6 +279,7 @@ static HRESULT DOMClassFactory_Create(const GUID *clsid, REFIID riid, void **ppv static ClassFactory xmldoccf = { { &ClassFactoryVtbl }, XMLDocument_create }; static ClassFactory httpreqcf = { { &ClassFactoryVtbl }, XMLHTTPRequest_create }; +static ClassFactory httpreqcf2 = { { &ClassFactoryVtbl }, XMLHTTPRequest2_create }; static ClassFactory serverhttp = { { &ClassFactoryVtbl }, ServerXMLHTTP_create }; static ClassFactory xsltemplatecf = { { &ClassFactoryVtbl }, XSLTemplate_create }; static ClassFactory mxnsmanagercf = { {&ClassFactoryVtbl }, MXNamespaceManager_create }; @@ -340,6 +341,10 @@ HRESULT WINAPI DllGetClassObject( REFCLSID rclsid, REFIID riid, void **ppv ) { cf = &httpreqcf.IClassFactory_iface; } + else if( IsEqualCLSID( rclsid, &CLSID_FreeThreadedXMLHTTP60 )) + { + cf = &httpreqcf2.IClassFactory_iface; + } else if( IsEqualCLSID( rclsid, &CLSID_ServerXMLHTTP ) || IsEqualCLSID( rclsid, &CLSID_ServerXMLHTTP30 ) || IsEqualCLSID( rclsid, &CLSID_ServerXMLHTTP40 ) || diff --git a/dlls/msxml3/httprequest.c b/dlls/msxml3/httprequest.c index cc384a380e5..98dd23c9fbb 100644 --- a/dlls/msxml3/httprequest.c +++ b/dlls/msxml3/httprequest.c @@ -38,10 +38,12 @@ #include "shlwapi.h" #include "msxml_dispex.h" +#include "initguid.h" +#include "rtworkq.h" #include "wine/debug.h" -WINE_DEFAULT_DEBUG_CHANNEL(msxml); +WINE_DEFAULT_DEBUG_CHANNEL(xmlhttp); static const WCHAR colspaceW[] = {':',' ',0}; static const WCHAR crlfW[] = {'\r','\n',0}; @@ -2058,6 +2060,468 @@ static const struct IServerXMLHTTPRequestVtbl ServerXMLHTTPRequestVtbl = ServerXMLHTTPRequest_setOption }; +static DWORD xhr2_work_queue; + +struct xml_http_request_2 +{ + httprequest req; + IXMLHTTPRequest3 IXMLHTTPRequest3_iface; + IRtwqAsyncCallback IRtwqAsyncCallback_iface; + IDispatch IDispatch_iface; + + IXMLHTTPRequest2Callback *callback; + IXMLHTTPRequest3Callback *callback3; + ISequentialStream *response_body; + ISequentialStream *request_body; + ULONGLONG request_body_size; +}; + +static inline struct xml_http_request_2 *impl_from_IXMLHTTPRequest3(IXMLHTTPRequest3 *iface) +{ + return CONTAINING_RECORD(iface, struct xml_http_request_2, IXMLHTTPRequest3_iface); +} + +static inline struct xml_http_request_2 *xml_http_request_2_from_IRtwqAsyncCallback(IRtwqAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct xml_http_request_2, IRtwqAsyncCallback_iface); +} + +static inline struct xml_http_request_2 *xml_http_request_2_from_IDispatch(IDispatch *iface) +{ + return CONTAINING_RECORD(iface, struct xml_http_request_2, IDispatch_iface); +} + +static HRESULT WINAPI xml_http_request_2_QueryInterface(IXMLHTTPRequest3 *iface, REFIID riid, void **obj) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); + + if (IsEqualGUID(riid, &IID_IXMLHTTPRequest3) || IsEqualGUID(riid, &IID_IXMLHTTPRequest2) + || IsEqualGUID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef((IUnknown*)*obj); + return S_OK; + } + + FIXME("Unsupported interface %s\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI xml_http_request_2_AddRef(IXMLHTTPRequest3 *iface) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + ULONG ref = InterlockedIncrement(&This->req.ref); + TRACE("(%p)->(%lu)\n", This, ref); + return ref; +} + +static ULONG WINAPI xml_http_request_2_Release(IXMLHTTPRequest3 *iface) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + ULONG ref = InterlockedDecrement(&This->req.ref); + + TRACE("(%p)->(%lu)\n", This, ref); + + if (ref == 0) + { + /* do not call httprequest_put_onreadystatechange to avoid ref cycle */ + This->req.sink = NULL; + if (This->response_body) ISequentialStream_Release(This->response_body); + if (This->request_body) ISequentialStream_Release(This->request_body); + if (This->callback3) IXMLHTTPRequest3Callback_Release(This->callback3); + if (This->callback) IXMLHTTPRequest2Callback_Release(This->callback); + heap_free(This); + RtwqShutdown(); + } + + return ref; +} + +static HRESULT WINAPI xml_http_request_2_Open(IXMLHTTPRequest3 *iface, const WCHAR *method, + const WCHAR *url, IXMLHTTPRequest2Callback *callback, + const WCHAR *username, const WCHAR *password, + const WCHAR *proxy_username, const WCHAR *proxy_password) +{ + static const WCHAR accept_encoding[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0}; + static const WCHAR empty = 0; + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + VARIANT async_v, username_v, password_v; + HRESULT hr; + + TRACE("(%p)->(%s %s %p %s %s %s %s)\n", This, debugstr_w(method), debugstr_w(url), callback, + debugstr_w(username), debugstr_w(password), debugstr_w(proxy_username), debugstr_w(proxy_password)); + + if (This->callback) IXMLHTTPRequest2Callback_Release(This->callback); + if (This->callback3) IXMLHTTPRequest3Callback_Release(This->callback3); + IXMLHTTPRequest2Callback_AddRef(callback); + This->callback = callback; + if (FAILED(IXMLHTTPRequest2Callback_QueryInterface(callback, &IID_IXMLHTTPRequest3Callback, (void **)&This->callback3))) + This->callback3 = NULL; + + if (proxy_username || proxy_password) FIXME("proxy credentials not implemented\n"); + + VariantInit(&async_v); + V_VT(&async_v) = VT_BOOL; + V_BOOL(&async_v) = FALSE; /* FIXME: TRUE needs a RTWQ_WINDOW_WORKQUEUE */ + + VariantInit(&username_v); + V_VT(&username_v) = VT_BSTR; + if (username) V_BSTR(&username_v) = SysAllocString(username); + else V_BSTR(&username_v) = SysAllocString(&empty); + + VariantInit(&password_v); + V_VT(&password_v) = VT_BSTR; + if (password) V_BSTR(&password_v) = SysAllocString(password); + else V_BSTR(&password_v) = SysAllocString(&empty); + + if (FAILED(hr = httprequest_open(&This->req, (BSTR)method, (BSTR)url, async_v, username_v, password_v))) + return hr; + return httprequest_setRequestHeader(&This->req, (BSTR)accept_encoding, (BSTR)&empty); +} + +static HRESULT WINAPI xml_http_request_2_Send(IXMLHTTPRequest3 *iface, ISequentialStream *body, ULONGLONG body_size) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + IRtwqAsyncResult *result; + HRESULT hr; + + TRACE("(%p)->(%p %s)\n", This, body, wine_dbgstr_longlong( body_size )); + + if (body_size) + { + ISequentialStream_AddRef(body); + This->request_body = body; + This->request_body_size = body_size; + } + + if (FAILED(hr = RtwqCreateAsyncResult(NULL, &This->IRtwqAsyncCallback_iface, NULL, &result))) + return hr; + // IRtwqAsyncCallback_Invoke(&This->IRtwqAsyncCallback_iface, result); + hr = RtwqPutWorkItem(xhr2_work_queue, 0, result); + if (result) IRtwqAsyncResult_Release(result); + + return hr; +} + +static HRESULT WINAPI xml_http_request_2_Abort(IXMLHTTPRequest3 *iface) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + TRACE("(%p) stub!\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI xml_http_request_2_SetCookie(IXMLHTTPRequest3 *iface, const XHR_COOKIE *cookie, DWORD *state) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + FIXME("(%p)->(%p %p) stub!\n", This, cookie, state); + return E_NOTIMPL; +} + +static HRESULT WINAPI xml_http_request_2_SetCustomResponseStream(IXMLHTTPRequest3 *iface, ISequentialStream *stream) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + FIXME("(%p)->(%p) stub!\n", This, stream); + return E_NOTIMPL; +} + +static HRESULT WINAPI xml_http_request_2_SetProperty(IXMLHTTPRequest3 *iface, XHR_PROPERTY property, ULONGLONG value) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + FIXME("(%p)->(%#x %s) stub!\n", This, property, wine_dbgstr_longlong( value )); + return E_NOTIMPL; +} + +static HRESULT WINAPI xml_http_request_2_SetRequestHeader(IXMLHTTPRequest3 *iface, + const WCHAR *header, const WCHAR *value) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + TRACE("(%p)->(%s %s)\n", This, debugstr_w(header), debugstr_w(value)); + return httprequest_setRequestHeader(&This->req, (BSTR)header, (BSTR)value); +} + +static HRESULT WINAPI xml_http_request_2_GetAllResponseHeaders(IXMLHTTPRequest3 *iface, WCHAR **headers) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + FIXME("(%p)->(%p) stub!\n", This, headers); + return E_NOTIMPL; +} + +static HRESULT WINAPI xml_http_request_2_GetCookie(IXMLHTTPRequest3 *iface, const WCHAR *url, + const WCHAR *name, DWORD flags, + ULONG *cookies_count, XHR_COOKIE **cookies) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + FIXME("(%p)->(%s %s %ld %p %p) stub!\n", This, debugstr_w(url), debugstr_w(name), flags, cookies_count, cookies); + return E_NOTIMPL; +} + +static HRESULT WINAPI xml_http_request_2_GetResponseHeader(IXMLHTTPRequest3 *iface, + const WCHAR *header, WCHAR **value) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + HRESULT hr; + + TRACE("(%p)->(%s %p)\n", This, debugstr_w(header), value); + + if (FAILED(hr = httprequest_getResponseHeader(&This->req, (BSTR)header, value))) + return hr; + +#define E_FILE_NOT_FOUND _HRESULT_TYPEDEF_(0x80070002) + + if (hr == S_FALSE) + { + *value = NULL; + return E_FILE_NOT_FOUND; + } + + return hr; +} + +static HRESULT WINAPI xml_http_request_3_SetClientCertificate(IXMLHTTPRequest3 *iface, DWORD count, const BYTE *hashes, const WCHAR *pin) +{ + struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); + FIXME("(%p)->(%ld %p %s) stub!\n", This, count, hashes, debugstr_w(pin)); + return E_NOTIMPL; +} + +static const struct IXMLHTTPRequest3Vtbl XMLHTTPRequest3Vtbl = { + /* IUnknown methods */ + xml_http_request_2_QueryInterface, + xml_http_request_2_AddRef, + xml_http_request_2_Release, + /* IXMLHTTPRequest2 methods */ + xml_http_request_2_Open, + xml_http_request_2_Send, + xml_http_request_2_Abort, + xml_http_request_2_SetCookie, + xml_http_request_2_SetCustomResponseStream, + xml_http_request_2_SetProperty, + xml_http_request_2_SetRequestHeader, + xml_http_request_2_GetAllResponseHeaders, + xml_http_request_2_GetCookie, + xml_http_request_2_GetResponseHeader, + /* IXMLHTTPRequest3 methods */ + xml_http_request_3_SetClientCertificate, +}; + +static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_QueryInterface(IRtwqAsyncCallback *iface, REFIID riid, void **obj) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); + + if (IsEqualGUID(riid, &IID_IRtwqAsyncCallback) || IsEqualGUID(riid, &IID_IUnknown)) + { + IRtwqAsyncCallback_AddRef(iface); + *obj = iface; + return S_OK; + } + + FIXME("Unsupported interface %s\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI xml_http_request_2_IRtwqAsyncCallback_AddRef(IRtwqAsyncCallback *iface) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); + TRACE("(%p)\n", This); + return xml_http_request_2_AddRef(&This->IXMLHTTPRequest3_iface); +} + +static ULONG WINAPI xml_http_request_2_IRtwqAsyncCallback_Release(IRtwqAsyncCallback *iface) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); + TRACE("(%p)\n", This); + return xml_http_request_2_Release(&This->IXMLHTTPRequest3_iface); +} + +static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_GetParameters(IRtwqAsyncCallback *iface, + DWORD *flags, DWORD *queue) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); + + TRACE("(%p)->(%p %p)\n", This, flags, queue); + + *flags = 0; + *queue = xhr2_work_queue; + return S_OK; +} + +static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_Invoke(IRtwqAsyncCallback *iface, + IRtwqAsyncResult *result) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); + VARIANT body_v; + HRESULT hr; + ULONG read; + + TRACE("(%p)->(%p)\n", This, result); + + VariantInit(&body_v); + + if (This->request_body) + { + V_VT(&body_v) = VT_BSTR; + V_BSTR(&body_v) = CoTaskMemAlloc(This->request_body_size); + + if (FAILED(hr = ISequentialStream_Read(This->request_body, V_BSTR(&body_v), This->request_body_size, &read)) || + read < This->request_body_size) + { + ERR("Failed to allocate request body memory, hr %#lx\n", hr); + CoTaskMemFree(V_BSTR(&body_v)); + goto done; + } + + ISequentialStream_Release(This->request_body); + This->request_body = NULL; + } + + hr = httprequest_send(&This->req, body_v); + +done: + return IRtwqAsyncResult_SetStatus(result, hr); +} + +static const struct IRtwqAsyncCallbackVtbl xml_http_request_2_IRtwqAsyncCallbackVtbl = { + /* IUnknown methods */ + xml_http_request_2_IRtwqAsyncCallback_QueryInterface, + xml_http_request_2_IRtwqAsyncCallback_AddRef, + xml_http_request_2_IRtwqAsyncCallback_Release, + /* IRtwqAsyncCallback methods */ + xml_http_request_2_IRtwqAsyncCallback_GetParameters, + xml_http_request_2_IRtwqAsyncCallback_Invoke, +}; + +static HRESULT WINAPI xml_http_request_2_IDispatch_QueryInterface(IDispatch *iface, REFIID riid, void **obj) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); + + if (IsEqualGUID(riid, &IID_IDispatch) || IsEqualGUID(riid, &IID_IUnknown)) + { + IDispatch_AddRef(iface); + *obj = iface; + return S_OK; + } + + FIXME("Unsupported interface %s\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI xml_http_request_2_IDispatch_AddRef(IDispatch *iface) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); + TRACE("(%p)\n", This); + return xml_http_request_2_AddRef(&This->IXMLHTTPRequest3_iface); +} + +static ULONG WINAPI xml_http_request_2_IDispatch_Release(IDispatch *iface) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); + TRACE("(%p)\n", This); + return xml_http_request_2_Release(&This->IXMLHTTPRequest3_iface); +} + +static HRESULT WINAPI xml_http_request_2_IDispatch_GetTypeInfoCount(IDispatch *iface, UINT *value) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); + FIXME("(%p)->(%p) stub!\n", This, value); + *value = 0; + return S_OK; +} + +static HRESULT WINAPI xml_http_request_2_IDispatch_GetTypeInfo(IDispatch *iface, UINT index, + LCID lcid, ITypeInfo **value) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); + FIXME("(%p)->(%d %lu %p) stub!\n", This, index, lcid, value); + *value = NULL; + return S_OK; +} + +static HRESULT WINAPI xml_http_request_2_IDispatch_GetIDsOfNames(IDispatch *iface, REFIID riid, + OLECHAR **names, UINT names_count, + LCID lcid, DISPID *disp_ids) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); + FIXME("(%p)->(%s %p %d %lu %p) stub!\n", This, debugstr_guid(riid), names, names_count, lcid, disp_ids); + return S_OK; +} + +static HRESULT WINAPI xml_http_request_2_IDispatch_Invoke(IDispatch *iface, DISPID id, REFIID riid, + LCID lcid, WORD flags, DISPPARAMS *params, + VARIANT *result, EXCEPINFO *exception, UINT *arg_err) +{ + struct xml_http_request_2 *This = xml_http_request_2_from_IDispatch(iface); + IXMLHTTPRequest2 *xhr2_iface = (IXMLHTTPRequest2*)&This->IXMLHTTPRequest3_iface; + HRESULT hr; + LONG status; + BSTR status_str = NULL; + + TRACE("(%p)->(%ld %s %lu %d %p %p %p %p) stub!\n", This, id, debugstr_guid(riid), lcid, flags, + params, result, exception, arg_err); + + if (This->req.state == READYSTATE_COMPLETE) + { + VARIANT body_v; + VariantInit(&body_v); + + IXMLHTTPRequest2Callback_AddRef(This->callback); + if (This->callback3) + { + IXMLHTTPRequest3Callback_AddRef(This->callback3); + IXMLHTTPRequest3Callback_OnServerCertificateReceived(This->callback3, (IXMLHTTPRequest3 *)xhr2_iface, 0, 1, NULL); + IXMLHTTPRequest3Callback_Release(This->callback3); + } + + if (FAILED(hr = httprequest_get_status(&This->req, &status)) || + FAILED(hr = httprequest_get_statusText(&This->req, &status_str))) + { + WARN("failed to get response status, error %#lx\n", hr); + IXMLHTTPRequest2Callback_OnError(This->callback, xhr2_iface, hr); + IXMLHTTPRequest2Callback_Release(This->callback); + return S_OK; + } + + IXMLHTTPRequest2Callback_OnHeadersAvailable(This->callback, xhr2_iface, status, status_str); + SysFreeString(status_str); + + if (This->response_body) ISequentialStream_Release(This->response_body); + This->response_body = NULL; + + if (FAILED(hr = httprequest_get_responseStream(&This->req, &body_v)) || + FAILED(hr = IUnknown_QueryInterface(V_UNKNOWN(&body_v), &IID_ISequentialStream, (void **)&This->response_body))) + { + WARN("failed to get response stream, error %#lx\n", hr); + IXMLHTTPRequest2Callback_OnError(This->callback, xhr2_iface, hr); + IXMLHTTPRequest2Callback_Release(This->callback); + return S_OK; + } + + IXMLHTTPRequest2Callback_OnDataAvailable(This->callback, xhr2_iface, This->response_body); + IXMLHTTPRequest2Callback_OnResponseReceived(This->callback, xhr2_iface, This->response_body); + IXMLHTTPRequest2Callback_Release(This->callback); + } + + return S_OK; +} + +static const struct IDispatchVtbl xml_http_request_2_IDispatchVtbl = { + /* IUnknown methods */ + xml_http_request_2_IDispatch_QueryInterface, + xml_http_request_2_IDispatch_AddRef, + xml_http_request_2_IDispatch_Release, + /* IDispatch methods */ + xml_http_request_2_IDispatch_GetTypeInfoCount, + xml_http_request_2_IDispatch_GetTypeInfo, + xml_http_request_2_IDispatch_GetIDsOfNames, + xml_http_request_2_IDispatch_Invoke, +}; + static void init_httprequest(httprequest *req) { req->IXMLHTTPRequest_iface.lpVtbl = &XMLHTTPRequestVtbl; @@ -2107,6 +2571,35 @@ HRESULT XMLHTTPRequest_create(void **obj) return S_OK; } +HRESULT XMLHTTPRequest2_create(void **obj) +{ + struct xml_http_request_2 *xhr2; + TRACE("(%p)\n", obj); + + if (!(xhr2 = heap_alloc(sizeof(*xhr2)))) return E_OUTOFMEMORY; + + init_httprequest(&xhr2->req); + xhr2->IXMLHTTPRequest3_iface.lpVtbl = &XMLHTTPRequest3Vtbl; + xhr2->IRtwqAsyncCallback_iface.lpVtbl = &xml_http_request_2_IRtwqAsyncCallbackVtbl; + xhr2->IDispatch_iface.lpVtbl = &xml_http_request_2_IDispatchVtbl; + + /* do not call httprequest_put_onreadystatechange to avoid ref cycle */ + xhr2->req.sink = &xhr2->IDispatch_iface; + + xhr2->callback = NULL; + xhr2->callback3 = NULL; + xhr2->request_body = NULL; + xhr2->response_body = NULL; + + /* for async http requests we need window message queue */ + RtwqStartup(); + if (!xhr2_work_queue) RtwqAllocateWorkQueue(RTWQ_MULTITHREADED_WORKQUEUE, &xhr2_work_queue); + + *obj = &xhr2->IXMLHTTPRequest3_iface; + TRACE("returning iface %p\n", *obj); + return S_OK; +} + HRESULT ServerXMLHTTP_create(void **obj) { serverhttp *req; diff --git a/dlls/msxml3/msxml_private.h b/dlls/msxml3/msxml_private.h index cd8ae547c73..f3c9edd2c70 100644 --- a/dlls/msxml3/msxml_private.h +++ b/dlls/msxml3/msxml_private.h @@ -344,6 +344,7 @@ extern HRESULT XMLDocument_create(void**) DECLSPEC_HIDDEN; extern HRESULT SAXXMLReader_create(MSXML_VERSION, void**) DECLSPEC_HIDDEN; extern HRESULT SAXAttributes_create(MSXML_VERSION, void**) DECLSPEC_HIDDEN; extern HRESULT XMLHTTPRequest_create(void **) DECLSPEC_HIDDEN; +extern HRESULT XMLHTTPRequest2_create(void **) DECLSPEC_HIDDEN; extern HRESULT ServerXMLHTTP_create(void **) DECLSPEC_HIDDEN; extern HRESULT XSLTemplate_create(void**) DECLSPEC_HIDDEN; extern HRESULT MXWriter_create(MSXML_VERSION, void**) DECLSPEC_HIDDEN; diff --git a/dlls/msxml3/tests/httpreq.c b/dlls/msxml3/tests/httpreq.c index bccfbaf582a..23d7680d196 100644 --- a/dlls/msxml3/tests/httpreq.c +++ b/dlls/msxml3/tests/httpreq.c @@ -26,9 +26,9 @@ #include #include "windows.h" - #include "msxml2.h" -#include "msxml2did.h" +#include "msxml6.h" +#include "msxml6did.h" #include "dispex.h" #include "initguid.h" @@ -1344,6 +1344,17 @@ static IXMLHttpRequest *create_xhr(void) return SUCCEEDED(hr) ? ret : NULL; } +static IXMLHTTPRequest2 *create_xhr2(void) +{ + IXMLHTTPRequest2 *ret; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_FreeThreadedXMLHTTP60, NULL, CLSCTX_INPROC_SERVER, + &IID_IXMLHTTPRequest2, (void**)&ret); + + return SUCCEEDED(hr) ? ret : NULL; +} + static IServerXMLHTTPRequest *create_server_xhr(void) { IServerXMLHTTPRequest *ret; @@ -1904,11 +1915,388 @@ static void test_supporterrorinfo(void) IServerXMLHTTPRequest_Release(server_xhr); } +struct xhr3_callback +{ + IXMLHTTPRequest3Callback IXMLHTTPRequest3Callback_iface; + LONG ref; + HANDLE event; +}; + +static inline struct xhr3_callback *xhr3_callback_from_IXMLHTTPRequest3Callback(IXMLHTTPRequest3Callback *iface) +{ + return CONTAINING_RECORD(iface, struct xhr3_callback, IXMLHTTPRequest3Callback_iface); +} + +static HRESULT WINAPI xhr3_callback_QueryInterface(IXMLHTTPRequest3Callback *iface, REFIID riid, void **obj) +{ + struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); + trace("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); + + if (IsEqualGUID(riid, &IID_IXMLHTTPRequest3Callback) || IsEqualGUID(riid, &IID_IXMLHTTPRequest2Callback) || IsEqualGUID(riid, &IID_IUnknown)) + { + IXMLHTTPRequest3Callback_AddRef(iface); + *obj = iface; + return S_OK; + } + + ok(0, "unexpected interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI xhr3_callback_AddRef(IXMLHTTPRequest3Callback *iface) +{ + struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); + ULONG ref = InterlockedIncrement(&This->ref); + trace("(%p)->(%lu)\n", This, ref); + return ref; +} + +static ULONG WINAPI xhr3_callback_Release(IXMLHTTPRequest3Callback *iface) +{ + struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); + ULONG ref = InterlockedDecrement(&This->ref); + trace("(%p)->(%lu)\n", This, ref); + if (ref == 0) HeapFree(GetProcessHeap(), 0, This); + return ref; +} + +static HRESULT WINAPI xhr3_callback_OnRedirect(IXMLHTTPRequest3Callback *iface, + IXMLHTTPRequest2 *request, const WCHAR* redirect_url) +{ + struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); + trace("(%p)->(%p %s)\n", This, request, debugstr_w(redirect_url)); + return S_OK; +} + +static HRESULT WINAPI xhr3_callback_OnHeadersAvailable(IXMLHTTPRequest3Callback *iface, + IXMLHTTPRequest2 *request, DWORD status, const WCHAR *status_str) +{ + struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); + WCHAR *header = NULL; + HRESULT hr; + + trace("(%p)->(%p %lu %s)\n", This, request, status, debugstr_w(status_str)); + + header = (void *)0xdeadbeef; + hr = IXMLHTTPRequest2_GetResponseHeader(request, L"Content-Length", &header); + trace("Content-Length: %p (%s), hr %#lx\n", header, debugstr_w(header), hr); + + return S_OK; +} + +static HRESULT WINAPI xhr3_callback_OnDataAvailable(IXMLHTTPRequest3Callback *iface, + IXMLHTTPRequest2 *request, ISequentialStream *response) +{ + struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); + trace("(%p)->(%p %p)\n", This, request, response); + return S_OK; +} + +static HRESULT WINAPI xhr3_callback_OnResponseReceived(IXMLHTTPRequest3Callback *iface, + IXMLHTTPRequest2 *request, ISequentialStream *response) +{ + struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); + WCHAR *header = NULL; + char *buffer = HeapAlloc( GetProcessHeap(), 0, 256 ); + ULONG read_size = 0; + HRESULT hr; + + memset(buffer, '?', 256); + buffer[255] = 0; + + trace("(%p)->(%p %p)\n", This, request, response); + + header = (void *)0xdeadbeef; + hr = IXMLHTTPRequest2_GetResponseHeader(request, L"Cache-Control", &header); + trace("Cache-Control: %p (%s), hr %#lx\n", header, debugstr_w(header), hr); + + header = (void *)0xdeadbeef; + hr = IXMLHTTPRequest2_GetResponseHeader(request, L"Expires", &header); + trace("Expires: %p (%s), hr %#lx\n", header, debugstr_w(header), hr); + + header = (void *)0xdeadbeef; + hr = IXMLHTTPRequest2_GetResponseHeader(request, L"Content-Type", &header); + trace("Content-Type: %p (%s), hr %#lx\n", header, debugstr_w(header), hr); + + read_size = 0xdeadbeef; + hr = ISequentialStream_Read(response, buffer, 214, &read_size); + trace("Response: (%ld) %s, hr %#lx\n", read_size, debugstr_a(buffer), hr); + + read_size = 0xdeadbeef; + hr = ISequentialStream_Read(response, buffer, 1, &read_size); + trace("Response: (%ld) %s, hr %#lx\n", read_size, debugstr_a(buffer), hr); + + HeapFree( GetProcessHeap(), 0, buffer ); + SetEvent(This->event); + + return S_OK; +} + +static HRESULT WINAPI xhr3_callback_OnError(IXMLHTTPRequest3Callback *iface, + IXMLHTTPRequest2 *request, HRESULT error) +{ + struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); + trace("(%p)->(%p %#lx)\n", This, request, error); + SetEvent(This->event); + return S_OK; +} + +static HRESULT WINAPI xhr3_callback_OnServerCertificateReceived(IXMLHTTPRequest3Callback *iface, + IXMLHTTPRequest3 *request, DWORD errors, DWORD chain_size, const XHR_CERT *chain) +{ + struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); + trace("(%p)->(%p %lu %lu %p)\n", This, request, errors, chain_size, chain); + return S_OK; +} + +static HRESULT WINAPI xhr3_callback_OnClientCertificateRequested(IXMLHTTPRequest3Callback *iface, + IXMLHTTPRequest3 *request, DWORD issuers_size, const WCHAR **issuers) +{ + struct xhr3_callback *This = xhr3_callback_from_IXMLHTTPRequest3Callback(iface); + trace("(%p)->(%p %lu %p)\n", This, request, issuers_size, issuers); + return S_OK; +} + +static const IXMLHTTPRequest3CallbackVtbl xhr3_callback_vtbl = +{ + xhr3_callback_QueryInterface, + xhr3_callback_AddRef, + xhr3_callback_Release, + /* IXMLHTTPRequest2Callback methods */ + xhr3_callback_OnRedirect, + xhr3_callback_OnHeadersAvailable, + xhr3_callback_OnDataAvailable, + xhr3_callback_OnResponseReceived, + xhr3_callback_OnError, + /* IXMLHTTPRequest3Callback methods */ + xhr3_callback_OnServerCertificateReceived, + xhr3_callback_OnClientCertificateRequested, +}; + +static IXMLHTTPRequest2Callback* xhr3_callback_create(HANDLE event) +{ + struct xhr3_callback *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + ok(This != NULL, "failed to allocate object\n"); + if (!This) return NULL; + + This->IXMLHTTPRequest3Callback_iface.lpVtbl = &xhr3_callback_vtbl; + This->ref = 1; + This->event = event; + + return (IXMLHTTPRequest2Callback*)&This->IXMLHTTPRequest3Callback_iface; +} + +struct xhr2_stream +{ + IStream IStream_iface; + LONG ref; + IStream *stream; +}; + +static inline struct xhr2_stream *xhr2_stream_from_IStream(IStream *iface) +{ + return CONTAINING_RECORD(iface, struct xhr2_stream, IStream_iface); +} + +static HRESULT WINAPI xhr2_stream_QueryInterface(IStream *iface, REFIID riid, void **obj) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); + + if (IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_ISequentialStream) || IsEqualGUID(riid, &IID_IUnknown)) + { + IStream_AddRef(iface); + *obj = iface; + return S_OK; + } + + ok(0, "unexpected interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI xhr2_stream_AddRef(IStream *iface) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + ULONG ref = InterlockedIncrement(&This->ref); + trace("(%p)->(%lu)\n", This, ref); + return ref; +} + +static ULONG WINAPI xhr2_stream_Release(IStream *iface) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + ULONG ref = InterlockedDecrement(&This->ref); + trace("(%p)->(%lu)\n", This, ref); + if (ref == 0) + { + IStream_Release(This->stream); + HeapFree(GetProcessHeap(), 0, This); + } + return ref; +} + +static HRESULT WINAPI xhr2_stream_Read(IStream *iface, void *pv, ULONG cb, + ULONG *pcbRead) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->(%p %lu %p)\n", This, pv, cb, pcbRead); + return IStream_Read(This->stream, pv, cb, pcbRead); +} + +static HRESULT WINAPI xhr2_stream_Write(IStream *iface, const void *pv, + ULONG cb, ULONG *pcbWritten) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->(%p %lu %p)\n", This, pv, cb, pcbWritten); + return IStream_Write(This->stream, pv, cb, pcbWritten); +} + +static HRESULT WINAPI xhr2_stream_Seek(IStream *iface, LARGE_INTEGER dlibMove, + DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->(%I64u, %lu %p)\n", This, dlibMove.QuadPart, dwOrigin, plibNewPosition); + return IStream_Seek(This->stream, dlibMove, dwOrigin, plibNewPosition); +} + +static HRESULT WINAPI xhr2_stream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->(%I64u)\n", This, libNewSize.QuadPart); + return IStream_SetSize(This->stream, libNewSize); +} + +static HRESULT WINAPI xhr2_stream_CopyTo(IStream *iface, IStream *pstm, + ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->(%p %I64u %p %p)\n", This, pstm, cb.QuadPart, pcbRead, pcbWritten); + return IStream_CopyTo(This->stream, pstm, cb, pcbRead, pcbWritten); +} + +static HRESULT WINAPI xhr2_stream_Commit(IStream *iface, DWORD grfCommitFlags) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->(%#lx)\n", This, grfCommitFlags); + return IStream_Commit(This->stream, grfCommitFlags); +} + +static HRESULT WINAPI xhr2_stream_Revert(IStream *iface) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->()\n", This); + return IStream_Revert(This->stream); +} + +static HRESULT WINAPI xhr2_stream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, DWORD dwLockType) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->(%I64u %I64u %lu)\n", This, libOffset.QuadPart, cb.QuadPart, dwLockType); + return IStream_LockRegion(This->stream, libOffset, cb, dwLockType); +} + +static HRESULT WINAPI xhr2_stream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, DWORD dwLockType) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->(%I64u %I64u %lu)\n", This, libOffset.QuadPart, cb.QuadPart, dwLockType); + return IStream_UnlockRegion(This->stream, libOffset, cb, dwLockType); +} + +static HRESULT WINAPI xhr2_stream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->(%p %#lx)\n", This, pstatstg, grfStatFlag); + return IStream_Stat(This->stream, pstatstg, grfStatFlag); +} + +static HRESULT WINAPI xhr2_stream_Clone(IStream *iface, IStream **ppstm) +{ + struct xhr2_stream *This = xhr2_stream_from_IStream(iface); + trace("(%p)->(%p)\n", This, ppstm); + return IStream_Clone(This->stream, ppstm); +} + +static const IStreamVtbl xhr2_stream_vtbl = +{ + xhr2_stream_QueryInterface, + xhr2_stream_AddRef, + xhr2_stream_Release, + /* IStream methods */ + xhr2_stream_Read, + xhr2_stream_Write, + xhr2_stream_Seek, + xhr2_stream_SetSize, + xhr2_stream_CopyTo, + xhr2_stream_Commit, + xhr2_stream_Revert, + xhr2_stream_LockRegion, + xhr2_stream_UnlockRegion, + xhr2_stream_Stat, + xhr2_stream_Clone +}; + +static ISequentialStream *xhr2_stream_create(void) +{ + struct xhr2_stream *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + ok(This != NULL, "failed to allocate object\n"); + if (!This) return NULL; + + This->IStream_iface.lpVtbl = &xhr2_stream_vtbl; + This->ref = 1; + CreateStreamOnHGlobal(NULL, TRUE, &This->stream); + + return (ISequentialStream*)&This->IStream_iface; +} + +static void test_IXMLHTTPRequest2(void) +{ + IXMLHTTPRequest2 *xhr2[16]; + IXMLHTTPRequest2Callback *xhr3_callback; + ISequentialStream *stream; + HANDLE events[16]; + HRESULT hr; + int i = 0; + + if (!(xhr2[i] = create_xhr2())) + { + win_skip("IXMLHTTPRequest2 is not available\n"); + return; + } + + events[i] = CreateEventW(NULL, FALSE, FALSE, NULL); + if (!(xhr3_callback = xhr3_callback_create(events[i]))) + return; + + trace("%lu: IXMLHTTPRequest2_Open (%p)->(L\"GET\", L\"http://test.winehq.org/\", xhr3_callback, NULL, NULL, NULL, NULL)\n", GetCurrentThreadId(), xhr2[i]); + hr = IXMLHTTPRequest2_Open(xhr2[i], L"GET", L"http://test.winehq.org/", xhr3_callback, NULL, NULL, NULL, NULL); + ok(SUCCEEDED(hr), "IXMLHTTPRequest2_Send failed %#lx\n", hr); + + if ((stream = xhr2_stream_create())) + { + trace("%lu: IXMLHTTPRequest2_Send (%p)->(%p 0)\n", GetCurrentThreadId(), xhr2[i], stream); + hr = IXMLHTTPRequest2_Send(xhr2[i], stream, 0); + ok(SUCCEEDED(hr), "IXMLHTTPRequest2_Send failed %#lx\n", hr); + + ISequentialStream_Release(stream); + } + + IXMLHTTPRequest2Callback_Release(xhr3_callback); + i++; + + while (i--) + { + WaitForSingleObject(events[i], INFINITE); + IXMLHTTPRequest2_Release(xhr2[i]); + } +} + START_TEST(httpreq) { IXMLHttpRequest *xhr; - CoInitialize(NULL); + CoInitializeEx(NULL, COINIT_MULTITHREADED); if (!(xhr = create_xhr())) { @@ -1923,6 +2311,7 @@ START_TEST(httpreq) test_server_xhr(); test_safe_httpreq(); test_supporterrorinfo(); + test_IXMLHTTPRequest2(); CoUninitialize(); } diff --git a/dlls/msxml3/tests/schema.c b/dlls/msxml3/tests/schema.c index 99c7d35cc10..2b63182e2e4 100644 --- a/dlls/msxml3/tests/schema.c +++ b/dlls/msxml3/tests/schema.c @@ -32,10 +32,16 @@ #include "dispex.h" #include "cguid.h" +DEFINE_GUID(CLSID_FreeThreadedDOMDocument60, 0x88d96a06, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_FreeThreadedXMLHTTP60, 0x88d96a09, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_MXXMLWriter60, 0x88d96a0f, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_SAXAttributes60, 0x88d96a0e, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_SAXXMLReader60, 0x88d96a0c, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_XMLSchemaCache60, 0x88d96a07, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(IID_IXMLHTTPRequest2, 0xe5d37dc0, 0x552a, 0x4d52, 0x9c,0xc0, 0xa1,0x4d,0x54,0x6f,0xbd,0x04); +DEFINE_GUID(IID_IXMLHTTPRequest3, 0xa1c9feee, 0x0617, 0x4f23, 0x9d,0x58, 0x89,0x61,0xea,0x43,0x56,0x7c); +DEFINE_GUID(IID_IXMLHTTPRequest2Callback, 0xa44a9299, 0xe321, 0x40de, 0x88,0x66, 0x34,0x1b,0x41,0x66,0x91,0x62); +DEFINE_GUID(IID_IXMLHTTPRequest3Callback, 0xb9e57830, 0x8c6c, 0x4a6f, 0x9c,0x13, 0x47,0x77,0x2b,0xb0,0x47,0xbb); #include "wine/test.h" diff --git a/dlls/msxml3/uuid.c b/dlls/msxml3/uuid.c index 333d4f3d3c7..1b4f0452c5f 100644 --- a/dlls/msxml3/uuid.c +++ b/dlls/msxml3/uuid.c @@ -43,6 +43,7 @@ /* Cannot include msxml6 here since we will get a duplicate LIBID_MSXML2 error. */ DEFINE_GUID(CLSID_FreeThreadedDOMDocument60, 0x88d96a06, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(CLSID_FreeThreadedXMLHTTP60, 0x88d96a09, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_MXNamespaceManager60, 0x88d96a11, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_MXXMLWriter60, 0x88d96a0f, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_SAXAttributes60, 0x88d96a0e, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); @@ -51,6 +52,10 @@ DEFINE_GUID(CLSID_ServerXMLHTTP60, 0x88d96a0b, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0 DEFINE_GUID(CLSID_XMLHTTP60, 0x88d96a0a, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_XMLSchemaCache60, 0x88d96a07, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); DEFINE_GUID(CLSID_XSLTemplate60, 0x88d96a08, 0xf192, 0x11d4, 0xa6,0x5f, 0x00,0x40,0x96,0x32,0x51,0xe5); +DEFINE_GUID(IID_IXMLHTTPRequest2, 0xe5d37dc0, 0x552a, 0x4d52, 0x9c,0xc0, 0xa1,0x4d,0x54,0x6f,0xbd,0x04); +DEFINE_GUID(IID_IXMLHTTPRequest3, 0xa1c9feee, 0x0617, 0x4f23, 0x9d,0x58, 0x89,0x61,0xea,0x43,0x56,0x7c); +DEFINE_GUID(IID_IXMLHTTPRequest2Callback, 0xa44a9299, 0xe321, 0x40de, 0x88,0x66, 0x34,0x1b,0x41,0x66,0x91,0x62); +DEFINE_GUID(IID_IXMLHTTPRequest3Callback, 0xb9e57830, 0x8c6c, 0x4a6f, 0x9c,0x13, 0x47,0x77,0x2b,0xb0,0x47,0xbb); /* * Note that because of a #define in msxml2.h, we end up initializing From dab9b7c5cae50651fe616ac978a776641ae030f3 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 5 Jan 2023 14:36:31 +1100 Subject: [PATCH 0704/1148] msxml3: Implement IXMLHTTPRequest3 SetProperty CW-Bug-Id: #22822 --- dlls/msxml3/httprequest.c | 77 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/dlls/msxml3/httprequest.c b/dlls/msxml3/httprequest.c index 98dd23c9fbb..d8360680411 100644 --- a/dlls/msxml3/httprequest.c +++ b/dlls/msxml3/httprequest.c @@ -102,6 +102,21 @@ typedef struct /* IObjectSafety */ DWORD safeopt; + + /* Properties */ + DWORD no_prompt; + DWORD no_auth; + DWORD timeout; + BOOL no_headeres; + BOOL redirect; + BOOL cache; + BOOL extended; + BOOL query_utf8; + BOOL ignore_errors; + BOOL threshold; + DWORD enterrprised_id; + DWORD max_connections; + } httprequest; typedef struct @@ -2230,8 +2245,52 @@ static HRESULT WINAPI xml_http_request_2_SetCustomResponseStream(IXMLHTTPRequest static HRESULT WINAPI xml_http_request_2_SetProperty(IXMLHTTPRequest3 *iface, XHR_PROPERTY property, ULONGLONG value) { struct xml_http_request_2 *This = impl_from_IXMLHTTPRequest3(iface); - FIXME("(%p)->(%#x %s) stub!\n", This, property, wine_dbgstr_longlong( value )); - return E_NOTIMPL; + + TRACE("(%p)->(%#x %s) stub!\n", This, property, wine_dbgstr_longlong( value )); + + switch (property) + { + case XHR_PROP_NO_CRED_PROMPT: + This->req.no_prompt = value; + break; + case XHR_PROP_NO_AUTH: + This->req.no_auth = value; + break; + case XHR_PROP_TIMEOUT: + This->req.timeout = value; + break; + case XHR_PROP_NO_DEFAULT_HEADERS: + This->req.no_headeres = value != 0; + break; + case XHR_PROP_REPORT_REDIRECT_STATUS: + This->req.redirect = value != 0; + break; + case XHR_PROP_NO_CACHE: + This->req.cache = value != 0; + break; + case XHR_PROP_EXTENDED_ERROR: + This->req.extended = value != 0; + break; + case XHR_PROP_QUERY_STRING_UTF8: + This->req.query_utf8 = value != 0; + break; + case XHR_PROP_IGNORE_CERT_ERRORS: + This->req.ignore_errors = value != 0; + break; + case XHR_PROP_ONDATA_THRESHOLD: + This->req.threshold = value; + break; + case XHR_PROP_SET_ENTERPRISEID: + This->req.enterrprised_id = value; + break; + case XHR_PROP_MAX_CONNECTIONS: + This->req.max_connections = value; + break; + default: + WARN("Invalid property %#x\n", property); + return E_INVALIDARG; + } + return S_OK; } static HRESULT WINAPI xml_http_request_2_SetRequestHeader(IXMLHTTPRequest3 *iface, @@ -2551,6 +2610,20 @@ static void init_httprequest(httprequest *req) req->site = NULL; req->safeopt = 0; + + /* Properties */ + req->no_prompt = XHR_CRED_PROMPT_ALL; + req->no_auth = XHR_AUTH_ALL; + req->timeout = 0xFFFFFFFF; + req->no_headeres = FALSE; + req->redirect = FALSE; + req->cache = FALSE; + req->extended = FALSE; + req->query_utf8 = FALSE;; + req->ignore_errors = FALSE;; + req->threshold = 0x100; + req->enterrprised_id = 0; + req->max_connections = 10; } HRESULT XMLHTTPRequest_create(void **obj) From e2b8621b9446437507742ecfe3a8a1f2e1df0624 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 12 Jan 2023 08:21:48 +1100 Subject: [PATCH 0705/1148] msxml3: Correct xml_http_request_2_GetResponseHeader CW-Bug-Id: #22822 --- dlls/msxml3/httprequest.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/msxml3/httprequest.c b/dlls/msxml3/httprequest.c index d8360680411..cd0b11e3faa 100644 --- a/dlls/msxml3/httprequest.c +++ b/dlls/msxml3/httprequest.c @@ -2325,8 +2325,7 @@ static HRESULT WINAPI xml_http_request_2_GetResponseHeader(IXMLHTTPRequest3 *ifa TRACE("(%p)->(%s %p)\n", This, debugstr_w(header), value); - if (FAILED(hr = httprequest_getResponseHeader(&This->req, (BSTR)header, value))) - return hr; + hr = httprequest_getResponseHeader(&This->req, (BSTR)header, value); #define E_FILE_NOT_FOUND _HRESULT_TYPEDEF_(0x80070002) From 530f1b2f839b7ab48be8d3c5ad6b7498cf444c26 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 5 Oct 2023 08:43:07 -0600 Subject: [PATCH 0706/1148] fixup! msxml3: Implement FreeThreadedXMLHTTP60. CW-Bug-Id: #22822 --- include/msxml6.idl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/include/msxml6.idl b/include/msxml6.idl index 7396826a1f6..b2d8bd3b337 100644 --- a/include/msxml6.idl +++ b/include/msxml6.idl @@ -1715,17 +1715,6 @@ interface ISAXDeclHandler : IUnknown [in] int nSystemId); } -[ - helpstring("Free Threaded XML HTTP Request class 6.0"), - progid("Msxml2.FreeThreadedXMLHTTP60.6.0"), - threading(both), - uuid(88d96a09-f192-11d4-a65f-0040963251e5) -] -coclass FreeThreadedXMLHTTP60 -{ - [default] interface IXMLHTTPRequest2; -} - [ object, local, @@ -3053,6 +3042,17 @@ interface ISchemaNotation; SCHEMATYPEVARIETY __schemaTypeVariety__; } __msxml6_ReferenceRemainingTypes__; +[ + helpstring("Free Threaded XML HTTP Request class 6.0"), + progid("Msxml2.FreeThreadedXMLHTTP60.6.0"), + threading(both), + uuid(88d96a09-f192-11d4-a65f-0040963251e5) +] +coclass FreeThreadedXMLHTTP60 +{ + [default] interface IXMLHTTPRequest2; +} + [ helpstring("XML DOM Document 6.0"), progid("Msxml2.DOMDocument.6.0"), From 63b6d8b285ef002a35942fc16804488a3a5b0b79 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 5 Oct 2023 11:38:00 -0600 Subject: [PATCH 0707/1148] msxml3: Treat body as data array in xml_http_request_2_IRtwqAsyncCallback_Invoke(). CW-Bug-Id: #22822 --- dlls/msxml3/httprequest.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/dlls/msxml3/httprequest.c b/dlls/msxml3/httprequest.c index cd0b11e3faa..ee6543dd8ea 100644 --- a/dlls/msxml3/httprequest.c +++ b/dlls/msxml3/httprequest.c @@ -2412,6 +2412,7 @@ static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_Invoke(IRtwqAsyncCal IRtwqAsyncResult *result) { struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); + SAFEARRAY *sa = NULL; VARIANT body_v; HRESULT hr; ULONG read; @@ -2422,14 +2423,25 @@ static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_Invoke(IRtwqAsyncCal if (This->request_body) { - V_VT(&body_v) = VT_BSTR; - V_BSTR(&body_v) = CoTaskMemAlloc(This->request_body_size); + SAFEARRAYBOUND bound; + void *ptr; - if (FAILED(hr = ISequentialStream_Read(This->request_body, V_BSTR(&body_v), This->request_body_size, &read)) || - read < This->request_body_size) + bound.lLbound = 0; + bound.cElements = This->request_body_size; + if (!(sa = SafeArrayCreate(VT_UI1, 1, &bound))) + { + ERR("No memory.\n"); + hr = E_OUTOFMEMORY; + goto done; + } + V_ARRAY(&body_v) = sa; + V_VT(&body_v) = VT_ARRAY | VT_UI1; + SafeArrayAccessData(sa, &ptr); + hr = ISequentialStream_Read(This->request_body, ptr, This->request_body_size, &read); + SafeArrayUnaccessData(sa); + if (FAILED(hr) || read < This->request_body_size) { - ERR("Failed to allocate request body memory, hr %#lx\n", hr); - CoTaskMemFree(V_BSTR(&body_v)); + ERR("Failed to read from stream, hr %#lx\n", hr); goto done; } @@ -2440,6 +2452,8 @@ static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_Invoke(IRtwqAsyncCal hr = httprequest_send(&This->req, body_v); done: + if (sa) + SafeArrayDestroy(sa); return IRtwqAsyncResult_SetStatus(result, hr); } From 1ada8bda12558de83460c67696d1189efda29b1d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 5 Oct 2023 12:05:28 -0600 Subject: [PATCH 0708/1148] msxml3: Try to get body size from stream in xml_http_request_2_IRtwqAsyncCallback_Invoke(). CW-Bug-Id: #22822 --- dlls/msxml3/httprequest.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/dlls/msxml3/httprequest.c b/dlls/msxml3/httprequest.c index ee6543dd8ea..6a40a02958e 100644 --- a/dlls/msxml3/httprequest.c +++ b/dlls/msxml3/httprequest.c @@ -2412,6 +2412,7 @@ static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_Invoke(IRtwqAsyncCal IRtwqAsyncResult *result) { struct xml_http_request_2 *This = xml_http_request_2_from_IRtwqAsyncCallback(iface); + IStream *stream = NULL; SAFEARRAY *sa = NULL; VARIANT body_v; HRESULT hr; @@ -2424,10 +2425,27 @@ static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_Invoke(IRtwqAsyncCal if (This->request_body) { SAFEARRAYBOUND bound; + ULONGLONG body_size; + STATSTG stream_stat; + LARGE_INTEGER li; void *ptr; + if (SUCCEEDED(ISequentialStream_QueryInterface(This->request_body, &IID_IStream, (void **)&stream)) + && SUCCEEDED(IStream_Stat(stream, &stream_stat, 0))) + { + body_size = stream_stat.cbSize.QuadPart; + li.QuadPart = 0; + IStream_Seek(stream, li, STREAM_SEEK_SET, NULL); + } + else + { + body_size = This->request_body_size; + } + + TRACE("body_size %I64u.\n", body_size); + bound.lLbound = 0; - bound.cElements = This->request_body_size; + bound.cElements = body_size; if (!(sa = SafeArrayCreate(VT_UI1, 1, &bound))) { ERR("No memory.\n"); @@ -2437,9 +2455,13 @@ static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_Invoke(IRtwqAsyncCal V_ARRAY(&body_v) = sa; V_VT(&body_v) = VT_ARRAY | VT_UI1; SafeArrayAccessData(sa, &ptr); - hr = ISequentialStream_Read(This->request_body, ptr, This->request_body_size, &read); + + if (stream) + hr = IStream_Read(stream, ptr, body_size, &read); + else + hr = ISequentialStream_Read(This->request_body, ptr, body_size, &read); SafeArrayUnaccessData(sa); - if (FAILED(hr) || read < This->request_body_size) + if (FAILED(hr) || read < body_size) { ERR("Failed to read from stream, hr %#lx\n", hr); goto done; @@ -2454,6 +2476,8 @@ static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_Invoke(IRtwqAsyncCal done: if (sa) SafeArrayDestroy(sa); + if (stream) + IStream_Release(stream); return IRtwqAsyncResult_SetStatus(result, hr); } From 57c5732ca8d1d7e1caa2e5d840b7087c55cac484 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 5 Oct 2023 12:02:44 -0600 Subject: [PATCH 0709/1148] msxml3: Don't fail xml_http_request_2_IRtwqAsyncCallback_Invoke() on stream read failures. CW-Bug-Id: #22822 --- dlls/msxml3/httprequest.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dlls/msxml3/httprequest.c b/dlls/msxml3/httprequest.c index 6a40a02958e..646ffbecf74 100644 --- a/dlls/msxml3/httprequest.c +++ b/dlls/msxml3/httprequest.c @@ -2463,8 +2463,12 @@ static HRESULT WINAPI xml_http_request_2_IRtwqAsyncCallback_Invoke(IRtwqAsyncCal SafeArrayUnaccessData(sa); if (FAILED(hr) || read < body_size) { - ERR("Failed to read from stream, hr %#lx\n", hr); - goto done; + /* Windows doesn't send the body in this case but still sends request with Content-Length + * set to requested body size. */ + ERR("Failed to read from stream, hr %#lx, read %lu\n", hr, read); + SafeArrayDestroy(sa); + sa = NULL; + V_VT(&body_v) = VT_NULL; } ISequentialStream_Release(This->request_body); From 9181b884fd3dce0a065e83b700810197cc9c5178 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 6 Oct 2023 17:54:50 -0600 Subject: [PATCH 0710/1148] imagehlp: Import functions instead of forwarding. CW-Bug-Id: #22828 --- dlls/imagehlp/imagehlp.spec | 148 ++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/dlls/imagehlp/imagehlp.spec b/dlls/imagehlp/imagehlp.spec index 744b339caf8..7d934dcc96e 100644 --- a/dlls/imagehlp/imagehlp.spec +++ b/dlls/imagehlp/imagehlp.spec @@ -1,35 +1,35 @@ @ stdcall BindImage(str str str) @ stdcall BindImageEx(long str str str ptr) @ stdcall CheckSumMappedFile(ptr long ptr ptr) -@ stdcall EnumerateLoadedModules64(long ptr ptr) dbghelp.EnumerateLoadedModules64 -@ stdcall EnumerateLoadedModules(long ptr ptr) dbghelp.EnumerateLoadedModules -@ stdcall FindDebugInfoFile(str str ptr) dbghelp.FindDebugInfoFile -@ stdcall FindDebugInfoFileEx(str str ptr ptr ptr) dbghelp.FindDebugInfoFileEx -@ stdcall FindExecutableImage(str str str) dbghelp.FindExecutableImage -@ stdcall FindExecutableImageEx(str str ptr ptr ptr) dbghelp.FindExecutableImageEx +@ stdcall -import EnumerateLoadedModules64(long ptr ptr) +@ stdcall -import EnumerateLoadedModules(long ptr ptr) +@ stdcall -import FindDebugInfoFile(str str ptr) +@ stdcall -import FindDebugInfoFileEx(str str ptr ptr ptr) +@ stdcall -import FindExecutableImage(str str str) +@ stdcall -import FindExecutableImageEx(str str ptr ptr ptr) @ stub FindFileInPath @ stub FindFileInSearchPath @ stdcall GetImageConfigInformation(ptr ptr) @ stdcall GetImageUnusedHeaderBytes(ptr ptr) -@ stdcall GetTimestampForLoadedLibrary(long) dbghelp.GetTimestampForLoadedLibrary +@ stdcall -import GetTimestampForLoadedLibrary(long) @ stdcall ImageAddCertificate(long ptr ptr) -@ stdcall ImageDirectoryEntryToData(ptr long long ptr) dbghelp.ImageDirectoryEntryToData -@ stdcall ImageDirectoryEntryToDataEx(ptr long long ptr ptr) dbghelp.ImageDirectoryEntryToDataEx +@ stdcall -import ImageDirectoryEntryToData(ptr long long ptr) +@ stdcall -import ImageDirectoryEntryToDataEx(ptr long long ptr ptr) @ stdcall ImageEnumerateCertificates(long long ptr ptr long) @ stdcall ImageGetCertificateData(long long ptr ptr) @ stdcall ImageGetCertificateHeader(long long ptr) @ stdcall ImageGetDigestStream(long long ptr long) @ stdcall ImageLoad(str str) -@ stdcall ImageNtHeader(ptr) ntdll.RtlImageNtHeader +@ stdcall -import ImageNtHeader(ptr) RtlImageNtHeader @ stdcall ImageRemoveCertificate(long long) -@ stdcall ImageRvaToSection(ptr ptr long) ntdll.RtlImageRvaToSection -@ stdcall ImageRvaToVa(ptr ptr long ptr) ntdll.RtlImageRvaToVa +@ stdcall -import ImageRvaToSection(ptr ptr long) RtlImageRvaToSection +@ stdcall -import ImageRvaToVa(ptr ptr long ptr) RtlImageRvaToVa @ stdcall ImageUnload(ptr) -@ stdcall ImagehlpApiVersion() dbghelp.ImagehlpApiVersion -@ stdcall ImagehlpApiVersionEx(ptr) dbghelp.ImagehlpApiVersionEx -@ stdcall MakeSureDirectoryPathExists(str) dbghelp.MakeSureDirectoryPathExists +@ stdcall -import ImagehlpApiVersion() +@ stdcall -import ImagehlpApiVersionEx(ptr) +@ stdcall -import MakeSureDirectoryPathExists(str) @ stdcall MapAndLoad(str str ptr long long) -@ stdcall MapDebugInformation(long str str long) dbghelp.MapDebugInformation +@ stdcall -import -arch=win32 MapDebugInformation(long str str long) @ stdcall MapFileAndCheckSumA(str ptr ptr) @ stdcall MapFileAndCheckSumW(wstr ptr ptr) @ stub MarkImageAsRunFromSwap @@ -38,72 +38,72 @@ @ stdcall RemovePrivateCvSymbolic(ptr ptr ptr) @ stub RemovePrivateCvSymbolicEx @ stdcall RemoveRelocations(ptr) -@ stdcall SearchTreeForFile(str str ptr) dbghelp.SearchTreeForFile +@ stdcall -import SearchTreeForFile(str str ptr) @ stdcall SetImageConfigInformation(ptr ptr) @ stdcall SplitSymbols(str str str long) -@ stdcall StackWalk64(long long long ptr ptr ptr ptr ptr ptr) dbghelp.StackWalk64 -@ stdcall StackWalk(long long long ptr ptr ptr ptr ptr ptr) dbghelp.StackWalk -@ stdcall SymCleanup(long) dbghelp.SymCleanup -@ stdcall SymEnumSourceFiles(ptr int64 str ptr ptr) dbghelp.SymEnumSourceFiles +@ stdcall -import StackWalk64(long long long ptr ptr ptr ptr ptr ptr) +@ stdcall -import StackWalk(long long long ptr ptr ptr ptr ptr ptr) +@ stdcall -import SymCleanup(long) +@ stdcall SymEnumSourceFiles(ptr int64 str ptr ptr) SymEnumSourceFiles @ stub SymEnumSym -@ stdcall SymEnumSymbols(ptr int64 str ptr ptr) dbghelp.SymEnumSymbols -@ stdcall SymEnumTypes(ptr int64 ptr ptr) dbghelp.SymEnumTypes -@ stdcall SymEnumerateModules64(long ptr ptr) dbghelp.SymEnumerateModules64 -@ stdcall SymEnumerateModules(long ptr ptr) dbghelp.SymEnumerateModules -@ stdcall SymEnumerateSymbols64(long int64 ptr ptr) dbghelp.SymEnumerateSymbols64 -@ stdcall SymEnumerateSymbols(long long ptr ptr) dbghelp.SymEnumerateSymbols +@ stdcall -import SymEnumSymbols(ptr int64 str ptr ptr) +@ stdcall -import SymEnumTypes(ptr int64 ptr ptr) +@ stdcall -import SymEnumerateModules64(long ptr ptr) +@ stdcall -import SymEnumerateModules(long ptr ptr) +@ stdcall -import SymEnumerateSymbols64(long int64 ptr ptr) +@ stdcall -import SymEnumerateSymbols(long long ptr ptr) @ stub SymEnumerateSymbolsW64 @ stub SymEnumerateSymbolsW -@ stdcall SymFindFileInPath(long str str ptr long long long ptr ptr ptr) dbghelp.SymFindFileInPath -@ stdcall SymFromAddr(ptr int64 ptr ptr) dbghelp.SymFromAddr -@ stdcall SymFromName(long str ptr) dbghelp.SymFromName -@ stdcall SymFunctionTableAccess64(long int64) dbghelp.SymFunctionTableAccess64 -@ stdcall SymFunctionTableAccess(long long) dbghelp.SymFunctionTableAccess -@ stdcall SymGetLineFromAddr64(long int64 ptr ptr) dbghelp.SymGetLineFromAddr64 -@ stdcall SymGetLineFromAddr(long long ptr ptr) dbghelp.SymGetLineFromAddr +@ stdcall -import SymFindFileInPath(long str str ptr long long long ptr ptr ptr) +@ stdcall -import SymFromAddr(ptr int64 ptr ptr) +@ stdcall -import SymFromName(long str ptr) +@ stdcall -import SymFunctionTableAccess64(long int64) +@ stdcall -import SymFunctionTableAccess(long long) +@ stdcall -import SymGetLineFromAddr64(long int64 ptr ptr) +@ stdcall -import SymGetLineFromAddr(long long ptr ptr) @ stub SymGetLineFromName64 @ stub SymGetLineFromName -@ stdcall SymGetLineNext64(long ptr) dbghelp.SymGetLineNext64 -@ stdcall SymGetLineNext(long ptr) dbghelp.SymGetLineNext -@ stdcall SymGetLinePrev64(long ptr) dbghelp.SymGetLinePrev64 -@ stdcall SymGetLinePrev(long ptr) dbghelp.SymGetLinePrev -@ stdcall SymGetModuleBase64(long int64) dbghelp.SymGetModuleBase64 -@ stdcall SymGetModuleBase(long long) dbghelp.SymGetModuleBase -@ stdcall SymGetModuleInfo64(long int64 ptr) dbghelp.SymGetModuleInfo64 -@ stdcall SymGetModuleInfo(long long ptr) dbghelp.SymGetModuleInfo -@ stdcall SymGetModuleInfoW64(long int64 ptr) dbghelp.SymGetModuleInfoW64 -@ stdcall SymGetModuleInfoW(long long ptr) dbghelp.SymGetModuleInfoW -@ stdcall SymGetOptions() dbghelp.SymGetOptions -@ stdcall SymGetSearchPath(long ptr long) dbghelp.SymGetSearchPath -@ stdcall SymGetSymFromAddr64(long int64 ptr ptr) dbghelp.SymGetSymFromAddr64 -@ stdcall SymGetSymFromAddr(long long ptr ptr) dbghelp.SymGetSymFromAddr -@ stdcall SymGetSymFromName64(long str ptr) dbghelp.SymGetSymFromName64 -@ stdcall SymGetSymFromName(long str ptr) dbghelp.SymGetSymFromName -@ stdcall SymGetSymNext64(long ptr) dbghelp.SymGetSymNext64 -@ stdcall SymGetSymNext(long ptr) dbghelp.SymGetSymNext -@ stdcall SymGetSymPrev64(long ptr) dbghelp.SymGetSymPrev64 -@ stdcall SymGetSymPrev(long ptr) dbghelp.SymGetSymPrev -@ stdcall SymGetTypeFromName(ptr int64 str ptr) dbghelp.SymGetTypeFromName -@ stdcall SymGetTypeInfo(ptr int64 long long ptr) dbghelp.SymGetTypeInfo -@ stdcall SymInitialize(long str long) dbghelp.SymInitialize -@ stdcall SymLoadModule64(long long str str int64 long) dbghelp.SymLoadModule64 -@ stdcall SymLoadModule(long long str str long long) dbghelp.SymLoadModule -@ stdcall SymMatchFileName(str str ptr ptr) dbghelp.SymMatchFileName -@ stdcall SymMatchString(str str long) dbghelp.SymMatchString -@ stdcall SymRegisterCallback64(long ptr int64) dbghelp.SymRegisterCallback64 -@ stdcall SymRegisterCallback(long ptr ptr) dbghelp.SymRegisterCallback -@ stdcall SymRegisterFunctionEntryCallback64(ptr ptr int64) dbghelp.SymRegisterFunctionEntryCallback64 -@ stdcall SymRegisterFunctionEntryCallback(ptr ptr ptr) dbghelp.SymRegisterFunctionEntryCallback -@ stdcall SymSetContext(long ptr ptr) dbghelp.SymSetContext -@ stdcall SymSetOptions(long) dbghelp.SymSetOptions -@ stdcall SymSetSearchPath(long str) dbghelp.SymSetSearchPath -@ stdcall SymUnDName64(ptr str long) dbghelp.SymUnDName64 -@ stdcall SymUnDName(ptr str long) dbghelp.SymUnDName -@ stdcall SymUnloadModule64(long int64) dbghelp.SymUnloadModule64 -@ stdcall SymUnloadModule(long long) dbghelp.SymUnloadModule +@ stdcall -import SymGetLineNext64(long ptr) +@ stdcall -import SymGetLineNext(long ptr) +@ stdcall -import SymGetLinePrev64(long ptr) +@ stdcall -import SymGetLinePrev(long ptr) +@ stdcall -import SymGetModuleBase64(long int64) +@ stdcall -import SymGetModuleBase(long long) +@ stdcall -import SymGetModuleInfo64(long int64 ptr) +@ stdcall -import SymGetModuleInfo(long long ptr) +@ stdcall -import SymGetModuleInfoW64(long int64 ptr) +@ stdcall -import SymGetModuleInfoW(long long ptr) +@ stdcall -import SymGetOptions() +@ stdcall -import SymGetSearchPath(long ptr long) +@ stdcall -import SymGetSymFromAddr64(long int64 ptr ptr) +@ stdcall -import SymGetSymFromAddr(long long ptr ptr) +@ stdcall -import SymGetSymFromName64(long str ptr) +@ stdcall -import SymGetSymFromName(long str ptr) +@ stdcall -import SymGetSymNext64(long ptr) +@ stdcall -import SymGetSymNext(long ptr) +@ stdcall -import SymGetSymPrev64(long ptr) +@ stdcall -import SymGetSymPrev(long ptr) +@ stdcall -import SymGetTypeFromName(ptr int64 str ptr) +@ stdcall -import SymGetTypeInfo(ptr int64 long long ptr) +@ stdcall -import SymInitialize(long str long) +@ stdcall -import SymLoadModule64(long long str str int64 long) +@ stdcall -import SymLoadModule(long long str str long long) +@ stdcall -import SymMatchFileName(str str ptr ptr) +@ stdcall -import SymMatchString(str str long) +@ stdcall -import SymRegisterCallback64(long ptr int64) +@ stdcall -import SymRegisterCallback(long ptr ptr) +@ stdcall -import SymRegisterFunctionEntryCallback64(ptr ptr int64) +@ stdcall -import SymRegisterFunctionEntryCallback(ptr ptr ptr) +@ stdcall -import SymSetContext(long ptr ptr) +@ stdcall -import SymSetOptions(long) +@ stdcall -import SymSetSearchPath(long str) +@ stdcall -import SymUnDName64(ptr str long) +@ stdcall -import SymUnDName(ptr str long) +@ stdcall -import SymUnloadModule64(long int64) +@ stdcall -import SymUnloadModule(long long) @ stdcall TouchFileTimes(long ptr) -@ stdcall UnDecorateSymbolName(str str long long) dbghelp.UnDecorateSymbolName +@ stdcall -import UnDecorateSymbolName(str str long long) @ stdcall UnMapAndLoad(ptr) -@ stdcall UnmapDebugInformation(ptr) dbghelp.UnmapDebugInformation +@ stdcall -import -arch=win32 UnmapDebugInformation(ptr) @ stdcall UpdateDebugInfoFile(str str str ptr) @ stdcall UpdateDebugInfoFileEx(str str str ptr long) From 77992a3afcab468b5cc66e0dd9099a650d537b58 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Apr 2022 20:27:25 +0300 Subject: [PATCH 0711/1148] winex11.drv: fshack: Don't set 'pending_fullscreen' if window already has NET_WM_STATE_FULLSCREEN. CW-Bug-Id: #20483 CW-Bug-Id: #22796 --- dlls/winex11.drv/window.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 4940ddc7804..4aa9878ad76 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1330,7 +1330,9 @@ void update_net_wm_states( struct x11drv_win_data *data ) if (i == NET_WM_STATE_FULLSCREEN) { - data->pending_fullscreen = (new_state & (1 << i)) != 0; + data->pending_fullscreen = (new_state & (1 << i)) + && !(data->net_wm_state & (1 << NET_WM_STATE_FULLSCREEN) + && wm_is_steamcompmgr(data->display)); TRACE( "set pending_fullscreen to: %u\n", data->pending_fullscreen ); } From cb163ce171f7662b37457202566a02794883735a Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Mon, 9 Oct 2023 15:49:27 -0500 Subject: [PATCH 0712/1148] mscoree: Add work-around for Grotesque Tactics. CW-Bug-Id: #22841 --- dlls/mscoree/metahost.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c index 6327a338596..8dd77e287b3 100644 --- a/dlls/mscoree/metahost.c +++ b/dlls/mscoree/metahost.c @@ -1906,6 +1906,12 @@ static MonoAssembly* CDECL wine_mono_assembly_preload_hook_v2_fn(MonoAssemblyNam "321360", /* Primal Carnage: Extinction */ "namespace ContentBrowser { class IContentBrowserBackendInterface {} class Package {} } " }, + { + "DockPanel", + "DockPanel.dll", + "46450", /* Grotesque Tactics: Evil Heroes */ + "namespace WeifenLuo.WinFormsUI { class DockPanel {} }" + }, }; for (i = 0; i < ARRAY_SIZE(assembly_hacks); ++i) From 59bd1f5bde1d230f7bea620cf5811d770a4072c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 10 Oct 2023 18:02:42 +0200 Subject: [PATCH 0713/1148] server: Send WM_WINE_SETCURSOR with the last thread input cursor handle. Which may be different from the last desktop cursor handle. This makes the behavior better match the old winex11 behavior, which queried the current thread input cursor handle on every mouse move to sync it with X11, although it contradicts MSDN documentation which states that the cursor handle is global. This fixes the X11 cursor being visible in "Deus Ex: GOTY Edition". CW-Bug-Id: #22838 --- server/queue.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/server/queue.c b/server/queue.c index 549d08cbcf5..084416e3776 100644 --- a/server/queue.c +++ b/server/queue.c @@ -514,13 +514,22 @@ static void queue_cursor_message( struct desktop *desktop, user_handle_t win, un queue_hardware_message( desktop, msg, 1 ); } -static int update_desktop_cursor_window( struct desktop *desktop, user_handle_t win ) +static int update_desktop_cursor_window( struct desktop *desktop, user_handle_t win, int x, int y ) { int updated = win != desktop->cursor_win; user_handle_t handle = desktop->cursor_handle; desktop->cursor_win = win; if (updated) { + struct thread *thread; + + if ((thread = window_thread_from_point( win, x, y ))) + { + struct thread_input *input = thread->queue->input; + if (input) handle = input->shared->cursor_count < 0 ? 0 : input->shared->cursor; + release_object( thread ); + } + /* 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 ); @@ -547,7 +556,7 @@ static int update_desktop_cursor_pos( struct desktop *desktop, user_handle_t win 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; + if (update_desktop_cursor_window( desktop, win, x, y )) updated = 1; return updated; } From 8a671f6fd6bf86d508ebe4ed829a06d9040d87cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 10 Oct 2023 18:47:30 +0200 Subject: [PATCH 0714/1148] fixup! server: Get rid of the global cursor structure. --- server/winstation.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server/winstation.c b/server/winstation.c index d9617b5509e..57668f0a025 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -262,6 +262,7 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned desktop->foreground_input = NULL; desktop->users = 0; desktop->cursor_win = 0; + desktop->cursor_handle = 0; desktop->last_press_alt = 0; list_add_tail( &winstation->desktops, &desktop->entry ); list_init( &desktop->hotkeys ); From c3448cdbb9d05b27d00924e55e01bda8a4adeb4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 11 Oct 2023 11:39:32 +0200 Subject: [PATCH 0715/1148] amend! win32u: Support multiple touches in WM_TOUCH message. win32u: Support multiple touches in WM_TOUCH message. CW-Bug-Id: #21796 CW-Bug-Id: #22849 --- dlls/win32u/defwnd.c | 58 +++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index eb1c082ff20..2015515b94f 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -2956,15 +2956,22 @@ LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, case WM_POINTERUP: case WM_POINTERUPDATE: { + TOUCHINPUT *touches, *end, *touch, *match = NULL; struct touchinput_thread_data *thread_data; - TOUCHINPUT *touches, *end, *touch; UINT i; if (!NtUserIsTouchWindow( hwnd, NULL )) return 0; if (!(thread_data = touch_input_thread_data())) return 0; - for (touches = thread_data->current, end = touches + ARRAY_SIZE(thread_data->current), touch = touches; touch < end; touch++) - if (!touch->dwID || touch->dwID == GET_POINTERID_WPARAM( wparam )) break; + touches = thread_data->current; + end = touches + ARRAY_SIZE(thread_data->current); + for (touch = touches; touch < end && touch->dwID; touch++) + { + if (touch->dwID == GET_POINTERID_WPARAM( wparam )) match = touch; + touch->dwFlags &= ~TOUCHEVENTF_DOWN; + touch->dwFlags |= TOUCHEVENTF_MOVE; + } + if (match) touch = match; if (touch == end || (msg != WM_POINTERDOWN && !touch->dwID)) { @@ -2973,36 +2980,33 @@ LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, break; } - while (end > touch && !(end - 1)->dwID) end--; + while (end > (touch + 1) && !(end - 1)->dwID) end--; - if (msg == WM_POINTERUP) - { - while (++touch < end) *(touch - 1) = *touch; - memset( touch - 1, 0, sizeof(*touch) ); - end--; - } - else - { - touch->x = LOWORD( lparam ) * 100; - touch->y = HIWORD( lparam ) * 100; - touch->hSource = WINE_MOUSE_HANDLE; - touch->dwID = GET_POINTERID_WPARAM( wparam ); - touch->dwFlags = TOUCHEVENTF_NOCOALESCE | TOUCHEVENTF_PALM; - if (msg == WM_POINTERDOWN) touch->dwFlags |= TOUCHEVENTF_DOWN; - if (msg == WM_POINTERUP) touch->dwFlags |= TOUCHEVENTF_UP; - if (msg == WM_POINTERUPDATE) touch->dwFlags |= TOUCHEVENTF_MOVE; - if (IS_POINTER_PRIMARY_WPARAM( wparam )) touch->dwFlags |= TOUCHEVENTF_PRIMARY; - touch->dwMask = 0; - touch->dwTime = NtGetTickCount(); - touch->dwExtraInfo = 0; - touch->cxContact = 0; - touch->cyContact = 0; - } + touch->x = LOWORD( lparam ) * 100; + touch->y = HIWORD( lparam ) * 100; + touch->hSource = WINE_MOUSE_HANDLE; + touch->dwID = GET_POINTERID_WPARAM( wparam ); + touch->dwFlags = 0; + if (msg == WM_POINTERUP) touch->dwFlags |= TOUCHEVENTF_UP; + if (msg == WM_POINTERDOWN) touch->dwFlags |= TOUCHEVENTF_INRANGE | TOUCHEVENTF_DOWN; + if (msg == WM_POINTERUPDATE) touch->dwFlags |= TOUCHEVENTF_INRANGE | TOUCHEVENTF_MOVE; + if (IS_POINTER_PRIMARY_WPARAM( wparam )) touch->dwFlags |= TOUCHEVENTF_PRIMARY; + touch->dwMask = 0; + touch->dwTime = NtGetTickCount(); + touch->dwExtraInfo = 0; + touch->cxContact = 0; + touch->cyContact = 0; i = thread_data->index++ % ARRAY_SIZE(thread_data->history); memcpy( thread_data->history + i, thread_data->current, sizeof(thread_data->current) ); send_message( hwnd, WM_TOUCH, MAKELONG(end - touches, 0), (LPARAM)i ); + + if (msg == WM_POINTERUP) + { + while (++touch < end) *(touch - 1) = *touch; + memset( touch - 1, 0, sizeof(*touch) ); + } break; } From 939c40cbb6326907e4d064ebb39e1aceef2cf37f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 10 Oct 2023 14:27:58 -0600 Subject: [PATCH 0716/1148] win32u: Don't fail update_display_cache_from_registry() if there is no adapters. CW-Bug-Id: #22813 --- dlls/win32u/sysparams.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 6630250b667..5a0f34fbc32 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1662,7 +1662,6 @@ static BOOL update_display_cache_from_registry(void) struct monitor *monitor, *monitor2; HANDLE mutex = NULL; NTSTATUS status; - BOOL ret; /* If user driver did initialize the registry, then exit */ if (!video_key && !(video_key = reg_open_key( NULL, devicemap_video_keyW, @@ -1720,11 +1719,15 @@ static BOOL update_display_cache_from_registry(void) } } - if ((ret = !list_empty( &adapters ) && !list_empty( &monitors ))) - last_query_display_time = key.LastWriteTime.QuadPart; + if (list_empty( &adapters )) + { + WARN( "No adapters found.\n" ); + assert( list_empty( &monitors )); + } + else if (!list_empty( &monitors )) last_query_display_time = key.LastWriteTime.QuadPart; pthread_mutex_unlock( &display_lock ); release_display_device_init_mutex( mutex ); - return ret; + return TRUE; } static BOOL update_display_cache( BOOL force ) @@ -2454,6 +2457,8 @@ static DEVMODEW *get_display_settings( const WCHAR *devname, const DEVMODEW *dev struct adapter *adapter; BOOL ret; + if (list_empty( &adapters )) return NULL; + /* allocate an extra mode for easier iteration */ if (!(displays = calloc( list_count( &adapters ) + 1, sizeof(DEVMODEW) ))) return NULL; mode = displays; From e229aae4af013589f7a8d06b219956019953e690 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 10 Oct 2023 13:08:12 +0300 Subject: [PATCH 0717/1148] xinput1_3: Hold module reference while the hid thread is running. We have no means of stopping the thread from DllMain on DLL_PROCESS_DETACH without having a race condition - waiting on the thread itself is not an option due to the loader lock. The best we can do is just never unload the DLL as long as the thread is active which is forever. This fixes crashes in Mighty Switch Force! Collection while controllers are unplugged - the game loads and frees xinput in a loop. (cherry picked from commit 10a0a8eabc5882bf045ca08d4fc4768533e9a8dc) --- dlls/xinput1_3/main.c | 52 +++++++++++-------------------------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index 192d75413fa..a18f63545cc 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -121,8 +121,6 @@ static struct xinput_controller controllers[XUSER_MAX_COUNT] = static HMODULE xinput_instance; static HANDLE start_event; -static HANDLE stop_event; -static HANDLE done_event; static HANDLE update_event; static HANDLE steam_overlay_event; static HANDLE steam_keyboard_event; @@ -569,23 +567,6 @@ static void update_controller_list(void) SetupDiDestroyDeviceInfoList(set); } -static void stop_update_thread(void) -{ - int i; - - SetEvent(stop_event); - WaitForSingleObject(done_event, INFINITE); - - CloseHandle(start_event); - CloseHandle(stop_event); - CloseHandle(done_event); - CloseHandle(update_event); - CloseHandle(steam_overlay_event); - CloseHandle(steam_keyboard_event); - - for (i = 0; i < XUSER_MAX_COUNT; i++) controller_destroy(&controllers[i], FALSE); -} - static LONG sign_extend(ULONG value, const HIDP_VALUE_CAPS *caps) { UINT sign = 1 << (caps->BitSize - 1); @@ -711,9 +692,9 @@ static LRESULT CALLBACK xinput_devnotify_wndproc(HWND hwnd, UINT msg, WPARAM wpa static DWORD WINAPI hid_update_thread_proc(void *param) { - struct xinput_controller *devices[XUSER_MAX_COUNT + 2]; - HANDLE events[XUSER_MAX_COUNT + 2]; - DWORD i, count = 2, ret = WAIT_TIMEOUT; + struct xinput_controller *devices[XUSER_MAX_COUNT + 1]; + HANDLE events[XUSER_MAX_COUNT + 1]; + DWORD i, count = 1, ret = WAIT_TIMEOUT; DEV_BROADCAST_DEVICEINTERFACE_W filter = { .dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE_W), @@ -745,7 +726,7 @@ static DWORD WINAPI hid_update_thread_proc(void *param) { if (ret == count) while (PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)) DispatchMessageW(&msg); if (ret == WAIT_TIMEOUT) update_controller_list(); - if (ret < count - 2) read_controller_state(devices[ret]); + if (ret < count - 1) read_controller_state(devices[ret]); count = 0; for (i = 0; i < XUSER_MAX_COUNT; ++i) @@ -761,23 +742,26 @@ static DWORD WINAPI hid_update_thread_proc(void *param) LeaveCriticalSection(&controllers[i].crit); } events[count++] = update_event; - events[count++] = stop_event; } - while ((ret = MsgWaitForMultipleObjectsEx(count, events, 2000, QS_ALLINPUT, MWMO_ALERTABLE)) < count - 1 || - ret == count || ret == WAIT_TIMEOUT); + while ((ret = MsgWaitForMultipleObjectsEx(count, events, 2000, QS_ALLINPUT, MWMO_ALERTABLE)) <= count || + ret == WAIT_TIMEOUT); + + ERR("wait failed in the update thread, ret %lu, error %lu\n", ret, GetLastError()); UnregisterDeviceNotification(notif); DestroyWindow(hwnd); UnregisterClassW(cls.lpszClassName, xinput_instance); - if (ret != count - 1) ERR("update thread exited unexpectedly, ret %lu\n", ret); - SetEvent(done_event); - return ret; + FreeLibraryAndExitThread(xinput_instance, ret); } static BOOL WINAPI start_update_thread_once( INIT_ONCE *once, void *param, void **context ) { HANDLE thread; + HMODULE module; + + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (void*)hid_update_thread_proc, &module)) + WARN("Failed to increase module's reference count, error: %lu\n", GetLastError()); steam_overlay_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_GameOverlayActivated"); steam_keyboard_event = CreateEventA(NULL, TRUE, FALSE, "__wine_steamclient_KeyboardActivated"); @@ -785,12 +769,6 @@ static BOOL WINAPI start_update_thread_once( INIT_ONCE *once, void *param, void start_event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!start_event) ERR("failed to create start event, error %lu\n", GetLastError()); - stop_event = CreateEventA(NULL, FALSE, FALSE, NULL); - if (!stop_event) ERR("failed to create stop event, error %lu\n", GetLastError()); - - done_event = CreateEventA(NULL, FALSE, FALSE, NULL); - if (!done_event) ERR("failed to create done event, error %lu\n", GetLastError()); - update_event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!update_event) ERR("failed to create update event, error %lu\n", GetLastError()); @@ -838,10 +816,6 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) xinput_instance = inst; DisableThreadLibraryCalls(inst); break; - case DLL_PROCESS_DETACH: - if (reserved) break; - stop_update_thread(); - break; } return TRUE; } From 520ac239a90989471941389821ea9d29cdc751fa Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Thu, 12 Oct 2023 10:25:02 +0300 Subject: [PATCH 0718/1148] amend! xinput1_3: Hold module reference while the hid thread is running. xinput1_3: Hold module reference while the hid thread is running. We have no means of stopping the thread from DllMain on DLL_PROCESS_DETACH without having a race condition - waiting on the thread itself is not an option due to the loader lock. The best we can do is just never unload the DLL as long as the thread is active which is forever. This fixes crashes in Mighty Switch Force! Collection while controllers are unplugged - the game loads and frees xinput in a loop. (cherry picked from commit 10a0a8eabc5882bf045ca08d4fc4768533e9a8dc) CW-Bug-Id: #22824 From fc55fe75ec58d2ab3fac72763ad821c896e5b276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 30 May 2023 23:25:27 +0200 Subject: [PATCH 0719/1148] mf: Clear queued topologies on session shutdown. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Programs might expect objects inside the queued toplogy to be freed, before they eventually call release on the session itself. This fixes reference leaks to stored objects in queued topology nodes, even when IMFMediaSession_Shutdown() was called. Signed-off-by: Bernhard Kölbl (cherry picked from commit bea37208fe2fc0ebff0d3c0fce5f98ce8f98b88b) --- dlls/mf/session.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 0d3ed620a0c..67e054a61bc 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -2264,6 +2264,7 @@ static HRESULT WINAPI mfsession_Shutdown(IMFMediaSession *iface) IMFPresentationClock_Release(session->clock); session->clock = NULL; session_clear_presentation(session); + session_clear_queued_topologies(session); session_submit_simple_command(session, SESSION_CMD_SHUTDOWN); } LeaveCriticalSection(&session->cs); From 2a1301f17302d4540e80e970e6304b09361b2cfa Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 12 Oct 2023 11:28:45 +0100 Subject: [PATCH 0720/1148] mf: Don't make stream sink shutdown dependent on IMFActivate presence in node. Base on Bernhard's upstream MR: https://gitlab.winehq.org/wine/wine/-/merge_requests/3067 This fixes a media engine leak after it's shut down. --- dlls/mf/session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 67e054a61bc..2b7b4361c61 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -785,7 +785,7 @@ static void session_shutdown_current_topology(struct media_session *session) WARN("Failed to shut down activation object for the sink, hr %#lx.\n", hr); IMFActivate_Release(activate); } - else if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink))) + if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink))) { if (SUCCEEDED(IMFStreamSink_GetMediaSink(stream_sink, &sink))) { From 0bf092301e8a82498092bba54ad92ad8a864f32c Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 11 Oct 2023 18:21:50 +0100 Subject: [PATCH 0721/1148] mf: Don't leak dropped samples. --- dlls/mf/sar.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/dlls/mf/sar.c b/dlls/mf/sar.c index 86af2a34f11..970063497b1 100644 --- a/dlls/mf/sar.c +++ b/dlls/mf/sar.c @@ -1347,13 +1347,6 @@ static HRESULT stream_queue_sample(struct audio_renderer *renderer, IMFSample *s sample_frames = sample_len / renderer->frame_size; - if (!(object = calloc(1, sizeof(*object)))) - return E_OUTOFMEMORY; - - object->type = OBJECT_TYPE_SAMPLE; - object->u.sample.sample = sample; - IMFSample_AddRef(object->u.sample.sample); - if (FAILED(hr = IMFSample_GetSampleTime(sample, &time))) { WARN("Failed to get sample time, hr %#lx.\n", hr); @@ -1372,6 +1365,12 @@ static HRESULT stream_queue_sample(struct audio_renderer *renderer, IMFSample *s FIXME("Dropping sample %p, time %I64u, clocktime %I64u, systime %I64u.\n", sample, time, clocktime, systime); else { + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->type = OBJECT_TYPE_SAMPLE; + object->u.sample.sample = sample; + IMFSample_AddRef(object->u.sample.sample); list_add_tail(&renderer->queue, &object->entry); renderer->queued_frames += sample_frames; } From 7940050635c1de9636194b59845e07ee7be49842 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 12 Oct 2023 21:38:46 -0600 Subject: [PATCH 0722/1148] ntdll: HACK: Avoid unitialized Dr7 in RtlRaiseException on x64. CW-Bug-Id: #22848 --- dlls/ntdll/signal_x86_64.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index f41a09f9ca4..3cb6e09736c 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -1553,6 +1553,8 @@ __ASM_GLOBAL_FUNC( RtlRaiseException, "movq 0x4f8(%rsp),%rax\n\t" /* return address */ "movq %rax,0xf8(%rdx)\n\t" /* context->Rip */ "movq %rax,0x10(%rcx)\n\t" /* rec->ExceptionAddress */ + "xor %rax,%rax\n\t" + "movq %rax,0x70(%rdx)\n\t" /* Context->Dr7 */ "movl $1,%r8d\n\t" "movq %gs:(0x30),%rax\n\t" /* Teb */ "movq 0x60(%rax),%rax\n\t" /* Peb */ From bab53884d690cd441774e0a451f7c25b22a92458 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 12 Jun 2023 10:10:27 +0100 Subject: [PATCH 0723/1148] winepulse: Don't overwrite the result in the insufficient buffer case. Fixes a regression caused by 13fa7a57df0d. (cherry picked from commit 2586652e1674213208f17402f15805730f1d1c8f) --- dlls/winepulse.drv/pulse.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index a16f0a2f1e1..429b50e6ec2 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -2368,8 +2368,7 @@ static NTSTATUS pulse_get_prop_value(void *args) if (strcmp(params->device, dev->pulse_name)) continue; if (IsEqualPropertyKey(*params->prop, devicepath_key)) { - if (!get_device_path(dev, params)) - break; + get_device_path(dev, params); return STATUS_SUCCESS; } else if (IsEqualGUID(¶ms->prop->fmtid, &PKEY_AudioEndpoint_GUID)) { switch (params->prop->pid) { From b5d37bf371bb9e388daf6c3a84ea9a01ae65f02a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 13 Oct 2023 13:34:32 +0200 Subject: [PATCH 0724/1148] winex11: Forcefully unmap the clipping window when it loses focus. Fixes cursor not being released when alt-tabbing out of an unresponsive foreground window. CW-Bug-Id: #22842 --- dlls/winex11.drv/event.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index a7d4f4a2436..5589b3b39ad 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1013,7 +1013,13 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev ) if (event->detail == NotifyPointer) { - if (!hwnd && event->window == x11drv_thread_data()->clip_window) NtUserClipCursor( NULL ); + if (!hwnd && event->window == x11drv_thread_data()->clip_window) + { + NtUserClipCursor( NULL ); + /* NtUserClipCursor will ask the foreground window to ungrab the cursor, but + * it might not be responsive, so unmap the clipping window ourselves too */ + XUnmapWindow( event->display, event->window ); + } return TRUE; } if (!hwnd) return FALSE; From c690b78915f3d03257ef4a2e465dbb68f93ff4bd Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 13 Oct 2023 18:56:48 -0600 Subject: [PATCH 0725/1148] winegstreamer: Add AVDecVideoAcceleration_H264 attribute for h264 decoder MFT. CW-Bug-Id: #22864 --- dlls/mf/tests/transform.c | 3 +++ dlls/winegstreamer/h264_decoder.c | 6 ++++++ include/codecapi.h | 2 ++ 3 files changed, 11 insertions(+) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index a0df24bab6e..2d79881983b 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -44,6 +44,8 @@ #include "initguid.h" +#include "codecapi.h" + DEFINE_GUID(DMOVideoFormat_RGB24,D3DFMT_R8G8B8,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70); DEFINE_GUID(DMOVideoFormat_RGB32,D3DFMT_X8R8G8B8,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70); DEFINE_GUID(DMOVideoFormat_RGB555,D3DFMT_X1R5G5B5,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70); @@ -3452,6 +3454,7 @@ static void test_h264_decoder(void) ATTR_UINT32(MF_SA_D3D11_AWARE, 1), ATTR_UINT32(MFT_DECODER_EXPOSE_OUTPUT_TYPES_IN_NATIVE_ORDER, 0, .todo = TRUE), /* more H264 decoder specific attributes from CODECAPI */ + ATTR_UINT32(AVDecVideoAcceleration_H264, 1), {0}, }; static const DWORD input_width = 120, input_height = 248; diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 1b1646a9ed3..6a686b93381 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -26,6 +26,10 @@ #include "wine/debug.h" +#include "initguid.h" + +#include "codecapi.h" + WINE_DEFAULT_DEBUG_CHANNEL(mfplat); WINE_DECLARE_DEBUG_CHANNEL(winediag); @@ -887,6 +891,8 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) goto failed; if (FAILED(hr = IMFAttributes_SetUINT32(decoder->attributes, &MF_SA_D3D11_AWARE, TRUE))) goto failed; + if (FAILED(hr = IMFAttributes_SetUINT32(decoder->attributes, &AVDecVideoAcceleration_H264, TRUE))) + goto failed; { const char *sgi; diff --git a/include/codecapi.h b/include/codecapi.h index 9719389b081..a9fe7c9de3f 100644 --- a/include/codecapi.h +++ b/include/codecapi.h @@ -61,4 +61,6 @@ enum eAVEncH264VLevel eAVEncH264VLevel5_2 = 52 }; +DEFINE_GUID(AVDecVideoAcceleration_H264, 0xf7db8a2f, 0x4f48, 0x4ee8, 0xae, 0x31, 0x8b, 0x6e, 0xbe, 0x55, 0x8a, 0xe2); + #endif /* __CODECAPI_H */ From 063a412c986c27bdfc62afa11b4ef63b763d90c4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 13 Oct 2023 20:55:55 -0600 Subject: [PATCH 0726/1148] Revert "secur32: Get rid of schannel free handle list." This reverts commit 513d371c76b45e383a20e05a64c8c75dc52d5b0b. CW-Bug-Id: #22864 --- dlls/secur32/schannel.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 8129c17ad17..a1c641cd34e 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -68,6 +68,7 @@ struct schan_context }; static struct schan_handle *schan_handle_table; +static struct schan_handle *schan_free_handles; static SIZE_T schan_handle_table_size; static SIZE_T schan_handle_count; static SRWLOCK handle_table_lock = SRWLOCK_INIT; @@ -81,17 +82,23 @@ static DWORD config_default_disabled_protocols; static ULONG_PTR schan_alloc_handle(void *object, enum schan_handle_type type) { struct schan_handle *handle; - ULONG_PTR index = SCHAN_INVALID_HANDLE, i; + ULONG_PTR index = SCHAN_INVALID_HANDLE; AcquireSRWLockExclusive(&handle_table_lock); - for (i = 0; i < schan_handle_table_size; ++i) + if (schan_free_handles) { - handle = &schan_handle_table[i]; + /* Use a free handle */ + handle = schan_free_handles; if (handle->type != SCHAN_HANDLE_FREE) - continue; + { + ERR("Handle %p is in the free list, but has type %#x.\n", handle, handle->type); + goto done; + } + index = schan_free_handles - schan_handle_table; + schan_free_handles = handle->object; handle->object = object; handle->type = type; - index = i; + goto done; } if (!(schan_handle_count < schan_handle_table_size)) @@ -139,8 +146,9 @@ static void *schan_free_handle(ULONG_PTR handle_idx, enum schan_handle_type type } object = handle->object; - handle->object = NULL; + handle->object = schan_free_handles; handle->type = SCHAN_HANDLE_FREE; + schan_free_handles = handle; done: ReleaseSRWLockExclusive(&handle_table_lock); From 5b4222030dc2672be18a0ef11e356815a85d6bc2 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 13 Oct 2023 21:55:21 -0600 Subject: [PATCH 0727/1148] winepulse.drv: Change device description to "Pulse Audio". CW-Bug-Id: #22864 --- dlls/winepulse.drv/pulse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 429b50e6ec2..b62ce566cc3 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -806,8 +806,8 @@ static NTSTATUS pulse_test_connect(void *args) list_init(&g_phys_speakers); list_init(&g_phys_sources); - pulse_add_device(&g_phys_speakers, NULL, 0, Speakers, 0, "", "PulseAudio"); - pulse_add_device(&g_phys_sources, NULL, 0, Microphone, 0, "", "PulseAudio"); + pulse_add_device(&g_phys_speakers, NULL, 0, Speakers, 0, "", "Pulse Audio"); + pulse_add_device(&g_phys_sources, NULL, 0, Microphone, 0, "", "Pulse Audio"); o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL); if (o) { From 7ed54be62c930d3b8a75a378d44fdef3c6cfe470 Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Fri, 6 Oct 2023 19:33:28 +0200 Subject: [PATCH 0728/1148] winevulkan: Update to VK spec version 1.3.267. (cherry picked from commit 17d9841d86b9f5a7863a45cdfc7a9a2153115621) --- 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 73681dcaff1..b08caeb973f 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.260" +VK_XML_VERSION = "1.3.267" WINE_VK_VERSION = (1, 3) # Filenames to create. From a89fbde2d57c2a04408b87ea8328d390bf5620e8 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 16 Oct 2023 13:36:09 +0300 Subject: [PATCH 0729/1148] winevulkan: Update vk.xml to 1.3.267. --- dlls/winevulkan/vk.xml | 609 +++++++++++++++++++++++++++++++++-------- 1 file changed, 488 insertions(+), 121 deletions(-) diff --git a/dlls/winevulkan/vk.xml b/dlls/winevulkan/vk.xml index 898bd9f967e..a696de6f012 100644 --- a/dlls/winevulkan/vk.xml +++ b/dlls/winevulkan/vk.xml @@ -175,11 +175,11 @@ 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 260 +#define VK_HEADER_VERSION 267 // 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 +#define VK_HEADER_VERSION 13 // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(VKSC_API_VARIANT, 1, 0, VK_HEADER_VERSION) @@ -190,7 +190,7 @@ branch of the member gitlab server. #ifndef VK_USE_64_BIT_PTR_DEFINES - #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) || (defined(__riscv) && __riscv_xlen == 64) #define VK_USE_64_BIT_PTR_DEFINES 1 #else #define VK_USE_64_BIT_PTR_DEFINES 0 @@ -478,6 +478,7 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkOpticalFlowUsageFlagsNV; typedef VkFlags VkOpticalFlowSessionCreateFlagsNV; typedef VkFlags VkOpticalFlowExecuteFlagsNV; + typedef VkFlags VkFrameBoundaryFlagsEXT; typedef VkFlags VkPresentScalingFlagsEXT; typedef VkFlags VkPresentGravityFlagsEXT; typedef VkFlags VkShaderCreateFlagsEXT; @@ -775,6 +776,7 @@ typedef void* MTLSharedEvent_id; + @@ -787,6 +789,9 @@ typedef void* MTLSharedEvent_id; + + + WSI extensions @@ -860,6 +865,8 @@ typedef void* MTLSharedEvent_id; + + Enumerated types in the header, but not used by the API @@ -1197,7 +1204,7 @@ typedef void* MTLSharedEvent_id; const void* pNext VkBufferCreateFlags flagsBuffer creation flags VkDeviceSize sizeSpecified in bytes - VkBufferUsageFlags usageBuffer usage flags + VkBufferUsageFlags usageBuffer usage flags VkSharingMode sharingMode uint32_t queueFamilyIndexCount const uint32_t* pQueueFamilyIndices @@ -4196,7 +4203,7 @@ typedef void* MTLSharedEvent_id; const void* pNext VkBool32 conditionalRenderingEnableWhether this secondary command buffer may be executed during an active conditional rendering - + VkStructureType sType void* pNext uint64_t externalFormat @@ -6972,10 +6979,10 @@ typedef void* MTLSharedEvent_id; VkStructureType sType - const void* pNext - uint32_t stdSPSCount + const void* pNext + uint32_t stdSPSCount const StdVideoH264SequenceParameterSet* pStdSPSs - uint32_t stdPPSCount + uint32_t stdPPSCount const StdVideoH264PictureParameterSet* pStdPPSsList of Picture Parameters associated with the spsStd, above @@ -7026,7 +7033,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkVideoEncodeH264RateControlFlagsEXT flags + VkVideoEncodeH264RateControlFlagsEXT flags uint32_t gopFrameCount uint32_t idrPeriod uint32_t consecutiveBFrameCount @@ -7110,11 +7117,11 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - uint32_t stdVPSCount + uint32_t stdVPSCount const StdVideoH265VideoParameterSet* pStdVPSs - uint32_t stdSPSCount + uint32_t stdSPSCount const StdVideoH265SequenceParameterSet* pStdSPSs - uint32_t stdPPSCount + uint32_t stdPPSCount const StdVideoH265PictureParameterSet* pStdPPSsList of Picture Parameters associated with the spsStd, above @@ -7158,7 +7165,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType const void* pNext - VkVideoEncodeH265RateControlFlagsEXT flags + VkVideoEncodeH265RateControlFlagsEXT flags uint32_t gopFrameCount uint32_t idrPeriod uint32_t consecutiveBFrameCount @@ -7766,6 +7773,18 @@ typedef void* MTLSharedEvent_id; size_t descriptorOffset uint32_t descriptorSize + + VkStructureType sType + void* pNext + VkBool32 nestedCommandBuffer + VkBool32 nestedCommandBufferRendering + VkBool32 nestedCommandBufferSimultaneousUse + + + VkStructureType sType + void* pNext + uint32_t maxCommandBufferNestingLevel + VkStructureType sType void* pNext @@ -8087,7 +8106,7 @@ typedef void* MTLSharedEvent_id; VkPipelineRobustnessImageBehaviorEXT images - VkStructureType sType + VkStructureType sType void* pNext VkPipelineRobustnessBufferBehaviorEXT defaultRobustnessStorageBuffers VkPipelineRobustnessBufferBehaviorEXT defaultRobustnessUniformBuffers @@ -8301,6 +8320,24 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 shaderCoreBuiltins + + VkStructureType sType + const void* pNext + VkFrameBoundaryFlagsEXT flags + uint64_t frameID + uint32_t imageCount + const VkImage* pImages + uint32_t bufferCount + const VkBuffer* pBuffers + uint64_t tagName + size_t tagSize + const void* pTag + + + VkStructureType sType + void* pNext + VkBool32 frameBoundary + VkStructureType sType void* pNext @@ -8381,6 +8418,18 @@ typedef void* MTLSharedEvent_id; void* pNext VkRayTracingInvocationReorderModeNV rayTracingInvocationReorderReorderingHint + + VkStructureType sType + void* pNext + VkBool32 extendedSparseAddressSpace + + + VkStructureType sType + void* pNext + VkDeviceSize extendedSparseAddressSpaceSizeTotal address space available for extended sparse allocations (bytes) + VkImageUsageFlags extendedSparseImageUsageFlagsBitfield of which image usages are supported for extended sparse allocations + VkBufferUsageFlags extendedSparseBufferUsageFlagsBitfield of which buffer usages are supported for extended sparse allocations + VkStructureType sType void* pNext @@ -8475,18 +8524,18 @@ typedef void* MTLSharedEvent_id; const VkSpecializationInfo* pSpecializationInfo - VkStructureType sType - void* pNext - VkBool32 shaderTileImageColorReadAccess - VkBool32 shaderTileImageDepthReadAccess - VkBool32 shaderTileImageStencilReadAccess + VkStructureType sType + void* pNext + VkBool32 shaderTileImageColorReadAccess + VkBool32 shaderTileImageDepthReadAccess + VkBool32 shaderTileImageStencilReadAccess - VkStructureType sType - void* pNext - VkBool32 shaderTileImageCoherentReadAccelerated - VkBool32 shaderTileImageReadSampleFromPixelRateInvocation - VkBool32 shaderTileImageReadFromHelperInvocation + VkStructureType sType + void* pNext + VkBool32 shaderTileImageCoherentReadAccelerated + VkBool32 shaderTileImageReadSampleFromPixelRateInvocation + VkBool32 shaderTileImageReadFromHelperInvocation VkStructureType sType @@ -8593,6 +8642,143 @@ typedef void* MTLSharedEvent_id; VkDeviceOrHostAddressConstAMDX infos uint64_t stride + + VkStructureType sType + void* pNext + VkBool32 cubicRangeClamp + + + VkStructureType sType + void* pNext + VkBool32 ycbcrDegamma + + + VkStructureType sType + void* pNext + VkBool32 enableYDegamma + VkBool32 enableCbCrDegamma + + + VkStructureType sType + void* pNext + VkBool32 selectableCubicWeights + + + VkStructureType sType + const void* pNext + VkCubicFilterWeightsQCOM cubicWeights + + + VkStructureType sType + const void* pNext + VkCubicFilterWeightsQCOM cubicWeights + + + VkStructureType sType + void* pNext + VkBool32 textureBlockMatch2 + + + VkStructureType sType + void* pNext + VkExtent2D maxBlockMatchWindow + + + VkStructureType sType + const void* pNext + VkExtent2D windowExtent + VkBlockMatchWindowCompareModeQCOM windowCompareMode + + + VkStructureType sType + void* pNext + VkBool32 descriptorPoolOverallocation + + + VkStructureType sType + void* pNext + VkLayeredDriverUnderlyingApiMSFT underlyingAPI + + + VkStructureType sType + void* pNext + VkBool32 externalFormatResolve + + + VkStructureType sType + void* pNext + VkBool32 nullColorAttachmentWithExternalFormatResolve + VkChromaLocation externalFormatResolveChromaOffsetX + VkChromaLocation externalFormatResolveChromaOffsetY + + + VkStructureType sType + void* pNext + VkFormat colorAttachmentFormat + + + VkStructureType sType + const void* pNext + VkBool32 lowLatencyMode + VkBool32 lowLatencyBoost + uint32_t minimumIntervalUs + + + VkStructureType sType + const void* pNext + VkSemaphore signalSemaphore + uint64_t value + + + VkStructureType sType + const void* pNext + uint64_t presentID + VkLatencyMarkerNV marker + + + VkStructureType sType + const void* pNext + VkLatencyTimingsFrameReportNV* pTimings + + + VkStructureType sType + const void* pNext + uint64_t presentID + uint64_t inputSampleTimeUs + uint64_t simStartTimeUs + uint64_t simEndTimeUs + uint64_t renderSubmitStartTimeUs + uint64_t renderSubmitEndTimeUs + uint64_t presentStartTimeUs + uint64_t presentEndTimeUs + uint64_t driverStartTimeUs + uint64_t driverEndTimeUs + uint64_t osRenderQueueStartTimeUs + uint64_t osRenderQueueEndTimeUs + uint64_t gpuRenderStartTimeUs + uint64_t gpuRenderEndTimeUs + + + VkStructureType sType + const void* pNext + VkOutOfBandQueueTypeNV queueType + + + VkStructureType sType + const void* pNext + uint64_t presentID + + + VkStructureType sType + const void* pNext + VkBool32 latencyModeEnable + + + VkStructureType sType + const void* pNext + uint32_t presentModeCount + VkPresentModeKHR* pPresentModes + @@ -9806,6 +9992,7 @@ typedef void* MTLSharedEvent_id; + @@ -10236,6 +10423,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -10315,6 +10505,7 @@ typedef void* MTLSharedEvent_id; + @@ -10357,6 +10548,8 @@ typedef void* MTLSharedEvent_id; + + @@ -10473,6 +10666,8 @@ typedef void* MTLSharedEvent_id; + + @@ -10667,6 +10862,38 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -11754,7 +11981,7 @@ typedef void* MTLSharedEvent_id; void vkCmdEndRenderPass VkCommandBuffer commandBuffer - + void vkCmdExecuteCommands VkCommandBuffer commandBuffer uint32_t commandBufferCount @@ -14497,11 +14724,11 @@ typedef void* MTLSharedEvent_id; VkShaderModuleIdentifierEXT* pIdentifier - void vkGetImageSubresourceLayout2KHR - VkDevice device - VkImage image - const VkImageSubresource2KHR* pSubresource - VkSubresourceLayout2KHR* pLayout + void vkGetImageSubresourceLayout2KHR + VkDevice device + VkImage image + const VkImageSubresource2KHR* pSubresource + VkSubresourceLayout2KHR* pLayout @@ -14680,6 +14907,36 @@ typedef void* MTLSharedEvent_id; VkDeviceAddress scratch VkDeviceAddress countInfo + + VkResult vkSetLatencySleepModeNV + VkDevice device + VkSwapchainKHR swapchain + const VkLatencySleepModeInfoNV* pSleepModeInfo + + + VkResult vkLatencySleepNV + VkDevice device + VkSwapchainKHR swapchain + const VkLatencySleepInfoNV* pSleepInfo + + + void vkSetLatencyMarkerNV + VkDevice device + VkSwapchainKHR swapchain + const VkSetLatencyMarkerInfoNV* pLatencyMarkerInfo + + + void vkGetLatencyTimingsNV + VkDevice device + VkSwapchainKHR swapchain + uint32_t* pTimingCount + VkGetLatencyMarkerInfoNV* pLatencyMarkerInfo + + + void vkQueueNotifyOutOfBandNV + VkQueue queue + const VkOutOfBandQueueTypeInfoNV* pQueueTypeInfo + @@ -16695,7 +16952,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16739,7 +16996,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16922,7 +17179,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18821,16 +19078,10 @@ typedef void* MTLSharedEvent_id; - + - - - - - - @@ -19043,7 +19294,6 @@ typedef void* MTLSharedEvent_id; - @@ -19920,7 +20170,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20087,13 +20337,13 @@ typedef void* MTLSharedEvent_id; - + - + @@ -20323,7 +20573,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20355,6 +20605,7 @@ typedef void* MTLSharedEvent_id; + @@ -20417,13 +20668,13 @@ typedef void* MTLSharedEvent_id; - + - + @@ -20470,7 +20721,12 @@ typedef void* MTLSharedEvent_id; - + + + + + + @@ -21378,10 +21634,16 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + @@ -21658,9 +21920,6 @@ typedef void* MTLSharedEvent_id; - - - @@ -21802,13 +22061,13 @@ typedef void* MTLSharedEvent_id; - + - + @@ -22098,13 +22357,19 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + - + @@ -22259,7 +22524,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22382,11 +22647,19 @@ typedef void* MTLSharedEvent_id; - + - - - + + + + + + + + + + + @@ -22395,7 +22668,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22433,7 +22706,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22444,8 +22717,8 @@ typedef void* MTLSharedEvent_id; - - + + @@ -22460,43 +22733,46 @@ typedef void* MTLSharedEvent_id; - + - + - + - + - - + + - - + + + + + - + - + - + - - + + @@ -22514,13 +22790,13 @@ typedef void* MTLSharedEvent_id; - - - + + + - - + + @@ -22703,8 +22979,9 @@ typedef void* MTLSharedEvent_id; - - + + + @@ -22808,10 +23085,14 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + @@ -22846,7 +23127,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22902,13 +23183,38 @@ typedef void* MTLSharedEvent_id; - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -22923,7 +23229,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22934,7 +23240,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23001,31 +23307,52 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + - + - - + + + + + + + + + - + - - + + + + + + - + - - + + + + + - + @@ -23089,10 +23416,13 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + @@ -23173,12 +23503,40 @@ typedef void* MTLSharedEvent_id; - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -24774,6 +25132,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -24792,6 +25153,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -25251,6 +25615,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -25334,7 +25701,7 @@ typedef void* MTLSharedEvent_id; - + @@ -25430,7 +25797,7 @@ typedef void* MTLSharedEvent_id; - + From bc295b3cf45e8cf1d9c2f2bbd878790ea2bfe43c Mon Sep 17 00:00:00 2001 From: Etaash Mathamsetty Date: Mon, 16 Oct 2023 15:33:54 -0400 Subject: [PATCH 0730/1148] winex11.drv: Ensure initialized thread data in X11DRV_get_ic. --- 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 209d63f0402..b4d675dc446 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -501,7 +501,7 @@ XIC X11DRV_get_ic( HWND hwnd ) XIC ret; if (!(data = get_win_data( hwnd ))) return 0; - x11drv_thread_data()->last_xic_hwnd = hwnd; + x11drv_init_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 ); From 577ec51b4aef0f90f03d415c5a6ee9abc2231c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 27 Sep 2023 08:24:58 +0200 Subject: [PATCH 0731/1148] winebus.sys: Append is_gamepad to the device instance id. --- dlls/winebus.sys/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 15898a23d79..d1f9e45cf02 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -190,7 +190,10 @@ static WCHAR *get_instance_id(DEVICE_OBJECT *device) WCHAR *dst; if ((dst = ExAllocatePool(PagedPool, len * sizeof(WCHAR)))) - swprintf(dst, len, L"%i&%s&%x&%i", ext->desc.version, ext->desc.serialnumber, ext->desc.uid, ext->index); + { + swprintf(dst, len, L"%u&%s&%x&%u&%u", ext->desc.version, ext->desc.serialnumber, + ext->desc.uid, ext->index, ext->desc.is_gamepad); + } return dst; } From 27529007237cdbf8c7828cd75b0786e571b912b4 Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Sat, 2 Sep 2023 23:00:29 +0200 Subject: [PATCH 0732/1148] winebus.sys: Also pass axis and button count to is_hidraw_enabled() This is useful to detect some devices which do not have a well-known PID set. Link: https://github.com/ValveSoftware/Proton/issues/6839#issuecomment-1703856375 --- dlls/winebus.sys/bus_udev.c | 11 +++++------ dlls/winebus.sys/unix_private.h | 2 +- dlls/winebus.sys/unixlib.c | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index d3c4b705968..5da979c6c5a 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1622,6 +1622,7 @@ static void udev_add_device(struct udev_device *dev, int fd) const char *subsystem; const char *devnode; int bus = 0; + int axes = -1, buttons = -1; if (!(devnode = udev_device_get_devnode(dev))) { @@ -1644,6 +1645,9 @@ static void udev_add_device(struct udev_device *dev, int fd) close(fd); return; } + + axes = count_abs_axis(fd); + buttons = count_buttons(fd, NULL); #endif get_device_subsystem_info(dev, "hid", &desc, &bus); @@ -1697,7 +1701,7 @@ static void udev_add_device(struct udev_device *dev, int fd) memcpy(desc.serialnumber, zeros, sizeof(zeros)); } - if (!is_hidraw_enabled(desc.vid, desc.pid)) + if (!is_hidraw_enabled(desc.vid, desc.pid, axes, buttons)) { TRACE("hidraw %s: deferring %s to a different backend\n", debugstr_a(devnode), debugstr_device_desc(&desc)); close(fd); @@ -1712,12 +1716,7 @@ static void udev_add_device(struct udev_device *dev, int fd) } #ifdef HAS_PROPER_INPUT_HEADER else - { - int axes=0, buttons=0; - axes = count_abs_axis(fd); - buttons = count_buttons(fd, NULL); desc.is_gamepad = (axes == 6 && buttons >= 14); - } #endif TRACE("dev %p, node %s, desc %s.\n", dev, debugstr_a(devnode), debugstr_device_desc(&desc)); diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index 87c3d4ad626..4e2b943e979 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -271,6 +271,6 @@ BOOL is_wine_blacklisted(WORD vid, WORD pid) DECLSPEC_HIDDEN; BOOL is_dualshock4_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; BOOL is_dualsense_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; BOOL is_logitech_g920(WORD vid, WORD pid) DECLSPEC_HIDDEN; -BOOL is_hidraw_enabled(WORD vid, WORD pid) DECLSPEC_HIDDEN; +BOOL is_hidraw_enabled(WORD vid, WORD pid, INT axes, INT buttons) DECLSPEC_HIDDEN; #endif /* __WINEBUS_UNIX_PRIVATE_H */ diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c index d4adc78f6ec..cbfc13cf578 100644 --- a/dlls/winebus.sys/unixlib.c +++ b/dlls/winebus.sys/unixlib.c @@ -105,7 +105,7 @@ static BOOL is_fanatec_pedals(WORD vid, WORD pid) return FALSE; } -BOOL is_hidraw_enabled(WORD vid, WORD pid) +BOOL is_hidraw_enabled(WORD vid, WORD pid, INT axes, INT buttons) { const char *enabled = getenv("PROTON_ENABLE_HIDRAW"); char needle[16]; From 229aea4cabda7491936bd9a3c3b286af13f3c666 Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Tue, 29 Aug 2023 19:09:31 +0200 Subject: [PATCH 0733/1148] winebus.sys: Enable hidraw for VKB Gladiator NXT EVO. These devices ship with 128 buttons by default. For game compatibility, the VKB Windows app can be used to change the HID descriptor to show only 32 buttons and have up to 4 virtual devices instead. These devices can also show up as a mouse or keyboard and send proper HID events for that configuration - not tested with this commit. The Linux input layer gets really confused by these devices as the HID descriptor spans multiple ranges of different device type event codes. Hopefully, winebus.sys hidraw mode can work around this. Also needs udev rules to enable hidraw access. Known limits: - Elite Dangerous: 32 buttons - Star Citizen: 50 buttons - some other games: 64 buttons Signed-off-by: Kai Krakow --- dlls/winebus.sys/unixlib.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c index cbfc13cf578..32c1f9a00d9 100644 --- a/dlls/winebus.sys/unixlib.c +++ b/dlls/winebus.sys/unixlib.c @@ -105,6 +105,19 @@ static BOOL is_fanatec_pedals(WORD vid, WORD pid) return FALSE; } +static BOOL is_vkb_controller(WORD vid, WORD pid, INT buttons) +{ + if (vid != 0x231D) return FALSE; + + /* comes with 128 buttons in the default configuration */ + if (buttons == 128) return TRUE; + + /* if customized, less than 128 buttons may be shown, decide by PID */ + if (pid == 0x0200) return TRUE; /* VKBsim Gladiator EVO Right Grip */ + if (pid == 0x0201) return TRUE; /* VKBsim Gladiator EVO Left Grip */ + return FALSE; +} + BOOL is_hidraw_enabled(WORD vid, WORD pid, INT axes, INT buttons) { const char *enabled = getenv("PROTON_ENABLE_HIDRAW"); @@ -115,6 +128,7 @@ BOOL is_hidraw_enabled(WORD vid, WORD pid, INT axes, INT buttons) if (is_thrustmaster_hotas(vid, pid)) return TRUE; if (is_simucube_wheel(vid, pid)) return TRUE; if (is_fanatec_pedals(vid, pid)) return TRUE; + if (is_vkb_controller(vid, pid, buttons)) return TRUE; sprintf(needle, "0x%04x/0x%04x", vid, pid); if (enabled) return strcasestr(enabled, needle) != NULL; From 2b2f87f7774cb001085b231cfd3c5016f28181b7 Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Fri, 1 Sep 2023 05:12:21 +0200 Subject: [PATCH 0734/1148] winebus.sys: Enable hidraw for Virpil Constellation ALPHA-R. Fixes: https://github.com/ValveSoftware/Proton/issues/6839 --- dlls/winebus.sys/unixlib.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c index 32c1f9a00d9..1e08f4a1ead 100644 --- a/dlls/winebus.sys/unixlib.c +++ b/dlls/winebus.sys/unixlib.c @@ -118,6 +118,18 @@ static BOOL is_vkb_controller(WORD vid, WORD pid, INT buttons) return FALSE; } +static BOOL is_virpil_controller(WORD vid, WORD pid, INT buttons) +{ + if (vid != 0x3344) return FALSE; + + /* comes with 31 buttons in the default configuration, or 128 max */ + if ((buttons == 31) || (buttons == 128)) return TRUE; + + /* if customized, arbitrary amount of buttons may be shown, decide by PID */ + if (pid == 0x412f) return TRUE; /* Virpil Constellation ALPHA-R */ + return FALSE; +} + BOOL is_hidraw_enabled(WORD vid, WORD pid, INT axes, INT buttons) { const char *enabled = getenv("PROTON_ENABLE_HIDRAW"); @@ -129,6 +141,7 @@ BOOL is_hidraw_enabled(WORD vid, WORD pid, INT axes, INT buttons) if (is_simucube_wheel(vid, pid)) return TRUE; if (is_fanatec_pedals(vid, pid)) return TRUE; if (is_vkb_controller(vid, pid, buttons)) return TRUE; + if (is_virpil_controller(vid, pid, buttons)) return TRUE; sprintf(needle, "0x%04x/0x%04x", vid, pid); if (enabled) return strcasestr(enabled, needle) != NULL; From 969203fe4ee8cf4e1a110433319c7d648ba68d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 09:47:56 +0200 Subject: [PATCH 0735/1148] amend! winex11.drv: Ensure initialized thread data in X11DRV_get_ic. winex11.drv: Ensure initialized thread data in X11DRV_get_ic. Link: https://github.com/ValveSoftware/wine/pull/201 From cd3d3fb7163862bb626d6f690b10a316009779c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 09:48:27 +0200 Subject: [PATCH 0736/1148] amend! winebus.sys: Append is_gamepad to the device instance id. winebus.sys: Append is_gamepad to the device instance id. Link: https://github.com/ValveSoftware/wine/pull/197 From b3d49e939c0f4e584a882dbeb8d8e13cd0e020f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 09:48:33 +0200 Subject: [PATCH 0737/1148] amend! winebus.sys: Also pass axis and button count to is_hidraw_enabled() winebus.sys: Also pass axis and button count to is_hidraw_enabled() This is useful to detect some devices which do not have a well-known PID set. Link: https://github.com/ValveSoftware/Proton/issues/6839#issuecomment-1703856375 Link: https://github.com/ValveSoftware/wine/pull/197 From 244511ac4b97818bac1f68eced9e180eaf17032d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 09:48:38 +0200 Subject: [PATCH 0738/1148] amend! winebus.sys: Enable hidraw for VKB Gladiator NXT EVO. winebus.sys: Enable hidraw for VKB Gladiator NXT EVO. These devices ship with 128 buttons by default. For game compatibility, the VKB Windows app can be used to change the HID descriptor to show only 32 buttons and have up to 4 virtual devices instead. These devices can also show up as a mouse or keyboard and send proper HID events for that configuration - not tested with this commit. The Linux input layer gets really confused by these devices as the HID descriptor spans multiple ranges of different device type event codes. Hopefully, winebus.sys hidraw mode can work around this. Also needs udev rules to enable hidraw access. Known limits: - Elite Dangerous: 32 buttons - Star Citizen: 50 buttons - some other games: 64 buttons Signed-off-by: Kai Krakow Link: https://github.com/ValveSoftware/wine/pull/197 From 221c8e3893b8f1b08a435a2717687543dc2cbb0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 09:48:43 +0200 Subject: [PATCH 0739/1148] amend! winebus.sys: Enable hidraw for Virpil Constellation ALPHA-R. winebus.sys: Enable hidraw for Virpil Constellation ALPHA-R. Fixes: https://github.com/ValveSoftware/Proton/issues/6839 Link: https://github.com/ValveSoftware/wine/pull/197 From 637902dcd157b3f4df4c497de7ac755115c81c99 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 17 Oct 2023 20:11:43 -0600 Subject: [PATCH 0740/1148] wine.inf: Substitute Times New Roman for Verdana. CW-Bug-Id: #22875 --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index e31c426ca04..f0395ecc4ca 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -569,6 +569,7 @@ HKLM,%FontsNT%,"Times New Roman (TrueType)",,"times.ttf" HKLM,%FontsNT%,"Webdings (TrueType)",,"webdings.ttf" HKLM,%FontsNT%,"Wingdings (TrueType)",,"wingdings.ttf" HKCU,Software\Wine\Fonts\Replacements,"Palatino Linotype",,"Times New Roman" +HKCU,Software\Wine\Fonts\Replacements,"Verdana",,"Times New Roman" [MCI] HKLM,%Mci32Str%,"AVIVideo",,"mciavi32.dll" From bd91aa348b41b661d5b339181d1ccda13c28fb89 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Wed, 25 Jan 2023 15:20:43 +0100 Subject: [PATCH 0741/1148] advapi32: Bump random buffer size. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54364 (cherry picked from commit 369e0cfae6052937ef6b90e8c9fe916ea6765d9b) --- dlls/advapi32/crypt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/advapi32/crypt.c b/dlls/advapi32/crypt.c index 22bcf03bba7..c3f927cfb60 100644 --- a/dlls/advapi32/crypt.c +++ b/dlls/advapi32/crypt.c @@ -2400,7 +2400,7 @@ static CRITICAL_SECTION_DEBUG random_debug = }; static CRITICAL_SECTION random_cs = { &random_debug, -1, 0, 0, 0, 0 }; -#define MAX_CPUS 128 +#define MAX_CPUS 256 static char random_buf[sizeof(SYSTEM_INTERRUPT_INFORMATION) * MAX_CPUS]; static ULONG random_len; static ULONG random_pos; From 8b9ee1b7a7da3929ae988e6a44ee48bb22aa1333 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 18 Oct 2023 18:03:34 +0200 Subject: [PATCH 0742/1148] winegstreamer: Enable new media source for Phantom Doctrine. --- dlls/winegstreamer/mf_handler.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/mf_handler.c b/dlls/winegstreamer/mf_handler.c index bd1bd810c96..0776b424da3 100644 --- a/dlls/winegstreamer/mf_handler.c +++ b/dlls/winegstreamer/mf_handler.c @@ -174,7 +174,8 @@ static HRESULT async_create_object_complete(struct async_create_object *async, if (async->flags & MF_RESOLUTION_MEDIASOURCE) { const char *sgi, *env = getenv("WINE_NEW_MEDIA_SOURCE"); - if (!env && (sgi = getenv("SteamGameId")) && (!strcmp(sgi, "692850"))) env = "1"; + if (!env && (sgi = getenv("SteamGameId")) && + (!strcmp(sgi, "692850") || !strcmp(sgi, "559100"))) env = "1"; if (!async->stream) hr = media_source_create_from_url(async->url, (IMFMediaSource **)&object); From ad7dd630b6c39b7b4c47400d44e5a147a34d8d60 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 19 Oct 2023 13:40:34 +0100 Subject: [PATCH 0743/1148] Revert "mf: Don't make stream sink shutdown dependent on IMFActivate presence in node." This reverts commit 2a1301f17302d4540e80e970e6304b09361b2cfa. This commit triggers a crash when playing videos in AVPro, reverting and looking for the correct fix for the media engine leak. --- dlls/mf/session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 2b7b4361c61..67e054a61bc 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -785,7 +785,7 @@ static void session_shutdown_current_topology(struct media_session *session) WARN("Failed to shut down activation object for the sink, hr %#lx.\n", hr); IMFActivate_Release(activate); } - if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink))) + else if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink))) { if (SUCCEEDED(IMFStreamSink_GetMediaSink(stream_sink, &sink))) { From 08e47ef7ce5f3287bff88480e6d6bb684947b0b0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 19 Oct 2023 18:46:13 -0600 Subject: [PATCH 0744/1148] ntdll: Define heap block's BLOCK_FLAG_LFH as 0x80. CW-Bug-Id: #22393 --- dlls/ntdll/heap.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dlls/ntdll/heap.c b/dlls/ntdll/heap.c index 9c1ccb559c0..2ec7ed1c982 100644 --- a/dlls/ntdll/heap.c +++ b/dlls/ntdll/heap.c @@ -104,12 +104,12 @@ C_ASSERT( sizeof(struct block) == 8 ); #define BLOCK_FLAG_PREV_FREE 0x02 #define BLOCK_FLAG_FREE_LINK 0x03 #define BLOCK_FLAG_LARGE 0x04 -#define BLOCK_FLAG_LFH 0x08 /* block is handled by the LFH frontend */ -#define BLOCK_FLAG_USER_INFO 0x10 /* user flags up to 0xf0 */ -#define BLOCK_FLAG_USER_MASK 0xf0 +#define BLOCK_FLAG_LFH 0x80 /* block is handled by the LFH frontend */ +#define BLOCK_FLAG_USER_INFO 0x08 /* user flags bits 3-6 */ +#define BLOCK_FLAG_USER_MASK 0x78 -#define BLOCK_USER_FLAGS( heap_flags ) (((heap_flags) >> 4) & BLOCK_FLAG_USER_MASK) -#define HEAP_USER_FLAGS( block_flags ) (((block_flags) & BLOCK_FLAG_USER_MASK) << 4) +#define BLOCK_USER_FLAGS( heap_flags ) (((heap_flags) >> 5) & BLOCK_FLAG_USER_MASK) +#define HEAP_USER_FLAGS( block_flags ) (((block_flags) & BLOCK_FLAG_USER_MASK) << 5) /* entry to link free blocks in free lists */ @@ -1949,7 +1949,7 @@ static NTSTATUS heap_allocate_block_lfh( struct heap *heap, ULONG flags, SIZE_T if ((block = find_free_bin_block( heap, flags, block_size, bin ))) { block_set_type( block, BLOCK_TYPE_USED ); - block_set_flags( block, ~BLOCK_FLAG_LFH, BLOCK_USER_FLAGS( flags ) ); + block_set_flags( block, (BYTE)~BLOCK_FLAG_LFH, BLOCK_USER_FLAGS( flags ) ); block->tail_size = block_size - sizeof(*block) - size; initialize_block( block, 0, size, flags ); mark_block_tail( block, flags ); @@ -1974,7 +1974,7 @@ static NTSTATUS heap_free_block_lfh( struct heap *heap, ULONG flags, struct bloc i = block_get_group_index( block ); valgrind_make_writable( block, sizeof(*block) ); block_set_type( block, BLOCK_TYPE_FREE ); - block_set_flags( block, ~BLOCK_FLAG_LFH, BLOCK_FLAG_FREE ); + block_set_flags( block, (BYTE)~BLOCK_FLAG_LFH, BLOCK_FLAG_FREE ); mark_block_free( block + 1, (char *)block + block_size - (char *)(block + 1), flags ); /* if this was the last used block in a group and GROUP_FLAG_FREE was set */ From 6137349ff137d9380b46731b171ff201a6cff8dd Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Oct 2023 14:03:20 -0600 Subject: [PATCH 0745/1148] dnsapi: Handle IP address as DNS name in DnsQuery_UTF8(). CW-Bug-Id: #22891 --- dlls/dnsapi/query.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/dlls/dnsapi/query.c b/dlls/dnsapi/query.c index 90a3a3e9950..09d2e9c7e62 100644 --- a/dlls/dnsapi/query.c +++ b/dlls/dnsapi/query.c @@ -21,11 +21,15 @@ #include #include "windef.h" #include "winbase.h" +#include "winternl.h" #include "winerror.h" #include "winnls.h" #include "windns.h" #include "nb30.h" #include "ws2def.h" +#include "in6addr.h" +#include "inaddr.h" +#include "ip2string.h" #include "wine/debug.h" #include "dnsapi.h" @@ -169,6 +173,8 @@ DNS_STATUS WINAPI DnsQuery_UTF8( const char *name, WORD type, DWORD options, voi unsigned char answer[4096]; DWORD len = sizeof(answer); struct query_params query_params = { name, type, options, answer, &len }; + DNS_RECORDA *r; + const char *end; TRACE( "(%s, %s, %#lx, %p, %p, %p)\n", debugstr_a(name), debugstr_type( type ), options, servers, result, reserved ); @@ -176,6 +182,39 @@ DNS_STATUS WINAPI DnsQuery_UTF8( const char *name, WORD type, DWORD options, voi if (!name || !result) return ERROR_INVALID_PARAMETER; + if (type == DNS_TYPE_A) + { + struct in_addr addr; + + if (!RtlIpv4StringToAddressA(name, TRUE, &end, &addr) && !*end && (r = calloc(1, sizeof(*r)))) + { + ret = ERROR_SUCCESS; + r->Data.A.IpAddress = addr.s_addr; + r->wDataLength = sizeof(r->Data.A); + } + } + else if (type == DNS_TYPE_AAAA) + { + struct in6_addr addr; + + if (!RtlIpv6StringToAddressA(name, &end, &addr) && !*end && (r = calloc(1, sizeof(*r)))) + { + ret = ERROR_SUCCESS; + memcpy(&r->Data.AAAA.Ip6Address, &addr, sizeof(r->Data.AAAA.Ip6Address)); + r->wDataLength = sizeof(r->Data.AAAA); + } + } + if (!ret) + { + r->wType = type; + r->dwTtl = 604800; + r->pName = strdup(name); + r->Flags.S.Reserved = 0x20; + r->Flags.S.CharSet = DnsCharSetUtf8; + *result = r; + return ret; + } + if ((ret = RESOLV_CALL( set_serverlist, servers ))) return ret; ret = RESOLV_CALL( query, &query_params ); From 740f66635052033d4543f71558b34f847e21fe16 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 24 Oct 2023 12:17:28 -0600 Subject: [PATCH 0746/1148] amd_ags_x64,atiadll,wbemprox,win32u: Bump AMD driver version. --- dlls/amd_ags_x64/amd_ags_x64_main.c | 4 ++-- dlls/atiadlxx/atiadlxx_main.c | 6 +++--- dlls/wbemprox/builtin.c | 4 ++-- dlls/win32u/sysparams.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index ca0fb573e3e..1ee87f77a1e 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -23,8 +23,8 @@ 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.8.1"; +static const char driver_version[] = "23.19.02-230831a-396538C-AMD-Software-Adrenalin-Edition"; +static const char radeon_version[] = "23.10.2"; enum amd_ags_version { diff --git a/dlls/atiadlxx/atiadlxx_main.c b/dlls/atiadlxx/atiadlxx_main.c index 8c52dbdc601..8fdd2e55d02 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.23.02-230720a-394204C-AMD-Software-Adrenalin-Edition", + "23.19.02-230831a-396538C-AMD-Software-Adrenalin-Edition", "", "http://support.amd.com/drivers/xml/driver_09_us.xml", }; static const ADLVersionsInfoX2 version2 = { - "23.10.23.02-230720a-394204C-AMD-Software-Adrenalin-Edition", + "23.19.02-230831a-396538C-AMD-Software-Adrenalin-Edition", "", - "23.8.1", + "23.10.2", "http://support.amd.com/drivers/xml/driver_09_us.xml", }; diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c index c98f1c6c975..18cea97bfe1 100644 --- a/dlls/wbemprox/builtin.c +++ b/dlls/wbemprox/builtin.c @@ -4161,8 +4161,8 @@ static enum fill_status fill_videocontroller( struct table *table, const struct rec->current_verticalres = vres; rec->description = wcsdup( name ); rec->device_id = L"VideoController1"; - rec->driverdate = L"20230420000000.000000-000"; - rec->driverversion = L"31.0.14051.5006"; + rec->driverdate = L"20230831000000.000000-000"; + rec->driverversion = L"31.0.21902.5"; rec->installeddriver = get_videocontroller_installeddriver( desc.VendorId ); rec->name = wcsdup( name ); rec->pnpdevice_id = get_videocontroller_pnpdeviceid( &desc ); diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 5a0f34fbc32..78eb933cc69 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1381,7 +1381,7 @@ static void add_gpu( const struct gdi_gpu *gpu, void *param ) break; /* AMD */ case 0x1002: - sprintf( buffer, "31.0.14051.5006" ); + sprintf( buffer, "31.0.21902.5" ); break; /* Nvidia */ case 0x10de: From ee9e5fcb0b27c52f955f2ea435f4117b6efd7ec5 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Mon, 23 Oct 2023 10:06:17 -0500 Subject: [PATCH 0747/1148] mscoree: Update Wine Mono to 8.1.0. (cherry picked from commit 9c1ffb545f350743e71cdfcc0dae5e98324d10ee) --- 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 3f0b8b2c03e..a2145a7fdb8 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.1" +#define MONO_VERSION "8.1.0" #if defined(__i386__) || defined(__x86_64__) #define MONO_ARCH "x86" -#define MONO_SHA "27240085f5b4f8b175ff0479f3d6cc4309b00adbb386c00ba1fddd30f0367976" +#define MONO_SHA "0ed3ec533aef79b2f312155931cf7b1080009ac0c5b4c2bcfeb678ac948e0810" #else #define MONO_ARCH "" #define MONO_SHA "???" diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h index ca8ba343be3..dcbb531770b 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.1" +#define WINE_MONO_VERSION "8.1.0" /* Mono embedding */ typedef struct _MonoDomain MonoDomain; diff --git a/tools/gitlab/test.yml b/tools/gitlab/test.yml index 09387a26439..852ae72d8ef 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.1 + MONO_VER: 8.1.0 cache: - key: wine-gecko-$GECKO_VER paths: From 8855b754068988adab22c50aa8c3a84075c2cd4f Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Wed, 25 Oct 2023 23:24:37 +0300 Subject: [PATCH 0748/1148] HACK: faudio: Support WMA3. Needs to be upstreamed. Helps with METAL GEAR SOLID 3: Snake Eater - Master Collection Version crashing. Transcoding doesn't quite work yet as we seem to lose some caps on the way to protonaudioconverter. --- libs/faudio/src/FAudio.c | 7 ++++++- libs/faudio/src/FAudio_internal.c | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libs/faudio/src/FAudio.c b/libs/faudio/src/FAudio.c index 559837c410b..7d34b666535 100644 --- a/libs/faudio/src/FAudio.c +++ b/libs/faudio/src/FAudio.c @@ -293,7 +293,8 @@ uint32_t FAudio_CreateSourceVoice( if ( pSourceFormat->wFormatTag == FAUDIO_FORMAT_PCM || pSourceFormat->wFormatTag == FAUDIO_FORMAT_IEEE_FLOAT || - pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO2 ) + pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO2 || + pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO3 ) { FAudioWaveFormatExtensible *fmtex = (FAudioWaveFormatExtensible*) audio->pMalloc( sizeof(FAudioWaveFormatExtensible) @@ -320,6 +321,10 @@ uint32_t FAudio_CreateSourceVoice( { FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_WMAUDIO2, sizeof(FAudioGUID)); } + else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_WMAUDIO3) + { + FAudio_memcpy(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_WMAUDIO3, sizeof(FAudioGUID)); + } (*ppSourceVoice)->src.format = &fmtex->Format; } else if (pSourceFormat->wFormatTag == FAUDIO_FORMAT_MSADPCM) diff --git a/libs/faudio/src/FAudio_internal.c b/libs/faudio/src/FAudio_internal.c index 85a0ca588f0..765b1fa0899 100644 --- a/libs/faudio/src/FAudio_internal.c +++ b/libs/faudio/src/FAudio_internal.c @@ -105,6 +105,7 @@ static const char *get_wformattag_string(const FAudioWaveFormatEx *fmt) FMT_STRING(IEEE_FLOAT) FMT_STRING(XMAUDIO2) FMT_STRING(WMAUDIO2) + FMT_STRING(WMAUDIO3) FMT_STRING(EXTENSIBLE) #undef FMT_STRING return "UNKNOWN!"; From 3ae123564b03cf01b5fab97153d5c698ae8ef101 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 25 Oct 2023 14:23:20 -0600 Subject: [PATCH 0749/1148] shell32: Use SearchPathW() for %l/%L in SHELL_ArgifyW(). CW-Bug-Id: #22879 --- dlls/shell32/shlexec.c | 13 +++++++++---- dlls/shell32/tests/shlexec.c | 27 ++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/dlls/shell32/shlexec.c b/dlls/shell32/shlexec.c index 2d1830ad838..2d869b765d6 100644 --- a/dlls/shell32/shlexec.c +++ b/dlls/shell32/shlexec.c @@ -77,7 +77,7 @@ static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lp BOOL found_p1 = FALSE; PWSTR res = out; PCWSTR cmd; - DWORD used = 0; + DWORD size, used = 0; TRACE("%p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt), debugstr_w(lpFile), pidl, args); @@ -164,11 +164,16 @@ static BOOL SHELL_ArgifyW(WCHAR* out, int len, const WCHAR* fmt, const WCHAR* lp case 'l': case 'L': if (lpFile) { - used += lstrlenW(lpFile); + if ((size = SearchPathW(NULL, lpFile, L".exe", ARRAY_SIZE(xlpFile), xlpFile, NULL) + && size <= ARRAY_SIZE(xlpFile))) + cmd = xlpFile; + else + cmd = lpFile; + used += lstrlenW(cmd); if (used < len) { - lstrcpyW(res, lpFile); - res += lstrlenW(lpFile); + lstrcpyW(res, cmd); + res += lstrlenW(cmd); } } found_p1 = TRUE; diff --git a/dlls/shell32/tests/shlexec.c b/dlls/shell32/tests/shlexec.c index b84a96a2434..0ed59215248 100644 --- a/dlls/shell32/tests/shlexec.c +++ b/dlls/shell32/tests/shlexec.c @@ -1624,7 +1624,7 @@ static void test_argify(void) static void test_filename(void) { - char filename[MAX_PATH + 20]; + char filename[MAX_PATH + 20], curdir[MAX_PATH]; const filename_tests_t* test; char* c; INT_PTR rc; @@ -1635,6 +1635,31 @@ static void test_filename(void) return; } + GetCurrentDirectoryA(sizeof(curdir), curdir); + + SetCurrentDirectoryA(tmpdir); + rc=shell_execute("QuotedLowerL", "simple.shlexec", NULL, NULL); + if (rc > 32) + rc=33; + okShell(rc == 33, "failed: rc=%Id err=%lu\n", rc, GetLastError()); + okChildInt("argcA", 5); + okChildString("argvA3", "QuotedLowerL"); + strcpy(filename, tmpdir); + strcat(filename, "\\simple.shlexec"); + okChildPath("argvA4", filename); + + rc=shell_execute("QuotedUpperL", "simple.shlexec", NULL, NULL); + if (rc > 32) + rc=33; + okShell(rc == 33, "failed: rc=%Id err=%lu\n", rc, GetLastError()); + okChildInt("argcA", 5); + okChildString("argvA3", "QuotedUpperL"); + strcpy(filename, tmpdir); + strcat(filename, "\\simple.shlexec"); + okChildPath("argvA4", filename); + + SetCurrentDirectoryA(curdir); + test=filename_tests; while (test->basename) { From a735619cbd73721ec52413a7b66294e95f6805f7 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 27 Oct 2023 19:35:17 +0100 Subject: [PATCH 0750/1148] mfmediaengine: Be a bit more conservative with locks in engine Shutdown. During engine shutdown we acquire engine lock first, then locks of its constituents (e.g. sample grabbers); whereas normally the order is the other way around (e.g. timer callback -> acquire sample grabber lock -> OnProcessSample callback -> engine lock). This is deadlock prone. With this commit, engine lock is released before we shutdown the inner media session. --- dlls/mfmediaengine/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index bbe7248c145..ed9bd75f797 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -2201,6 +2201,7 @@ static HRESULT WINAPI media_engine_GetVideoAspectRatio(IMFMediaEngineEx *iface, static HRESULT WINAPI media_engine_Shutdown(IMFMediaEngineEx *iface) { struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + IMFMediaSession *session; HRESULT hr = S_OK; TRACE("%p.\n", iface); @@ -2212,10 +2213,13 @@ static HRESULT WINAPI media_engine_Shutdown(IMFMediaEngineEx *iface) { media_engine_set_flag(engine, FLAGS_ENGINE_SHUT_DOWN, TRUE); media_engine_clear_presentation(engine); - IMFMediaSession_Shutdown(engine->session); + IMFMediaSession_AddRef(engine->session); + session = engine->session; } LeaveCriticalSection(&engine->cs); + IMFMediaSession_Shutdown(session); + IMFMediaSession_Release(session); return hr; } From 7170754230ed10cfc76130d9d2c0e12123dd1577 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 27 Oct 2023 19:37:03 +0100 Subject: [PATCH 0751/1148] Revert "Revert "mf: Don't make stream sink shutdown dependent on IMFActivate presence in node."" This reverts commit ad7dd630b6c39b7b4c47400d44e5a147a34d8d60. I have identified and fixed the deadlock unveiled by the quoted fix, so reverting the revert. --- dlls/mf/session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 67e054a61bc..2b7b4361c61 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -785,7 +785,7 @@ static void session_shutdown_current_topology(struct media_session *session) WARN("Failed to shut down activation object for the sink, hr %#lx.\n", hr); IMFActivate_Release(activate); } - else if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink))) + if (SUCCEEDED(topology_node_get_object(node, &IID_IMFStreamSink, (void **)&stream_sink))) { if (SUCCEEDED(IMFStreamSink_GetMediaSink(stream_sink, &sink))) { From c701ddda006e3ded4f6a2b5d6d35a33f77f6a0d7 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 27 Oct 2023 19:48:07 +0100 Subject: [PATCH 0752/1148] amend! Revert "Revert "mf: Don't make stream sink shutdown dependent on IMFActivate presence in node."" Revert "Revert "mf: Don't make stream sink shutdown dependent on IMFActivate presence in node."" This reverts commit ad7dd630b6c39b7b4c47400d44e5a147a34d8d60. I have identified and fixed the deadlock unveiled by the quoted fix, so reverting the revert. CW-Bug-Id: #22798 From b4f03ce16929e49209a98d3a66097afaa6508c87 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 27 Oct 2023 19:48:40 +0100 Subject: [PATCH 0753/1148] amend! mfmediaengine: Be a bit more conservative with locks in engine Shutdown. mfmediaengine: Be a bit more conservative with locks in engine Shutdown. During engine shutdown we acquire engine lock first, then locks of its constituents (e.g. sample grabbers); whereas normally the order is the other way around (e.g. timer callback -> acquire sample grabber lock -> OnProcessSample callback -> engine lock). This is deadlock prone. With this commit, engine lock is released before we shutdown the inner media session. CW-Bug-Id: #22798 From 1182ef740346c62b76992f425593fa72e23203a7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 30 Oct 2023 10:48:12 -0600 Subject: [PATCH 0754/1148] win32u: Support DisplayConfigGetDeviceInfo(DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO) based on Gamescope setting. CW-Bug-Id: #22912 --- dlls/win32u/sysparams.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 78eb933cc69..a771b1fbc02 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -6101,11 +6101,36 @@ NTSTATUS WINAPI NtUserDisplayConfigGetDeviceInfo( DISPLAYCONFIG_DEVICE_INFO_HEAD return STATUS_NOT_SUPPORTED; } + case DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO: + { + DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO *info = (DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO *)packet; + const char *env; + + FIXME( "DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO semi-stub.\n" ); + + if (packet->size < sizeof(*info)) + return STATUS_INVALID_PARAMETER; + + info->advancedColorSupported = 0; + info->advancedColorEnabled = 0; + info->wideColorEnforced = 0; + info->advancedColorForceDisabled = 0; + info->colorEncoding = DISPLAYCONFIG_COLOR_ENCODING_RGB; + info->bitsPerColorChannel = 8; + if ((env = getenv("DXVK_HDR")) && *env == '1') + { + TRACE( "HDR is enabled.\n" ); + info->advancedColorSupported = 1; + info->advancedColorEnabled = 1; + info->bitsPerColorChannel = 10; + } + + return STATUS_SUCCESS; + } case DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE: case DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE: case DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION: case DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION: - case DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO: case DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE: case DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL: default: From 37babf65d536102e438f6820faa48aa68e0330b4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 30 Oct 2023 17:54:13 -0600 Subject: [PATCH 0755/1148] ntdll: Handle LFH blocks allocated in large blocks in heap_validate_ptr(). Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55842 CW-Bug-Id: #22393 --- dlls/ntdll/heap.c | 62 +++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/dlls/ntdll/heap.c b/dlls/ntdll/heap.c index 2ec7ed1c982..7c69539be52 100644 --- a/dlls/ntdll/heap.c +++ b/dlls/ntdll/heap.c @@ -1053,17 +1053,17 @@ static NTSTATUS heap_free_large( struct heap *heap, ULONG flags, struct block *b } -/*********************************************************************** - * find_large_block - */ -static BOOL find_large_block( const struct heap *heap, const struct block *block ) +static ARENA_LARGE *find_arena_large( const struct heap *heap, const struct block *block, BOOL heap_walk ) { ARENA_LARGE *arena; LIST_FOR_EACH_ENTRY( arena, &heap->large_list, ARENA_LARGE, entry ) - if (block == &arena->block) return TRUE; + { + if (contains( &arena->block, arena->block_size, block, 1 )) + return !heap_walk || block == &arena->block ? arena : NULL; + } - return FALSE; + return NULL; } static BOOL validate_large_block( const struct heap *heap, const struct block *block ) @@ -1223,11 +1223,35 @@ static BOOL validate_free_block( const struct heap *heap, const SUBHEAP *subheap static BOOL validate_used_block( const struct heap *heap, const SUBHEAP *subheap, const struct block *block, unsigned int expected_block_type ) { - const char *err = NULL, *base = subheap_base( subheap ), *commit_end = subheap_commit_end( subheap ); + const char *err = NULL, *base = NULL, *commit_end; DWORD flags = heap->flags; const struct block *next; + ARENA_LARGE *arena_large; int i; + if (subheap) + { + base = subheap_base( subheap ); + commit_end = subheap_commit_end( subheap ); + } + else if ((arena_large = find_arena_large( heap, block, FALSE ))) + { + if (!validate_large_block( heap, &arena_large->block )) return FALSE; + if (block == &arena_large->block) return TRUE; + + if (block_get_flags( block ) & BLOCK_FLAG_LFH + && contains( &arena_large->block + 1, arena_large->data_size, block, 1 )) + { + base = (const char *)(&arena_large->block + 1); + commit_end = base + arena_large->data_size; + } + } + if (!base) + { + WARN( "heap %p, ptr %p: block region not found.\n", heap, block + 1 ); + return FALSE; + } + if ((ULONG_PTR)(block + 1) % BLOCK_ALIGN) err = "invalid block BLOCK_ALIGN"; else if (block_get_type( block ) != BLOCK_TYPE_USED && block_get_type( block ) != BLOCK_TYPE_DEAD) @@ -1240,9 +1264,8 @@ static BOOL validate_used_block( const struct heap *heap, const SUBHEAP *subheap err = "invalid block size"; else if (block->tail_size > block_get_size( block ) - sizeof(*block)) err = "invalid block unused size"; - else if ((next = next_block( subheap, block )) && (block_get_flags( next ) & BLOCK_FLAG_PREV_FREE) && - /* LFH blocks do not use BLOCK_FLAG_PREV_FREE or back pointer */ - !(block_get_flags( block ) & BLOCK_FLAG_LFH)) + else if (!(block_get_flags( block ) & BLOCK_FLAG_LFH) /* LFH blocks do not use BLOCK_FLAG_PREV_FREE or back pointer */ + && (next = next_block( subheap, block )) && (block_get_flags( next ) & BLOCK_FLAG_PREV_FREE)) err = "invalid next block flags"; else if (block_get_flags( block ) & BLOCK_FLAG_PREV_FREE) { @@ -1283,20 +1306,8 @@ static BOOL validate_used_block( const struct heap *heap, const SUBHEAP *subheap static BOOL heap_validate_ptr( const struct heap *heap, const void *ptr ) { const struct block *block = (struct block *)ptr - 1; - const SUBHEAP *subheap; - - if (!(subheap = find_subheap( heap, block, FALSE ))) - { - if (!find_large_block( heap, block )) - { - WARN("heap %p, ptr %p: block region not found\n", heap, ptr ); - return FALSE; - } - return validate_large_block( heap, block ); - } - - return validate_used_block( heap, subheap, block, BLOCK_TYPE_USED ); + return validate_used_block( heap, find_subheap( heap, block, FALSE ), block, BLOCK_TYPE_USED ); } static BOOL heap_validate( const struct heap *heap ) @@ -2453,7 +2464,7 @@ static NTSTATUS heap_walk_blocks( const struct heap *heap, const SUBHEAP *subhea static NTSTATUS heap_walk( const struct heap *heap, struct rtl_heap_entry *entry ) { const char *data = entry->lpData; - const ARENA_LARGE *large = NULL; + const ARENA_LARGE *large; const struct block *block; const struct list *next; const SUBHEAP *subheap; @@ -2464,9 +2475,8 @@ static NTSTATUS heap_walk( const struct heap *heap, struct rtl_heap_entry *entry else if (entry->wFlags & RTL_HEAP_ENTRY_BUSY) block = (struct block *)data - 1; else block = (struct block *)(data - sizeof(struct list)) - 1; - if (find_large_block( heap, block )) + if ((large = find_arena_large( heap, block, TRUE ))) { - large = CONTAINING_RECORD( block, ARENA_LARGE, block ); next = &large->entry; } else if ((subheap = find_subheap( heap, block, TRUE ))) From f6c1a78909f93b364aa4f90e77feb30683dee203 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 30 Oct 2023 19:43:51 -0600 Subject: [PATCH 0756/1148] ntdll: Fix pending free block validation in heap_validate() for LFH blocks. CW-Bug-Id: #22393 --- dlls/ntdll/heap.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/dlls/ntdll/heap.c b/dlls/ntdll/heap.c index 7c69539be52..70df6815ef1 100644 --- a/dlls/ntdll/heap.c +++ b/dlls/ntdll/heap.c @@ -1346,15 +1346,11 @@ static BOOL heap_validate( const struct heap *heap ) { if (!(block = heap->pending_free[i])) break; - subheap = find_subheap( heap, block, FALSE ); - if (!subheap) + if (!validate_used_block( heap, find_subheap( heap, block, FALSE ), block, BLOCK_TYPE_DEAD )) { - ERR( "heap %p: cannot find valid subheap for delayed freed block %p\n", heap, block ); - if (TRACE_ON(heap)) heap_dump( heap ); + ERR( "heap %p: failed to to validate delayed free block %p\n", heap, block ); return FALSE; } - - if (!validate_used_block( heap, subheap, block, BLOCK_TYPE_DEAD )) return FALSE; } for (; i < MAX_FREE_PENDING; i++) From b289187d81465a2d4c29a125123807bca851231c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 30 Oct 2023 20:31:58 -0600 Subject: [PATCH 0757/1148] winmm: Fix pszSound allocation in PlaySound_Alloc(). CW-Bug-Id: #22393 --- dlls/winmm/playsound.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/winmm/playsound.c b/dlls/winmm/playsound.c index d844bf9b0ab..314eb7fbe37 100644 --- a/dlls/winmm/playsound.c +++ b/dlls/winmm/playsound.c @@ -233,8 +233,9 @@ static WINE_PLAYSOUND* PlaySound_Alloc(const void* pszSound, HMODULE hmod, else { UNICODE_STRING usBuffer; - RtlCreateUnicodeStringFromAsciiz(&usBuffer, pszSound); - wps->pszSound = usBuffer.Buffer; + if (!RtlCreateUnicodeStringFromAsciiz(&usBuffer, pszSound)) goto oom_error; + wps->pszSound = wcsdup(usBuffer.Buffer); + HeapFree(GetProcessHeap(), 0, usBuffer.Buffer); if (!wps->pszSound) goto oom_error; wps->bAlloc = TRUE; } From 9b1f375674e9e72511b0d045d00a10d49cc57c1f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 30 Oct 2023 22:53:38 -0600 Subject: [PATCH 0758/1148] Revert "winmm: Fix pszSound allocation in PlaySound_Alloc()." This reverts commit b289187d81465a2d4c29a125123807bca851231c. --- dlls/winmm/playsound.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dlls/winmm/playsound.c b/dlls/winmm/playsound.c index 314eb7fbe37..d844bf9b0ab 100644 --- a/dlls/winmm/playsound.c +++ b/dlls/winmm/playsound.c @@ -233,9 +233,8 @@ static WINE_PLAYSOUND* PlaySound_Alloc(const void* pszSound, HMODULE hmod, else { UNICODE_STRING usBuffer; - if (!RtlCreateUnicodeStringFromAsciiz(&usBuffer, pszSound)) goto oom_error; - wps->pszSound = wcsdup(usBuffer.Buffer); - HeapFree(GetProcessHeap(), 0, usBuffer.Buffer); + RtlCreateUnicodeStringFromAsciiz(&usBuffer, pszSound); + wps->pszSound = usBuffer.Buffer; if (!wps->pszSound) goto oom_error; wps->bAlloc = TRUE; } From 0c1e130d6415dce0daa1c3cca72db5601c1860bc Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 31 Oct 2023 17:55:00 -0600 Subject: [PATCH 0759/1148] winex11.drv: fshack: Add optional centered low res modes. CW-Bug-Id: #22802 --- dlls/winex11.drv/fs.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/fs.c b/dlls/winex11.drv/fs.c index 1c135fbebed..c883b8f358e 100644 --- a/dlls/winex11.drv/fs.c +++ b/dlls/winex11.drv/fs.c @@ -248,6 +248,8 @@ static void modes_append( DEVMODEW *modes, UINT *mode_count, UINT *resolutions, if (modes[i].dmBitsPerPel != mode->dmBitsPerPel) continue; if (modes[i].dmDisplayFrequency != mode->dmDisplayFrequency) continue; if (modes[i].dmDisplayOrientation != mode->dmDisplayOrientation) continue; + if ((mode->dmFields & DM_DISPLAYFIXEDOUTPUT) != (modes[i].dmFields & DM_DISPLAYFIXEDOUTPUT)) continue; + if (mode->dmFields & DM_DISPLAYFIXEDOUTPUT && modes[i].dmDisplayFixedOutput != mode->dmDisplayFixedOutput) continue; return; /* The exact mode is already added, nothing to do */ } @@ -259,7 +261,7 @@ static void modes_append( DEVMODEW *modes, UINT *mode_count, UINT *resolutions, } mode->dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | - DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY; + DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY | (mode->dmFields & DM_DISPLAYFIXEDOUTPUT); mode->dmSize = sizeof(DEVMODEW); mode->dmDriverExtra = 0; mode->dmDisplayFlags = 0; @@ -274,7 +276,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, landscape; + BOOL additional_modes = FALSE, center_modes = FALSE, landscape; const char *env; *mode_count = 0; @@ -304,6 +306,11 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN else if ((env = getenv( "SteamAppId" ))) additional_modes = !strcmp( env, "979400" ); + if ((env = getenv( "WINE_CENTER_DISPLAY_MODES" ))) + center_modes = (env[0] != '0'); + else if ((env = getenv( "SteamAppId" ))) + center_modes = !strcmp( env, "359870" ); + /* Linux reports far fewer resolutions than Windows. Add modes that some games may expect. */ for (i = 0; i < ARRAY_SIZE(fs_monitor_sizes); ++i) { @@ -332,6 +339,13 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN mode.dmDisplayFrequency = 60; modes_append( *modes, mode_count, &resolutions, &mode ); } + + if (center_modes && mode.dmPelsWidth != mode_host.dmPelsWidth && mode.dmPelsHeight != mode_host.dmPelsHeight) + { + mode.dmFields |= DM_DISPLAYFIXEDOUTPUT; + mode.dmDisplayFixedOutput = DMDFO_CENTER; + modes_append( *modes, mode_count, &resolutions, &mode ); + } } for (i = 0, real_mode = real_modes; i < real_mode_count; ++i) @@ -340,8 +354,17 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN /* Don't report modes that are larger than the current mode */ if (mode.dmPelsWidth <= mode_host.dmPelsWidth && mode.dmPelsHeight <= mode_host.dmPelsHeight) + { modes_append( *modes, mode_count, &resolutions, &mode ); + if (center_modes && mode.dmPelsWidth != mode_host.dmPelsWidth && mode.dmPelsHeight != mode_host.dmPelsHeight) + { + mode.dmFields |= DM_DISPLAYFIXEDOUTPUT; + mode.dmDisplayFixedOutput = DMDFO_CENTER; + modes_append( *modes, mode_count, &resolutions, &mode ); + } + } + real_mode = NEXT_DEVMODEW(real_mode); } From 14c2a2b8ad37d514f778d19f997af9d45e8f18b4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 2 Nov 2023 10:03:35 -0600 Subject: [PATCH 0760/1148] fixup! winex11.drv: fshack: Add optional centered low res modes. CW-Bug-Id: #22802 --- dlls/winex11.drv/fs.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/dlls/winex11.drv/fs.c b/dlls/winex11.drv/fs.c index c883b8f358e..4113d1eb4e6 100644 --- a/dlls/winex11.drv/fs.c +++ b/dlls/winex11.drv/fs.c @@ -286,7 +286,14 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN /* Fullscreen hack doesn't support changing display orientations */ if (!real_settings_handler.get_modes( monitor->adapter_id, 0, &real_modes, &real_mode_count )) return; + if ((env = getenv( "WINE_CENTER_DISPLAY_MODES" ))) + center_modes = (env[0] != '0'); + else if ((env = getenv( "SteamAppId" ))) + center_modes = !strcmp( env, "359870" ); + max_count = ARRAY_SIZE(fs_monitor_sizes) * DEPTH_COUNT + real_mode_count; + if (center_modes) max_count += ARRAY_SIZE(fs_monitor_sizes) + real_mode_count; + if (!(*modes = calloc( max_count, sizeof(DEVMODEW) ))) { real_settings_handler.free_modes( real_modes ); @@ -306,11 +313,6 @@ static void monitor_get_modes( struct fs_monitor *monitor, DEVMODEW **modes, UIN else if ((env = getenv( "SteamAppId" ))) additional_modes = !strcmp( env, "979400" ); - if ((env = getenv( "WINE_CENTER_DISPLAY_MODES" ))) - center_modes = (env[0] != '0'); - else if ((env = getenv( "SteamAppId" ))) - center_modes = !strcmp( env, "359870" ); - /* Linux reports far fewer resolutions than Windows. Add modes that some games may expect. */ for (i = 0; i < ARRAY_SIZE(fs_monitor_sizes); ++i) { From ea2ee1a6e991d98a00a402ee656fe0b03cad3b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 18 Oct 2023 14:28:52 +0200 Subject: [PATCH 0761/1148] win32u: Ignore emulated mouse messages on touch-enabled windows. CW-Bug-Id: #22849 --- dlls/win32u/message.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 6a9e77f796c..6284f0452b9 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -1639,6 +1639,12 @@ static BOOL process_mouse_message( MSG *msg, UINT hw_id, ULONG_PTR extra_info, H msg->pt = point_phys_to_win_dpi( msg->hwnd, msg->pt ); SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( msg->hwnd )); + if ((extra_info & 0xffffff00) == 0xff515700 && NtUserIsTouchWindow( msg->hwnd, NULL )) + { + accept_hardware_message( hw_id ); + return FALSE; + } + if ((extra_info & 0xffffff00) != 0xff515700 && enable_mouse_in_pointer) { WORD flags = POINTER_MESSAGE_FLAG_PRIMARY; From 2095aab12e080deec6bfab66d5c2f1da74c5f464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Sep 2023 11:39:32 +0200 Subject: [PATCH 0762/1148] Revert "dmime: Play a sound in IDirectMusicPerformance8 PlaySegmentEx" This reverts commit 54f9d5977c4aa6cde75b9384e8c2e4a4b0afbbde. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 1 - dlls/dmime/performance.c | 25 ++++++------------------- dlls/dmime/segment.c | 6 ------ 3 files changed, 6 insertions(+), 26 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index d09aba02a5c..030aab50094 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -72,7 +72,6 @@ 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 03e59e95af3..5578c3e523b 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1043,26 +1043,13 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_PlaySegmentEx(IDirectMusicPer __int64 i64StartTime, IDirectMusicSegmentState **ppSegmentState, IUnknown *pFrom, IUnknown *pAudioPath) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); - IDirectMusicSegment8 *segment; - IDirectSoundBuffer *buffer; - HRESULT hr; - - 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); + IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); - if (ppSegmentState) - return create_dmsegmentstate(&IID_IDirectMusicSegmentState,(void**)ppSegmentState); - return S_OK; + 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; } static HRESULT WINAPI IDirectMusicPerformance8Impl_StopEx(IDirectMusicPerformance8 *iface, diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index b21f93bbfc6..0ea0c15c5e0 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -47,12 +47,6 @@ 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 0f3347656e5f5002744829499ef6d1a1f762b1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Sep 2023 11:39:33 +0200 Subject: [PATCH 0763/1148] Revert "dmime: Implement IDirectMusicSegment8 Download" This reverts commit 2e430ec92e2628a563135470348c6190b80fa970. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 2 - dlls/dmime/performance.c | 7 --- dlls/dmime/segment.c | 87 ++------------------------------------ 3 files changed, 3 insertions(+), 93 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 030aab50094..4159abffa99 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -71,8 +71,6 @@ 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 5578c3e523b..d69a27540d6 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -252,13 +252,6 @@ 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 0ea0c15c5e0..6bf9f3abf0c 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -37,7 +37,6 @@ typedef struct IDirectMusicSegment8Impl { PCMWAVEFORMAT wave_format; void *wave_data; int data_size; - IDirectSoundBuffer *buffer; } IDirectMusicSegment8Impl; IDirectMusicSegment8Impl *create_segment(void); @@ -91,8 +90,6 @@ 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); @@ -562,87 +559,9 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_Compose(IDirectMusicSegment8 *ifa static HRESULT WINAPI IDirectMusicSegment8Impl_Download(IDirectMusicSegment8 *iface, IUnknown *pAudioPath) { - 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; + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + FIXME("(%p, %p): stub\n", This, pAudioPath); + return S_OK; } static HRESULT WINAPI IDirectMusicSegment8Impl_Unload(IDirectMusicSegment8 *iface, From 41366ffb10c0e031b1122a3b744012ef8c82e0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Sep 2023 11:39:35 +0200 Subject: [PATCH 0764/1148] Revert "dmime: Store WAVE data when Loading." This reverts commit 4c6b451318f4b6cd0ff8093a2a334fe46374f02d. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 56 ++++---------------------------------------- 1 file changed, 4 insertions(+), 52 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 6bf9f3abf0c..bf44c5e73b3 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -33,10 +33,6 @@ 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); @@ -90,9 +86,6 @@ 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(); } @@ -825,49 +818,6 @@ 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); @@ -897,8 +847,10 @@ 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 - hr = parse_wave_form(This, stream, &riff); + else { + FIXME("WAVE form loading not implemented\n"); + hr = S_OK; + } return hr; } From b5f4c306fa9d5d5b7f940e3e23e498802feb116a Mon Sep 17 00:00:00 2001 From: Michael Stefaniuc Date: Wed, 29 Mar 2023 22:35:32 +0200 Subject: [PATCH 0765/1148] dmsynth: Don't crash when Open() gets a DMUS_PORTPARAMS7. Wrong sizeof check: pointer instead of referenced structure. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53704 (cherry picked from commit 48fdc8e08c4150c74e6c740df82518405125541d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 121d9aadf35..3631bcfe784 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -163,7 +163,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_Open(IDirectMusicSynth8 *iface, DMU modified = TRUE; params->fShare = FALSE; - if (params->dwSize >= sizeof(params)) { + if (params->dwSize >= sizeof(*params)) { if (params->dwValidParams & DMUS_PORTPARAMS_FEATURES && params->dwFeatures) { if (params->dwFeatures & ~(DMUS_PORT_FEATURE_AUDIOPATH|DMUS_PORT_FEATURE_STREAMING)) { modified = TRUE; From 0c06a734d36ab7e377e4ac623d2a154ff1b8b79b Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 29 Jun 2023 18:17:56 +1000 Subject: [PATCH 0766/1148] dmsynth: Remove DECLSPEC_HIDDEN usage. (cherry picked from commit b1a69eab126332e64860f483d99fee60acedfa8c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/dmsynth_private.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/dmsynth/dmsynth_private.h b/dlls/dmsynth/dmsynth_private.h index b0e4c66f416..c21a9e84a84 100644 --- a/dlls/dmsynth/dmsynth_private.h +++ b/dlls/dmsynth/dmsynth_private.h @@ -49,8 +49,8 @@ typedef struct IDirectMusicSynthSinkImpl IDirectMusicSynthSinkImpl; /***************************************************************************** * ClassFactory */ -extern HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj) DECLSPEC_HIDDEN; -extern HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ppobj) DECLSPEC_HIDDEN; +extern HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj); +extern HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ppobj); /***************************************************************************** * IDirectMusicSynth8Impl implementation structure @@ -83,7 +83,7 @@ struct IDirectMusicSynthSinkImpl { /********************************************************************** * Dll lifetime tracking declaration for dmsynth.dll */ -extern LONG DMSYNTH_refCount DECLSPEC_HIDDEN; +extern LONG DMSYNTH_refCount; static inline void DMSYNTH_LockModule(void) { InterlockedIncrement( &DMSYNTH_refCount ); } static inline void DMSYNTH_UnlockModule(void) { InterlockedDecrement( &DMSYNTH_refCount ); } @@ -106,6 +106,6 @@ typedef struct { #define GE(x) { &x, #x } /* returns name of given GUID */ -extern const char *debugstr_dmguid (const GUID *id) DECLSPEC_HIDDEN; +extern const char *debugstr_dmguid (const GUID *id); #endif /* __WINE_DMSYNTH_PRIVATE_H */ From 603de4d146b6cbbacdbef259af542329ae94d25d Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 29 Jun 2023 18:17:56 +1000 Subject: [PATCH 0767/1148] dmstyle: Remove DECLSPEC_HIDDEN usage. (cherry picked from commit 5a18153fcd398b5becd7ac2309eb97ea68884781) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmstyle/dmobject.h | 50 +++++++++++++++++----------------- dlls/dmstyle/dmstyle_private.h | 16 +++++------ dlls/dmstyle/dmutils.h | 4 +-- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/dlls/dmstyle/dmobject.h b/dlls/dmstyle/dmobject.h index afe721dc824..772be015c80 100644 --- a/dlls/dmstyle/dmobject.h +++ b/dlls/dmstyle/dmobject.h @@ -31,16 +31,16 @@ struct chunk_entry { const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ }; -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; + unsigned int *count, DWORD elem_size); HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) { @@ -71,21 +71,21 @@ struct dmobject { DMUS_OBJECTDESC desc; }; -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) DECLSPEC_HIDDEN; +void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); /* Generic IDirectMusicObject methods */ HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); +ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); /* Helper for IDirectMusicObject::ParseDescriptor */ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc, DWORD supported); /* Additional supported flags for dmobj_parsedescriptor. DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ @@ -93,28 +93,28 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, /* 'DMRF' (reference list) helper */ HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + IDirectMusicObject **dmobj); /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) DECLSPEC_HIDDEN; -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); +ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); +HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); /* IPersistStream methods not implemented in native */ HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class) DECLSPEC_HIDDEN; -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) DECLSPEC_HIDDEN; + CLSID *class); +HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) DECLSPEC_HIDDEN; + BOOL clear_dirty); HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size) DECLSPEC_HIDDEN; + ULARGE_INTEGER *size); /* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; -const char *debugstr_dmguid(const GUID *id) DECLSPEC_HIDDEN; -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; +const char *debugstr_chunk(const struct chunk_entry *chunk); +const char *debugstr_dmguid(const GUID *id); +void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); static inline const char *debugstr_fourcc(DWORD fourcc) { diff --git a/dlls/dmstyle/dmstyle_private.h b/dlls/dmstyle/dmstyle_private.h index 0d7ede7f00d..2807e98fe61 100644 --- a/dlls/dmstyle/dmstyle_private.h +++ b/dlls/dmstyle/dmstyle_private.h @@ -44,13 +44,13 @@ /***************************************************************************** * ClassFactory */ -extern HRESULT create_dmstyle(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmauditiontrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmchordtrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmcommandtrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmmotiftrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmmutetrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmstyletrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; +extern HRESULT create_dmstyle(REFIID riid, void **ret_iface); +extern HRESULT create_dmauditiontrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmchordtrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmcommandtrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmmotiftrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmmutetrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmstyletrack(REFIID riid, void **ret_iface); /***************************************************************************** * Auxiliary definitions @@ -64,7 +64,7 @@ typedef struct _DMUS_PRIVATE_COMMAND { /********************************************************************** * Dll lifetime tracking declaration for dmstyle.dll */ -extern LONG DMSTYLE_refCount DECLSPEC_HIDDEN; +extern LONG DMSTYLE_refCount; static inline void DMSTYLE_LockModule(void) { InterlockedIncrement( &DMSTYLE_refCount ); } static inline void DMSTYLE_UnlockModule(void) { InterlockedDecrement( &DMSTYLE_refCount ); } diff --git a/dlls/dmstyle/dmutils.h b/dlls/dmstyle/dmutils.h index dbeb7a2a978..4f50bb0bfce 100644 --- a/dlls/dmstyle/dmutils.h +++ b/dlls/dmstyle/dmutils.h @@ -30,7 +30,7 @@ typedef struct _DMUS_PRIVATE_CHUNK { /** * Parsing utilities */ -extern HRESULT IDirectMusicUtils_IPersistStream_ParseDescGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc) DECLSPEC_HIDDEN; -extern HRESULT IDirectMusicUtils_IPersistStream_ParseUNFOGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc) DECLSPEC_HIDDEN; +extern HRESULT IDirectMusicUtils_IPersistStream_ParseDescGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc); +extern HRESULT IDirectMusicUtils_IPersistStream_ParseUNFOGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc); #endif /* __WINE_DMUTILS_H */ From 99550b4888ef86539468536e3d336d16d44c2bef Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 29 Jun 2023 18:17:56 +1000 Subject: [PATCH 0768/1148] dmband: Remove DECLSPEC_HIDDEN usage. (cherry picked from commit 63d3eb77e0013bc6439054a61daaa467c65e587f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/dmband_private.h | 6 ++--- dlls/dmband/dmobject.h | 50 ++++++++++++++++++------------------ dlls/dmband/dmutils.h | 6 ++--- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dlls/dmband/dmband_private.h b/dlls/dmband/dmband_private.h index b0b26076285..b89dd08dd83 100644 --- a/dlls/dmband/dmband_private.h +++ b/dlls/dmband/dmband_private.h @@ -44,8 +44,8 @@ /***************************************************************************** * ClassFactory */ -extern HRESULT create_dmband(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmbandtrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; +extern HRESULT create_dmband(REFIID riid, void **ret_iface); +extern HRESULT create_dmbandtrack(REFIID riid, void **ret_iface); /***************************************************************************** @@ -77,7 +77,7 @@ typedef struct _DMUS_PRIVATE_BAND { /********************************************************************** * Dll lifetime tracking declaration for dmband.dll */ -extern LONG DMBAND_refCount DECLSPEC_HIDDEN; +extern LONG DMBAND_refCount; static inline void DMBAND_LockModule(void) { InterlockedIncrement( &DMBAND_refCount ); } static inline void DMBAND_UnlockModule(void) { InterlockedDecrement( &DMBAND_refCount ); } diff --git a/dlls/dmband/dmobject.h b/dlls/dmband/dmobject.h index afe721dc824..772be015c80 100644 --- a/dlls/dmband/dmobject.h +++ b/dlls/dmband/dmobject.h @@ -31,16 +31,16 @@ struct chunk_entry { const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ }; -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; + unsigned int *count, DWORD elem_size); HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) { @@ -71,21 +71,21 @@ struct dmobject { DMUS_OBJECTDESC desc; }; -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) DECLSPEC_HIDDEN; +void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); /* Generic IDirectMusicObject methods */ HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); +ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); /* Helper for IDirectMusicObject::ParseDescriptor */ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc, DWORD supported); /* Additional supported flags for dmobj_parsedescriptor. DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ @@ -93,28 +93,28 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, /* 'DMRF' (reference list) helper */ HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + IDirectMusicObject **dmobj); /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) DECLSPEC_HIDDEN; -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); +ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); +HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); /* IPersistStream methods not implemented in native */ HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class) DECLSPEC_HIDDEN; -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) DECLSPEC_HIDDEN; + CLSID *class); +HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) DECLSPEC_HIDDEN; + BOOL clear_dirty); HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size) DECLSPEC_HIDDEN; + ULARGE_INTEGER *size); /* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; -const char *debugstr_dmguid(const GUID *id) DECLSPEC_HIDDEN; -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; +const char *debugstr_chunk(const struct chunk_entry *chunk); +const char *debugstr_dmguid(const GUID *id); +void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); static inline const char *debugstr_fourcc(DWORD fourcc) { diff --git a/dlls/dmband/dmutils.h b/dlls/dmband/dmutils.h index 2f13c4b1f44..7f9b98cf310 100644 --- a/dlls/dmband/dmutils.h +++ b/dlls/dmband/dmutils.h @@ -30,8 +30,8 @@ typedef struct _DMUS_PRIVATE_CHUNK { /** * Parsing utilities */ -extern HRESULT IDirectMusicUtils_IPersistStream_ParseDescGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc) DECLSPEC_HIDDEN; -extern HRESULT IDirectMusicUtils_IPersistStream_ParseUNFOGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc) DECLSPEC_HIDDEN; -extern HRESULT IDirectMusicUtils_IPersistStream_ParseReference (LPPERSISTSTREAM iface, DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, IDirectMusicObject** ppObject) DECLSPEC_HIDDEN; +extern HRESULT IDirectMusicUtils_IPersistStream_ParseDescGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc); +extern HRESULT IDirectMusicUtils_IPersistStream_ParseUNFOGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc); +extern HRESULT IDirectMusicUtils_IPersistStream_ParseReference (LPPERSISTSTREAM iface, DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, IDirectMusicObject** ppObject); #endif /* __WINE_DMUTILS_H */ From 0b88731f2936649e3bf360168a0f1c02e921cf4b Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 29 Jun 2023 18:17:57 +1000 Subject: [PATCH 0769/1148] dmcompos: Remove DECLSPEC_HIDDEN usage. (cherry picked from commit abd3a042215986df45b12f0254a3af06eaf81b79) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmcompos/dmcompos_private.h | 10 +++---- dlls/dmcompos/dmobject.h | 50 ++++++++++++++++---------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/dlls/dmcompos/dmcompos_private.h b/dlls/dmcompos/dmcompos_private.h index bd44607d00c..c04d7da6060 100644 --- a/dlls/dmcompos/dmcompos_private.h +++ b/dlls/dmcompos/dmcompos_private.h @@ -44,15 +44,15 @@ /***************************************************************************** * ClassFactory */ -extern HRESULT create_dmchordmap(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmcomposer(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmchordmaptrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmsignposttrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; +extern HRESULT create_dmchordmap(REFIID riid, void **ret_iface); +extern HRESULT create_dmcomposer(REFIID riid, void **ret_iface); +extern HRESULT create_dmchordmaptrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmsignposttrack(REFIID riid, void **ret_iface); /********************************************************************** * Dll lifetime tracking declaration for dmcompos.dll */ -extern LONG DMCOMPOS_refCount DECLSPEC_HIDDEN; +extern LONG DMCOMPOS_refCount; static inline void DMCOMPOS_LockModule(void) { InterlockedIncrement( &DMCOMPOS_refCount ); } static inline void DMCOMPOS_UnlockModule(void) { InterlockedDecrement( &DMCOMPOS_refCount ); } diff --git a/dlls/dmcompos/dmobject.h b/dlls/dmcompos/dmobject.h index afe721dc824..772be015c80 100644 --- a/dlls/dmcompos/dmobject.h +++ b/dlls/dmcompos/dmobject.h @@ -31,16 +31,16 @@ struct chunk_entry { const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ }; -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; + unsigned int *count, DWORD elem_size); HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) { @@ -71,21 +71,21 @@ struct dmobject { DMUS_OBJECTDESC desc; }; -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) DECLSPEC_HIDDEN; +void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); /* Generic IDirectMusicObject methods */ HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); +ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); /* Helper for IDirectMusicObject::ParseDescriptor */ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc, DWORD supported); /* Additional supported flags for dmobj_parsedescriptor. DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ @@ -93,28 +93,28 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, /* 'DMRF' (reference list) helper */ HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + IDirectMusicObject **dmobj); /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) DECLSPEC_HIDDEN; -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); +ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); +HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); /* IPersistStream methods not implemented in native */ HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class) DECLSPEC_HIDDEN; -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) DECLSPEC_HIDDEN; + CLSID *class); +HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) DECLSPEC_HIDDEN; + BOOL clear_dirty); HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size) DECLSPEC_HIDDEN; + ULARGE_INTEGER *size); /* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; -const char *debugstr_dmguid(const GUID *id) DECLSPEC_HIDDEN; -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; +const char *debugstr_chunk(const struct chunk_entry *chunk); +const char *debugstr_dmguid(const GUID *id); +void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); static inline const char *debugstr_fourcc(DWORD fourcc) { From de9a93434cb93af0346d3fec207015463fd8917c Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 29 Jun 2023 18:17:57 +1000 Subject: [PATCH 0770/1148] dmusic: Remove DECLSPEC_HIDDEN usage. (cherry picked from commit 82c895fec3638aac103fe8b851945708f75a9341) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/dmobject.h | 50 ++++++++++++++++++------------------ dlls/dmusic/dmusic_private.h | 32 +++++++++++------------ 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/dlls/dmusic/dmobject.h b/dlls/dmusic/dmobject.h index afe721dc824..772be015c80 100644 --- a/dlls/dmusic/dmobject.h +++ b/dlls/dmusic/dmobject.h @@ -31,16 +31,16 @@ struct chunk_entry { const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ }; -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; + unsigned int *count, DWORD elem_size); HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) { @@ -71,21 +71,21 @@ struct dmobject { DMUS_OBJECTDESC desc; }; -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) DECLSPEC_HIDDEN; +void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); /* Generic IDirectMusicObject methods */ HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); +ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); /* Helper for IDirectMusicObject::ParseDescriptor */ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc, DWORD supported); /* Additional supported flags for dmobj_parsedescriptor. DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ @@ -93,28 +93,28 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, /* 'DMRF' (reference list) helper */ HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + IDirectMusicObject **dmobj); /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) DECLSPEC_HIDDEN; -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); +ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); +HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); /* IPersistStream methods not implemented in native */ HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class) DECLSPEC_HIDDEN; -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) DECLSPEC_HIDDEN; + CLSID *class); +HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) DECLSPEC_HIDDEN; + BOOL clear_dirty); HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size) DECLSPEC_HIDDEN; + ULARGE_INTEGER *size); /* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; -const char *debugstr_dmguid(const GUID *id) DECLSPEC_HIDDEN; -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; +const char *debugstr_chunk(const struct chunk_entry *chunk); +const char *debugstr_dmguid(const GUID *id); +void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); static inline const char *debugstr_fourcc(DWORD fourcc) { diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 18d3e079b12..1b91f4bac94 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -95,14 +95,14 @@ typedef struct instrument_articulation { */ /* CLSID */ -extern HRESULT DMUSIC_CreateDirectMusicImpl(REFIID riid, void **ret_iface, IUnknown *pUnkOuter) DECLSPEC_HIDDEN; -extern HRESULT DMUSIC_CreateDirectMusicCollectionImpl(REFIID riid, void **ppobj, IUnknown *pUnkOuter) DECLSPEC_HIDDEN; +extern HRESULT DMUSIC_CreateDirectMusicImpl(REFIID riid, void **ret_iface, IUnknown *pUnkOuter); +extern HRESULT DMUSIC_CreateDirectMusicCollectionImpl(REFIID riid, void **ppobj, IUnknown *pUnkOuter); /* Internal */ -extern HRESULT DMUSIC_CreateDirectMusicBufferImpl(LPDMUS_BUFFERDESC desc, LPVOID* ret_iface) DECLSPEC_HIDDEN; -extern HRESULT DMUSIC_CreateDirectMusicDownloadImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter) DECLSPEC_HIDDEN; -extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter) DECLSPEC_HIDDEN; -extern HRESULT DMUSIC_CreateDirectMusicInstrumentImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter) DECLSPEC_HIDDEN; +extern HRESULT DMUSIC_CreateDirectMusicBufferImpl(LPDMUS_BUFFERDESC desc, LPVOID* ret_iface); +extern HRESULT DMUSIC_CreateDirectMusicDownloadImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter); +extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter); +extern HRESULT DMUSIC_CreateDirectMusicInstrumentImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter); /***************************************************************************** * IDirectMusic8Impl implementation structure @@ -160,11 +160,11 @@ struct IDirectMusicDownloadImpl { /** Internal factory */ extern HRESULT synth_port_create(IDirectMusic8Impl *parent, DMUS_PORTPARAMS *port_params, - DMUS_PORTCAPS *port_caps, IDirectMusicPort **port) DECLSPEC_HIDDEN; + DMUS_PORTCAPS *port_caps, IDirectMusicPort **port); extern HRESULT midi_out_port_create(IDirectMusic8Impl *parent, DMUS_PORTPARAMS *port_params, - DMUS_PORTCAPS *port_caps, IDirectMusicPort **port) DECLSPEC_HIDDEN; + DMUS_PORTCAPS *port_caps, IDirectMusicPort **port); extern HRESULT midi_in_port_create(IDirectMusic8Impl *parent, DMUS_PORTPARAMS *port_params, - DMUS_PORTCAPS *port_caps, IDirectMusicPort **port) DECLSPEC_HIDDEN; + DMUS_PORTCAPS *port_caps, IDirectMusicPort **port); /***************************************************************************** * IReferenceClockImpl implementation structure @@ -215,12 +215,12 @@ static inline IDirectMusicInstrumentImpl *impl_from_IDirectMusicInstrument(IDire } /* custom :) */ -extern HRESULT IDirectMusicInstrumentImpl_CustomLoad(IDirectMusicInstrument *iface, IStream *stream) DECLSPEC_HIDDEN; +extern HRESULT IDirectMusicInstrumentImpl_CustomLoad(IDirectMusicInstrument *iface, IStream *stream); /********************************************************************** * Dll lifetime tracking declaration for dmusic.dll */ -extern LONG DMUSIC_refCount DECLSPEC_HIDDEN; +extern LONG DMUSIC_refCount; static inline void DMUSIC_LockModule(void) { InterlockedIncrement( &DMUSIC_refCount ); } static inline void DMUSIC_UnlockModule(void) { InterlockedDecrement( &DMUSIC_refCount ); } @@ -228,7 +228,7 @@ static inline void DMUSIC_UnlockModule(void) { InterlockedDecrement( &DMUSIC_ref /***************************************************************************** * Misc. */ -void dmusic_remove_port(IDirectMusic8Impl *dmusic, IDirectMusicPort *port) DECLSPEC_HIDDEN; +void dmusic_remove_port(IDirectMusic8Impl *dmusic, IDirectMusicPort *port); /* for simpler reading */ typedef struct _DMUS_PRIVATE_CHUNK { @@ -245,13 +245,13 @@ typedef struct { #define FE(x) { x, #x } /* dwPatch from MIDILOCALE */ -extern DWORD MIDILOCALE2Patch (const MIDILOCALE *pLocale) DECLSPEC_HIDDEN; +extern DWORD MIDILOCALE2Patch (const MIDILOCALE *pLocale); /* MIDILOCALE from dwPatch */ -extern void Patch2MIDILOCALE (DWORD dwPatch, LPMIDILOCALE pLocale) DECLSPEC_HIDDEN; +extern void Patch2MIDILOCALE (DWORD dwPatch, LPMIDILOCALE pLocale); /* check whether the given DWORD is even (return 0) or odd (return 1) */ -extern int even_or_odd (DWORD number) DECLSPEC_HIDDEN; +extern int even_or_odd (DWORD number); /* Dump whole DMUS_PORTPARAMS struct */ -extern void dump_DMUS_PORTPARAMS(LPDMUS_PORTPARAMS params) DECLSPEC_HIDDEN; +extern void dump_DMUS_PORTPARAMS(LPDMUS_PORTPARAMS params); #endif /* __WINE_DMUSIC_PRIVATE_H */ From 9d1c070f9b867473f0dd60a5a0008cca088ffc1e Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 29 Jun 2023 18:17:57 +1000 Subject: [PATCH 0771/1148] dmscript: Remove DECLSPEC_HIDDEN usage. (cherry picked from commit 90244981e7d70562b184a29ece6fadbdec234199) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmscript/dmobject.h | 50 ++++++++++++++++---------------- dlls/dmscript/dmscript_private.h | 6 ++-- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/dlls/dmscript/dmobject.h b/dlls/dmscript/dmobject.h index afe721dc824..772be015c80 100644 --- a/dlls/dmscript/dmobject.h +++ b/dlls/dmscript/dmobject.h @@ -31,16 +31,16 @@ struct chunk_entry { const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ }; -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; + unsigned int *count, DWORD elem_size); HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) { @@ -71,21 +71,21 @@ struct dmobject { DMUS_OBJECTDESC desc; }; -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) DECLSPEC_HIDDEN; +void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); /* Generic IDirectMusicObject methods */ HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); +ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); /* Helper for IDirectMusicObject::ParseDescriptor */ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc, DWORD supported); /* Additional supported flags for dmobj_parsedescriptor. DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ @@ -93,28 +93,28 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, /* 'DMRF' (reference list) helper */ HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + IDirectMusicObject **dmobj); /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) DECLSPEC_HIDDEN; -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); +ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); +HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); /* IPersistStream methods not implemented in native */ HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class) DECLSPEC_HIDDEN; -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) DECLSPEC_HIDDEN; + CLSID *class); +HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) DECLSPEC_HIDDEN; + BOOL clear_dirty); HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size) DECLSPEC_HIDDEN; + ULARGE_INTEGER *size); /* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; -const char *debugstr_dmguid(const GUID *id) DECLSPEC_HIDDEN; -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; +const char *debugstr_chunk(const struct chunk_entry *chunk); +const char *debugstr_dmguid(const GUID *id); +void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); static inline const char *debugstr_fourcc(DWORD fourcc) { diff --git a/dlls/dmscript/dmscript_private.h b/dlls/dmscript/dmscript_private.h index 3b3a4c86c84..90808d59f00 100644 --- a/dlls/dmscript/dmscript_private.h +++ b/dlls/dmscript/dmscript_private.h @@ -44,14 +44,14 @@ /***************************************************************************** * ClassFactory */ -extern HRESULT DMUSIC_CreateDirectMusicScriptImpl(REFIID riid, void **ppobj, IUnknown *pUnkOuter) DECLSPEC_HIDDEN; +extern HRESULT DMUSIC_CreateDirectMusicScriptImpl(REFIID riid, void **ppobj, IUnknown *pUnkOuter); -extern HRESULT DMUSIC_CreateDirectMusicScriptTrack(REFIID riid, void **ppobj, IUnknown *pUnkOuter) DECLSPEC_HIDDEN; +extern HRESULT DMUSIC_CreateDirectMusicScriptTrack(REFIID riid, void **ppobj, IUnknown *pUnkOuter); /********************************************************************** * Dll lifetime tracking declaration for dmscript.dll */ -extern LONG DMSCRIPT_refCount DECLSPEC_HIDDEN; +extern LONG DMSCRIPT_refCount; static inline void DMSCRIPT_LockModule(void) { InterlockedIncrement( &DMSCRIPT_refCount ); } static inline void DMSCRIPT_UnlockModule(void) { InterlockedDecrement( &DMSCRIPT_refCount ); } From 938080e955d6e595661724953270636cd01986f0 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 29 Jun 2023 18:17:57 +1000 Subject: [PATCH 0772/1148] dswave: Remove DECLSPEC_HIDDEN usage. (cherry picked from commit 860c575e7a8c93c53d4e38c3f0535ab498f59c8d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dswave/dmobject.h | 50 ++++++++++++++++++------------------ dlls/dswave/dswave_private.h | 4 +-- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/dlls/dswave/dmobject.h b/dlls/dswave/dmobject.h index afe721dc824..772be015c80 100644 --- a/dlls/dswave/dmobject.h +++ b/dlls/dswave/dmobject.h @@ -31,16 +31,16 @@ struct chunk_entry { const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ }; -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; + unsigned int *count, DWORD elem_size); HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) { @@ -71,21 +71,21 @@ struct dmobject { DMUS_OBJECTDESC desc; }; -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) DECLSPEC_HIDDEN; +void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); /* Generic IDirectMusicObject methods */ HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); +ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); /* Helper for IDirectMusicObject::ParseDescriptor */ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc, DWORD supported); /* Additional supported flags for dmobj_parsedescriptor. DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ @@ -93,28 +93,28 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, /* 'DMRF' (reference list) helper */ HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + IDirectMusicObject **dmobj); /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) DECLSPEC_HIDDEN; -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); +ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); +HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); /* IPersistStream methods not implemented in native */ HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class) DECLSPEC_HIDDEN; -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) DECLSPEC_HIDDEN; + CLSID *class); +HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) DECLSPEC_HIDDEN; + BOOL clear_dirty); HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size) DECLSPEC_HIDDEN; + ULARGE_INTEGER *size); /* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; -const char *debugstr_dmguid(const GUID *id) DECLSPEC_HIDDEN; -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; +const char *debugstr_chunk(const struct chunk_entry *chunk); +const char *debugstr_dmguid(const GUID *id); +void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); static inline const char *debugstr_fourcc(DWORD fourcc) { diff --git a/dlls/dswave/dswave_private.h b/dlls/dswave/dswave_private.h index dfd8ed6f51c..a7758a970bc 100644 --- a/dlls/dswave/dswave_private.h +++ b/dlls/dswave/dswave_private.h @@ -42,12 +42,12 @@ /***************************************************************************** * ClassFactory */ -extern HRESULT create_dswave(REFIID lpcGUID, void **ret_iface) DECLSPEC_HIDDEN; +extern HRESULT create_dswave(REFIID lpcGUID, void **ret_iface); /********************************************************************** * Dll lifetime tracking declaration for dswave.dll */ -extern LONG DSWAVE_refCount DECLSPEC_HIDDEN; +extern LONG DSWAVE_refCount; static inline void DSWAVE_LockModule(void) { InterlockedIncrement( &DSWAVE_refCount ); } static inline void DSWAVE_UnlockModule(void) { InterlockedDecrement( &DSWAVE_refCount ); } From 3f56ecf10946b354f03d9ed95c01c5b01415c476 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 29 Jun 2023 18:17:57 +1000 Subject: [PATCH 0773/1148] dmime: Remove DECLSPEC_HIDDEN usage. (cherry picked from commit cc59b783162e6f73fcf45f73f8fc6ef319a5258f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 40 +++++++++++++++--------------- dlls/dmime/dmobject.h | 50 +++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 4159abffa99..7cdc1534866 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -51,25 +51,25 @@ typedef struct IDirectMusicAudioPathImpl IDirectMusicAudioPathImpl; /***************************************************************************** * ClassFactory */ -extern HRESULT create_dmperformance(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmsegment(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmsegmentstate(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmgraph(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmaudiopath(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; - -extern HRESULT create_dmlyricstrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmmarkertrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmparamcontroltrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmsegtriggertrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmseqtrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmsysextrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmtempotrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmtimesigtrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmwavetrack(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; - -extern void set_audiopath_perf_pointer(IDirectMusicAudioPath*,IDirectMusicPerformance8*) DECLSPEC_HIDDEN; -extern void set_audiopath_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*) DECLSPEC_HIDDEN; -extern void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*) DECLSPEC_HIDDEN; +extern HRESULT create_dmperformance(REFIID riid, void **ret_iface); +extern HRESULT create_dmsegment(REFIID riid, void **ret_iface); +extern HRESULT create_dmsegmentstate(REFIID riid, void **ret_iface); +extern HRESULT create_dmgraph(REFIID riid, void **ret_iface); +extern HRESULT create_dmaudiopath(REFIID riid, void **ret_iface); + +extern HRESULT create_dmlyricstrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmmarkertrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmparamcontroltrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmsegtriggertrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmseqtrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmsysextrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmtempotrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmtimesigtrack(REFIID riid, void **ret_iface); +extern HRESULT create_dmwavetrack(REFIID riid, void **ret_iface); + +extern void set_audiopath_perf_pointer(IDirectMusicAudioPath*,IDirectMusicPerformance8*); +extern void set_audiopath_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*); +extern void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*); /***************************************************************************** * Auxiliary definitions @@ -99,7 +99,7 @@ typedef struct _DMUS_PRIVATE_TEMPO_PLAY_STATE { /********************************************************************** * Dll lifetime tracking declaration for dmime.dll */ -extern LONG DMIME_refCount DECLSPEC_HIDDEN; +extern LONG DMIME_refCount; static inline void DMIME_LockModule(void) { InterlockedIncrement( &DMIME_refCount ); } static inline void DMIME_UnlockModule(void) { InterlockedDecrement( &DMIME_refCount ); } diff --git a/dlls/dmime/dmobject.h b/dlls/dmime/dmobject.h index afe721dc824..772be015c80 100644 --- a/dlls/dmime/dmobject.h +++ b/dlls/dmime/dmobject.h @@ -31,16 +31,16 @@ struct chunk_entry { const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ }; -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; + unsigned int *count, DWORD elem_size); HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) { @@ -71,21 +71,21 @@ struct dmobject { DMUS_OBJECTDESC desc; }; -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) DECLSPEC_HIDDEN; +void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); /* Generic IDirectMusicObject methods */ HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); +ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); /* Helper for IDirectMusicObject::ParseDescriptor */ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc, DWORD supported); /* Additional supported flags for dmobj_parsedescriptor. DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ @@ -93,28 +93,28 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, /* 'DMRF' (reference list) helper */ HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + IDirectMusicObject **dmobj); /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) DECLSPEC_HIDDEN; -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); +ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); +HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); /* IPersistStream methods not implemented in native */ HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class) DECLSPEC_HIDDEN; -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) DECLSPEC_HIDDEN; + CLSID *class); +HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) DECLSPEC_HIDDEN; + BOOL clear_dirty); HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size) DECLSPEC_HIDDEN; + ULARGE_INTEGER *size); /* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; -const char *debugstr_dmguid(const GUID *id) DECLSPEC_HIDDEN; -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; +const char *debugstr_chunk(const struct chunk_entry *chunk); +const char *debugstr_dmguid(const GUID *id); +void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); static inline const char *debugstr_fourcc(DWORD fourcc) { From 11fa6687b390a8c0cbff74867b028885d021bd0f Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 29 Jun 2023 18:17:57 +1000 Subject: [PATCH 0774/1148] dmloader: Remove DECLSPEC_HIDDEN usage. (cherry picked from commit 3d28f9d362e6d9871747231b210c559536bb6dd4) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/debug.h | 8 ++--- dlls/dmloader/dmloader_private.h | 18 ++++++------ dlls/dmloader/dmobject.h | 50 ++++++++++++++++---------------- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/dlls/dmloader/debug.h b/dlls/dmloader/debug.h index 43f48e832c4..b61d17bda62 100644 --- a/dlls/dmloader/debug.h +++ b/dlls/dmloader/debug.h @@ -36,11 +36,11 @@ typedef struct { #define FE(x) { x, #x } /* check whether chunkID is valid dmobject form chunk */ -extern BOOL IS_VALID_DMFORM (FOURCC chunkID) DECLSPEC_HIDDEN; +extern BOOL IS_VALID_DMFORM (FOURCC chunkID); /* translate STREAM_SEEK flag to string */ -extern const char *resolve_STREAM_SEEK (DWORD flag) DECLSPEC_HIDDEN; +extern const char *resolve_STREAM_SEEK (DWORD flag); -extern const char *debugstr_DMUS_IO_CONTAINER_HEADER (LPDMUS_IO_CONTAINER_HEADER pHeader) DECLSPEC_HIDDEN; -extern const char *debugstr_DMUS_IO_CONTAINED_OBJECT_HEADER (LPDMUS_IO_CONTAINED_OBJECT_HEADER pHeader) DECLSPEC_HIDDEN; +extern const char *debugstr_DMUS_IO_CONTAINER_HEADER (LPDMUS_IO_CONTAINER_HEADER pHeader); +extern const char *debugstr_DMUS_IO_CONTAINED_OBJECT_HEADER (LPDMUS_IO_CONTAINED_OBJECT_HEADER pHeader); #endif /* __WINE_DMLOADER_DEBUG_H */ diff --git a/dlls/dmloader/dmloader_private.h b/dlls/dmloader/dmloader_private.h index 47994a48c4e..204c9689ffb 100644 --- a/dlls/dmloader/dmloader_private.h +++ b/dlls/dmloader/dmloader_private.h @@ -45,7 +45,7 @@ #define ICOM_THIS_MULTI(impl,field,iface) impl* const This=(impl*)((char*)(iface) - offsetof(impl,field)) /* dmloader.dll global (for DllCanUnloadNow) */ -extern LONG module_ref DECLSPEC_HIDDEN; +extern LONG module_ref; static inline void lock_module(void) { InterlockedIncrement( &module_ref ); } static inline void unlock_module(void) { InterlockedDecrement( &module_ref ); } @@ -62,11 +62,11 @@ typedef struct IDirectMusicLoaderGenericStream IDirectMusicLoaderGenericStream; /***************************************************************************** * Creation helpers */ -extern HRESULT create_dmloader(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT create_dmcontainer(REFIID riid, void **ret_iface) DECLSPEC_HIDDEN; -extern HRESULT DMUSIC_CreateDirectMusicLoaderFileStream(void **ppobj) DECLSPEC_HIDDEN; -extern HRESULT DMUSIC_CreateDirectMusicLoaderResourceStream(void **ppobj) DECLSPEC_HIDDEN; -extern HRESULT DMUSIC_CreateDirectMusicLoaderGenericStream(void **ppobj) DECLSPEC_HIDDEN; +extern HRESULT create_dmloader(REFIID riid, void **ret_iface); +extern HRESULT create_dmcontainer(REFIID riid, void **ret_iface); +extern HRESULT DMUSIC_CreateDirectMusicLoaderFileStream(void **ppobj); +extern HRESULT DMUSIC_CreateDirectMusicLoaderResourceStream(void **ppobj); +extern HRESULT DMUSIC_CreateDirectMusicLoaderGenericStream(void **ppobj); /***************************************************************************** * IDirectMusicLoaderFileStream implementation structure @@ -85,7 +85,7 @@ struct IDirectMusicLoaderFileStream { }; /* Custom: */ -extern HRESULT WINAPI IDirectMusicLoaderFileStream_Attach (LPSTREAM iface, LPCWSTR wzFile, LPDIRECTMUSICLOADER8 pLoader) DECLSPEC_HIDDEN; +extern HRESULT WINAPI IDirectMusicLoaderFileStream_Attach (LPSTREAM iface, LPCWSTR wzFile, LPDIRECTMUSICLOADER8 pLoader); /***************************************************************************** * IDirectMusicLoaderResourceStream implementation structure @@ -106,7 +106,7 @@ struct IDirectMusicLoaderResourceStream { }; /* Custom: */ -extern HRESULT WINAPI IDirectMusicLoaderResourceStream_Attach (LPSTREAM iface, LPBYTE pbMemData, LONGLONG llMemLength, LONGLONG llPos, LPDIRECTMUSICLOADER8 pLoader) DECLSPEC_HIDDEN; +extern HRESULT WINAPI IDirectMusicLoaderResourceStream_Attach (LPSTREAM iface, LPBYTE pbMemData, LONGLONG llMemLength, LONGLONG llPos, LPDIRECTMUSICLOADER8 pLoader); /***************************************************************************** * IDirectMusicLoaderGenericStream implementation structure @@ -124,7 +124,7 @@ struct IDirectMusicLoaderGenericStream { }; /* Custom: */ -extern HRESULT WINAPI IDirectMusicLoaderGenericStream_Attach (LPSTREAM iface, LPSTREAM pStream, LPDIRECTMUSICLOADER8 pLoader) DECLSPEC_HIDDEN; +extern HRESULT WINAPI IDirectMusicLoaderGenericStream_Attach (LPSTREAM iface, LPSTREAM pStream, LPDIRECTMUSICLOADER8 pLoader); #include "debug.h" diff --git a/dlls/dmloader/dmobject.h b/dlls/dmloader/dmobject.h index afe721dc824..772be015c80 100644 --- a/dlls/dmloader/dmobject.h +++ b/dlls/dmloader/dmobject.h @@ -31,16 +31,16 @@ struct chunk_entry { const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ }; -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) DECLSPEC_HIDDEN; -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) DECLSPEC_HIDDEN; +HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); +HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) DECLSPEC_HIDDEN; + unsigned int *count, DWORD elem_size); HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) DECLSPEC_HIDDEN; + ULONG size); static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) { @@ -71,21 +71,21 @@ struct dmobject { DMUS_OBJECTDESC desc; }; -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) DECLSPEC_HIDDEN; +void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); /* Generic IDirectMusicObject methods */ HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); +ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc); /* Helper for IDirectMusicObject::ParseDescriptor */ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) DECLSPEC_HIDDEN; + DMUS_OBJECTDESC *desc, DWORD supported); /* Additional supported flags for dmobj_parsedescriptor. DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ @@ -93,28 +93,28 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, /* 'DMRF' (reference list) helper */ HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) DECLSPEC_HIDDEN; + IDirectMusicObject **dmobj); /* Generic IPersistStream methods */ HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) DECLSPEC_HIDDEN; -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) DECLSPEC_HIDDEN; -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) DECLSPEC_HIDDEN; + void **ret_iface); +ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); +ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); +HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); /* IPersistStream methods not implemented in native */ HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class) DECLSPEC_HIDDEN; -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) DECLSPEC_HIDDEN; + CLSID *class); +HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) DECLSPEC_HIDDEN; + BOOL clear_dirty); HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size) DECLSPEC_HIDDEN; + ULARGE_INTEGER *size); /* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk) DECLSPEC_HIDDEN; -const char *debugstr_dmguid(const GUID *id) DECLSPEC_HIDDEN; -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) DECLSPEC_HIDDEN; +const char *debugstr_chunk(const struct chunk_entry *chunk); +const char *debugstr_dmguid(const GUID *id); +void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); static inline const char *debugstr_fourcc(DWORD fourcc) { From c4a2aad66f6b3831e2d97fa3f4d519a88224e3a1 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Wed, 3 Aug 2022 16:25:11 +1000 Subject: [PATCH 0775/1148] dmime: Store WAVE data when Loading. (cherry picked from commit 0774873ea26c11757cdf511943c17ec81c6e11c5) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 57 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index bf44c5e73b3..c005d506cad 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,52 @@ 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, + sizeof(This->wave_format))) ) + 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); + if (This->wave_data) + ERR("Multiple data streams detected\n"); + 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 SUCCEEDED(hr) ? S_OK : hr; +} + static HRESULT WINAPI seg_IPersistStream_Load(IPersistStream *iface, IStream *stream) { IDirectMusicSegment8Impl *This = impl_from_IPersistStream(iface); @@ -847,8 +900,10 @@ 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 if(riff.type == mmioFOURCC('W','A','V','E')) + hr = parse_wave_form(This, stream, &riff); else { - FIXME("WAVE form loading not implemented\n"); + FIXME("Unknown type %s\n", debugstr_chunk(&riff)); hr = S_OK; } From 9d3fcefe2b9ca89ae3a42c3ac92a760b5d13af03 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Tue, 22 Aug 2023 22:28:38 -0600 Subject: [PATCH 0776/1148] dmusic: Use CRT allocation functions. (cherry picked from commit 4ef31ae987b80ae7794c0a72a99e9999efda8a50) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/buffer.c | 10 +++++----- dlls/dmusic/clock.c | 4 ++-- dlls/dmusic/collection.c | 14 +++++++------- dlls/dmusic/dmobject.c | 7 +++---- dlls/dmusic/dmusic.c | 29 +++++++++++------------------ dlls/dmusic/download.c | 4 ++-- dlls/dmusic/instrument.c | 23 ++++++++++------------- dlls/dmusic/port.c | 29 ++++++++++++++--------------- 8 files changed, 54 insertions(+), 66 deletions(-) diff --git a/dlls/dmusic/buffer.c b/dlls/dmusic/buffer.c index c328c15541e..9aaa1a074b4 100644 --- a/dlls/dmusic/buffer.c +++ b/dlls/dmusic/buffer.c @@ -69,8 +69,8 @@ static ULONG WINAPI IDirectMusicBufferImpl_Release(LPDIRECTMUSICBUFFER iface) TRACE("(%p): new ref = %lu\n", iface, ref); if (!ref) { - HeapFree(GetProcessHeap(), 0, This->data); - HeapFree(GetProcessHeap(), 0, This); + free(This->data); + free(This); DMUSIC_UnlockModule(); } @@ -300,7 +300,7 @@ HRESULT DMUSIC_CreateDirectMusicBufferImpl(LPDMUS_BUFFERDESC desc, LPVOID* ret_i *ret_iface = NULL; - dmbuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicBufferImpl)); + dmbuffer = calloc(1, sizeof(IDirectMusicBufferImpl)); if (!dmbuffer) return E_OUTOFMEMORY; @@ -313,9 +313,9 @@ HRESULT DMUSIC_CreateDirectMusicBufferImpl(LPDMUS_BUFFERDESC desc, LPVOID* ret_i dmbuffer->format = desc->guidBufferFormat; dmbuffer->size = (desc->cbBuffer + 3) & ~3; /* Buffer size must be multiple of 4 bytes */ - dmbuffer->data = HeapAlloc(GetProcessHeap(), 0, dmbuffer->size); + dmbuffer->data = malloc(dmbuffer->size); if (!dmbuffer->data) { - HeapFree(GetProcessHeap(), 0, dmbuffer); + free(dmbuffer); return E_OUTOFMEMORY; } diff --git a/dlls/dmusic/clock.c b/dlls/dmusic/clock.c index 97bd05c4524..15a04c844e5 100644 --- a/dlls/dmusic/clock.c +++ b/dlls/dmusic/clock.c @@ -62,7 +62,7 @@ static ULONG WINAPI IReferenceClockImpl_Release(IReferenceClock *iface) TRACE("(%p): new ref = %lu\n", This, ref); if (!ref) { - HeapFree(GetProcessHeap(), 0, This); + free(This); DMUSIC_UnlockModule(); } @@ -126,7 +126,7 @@ HRESULT DMUSIC_CreateReferenceClockImpl(LPCGUID riid, LPVOID* ret_iface, LPUNKNO TRACE("(%s, %p, %p)\n", debugstr_guid(riid), ret_iface, unkouter); - clock = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IReferenceClockImpl)); + clock = calloc(1, sizeof(IReferenceClockImpl)); if (!clock) { *ret_iface = NULL; return E_OUTOFMEMORY; diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 8473c0078ec..0febf9dfd08 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -98,7 +98,7 @@ static ULONG WINAPI IDirectMusicCollectionImpl_Release(IDirectMusicCollection *i TRACE("(%p): new ref = %lu\n", iface, ref); if (!ref) { - HeapFree(GetProcessHeap(), 0, This); + free(This); DMUSIC_UnlockModule(); } @@ -257,7 +257,7 @@ static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, switch (chunk.fccID) { case FOURCC_COLH: { TRACE_(dmfile)(": collection header chunk\n"); - This->pHeader = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, chunk.dwSize); + This->pHeader = calloc(1, chunk.dwSize); IStream_Read(stream, This->pHeader, chunk.dwSize, NULL); break; } @@ -275,10 +275,10 @@ static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, } case FOURCC_PTBL: { TRACE_(dmfile)(": pool table chunk\n"); - This->pPoolTable = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(POOLTABLE)); + This->pPoolTable = calloc(1, sizeof(POOLTABLE)); IStream_Read(stream, This->pPoolTable, sizeof(POOLTABLE), NULL); chunk.dwSize -= sizeof(POOLTABLE); - This->pPoolCues = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pPoolTable->cCues * sizeof(POOLCUE)); + This->pPoolCues = calloc(This->pPoolTable->cCues, sizeof(POOLCUE)); IStream_Read(stream, This->pPoolCues, chunk.dwSize, NULL); break; } @@ -320,7 +320,7 @@ static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, } case mmioFOURCC('I','C','O','P'): { TRACE_(dmfile)(": copyright chunk\n"); - This->szCopyright = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, chunk.dwSize); + This->szCopyright = calloc(1, chunk.dwSize); IStream_Read(stream, This->szCopyright, chunk.dwSize, NULL); if (even_or_odd(chunk.dwSize)) { ListCount[0]++; @@ -387,7 +387,7 @@ static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, ListCount[1] = 0; switch (chunk.fccID) { case FOURCC_INS: { - LPDMUS_PRIVATE_INSTRUMENTENTRY new_instrument = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_INSTRUMENTENTRY)); + DMUS_PRIVATE_INSTRUMENTENTRY *new_instrument = calloc(1, sizeof(DMUS_PRIVATE_INSTRUMENTENTRY)); TRACE_(dmfile)(": instrument list\n"); /* Only way to create this one... even M$ does it discretely */ DMUSIC_CreateDirectMusicInstrumentImpl(&IID_IDirectMusicInstrument, (void**)&new_instrument->pInstrument, NULL); @@ -531,7 +531,7 @@ HRESULT DMUSIC_CreateDirectMusicCollectionImpl(REFIID lpcGUID, void **ppobj, IUn if (pUnkOuter) return CLASS_E_NOAGGREGATION; - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicCollectionImpl)); + obj = calloc(1, sizeof(IDirectMusicCollectionImpl)); if (!obj) return E_OUTOFMEMORY; diff --git a/dlls/dmusic/dmobject.c b/dlls/dmusic/dmobject.c index b526b23d031..07d887a376c 100644 --- a/dlls/dmusic/dmobject.c +++ b/dlls/dmusic/dmobject.c @@ -28,7 +28,6 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -375,7 +374,7 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) /* Reads chunk data of the form: DWORD - size of array element element[] - Array of elements - The caller needs to heap_free() the array. + The caller needs to free() the array. */ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, unsigned int *count, DWORD elem_size) @@ -400,10 +399,10 @@ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, *count = (chunk->size - sizeof(DWORD)) / elem_size; size = *count * elem_size; - if (!(*array = heap_alloc(size))) + if (!(*array = malloc(size))) return E_OUTOFMEMORY; if (FAILED(hr = stream_read(stream, *array, size))) { - heap_free(*array); + free(*array); *array = NULL; return hr; } diff --git a/dlls/dmusic/dmusic.c b/dlls/dmusic/dmusic.c index 3a1824143d7..8fb35e65809 100644 --- a/dlls/dmusic/dmusic.c +++ b/dlls/dmusic/dmusic.c @@ -23,7 +23,6 @@ #include "dmusic_private.h" #include "dmobject.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); @@ -75,7 +74,7 @@ static ULONG WINAPI master_IReferenceClock_Release(IReferenceClock *iface) TRACE("(%p) ref = %lu\n", iface, ref); if (!ref) - heap_free(This); + free(This); return ref; } @@ -136,7 +135,7 @@ static HRESULT master_clock_create(IReferenceClock **clock) TRACE("(%p)\n", clock); - if (!(obj = heap_alloc_zero(sizeof(*obj)))) + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IReferenceClock_iface.lpVtbl = &master_clock_vtbl; @@ -199,9 +198,9 @@ static ULONG WINAPI IDirectMusic8Impl_Release(LPDIRECTMUSIC8 iface) IReferenceClock_Release(This->master_clock); if (This->dsound) IDirectSound_Release(This->dsound); - HeapFree(GetProcessHeap(), 0, This->system_ports); - HeapFree(GetProcessHeap(), 0, This->ports); - HeapFree(GetProcessHeap(), 0, This); + free(This->system_ports); + free(This->ports); + free(This); DMUSIC_UnlockModule(); } @@ -283,12 +282,7 @@ static HRESULT WINAPI IDirectMusic8Impl_CreatePort(LPDIRECTMUSIC8 iface, REFCLSI return hr; } This->num_ports++; - if (!This->ports) - This->ports = HeapAlloc(GetProcessHeap(), 0, - sizeof(*This->ports) * This->num_ports); - else - This->ports = HeapReAlloc(GetProcessHeap(), 0, This->ports, - sizeof(*This->ports) * This->num_ports); + This->ports = realloc(This->ports, sizeof(*This->ports) * This->num_ports); This->ports[This->num_ports - 1] = new_port; *port = new_port; return S_OK; @@ -320,15 +314,14 @@ void dmusic_remove_port(IDirectMusic8Impl *dmusic, IDirectMusicPort *port) } if (!--dmusic->num_ports) { - HeapFree(GetProcessHeap(), 0, dmusic->ports); + free(dmusic->ports); dmusic->ports = NULL; return; } memmove(&dmusic->ports[i], &dmusic->ports[i + 1], (dmusic->num_ports - i) * sizeof(*dmusic->ports)); - dmusic->ports = HeapReAlloc(GetProcessHeap(), 0, dmusic->ports, - sizeof(*dmusic->ports) * dmusic->num_ports); + dmusic->ports = realloc(dmusic->ports, sizeof(*dmusic->ports) * dmusic->num_ports); } static HRESULT WINAPI IDirectMusic8Impl_EnumMasterClock(LPDIRECTMUSIC8 iface, DWORD index, LPDMUS_CLOCKINFO clock_info) @@ -517,7 +510,7 @@ static void create_system_ports_list(IDirectMusic8Impl* object) nb_midi_in = midiInGetNumDevs(); nb_ports = 1 /* midi mapper */ + nb_midi_out + nb_midi_in + 1 /* synth port */; - port = object->system_ports = HeapAlloc(GetProcessHeap(), 0, nb_ports * sizeof(port_info)); + port = object->system_ports = malloc(nb_ports * sizeof(port_info)); if (!object->system_ports) return; @@ -599,7 +592,7 @@ HRESULT DMUSIC_CreateDirectMusicImpl(REFIID riid, void **ret_iface, IUnknown *un if (unkouter) return CLASS_E_NOAGGREGATION; - dmusic = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusic8Impl)); + dmusic = calloc(1, sizeof(IDirectMusic8Impl)); if (!dmusic) return E_OUTOFMEMORY; @@ -607,7 +600,7 @@ HRESULT DMUSIC_CreateDirectMusicImpl(REFIID riid, void **ret_iface, IUnknown *un dmusic->ref = 1; ret = master_clock_create(&dmusic->master_clock); if (FAILED(ret)) { - HeapFree(GetProcessHeap(), 0, dmusic); + free(dmusic); return ret; } diff --git a/dlls/dmusic/download.c b/dlls/dmusic/download.c index 56ee9c7477d..48f0efd5f69 100644 --- a/dlls/dmusic/download.c +++ b/dlls/dmusic/download.c @@ -64,7 +64,7 @@ static ULONG WINAPI IDirectMusicDownloadImpl_Release(IDirectMusicDownload *iface TRACE("(%p): new ref = %lu\n", iface, ref); if (!ref) { - HeapFree(GetProcessHeap(), 0, This); + free(This); DMUSIC_UnlockModule(); } @@ -91,7 +91,7 @@ HRESULT DMUSIC_CreateDirectMusicDownloadImpl(const GUID *guid, void **ret_iface, { IDirectMusicDownloadImpl *download; - download = HeapAlloc(GetProcessHeap(), 0, sizeof(*download)); + download = malloc(sizeof(*download)); if (!download) { *ret_iface = NULL; diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index f90e08b9128..7b7ee3def64 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -77,11 +77,11 @@ static ULONG WINAPI IDirectMusicInstrumentImpl_Release(LPDIRECTMUSICINSTRUMENT i { ULONG i; - HeapFree(GetProcessHeap(), 0, This->regions); + free(This->regions); for (i = 0; i < This->nb_articulations; i++) - HeapFree(GetProcessHeap(), 0, This->articulations->connections); - HeapFree(GetProcessHeap(), 0, This->articulations); - HeapFree(GetProcessHeap(), 0, This); + free(This->articulations->connections); + free(This->articulations); + free(This); DMUSIC_UnlockModule(); } @@ -125,7 +125,7 @@ HRESULT DMUSIC_CreateDirectMusicInstrumentImpl (LPCGUID lpcGUID, LPVOID* ppobj, IDirectMusicInstrumentImpl* dminst; HRESULT hr; - dminst = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicInstrumentImpl)); + dminst = calloc(1, sizeof(IDirectMusicInstrumentImpl)); if (NULL == dminst) { *ppobj = NULL; return E_OUTOFMEMORY; @@ -257,10 +257,7 @@ static HRESULT load_articulation(IDirectMusicInstrumentImpl *This, IStream *stre HRESULT ret; instrument_articulation *articulation; - if (!This->articulations) - This->articulations = HeapAlloc(GetProcessHeap(), 0, sizeof(*This->articulations)); - else - This->articulations = HeapReAlloc(GetProcessHeap(), 0, This->articulations, sizeof(*This->articulations) * (This->nb_articulations + 1)); + This->articulations = realloc(This->articulations, sizeof(*This->articulations) * (This->nb_articulations + 1)); if (!This->articulations) return E_OUTOFMEMORY; @@ -270,14 +267,14 @@ static HRESULT load_articulation(IDirectMusicInstrumentImpl *This, IStream *stre if (FAILED(ret)) return ret; - articulation->connections = HeapAlloc(GetProcessHeap(), 0, sizeof(CONNECTION) * articulation->connections_list.cConnections); + articulation->connections = malloc(sizeof(CONNECTION) * articulation->connections_list.cConnections); if (!articulation->connections) return E_OUTOFMEMORY; ret = read_from_stream(stream, articulation->connections, sizeof(CONNECTION) * articulation->connections_list.cConnections); if (FAILED(ret)) { - HeapFree(GetProcessHeap(), 0, articulation->connections); + free(articulation->connections); return ret; } @@ -309,7 +306,7 @@ HRESULT IDirectMusicInstrumentImpl_CustomLoad(IDirectMusicInstrument *iface, ISt return DMUS_E_UNSUPPORTED_STREAM; } - This->regions = HeapAlloc(GetProcessHeap(), 0, sizeof(*This->regions) * This->header.cRegions); + This->regions = malloc(sizeof(*This->regions) * This->header.cRegions); if (!This->regions) return E_OUTOFMEMORY; @@ -438,7 +435,7 @@ HRESULT IDirectMusicInstrumentImpl_CustomLoad(IDirectMusicInstrument *iface, ISt return S_OK; error: - HeapFree(GetProcessHeap(), 0, This->regions); + free(This->regions); This->regions = NULL; return DMUS_E_UNSUPPORTED_STREAM; diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index 8549c62c4b1..518b4fe9666 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -22,7 +22,6 @@ #include #include "dmusic_private.h" #include "dmobject.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); @@ -104,8 +103,8 @@ static ULONG WINAPI IDirectMusicDownloadedInstrumentImpl_Release(LPDIRECTMUSICDO if (!ref) { - HeapFree(GetProcessHeap(), 0, This->data); - HeapFree(GetProcessHeap(), 0, This); + free(This->data); + free(This); DMUSIC_UnlockModule(); } @@ -131,7 +130,7 @@ static HRESULT DMUSIC_CreateDirectMusicDownloadedInstrumentImpl(IDirectMusicDown { IDirectMusicDownloadedInstrumentImpl *object; - object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)); + object = calloc(1, sizeof(*object)); if (!object) { *instrument = NULL; @@ -202,7 +201,7 @@ static ULONG WINAPI synth_port_Release(IDirectMusicPort *iface) IDirectSoundBuffer_Release(This->dsbuffer); if (This->dsound) IDirectSound_Release(This->dsound); - HeapFree(GetProcessHeap(), 0, This); + free(This); } DMUSIC_UnlockModule(); @@ -255,7 +254,7 @@ static HRESULT WINAPI synth_port_DownloadInstrument(IDirectMusicPort *iface, IDi struct synth_port *This = synth_from_IDirectMusicPort(iface); IDirectMusicInstrumentImpl *instrument_object; HRESULT ret; - BOOL free; + BOOL on_heap; HANDLE download; DMUS_DOWNLOADINFO *info; DMUS_OFFSETTABLE *offset_table; @@ -276,7 +275,7 @@ static HRESULT WINAPI synth_port_DownloadInstrument(IDirectMusicPort *iface, IDi nb_regions = instrument_object->header.cRegions; size = sizeof(DMUS_DOWNLOADINFO) + sizeof(ULONG) * (1 + nb_regions) + sizeof(DMUS_INSTRUMENT) + sizeof(DMUS_REGION) * nb_regions; - data = HeapAlloc(GetProcessHeap(), 0, size); + data = malloc(size); if (!data) return E_OUTOFMEMORY; @@ -317,7 +316,7 @@ static HRESULT WINAPI synth_port_DownloadInstrument(IDirectMusicPort *iface, IDi region->WLOOP[0] = instrument_object->regions[i].wave_loop; } - ret = IDirectMusicSynth8_Download(This->synth, &download, (VOID*)data, &free); + ret = IDirectMusicSynth8_Download(This->synth, &download, (void*)data, &on_heap); if (SUCCEEDED(ret)) ret = DMUSIC_CreateDirectMusicDownloadedInstrumentImpl(downloaded_instrument); @@ -331,7 +330,7 @@ static HRESULT WINAPI synth_port_DownloadInstrument(IDirectMusicPort *iface, IDi } *downloaded_instrument = NULL; - HeapFree(GetProcessHeap(), 0, data); + free(data); return E_FAIL; } @@ -349,7 +348,7 @@ static HRESULT WINAPI synth_port_UnloadInstrument(IDirectMusicPort *iface, if (!downloaded_object->downloaded) return DMUS_E_NOT_DOWNLOADED_TO_PORT; - HeapFree(GetProcessHeap(), 0, downloaded_object->data); + free(downloaded_object->data); downloaded_object->data = NULL; downloaded_object->downloaded = FALSE; @@ -779,7 +778,7 @@ HRESULT synth_port_create(IDirectMusic8Impl *parent, DMUS_PORTPARAMS *port_param *port = NULL; - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj)); + obj = calloc(1, sizeof(*obj)); if (!obj) return E_OUTOFMEMORY; @@ -843,7 +842,7 @@ HRESULT synth_port_create(IDirectMusic8Impl *parent, DMUS_PORTPARAMS *port_param IDirectMusicSynth_Release(obj->synth); if (obj->synth_sink) IDirectMusicSynthSink_Release(obj->synth_sink); - HeapFree(GetProcessHeap(), 0, obj); + free(obj); return hr; } @@ -902,7 +901,7 @@ static ULONG WINAPI midi_IDirectMusicPort_Release(IDirectMusicPort *iface) if (!ref) { if (This->clock) IReferenceClock_Release(This->clock); - heap_free(This); + free(This); } return ref; @@ -1124,7 +1123,7 @@ static HRESULT midi_port_create(IDirectMusic8Impl *parent, DMUS_PORTPARAMS *para struct midi_port *obj; HRESULT hr; - if (!(obj = heap_alloc_zero(sizeof(*obj)))) + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicPort_iface.lpVtbl = &midi_port_vtbl; @@ -1133,7 +1132,7 @@ static HRESULT midi_port_create(IDirectMusic8Impl *parent, DMUS_PORTPARAMS *para hr = DMUSIC_CreateReferenceClockImpl(&IID_IReferenceClock, (void **)&obj->clock, NULL); if (hr != S_OK) { - HeapFree(GetProcessHeap(), 0, obj); + free(obj); return hr; } From f221cc47106f87685660583b9178b3596bd6c3d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 24 Aug 2023 11:05:32 +0200 Subject: [PATCH 0777/1148] dmsynth: Remove unnecessary comments. (cherry picked from commit 23b8988464135b3979270ed0993b51f35fc1f718) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 4 ---- dlls/dmsynth/synthsink.c | 3 --- 2 files changed, 7 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 3631bcfe784..2c4a6f04e81 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -36,7 +36,6 @@ static inline IDirectMusicSynth8Impl *impl_from_IDirectMusicSynth8(IDirectMusicS return CONTAINING_RECORD(iface, IDirectMusicSynth8Impl, IDirectMusicSynth8_iface); } -/* IDirectMusicSynth8Impl IUnknown part: */ static HRESULT WINAPI IDirectMusicSynth8Impl_QueryInterface(IDirectMusicSynth8 *iface, REFIID riid, void **ret_iface) { @@ -93,7 +92,6 @@ static ULONG WINAPI IDirectMusicSynth8Impl_Release(IDirectMusicSynth8 *iface) return ref; } -/* IDirectMusicSynth8Impl IDirectMusicSynth part: */ static HRESULT WINAPI IDirectMusicSynth8Impl_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *params) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -542,7 +540,6 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_GetAppend(IDirectMusicSynth8 *iface return S_OK; } -/* IDirectMusicSynth8Impl IDirectMusicSynth8 part: */ static HRESULT WINAPI IDirectMusicSynth8Impl_PlayVoice(IDirectMusicSynth8 *iface, REFERENCE_TIME ref_time, DWORD voice_id, DWORD channel_group, DWORD channel, DWORD dwDLId, LONG prPitch, LONG vrVolume, SAMPLE_TIME stVoiceStart, SAMPLE_TIME stLoopStart, @@ -728,7 +725,6 @@ static const IKsControlVtbl DMSynthImpl_IKsControl_Vtbl = { DMSynthImpl_IKsControl_KsEvent }; -/* for ClassFactory */ HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj) { IDirectMusicSynth8Impl *obj; diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index a277a05bafb..4354069b274 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -32,7 +32,6 @@ static inline IDirectMusicSynthSinkImpl *impl_from_IDirectMusicSynthSink(IDirect return CONTAINING_RECORD(iface, IDirectMusicSynthSinkImpl, IDirectMusicSynthSink_iface); } -/* IDirectMusicSynthSinkImpl IUnknown part: */ static HRESULT WINAPI IDirectMusicSynthSinkImpl_QueryInterface(IDirectMusicSynthSink *iface, REFIID riid, void **ret_iface) { @@ -90,7 +89,6 @@ static ULONG WINAPI IDirectMusicSynthSinkImpl_Release(IDirectMusicSynthSink *ifa return ref; } -/* IDirectMusicSynthSinkImpl IDirectMusicSynthSink part: */ static HRESULT WINAPI IDirectMusicSynthSinkImpl_Init(IDirectMusicSynthSink *iface, IDirectMusicSynth *synth) { @@ -297,7 +295,6 @@ static const IKsControlVtbl DMSynthSinkImpl_IKsControl_Vtbl = { DMSynthSinkImpl_IKsControl_KsEvent }; -/* for ClassFactory */ HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ret_iface) { IDirectMusicSynthSinkImpl *obj; From 3e498ca0cb31c85aefc6b710dc99fffa7ef4a800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 22 Aug 2023 17:44:17 +0200 Subject: [PATCH 0778/1148] dmsynth: Always return S_FALSE from DllCanUnloadNow. (cherry picked from commit 3b5160c97567ceb13d719219e45972f07f990fbb) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/dmsynth_main.c | 22 ---------------------- dlls/dmsynth/dmsynth_private.h | 7 ------- dlls/dmsynth/synth.c | 2 -- dlls/dmsynth/synthsink.c | 2 -- 4 files changed, 33 deletions(-) diff --git a/dlls/dmsynth/dmsynth_main.c b/dlls/dmsynth/dmsynth_main.c index a5e2e605419..edeaab8474e 100644 --- a/dlls/dmsynth/dmsynth_main.c +++ b/dlls/dmsynth/dmsynth_main.c @@ -25,8 +25,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); -LONG DMSYNTH_refCount = 0; - typedef struct { IClassFactory IClassFactory_iface; HRESULT (*fnCreateInstance)(REFIID riid, void **ppv); @@ -62,15 +60,11 @@ static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID r static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) { - DMSYNTH_LockModule(); - return 2; /* non-heap based object */ } static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) { - DMSYNTH_UnlockModule(); - return 1; /* non-heap based object */ } @@ -90,12 +84,6 @@ static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock) { TRACE("(%d)\n", dolock); - - if (dolock) - DMSYNTH_LockModule(); - else - DMSYNTH_UnlockModule(); - return S_OK; } @@ -111,16 +99,6 @@ static IClassFactoryImpl Synth_CF = {{&classfactory_vtbl}, DMUSIC_CreateDirectMu static IClassFactoryImpl SynthSink_CF = {{&classfactory_vtbl}, DMUSIC_CreateDirectMusicSynthSinkImpl}; -/****************************************************************** - * DllCanUnloadNow (DMSYNTH.@) - * - * - */ -HRESULT WINAPI DllCanUnloadNow(void) -{ - return DMSYNTH_refCount != 0 ? S_FALSE : S_OK; -} - /****************************************************************** * DllGetClassObject (DMSYNTH.@) diff --git a/dlls/dmsynth/dmsynth_private.h b/dlls/dmsynth/dmsynth_private.h index c21a9e84a84..3a3e76dfaf4 100644 --- a/dlls/dmsynth/dmsynth_private.h +++ b/dlls/dmsynth/dmsynth_private.h @@ -80,13 +80,6 @@ struct IDirectMusicSynthSinkImpl { BOOL active; }; -/********************************************************************** - * Dll lifetime tracking declaration for dmsynth.dll - */ -extern LONG DMSYNTH_refCount; -static inline void DMSYNTH_LockModule(void) { InterlockedIncrement( &DMSYNTH_refCount ); } -static inline void DMSYNTH_UnlockModule(void) { InterlockedDecrement( &DMSYNTH_refCount ); } - /***************************************************************************** * Misc. */ diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 2c4a6f04e81..29aff9e988f 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -86,7 +86,6 @@ static ULONG WINAPI IDirectMusicSynth8Impl_Release(IDirectMusicSynth8 *iface) if (This->latency_clock) IReferenceClock_Release(This->latency_clock); HeapFree(GetProcessHeap(), 0, This); - DMSYNTH_UnlockModule(); } return ref; @@ -753,7 +752,6 @@ HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj) obj->caps.dwEffectFlags = DMUS_EFFECT_REVERB; lstrcpyW(obj->caps.wszDescription, L"Microsoft Synthesizer"); - DMSYNTH_LockModule(); hr = IDirectMusicSynth8_QueryInterface(&obj->IDirectMusicSynth8_iface, riid, ppobj); IDirectMusicSynth8_Release(&obj->IDirectMusicSynth8_iface); diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index 4354069b274..405fe4af1f0 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -83,7 +83,6 @@ static ULONG WINAPI IDirectMusicSynthSinkImpl_Release(IDirectMusicSynthSink *ifa if (This->master_clock) IReferenceClock_Release(This->master_clock); HeapFree(GetProcessHeap(), 0, This); - DMSYNTH_UnlockModule(); } return ref; @@ -319,7 +318,6 @@ HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ret_iface) return hr; } - DMSYNTH_LockModule(); hr = IDirectMusicSynthSink_QueryInterface(&obj->IDirectMusicSynthSink_iface, riid, ret_iface); IDirectMusicSynthSink_Release(&obj->IDirectMusicSynthSink_iface); From c7d8649e162a9a810b14cd08be5c14fa7594dd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 18 Aug 2023 21:34:24 +0200 Subject: [PATCH 0779/1148] dmsynth: Move IDirectMusicSynthSinkImpl struct to where it is used. (cherry picked from commit da256c4426baff6dafb26c1d524d13b3fc018ac1) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/dmsynth_private.h | 14 -------------- dlls/dmsynth/synthsink.c | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/dlls/dmsynth/dmsynth_private.h b/dlls/dmsynth/dmsynth_private.h index 3a3e76dfaf4..b9f160f000d 100644 --- a/dlls/dmsynth/dmsynth_private.h +++ b/dlls/dmsynth/dmsynth_private.h @@ -44,7 +44,6 @@ * Interfaces */ typedef struct IDirectMusicSynth8Impl IDirectMusicSynth8Impl; -typedef struct IDirectMusicSynthSinkImpl IDirectMusicSynthSinkImpl; /***************************************************************************** * ClassFactory @@ -67,19 +66,6 @@ struct IDirectMusicSynth8Impl { IDirectMusicSynthSink *sink; }; -/***************************************************************************** - * IDirectMusicSynthSinkImpl implementation structure - */ -struct IDirectMusicSynthSinkImpl { - IDirectMusicSynthSink IDirectMusicSynthSink_iface; - IKsControl IKsControl_iface; - LONG ref; - IReferenceClock *latency_clock; - IReferenceClock *master_clock; - IDirectMusicSynth *synth; /* No reference hold! */ - BOOL active; -}; - /***************************************************************************** * Misc. */ diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index 405fe4af1f0..3f63e69746c 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -27,6 +27,20 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); +typedef struct IDirectMusicSynthSinkImpl IDirectMusicSynthSinkImpl; + +struct IDirectMusicSynthSinkImpl +{ + IDirectMusicSynthSink IDirectMusicSynthSink_iface; + IKsControl IKsControl_iface; + LONG ref; + + IReferenceClock *latency_clock; + IReferenceClock *master_clock; + IDirectMusicSynth *synth; /* No reference hold! */ + BOOL active; +}; + static inline IDirectMusicSynthSinkImpl *impl_from_IDirectMusicSynthSink(IDirectMusicSynthSink *iface) { return CONTAINING_RECORD(iface, IDirectMusicSynthSinkImpl, IDirectMusicSynthSink_iface); From c2b6a5b9487a192e047da03a66417a97ad2f987c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 18 Aug 2023 21:49:49 +0200 Subject: [PATCH 0780/1148] dmsynth: Rename IDirectMusicSynthSinkImpl method prefix to synth_sink. (cherry picked from commit 1f7629dc0a22f4304f78e61e9c9330081d7b1449) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synthsink.c | 90 ++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index 3f63e69746c..1e72e5d1f1b 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -46,7 +46,7 @@ static inline IDirectMusicSynthSinkImpl *impl_from_IDirectMusicSynthSink(IDirect return CONTAINING_RECORD(iface, IDirectMusicSynthSinkImpl, IDirectMusicSynthSink_iface); } -static HRESULT WINAPI IDirectMusicSynthSinkImpl_QueryInterface(IDirectMusicSynthSink *iface, +static HRESULT WINAPI synth_sink_QueryInterface(IDirectMusicSynthSink *iface, REFIID riid, void **ret_iface) { IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); @@ -74,7 +74,7 @@ static HRESULT WINAPI IDirectMusicSynthSinkImpl_QueryInterface(IDirectMusicSynth return E_NOINTERFACE; } -static ULONG WINAPI IDirectMusicSynthSinkImpl_AddRef(IDirectMusicSynthSink *iface) +static ULONG WINAPI synth_sink_AddRef(IDirectMusicSynthSink *iface) { IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); ULONG ref = InterlockedIncrement(&This->ref); @@ -84,7 +84,7 @@ static ULONG WINAPI IDirectMusicSynthSinkImpl_AddRef(IDirectMusicSynthSink *ifac return ref; } -static ULONG WINAPI IDirectMusicSynthSinkImpl_Release(IDirectMusicSynthSink *iface) +static ULONG WINAPI synth_sink_Release(IDirectMusicSynthSink *iface) { IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); ULONG ref = InterlockedDecrement(&This->ref); @@ -102,7 +102,7 @@ static ULONG WINAPI IDirectMusicSynthSinkImpl_Release(IDirectMusicSynthSink *ifa return ref; } -static HRESULT WINAPI IDirectMusicSynthSinkImpl_Init(IDirectMusicSynthSink *iface, +static HRESULT WINAPI synth_sink_Init(IDirectMusicSynthSink *iface, IDirectMusicSynth *synth) { IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); @@ -116,7 +116,7 @@ static HRESULT WINAPI IDirectMusicSynthSinkImpl_Init(IDirectMusicSynthSink *ifac return S_OK; } -static HRESULT WINAPI IDirectMusicSynthSinkImpl_SetMasterClock(IDirectMusicSynthSink *iface, +static HRESULT WINAPI synth_sink_SetMasterClock(IDirectMusicSynthSink *iface, IReferenceClock *clock) { IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); @@ -134,7 +134,7 @@ static HRESULT WINAPI IDirectMusicSynthSinkImpl_SetMasterClock(IDirectMusicSynth return S_OK; } -static HRESULT WINAPI IDirectMusicSynthSinkImpl_GetLatencyClock(IDirectMusicSynthSink *iface, +static HRESULT WINAPI synth_sink_GetLatencyClock(IDirectMusicSynthSink *iface, IReferenceClock **clock) { IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); @@ -150,7 +150,7 @@ static HRESULT WINAPI IDirectMusicSynthSinkImpl_GetLatencyClock(IDirectMusicSynt return S_OK; } -static HRESULT WINAPI IDirectMusicSynthSinkImpl_Activate(IDirectMusicSynthSink *iface, +static HRESULT WINAPI synth_sink_Activate(IDirectMusicSynthSink *iface, BOOL enable) { IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); @@ -160,7 +160,7 @@ static HRESULT WINAPI IDirectMusicSynthSinkImpl_Activate(IDirectMusicSynthSink * return S_OK; } -static HRESULT WINAPI IDirectMusicSynthSinkImpl_SampleToRefTime(IDirectMusicSynthSink *iface, +static HRESULT WINAPI synth_sink_SampleToRefTime(IDirectMusicSynthSink *iface, LONGLONG sample_time, REFERENCE_TIME *ref_time) { IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); @@ -170,7 +170,7 @@ static HRESULT WINAPI IDirectMusicSynthSinkImpl_SampleToRefTime(IDirectMusicSynt return S_OK; } -static HRESULT WINAPI IDirectMusicSynthSinkImpl_RefTimeToSample(IDirectMusicSynthSink *iface, +static HRESULT WINAPI synth_sink_RefTimeToSample(IDirectMusicSynthSink *iface, REFERENCE_TIME ref_time, LONGLONG *sample_time) { IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); @@ -180,7 +180,7 @@ static HRESULT WINAPI IDirectMusicSynthSinkImpl_RefTimeToSample(IDirectMusicSynt return S_OK; } -static HRESULT WINAPI IDirectMusicSynthSinkImpl_SetDirectSound(IDirectMusicSynthSink *iface, +static HRESULT WINAPI synth_sink_SetDirectSound(IDirectMusicSynthSink *iface, IDirectSound *dsound, IDirectSoundBuffer *dsound_buffer) { IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); @@ -190,7 +190,7 @@ static HRESULT WINAPI IDirectMusicSynthSinkImpl_SetDirectSound(IDirectMusicSynth return S_OK; } -static HRESULT WINAPI IDirectMusicSynthSinkImpl_GetDesiredBufferSize(IDirectMusicSynthSink *iface, +static HRESULT WINAPI synth_sink_GetDesiredBufferSize(IDirectMusicSynthSink *iface, DWORD *size) { IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); @@ -211,18 +211,19 @@ static HRESULT WINAPI IDirectMusicSynthSinkImpl_GetDesiredBufferSize(IDirectMusi return S_OK; } -static const IDirectMusicSynthSinkVtbl DirectMusicSynthSink_Vtbl = { - IDirectMusicSynthSinkImpl_QueryInterface, - IDirectMusicSynthSinkImpl_AddRef, - IDirectMusicSynthSinkImpl_Release, - IDirectMusicSynthSinkImpl_Init, - IDirectMusicSynthSinkImpl_SetMasterClock, - IDirectMusicSynthSinkImpl_GetLatencyClock, - IDirectMusicSynthSinkImpl_Activate, - IDirectMusicSynthSinkImpl_SampleToRefTime, - IDirectMusicSynthSinkImpl_RefTimeToSample, - IDirectMusicSynthSinkImpl_SetDirectSound, - IDirectMusicSynthSinkImpl_GetDesiredBufferSize +static const IDirectMusicSynthSinkVtbl synth_sink_vtbl = +{ + synth_sink_QueryInterface, + synth_sink_AddRef, + synth_sink_Release, + synth_sink_Init, + synth_sink_SetMasterClock, + synth_sink_GetLatencyClock, + synth_sink_Activate, + synth_sink_SampleToRefTime, + synth_sink_RefTimeToSample, + synth_sink_SetDirectSound, + synth_sink_GetDesiredBufferSize, }; static inline IDirectMusicSynthSinkImpl *impl_from_IKsControl(IKsControl *iface) @@ -230,29 +231,29 @@ static inline IDirectMusicSynthSinkImpl *impl_from_IKsControl(IKsControl *iface) return CONTAINING_RECORD(iface, IDirectMusicSynthSinkImpl, IKsControl_iface); } -static HRESULT WINAPI DMSynthSinkImpl_IKsControl_QueryInterface(IKsControl* iface, REFIID riid, LPVOID *ppobj) +static HRESULT WINAPI synth_sink_control_QueryInterface(IKsControl* iface, REFIID riid, LPVOID *ppobj) { IDirectMusicSynthSinkImpl *This = impl_from_IKsControl(iface); - return IDirectMusicSynthSinkImpl_QueryInterface(&This->IDirectMusicSynthSink_iface, riid, ppobj); + return synth_sink_QueryInterface(&This->IDirectMusicSynthSink_iface, riid, ppobj); } -static ULONG WINAPI DMSynthSinkImpl_IKsControl_AddRef(IKsControl* iface) +static ULONG WINAPI synth_sink_control_AddRef(IKsControl* iface) { IDirectMusicSynthSinkImpl *This = impl_from_IKsControl(iface); - return IDirectMusicSynthSinkImpl_AddRef(&This->IDirectMusicSynthSink_iface); + return synth_sink_AddRef(&This->IDirectMusicSynthSink_iface); } -static ULONG WINAPI DMSynthSinkImpl_IKsControl_Release(IKsControl* iface) +static ULONG WINAPI synth_sink_control_Release(IKsControl* iface) { IDirectMusicSynthSinkImpl *This = impl_from_IKsControl(iface); - return IDirectMusicSynthSinkImpl_Release(&This->IDirectMusicSynthSink_iface); + return synth_sink_Release(&This->IDirectMusicSynthSink_iface); } -static HRESULT WINAPI DMSynthSinkImpl_IKsControl_KsProperty(IKsControl* iface, PKSPROPERTY Property, ULONG PropertyLength, LPVOID PropertyData, - ULONG DataLength, ULONG* BytesReturned) +static HRESULT WINAPI synth_sink_control_KsProperty(IKsControl* iface, PKSPROPERTY Property, + ULONG PropertyLength, LPVOID PropertyData, ULONG DataLength, ULONG* BytesReturned) { TRACE("(%p, %p, %lu, %p, %lu, %p)\n", iface, Property, PropertyLength, PropertyData, DataLength, BytesReturned); @@ -282,16 +283,16 @@ static HRESULT WINAPI DMSynthSinkImpl_IKsControl_KsProperty(IKsControl* iface, P return S_OK; } -static HRESULT WINAPI DMSynthSinkImpl_IKsControl_KsMethod(IKsControl* iface, PKSMETHOD Method, ULONG MethodLength, LPVOID MethodData, - ULONG DataLength, ULONG* BytesReturned) +static HRESULT WINAPI synth_sink_control_KsMethod(IKsControl* iface, PKSMETHOD Method, + ULONG MethodLength, LPVOID MethodData, ULONG DataLength, ULONG* BytesReturned) { FIXME("(%p, %p, %lu, %p, %lu, %p): stub\n", iface, Method, MethodLength, MethodData, DataLength, BytesReturned); return E_NOTIMPL; } -static HRESULT WINAPI DMSynthSinkImpl_IKsControl_KsEvent(IKsControl* iface, PKSEVENT Event, ULONG EventLength, LPVOID EventData, - ULONG DataLength, ULONG* BytesReturned) +static HRESULT WINAPI synth_sink_control_KsEvent(IKsControl* iface, PKSEVENT Event, + ULONG EventLength, LPVOID EventData, ULONG DataLength, ULONG* BytesReturned) { FIXME("(%p, %p, %lu, %p, %lu, %p): stub\n", iface, Event, EventLength, EventData, DataLength, BytesReturned); @@ -299,13 +300,14 @@ static HRESULT WINAPI DMSynthSinkImpl_IKsControl_KsEvent(IKsControl* iface, PKSE } -static const IKsControlVtbl DMSynthSinkImpl_IKsControl_Vtbl = { - DMSynthSinkImpl_IKsControl_QueryInterface, - DMSynthSinkImpl_IKsControl_AddRef, - DMSynthSinkImpl_IKsControl_Release, - DMSynthSinkImpl_IKsControl_KsProperty, - DMSynthSinkImpl_IKsControl_KsMethod, - DMSynthSinkImpl_IKsControl_KsEvent +static const IKsControlVtbl synth_sink_control = +{ + synth_sink_control_QueryInterface, + synth_sink_control_AddRef, + synth_sink_control_Release, + synth_sink_control_KsProperty, + synth_sink_control_KsMethod, + synth_sink_control_KsEvent, }; HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ret_iface) @@ -321,8 +323,8 @@ HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ret_iface) if (!obj) return E_OUTOFMEMORY; - obj->IDirectMusicSynthSink_iface.lpVtbl = &DirectMusicSynthSink_Vtbl; - obj->IKsControl_iface.lpVtbl = &DMSynthSinkImpl_IKsControl_Vtbl; + obj->IDirectMusicSynthSink_iface.lpVtbl = &synth_sink_vtbl; + obj->IKsControl_iface.lpVtbl = &synth_sink_control; obj->ref = 1; hr = CoCreateInstance(&CLSID_SystemClock, NULL, CLSCTX_INPROC_SERVER, &IID_IReferenceClock, (LPVOID*)&obj->latency_clock); From a90f8715c6fce1d12923428f721b5d2793b98822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 24 Aug 2023 15:29:23 +0200 Subject: [PATCH 0781/1148] dmsynth: Get rid of the IDirectMusicSynthSinkImpl typedef. (cherry picked from commit 1f990e59c002180028979e1b0373ddd7a28216fd) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synthsink.c | 44 +++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index 1e72e5d1f1b..29dd524ce89 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -27,9 +27,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); -typedef struct IDirectMusicSynthSinkImpl IDirectMusicSynthSinkImpl; - -struct IDirectMusicSynthSinkImpl +struct synth_sink { IDirectMusicSynthSink IDirectMusicSynthSink_iface; IKsControl IKsControl_iface; @@ -41,15 +39,15 @@ struct IDirectMusicSynthSinkImpl BOOL active; }; -static inline IDirectMusicSynthSinkImpl *impl_from_IDirectMusicSynthSink(IDirectMusicSynthSink *iface) +static inline struct synth_sink *impl_from_IDirectMusicSynthSink(IDirectMusicSynthSink *iface) { - return CONTAINING_RECORD(iface, IDirectMusicSynthSinkImpl, IDirectMusicSynthSink_iface); + return CONTAINING_RECORD(iface, struct synth_sink, IDirectMusicSynthSink_iface); } static HRESULT WINAPI synth_sink_QueryInterface(IDirectMusicSynthSink *iface, REFIID riid, void **ret_iface) { - IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); + struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); TRACE("(%p)->(%s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); @@ -76,7 +74,7 @@ static HRESULT WINAPI synth_sink_QueryInterface(IDirectMusicSynthSink *iface, static ULONG WINAPI synth_sink_AddRef(IDirectMusicSynthSink *iface) { - IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); + struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p): new ref = %lu\n", This, ref); @@ -86,7 +84,7 @@ static ULONG WINAPI synth_sink_AddRef(IDirectMusicSynthSink *iface) static ULONG WINAPI synth_sink_Release(IDirectMusicSynthSink *iface) { - IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); + struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p): new ref = %lu\n", This, ref); @@ -105,7 +103,7 @@ static ULONG WINAPI synth_sink_Release(IDirectMusicSynthSink *iface) static HRESULT WINAPI synth_sink_Init(IDirectMusicSynthSink *iface, IDirectMusicSynth *synth) { - IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); + struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); TRACE("(%p)->(%p)\n", This, synth); @@ -119,7 +117,7 @@ static HRESULT WINAPI synth_sink_Init(IDirectMusicSynthSink *iface, static HRESULT WINAPI synth_sink_SetMasterClock(IDirectMusicSynthSink *iface, IReferenceClock *clock) { - IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); + struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); TRACE("(%p)->(%p)\n", This, clock); @@ -137,7 +135,7 @@ static HRESULT WINAPI synth_sink_SetMasterClock(IDirectMusicSynthSink *iface, static HRESULT WINAPI synth_sink_GetLatencyClock(IDirectMusicSynthSink *iface, IReferenceClock **clock) { - IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); + struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); TRACE("(%p)->(%p)\n", iface, clock); @@ -153,7 +151,7 @@ static HRESULT WINAPI synth_sink_GetLatencyClock(IDirectMusicSynthSink *iface, static HRESULT WINAPI synth_sink_Activate(IDirectMusicSynthSink *iface, BOOL enable) { - IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); + struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); FIXME("(%p)->(%d): stub\n", This, enable); @@ -163,7 +161,7 @@ static HRESULT WINAPI synth_sink_Activate(IDirectMusicSynthSink *iface, static HRESULT WINAPI synth_sink_SampleToRefTime(IDirectMusicSynthSink *iface, LONGLONG sample_time, REFERENCE_TIME *ref_time) { - IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); + struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); FIXME("(%p)->(0x%s, %p): stub\n", This, wine_dbgstr_longlong(sample_time), ref_time); @@ -173,7 +171,7 @@ static HRESULT WINAPI synth_sink_SampleToRefTime(IDirectMusicSynthSink *iface, static HRESULT WINAPI synth_sink_RefTimeToSample(IDirectMusicSynthSink *iface, REFERENCE_TIME ref_time, LONGLONG *sample_time) { - IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); + struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); FIXME("(%p)->(0x%s, %p): stub\n", This, wine_dbgstr_longlong(ref_time), sample_time); @@ -183,7 +181,7 @@ static HRESULT WINAPI synth_sink_RefTimeToSample(IDirectMusicSynthSink *iface, static HRESULT WINAPI synth_sink_SetDirectSound(IDirectMusicSynthSink *iface, IDirectSound *dsound, IDirectSoundBuffer *dsound_buffer) { - IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); + struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); FIXME("(%p)->(%p, %p): stub\n", This, dsound, dsound_buffer); @@ -193,7 +191,7 @@ static HRESULT WINAPI synth_sink_SetDirectSound(IDirectMusicSynthSink *iface, static HRESULT WINAPI synth_sink_GetDesiredBufferSize(IDirectMusicSynthSink *iface, DWORD *size) { - IDirectMusicSynthSinkImpl *This = impl_from_IDirectMusicSynthSink(iface); + struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); WAVEFORMATEX format; DWORD fmtsize = sizeof(format); @@ -226,28 +224,28 @@ static const IDirectMusicSynthSinkVtbl synth_sink_vtbl = synth_sink_GetDesiredBufferSize, }; -static inline IDirectMusicSynthSinkImpl *impl_from_IKsControl(IKsControl *iface) +static inline struct synth_sink *impl_from_IKsControl(IKsControl *iface) { - return CONTAINING_RECORD(iface, IDirectMusicSynthSinkImpl, IKsControl_iface); + return CONTAINING_RECORD(iface, struct synth_sink, IKsControl_iface); } static HRESULT WINAPI synth_sink_control_QueryInterface(IKsControl* iface, REFIID riid, LPVOID *ppobj) { - IDirectMusicSynthSinkImpl *This = impl_from_IKsControl(iface); + struct synth_sink *This = impl_from_IKsControl(iface); return synth_sink_QueryInterface(&This->IDirectMusicSynthSink_iface, riid, ppobj); } static ULONG WINAPI synth_sink_control_AddRef(IKsControl* iface) { - IDirectMusicSynthSinkImpl *This = impl_from_IKsControl(iface); + struct synth_sink *This = impl_from_IKsControl(iface); return synth_sink_AddRef(&This->IDirectMusicSynthSink_iface); } static ULONG WINAPI synth_sink_control_Release(IKsControl* iface) { - IDirectMusicSynthSinkImpl *This = impl_from_IKsControl(iface); + struct synth_sink *This = impl_from_IKsControl(iface); return synth_sink_Release(&This->IDirectMusicSynthSink_iface); } @@ -312,14 +310,14 @@ static const IKsControlVtbl synth_sink_control = HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ret_iface) { - IDirectMusicSynthSinkImpl *obj; + struct synth_sink *obj; HRESULT hr; TRACE("(%s, %p)\n", debugstr_guid(riid), ret_iface); *ret_iface = NULL; - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicSynthSinkImpl)); + obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct synth_sink)); if (!obj) return E_OUTOFMEMORY; From abe7ee2f0830f0d3dfe98f0624ca4eccd7d33ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 18 Aug 2023 21:34:24 +0200 Subject: [PATCH 0782/1148] dmsynth: Move IDirectMusicSynthImpl struct to where it is used. (cherry picked from commit 68b356142c67343b6a69f0a22a42edf045c0a46d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/dmsynth_private.h | 20 -------------------- dlls/dmsynth/synth.c | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/dlls/dmsynth/dmsynth_private.h b/dlls/dmsynth/dmsynth_private.h index b9f160f000d..35c87775cf7 100644 --- a/dlls/dmsynth/dmsynth_private.h +++ b/dlls/dmsynth/dmsynth_private.h @@ -40,32 +40,12 @@ #include "dmusics.h" #include "dmksctrl.h" -/***************************************************************************** - * Interfaces - */ -typedef struct IDirectMusicSynth8Impl IDirectMusicSynth8Impl; - /***************************************************************************** * ClassFactory */ extern HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj); extern HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ppobj); -/***************************************************************************** - * IDirectMusicSynth8Impl implementation structure - */ -struct IDirectMusicSynth8Impl { - IDirectMusicSynth8 IDirectMusicSynth8_iface; - IKsControl IKsControl_iface; - LONG ref; - DMUS_PORTCAPS caps; - DMUS_PORTPARAMS params; - BOOL active; - BOOL open; - IReferenceClock *latency_clock; - IDirectMusicSynthSink *sink; -}; - /***************************************************************************** * Misc. */ diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 29aff9e988f..577cd755393 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -31,6 +31,22 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); +typedef struct IDirectMusicSynth8Impl IDirectMusicSynth8Impl; + +struct IDirectMusicSynth8Impl +{ + IDirectMusicSynth8 IDirectMusicSynth8_iface; + IKsControl IKsControl_iface; + LONG ref; + + DMUS_PORTCAPS caps; + DMUS_PORTPARAMS params; + BOOL active; + BOOL open; + IReferenceClock *latency_clock; + IDirectMusicSynthSink *sink; +}; + static inline IDirectMusicSynth8Impl *impl_from_IDirectMusicSynth8(IDirectMusicSynth8 *iface) { return CONTAINING_RECORD(iface, IDirectMusicSynth8Impl, IDirectMusicSynth8_iface); From ff2c6a1987885fd461789200b84084a9443d6c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 18 Aug 2023 21:51:42 +0200 Subject: [PATCH 0783/1148] dmsynth: Rename IDirectMusicSynth8Impl method prefix to synth. (cherry picked from commit 011dd289e66b2d1db121ec6e21d2dfe2275b8840) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 146 ++++++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 72 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 577cd755393..fcb6892cdcd 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -52,7 +52,7 @@ static inline IDirectMusicSynth8Impl *impl_from_IDirectMusicSynth8(IDirectMusicS return CONTAINING_RECORD(iface, IDirectMusicSynth8Impl, IDirectMusicSynth8_iface); } -static HRESULT WINAPI IDirectMusicSynth8Impl_QueryInterface(IDirectMusicSynth8 *iface, REFIID riid, +static HRESULT WINAPI synth_QueryInterface(IDirectMusicSynth8 *iface, REFIID riid, void **ret_iface) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -81,7 +81,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_QueryInterface(IDirectMusicSynth8 * return E_NOINTERFACE; } -static ULONG WINAPI IDirectMusicSynth8Impl_AddRef(IDirectMusicSynth8 *iface) +static ULONG WINAPI synth_AddRef(IDirectMusicSynth8 *iface) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); ULONG ref = InterlockedIncrement(&This->ref); @@ -91,7 +91,7 @@ static ULONG WINAPI IDirectMusicSynth8Impl_AddRef(IDirectMusicSynth8 *iface) return ref; } -static ULONG WINAPI IDirectMusicSynth8Impl_Release(IDirectMusicSynth8 *iface) +static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); ULONG ref = InterlockedDecrement(&This->ref); @@ -107,7 +107,7 @@ static ULONG WINAPI IDirectMusicSynth8Impl_Release(IDirectMusicSynth8 *iface) return ref; } -static HRESULT WINAPI IDirectMusicSynth8Impl_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *params) +static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *params) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); BOOL modified = FALSE; @@ -193,7 +193,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_Open(IDirectMusicSynth8 *iface, DMU return modified ? S_FALSE : S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_Close(IDirectMusicSynth8 *iface) +static HRESULT WINAPI synth_Close(IDirectMusicSynth8 *iface) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -207,7 +207,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_Close(IDirectMusicSynth8 *iface) return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_SetNumChannelGroups(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_SetNumChannelGroups(IDirectMusicSynth8 *iface, DWORD groups) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -217,7 +217,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_SetNumChannelGroups(IDirectMusicSyn return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_Download(IDirectMusicSynth8 *iface, HANDLE *hDownload, +static HRESULT WINAPI synth_Download(IDirectMusicSynth8 *iface, HANDLE *hDownload, void *data, BOOL *free) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -352,7 +352,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_Download(IDirectMusicSynth8 *iface, return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_Unload(IDirectMusicSynth8 *iface, HANDLE hDownload, +static HRESULT WINAPI synth_Unload(IDirectMusicSynth8 *iface, HANDLE hDownload, HRESULT (CALLBACK *lpFreeHandle)(HANDLE,HANDLE), HANDLE hUserData) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -362,7 +362,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_Unload(IDirectMusicSynth8 *iface, H return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_PlayBuffer(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_PlayBuffer(IDirectMusicSynth8 *iface, REFERENCE_TIME rt, BYTE *buffer, DWORD size) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -372,7 +372,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_PlayBuffer(IDirectMusicSynth8 *ifac return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_GetRunningStats(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_GetRunningStats(IDirectMusicSynth8 *iface, DMUS_SYNTHSTATS *stats) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -382,7 +382,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_GetRunningStats(IDirectMusicSynth8 return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_GetPortCaps(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_GetPortCaps(IDirectMusicSynth8 *iface, DMUS_PORTCAPS *caps) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -397,7 +397,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_GetPortCaps(IDirectMusicSynth8 *ifa return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_SetMasterClock(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_SetMasterClock(IDirectMusicSynth8 *iface, IReferenceClock *clock) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -410,7 +410,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_SetMasterClock(IDirectMusicSynth8 * return IDirectMusicSynthSink_SetMasterClock(This->sink, clock); } -static HRESULT WINAPI IDirectMusicSynth8Impl_GetLatencyClock(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_GetLatencyClock(IDirectMusicSynth8 *iface, IReferenceClock **clock) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -429,7 +429,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_GetLatencyClock(IDirectMusicSynth8 return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_Activate(IDirectMusicSynth8 *iface, BOOL enable) +static HRESULT WINAPI synth_Activate(IDirectMusicSynth8 *iface, BOOL enable) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); HRESULT hr; @@ -458,7 +458,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_Activate(IDirectMusicSynth8 *iface, return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_SetSynthSink(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_SetSynthSink(IDirectMusicSynth8 *iface, IDirectMusicSynthSink *sink) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -486,7 +486,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_SetSynthSink(IDirectMusicSynth8 *if return IDirectMusicSynthSink_GetLatencyClock(sink, &This->latency_clock); } -static HRESULT WINAPI IDirectMusicSynth8Impl_Render(IDirectMusicSynth8 *iface, short *buffer, +static HRESULT WINAPI synth_Render(IDirectMusicSynth8 *iface, short *buffer, DWORD length, LONGLONG position) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -496,7 +496,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_Render(IDirectMusicSynth8 *iface, s return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_SetChannelPriority(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_SetChannelPriority(IDirectMusicSynth8 *iface, DWORD channel_group, DWORD channel, DWORD priority) { /* IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); */ @@ -507,7 +507,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_SetChannelPriority(IDirectMusicSynt return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_GetChannelPriority(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_GetChannelPriority(IDirectMusicSynth8 *iface, DWORD channel_group, DWORD channel, DWORD *priority) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -517,7 +517,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_GetChannelPriority(IDirectMusicSynt return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_GetFormat(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_GetFormat(IDirectMusicSynth8 *iface, WAVEFORMATEX *format, DWORD *size) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -545,7 +545,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_GetFormat(IDirectMusicSynth8 *iface return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_GetAppend(IDirectMusicSynth8 *iface, DWORD *append) +static HRESULT WINAPI synth_GetAppend(IDirectMusicSynth8 *iface, DWORD *append) { TRACE("(%p)->(%p)\n", iface, append); @@ -555,7 +555,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_GetAppend(IDirectMusicSynth8 *iface return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_PlayVoice(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_PlayVoice(IDirectMusicSynth8 *iface, REFERENCE_TIME ref_time, DWORD voice_id, DWORD channel_group, DWORD channel, DWORD dwDLId, LONG prPitch, LONG vrVolume, SAMPLE_TIME stVoiceStart, SAMPLE_TIME stLoopStart, SAMPLE_TIME stLoopEnd) @@ -569,7 +569,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_PlayVoice(IDirectMusicSynth8 *iface return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_StopVoice(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_StopVoice(IDirectMusicSynth8 *iface, REFERENCE_TIME ref_time, DWORD voice_id) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -579,7 +579,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_StopVoice(IDirectMusicSynth8 *iface return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_GetVoiceState(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_GetVoiceState(IDirectMusicSynth8 *iface, DWORD dwVoice[], DWORD cbVoice, DMUS_VOICE_STATE dwVoiceState[]) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -589,7 +589,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_GetVoiceState(IDirectMusicSynth8 *i return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_Refresh(IDirectMusicSynth8 *iface, DWORD download_id, +static HRESULT WINAPI synth_Refresh(IDirectMusicSynth8 *iface, DWORD download_id, DWORD flags) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -599,7 +599,7 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_Refresh(IDirectMusicSynth8 *iface, return S_OK; } -static HRESULT WINAPI IDirectMusicSynth8Impl_AssignChannelToBuses(IDirectMusicSynth8 *iface, +static HRESULT WINAPI synth_AssignChannelToBuses(IDirectMusicSynth8 *iface, DWORD channel_group, DWORD channel, DWORD *pdwBuses, DWORD cBuses) { IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); @@ -609,32 +609,33 @@ static HRESULT WINAPI IDirectMusicSynth8Impl_AssignChannelToBuses(IDirectMusicSy return S_OK; } -static const IDirectMusicSynth8Vtbl DirectMusicSynth8_Vtbl = { - IDirectMusicSynth8Impl_QueryInterface, - IDirectMusicSynth8Impl_AddRef, - IDirectMusicSynth8Impl_Release, - IDirectMusicSynth8Impl_Open, - IDirectMusicSynth8Impl_Close, - IDirectMusicSynth8Impl_SetNumChannelGroups, - IDirectMusicSynth8Impl_Download, - IDirectMusicSynth8Impl_Unload, - IDirectMusicSynth8Impl_PlayBuffer, - IDirectMusicSynth8Impl_GetRunningStats, - IDirectMusicSynth8Impl_GetPortCaps, - IDirectMusicSynth8Impl_SetMasterClock, - IDirectMusicSynth8Impl_GetLatencyClock, - IDirectMusicSynth8Impl_Activate, - IDirectMusicSynth8Impl_SetSynthSink, - IDirectMusicSynth8Impl_Render, - IDirectMusicSynth8Impl_SetChannelPriority, - IDirectMusicSynth8Impl_GetChannelPriority, - IDirectMusicSynth8Impl_GetFormat, - IDirectMusicSynth8Impl_GetAppend, - IDirectMusicSynth8Impl_PlayVoice, - IDirectMusicSynth8Impl_StopVoice, - IDirectMusicSynth8Impl_GetVoiceState, - IDirectMusicSynth8Impl_Refresh, - IDirectMusicSynth8Impl_AssignChannelToBuses +static const IDirectMusicSynth8Vtbl synth_vtbl = +{ + synth_QueryInterface, + synth_AddRef, + synth_Release, + synth_Open, + synth_Close, + synth_SetNumChannelGroups, + synth_Download, + synth_Unload, + synth_PlayBuffer, + synth_GetRunningStats, + synth_GetPortCaps, + synth_SetMasterClock, + synth_GetLatencyClock, + synth_Activate, + synth_SetSynthSink, + synth_Render, + synth_SetChannelPriority, + synth_GetChannelPriority, + synth_GetFormat, + synth_GetAppend, + synth_PlayVoice, + synth_StopVoice, + synth_GetVoiceState, + synth_Refresh, + synth_AssignChannelToBuses, }; static inline IDirectMusicSynth8Impl *impl_from_IKsControl(IKsControl *iface) @@ -642,29 +643,29 @@ static inline IDirectMusicSynth8Impl *impl_from_IKsControl(IKsControl *iface) return CONTAINING_RECORD(iface, IDirectMusicSynth8Impl, IKsControl_iface); } -static HRESULT WINAPI DMSynthImpl_IKsControl_QueryInterface(IKsControl* iface, REFIID riid, LPVOID *ppobj) +static HRESULT WINAPI synth_control_QueryInterface(IKsControl* iface, REFIID riid, LPVOID *ppobj) { IDirectMusicSynth8Impl *This = impl_from_IKsControl(iface); - return IDirectMusicSynth8Impl_QueryInterface(&This->IDirectMusicSynth8_iface, riid, ppobj); + return synth_QueryInterface(&This->IDirectMusicSynth8_iface, riid, ppobj); } -static ULONG WINAPI DMSynthImpl_IKsControl_AddRef(IKsControl* iface) +static ULONG WINAPI synth_control_AddRef(IKsControl* iface) { IDirectMusicSynth8Impl *This = impl_from_IKsControl(iface); - return IDirectMusicSynth8Impl_AddRef(&This->IDirectMusicSynth8_iface); + return synth_AddRef(&This->IDirectMusicSynth8_iface); } -static ULONG WINAPI DMSynthImpl_IKsControl_Release(IKsControl* iface) +static ULONG WINAPI synth_control_Release(IKsControl* iface) { IDirectMusicSynth8Impl *This = impl_from_IKsControl(iface); - return IDirectMusicSynth8Impl_Release(&This->IDirectMusicSynth8_iface); + return synth_Release(&This->IDirectMusicSynth8_iface); } -static HRESULT WINAPI DMSynthImpl_IKsControl_KsProperty(IKsControl* iface, PKSPROPERTY Property, ULONG PropertyLength, LPVOID PropertyData, - ULONG DataLength, ULONG* BytesReturned) +static HRESULT WINAPI synth_control_KsProperty(IKsControl* iface, PKSPROPERTY Property, + ULONG PropertyLength, LPVOID PropertyData, ULONG DataLength, ULONG* BytesReturned) { TRACE("(%p, %p, %lu, %p, %lu, %p)\n", iface, Property, PropertyLength, PropertyData, DataLength, BytesReturned); @@ -714,16 +715,16 @@ static HRESULT WINAPI DMSynthImpl_IKsControl_KsProperty(IKsControl* iface, PKSPR return S_OK; } -static HRESULT WINAPI DMSynthImpl_IKsControl_KsMethod(IKsControl* iface, PKSMETHOD Method, ULONG MethodLength, LPVOID MethodData, - ULONG DataLength, ULONG* BytesReturned) +static HRESULT WINAPI synth_control_KsMethod(IKsControl* iface, PKSMETHOD Method, + ULONG MethodLength, LPVOID MethodData, ULONG DataLength, ULONG* BytesReturned) { FIXME("(%p, %p, %lu, %p, %lu, %p): stub\n", iface, Method, MethodLength, MethodData, DataLength, BytesReturned); return E_NOTIMPL; } -static HRESULT WINAPI DMSynthImpl_IKsControl_KsEvent(IKsControl* iface, PKSEVENT Event, ULONG EventLength, LPVOID EventData, - ULONG DataLength, ULONG* BytesReturned) +static HRESULT WINAPI synth_control_KsEvent(IKsControl* iface, PKSEVENT Event, + ULONG EventLength, LPVOID EventData, ULONG DataLength, ULONG* BytesReturned) { FIXME("(%p, %p, %lu, %p, %lu, %p): stub\n", iface, Event, EventLength, EventData, DataLength, BytesReturned); @@ -731,13 +732,14 @@ static HRESULT WINAPI DMSynthImpl_IKsControl_KsEvent(IKsControl* iface, PKSEVENT } -static const IKsControlVtbl DMSynthImpl_IKsControl_Vtbl = { - DMSynthImpl_IKsControl_QueryInterface, - DMSynthImpl_IKsControl_AddRef, - DMSynthImpl_IKsControl_Release, - DMSynthImpl_IKsControl_KsProperty, - DMSynthImpl_IKsControl_KsMethod, - DMSynthImpl_IKsControl_KsEvent +static const IKsControlVtbl synth_control_vtbl = +{ + synth_control_QueryInterface, + synth_control_AddRef, + synth_control_Release, + synth_control_KsProperty, + synth_control_KsMethod, + synth_control_KsEvent, }; HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj) @@ -752,8 +754,8 @@ HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj) *ppobj = NULL; return E_OUTOFMEMORY; } - obj->IDirectMusicSynth8_iface.lpVtbl = &DirectMusicSynth8_Vtbl; - obj->IKsControl_iface.lpVtbl = &DMSynthImpl_IKsControl_Vtbl; + obj->IDirectMusicSynth8_iface.lpVtbl = &synth_vtbl; + obj->IKsControl_iface.lpVtbl = &synth_control_vtbl; obj->ref = 1; /* fill in caps */ obj->caps.dwSize = sizeof(DMUS_PORTCAPS); From 8e0a6223125f18d0c11fec7489e4be8bb25a242c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 24 Aug 2023 15:30:25 +0200 Subject: [PATCH 0784/1148] dmsynth: Get rid of the IDirectMusicSynth8Impl typedef. (cherry picked from commit 5e29d14789407a49576ad7be296bbd80b3ff773f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 68 +++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index fcb6892cdcd..79bf9f3bf92 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -31,9 +31,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); -typedef struct IDirectMusicSynth8Impl IDirectMusicSynth8Impl; - -struct IDirectMusicSynth8Impl +struct synth { IDirectMusicSynth8 IDirectMusicSynth8_iface; IKsControl IKsControl_iface; @@ -47,15 +45,15 @@ struct IDirectMusicSynth8Impl IDirectMusicSynthSink *sink; }; -static inline IDirectMusicSynth8Impl *impl_from_IDirectMusicSynth8(IDirectMusicSynth8 *iface) +static inline struct synth *impl_from_IDirectMusicSynth8(IDirectMusicSynth8 *iface) { - return CONTAINING_RECORD(iface, IDirectMusicSynth8Impl, IDirectMusicSynth8_iface); + return CONTAINING_RECORD(iface, struct synth, IDirectMusicSynth8_iface); } static HRESULT WINAPI synth_QueryInterface(IDirectMusicSynth8 *iface, REFIID riid, void **ret_iface) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); TRACE("(%p)->(%s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); @@ -83,7 +81,7 @@ static HRESULT WINAPI synth_QueryInterface(IDirectMusicSynth8 *iface, REFIID rii static ULONG WINAPI synth_AddRef(IDirectMusicSynth8 *iface) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p): new ref = %lu\n", This, ref); @@ -93,7 +91,7 @@ static ULONG WINAPI synth_AddRef(IDirectMusicSynth8 *iface) static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p): new ref = %lu\n", This, ref); @@ -109,7 +107,7 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *params) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); BOOL modified = FALSE; const DMUS_PORTPARAMS def = { .dwValidParams = DMUS_PORTPARAMS_VOICES|DMUS_PORTPARAMS_CHANNELGROUPS| @@ -195,7 +193,7 @@ static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *par static HRESULT WINAPI synth_Close(IDirectMusicSynth8 *iface) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); TRACE("(%p)\n", This); @@ -210,7 +208,7 @@ static HRESULT WINAPI synth_Close(IDirectMusicSynth8 *iface) static HRESULT WINAPI synth_SetNumChannelGroups(IDirectMusicSynth8 *iface, DWORD groups) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); FIXME("(%p, %ld): stub\n", This, groups); @@ -220,7 +218,7 @@ static HRESULT WINAPI synth_SetNumChannelGroups(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_Download(IDirectMusicSynth8 *iface, HANDLE *hDownload, void *data, BOOL *free) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); LPBYTE buffer = data; DMUS_DOWNLOADINFO *info = (DMUS_DOWNLOADINFO*)buffer; ULONG *offsets = ((DMUS_OFFSETTABLE*)(buffer + sizeof(DMUS_DOWNLOADINFO)))->ulOffsetTable; @@ -355,7 +353,7 @@ static HRESULT WINAPI synth_Download(IDirectMusicSynth8 *iface, HANDLE *hDownloa static HRESULT WINAPI synth_Unload(IDirectMusicSynth8 *iface, HANDLE hDownload, HRESULT (CALLBACK *lpFreeHandle)(HANDLE,HANDLE), HANDLE hUserData) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); FIXME("(%p)->(%p, %p, %p): stub\n", This, hDownload, lpFreeHandle, hUserData); @@ -365,7 +363,7 @@ static HRESULT WINAPI synth_Unload(IDirectMusicSynth8 *iface, HANDLE hDownload, static HRESULT WINAPI synth_PlayBuffer(IDirectMusicSynth8 *iface, REFERENCE_TIME rt, BYTE *buffer, DWORD size) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); FIXME("(%p, 0x%s, %p, %lu): stub\n", This, wine_dbgstr_longlong(rt), buffer, size); @@ -375,7 +373,7 @@ static HRESULT WINAPI synth_PlayBuffer(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_GetRunningStats(IDirectMusicSynth8 *iface, DMUS_SYNTHSTATS *stats) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); FIXME("(%p)->(%p): stub\n", This, stats); @@ -385,7 +383,7 @@ static HRESULT WINAPI synth_GetRunningStats(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_GetPortCaps(IDirectMusicSynth8 *iface, DMUS_PORTCAPS *caps) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); TRACE("(%p)->(%p)\n", This, caps); @@ -400,7 +398,7 @@ static HRESULT WINAPI synth_GetPortCaps(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_SetMasterClock(IDirectMusicSynth8 *iface, IReferenceClock *clock) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); TRACE("(%p)->(%p)\n", This, clock); @@ -413,7 +411,7 @@ static HRESULT WINAPI synth_SetMasterClock(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_GetLatencyClock(IDirectMusicSynth8 *iface, IReferenceClock **clock) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); TRACE("(%p)->(%p)\n", iface, clock); @@ -431,7 +429,7 @@ static HRESULT WINAPI synth_GetLatencyClock(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_Activate(IDirectMusicSynth8 *iface, BOOL enable) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); HRESULT hr; TRACE("(%p)->(%d)\n", This, enable); @@ -461,7 +459,7 @@ static HRESULT WINAPI synth_Activate(IDirectMusicSynth8 *iface, BOOL enable) static HRESULT WINAPI synth_SetSynthSink(IDirectMusicSynth8 *iface, IDirectMusicSynthSink *sink) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); HRESULT hr; TRACE("(%p)->(%p)\n", iface, sink); @@ -489,7 +487,7 @@ static HRESULT WINAPI synth_SetSynthSink(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_Render(IDirectMusicSynth8 *iface, short *buffer, DWORD length, LONGLONG position) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); FIXME("(%p, %p, %ld, 0x%s): stub\n", This, buffer, length, wine_dbgstr_longlong(position)); @@ -499,7 +497,7 @@ static HRESULT WINAPI synth_Render(IDirectMusicSynth8 *iface, short *buffer, static HRESULT WINAPI synth_SetChannelPriority(IDirectMusicSynth8 *iface, DWORD channel_group, DWORD channel, DWORD priority) { - /* IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); */ + /* struct synth *This = impl_from_IDirectMusicSynth8(iface); */ /* Silenced because of too many messages - 1000 groups * 16 channels ;=) */ /* FIXME("(%p)->(%ld, %ld, %ld): stub\n", This, channel_group, channel, priority); */ @@ -510,7 +508,7 @@ static HRESULT WINAPI synth_SetChannelPriority(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_GetChannelPriority(IDirectMusicSynth8 *iface, DWORD channel_group, DWORD channel, DWORD *priority) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); FIXME("(%p, %ld, %ld, %p): stub\n", This, channel_group, channel, priority); @@ -520,7 +518,7 @@ static HRESULT WINAPI synth_GetChannelPriority(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_GetFormat(IDirectMusicSynth8 *iface, WAVEFORMATEX *format, DWORD *size) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); WAVEFORMATEX fmt; TRACE("(%p, %p, %p)\n", This, format, size); @@ -560,7 +558,7 @@ static HRESULT WINAPI synth_PlayVoice(IDirectMusicSynth8 *iface, LONG prPitch, LONG vrVolume, SAMPLE_TIME stVoiceStart, SAMPLE_TIME stLoopStart, SAMPLE_TIME stLoopEnd) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); FIXME("(%p, 0x%s, %ld, %ld, %ld, %ld, %li, %li, 0x%s, 0x%s, 0x%s): stub\n", This, wine_dbgstr_longlong(ref_time), voice_id, channel_group, channel, dwDLId, prPitch, vrVolume, @@ -572,7 +570,7 @@ static HRESULT WINAPI synth_PlayVoice(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_StopVoice(IDirectMusicSynth8 *iface, REFERENCE_TIME ref_time, DWORD voice_id) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); FIXME("(%p, 0x%s, %ld): stub\n", This, wine_dbgstr_longlong(ref_time), voice_id); @@ -582,7 +580,7 @@ static HRESULT WINAPI synth_StopVoice(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_GetVoiceState(IDirectMusicSynth8 *iface, DWORD dwVoice[], DWORD cbVoice, DMUS_VOICE_STATE dwVoiceState[]) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); FIXME("(%p, %p, %ld, %p): stub\n", This, dwVoice, cbVoice, dwVoiceState); @@ -592,7 +590,7 @@ static HRESULT WINAPI synth_GetVoiceState(IDirectMusicSynth8 *iface, static HRESULT WINAPI synth_Refresh(IDirectMusicSynth8 *iface, DWORD download_id, DWORD flags) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); FIXME("(%p, %ld, %ld): stub\n", This, download_id, flags); @@ -602,7 +600,7 @@ static HRESULT WINAPI synth_Refresh(IDirectMusicSynth8 *iface, DWORD download_id static HRESULT WINAPI synth_AssignChannelToBuses(IDirectMusicSynth8 *iface, DWORD channel_group, DWORD channel, DWORD *pdwBuses, DWORD cBuses) { - IDirectMusicSynth8Impl *This = impl_from_IDirectMusicSynth8(iface); + struct synth *This = impl_from_IDirectMusicSynth8(iface); FIXME("(%p, %ld, %ld, %p, %ld): stub\n", This, channel_group, channel, pdwBuses, cBuses); @@ -638,28 +636,28 @@ static const IDirectMusicSynth8Vtbl synth_vtbl = synth_AssignChannelToBuses, }; -static inline IDirectMusicSynth8Impl *impl_from_IKsControl(IKsControl *iface) +static inline struct synth *impl_from_IKsControl(IKsControl *iface) { - return CONTAINING_RECORD(iface, IDirectMusicSynth8Impl, IKsControl_iface); + return CONTAINING_RECORD(iface, struct synth, IKsControl_iface); } static HRESULT WINAPI synth_control_QueryInterface(IKsControl* iface, REFIID riid, LPVOID *ppobj) { - IDirectMusicSynth8Impl *This = impl_from_IKsControl(iface); + struct synth *This = impl_from_IKsControl(iface); return synth_QueryInterface(&This->IDirectMusicSynth8_iface, riid, ppobj); } static ULONG WINAPI synth_control_AddRef(IKsControl* iface) { - IDirectMusicSynth8Impl *This = impl_from_IKsControl(iface); + struct synth *This = impl_from_IKsControl(iface); return synth_AddRef(&This->IDirectMusicSynth8_iface); } static ULONG WINAPI synth_control_Release(IKsControl* iface) { - IDirectMusicSynth8Impl *This = impl_from_IKsControl(iface); + struct synth *This = impl_from_IKsControl(iface); return synth_Release(&This->IDirectMusicSynth8_iface); } @@ -744,7 +742,7 @@ static const IKsControlVtbl synth_control_vtbl = HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj) { - IDirectMusicSynth8Impl *obj; + struct synth *obj; HRESULT hr; TRACE("(%s, %p)\n", debugstr_guid(riid), ppobj); From 4342c4860aa864fe6e181ae3e71af01bcbb03623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 29 Aug 2023 15:19:11 +0200 Subject: [PATCH 0785/1148] dmsynth: Use CRT allocation functions. (cherry picked from commit 6dedfec9c323dafe6d6bab33fee68c12606d6466) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 9 +++------ dlls/dmsynth/synthsink.c | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 79bf9f3bf92..fecbe21c2e3 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -99,7 +99,7 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) if (!ref) { if (This->latency_clock) IReferenceClock_Release(This->latency_clock); - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -747,11 +747,8 @@ HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj) TRACE("(%s, %p)\n", debugstr_guid(riid), ppobj); - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj)); - if (NULL == obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicSynth8_iface.lpVtbl = &synth_vtbl; obj->IKsControl_iface.lpVtbl = &synth_control_vtbl; obj->ref = 1; diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index 29dd524ce89..2200abd93b1 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -94,7 +94,7 @@ static ULONG WINAPI synth_sink_Release(IDirectMusicSynthSink *iface) IReferenceClock_Release(This->latency_clock); if (This->master_clock) IReferenceClock_Release(This->master_clock); - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -317,10 +317,7 @@ HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ret_iface) *ret_iface = NULL; - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct synth_sink)); - if (!obj) - return E_OUTOFMEMORY; - + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicSynthSink_iface.lpVtbl = &synth_sink_vtbl; obj->IKsControl_iface.lpVtbl = &synth_sink_control; obj->ref = 1; @@ -328,7 +325,7 @@ HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ret_iface) hr = CoCreateInstance(&CLSID_SystemClock, NULL, CLSCTX_INPROC_SERVER, &IID_IReferenceClock, (LPVOID*)&obj->latency_clock); if (FAILED(hr)) { - HeapFree(GetProcessHeap(), 0, obj); + free(obj); return hr; } From 51c862389c7e8c06649178c6d147ffa081c41f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 24 Aug 2023 15:59:50 +0200 Subject: [PATCH 0786/1148] dmsynth/tests: Avoid dynamic format string. (cherry picked from commit 40dcb5954bf9ff6161222ce703c6fc60a22c6b53) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/tests/dmsynth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index a8bf65cd73e..77c11dadd27 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -57,7 +57,7 @@ static void test_synth_getformat(IDirectMusicSynth *synth, DMUS_PORTPARAMS *para DWORD size; HRESULT hr; - winetest_push_context(context); + winetest_push_context("%s", context); size = sizeof(format); hr = IDirectMusicSynth_GetFormat(synth, &format, &size); From 7776963d4836290df2c5761bd6415fc9b6ac8fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 10 Aug 2023 16:00:22 +0200 Subject: [PATCH 0787/1148] dmsynth/tests: Import and use a check_interface helper. (cherry picked from commit e477e3e30daf1c78c28c39b1c7948e12f84fce7d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/tests/dmsynth.c | 72 ++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 77c11dadd27..99b03f63847 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -51,6 +51,27 @@ static ULONG get_refcount(void *iface) return IUnknown_Release(unknown); } +#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) +static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) +{ + ULONG expect_ref = get_refcount(iface_ptr); + IUnknown *iface = iface_ptr; + HRESULT hr, expected; + IUnknown *unk; + + expected = supported ? S_OK : E_NOINTERFACE; + hr = IUnknown_QueryInterface(iface, iid, (void **)&unk); + ok_(__FILE__, line)(hr == expected, "got hr %#lx, expected %#lx.\n", hr, expected); + if (SUCCEEDED(hr)) + { + LONG ref = get_refcount(unk); + ok_(__FILE__, line)(ref == expect_ref + 1, "got %ld\n", ref); + IUnknown_Release(unk); + ref = get_refcount(iface_ptr); + ok_(__FILE__, line)(ref == expect_ref, "got %ld\n", ref); + } +} + static void test_synth_getformat(IDirectMusicSynth *synth, DMUS_PORTPARAMS *params, const char *context) { WAVEFORMATEX format; @@ -317,9 +338,6 @@ static void test_dmsynth(void) static void test_COM(void) { IDirectMusicSynth8 *dms8 = (IDirectMusicSynth8*)0xdeadbeef; - IKsControl *iksc; - IUnknown *unk; - ULONG refcount; HRESULT hr; /* COM aggregation */ @@ -334,40 +352,23 @@ static void test_COM(void) &IID_IDirectMusicObject, (void**)&dms8); ok(hr == E_NOINTERFACE, "DirectMusicSynth create failed: %#lx, expected E_NOINTERFACE\n", hr); - /* Same refcount for all DirectMusicSynth interfaces */ hr = CoCreateInstance(&CLSID_DirectMusicSynth, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicSynth8, (void**)&dms8); ok(hr == S_OK, "DirectMusicSynth create failed: %#lx, expected S_OK\n", hr); - refcount = IDirectMusicSynth8_AddRef(dms8); - ok(refcount == 2, "refcount == %lu, expected 2\n", refcount); - - hr = IDirectMusicSynth8_QueryInterface(dms8, &IID_IKsControl, (void**)&iksc); - ok(hr == S_OK, "QueryInterface for IID_IKsControl failed: %#lx\n", hr); - refcount = IKsControl_AddRef(iksc); - ok(refcount == 4, "refcount == %lu, expected 4\n", refcount); - IKsControl_Release(iksc); - hr = IDirectMusicSynth8_QueryInterface(dms8, &IID_IUnknown, (void**)&unk); - ok(hr == S_OK, "QueryInterface for IID_IUnknown failed: %#lx\n", hr); - refcount = IUnknown_AddRef(unk); - ok(refcount == 5, "refcount == %lu, expected 5\n", refcount); - IUnknown_Release(unk); + check_interface(dms8, &IID_IUnknown, TRUE); + check_interface(dms8, &IID_IKsControl, TRUE); /* Unsupported interfaces */ - hr = IDirectMusicSynth8_QueryInterface(dms8, &IID_IDirectMusicSynthSink, (void**)&unk); - ok(hr == E_NOINTERFACE, "QueryInterface for IID_IDirectMusicSynthSink failed: %#lx\n", hr); - hr = IDirectMusicSynth8_QueryInterface(dms8, &IID_IReferenceClock, (void**)&unk); - ok(hr == E_NOINTERFACE, "QueryInterface for IID_IReferenceClock failed: %#lx\n", hr); + check_interface(dms8, &IID_IDirectMusicSynthSink, FALSE); + check_interface(dms8, &IID_IReferenceClock, FALSE); - while (IDirectMusicSynth8_Release(dms8)); + IDirectMusicSynth8_Release(dms8); } static void test_COM_synthsink(void) { IDirectMusicSynthSink *dmss = (IDirectMusicSynthSink*)0xdeadbeef; - IKsControl *iksc; - IUnknown *unk; - ULONG refcount; HRESULT hr; /* COM aggregation */ @@ -386,27 +387,16 @@ static void test_COM_synthsink(void) hr = CoCreateInstance(&CLSID_DirectMusicSynthSink, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicSynthSink, (void**)&dmss); ok(hr == S_OK, "DirectMusicSynthSink create failed: %#lx, expected S_OK\n", hr); - refcount = IDirectMusicSynthSink_AddRef(dmss); - ok(refcount == 2, "refcount == %lu, expected 2\n", refcount); - - hr = IDirectMusicSynthSink_QueryInterface(dmss, &IID_IKsControl, (void**)&iksc); - ok(hr == S_OK, "QueryInterface for IID_IKsControl failed: %#lx\n", hr); - refcount = IKsControl_AddRef(iksc); - ok(refcount == 4, "refcount == %lu, expected 4\n", refcount); - IKsControl_Release(iksc); - hr = IDirectMusicSynthSink_QueryInterface(dmss, &IID_IUnknown, (void**)&unk); - ok(hr == S_OK, "QueryInterface for IID_IUnknown failed: %#lx\n", hr); - refcount = IUnknown_AddRef(unk); - ok(refcount == 5, "refcount == %lu, expected 5\n", refcount); - IUnknown_Release(unk); + check_interface(dmss, &IID_IUnknown, TRUE); + check_interface(dmss, &IID_IKsControl, TRUE); /* Unsupported interfaces */ - hr = IDirectMusicSynthSink_QueryInterface(dmss, &IID_IReferenceClock, (void**)&unk); - ok(hr == E_NOINTERFACE, "QueryInterface for IID_IReferenceClock failed: %#lx\n", hr); + check_interface(dmss, &IID_IReferenceClock, FALSE); - while (IDirectMusicSynthSink_Release(dmss)); + IDirectMusicSynthSink_Release(dmss); } + START_TEST(dmsynth) { CoInitializeEx(NULL, COINIT_MULTITHREADED); From 624cdd266516e915792ded7c2abd89fd1672fb88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 27 Aug 2023 13:54:19 +0200 Subject: [PATCH 0788/1148] dmsynth/tests: Test DirectMusicSynthSink class in isolation. (cherry picked from commit a2dac48f6c6f75da2f0bb3d7a374b58f2ef9fb45) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/tests/Makefile.in | 2 +- dlls/dmsynth/tests/dmsynth.c | 387 +++++++++++++++++++++++++++++++++ 2 files changed, 388 insertions(+), 1 deletion(-) diff --git a/dlls/dmsynth/tests/Makefile.in b/dlls/dmsynth/tests/Makefile.in index 5f2fce7b8b1..1119bb09ce7 100644 --- a/dlls/dmsynth/tests/Makefile.in +++ b/dlls/dmsynth/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = dmsynth.dll -IMPORTS = oleaut32 ole32 uuid +IMPORTS = oleaut32 ole32 uuid dsound user32 C_SRCS = \ dmsynth.c diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 99b03f63847..c8976f50e21 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -72,6 +72,217 @@ static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOO } } +struct test_synth +{ + IDirectMusicSynth8 IDirectMusicSynth8_iface; + LONG refcount; +}; + +static HRESULT WINAPI test_synth_QueryInterface(IDirectMusicSynth8 *iface, REFIID riid, void **ret_iface) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static ULONG WINAPI test_synth_AddRef(IDirectMusicSynth8 *iface) +{ + struct test_synth *sink = CONTAINING_RECORD(iface, struct test_synth, IDirectMusicSynth8_iface); + return InterlockedIncrement(&sink->refcount); +} + +static ULONG WINAPI test_synth_Release(IDirectMusicSynth8 *iface) +{ + struct test_synth *sink = CONTAINING_RECORD(iface, struct test_synth, IDirectMusicSynth8_iface); + ULONG ref = InterlockedDecrement(&sink->refcount); + if (!ref) free(sink); + return ref; +} + +static HRESULT WINAPI test_synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *params) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_Close(IDirectMusicSynth8 *iface) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_SetNumChannelGroups(IDirectMusicSynth8 *iface, DWORD groups) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_Download(IDirectMusicSynth8 *iface, HANDLE *handle, void *data, BOOL *can_free) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_Unload(IDirectMusicSynth8 *iface, HANDLE handle, + HRESULT (CALLBACK *free_callback)(HANDLE,HANDLE), HANDLE user_data) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_PlayBuffer(IDirectMusicSynth8 *iface, REFERENCE_TIME time, BYTE *buffer, DWORD size) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_GetRunningStats(IDirectMusicSynth8 *iface, DMUS_SYNTHSTATS *stats) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_GetPortCaps(IDirectMusicSynth8 *iface, DMUS_PORTCAPS *caps) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_SetMasterClock(IDirectMusicSynth8 *iface, IReferenceClock *clock) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_GetLatencyClock(IDirectMusicSynth8 *iface, IReferenceClock **clock) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_Activate(IDirectMusicSynth8 *iface, BOOL enable) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_SetSynthSink(IDirectMusicSynth8 *iface, IDirectMusicSynthSink *sink) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_Render(IDirectMusicSynth8 *iface, short *buffer, DWORD length, LONGLONG position) +{ + return S_OK; +} + +static HRESULT WINAPI test_synth_SetChannelPriority(IDirectMusicSynth8 *iface, DWORD group, DWORD channel, DWORD priority) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_GetChannelPriority(IDirectMusicSynth8 *iface, DWORD group, DWORD channel, DWORD *priority) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_GetFormat(IDirectMusicSynth8 *iface, WAVEFORMATEX *format, DWORD *ext_size) +{ + *ext_size = 0; + memset(format, 0, sizeof(*format)); + format->wFormatTag = WAVE_FORMAT_PCM; + format->nChannels = 2; + format->wBitsPerSample = 16; + format->nSamplesPerSec = 44100; + format->nBlockAlign = format->nChannels * format->wBitsPerSample / 8; + format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign; + return S_OK; +} + +static HRESULT WINAPI test_synth_GetAppend(IDirectMusicSynth8 *iface, DWORD *append) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_PlayVoice(IDirectMusicSynth8 *iface, REFERENCE_TIME rt, DWORD voice_id, + DWORD group, DWORD channel, DWORD dlid, LONG pitch, LONG volume, SAMPLE_TIME voice_start, + SAMPLE_TIME loop_start, SAMPLE_TIME loop_end) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_StopVoice(IDirectMusicSynth8 *iface, REFERENCE_TIME rt, DWORD voice_id) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_GetVoiceState(IDirectMusicSynth8 *iface, DWORD voice_buf[], DWORD voice_len, + DMUS_VOICE_STATE voice_state[]) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_Refresh(IDirectMusicSynth8 *iface, DWORD dlid, DWORD flags) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_synth_AssignChannelToBuses(IDirectMusicSynth8 *iface, DWORD group, DWORD channel, + DWORD *buses, DWORD buses_count) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static const IDirectMusicSynth8Vtbl test_synth_vtbl = +{ + test_synth_QueryInterface, + test_synth_AddRef, + test_synth_Release, + test_synth_Open, + test_synth_Close, + test_synth_SetNumChannelGroups, + test_synth_Download, + test_synth_Unload, + test_synth_PlayBuffer, + test_synth_GetRunningStats, + test_synth_GetPortCaps, + test_synth_SetMasterClock, + test_synth_GetLatencyClock, + test_synth_Activate, + test_synth_SetSynthSink, + test_synth_Render, + test_synth_SetChannelPriority, + test_synth_GetChannelPriority, + test_synth_GetFormat, + test_synth_GetAppend, + test_synth_PlayVoice, + test_synth_StopVoice, + test_synth_GetVoiceState, + test_synth_Refresh, + test_synth_AssignChannelToBuses, +}; + +static HRESULT test_synth_create(IDirectMusicSynth8 **out) +{ + struct test_synth *synth; + + *out = NULL; + if (!(synth = calloc(1, sizeof(*synth)))) return E_OUTOFMEMORY; + synth->IDirectMusicSynth8_iface.lpVtbl = &test_synth_vtbl; + synth->refcount = 1; + + *out = &synth->IDirectMusicSynth8_iface; + return S_OK; +} + static void test_synth_getformat(IDirectMusicSynth *synth, DMUS_PORTPARAMS *params, const char *context) { WAVEFORMATEX format; @@ -397,6 +608,181 @@ static void test_COM_synthsink(void) IDirectMusicSynthSink_Release(dmss); } +static void test_IDirectMusicSynthSink(void) +{ + IReferenceClock *latency_clock; + REFERENCE_TIME time, tmp_time; + IDirectMusicSynthSink *sink; + IDirectMusicSynth8 *synth; + IReferenceClock *clock; + IDirectSound *dsound; + IDirectMusic *music; + LONGLONG sample; + HRESULT hr; + DWORD size; + ULONG ref; + + hr = DirectSoundCreate(NULL, &dsound, NULL); + ok(hr == S_OK || broken(hr == DSERR_NODRIVER), "got %#lx\n", hr); + if (broken(hr == DSERR_NODRIVER)) + { + win_skip("Failed to create IDirectSound, skipping tests"); + return; + } + + hr = IDirectSound_SetCooperativeLevel(dsound, GetDesktopWindow(), DSSCL_PRIORITY); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusic, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusic, (void **)&music); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusic_GetMasterClock(music, NULL, &clock); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusic_Release(music); + + hr = test_synth_create(&synth); + ok(hr == S_OK, "got %#lx\n", hr); + + + hr = CoCreateInstance(&CLSID_DirectMusicSynthSink, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicSynthSink, (void **)&sink); + ok(hr == S_OK, "got %#lx\n", hr); + + /* sink is not configured */ + hr = IDirectMusicSynthSink_Activate(sink, TRUE); + todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_Activate(sink, FALSE); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicSynthSink_SampleToRefTime(sink, 0, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + time = 0xdeadbeef; + hr = IDirectMusicSynthSink_SampleToRefTime(sink, 10, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time == 4000, "got %I64d\n", time); + + hr = IDirectMusicSynthSink_RefTimeToSample(sink, 0, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + sample = 0xdeadbeef; + hr = IDirectMusicSynthSink_RefTimeToSample(sink, 4000, &sample); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(sample == 8, "got %I64d\n", sample); + + hr = IDirectMusicSynthSink_GetDesiredBufferSize(sink, &size); + ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + + /* latency clock is available but not usable */ + hr = IDirectMusicSynthSink_GetLatencyClock(sink, NULL); + ok(hr == E_POINTER, "got %#lx\n", hr); + ref = get_refcount(sink); + ok(ref == 1, "got %#lx\n", ref); + hr = IDirectMusicSynthSink_GetLatencyClock(sink, &latency_clock); + ok(hr == S_OK, "got %#lx\n", hr); + ok(latency_clock != clock, "got same clock\n"); + ref = get_refcount(sink); + todo_wine ok(ref == 2, "got %#lx\n", ref); + + hr = IReferenceClock_GetTime(latency_clock, NULL); + todo_wine ok(hr == E_INVALIDARG, "got %#lx\n", hr); + hr = IReferenceClock_GetTime(latency_clock, &time); + todo_wine ok(hr == E_FAIL, "got %#lx\n", hr); + + hr = IDirectMusicSynthSink_Init(sink, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_SetDirectSound(sink, NULL, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_SetDirectSound(sink, dsound, NULL); + todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + + /* Activate requires a synth, dsound and a clock */ + ref = get_refcount(synth); + ok(ref == 1, "got %#lx\n", ref); + hr = IDirectMusicSynthSink_Init(sink, (IDirectMusicSynth *)synth); + ok(hr == S_OK, "got %#lx\n", hr); + ref = get_refcount(synth); + ok(ref == 1, "got %#lx\n", ref); + hr = IDirectMusicSynthSink_GetDesiredBufferSize(sink, &size); + ok(hr == S_OK, "got %#lx\n", hr); + ok(size == 352800, "got %lu\n", size); + hr = IDirectMusicSynthSink_Activate(sink, TRUE); + todo_wine ok(hr == DMUS_E_DSOUND_NOT_SET, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_SetDirectSound(sink, dsound, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_Activate(sink, TRUE); + todo_wine ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_SetMasterClock(sink, clock); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IReferenceClock_GetTime(latency_clock, &time); + todo_wine ok(hr == E_FAIL, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_Activate(sink, TRUE); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_Activate(sink, TRUE); + todo_wine ok(hr == DMUS_E_SYNTHACTIVE, "got %#lx\n", hr); + ref = get_refcount(synth); + ok(ref == 1, "got %#lx\n", ref); + + hr = IDirectMusicSynthSink_GetDesiredBufferSize(sink, &size); + ok(hr == S_OK, "got %#lx\n", hr); + ok(size == 352800, "got %lu\n", size); + + /* conversion functions now use the activation time and master clock */ + hr = IReferenceClock_GetTime(clock, &time); + ok(hr == S_OK, "got %#lx\n", hr); + sample = 0xdeadbeef; + hr = IDirectMusicSynthSink_RefTimeToSample(sink, time, &sample); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(sample <= 3000, "got %I64d\n", sample); + tmp_time = time + 1; + hr = IDirectMusicSynthSink_SampleToRefTime(sink, sample, &tmp_time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(tmp_time <= time, "got %I64d\n", tmp_time - time); + ok(time - tmp_time <= 5000, "got %I64d\n", time - tmp_time); + + /* latency clock now works fine */ + tmp_time = time; + hr = IReferenceClock_GetTime(latency_clock, &tmp_time); + todo_wine_if(hr == S_FALSE) ok(hr == S_OK, "got %#lx\n", hr); + todo_wine_if(tmp_time <= time) ok(tmp_time > time, "got %I64d\n", tmp_time - time); + ok(tmp_time - time <= 2000000, "got %I64d\n", tmp_time - time); + + /* setting the clock while active is fine */ + hr = IDirectMusicSynthSink_SetMasterClock(sink, clock); + ok(hr == S_OK, "got %#lx\n", hr); + + /* removing synth while active is fine */ + hr = IDirectMusicSynthSink_Init(sink, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + ref = get_refcount(synth); + ok(ref == 1, "got %#lx\n", ref); + hr = IDirectMusicSynthSink_Activate(sink, TRUE); + todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + + /* changing dsound while active fails */ + hr = IDirectMusicSynthSink_SetDirectSound(sink, dsound, NULL); + todo_wine ok(hr == DMUS_E_SYNTHACTIVE, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_SetDirectSound(sink, NULL, NULL); + todo_wine ok(hr == DMUS_E_SYNTHACTIVE, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_Activate(sink, FALSE); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_SetDirectSound(sink, NULL, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + /* SetMasterClock doesn't need the sink to be configured */ + hr = IDirectMusicSynthSink_SetMasterClock(sink, NULL); + ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicSynthSink_SetMasterClock(sink, clock); + ok(hr == S_OK, "got %#lx\n", hr); + + IReferenceClock_Release(latency_clock); + IDirectMusicSynthSink_Release(sink); + + + IDirectMusicSynth8_Release(synth); + IReferenceClock_Release(clock); + + IDirectSound_Release(dsound); +} + START_TEST(dmsynth) { CoInitializeEx(NULL, COINIT_MULTITHREADED); @@ -410,6 +796,7 @@ START_TEST(dmsynth) test_dmsynth(); test_COM(); test_COM_synthsink(); + test_IDirectMusicSynthSink(); CoUninitialize(); } From 5f0196c25b6032ad401c34c47bac167ed93cbe71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 27 Aug 2023 16:17:15 +0200 Subject: [PATCH 0789/1148] dmsynth/tests: Test DirectMusicSynth class in isolation. And more specifically how generating samples with Render works. (cherry picked from commit ce40b4d8fcb08d54df7ada430bc8dfe53392bd23) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/tests/dmsynth.c | 543 +++++++++++++++++++++++++++++++++++ 1 file changed, 543 insertions(+) diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index c8976f50e21..2fd62294df8 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -608,6 +608,548 @@ static void test_COM_synthsink(void) IDirectMusicSynthSink_Release(dmss); } +struct test_sink +{ + IDirectMusicSynthSink IDirectMusicSynthSink_iface; + IReferenceClock IReferenceClock_iface; + LONG refcount; + + IReferenceClock *clock; + IDirectMusicSynth *synth; + REFERENCE_TIME activate_time; + REFERENCE_TIME latency_time; + DWORD written; +}; + +static HRESULT WINAPI test_sink_QueryInterface(IDirectMusicSynthSink *iface, REFIID riid, void **ret_iface) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static ULONG WINAPI test_sink_AddRef(IDirectMusicSynthSink *iface) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IDirectMusicSynthSink_iface); + return InterlockedIncrement(&sink->refcount); +} + +static ULONG WINAPI test_sink_Release(IDirectMusicSynthSink *iface) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IDirectMusicSynthSink_iface); + ULONG ref = InterlockedDecrement(&sink->refcount); + if (!ref) free(sink); + return ref; +} + +static HRESULT WINAPI test_sink_Init(IDirectMusicSynthSink *iface, IDirectMusicSynth *synth) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IDirectMusicSynthSink_iface); + sink->synth = synth; + return S_OK; +} + +static HRESULT WINAPI test_sink_SetMasterClock(IDirectMusicSynthSink *iface, IReferenceClock *clock) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IDirectMusicSynthSink_iface); + if (sink->clock) + IReferenceClock_Release(sink->clock); + if ((sink->clock = clock)) + IReferenceClock_AddRef(sink->clock); + return S_OK; +} + +static HRESULT WINAPI test_sink_GetLatencyClock(IDirectMusicSynthSink *iface, IReferenceClock **clock) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IDirectMusicSynthSink_iface); + *clock = &sink->IReferenceClock_iface; + IReferenceClock_AddRef(*clock); + return S_OK; +} + +static HRESULT WINAPI test_sink_Activate(IDirectMusicSynthSink *iface, BOOL enable) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IDirectMusicSynthSink_iface); + + if (!sink->clock) + return DMUS_E_NO_MASTER_CLOCK; + + IReferenceClock_GetTime(sink->clock, &sink->activate_time); + sink->latency_time = sink->activate_time; + return S_OK; +} + +static HRESULT WINAPI test_sink_SampleToRefTime(IDirectMusicSynthSink *iface, LONGLONG sample, REFERENCE_TIME *time) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IDirectMusicSynthSink_iface); + WAVEFORMATEX format; + DWORD format_size = sizeof(format); + HRESULT hr; + + hr = IDirectMusicSynth_GetFormat(sink->synth, &format, &format_size); + ok(hr == S_OK, "got %#lx\n", hr); + + *time = sink->activate_time + ((sample * 10000) / format.nSamplesPerSec) * 1000; + return S_OK; +} + +static HRESULT WINAPI test_sink_RefTimeToSample(IDirectMusicSynthSink *iface, REFERENCE_TIME time, LONGLONG *sample) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IDirectMusicSynthSink_iface); + WAVEFORMATEX format; + DWORD format_size = sizeof(format); + HRESULT hr; + + hr = IDirectMusicSynth_GetFormat(sink->synth, &format, &format_size); + ok(hr == S_OK, "got %#lx\n", hr); + + *sample = (((time - sink->activate_time) / 1000) * format.nSamplesPerSec) / 10000; + return S_OK; +} + +static HRESULT WINAPI test_sink_SetDirectSound(IDirectMusicSynthSink *iface, IDirectSound *dsound, + IDirectSoundBuffer *dsound_buffer) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static HRESULT WINAPI test_sink_GetDesiredBufferSize(IDirectMusicSynthSink *iface, DWORD *size) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static const IDirectMusicSynthSinkVtbl test_sink_vtbl = +{ + test_sink_QueryInterface, + test_sink_AddRef, + test_sink_Release, + test_sink_Init, + test_sink_SetMasterClock, + test_sink_GetLatencyClock, + test_sink_Activate, + test_sink_SampleToRefTime, + test_sink_RefTimeToSample, + test_sink_SetDirectSound, + test_sink_GetDesiredBufferSize, +}; + +static HRESULT WINAPI test_sink_latency_clock_QueryInterface(IReferenceClock *iface, REFIID iid, void **out) +{ + ok(0, "unexpected %s\n", __func__); + return E_NOINTERFACE; +} + +static ULONG WINAPI test_sink_latency_clock_AddRef(IReferenceClock *iface) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IReferenceClock_iface); + return IDirectMusicSynthSink_AddRef(&sink->IDirectMusicSynthSink_iface); +} + +static ULONG WINAPI test_sink_latency_clock_Release(IReferenceClock *iface) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IReferenceClock_iface); + return IDirectMusicSynthSink_Release(&sink->IDirectMusicSynthSink_iface); +} + +static HRESULT WINAPI test_sink_latency_clock_GetTime(IReferenceClock *iface, REFERENCE_TIME *time) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IReferenceClock_iface); + *time = sink->latency_time; + return S_OK; +} + +static HRESULT WINAPI test_sink_latency_clock_AdviseTime(IReferenceClock *iface, + REFERENCE_TIME base, REFERENCE_TIME offset, HANDLE event, DWORD *cookie) +{ + ok(0, "unexpected %s\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_sink_latency_clock_AdvisePeriodic(IReferenceClock *iface, + REFERENCE_TIME start, REFERENCE_TIME period, HANDLE semaphore, DWORD *cookie) +{ + ok(0, "unexpected %s\n", __func__); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_sink_latency_clock_Unadvise(IReferenceClock *iface, DWORD cookie) +{ + ok(0, "unexpected %s\n", __func__); + return E_NOTIMPL; +} + +static const IReferenceClockVtbl test_sink_latency_clock_vtbl = +{ + test_sink_latency_clock_QueryInterface, + test_sink_latency_clock_AddRef, + test_sink_latency_clock_Release, + test_sink_latency_clock_GetTime, + test_sink_latency_clock_AdviseTime, + test_sink_latency_clock_AdvisePeriodic, + test_sink_latency_clock_Unadvise, +}; + +static HRESULT test_sink_create(IDirectMusicSynthSink **out) +{ + struct test_sink *sink; + + *out = NULL; + if (!(sink = calloc(1, sizeof(*sink)))) return E_OUTOFMEMORY; + sink->IDirectMusicSynthSink_iface.lpVtbl = &test_sink_vtbl; + sink->IReferenceClock_iface.lpVtbl = &test_sink_latency_clock_vtbl; + sink->refcount = 1; + + *out = &sink->IDirectMusicSynthSink_iface; + return S_OK; +} + +static void test_sink_render(IDirectMusicSynthSink *iface, void *buffer, DWORD buffer_size, HANDLE output) +{ + struct test_sink *sink = CONTAINING_RECORD(iface, struct test_sink, IDirectMusicSynthSink_iface); + DWORD written, format_size; + WAVEFORMATEX format; + HRESULT hr; + + format_size = sizeof(format); + hr = IDirectMusicSynth_GetFormat(sink->synth, &format, &format_size); + ok(hr == S_OK, "got %#lx\n", hr); + + memset(buffer, 0, buffer_size); + hr = IDirectMusicSynth_Render(sink->synth, buffer, buffer_size / format.nBlockAlign, sink->written / format.nBlockAlign); + ok(hr == S_OK, "got %#lx\n", hr); + sink->written += buffer_size; + + hr = IDirectMusicSynthSink_SampleToRefTime(iface, sink->written / format.nBlockAlign, &sink->latency_time); + ok(hr == S_OK, "got %#lx\n", hr); + + if (output) + { + BOOL ret = WriteFile(output, buffer, buffer_size, &written, NULL); + ok(!!ret, "WriteFile failed, error %lu.\n", GetLastError()); + } +} + +static void test_IDirectMusicSynth(void) +{ + static const UINT RENDER_ITERATIONS = 8; + + struct wave_download + { + DMUS_DOWNLOADINFO info; + ULONG offsets[2]; + DMUS_WAVE wave; + union + { + DMUS_WAVEDATA wave_data; + struct + { + ULONG size; + BYTE samples[256]; + }; + }; + } wave_download = + { + .info = + { + .dwDLType = DMUS_DOWNLOADINFO_WAVE, + .dwDLId = 1, + .dwNumOffsetTableEntries = 2, + .cbSize = sizeof(struct wave_download), + }, + .offsets = + { + offsetof(struct wave_download, wave), + offsetof(struct wave_download, wave_data), + }, + .wave = + { + .ulWaveDataIdx = 1, + .WaveformatEx = + { + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 1, + .wBitsPerSample = 8, + .nSamplesPerSec = 44100, + .nAvgBytesPerSec = 44100, + .nBlockAlign = 1, + }, + }, + .wave_data = + { + .cbSize = sizeof(wave_download.samples), + }, + }; + struct instrument_download + { + DMUS_DOWNLOADINFO info; + ULONG offsets[4]; + DMUS_INSTRUMENT instrument; + DMUS_REGION region; + DMUS_ARTICULATION articulation; + DMUS_ARTICPARAMS artic_params; + } instrument_download = + { + .info = + { + .dwDLType = DMUS_DOWNLOADINFO_INSTRUMENT, + .dwDLId = 2, + .dwNumOffsetTableEntries = 4, + .cbSize = sizeof(struct instrument_download), + }, + .offsets = + { + offsetof(struct instrument_download, instrument), + offsetof(struct instrument_download, region), + offsetof(struct instrument_download, articulation), + offsetof(struct instrument_download, artic_params), + }, + .instrument = + { + .ulPatch = 0, + .ulFirstRegionIdx = 1, + .ulGlobalArtIdx = 2, + }, + .region = + { + .RangeKey = {.usLow = 0, .usHigh = 127}, + .RangeVelocity = {.usLow = 0, .usHigh = 127}, + .fusOptions = F_RGN_OPTION_SELFNONEXCLUSIVE, + .WaveLink = {.ulChannel = 1, .ulTableIndex = 1}, + .WSMP = {.cbSize = sizeof(WSMPL), .usUnityNote = 60, .fulOptions = F_WSMP_NO_TRUNCATION}, + .WLOOP[0] = {.cbSize = sizeof(WLOOP), .ulType = WLOOP_TYPE_FORWARD}, + }, + .articulation = {.ulArt1Idx = 3}, + .artic_params = + { + .VolEG = {.tcAttack = 32768u << 16, .tcDecay = 32768u << 16, .ptSustain = 10000 << 16, .tcRelease = 32768u << 16}, + }, + }; + DMUS_BUFFERDESC buffer_desc = + { + .dwSize = sizeof(DMUS_BUFFERDESC), + .cbBuffer = 4096, + }; + DMUS_PORTPARAMS port_params = + { + .dwSize = sizeof(DMUS_PORTPARAMS), + .dwValidParams = DMUS_PORTPARAMS_AUDIOCHANNELS | DMUS_PORTPARAMS_SAMPLERATE, + .dwAudioChannels = 2, + .dwSampleRate = 44100, + }; + WCHAR temp_path[MAX_PATH], temp_file[MAX_PATH]; + IReferenceClock *latency_clock; + IDirectMusicSynthSink *sink; + IDirectMusicBuffer *buffer; + DWORD format_size, written; + IDirectMusicSynth *synth; + HANDLE wave_file, handle; + IReferenceClock *clock; + BOOL can_free = FALSE; + REFERENCE_TIME time; + WAVEFORMATEX format; + IDirectMusic *music; + short samples[256]; + ULONG i, ref; + HRESULT hr; + DWORD len; + BYTE *raw; + BOOL ret; + + hr = CoCreateInstance(&CLSID_DirectMusic, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusic, (void **)&music); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusic_GetMasterClock(music, NULL, &clock); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = test_sink_create(&sink); + ok(hr == S_OK, "got %#lx\n", hr); + + + hr = CoCreateInstance(&CLSID_DirectMusicSynth, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicSynth, (void **)&synth); + ok(hr == S_OK, "got %#lx\n", hr); + + /* SetNumChannelGroups needs Open */ + hr = IDirectMusicSynth_SetNumChannelGroups(synth, 1); + todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + /* GetFormat needs Open */ + hr = IDirectMusicSynth_GetFormat(synth, NULL, NULL); + ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicSynth_GetFormat(synth, NULL, &format_size); + ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + + /* Open / Close don't need a sink */ + hr = IDirectMusicSynth_Open(synth, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynth_Open(synth, NULL); + ok(hr == DMUS_E_ALREADYOPEN, "got %#lx\n", hr); + hr = IDirectMusicSynth_SetNumChannelGroups(synth, 1); + ok(hr == S_OK, "got %#lx\n", hr); + format_size = sizeof(format); + hr = IDirectMusicSynth_GetFormat(synth, NULL, &format_size); + ok(hr == S_OK, "got %#lx\n", hr); + ok(format_size == sizeof(format), "got %lu\n", format_size); + hr = IDirectMusicSynth_Close(synth); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynth_Close(synth); + ok(hr == DMUS_E_ALREADYCLOSED, "got %#lx\n", hr); + + /* GetLatencyClock needs a sink */ + hr = IDirectMusicSynth_GetLatencyClock(synth, NULL); + ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicSynth_GetLatencyClock(synth, &latency_clock); + ok(hr == DMUS_E_NOSYNTHSINK, "got %#lx\n", hr); + + /* Activate needs a sink, synth to be open, and a master clock on the sink */ + hr = IDirectMusicSynth_Open(synth, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynth_Activate(synth, TRUE); + ok(hr == DMUS_E_NOSYNTHSINK, "got %#lx\n", hr); + hr = IDirectMusicSynth_Activate(synth, FALSE); + todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + + hr = IDirectMusicSynth_SetSynthSink(synth, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynth_SetSynthSink(synth, sink); + ok(hr == S_OK, "got %#lx\n", hr); + ref = get_refcount(sink); + todo_wine ok(ref == 2, "got %lu\n", ref); + hr = IDirectMusicSynth_Activate(synth, TRUE); + todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + + /* SetMasterClock does nothing */ + hr = IDirectMusicSynth_SetMasterClock(synth, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicSynth_SetMasterClock(synth, clock); + ok(hr == S_OK, "got %#lx\n", hr); + ref = get_refcount(clock); + todo_wine ok(ref == 1, "got %lu\n", ref); + hr = IDirectMusicSynth_Activate(synth, TRUE); + todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + + /* SetMasterClock needs to be called on the sink */ + hr = IDirectMusicSynthSink_SetMasterClock(sink, clock); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynth_Activate(synth, TRUE); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynth_Activate(synth, TRUE); + todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + + /* Close is fine while active */ + hr = IDirectMusicSynth_Close(synth); + ok(hr == S_OK, "got %#lx\n", hr); + /* Removing the sink is fine while active */ + hr = IDirectMusicSynth_SetSynthSink(synth, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + ref = get_refcount(sink); + ok(ref == 1, "got %lu\n", ref); + + /* but Activate might fail then */ + hr = IDirectMusicSynth_Activate(synth, FALSE); + todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + hr = IDirectMusicSynth_Activate(synth, FALSE); + todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + + + /* Test generating some samples */ + hr = IDirectMusicSynth_Open(synth, &port_params); + ok(hr == S_OK, "got %#lx\n", hr); + + format_size = sizeof(format); + hr = IDirectMusicSynth_GetFormat(synth, &format, &format_size); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynth_SetSynthSink(synth, sink); + ok(hr == S_OK, "got %#lx\n", hr); + ref = get_refcount(sink); + todo_wine ok(ref == 2, "got %lu\n", ref); + hr = IDirectMusicSynth_Activate(synth, TRUE); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + + GetTempPathW(MAX_PATH, temp_path); + GetTempFileNameW(temp_path, L"synth", 0, temp_file); + wave_file = CreateFileW(temp_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(wave_file != INVALID_HANDLE_VALUE, "CreateFileW failed, error %lu.\n", GetLastError()); + ret = WriteFile(wave_file, "RIFF", 4, &written, NULL); + ok(ret, "WriteFile failed, error %lu.\n", GetLastError()); + format_size = (RENDER_ITERATIONS + 1) * sizeof(samples) + sizeof(format) + 20; + ret = WriteFile(wave_file, &format_size, 4, &written, NULL); + ok(ret, "WriteFile failed, error %lu.\n", GetLastError()); + ret = WriteFile(wave_file, "WAVEfmt ", 8, &written, NULL); + ok(ret, "WriteFile failed, error %lu.\n", GetLastError()); + format_size = sizeof(format); + ret = WriteFile(wave_file, &format_size, 4, &written, NULL); + ok(ret, "WriteFile failed, error %lu.\n", GetLastError()); + ret = WriteFile(wave_file, &format, format_size, &written, NULL); + ok(ret, "WriteFile failed, error %lu.\n", GetLastError()); + ret = WriteFile(wave_file, "data", 4, &written, NULL); + ok(ret, "WriteFile failed, error %lu.\n", GetLastError()); + format_size = (RENDER_ITERATIONS + 1) * sizeof(samples); + ret = WriteFile(wave_file, &format_size, 4, &written, NULL); + ok(ret, "WriteFile failed, error %lu.\n", GetLastError()); + + /* native needs to render at least once before producing samples */ + test_sink_render(sink, samples, sizeof(samples), wave_file); + + for (i = 0; i < ARRAY_SIZE(wave_download.samples); i++) + wave_download.samples[i] = i; + + can_free = 0xdeadbeef; + handle = (HANDLE)0xdeadbeef; + hr = IDirectMusicSynth_Download(synth, &handle, &wave_download, &can_free); + ok(hr == S_OK, "got %#lx\n", hr); + ok(handle != 0, "got %p\n", handle); + todo_wine ok(can_free == FALSE, "got %u\n", can_free); + + can_free = 0xdeadbeef; + handle = (HANDLE)0xdeadbeef; + hr = IDirectMusicSynth_Download(synth, &handle, &instrument_download, &can_free); + ok(hr == S_OK, "got %#lx\n", hr); + ok(handle != 0, "got %p\n", handle); + todo_wine ok(can_free == TRUE, "got %u\n", can_free); + + /* add a MIDI note to a buffer and play it */ + hr = IDirectMusicSynth_GetLatencyClock(synth, &latency_clock); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusic_CreateMusicBuffer(music, &buffer_desc, &buffer, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + /* status = 0x90 (NOTEON / channel 0), key = 0x27 (39), vel = 0x78 (120) */ + hr = IDirectMusicBuffer_PackStructured(buffer, 0, 1, 0x782790); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IReferenceClock_GetTime(latency_clock, &time); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicBuffer_GetRawBufferPtr(buffer, (BYTE **)&raw); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicBuffer_GetUsedBytes(buffer, &len); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynth_PlayBuffer(synth, time, (BYTE *)raw, len); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicBuffer_Release(buffer); + IReferenceClock_Release(latency_clock); + + for (i = 0; i < RENDER_ITERATIONS; i++) + test_sink_render(sink, samples, sizeof(samples), wave_file); + + CloseHandle(wave_file); + trace("Rendered samples to %s\n", debugstr_w(temp_file)); + + hr = IDirectMusicSynth_Activate(synth, FALSE); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynth_SetSynthSink(synth, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + ref = get_refcount(sink); + ok(ref == 1, "got %lu\n", ref); + hr = IDirectMusicSynth_Close(synth); + ok(hr == S_OK, "got %#lx\n", hr); + + IDirectMusicSynth_Release(synth); + + + if (strcmp(winetest_platform, "wine")) IDirectMusicSynthSink_Release(sink); + IReferenceClock_Release(clock); + IDirectMusic_Release(music); +} + static void test_IDirectMusicSynthSink(void) { IReferenceClock *latency_clock; @@ -796,6 +1338,7 @@ START_TEST(dmsynth) test_dmsynth(); test_COM(); test_COM_synthsink(); + test_IDirectMusicSynth(); test_IDirectMusicSynthSink(); CoUninitialize(); From 1bb7c629b87f56b50f8f3ffa6cf7d840005147cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 17:58:05 +0200 Subject: [PATCH 0790/1148] include: Fix incorrect IDirectMusicPortDownload_Unload macro. (cherry picked from commit bb9e54808c2a0458d8d76b7c29c257954a3de0e4) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- include/dmusicc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dmusicc.h b/include/dmusicc.h index cdae16c75bd..94ff184dc9f 100644 --- a/include/dmusicc.h +++ b/include/dmusicc.h @@ -635,7 +635,7 @@ DECLARE_INTERFACE_(IDirectMusicPortDownload,IUnknown) #define IDirectMusicPortDownload_GetDLId(p,a,b) (p)->lpVtbl->GetDLId(p,a,b) #define IDirectMusicPortDownload_GetAppend(p,a) (p)->lpVtbl->GetAppend(p,a) #define IDirectMusicPortDownload_Download(p,a) (p)->lpVtbl->Download(p,a) -#define IDirectMusicPortDownload_Unload(p,a) (p)->lpVtbl->GetBuffer(p,a) +#define IDirectMusicPortDownload_Unload(p,a) (p)->lpVtbl->Unload(p,a) #endif From 090790d6b3e50932706ca892eca0489093ee9916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 17:58:05 +0200 Subject: [PATCH 0791/1148] dmusic/tests: Test IDirectMusic(Port)Download interfaces. (cherry picked from commit d6e1e0efa2802c6c33cdf2cf2f02d8dd05281a28) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/tests/dmusic.c | 148 +++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 37b517fe0ca..fd012111df0 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -947,6 +947,153 @@ static void test_synthport(void) IDirectMusic_Release(dmusic); } +static void test_port_download(void) +{ + struct wave_download + { + DMUS_DOWNLOADINFO info; + ULONG offsets[2]; + DMUS_WAVE wave; + DMUS_WAVEDATA wave_data; + }; + + static void *invalid_ptr = (void *)0xdeadbeef; + IDirectMusicDownload *download, *tmp_download; + struct wave_download *wave_download; + IDirectMusicPortDownload *port; + IDirectMusicPort *tmp_port; + DWORD ids[4], append, size; + IDirectMusic *dmusic; + void *buffer; + HRESULT hr; + + tmp_port = create_synth_port(&dmusic); + hr = IDirectMusicPort_QueryInterface(tmp_port, &IID_IDirectMusicPortDownload, (void **)&port); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicPort_Release(tmp_port); + + /* GetBuffer only works with pre-allocated DLId */ + hr = IDirectMusicPortDownload_GetBuffer(port, 0, NULL); + ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicPortDownload_GetBuffer(port, 0, &download); + todo_wine ok(hr == DMUS_E_INVALID_DOWNLOADID, "got %#lx\n", hr); + hr = IDirectMusicPortDownload_GetBuffer(port, 0xdeadbeef, &download); + todo_wine ok(hr == DMUS_E_INVALID_DOWNLOADID, "got %#lx\n", hr); + + /* AllocateBuffer use the exact requested size */ + hr = IDirectMusicPortDownload_AllocateBuffer(port, 0, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicPortDownload_AllocateBuffer(port, 0, &download); + todo_wine ok(hr == E_INVALIDARG, "got %#lx\n", hr); + + hr = IDirectMusicPortDownload_AllocateBuffer(port, 1, &download); + ok(hr == S_OK, "got %#lx\n", hr); + size = 0xdeadbeef; + buffer = invalid_ptr; + hr = IDirectMusicDownload_GetBuffer(download, (void **)&buffer, &size); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(size == 1, "got %#lx\n", size); + todo_wine ok(buffer != invalid_ptr, "got %p\n", buffer); + IDirectMusicDownload_Release(download); + + /* GetDLId allocates the given number of slots and returns only the first */ + hr = IDirectMusicPortDownload_GetDLId(port, NULL, 0); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicPortDownload_GetDLId(port, ids, 0); + todo_wine ok(hr == E_INVALIDARG, "got %#lx\n", hr); + + memset(ids, 0xcc, sizeof(ids)); + hr = IDirectMusicPortDownload_GetDLId(port, ids, 4); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(ids[0] == 0, "got %#lx\n", ids[0]); + ok(ids[1] == 0xcccccccc, "got %#lx\n", ids[1]); + + /* GetBuffer looks up allocated ids to find downloaded buffers */ + hr = IDirectMusicPortDownload_GetBuffer(port, 2, &download); + todo_wine ok(hr == DMUS_E_NOT_DOWNLOADED_TO_PORT, "got %#lx\n", hr); + + hr = IDirectMusicPortDownload_GetAppend(port, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + append = 0xdeadbeef; + hr = IDirectMusicPortDownload_GetAppend(port, &append); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(append == 2, "got %#lx\n", append); + + /* test Download / Unload on invalid and valid buffers */ + + download = invalid_ptr; + hr = IDirectMusicPortDownload_AllocateBuffer(port, sizeof(struct wave_download), &download); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(download != invalid_ptr, "got %p\n", download); + if (download == invalid_ptr) goto skip_tests; + size = 0xdeadbeef; + wave_download = invalid_ptr; + hr = IDirectMusicDownload_GetBuffer(download, (void **)&wave_download, &size); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(size == sizeof(struct wave_download), "got %#lx\n", size); + todo_wine ok(wave_download != invalid_ptr, "got %p\n", wave_download); + wave_download->info.cbSize = sizeof(struct wave_download); + wave_download->info.dwDLId = 2; + wave_download->info.dwDLType = 0; + wave_download->info.dwNumOffsetTableEntries = 0; + hr = IDirectMusicPortDownload_GetBuffer(port, 2, &tmp_download); + todo_wine ok(hr == DMUS_E_NOT_DOWNLOADED_TO_PORT, "got %#lx\n", hr); + + hr = IDirectMusicPortDownload_Download(port, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicPortDownload_Download(port, download); + todo_wine ok(hr == DMUS_E_UNKNOWNDOWNLOAD, "got %#lx\n", hr); + + wave_download->info.dwDLType = DMUS_DOWNLOADINFO_WAVE; + wave_download->info.dwNumOffsetTableEntries = 2; + wave_download->offsets[0] = offsetof(struct wave_download, wave); + wave_download->offsets[1] = offsetof(struct wave_download, wave_data); + wave_download->wave.WaveformatEx.wFormatTag = WAVE_FORMAT_PCM; + wave_download->wave.WaveformatEx.nChannels = 1; + wave_download->wave.WaveformatEx.nSamplesPerSec = 44100; + wave_download->wave.WaveformatEx.nAvgBytesPerSec = 44100; + wave_download->wave.WaveformatEx.nBlockAlign = 1; + wave_download->wave.WaveformatEx.wBitsPerSample = 8; + wave_download->wave.WaveformatEx.cbSize = 0; + wave_download->wave.ulWaveDataIdx = 1; + wave_download->wave.ulCopyrightIdx = 0; + wave_download->wave.ulFirstExtCkIdx = 0; + wave_download->wave_data.cbSize = 1; + + hr = IDirectMusicPortDownload_Download(port, download); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPortDownload_Download(port, download); + todo_wine ok(hr == DMUS_E_ALREADY_DOWNLOADED, "got %#lx\n", hr); + + tmp_download = invalid_ptr; + hr = IDirectMusicPortDownload_GetBuffer(port, 2, &tmp_download); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(tmp_download == download, "got %p\n", tmp_download); + if (tmp_download != invalid_ptr) IDirectMusicDownload_Release(tmp_download); + + hr = IDirectMusicPortDownload_Unload(port, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicPortDownload_Unload(port, download); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPortDownload_GetBuffer(port, 2, &tmp_download); + todo_wine ok(hr == DMUS_E_NOT_DOWNLOADED_TO_PORT, "got %#lx\n", hr); + + hr = IDirectMusicPortDownload_Unload(port, download); + ok(hr == S_OK, "got %#lx\n", hr); + + /* DLIds are never released */ + hr = IDirectMusicPortDownload_GetDLId(port, ids, 1); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(ids[0] == 4, "got %#lx\n", ids[0]); + + IDirectMusicDownload_Release(download); + +skip_tests: + IDirectMusicPortDownload_Release(port); + IDirectMusic_Release(dmusic); +} + START_TEST(dmusic) { CoInitializeEx(NULL, COINIT_MULTITHREADED); @@ -967,6 +1114,7 @@ START_TEST(dmusic) test_parsedescriptor(); test_master_clock(); test_synthport(); + test_port_download(); CoUninitialize(); } From 8f215dbc7d99a9c2e0c1953d73aca429b38c9ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 30 Aug 2023 18:48:11 +0200 Subject: [PATCH 0792/1148] dmusic/tests: Test IDirectMusicPort_(Download|Unload)Instrument. (cherry picked from commit 041b468b23f680f1dd867b9a7c8f38c8b2d628de) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/tests/dmusic.c | 240 ++++++++++++++++++++++++++++++++++++- 1 file changed, 234 insertions(+), 6 deletions(-) diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index fd012111df0..6df37e3d63d 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -30,6 +30,95 @@ #include "dmusicf.h" #include "dmksctrl.h" +static ULONG get_refcount(void *iface) +{ + IUnknown *unknown = iface; + IUnknown_AddRef(unknown); + return IUnknown_Release(unknown); +} + +#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) +static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) +{ + ULONG expect_ref = get_refcount(iface_ptr); + IUnknown *iface = iface_ptr; + HRESULT hr, expected; + IUnknown *unk; + + expected = supported ? S_OK : E_NOINTERFACE; + hr = IUnknown_QueryInterface(iface, iid, (void **)&unk); + ok_(__FILE__, line)(hr == expected, "got hr %#lx, expected %#lx.\n", hr, expected); + if (SUCCEEDED(hr)) + { + LONG ref = get_refcount(unk); + ok_(__FILE__, line)(ref == expect_ref + 1, "got %ld\n", ref); + IUnknown_Release(unk); + ref = get_refcount(iface_ptr); + ok_(__FILE__, line)(ref == expect_ref, "got %ld\n", ref); + } +} + +static void stream_begin_chunk(IStream *stream, const char type[5], ULARGE_INTEGER *offset) +{ + static const LARGE_INTEGER zero = {0}; + HRESULT hr; + hr = IStream_Write(stream, type, 4, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, offset); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IStream_Write(stream, "\0\0\0\0", 4, NULL); + ok(hr == S_OK, "got %#lx\n", hr); +} + +static void stream_end_chunk(IStream *stream, ULARGE_INTEGER *offset) +{ + static const LARGE_INTEGER zero = {0}; + ULARGE_INTEGER position; + HRESULT hr; + UINT size; + hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &position); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IStream_Seek(stream, *(LARGE_INTEGER *)offset, STREAM_SEEK_SET, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + size = position.QuadPart - offset->QuadPart - 4; + hr = IStream_Write(stream, &size, 4, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IStream_Seek(stream, *(LARGE_INTEGER *)&position, STREAM_SEEK_SET, NULL); + ok(hr == S_OK, "got %#lx\n", hr); +} + +#define CHUNK_BEGIN(stream, type) \ + do { \ + ULARGE_INTEGER __off; \ + stream_begin_chunk(stream, type, &__off); \ + do + +#define CHUNK_RIFF(stream, form) \ + do { \ + ULARGE_INTEGER __off; \ + stream_begin_chunk(stream, "RIFF", &__off); \ + IStream_Write(stream, form, 4, NULL); \ + do + +#define CHUNK_LIST(stream, form) \ + do { \ + ULARGE_INTEGER __off; \ + stream_begin_chunk(stream, "LIST", &__off); \ + IStream_Write(stream, form, 4, NULL); \ + do + +#define CHUNK_END \ + while (0); \ + stream_end_chunk(stream, &__off); \ + } while (0) + +#define CHUNK_DATA(stream, type, data) \ + CHUNK_BEGIN(stream, type) \ + { \ + IStream_Write((stream), &(data), sizeof(data), NULL); \ + } \ + CHUNK_END + static BOOL compare_time(REFERENCE_TIME x, REFERENCE_TIME y, unsigned int max_diff) { REFERENCE_TIME diff = x > y ? x - y : y - x; @@ -97,12 +186,6 @@ static void test_dmusic(void) IDirectMusic_Release(dmusic); } -static ULONG get_refcount(IDirectSound *iface) -{ - IDirectSound_AddRef(iface); - return IDirectSound_Release(iface); -} - static void test_setdsound(void) { IDirectMusic *dmusic; @@ -1091,6 +1174,150 @@ static void test_port_download(void) skip_tests: IDirectMusicPortDownload_Release(port); +} + +static void test_download_instrument(void) +{ + static const LARGE_INTEGER zero = {0}; + IDirectMusicDownloadedInstrument *downloaded; + IDirectMusicCollection *collection; + IDirectMusicInstrument *instrument; + IPersistStream *persist; + IDirectMusicPort *port; + IDirectMusic *dmusic; + WCHAR name[MAX_PATH]; + IStream *stream; + DWORD patch; + HRESULT hr; + + port = create_synth_port(&dmusic); + + hr = CoCreateInstance(&CLSID_DirectMusicCollection, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicCollection, (void **)&collection); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicCollection_QueryInterface(collection, &IID_IPersistStream, (void **)&persist); + ok(hr == S_OK, "got %#lx\n", hr); + hr = CreateStreamOnHGlobal(0, TRUE, &stream); + ok(hr == S_OK, "got %#lx\n", hr); + + CHUNK_RIFF(stream, "DLS ") + { + DLSHEADER colh = {.cInstruments = 1}; + struct + { + POOLTABLE head; + POOLCUE cues[1]; + } ptbl = + { + .head = {.cbSize = sizeof(POOLTABLE), .cCues = ARRAY_SIZE(ptbl.cues)}, + .cues = {{.ulOffset = 0}}, /* offsets in wvpl */ + }; + + CHUNK_DATA(stream, "colh", colh); + CHUNK_LIST(stream, "lins") + { + CHUNK_LIST(stream, "ins ") + { + INSTHEADER insh = {.cRegions = 1, .Locale = {.ulBank = 0x12, .ulInstrument = 0x34}}; + + CHUNK_DATA(stream, "insh", insh); + CHUNK_LIST(stream, "lrgn") + { + CHUNK_LIST(stream, "rgn ") + { + RGNHEADER rgnh = + { + .RangeKey = {.usLow = 0, .usHigh = 127}, + .RangeVelocity = {.usLow = 1, .usHigh = 127}, + }; + WAVELINK wlnk = {.ulChannel = 1, .ulTableIndex = 0}; + WSMPL wsmp = {.cbSize = sizeof(WSMPL)}; + + CHUNK_DATA(stream, "rgnh", rgnh); + CHUNK_DATA(stream, "wsmp", wsmp); + CHUNK_DATA(stream, "wlnk", wlnk); + } + CHUNK_END; + } + CHUNK_END; + + CHUNK_LIST(stream, "lart") + { + CONNECTIONLIST connections = {.cbSize = sizeof(connections)}; + CHUNK_DATA(stream, "art1", connections); + } + CHUNK_END; + } + CHUNK_END; + } + CHUNK_END; + CHUNK_DATA(stream, "ptbl", ptbl); + CHUNK_LIST(stream, "wvpl") + { + CHUNK_LIST(stream, "wave") + { + WAVEFORMATEX fmt = + { + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 1, + .wBitsPerSample = 8, + .nSamplesPerSec = 22050, + .nAvgBytesPerSec = 22050, + .nBlockAlign = 1, + }; + BYTE data[16] = {0}; + + /* native returns DMUS_E_INVALIDOFFSET from DownloadInstrument if data is last */ + CHUNK_DATA(stream, "data", data); + CHUNK_DATA(stream, "fmt ", fmt); + } + CHUNK_END; + } + CHUNK_END; + } + CHUNK_END; + + hr = IStream_Seek(stream, zero, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IPersistStream_Load(persist, stream); + ok(hr == S_OK, "got %#lx\n", hr); + IPersistStream_Release(persist); + IStream_Release(stream); + + patch = 0xdeadbeef; + wcscpy(name, L"DeadBeef"); + hr = IDirectMusicCollection_EnumInstrument(collection, 0, &patch, name, ARRAY_SIZE(name)); + ok(hr == S_OK, "got %#lx\n", hr); + ok(patch == 0x1234, "got %#lx\n", patch); + ok(*name == 0, "got %s\n", debugstr_w(name)); + hr = IDirectMusicCollection_EnumInstrument(collection, 1, &patch, name, ARRAY_SIZE(name)); + ok(hr == S_FALSE, "got %#lx\n", hr); + + hr = IDirectMusicCollection_GetInstrument(collection, 0x1234, &instrument); + ok(hr == S_OK, "got %#lx\n", hr); + + check_interface(instrument, &IID_IDirectMusicObject, FALSE); + check_interface(instrument, &IID_IDirectMusicDownload, FALSE); + check_interface(instrument, &IID_IDirectMusicDownloadedInstrument, FALSE); + + hr = IDirectMusicPort_DownloadInstrument(port, instrument, &downloaded, NULL, 0); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (hr != S_OK) goto skip_tests; + + check_interface(downloaded, &IID_IDirectMusicObject, FALSE); + check_interface(downloaded, &IID_IDirectMusicDownload, FALSE); + check_interface(downloaded, &IID_IDirectMusicInstrument, FALSE); + + hr = IDirectMusicPort_UnloadInstrument(port, downloaded); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicDownloadedInstrument_Release(downloaded); + + IDirectMusicInstrument_Release(instrument); + +skip_tests: + IDirectMusicCollection_Release(collection); + IDirectMusicPort_Release(port); IDirectMusic_Release(dmusic); } @@ -1115,6 +1342,7 @@ START_TEST(dmusic) test_master_clock(); test_synthport(); test_port_download(); + test_download_instrument(); CoUninitialize(); } From acf00fa5dcb2555a2991875caff5eaf56ee7030e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 22 Aug 2023 17:45:36 +0200 Subject: [PATCH 0793/1148] dmusic: Always return S_FALSE from DllCanUnloadNow. (cherry picked from commit abc6ecc89dda625a93266851cc8aff4d6626f87b) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/buffer.c | 2 -- dlls/dmusic/clock.c | 2 -- dlls/dmusic/collection.c | 2 -- dlls/dmusic/dmusic.c | 2 -- dlls/dmusic/dmusic_main.c | 21 --------------------- dlls/dmusic/dmusic_private.h | 8 -------- dlls/dmusic/download.c | 2 -- dlls/dmusic/instrument.c | 2 -- dlls/dmusic/port.c | 6 ------ 9 files changed, 47 deletions(-) diff --git a/dlls/dmusic/buffer.c b/dlls/dmusic/buffer.c index 9aaa1a074b4..a2ad137fe4b 100644 --- a/dlls/dmusic/buffer.c +++ b/dlls/dmusic/buffer.c @@ -71,7 +71,6 @@ static ULONG WINAPI IDirectMusicBufferImpl_Release(LPDIRECTMUSICBUFFER iface) if (!ref) { free(This->data); free(This); - DMUSIC_UnlockModule(); } return ref; @@ -319,7 +318,6 @@ HRESULT DMUSIC_CreateDirectMusicBufferImpl(LPDMUS_BUFFERDESC desc, LPVOID* ret_i return E_OUTOFMEMORY; } - DMUSIC_LockModule(); *ret_iface = &dmbuffer->IDirectMusicBuffer_iface; return S_OK; diff --git a/dlls/dmusic/clock.c b/dlls/dmusic/clock.c index 15a04c844e5..19441d3d56f 100644 --- a/dlls/dmusic/clock.c +++ b/dlls/dmusic/clock.c @@ -63,7 +63,6 @@ static ULONG WINAPI IReferenceClockImpl_Release(IReferenceClock *iface) if (!ref) { free(This); - DMUSIC_UnlockModule(); } return ref; @@ -137,7 +136,6 @@ HRESULT DMUSIC_CreateReferenceClockImpl(LPCGUID riid, LPVOID* ret_iface, LPUNKNO clock->rtTime = 0; clock->pClockInfo.dwSize = sizeof (DMUS_CLOCKINFO); - DMUSIC_LockModule(); hr = IReferenceClockImpl_QueryInterface(&clock->IReferenceClock_iface, riid, ret_iface); IReferenceClock_Release(&clock->IReferenceClock_iface); diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 0febf9dfd08..3f1c70b01e3 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -99,7 +99,6 @@ static ULONG WINAPI IDirectMusicCollectionImpl_Release(IDirectMusicCollection *i if (!ref) { free(This); - DMUSIC_UnlockModule(); } return ref; @@ -544,7 +543,6 @@ HRESULT DMUSIC_CreateDirectMusicCollectionImpl(REFIID lpcGUID, void **ppobj, IUn list_init (&obj->Instruments); - DMUSIC_LockModule(); hr = IDirectMusicCollection_QueryInterface(&obj->IDirectMusicCollection_iface, lpcGUID, ppobj); IDirectMusicCollection_Release(&obj->IDirectMusicCollection_iface); diff --git a/dlls/dmusic/dmusic.c b/dlls/dmusic/dmusic.c index 8fb35e65809..d284b32fdd7 100644 --- a/dlls/dmusic/dmusic.c +++ b/dlls/dmusic/dmusic.c @@ -201,7 +201,6 @@ static ULONG WINAPI IDirectMusic8Impl_Release(LPDIRECTMUSIC8 iface) free(This->system_ports); free(This->ports); free(This); - DMUSIC_UnlockModule(); } return ref; @@ -606,7 +605,6 @@ HRESULT DMUSIC_CreateDirectMusicImpl(REFIID riid, void **ret_iface, IUnknown *un create_system_ports_list(dmusic); - DMUSIC_LockModule(); ret = IDirectMusic8Impl_QueryInterface(&dmusic->IDirectMusic8_iface, riid, ret_iface); IDirectMusic8_Release(&dmusic->IDirectMusic8_iface); diff --git a/dlls/dmusic/dmusic_main.c b/dlls/dmusic/dmusic_main.c index 5d4939937a9..509e80f872b 100644 --- a/dlls/dmusic/dmusic_main.c +++ b/dlls/dmusic/dmusic_main.c @@ -39,8 +39,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmusic); -LONG DMUSIC_refCount = 0; - typedef struct { IClassFactory IClassFactory_iface; HRESULT (*fnCreateInstance)(REFIID riid, void **ppv, IUnknown *pUnkOuter); @@ -76,15 +74,11 @@ static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID r static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) { - DMUSIC_LockModule(); - return 2; /* non-heap based object */ } static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) { - DMUSIC_UnlockModule(); - return 1; /* non-heap based object */ } @@ -101,12 +95,6 @@ static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock) { TRACE("(%d)\n", dolock); - - if (dolock) - DMUSIC_LockModule(); - else - DMUSIC_UnlockModule(); - return S_OK; } @@ -122,15 +110,6 @@ static IClassFactoryImpl DirectMusic_CF = {{&classfactory_vtbl}, DMUSIC_CreateDi static IClassFactoryImpl Collection_CF = {{&classfactory_vtbl}, DMUSIC_CreateDirectMusicCollectionImpl}; -/****************************************************************** - * DllCanUnloadNow (DMUSIC.@) - * - * - */ -HRESULT WINAPI DllCanUnloadNow(void) -{ - return DMUSIC_refCount != 0 ? S_FALSE : S_OK; -} /****************************************************************** diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 1b91f4bac94..ffc6aaca539 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -217,14 +217,6 @@ static inline IDirectMusicInstrumentImpl *impl_from_IDirectMusicInstrument(IDire /* custom :) */ extern HRESULT IDirectMusicInstrumentImpl_CustomLoad(IDirectMusicInstrument *iface, IStream *stream); -/********************************************************************** - * Dll lifetime tracking declaration for dmusic.dll - */ -extern LONG DMUSIC_refCount; -static inline void DMUSIC_LockModule(void) { InterlockedIncrement( &DMUSIC_refCount ); } -static inline void DMUSIC_UnlockModule(void) { InterlockedDecrement( &DMUSIC_refCount ); } - - /***************************************************************************** * Misc. */ diff --git a/dlls/dmusic/download.c b/dlls/dmusic/download.c index 48f0efd5f69..095ea23235b 100644 --- a/dlls/dmusic/download.c +++ b/dlls/dmusic/download.c @@ -65,7 +65,6 @@ static ULONG WINAPI IDirectMusicDownloadImpl_Release(IDirectMusicDownload *iface if (!ref) { free(This); - DMUSIC_UnlockModule(); } return ref; @@ -102,6 +101,5 @@ HRESULT DMUSIC_CreateDirectMusicDownloadImpl(const GUID *guid, void **ret_iface, download->ref = 1; *ret_iface = download; - DMUSIC_LockModule(); return S_OK; } diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 7b7ee3def64..767c4419225 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -82,7 +82,6 @@ static ULONG WINAPI IDirectMusicInstrumentImpl_Release(LPDIRECTMUSICINSTRUMENT i free(This->articulations->connections); free(This->articulations); free(This); - DMUSIC_UnlockModule(); } return ref; @@ -133,7 +132,6 @@ HRESULT DMUSIC_CreateDirectMusicInstrumentImpl (LPCGUID lpcGUID, LPVOID* ppobj, dminst->IDirectMusicInstrument_iface.lpVtbl = &DirectMusicInstrument_Vtbl; dminst->ref = 1; - DMUSIC_LockModule(); hr = IDirectMusicInstrument_QueryInterface(&dminst->IDirectMusicInstrument_iface, lpcGUID, ppobj); IDirectMusicInstrument_Release(&dminst->IDirectMusicInstrument_iface); diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index 518b4fe9666..ccf785d4b74 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -105,7 +105,6 @@ static ULONG WINAPI IDirectMusicDownloadedInstrumentImpl_Release(LPDIRECTMUSICDO { free(This->data); free(This); - DMUSIC_UnlockModule(); } return ref; @@ -141,7 +140,6 @@ static HRESULT DMUSIC_CreateDirectMusicDownloadedInstrumentImpl(IDirectMusicDown object->ref = 1; *instrument = &object->IDirectMusicDownloadedInstrument_iface; - DMUSIC_LockModule(); return S_OK; } @@ -178,8 +176,6 @@ static ULONG WINAPI synth_port_AddRef(IDirectMusicPort *iface) TRACE("(%p): new ref = %lu\n", This, ref); - DMUSIC_LockModule(); - return ref; } @@ -204,8 +200,6 @@ static ULONG WINAPI synth_port_Release(IDirectMusicPort *iface) free(This); } - DMUSIC_UnlockModule(); - return ref; } From be699385a7d5aedcba2c965cb6b0714355f6ed74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 11:16:59 +0200 Subject: [PATCH 0794/1148] dmusic: Simplify and cleanup IDirectMusicDownload constructor. (cherry picked from commit ac25d09955a711aac61bbe30656eef2089e73bc3) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/dmusic_private.h | 3 ++- dlls/dmusic/download.c | 15 +++++---------- dlls/dmusic/port.c | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index ffc6aaca539..1f8c6e227ff 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -100,10 +100,11 @@ extern HRESULT DMUSIC_CreateDirectMusicCollectionImpl(REFIID riid, void **ppobj, /* Internal */ extern HRESULT DMUSIC_CreateDirectMusicBufferImpl(LPDMUS_BUFFERDESC desc, LPVOID* ret_iface); -extern HRESULT DMUSIC_CreateDirectMusicDownloadImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter); extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter); extern HRESULT DMUSIC_CreateDirectMusicInstrumentImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter); +extern HRESULT download_create(IDirectMusicDownload **ret_iface); + /***************************************************************************** * IDirectMusic8Impl implementation structure */ diff --git a/dlls/dmusic/download.c b/dlls/dmusic/download.c index 095ea23235b..f7ac8d92ee6 100644 --- a/dlls/dmusic/download.c +++ b/dlls/dmusic/download.c @@ -85,21 +85,16 @@ static const IDirectMusicDownloadVtbl DirectMusicDownload_Vtbl = { IDirectMusicDownloadImpl_GetBuffer }; -/* for ClassFactory */ -HRESULT DMUSIC_CreateDirectMusicDownloadImpl(const GUID *guid, void **ret_iface, IUnknown *unk_outer) +HRESULT download_create(IDirectMusicDownload **ret_iface) { IDirectMusicDownloadImpl *download; - download = malloc(sizeof(*download)); - if (!download) - { - *ret_iface = NULL; - return E_OUTOFMEMORY; - } - + *ret_iface = NULL; + if (!(download = calloc(1, sizeof(*download)))) return E_OUTOFMEMORY; download->IDirectMusicDownload_iface.lpVtbl = &DirectMusicDownload_Vtbl; download->ref = 1; - *ret_iface = download; + TRACE("Created DirectMusicDownload %p\n", download); + *ret_iface = &download->IDirectMusicDownload_iface; return S_OK; } diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index ccf785d4b74..18e290cc897 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -582,7 +582,7 @@ static HRESULT WINAPI synth_port_download_GetBuffer(IDirectMusicPortDownload *if if (!IDMDownload) return E_POINTER; - return DMUSIC_CreateDirectMusicDownloadImpl(&IID_IDirectMusicDownload, (LPVOID*)IDMDownload, NULL); + return download_create(IDMDownload); } static HRESULT WINAPI synth_port_download_AllocateBuffer(IDirectMusicPortDownload *iface, DWORD size, From c134ea2cc887f2546a712ed0411bad81ca444c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 24 Aug 2023 11:03:57 +0200 Subject: [PATCH 0795/1148] dmusic: Move IDirectMusicDownloadImpl struct to where it is used. (cherry picked from commit f96f53ea85818775d2fcafe63412cb76c71ecb22) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/dmusic_private.h | 12 ------------ dlls/dmusic/download.c | 8 ++++++++ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 1f8c6e227ff..bc8ba5b483c 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -50,7 +50,6 @@ typedef struct IDirectMusic8Impl IDirectMusic8Impl; typedef struct IDirectMusicBufferImpl IDirectMusicBufferImpl; typedef struct IDirectMusicDownloadedInstrumentImpl IDirectMusicDownloadedInstrumentImpl; -typedef struct IDirectMusicDownloadImpl IDirectMusicDownloadImpl; typedef struct IReferenceClockImpl IReferenceClockImpl; typedef struct IDirectMusicInstrumentImpl IDirectMusicInstrumentImpl; @@ -148,17 +147,6 @@ struct IDirectMusicDownloadedInstrumentImpl { void *data; }; -/***************************************************************************** - * IDirectMusicDownloadImpl implementation structure - */ -struct IDirectMusicDownloadImpl { - /* IUnknown fields */ - IDirectMusicDownload IDirectMusicDownload_iface; - LONG ref; - - /* IDirectMusicDownloadImpl fields */ -}; - /** Internal factory */ extern HRESULT synth_port_create(IDirectMusic8Impl *parent, DMUS_PORTPARAMS *port_params, DMUS_PORTCAPS *port_caps, IDirectMusicPort **port); diff --git a/dlls/dmusic/download.c b/dlls/dmusic/download.c index f7ac8d92ee6..fc94cac6a68 100644 --- a/dlls/dmusic/download.c +++ b/dlls/dmusic/download.c @@ -23,6 +23,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmusic); +struct IDirectMusicDownloadImpl +{ + IDirectMusicDownload IDirectMusicDownload_iface; + LONG ref; +}; + +typedef struct IDirectMusicDownloadImpl IDirectMusicDownloadImpl; + static inline IDirectMusicDownloadImpl* impl_from_IDirectMusicDownload(IDirectMusicDownload *iface) { return CONTAINING_RECORD(iface, IDirectMusicDownloadImpl, IDirectMusicDownload_iface); From 7a177f8c20fe8fc303f2e90565a551e1ace2b47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 24 Aug 2023 10:59:27 +0200 Subject: [PATCH 0796/1148] dmusic: Rename IDirectMusicDownloadImpl method prefix to download. (cherry picked from commit 770749491d786da425c0fb6e80460b7f29d5ff66) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/download.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/dlls/dmusic/download.c b/dlls/dmusic/download.c index fc94cac6a68..9c3f0d6b0cd 100644 --- a/dlls/dmusic/download.c +++ b/dlls/dmusic/download.c @@ -23,21 +23,18 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmusic); -struct IDirectMusicDownloadImpl +struct download { IDirectMusicDownload IDirectMusicDownload_iface; LONG ref; }; -typedef struct IDirectMusicDownloadImpl IDirectMusicDownloadImpl; - -static inline IDirectMusicDownloadImpl* impl_from_IDirectMusicDownload(IDirectMusicDownload *iface) +static inline struct download *impl_from_IDirectMusicDownload(IDirectMusicDownload *iface) { - return CONTAINING_RECORD(iface, IDirectMusicDownloadImpl, IDirectMusicDownload_iface); + return CONTAINING_RECORD(iface, struct download, IDirectMusicDownload_iface); } -/* IDirectMusicDownloadImpl IUnknown part: */ -static HRESULT WINAPI IDirectMusicDownloadImpl_QueryInterface(IDirectMusicDownload *iface, REFIID riid, void **ret_iface) +static HRESULT WINAPI download_QueryInterface(IDirectMusicDownload *iface, REFIID riid, void **ret_iface) { TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); @@ -54,9 +51,9 @@ static HRESULT WINAPI IDirectMusicDownloadImpl_QueryInterface(IDirectMusicDownlo return E_NOINTERFACE; } -static ULONG WINAPI IDirectMusicDownloadImpl_AddRef(IDirectMusicDownload *iface) +static ULONG WINAPI download_AddRef(IDirectMusicDownload *iface) { - IDirectMusicDownloadImpl *This = impl_from_IDirectMusicDownload(iface); + struct download *This = impl_from_IDirectMusicDownload(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p): new ref = %lu\n", iface, ref); @@ -64,9 +61,9 @@ static ULONG WINAPI IDirectMusicDownloadImpl_AddRef(IDirectMusicDownload *iface) return ref; } -static ULONG WINAPI IDirectMusicDownloadImpl_Release(IDirectMusicDownload *iface) +static ULONG WINAPI download_Release(IDirectMusicDownload *iface) { - IDirectMusicDownloadImpl *This = impl_from_IDirectMusicDownload(iface); + struct download *This = impl_from_IDirectMusicDownload(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p): new ref = %lu\n", iface, ref); @@ -78,28 +75,28 @@ static ULONG WINAPI IDirectMusicDownloadImpl_Release(IDirectMusicDownload *iface return ref; } -/* IDirectMusicDownloadImpl IDirectMusicDownload part: */ -static HRESULT WINAPI IDirectMusicDownloadImpl_GetBuffer(IDirectMusicDownload *iface, void **buffer, DWORD *size) +static HRESULT WINAPI download_GetBuffer(IDirectMusicDownload *iface, void **buffer, DWORD *size) { FIXME("(%p, %p, %p): stub\n", iface, buffer, size); return S_OK; } -static const IDirectMusicDownloadVtbl DirectMusicDownload_Vtbl = { - IDirectMusicDownloadImpl_QueryInterface, - IDirectMusicDownloadImpl_AddRef, - IDirectMusicDownloadImpl_Release, - IDirectMusicDownloadImpl_GetBuffer +static const IDirectMusicDownloadVtbl download_vtbl = +{ + download_QueryInterface, + download_AddRef, + download_Release, + download_GetBuffer, }; HRESULT download_create(IDirectMusicDownload **ret_iface) { - IDirectMusicDownloadImpl *download; + struct download *download; *ret_iface = NULL; if (!(download = calloc(1, sizeof(*download)))) return E_OUTOFMEMORY; - download->IDirectMusicDownload_iface.lpVtbl = &DirectMusicDownload_Vtbl; + download->IDirectMusicDownload_iface.lpVtbl = &download_vtbl; download->ref = 1; TRACE("Created DirectMusicDownload %p\n", download); From 57100447bda823a4e2448624d8c2fde26a4cf210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 17:21:09 +0200 Subject: [PATCH 0797/1148] dmusic: Implement synth port IDirectMusicPortDownload_GetDLId. (cherry picked from commit 45f61965dc386f76c139adb358edc780d144a0f1) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/port.c | 12 ++++++++++-- dlls/dmusic/tests/dmusic.c | 8 ++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index 18e290cc897..caae177a3a9 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -40,6 +40,8 @@ struct synth_port { DMUS_PORTPARAMS params; int nrofgroups; DMUSIC_PRIVATE_CHANNEL_GROUP group[1]; + + DWORD next_dlid; }; static inline IDirectMusicDownloadedInstrumentImpl* impl_from_IDirectMusicDownloadedInstrument(IDirectMusicDownloadedInstrument *iface) @@ -595,11 +597,17 @@ static HRESULT WINAPI synth_port_download_AllocateBuffer(IDirectMusicPortDownloa return S_OK; } -static HRESULT WINAPI synth_port_download_GetDLId(IDirectMusicPortDownload *iface, DWORD *start_DLId, DWORD count) +static HRESULT WINAPI synth_port_download_GetDLId(IDirectMusicPortDownload *iface, DWORD *first, DWORD count) { struct synth_port *This = synth_from_IDirectMusicPortDownload(iface); - FIXME("(%p/%p, %p, %lu): stub\n", iface, This, start_DLId, count); + TRACE("(%p/%p, %p, %lu)\n", iface, This, first, count); + + if (!first) return E_POINTER; + if (!count) return E_INVALIDARG; + + *first = This->next_dlid; + This->next_dlid += count; return S_OK; } diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 6df37e3d63d..05568208fee 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1081,14 +1081,14 @@ static void test_port_download(void) /* GetDLId allocates the given number of slots and returns only the first */ hr = IDirectMusicPortDownload_GetDLId(port, NULL, 0); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicPortDownload_GetDLId(port, ids, 0); - todo_wine ok(hr == E_INVALIDARG, "got %#lx\n", hr); + ok(hr == E_INVALIDARG, "got %#lx\n", hr); memset(ids, 0xcc, sizeof(ids)); hr = IDirectMusicPortDownload_GetDLId(port, ids, 4); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(ids[0] == 0, "got %#lx\n", ids[0]); + ok(ids[0] == 0, "got %#lx\n", ids[0]); ok(ids[1] == 0xcccccccc, "got %#lx\n", ids[1]); /* GetBuffer looks up allocated ids to find downloaded buffers */ @@ -1168,7 +1168,7 @@ static void test_port_download(void) /* DLIds are never released */ hr = IDirectMusicPortDownload_GetDLId(port, ids, 1); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(ids[0] == 4, "got %#lx\n", ids[0]); + ok(ids[0] == 4, "got %#lx\n", ids[0]); IDirectMusicDownload_Release(download); From 9a795c86d76530014e72040243d3607ec77b4cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 15:55:47 +0200 Subject: [PATCH 0798/1148] dmusic: Implement IDirectMusicPortDownload_AllocateBuffer. (cherry picked from commit 30df87af090801ae3d7d619325f15ddab462d266) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/dmusic_private.h | 2 +- dlls/dmusic/download.c | 17 ++++++++++++++--- dlls/dmusic/port.c | 11 +++++++---- dlls/dmusic/tests/dmusic.c | 16 +++++++--------- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index bc8ba5b483c..1eea2f845cb 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -102,7 +102,7 @@ extern HRESULT DMUSIC_CreateDirectMusicBufferImpl(LPDMUS_BUFFERDESC desc, LPVOID extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter); extern HRESULT DMUSIC_CreateDirectMusicInstrumentImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter); -extern HRESULT download_create(IDirectMusicDownload **ret_iface); +extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface); /***************************************************************************** * IDirectMusic8Impl implementation structure diff --git a/dlls/dmusic/download.c b/dlls/dmusic/download.c index 9c3f0d6b0cd..6dac3e1b725 100644 --- a/dlls/dmusic/download.c +++ b/dlls/dmusic/download.c @@ -27,8 +27,13 @@ struct download { IDirectMusicDownload IDirectMusicDownload_iface; LONG ref; + + DWORD size; + BYTE data[]; }; +C_ASSERT(sizeof(struct download) == offsetof(struct download, data[0])); + static inline struct download *impl_from_IDirectMusicDownload(IDirectMusicDownload *iface) { return CONTAINING_RECORD(iface, struct download, IDirectMusicDownload_iface); @@ -77,7 +82,12 @@ static ULONG WINAPI download_Release(IDirectMusicDownload *iface) static HRESULT WINAPI download_GetBuffer(IDirectMusicDownload *iface, void **buffer, DWORD *size) { - FIXME("(%p, %p, %p): stub\n", iface, buffer, size); + struct download *This = impl_from_IDirectMusicDownload(iface); + + TRACE("(%p, %p, %p)\n", iface, buffer, size); + + *buffer = This->data; + *size = This->size; return S_OK; } @@ -90,14 +100,15 @@ static const IDirectMusicDownloadVtbl download_vtbl = download_GetBuffer, }; -HRESULT download_create(IDirectMusicDownload **ret_iface) +HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface) { struct download *download; *ret_iface = NULL; - if (!(download = calloc(1, sizeof(*download)))) return E_OUTOFMEMORY; + if (!(download = malloc(offsetof(struct download, data[size])))) return E_OUTOFMEMORY; download->IDirectMusicDownload_iface.lpVtbl = &download_vtbl; download->ref = 1; + download->size = size; TRACE("Created DirectMusicDownload %p\n", download); *ret_iface = &download->IDirectMusicDownload_iface; diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index caae177a3a9..9c932420623 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -584,17 +584,20 @@ static HRESULT WINAPI synth_port_download_GetBuffer(IDirectMusicPortDownload *if if (!IDMDownload) return E_POINTER; - return download_create(IDMDownload); + return download_create(0, IDMDownload); } static HRESULT WINAPI synth_port_download_AllocateBuffer(IDirectMusicPortDownload *iface, DWORD size, - IDirectMusicDownload **IDMDownload) + IDirectMusicDownload **download) { struct synth_port *This = synth_from_IDirectMusicPortDownload(iface); - FIXME("(%p/%p, %lu, %p): stub\n", iface, This, size, IDMDownload); + TRACE("(%p/%p, %lu, %p)\n", iface, This, size, download); - return S_OK; + if (!download) return E_POINTER; + if (!size) return E_INVALIDARG; + + return download_create(size, download); } static HRESULT WINAPI synth_port_download_GetDLId(IDirectMusicPortDownload *iface, DWORD *first, DWORD count) diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 05568208fee..8cfcc11ffd8 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1065,9 +1065,9 @@ static void test_port_download(void) /* AllocateBuffer use the exact requested size */ hr = IDirectMusicPortDownload_AllocateBuffer(port, 0, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicPortDownload_AllocateBuffer(port, 0, &download); - todo_wine ok(hr == E_INVALIDARG, "got %#lx\n", hr); + ok(hr == E_INVALIDARG, "got %#lx\n", hr); hr = IDirectMusicPortDownload_AllocateBuffer(port, 1, &download); ok(hr == S_OK, "got %#lx\n", hr); @@ -1075,8 +1075,8 @@ static void test_port_download(void) buffer = invalid_ptr; hr = IDirectMusicDownload_GetBuffer(download, (void **)&buffer, &size); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(size == 1, "got %#lx\n", size); - todo_wine ok(buffer != invalid_ptr, "got %p\n", buffer); + ok(size == 1, "got %#lx\n", size); + ok(buffer != invalid_ptr, "got %p\n", buffer); IDirectMusicDownload_Release(download); /* GetDLId allocates the given number of slots and returns only the first */ @@ -1107,14 +1107,13 @@ static void test_port_download(void) download = invalid_ptr; hr = IDirectMusicPortDownload_AllocateBuffer(port, sizeof(struct wave_download), &download); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(download != invalid_ptr, "got %p\n", download); - if (download == invalid_ptr) goto skip_tests; + ok(download != invalid_ptr, "got %p\n", download); size = 0xdeadbeef; wave_download = invalid_ptr; hr = IDirectMusicDownload_GetBuffer(download, (void **)&wave_download, &size); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(size == sizeof(struct wave_download), "got %#lx\n", size); - todo_wine ok(wave_download != invalid_ptr, "got %p\n", wave_download); + ok(size == sizeof(struct wave_download), "got %#lx\n", size); + ok(wave_download != invalid_ptr, "got %p\n", wave_download); wave_download->info.cbSize = sizeof(struct wave_download); wave_download->info.dwDLId = 2; wave_download->info.dwDLType = 0; @@ -1172,7 +1171,6 @@ static void test_port_download(void) IDirectMusicDownload_Release(download); -skip_tests: IDirectMusicPortDownload_Release(port); } From dfcb8ee21a6f9878deaae9bf9c766a1c10004489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 18:16:00 +0200 Subject: [PATCH 0799/1148] dmusic: Implement IDirectMusicPortDownload_(Download|Unload). (cherry picked from commit 73d1e0663080c4e6020fc7e729e0a0d014ca9d62) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/port.c | 70 ++++++++++++++++++++++++++++++++++---- dlls/dmusic/tests/dmusic.c | 6 ++-- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index 9c932420623..316af0c4550 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -25,6 +25,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmusic); +struct download_entry +{ + struct list entry; + IDirectMusicDownload *download; + HANDLE handle; + DWORD id; +}; + struct synth_port { IDirectMusicPort IDirectMusicPort_iface; IDirectMusicPortDownload IDirectMusicPortDownload_iface; @@ -41,6 +49,7 @@ struct synth_port { int nrofgroups; DMUSIC_PRIVATE_CHANNEL_GROUP group[1]; + struct list downloads; DWORD next_dlid; }; @@ -190,6 +199,15 @@ static ULONG WINAPI synth_port_Release(IDirectMusicPort *iface) if (!ref) { + struct download_entry *entry, *next; + + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &This->downloads, struct download_entry, entry) + { + list_remove(&entry->entry); + IDirectMusicDownload_Release(entry->download); + free(entry); + } + dmusic_remove_port(This->parent, iface); IDirectMusicSynthSink_Release(This->synth_sink); IDirectMusicSynth_Activate(This->synth, FALSE); @@ -624,22 +642,61 @@ static HRESULT WINAPI synth_port_download_GetAppend(IDirectMusicPortDownload *if return S_OK; } -static HRESULT WINAPI synth_port_download_Download(IDirectMusicPortDownload *iface, IDirectMusicDownload *IDMDownload) +static HRESULT WINAPI synth_port_download_Download(IDirectMusicPortDownload *iface, IDirectMusicDownload *download) { struct synth_port *This = synth_from_IDirectMusicPortDownload(iface); + struct download_entry *entry; + DMUS_DOWNLOADINFO *info; + HANDLE handle; + BOOL can_free; + DWORD size; + HRESULT hr; - FIXME("(%p/%p)->(%p): stub\n", iface, This, IDMDownload); + TRACE("(%p/%p)->(%p)\n", iface, This, download); - return S_OK; + if (!download) return E_POINTER; + + LIST_FOR_EACH_ENTRY(entry, &This->downloads, struct download_entry, entry) + if (entry->download == download) return DMUS_E_ALREADY_DOWNLOADED; + + if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY; + if (SUCCEEDED(hr = IDirectMusicDownload_GetBuffer(download, (void **)&info, &size)) + && SUCCEEDED(hr = IDirectMusicSynth_Download(This->synth, &handle, info, &can_free))) + { + entry->download = download; + IDirectMusicDownload_AddRef(download); + entry->id = info->dwDLId; + entry->handle = handle; + list_add_tail(&This->downloads, &entry->entry); + } + + if (FAILED(hr)) free(entry); + return hr; } -static HRESULT WINAPI synth_port_download_Unload(IDirectMusicPortDownload *iface, IDirectMusicDownload *IDMDownload) +static HRESULT WINAPI synth_port_download_Unload(IDirectMusicPortDownload *iface, IDirectMusicDownload *download) { struct synth_port *This = synth_from_IDirectMusicPortDownload(iface); + struct download_entry *entry; + HANDLE handle = 0; - FIXME("(%p/%p)->(%p): stub\n", iface, This, IDMDownload); + TRACE("(%p/%p)->(%p)\n", iface, This, download); - return S_OK; + if (!download) return E_POINTER; + + LIST_FOR_EACH_ENTRY(entry, &This->downloads, struct download_entry, entry) + { + if (entry->download == download) + { + list_remove(&entry->entry); + IDirectMusicDownload_Release(entry->download); + handle = entry->handle; + free(entry); + break; + } + } + + return IDirectMusicSynth_Unload(This->synth, handle, NULL, NULL); } static const IDirectMusicPortDownloadVtbl synth_port_download_vtbl = { @@ -795,6 +852,7 @@ HRESULT synth_port_create(IDirectMusic8Impl *parent, DMUS_PORTPARAMS *port_param obj->parent = parent; obj->active = FALSE; obj->params = *port_params; + list_init(&obj->downloads); hr = CoCreateInstance(&CLSID_DirectMusicSynth, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicSynth, (void **)&obj->synth); diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 8cfcc11ffd8..13716c096e9 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1122,7 +1122,7 @@ static void test_port_download(void) todo_wine ok(hr == DMUS_E_NOT_DOWNLOADED_TO_PORT, "got %#lx\n", hr); hr = IDirectMusicPortDownload_Download(port, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicPortDownload_Download(port, download); todo_wine ok(hr == DMUS_E_UNKNOWNDOWNLOAD, "got %#lx\n", hr); @@ -1145,7 +1145,7 @@ static void test_port_download(void) hr = IDirectMusicPortDownload_Download(port, download); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPortDownload_Download(port, download); - todo_wine ok(hr == DMUS_E_ALREADY_DOWNLOADED, "got %#lx\n", hr); + ok(hr == DMUS_E_ALREADY_DOWNLOADED, "got %#lx\n", hr); tmp_download = invalid_ptr; hr = IDirectMusicPortDownload_GetBuffer(port, 2, &tmp_download); @@ -1154,7 +1154,7 @@ static void test_port_download(void) if (tmp_download != invalid_ptr) IDirectMusicDownload_Release(tmp_download); hr = IDirectMusicPortDownload_Unload(port, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicPortDownload_Unload(port, download); ok(hr == S_OK, "got %#lx\n", hr); From 7f53cbeac19a04318ca2bb5ee72c1fb26c3a5010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 15:51:09 +0200 Subject: [PATCH 0800/1148] dmusic: Implement IDirectMusicPortDownload_GetBuffer. (cherry picked from commit d3b191503e7316890d630c2fabff16ef01bb63c1) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/port.c | 23 +++++++++++++++++------ dlls/dmusic/tests/dmusic.c | 14 +++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index 316af0c4550..eaf2bb139f1 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -592,17 +592,28 @@ static ULONG WINAPI synth_port_download_Release(IDirectMusicPortDownload *iface) return IDirectMusicPort_Release(&This->IDirectMusicPort_iface); } -static HRESULT WINAPI synth_port_download_GetBuffer(IDirectMusicPortDownload *iface, DWORD DLId, - IDirectMusicDownload **IDMDownload) +static HRESULT WINAPI synth_port_download_GetBuffer(IDirectMusicPortDownload *iface, DWORD id, + IDirectMusicDownload **download) { struct synth_port *This = synth_from_IDirectMusicPortDownload(iface); + struct download_entry *entry; - FIXME("(%p/%p, %lu, %p): stub\n", iface, This, DLId, IDMDownload); + TRACE("(%p/%p, %lu, %p)\n", iface, This, id, download); - if (!IDMDownload) - return E_POINTER; + if (!download) return E_POINTER; + if (id >= This->next_dlid) return DMUS_E_INVALID_DOWNLOADID; + + LIST_FOR_EACH_ENTRY(entry, &This->downloads, struct download_entry, entry) + { + if (entry->id == id) + { + *download = entry->download; + IDirectMusicDownload_AddRef(entry->download); + return S_OK; + } + } - return download_create(0, IDMDownload); + return DMUS_E_NOT_DOWNLOADED_TO_PORT; } static HRESULT WINAPI synth_port_download_AllocateBuffer(IDirectMusicPortDownload *iface, DWORD size, diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 13716c096e9..a8152822613 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1059,9 +1059,9 @@ static void test_port_download(void) hr = IDirectMusicPortDownload_GetBuffer(port, 0, NULL); ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicPortDownload_GetBuffer(port, 0, &download); - todo_wine ok(hr == DMUS_E_INVALID_DOWNLOADID, "got %#lx\n", hr); + ok(hr == DMUS_E_INVALID_DOWNLOADID, "got %#lx\n", hr); hr = IDirectMusicPortDownload_GetBuffer(port, 0xdeadbeef, &download); - todo_wine ok(hr == DMUS_E_INVALID_DOWNLOADID, "got %#lx\n", hr); + ok(hr == DMUS_E_INVALID_DOWNLOADID, "got %#lx\n", hr); /* AllocateBuffer use the exact requested size */ hr = IDirectMusicPortDownload_AllocateBuffer(port, 0, NULL); @@ -1093,7 +1093,7 @@ static void test_port_download(void) /* GetBuffer looks up allocated ids to find downloaded buffers */ hr = IDirectMusicPortDownload_GetBuffer(port, 2, &download); - todo_wine ok(hr == DMUS_E_NOT_DOWNLOADED_TO_PORT, "got %#lx\n", hr); + ok(hr == DMUS_E_NOT_DOWNLOADED_TO_PORT, "got %#lx\n", hr); hr = IDirectMusicPortDownload_GetAppend(port, NULL); todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); @@ -1119,7 +1119,7 @@ static void test_port_download(void) wave_download->info.dwDLType = 0; wave_download->info.dwNumOffsetTableEntries = 0; hr = IDirectMusicPortDownload_GetBuffer(port, 2, &tmp_download); - todo_wine ok(hr == DMUS_E_NOT_DOWNLOADED_TO_PORT, "got %#lx\n", hr); + ok(hr == DMUS_E_NOT_DOWNLOADED_TO_PORT, "got %#lx\n", hr); hr = IDirectMusicPortDownload_Download(port, NULL); ok(hr == E_POINTER, "got %#lx\n", hr); @@ -1150,8 +1150,8 @@ static void test_port_download(void) tmp_download = invalid_ptr; hr = IDirectMusicPortDownload_GetBuffer(port, 2, &tmp_download); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(tmp_download == download, "got %p\n", tmp_download); - if (tmp_download != invalid_ptr) IDirectMusicDownload_Release(tmp_download); + ok(tmp_download == download, "got %p\n", tmp_download); + IDirectMusicDownload_Release(tmp_download); hr = IDirectMusicPortDownload_Unload(port, NULL); ok(hr == E_POINTER, "got %#lx\n", hr); @@ -1159,7 +1159,7 @@ static void test_port_download(void) ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPortDownload_GetBuffer(port, 2, &tmp_download); - todo_wine ok(hr == DMUS_E_NOT_DOWNLOADED_TO_PORT, "got %#lx\n", hr); + ok(hr == DMUS_E_NOT_DOWNLOADED_TO_PORT, "got %#lx\n", hr); hr = IDirectMusicPortDownload_Unload(port, download); ok(hr == S_OK, "got %#lx\n", hr); From 4c9c1e89dbf0e4451c8eaa50ebfb8100a0c110b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 27 Aug 2023 16:30:15 +0200 Subject: [PATCH 0801/1148] dmsynth: Implement IDirectMusicSynthSink_SetDirectSound semi-stub. (cherry picked from commit be2cdd3cb278bd84c95556ed945199c1aa2fe615) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synthsink.c | 13 ++++++++++++- dlls/dmsynth/tests/dmsynth.c | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index 2200abd93b1..a6d6276dddf 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -36,6 +36,7 @@ struct synth_sink IReferenceClock *latency_clock; IReferenceClock *master_clock; IDirectMusicSynth *synth; /* No reference hold! */ + IDirectSound *dsound; BOOL active; }; @@ -183,7 +184,17 @@ static HRESULT WINAPI synth_sink_SetDirectSound(IDirectMusicSynthSink *iface, { struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); - FIXME("(%p)->(%p, %p): stub\n", This, dsound, dsound_buffer); + TRACE("(%p)->(%p, %p)\n", This, dsound, dsound_buffer); + + if (dsound_buffer) FIXME("Ignoring IDirectSoundBuffer parameter.\n"); + if (This->active) return DMUS_E_SYNTHACTIVE; + + if (This->dsound) IDirectSound_Release(This->dsound); + This->dsound = NULL; + if (!dsound) return S_OK; + + if (!This->synth) return DMUS_E_SYNTHNOTCONFIGURED; + if ((This->dsound = dsound)) IDirectSound_AddRef(This->dsound); return S_OK; } diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 2fd62294df8..d74b20c9cd4 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1234,7 +1234,7 @@ static void test_IDirectMusicSynthSink(void) hr = IDirectMusicSynthSink_SetDirectSound(sink, NULL, NULL); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicSynthSink_SetDirectSound(sink, dsound, NULL); - todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); /* Activate requires a synth, dsound and a clock */ ref = get_refcount(synth); From 9ceeeced7c871eef9e0f8c5e48cf622b058b4c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 27 Aug 2023 16:25:14 +0200 Subject: [PATCH 0802/1148] dmsynth: Avoid leaking master clock references. (cherry picked from commit 48536877fe4501d5dba78ffb2498ee4da2a5f17d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synthsink.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index a6d6276dddf..ed1ef6a0027 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -127,6 +127,7 @@ static HRESULT WINAPI synth_sink_SetMasterClock(IDirectMusicSynthSink *iface, if (This->active) return E_FAIL; + if (This->master_clock) IReferenceClock_Release(This->master_clock); IReferenceClock_AddRef(clock); This->master_clock = clock; From 3935c9cdb6d0884994fa2ec30aaa05b0e478b843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 27 Aug 2023 16:25:25 +0200 Subject: [PATCH 0803/1148] dmsynth: Allow changing master clock while active. (cherry picked from commit 1b3cc0ce49080b0a9a5b920b6210ce006ec46300) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synthsink.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index ed1ef6a0027..1e6ffa5b51f 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -124,8 +124,6 @@ static HRESULT WINAPI synth_sink_SetMasterClock(IDirectMusicSynthSink *iface, if (!clock) return E_POINTER; - if (This->active) - return E_FAIL; if (This->master_clock) IReferenceClock_Release(This->master_clock); IReferenceClock_AddRef(clock); From 28866ef5d32ec73a0a4cb10453ce8a6105b19cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 27 Aug 2023 16:32:53 +0200 Subject: [PATCH 0804/1148] dmsynth: Implement IDirectMusicSynthSink_Activate semi-stub. (cherry picked from commit 10da838a785b81d6a09aa6034aceb53f2d0b4798) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synthsink.c | 27 +++++++++++++++++++++++++-- dlls/dmsynth/tests/dmsynth.c | 14 +++++++------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index 1e6ffa5b51f..b525a81d876 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -37,7 +37,9 @@ struct synth_sink IReferenceClock *master_clock; IDirectMusicSynth *synth; /* No reference hold! */ IDirectSound *dsound; + BOOL active; + REFERENCE_TIME activate_time; }; static inline struct synth_sink *impl_from_IDirectMusicSynthSink(IDirectMusicSynthSink *iface) @@ -45,6 +47,27 @@ static inline struct synth_sink *impl_from_IDirectMusicSynthSink(IDirectMusicSyn return CONTAINING_RECORD(iface, struct synth_sink, IDirectMusicSynthSink_iface); } +static HRESULT synth_sink_activate(struct synth_sink *This) +{ + HRESULT hr; + + if (!This->synth) return DMUS_E_SYNTHNOTCONFIGURED; + if (!This->dsound) return DMUS_E_DSOUND_NOT_SET; + if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK; + if (This->active) return DMUS_E_SYNTHACTIVE; + + if (FAILED(hr = IReferenceClock_GetTime(This->master_clock, &This->activate_time))) return hr; + + This->active = TRUE; + return S_OK; +} + +static HRESULT synth_sink_deactivate(struct synth_sink *This) +{ + This->active = FALSE; + return S_OK; +} + static HRESULT WINAPI synth_sink_QueryInterface(IDirectMusicSynthSink *iface, REFIID riid, void **ret_iface) { @@ -153,9 +176,9 @@ static HRESULT WINAPI synth_sink_Activate(IDirectMusicSynthSink *iface, { struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); - FIXME("(%p)->(%d): stub\n", This, enable); + FIXME("(%p)->(%d): semi-stub\n", This, enable); - return S_OK; + return enable ? synth_sink_activate(This) : synth_sink_deactivate(This); } static HRESULT WINAPI synth_sink_SampleToRefTime(IDirectMusicSynthSink *iface, diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index d74b20c9cd4..ffc79f2c24b 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1192,7 +1192,7 @@ static void test_IDirectMusicSynthSink(void) /* sink is not configured */ hr = IDirectMusicSynthSink_Activate(sink, TRUE); - todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); hr = IDirectMusicSynthSink_Activate(sink, FALSE); ok(hr == S_OK, "got %#lx\n", hr); @@ -1247,11 +1247,11 @@ static void test_IDirectMusicSynthSink(void) ok(hr == S_OK, "got %#lx\n", hr); ok(size == 352800, "got %lu\n", size); hr = IDirectMusicSynthSink_Activate(sink, TRUE); - todo_wine ok(hr == DMUS_E_DSOUND_NOT_SET, "got %#lx\n", hr); + ok(hr == DMUS_E_DSOUND_NOT_SET, "got %#lx\n", hr); hr = IDirectMusicSynthSink_SetDirectSound(sink, dsound, NULL); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicSynthSink_Activate(sink, TRUE); - todo_wine ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); + ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); hr = IDirectMusicSynthSink_SetMasterClock(sink, clock); ok(hr == S_OK, "got %#lx\n", hr); hr = IReferenceClock_GetTime(latency_clock, &time); @@ -1259,7 +1259,7 @@ static void test_IDirectMusicSynthSink(void) hr = IDirectMusicSynthSink_Activate(sink, TRUE); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicSynthSink_Activate(sink, TRUE); - todo_wine ok(hr == DMUS_E_SYNTHACTIVE, "got %#lx\n", hr); + ok(hr == DMUS_E_SYNTHACTIVE, "got %#lx\n", hr); ref = get_refcount(synth); ok(ref == 1, "got %#lx\n", ref); @@ -1297,13 +1297,13 @@ static void test_IDirectMusicSynthSink(void) ref = get_refcount(synth); ok(ref == 1, "got %#lx\n", ref); hr = IDirectMusicSynthSink_Activate(sink, TRUE); - todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); /* changing dsound while active fails */ hr = IDirectMusicSynthSink_SetDirectSound(sink, dsound, NULL); - todo_wine ok(hr == DMUS_E_SYNTHACTIVE, "got %#lx\n", hr); + ok(hr == DMUS_E_SYNTHACTIVE, "got %#lx\n", hr); hr = IDirectMusicSynthSink_SetDirectSound(sink, NULL, NULL); - todo_wine ok(hr == DMUS_E_SYNTHACTIVE, "got %#lx\n", hr); + ok(hr == DMUS_E_SYNTHACTIVE, "got %#lx\n", hr); hr = IDirectMusicSynthSink_Activate(sink, FALSE); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicSynthSink_SetDirectSound(sink, NULL, NULL); From 73b20f8a70b4ec79d4c0e2f7c11a2f3875e5cd34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 13:20:07 +0200 Subject: [PATCH 0805/1148] dmsynth: Implement SampleToRefTime and RefTimeToSample. (cherry picked from commit 3610b54bd6df4f653c76b8487458df1db74ccab5) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synthsink.c | 36 ++++++++++++++++++++++++++++++++++-- dlls/dmsynth/tests/dmsynth.c | 14 +++++++------- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index b525a81d876..9da462b8074 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -47,6 +47,25 @@ static inline struct synth_sink *impl_from_IDirectMusicSynthSink(IDirectMusicSyn return CONTAINING_RECORD(iface, struct synth_sink, IDirectMusicSynthSink_iface); } +static void synth_sink_get_format(struct synth_sink *This, WAVEFORMATEX *format) +{ + DWORD size = sizeof(*format); + HRESULT hr; + + format->wFormatTag = WAVE_FORMAT_PCM; + format->nChannels = 2; + format->wBitsPerSample = 16; + format->nSamplesPerSec = 22050; + format->nBlockAlign = format->nChannels * format->wBitsPerSample / 8; + format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign; + + if (This->synth) + { + if (FAILED(hr = IDirectMusicSynth_GetFormat(This->synth, format, &size))) + WARN("Failed to get synth buffer format, hr %#lx\n", hr); + } +} + static HRESULT synth_sink_activate(struct synth_sink *This) { HRESULT hr; @@ -185,8 +204,14 @@ static HRESULT WINAPI synth_sink_SampleToRefTime(IDirectMusicSynthSink *iface, LONGLONG sample_time, REFERENCE_TIME *ref_time) { struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); + WAVEFORMATEX format; + + TRACE("(%p)->(%I64d, %p)\n", This, sample_time, ref_time); + + if (!ref_time) return E_POINTER; - FIXME("(%p)->(0x%s, %p): stub\n", This, wine_dbgstr_longlong(sample_time), ref_time); + synth_sink_get_format(This, &format); + *ref_time = This->activate_time + ((sample_time * 10000) / format.nSamplesPerSec) * 1000; return S_OK; } @@ -195,8 +220,15 @@ static HRESULT WINAPI synth_sink_RefTimeToSample(IDirectMusicSynthSink *iface, REFERENCE_TIME ref_time, LONGLONG *sample_time) { struct synth_sink *This = impl_from_IDirectMusicSynthSink(iface); + WAVEFORMATEX format; + + TRACE("(%p)->(%I64d, %p)\n", This, ref_time, sample_time); + + if (!sample_time) return E_POINTER; - FIXME("(%p)->(0x%s, %p): stub\n", This, wine_dbgstr_longlong(ref_time), sample_time); + synth_sink_get_format(This, &format); + ref_time -= This->activate_time; + *sample_time = ((ref_time / 1000) * format.nSamplesPerSec) / 10000; return S_OK; } diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index ffc79f2c24b..97029c98294 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1197,18 +1197,18 @@ static void test_IDirectMusicSynthSink(void) ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicSynthSink_SampleToRefTime(sink, 0, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); time = 0xdeadbeef; hr = IDirectMusicSynthSink_SampleToRefTime(sink, 10, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time == 4000, "got %I64d\n", time); + ok(time == 4000, "got %I64d\n", time); hr = IDirectMusicSynthSink_RefTimeToSample(sink, 0, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); sample = 0xdeadbeef; hr = IDirectMusicSynthSink_RefTimeToSample(sink, 4000, &sample); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(sample == 8, "got %I64d\n", sample); + ok(sample == 8, "got %I64d\n", sample); hr = IDirectMusicSynthSink_GetDesiredBufferSize(sink, &size); ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); @@ -1273,12 +1273,12 @@ static void test_IDirectMusicSynthSink(void) sample = 0xdeadbeef; hr = IDirectMusicSynthSink_RefTimeToSample(sink, time, &sample); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(sample <= 3000, "got %I64d\n", sample); + ok(sample <= 3000, "got %I64d\n", sample); tmp_time = time + 1; hr = IDirectMusicSynthSink_SampleToRefTime(sink, sample, &tmp_time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(tmp_time <= time, "got %I64d\n", tmp_time - time); - ok(time - tmp_time <= 5000, "got %I64d\n", time - tmp_time); + ok(tmp_time <= time, "got %I64d\n", tmp_time - time); + ok(time - tmp_time <= 5000, "got %I64d\n", tmp_time); /* latency clock now works fine */ tmp_time = time; From fb5ff13178b0572172521ba3aa4a2efcf93257de Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Wed, 6 Sep 2023 11:24:29 +0200 Subject: [PATCH 0806/1148] dmsynth/tests: Add the trailing linefeed to a win_skip() message. The missing linefeed was causing the test summary line to be garbled. (cherry picked from commit a6d6f914b3f86a3fe6fd70c65e10eab5a084af83) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/tests/dmsynth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 97029c98294..a27a101bd07 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1168,7 +1168,7 @@ static void test_IDirectMusicSynthSink(void) ok(hr == S_OK || broken(hr == DSERR_NODRIVER), "got %#lx\n", hr); if (broken(hr == DSERR_NODRIVER)) { - win_skip("Failed to create IDirectSound, skipping tests"); + win_skip("Failed to create IDirectSound, skipping tests\n"); return; } From 1001164b07ba8f8ffb166fe351a303ffbffb4400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 11:36:10 +0200 Subject: [PATCH 0807/1148] dmsynth: Move constructor parameter checks to class factory. (cherry picked from commit ec2a7a325176c6c8d3278bcaab9bae8418eed880) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/dmsynth_main.c | 28 +++++++++++++++++----------- dlls/dmsynth/dmsynth_private.h | 4 ++-- dlls/dmsynth/synth.c | 16 +++++++--------- dlls/dmsynth/synthsink.c | 14 ++++++-------- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/dlls/dmsynth/dmsynth_main.c b/dlls/dmsynth/dmsynth_main.c index edeaab8474e..84caeceb472 100644 --- a/dlls/dmsynth/dmsynth_main.c +++ b/dlls/dmsynth/dmsynth_main.c @@ -27,7 +27,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); typedef struct { IClassFactory IClassFactory_iface; - HRESULT (*fnCreateInstance)(REFIID riid, void **ppv); + HRESULT (*create_instance)(IUnknown **ret_iface); } IClassFactoryImpl; /****************************************************************** @@ -68,17 +68,24 @@ static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) return 1; /* non-heap based object */ } -static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter, - REFIID riid, void **ppv) +static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *unk_outer, + REFIID riid, void **ret_iface) { - IClassFactoryImpl *This = impl_from_IClassFactory(iface); + IClassFactoryImpl *This = impl_from_IClassFactory(iface); + IUnknown *object; + HRESULT hr; - TRACE ("(%p, %s, %p)\n", pUnkOuter, debugstr_dmguid(riid), ppv); + TRACE("(%p, %s, %p)\n", unk_outer, debugstr_dmguid(riid), ret_iface); - if (pUnkOuter) - return CLASS_E_NOAGGREGATION; + *ret_iface = NULL; + if (unk_outer) return CLASS_E_NOAGGREGATION; + if (SUCCEEDED(hr = This->create_instance(&object))) + { + hr = IUnknown_QueryInterface(object, riid, ret_iface); + IUnknown_Release(object); + } - return This->fnCreateInstance(riid, ppv); + return hr; } static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock) @@ -95,9 +102,8 @@ static const IClassFactoryVtbl classfactory_vtbl = { ClassFactory_LockServer }; -static IClassFactoryImpl Synth_CF = {{&classfactory_vtbl}, DMUSIC_CreateDirectMusicSynthImpl}; -static IClassFactoryImpl SynthSink_CF = {{&classfactory_vtbl}, - DMUSIC_CreateDirectMusicSynthSinkImpl}; +static IClassFactoryImpl Synth_CF = {{&classfactory_vtbl}, synth_create}; +static IClassFactoryImpl SynthSink_CF = {{&classfactory_vtbl}, synth_sink_create}; /****************************************************************** diff --git a/dlls/dmsynth/dmsynth_private.h b/dlls/dmsynth/dmsynth_private.h index 35c87775cf7..529b46ca978 100644 --- a/dlls/dmsynth/dmsynth_private.h +++ b/dlls/dmsynth/dmsynth_private.h @@ -43,8 +43,8 @@ /***************************************************************************** * ClassFactory */ -extern HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj); -extern HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ppobj); +extern HRESULT synth_create(IUnknown **ret_iface); +extern HRESULT synth_sink_create(IUnknown **ret_iface); /***************************************************************************** * Misc. diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index fecbe21c2e3..91826c076e4 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -740,19 +740,18 @@ static const IKsControlVtbl synth_control_vtbl = synth_control_KsEvent, }; -HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj) +HRESULT synth_create(IUnknown **ret_iface) { struct synth *obj; - HRESULT hr; - TRACE("(%s, %p)\n", debugstr_guid(riid), ppobj); + TRACE("(%p)\n", ret_iface); - *ppobj = NULL; + *ret_iface = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicSynth8_iface.lpVtbl = &synth_vtbl; obj->IKsControl_iface.lpVtbl = &synth_control_vtbl; obj->ref = 1; - /* fill in caps */ + obj->caps.dwSize = sizeof(DMUS_PORTCAPS); obj->caps.dwFlags = DMUS_PC_DLS | DMUS_PC_SOFTWARESYNTH | DMUS_PC_DIRECTSOUND | DMUS_PC_DLS2 | DMUS_PC_AUDIOPATH | DMUS_PC_WAVE; obj->caps.guidPort = CLSID_DirectMusicSynth; @@ -765,8 +764,7 @@ HRESULT DMUSIC_CreateDirectMusicSynthImpl(REFIID riid, void **ppobj) obj->caps.dwEffectFlags = DMUS_EFFECT_REVERB; lstrcpyW(obj->caps.wszDescription, L"Microsoft Synthesizer"); - hr = IDirectMusicSynth8_QueryInterface(&obj->IDirectMusicSynth8_iface, riid, ppobj); - IDirectMusicSynth8_Release(&obj->IDirectMusicSynth8_iface); - - return hr; + TRACE("Created DirectMusicSynth %p\n", obj); + *ret_iface = (IUnknown *)&obj->IDirectMusicSynth8_iface; + return S_OK; } diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index 9da462b8074..2d17a3e56bf 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -373,29 +373,27 @@ static const IKsControlVtbl synth_sink_control = synth_sink_control_KsEvent, }; -HRESULT DMUSIC_CreateDirectMusicSynthSinkImpl(REFIID riid, void **ret_iface) +HRESULT synth_sink_create(IUnknown **ret_iface) { struct synth_sink *obj; HRESULT hr; - TRACE("(%s, %p)\n", debugstr_guid(riid), ret_iface); + TRACE("(%p)\n", ret_iface); *ret_iface = NULL; - if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicSynthSink_iface.lpVtbl = &synth_sink_vtbl; obj->IKsControl_iface.lpVtbl = &synth_sink_control; obj->ref = 1; - hr = CoCreateInstance(&CLSID_SystemClock, NULL, CLSCTX_INPROC_SERVER, &IID_IReferenceClock, (LPVOID*)&obj->latency_clock); + hr = CoCreateInstance(&CLSID_SystemClock, NULL, CLSCTX_INPROC_SERVER, &IID_IReferenceClock, (void **)&obj->latency_clock); if (FAILED(hr)) { free(obj); return hr; } - hr = IDirectMusicSynthSink_QueryInterface(&obj->IDirectMusicSynthSink_iface, riid, ret_iface); - IDirectMusicSynthSink_Release(&obj->IDirectMusicSynthSink_iface); - - return hr; + TRACE("Created DirectMusicSynthSink %p\n", obj); + *ret_iface = (IUnknown *)&obj->IDirectMusicSynthSink_iface; + return S_OK; } From 13d6d0773faabb23e99c23d2078e5e7301a42396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 13:59:42 +0200 Subject: [PATCH 0808/1148] include: Use IReferenceClock interface from strmif.idl in dmusicc.h. (cherry picked from commit 23b61b0dc876f66ab3c9b9719b7ec425412b043d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/tests/dmsynth.c | 6 ++--- dlls/dmusic/clock.c | 20 ++++++--------- dlls/dmusic/dmusic.c | 18 ++++++-------- dlls/dmusic/tests/dmusic.c | 6 ++--- include/dmusicc.h | 48 ++++-------------------------------- 5 files changed, 27 insertions(+), 71 deletions(-) diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index a27a101bd07..53e04152bee 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -760,20 +760,20 @@ static HRESULT WINAPI test_sink_latency_clock_GetTime(IReferenceClock *iface, RE } static HRESULT WINAPI test_sink_latency_clock_AdviseTime(IReferenceClock *iface, - REFERENCE_TIME base, REFERENCE_TIME offset, HANDLE event, DWORD *cookie) + REFERENCE_TIME base, REFERENCE_TIME offset, HEVENT event, DWORD_PTR *cookie) { ok(0, "unexpected %s\n", __func__); return E_NOTIMPL; } static HRESULT WINAPI test_sink_latency_clock_AdvisePeriodic(IReferenceClock *iface, - REFERENCE_TIME start, REFERENCE_TIME period, HANDLE semaphore, DWORD *cookie) + REFERENCE_TIME start, REFERENCE_TIME period, HSEMAPHORE semaphore, DWORD_PTR *cookie) { ok(0, "unexpected %s\n", __func__); return E_NOTIMPL; } -static HRESULT WINAPI test_sink_latency_clock_Unadvise(IReferenceClock *iface, DWORD cookie) +static HRESULT WINAPI test_sink_latency_clock_Unadvise(IReferenceClock *iface, DWORD_PTR cookie) { ok(0, "unexpected %s\n", __func__); return E_NOTIMPL; diff --git a/dlls/dmusic/clock.c b/dlls/dmusic/clock.c index 19441d3d56f..7b574dd4432 100644 --- a/dlls/dmusic/clock.c +++ b/dlls/dmusic/clock.c @@ -80,30 +80,26 @@ static HRESULT WINAPI IReferenceClockImpl_GetTime(IReferenceClock *iface, REFERE return S_OK; } -static HRESULT WINAPI IReferenceClockImpl_AdviseTime(IReferenceClock *iface, REFERENCE_TIME baseTime, REFERENCE_TIME streamTime, HANDLE hEvent, DWORD* pdwAdviseCookie) +static HRESULT WINAPI IReferenceClockImpl_AdviseTime(IReferenceClock *iface, REFERENCE_TIME base, + REFERENCE_TIME offset, HEVENT event, DWORD_PTR *cookie) { IReferenceClockImpl *This = impl_from_IReferenceClock(iface); - - FIXME("(%p)->(0x%s, 0x%s, %p, %p): stub\n", This, wine_dbgstr_longlong(baseTime), wine_dbgstr_longlong(streamTime), hEvent, pdwAdviseCookie); - + FIXME("(%p)->(%I64d, %I64d, %#Ix, %p): stub\n", This, base, offset, event, cookie); return S_OK; } -static HRESULT WINAPI IReferenceClockImpl_AdvisePeriodic(IReferenceClock *iface, REFERENCE_TIME startTime, REFERENCE_TIME periodTime, HANDLE hSemaphore, DWORD* pdwAdviseCookie) +static HRESULT WINAPI IReferenceClockImpl_AdvisePeriodic(IReferenceClock *iface, REFERENCE_TIME start, + REFERENCE_TIME period, HSEMAPHORE semaphore, DWORD_PTR *cookie) { IReferenceClockImpl *This = impl_from_IReferenceClock(iface); - - FIXME("(%p)->(0x%s, 0x%s, %p, %p): stub\n", This, wine_dbgstr_longlong(startTime), wine_dbgstr_longlong(periodTime), hSemaphore, pdwAdviseCookie); - + FIXME("(%p)->(%I64d, %I64d, %#Ix, %p): stub\n", This, start, period, semaphore, cookie); return S_OK; } -static HRESULT WINAPI IReferenceClockImpl_Unadvise(IReferenceClock *iface, DWORD dwAdviseCookie) +static HRESULT WINAPI IReferenceClockImpl_Unadvise(IReferenceClock *iface, DWORD_PTR cookie) { IReferenceClockImpl *This = impl_from_IReferenceClock(iface); - - FIXME("(%p, %ld): stub\n", This, dwAdviseCookie); - + FIXME("(%p, %#Ix): stub\n", This, cookie); return S_OK; } diff --git a/dlls/dmusic/dmusic.c b/dlls/dmusic/dmusic.c index d284b32fdd7..970fb28ebd2 100644 --- a/dlls/dmusic/dmusic.c +++ b/dlls/dmusic/dmusic.c @@ -96,25 +96,23 @@ static HRESULT WINAPI master_IReferenceClock_GetTime(IReferenceClock *iface, return hr; } -static HRESULT WINAPI master_IReferenceClock_AdviseTime(IReferenceClock *iface, - REFERENCE_TIME base, REFERENCE_TIME offset, HANDLE event, DWORD *cookie) +static HRESULT WINAPI master_IReferenceClock_AdviseTime(IReferenceClock *iface, REFERENCE_TIME base, + REFERENCE_TIME offset, HEVENT event, DWORD_PTR *cookie) { - TRACE("(%p, %s, %s, %p, %p): method not implemented\n", iface, wine_dbgstr_longlong(base), - wine_dbgstr_longlong(offset), event, cookie); + FIXME("(%p, %I64d, %I64d, %#Ix, %p): stub\n", iface, base, offset, event, cookie); return E_NOTIMPL; } -static HRESULT WINAPI master_IReferenceClock_AdvisePeriodic(IReferenceClock *iface, - REFERENCE_TIME start, REFERENCE_TIME period, HANDLE semaphore, DWORD *cookie) +static HRESULT WINAPI master_IReferenceClock_AdvisePeriodic(IReferenceClock *iface, REFERENCE_TIME start, + REFERENCE_TIME period, HSEMAPHORE semaphore, DWORD_PTR *cookie) { - TRACE("(%p, %s, %s, %p, %p): method not implemented\n", iface, wine_dbgstr_longlong(start), - wine_dbgstr_longlong(period), semaphore, cookie); + FIXME("(%p, %I64d, %I64d, %#Ix, %p): stub\n", iface, start, period, semaphore, cookie); return E_NOTIMPL; } -static HRESULT WINAPI master_IReferenceClock_Unadvise(IReferenceClock *iface, DWORD cookie) +static HRESULT WINAPI master_IReferenceClock_Unadvise(IReferenceClock *iface, DWORD_PTR cookie) { - TRACE("(%p, %#lx): method not implemented\n", iface, cookie); + FIXME("(%p, %#Ix): stub\n", iface, cookie); return E_NOTIMPL; } diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index a8152822613..1561c51df76 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -862,7 +862,7 @@ static void test_master_clock(void) LARGE_INTEGER counter, freq; DMUS_CLOCKINFO clock_info; IDirectMusic *dmusic; - DWORD cookie; + DWORD_PTR cookie; HRESULT hr; ULONG ref; GUID guid; @@ -912,10 +912,10 @@ static void test_master_clock(void) ok(time2 - time1 > 80 * 10000, "Expected about %s, but got %s.\n", wine_dbgstr_longlong(time1 + 100 * 10000), wine_dbgstr_longlong(time2)); - hr = IReferenceClock_AdviseTime(clock, 0, 0, NULL, &cookie); + hr = IReferenceClock_AdviseTime(clock, 0, 0, 0, &cookie); ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr); - hr = IReferenceClock_AdvisePeriodic(clock, 0, 0, NULL, &cookie); + hr = IReferenceClock_AdvisePeriodic(clock, 0, 0, 0, &cookie); ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr); hr = IReferenceClock_Unadvise(clock, 0); diff --git a/include/dmusicc.h b/include/dmusicc.h index 94ff184dc9f..351f2733ee2 100644 --- a/include/dmusicc.h +++ b/include/dmusicc.h @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -108,8 +109,6 @@ typedef struct IDirectMusicPort *LPDIRECTMUSICPORT; typedef struct IDirectMusicPort IDirectMusicPort8, *LPDIRECTMUSICPORT8; typedef struct IDirectMusicThru *LPDIRECTMUSICTHRU; typedef struct IDirectMusicThru IDirectMusicThru8, *LPDIRECTMUSICTHRU8; -typedef struct IReferenceClock *LPREFERENCECLOCK; - /***************************************************************************** * Typedef definitions @@ -381,7 +380,7 @@ DECLARE_INTERFACE_(IDirectMusic,IUnknown) STDMETHOD(CreateMusicBuffer)(THIS_ LPDMUS_BUFFERDESC pBufferDesc, LPDIRECTMUSICBUFFER *ppBuffer, LPUNKNOWN pUnkOuter) PURE; STDMETHOD(CreatePort)(THIS_ REFCLSID rclsidPort, LPDMUS_PORTPARAMS pPortParams, LPDIRECTMUSICPORT *ppPort, LPUNKNOWN pUnkOuter) PURE; STDMETHOD(EnumMasterClock)(THIS_ DWORD dwIndex, LPDMUS_CLOCKINFO lpClockInfo) PURE; - STDMETHOD(GetMasterClock)(THIS_ LPGUID pguidClock, struct IReferenceClock **ppReferenceClock) PURE; + STDMETHOD(GetMasterClock)(THIS_ LPGUID pguidClock, IReferenceClock **ppReferenceClock) PURE; STDMETHOD(SetMasterClock)(THIS_ REFGUID rguidClock) PURE; STDMETHOD(Activate)(THIS_ BOOL fEnable) PURE; STDMETHOD(GetDefaultPort)(THIS_ LPGUID pguidPort) PURE; @@ -422,13 +421,13 @@ DECLARE_INTERFACE_(IDirectMusic8,IDirectMusic) STDMETHOD(CreateMusicBuffer)(THIS_ LPDMUS_BUFFERDESC pBufferDesc, LPDIRECTMUSICBUFFER *ppBuffer, LPUNKNOWN pUnkOuter) PURE; STDMETHOD(CreatePort)(THIS_ REFCLSID rclsidPort, LPDMUS_PORTPARAMS pPortParams, LPDIRECTMUSICPORT *ppPort, LPUNKNOWN pUnkOuter) PURE; STDMETHOD(EnumMasterClock)(THIS_ DWORD dwIndex, LPDMUS_CLOCKINFO lpClockInfo) PURE; - STDMETHOD(GetMasterClock)(THIS_ LPGUID pguidClock, struct IReferenceClock **ppReferenceClock) PURE; + STDMETHOD(GetMasterClock)(THIS_ LPGUID pguidClock, IReferenceClock **ppReferenceClock) PURE; STDMETHOD(SetMasterClock)(THIS_ REFGUID rguidClock) PURE; STDMETHOD(Activate)(THIS_ BOOL fEnable) PURE; STDMETHOD(GetDefaultPort)(THIS_ LPGUID pguidPort) PURE; STDMETHOD(SetDirectSound)(THIS_ LPDIRECTSOUND pDirectSound, HWND hWnd) PURE; /*** IDirectMusic8 methods ***/ - STDMETHOD(SetExternalMasterClock)(THIS_ struct IReferenceClock *pClock) PURE; + STDMETHOD(SetExternalMasterClock)(THIS_ IReferenceClock *pClock) PURE; }; #undef INTERFACE @@ -655,7 +654,7 @@ DECLARE_INTERFACE_(IDirectMusicPort,IUnknown) STDMETHOD(Read)(THIS_ LPDIRECTMUSICBUFFER pBuffer) PURE; STDMETHOD(DownloadInstrument)(THIS_ IDirectMusicInstrument *pInstrument, IDirectMusicDownloadedInstrument **ppDownloadedInstrument, DMUS_NOTERANGE *pNoteRanges, DWORD dwNumNoteRanges) PURE; STDMETHOD(UnloadInstrument)(THIS_ IDirectMusicDownloadedInstrument *pDownloadedInstrument) PURE; - STDMETHOD(GetLatencyClock)(THIS_ struct IReferenceClock **ppClock) PURE; + STDMETHOD(GetLatencyClock)(THIS_ IReferenceClock **ppClock) PURE; STDMETHOD(GetRunningStats)(THIS_ LPDMUS_SYNTHSTATS pStats) PURE; STDMETHOD(Compact)(THIS) PURE; STDMETHOD(GetCaps)(THIS_ LPDMUS_PORTCAPS pPortCaps) PURE; @@ -720,43 +719,6 @@ DECLARE_INTERFACE_(IDirectMusicThru,IUnknown) #define IDirectMusicThru_ThruChannel(p,a,b,c,d,e) (p)->lpVtbl->ThruChannel(p,a,b,c,d,e) #endif - -#ifndef __IReferenceClock_INTERFACE_DEFINED__ -#define __IReferenceClock_INTERFACE_DEFINED__ -DEFINE_GUID(IID_IReferenceClock,0x56a86897,0x0ad4,0x11ce,0xb0,0x3a,0x00,0x20,0xaf,0x0b,0xa7,0x70); - -/***************************************************************************** - * IReferenceClock interface - */ -#define INTERFACE IReferenceClock -DECLARE_INTERFACE_(IReferenceClock,IUnknown) -{ - /*** IUnknown methods ***/ - STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - /*** IReferenceClock methods ***/ - STDMETHOD(GetTime)(THIS_ REFERENCE_TIME *pTime) PURE; - STDMETHOD(AdviseTime)(THIS_ REFERENCE_TIME baseTime, REFERENCE_TIME streamTime, HANDLE hEvent, DWORD *pdwAdviseCookie) PURE; - STDMETHOD(AdvisePeriodic)(THIS_ REFERENCE_TIME startTime, REFERENCE_TIME periodTime, HANDLE hSemaphore, DWORD *pdwAdviseCookie) PURE; - STDMETHOD(Unadvise)(THIS_ DWORD dwAdviseCookie) PURE; -}; -#undef INTERFACE - -#if !defined(__cplusplus) || defined(CINTERFACE) -/*** IUnknown methods ***/ -#define IReferenceClock_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IReferenceClock_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IReferenceClock_Release(p) (p)->lpVtbl->Release(p) -/*** IReferenceClock methods ***/ -#define IReferenceClock_GetTime(p,a) (p)->lpVtbl->GetTime(p,a) -#define IReferenceClock_AdviseTime(p,a,b,c,d) (p)->lpVtbl->AdviseTime(p,a,b,c,d) -#define IReferenceClock_AdvisePeriodic(p,a,b,c,d) (p)->lpVtbl->AdvisePeriodic(p,a,b,c,d) -#define IReferenceClock_Unadvise(p,a) (p)->lpVtbl->Unadvise(p,a) -#endif - -#endif /* __IReferenceClock_INTERFACE_DEFINED__ */ - #ifdef __cplusplus } #endif From 9819152651a9b2b252b2e6763953f1fcedd805fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 6 Sep 2023 08:29:37 +0200 Subject: [PATCH 0809/1148] dmsynth: Implement latency IReferenceClock interface on the sink. (cherry picked from commit 2a1b03ccc35148ae09bb40438613c89f26bb6a56) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synthsink.c | 97 +++++++++++++++++++++++++++++++----- dlls/dmsynth/tests/dmsynth.c | 12 ++--- 2 files changed, 90 insertions(+), 19 deletions(-) diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index 2d17a3e56bf..db1fecb8d48 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -31,15 +31,16 @@ struct synth_sink { IDirectMusicSynthSink IDirectMusicSynthSink_iface; IKsControl IKsControl_iface; + IReferenceClock IReferenceClock_iface; LONG ref; - IReferenceClock *latency_clock; IReferenceClock *master_clock; IDirectMusicSynth *synth; /* No reference hold! */ IDirectSound *dsound; BOOL active; REFERENCE_TIME activate_time; + REFERENCE_TIME latency_time; }; static inline struct synth_sink *impl_from_IDirectMusicSynthSink(IDirectMusicSynthSink *iface) @@ -76,6 +77,7 @@ static HRESULT synth_sink_activate(struct synth_sink *This) if (This->active) return DMUS_E_SYNTHACTIVE; if (FAILED(hr = IReferenceClock_GetTime(This->master_clock, &This->activate_time))) return hr; + This->latency_time = This->activate_time; This->active = TRUE; return S_OK; @@ -133,8 +135,6 @@ static ULONG WINAPI synth_sink_Release(IDirectMusicSynthSink *iface) TRACE("(%p): new ref = %lu\n", This, ref); if (!ref) { - if (This->latency_clock) - IReferenceClock_Release(This->latency_clock); if (This->master_clock) IReferenceClock_Release(This->master_clock); free(This); @@ -184,8 +184,8 @@ static HRESULT WINAPI synth_sink_GetLatencyClock(IDirectMusicSynthSink *iface, if (!clock) return E_POINTER; - *clock = This->latency_clock; - IReferenceClock_AddRef(This->latency_clock); + *clock = &This->IReferenceClock_iface; + IReferenceClock_AddRef(*clock); return S_OK; } @@ -373,10 +373,87 @@ static const IKsControlVtbl synth_sink_control = synth_sink_control_KsEvent, }; +static inline struct synth_sink *impl_from_IReferenceClock(IReferenceClock *iface) +{ + return CONTAINING_RECORD(iface, struct synth_sink, IReferenceClock_iface); +} + +static HRESULT WINAPI latency_clock_QueryInterface(IReferenceClock *iface, REFIID iid, void **out) +{ + TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(iid), out); + + if (IsEqualIID(iid, &IID_IUnknown) + || IsEqualIID(iid, &IID_IReferenceClock)) + { + IUnknown_AddRef(iface); + *out = iface; + return S_OK; + } + + FIXME("no interface for %s\n", debugstr_dmguid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI latency_clock_AddRef(IReferenceClock *iface) +{ + struct synth_sink *This = impl_from_IReferenceClock(iface); + return IDirectMusicSynthSink_AddRef(&This->IDirectMusicSynthSink_iface); +} + +static ULONG WINAPI latency_clock_Release(IReferenceClock *iface) +{ + struct synth_sink *This = impl_from_IReferenceClock(iface); + return IDirectMusicSynthSink_Release(&This->IDirectMusicSynthSink_iface); +} + +static HRESULT WINAPI latency_clock_GetTime(IReferenceClock *iface, REFERENCE_TIME *time) +{ + struct synth_sink *This = impl_from_IReferenceClock(iface); + + TRACE("(%p, %p)\n", iface, time); + + if (!time) return E_INVALIDARG; + if (!This->active) return E_FAIL; + *time = This->latency_time; + + return S_OK; +} + +static HRESULT WINAPI latency_clock_AdviseTime(IReferenceClock *iface, REFERENCE_TIME base, + REFERENCE_TIME offset, HEVENT event, DWORD_PTR *cookie) +{ + FIXME("(%p, %I64d, %I64d, %#Ix, %p): stub\n", iface, base, offset, event, cookie); + return E_NOTIMPL; +} + +static HRESULT WINAPI latency_clock_AdvisePeriodic(IReferenceClock *iface, REFERENCE_TIME start, + REFERENCE_TIME period, HSEMAPHORE semaphore, DWORD_PTR *cookie) +{ + FIXME("(%p, %I64d, %I64d, %#Ix, %p): stub\n", iface, start, period, semaphore, cookie); + return E_NOTIMPL; +} + +static HRESULT WINAPI latency_clock_Unadvise(IReferenceClock *iface, DWORD_PTR cookie) +{ + FIXME("(%p, %#Ix): stub\n", iface, cookie); + return E_NOTIMPL; +} + +static const IReferenceClockVtbl latency_clock_vtbl = +{ + latency_clock_QueryInterface, + latency_clock_AddRef, + latency_clock_Release, + latency_clock_GetTime, + latency_clock_AdviseTime, + latency_clock_AdvisePeriodic, + latency_clock_Unadvise, +}; + HRESULT synth_sink_create(IUnknown **ret_iface) { struct synth_sink *obj; - HRESULT hr; TRACE("(%p)\n", ret_iface); @@ -384,15 +461,9 @@ HRESULT synth_sink_create(IUnknown **ret_iface) if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicSynthSink_iface.lpVtbl = &synth_sink_vtbl; obj->IKsControl_iface.lpVtbl = &synth_sink_control; + obj->IReferenceClock_iface.lpVtbl = &latency_clock_vtbl; obj->ref = 1; - hr = CoCreateInstance(&CLSID_SystemClock, NULL, CLSCTX_INPROC_SERVER, &IID_IReferenceClock, (void **)&obj->latency_clock); - if (FAILED(hr)) - { - free(obj); - return hr; - } - TRACE("Created DirectMusicSynthSink %p\n", obj); *ret_iface = (IUnknown *)&obj->IDirectMusicSynthSink_iface; return S_OK; diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 53e04152bee..61bfeda072c 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1222,12 +1222,12 @@ static void test_IDirectMusicSynthSink(void) ok(hr == S_OK, "got %#lx\n", hr); ok(latency_clock != clock, "got same clock\n"); ref = get_refcount(sink); - todo_wine ok(ref == 2, "got %#lx\n", ref); + ok(ref == 2, "got %#lx\n", ref); hr = IReferenceClock_GetTime(latency_clock, NULL); - todo_wine ok(hr == E_INVALIDARG, "got %#lx\n", hr); + ok(hr == E_INVALIDARG, "got %#lx\n", hr); hr = IReferenceClock_GetTime(latency_clock, &time); - todo_wine ok(hr == E_FAIL, "got %#lx\n", hr); + ok(hr == E_FAIL, "got %#lx\n", hr); hr = IDirectMusicSynthSink_Init(sink, NULL); ok(hr == S_OK, "got %#lx\n", hr); @@ -1255,7 +1255,7 @@ static void test_IDirectMusicSynthSink(void) hr = IDirectMusicSynthSink_SetMasterClock(sink, clock); ok(hr == S_OK, "got %#lx\n", hr); hr = IReferenceClock_GetTime(latency_clock, &time); - todo_wine ok(hr == E_FAIL, "got %#lx\n", hr); + ok(hr == E_FAIL, "got %#lx\n", hr); hr = IDirectMusicSynthSink_Activate(sink, TRUE); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicSynthSink_Activate(sink, TRUE); @@ -1283,8 +1283,8 @@ static void test_IDirectMusicSynthSink(void) /* latency clock now works fine */ tmp_time = time; hr = IReferenceClock_GetTime(latency_clock, &tmp_time); - todo_wine_if(hr == S_FALSE) ok(hr == S_OK, "got %#lx\n", hr); - todo_wine_if(tmp_time <= time) ok(tmp_time > time, "got %I64d\n", tmp_time - time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(tmp_time > time, "got %I64d\n", tmp_time - time); ok(tmp_time - time <= 2000000, "got %I64d\n", tmp_time - time); /* setting the clock while active is fine */ From 5ff1845802d6ebbe517f31f14693afedadc124d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 29 Aug 2023 14:24:00 +0200 Subject: [PATCH 0810/1148] dmsynth: Forward IDirectMusicSynth_GetLatencyClock to the sink. (cherry picked from commit 722262b023e1840abd91f0370930bfd6e7b2e025) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 25 ++++--------------------- dlls/dmsynth/tests/dmsynth.c | 6 +++--- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 91826c076e4..967de01e95c 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -41,7 +41,6 @@ struct synth DMUS_PORTPARAMS params; BOOL active; BOOL open; - IReferenceClock *latency_clock; IDirectMusicSynthSink *sink; }; @@ -96,11 +95,7 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) TRACE("(%p): new ref = %lu\n", This, ref); - if (!ref) { - if (This->latency_clock) - IReferenceClock_Release(This->latency_clock); - free(This); - } + if (!ref) free(This); return ref; } @@ -417,14 +412,10 @@ static HRESULT WINAPI synth_GetLatencyClock(IDirectMusicSynth8 *iface, if (!clock) return E_POINTER; - if (!This->sink) return DMUS_E_NOSYNTHSINK; - *clock = This->latency_clock; - IReferenceClock_AddRef(This->latency_clock); - - return S_OK; + return IDirectMusicSynthSink_GetLatencyClock(This->sink, clock); } static HRESULT WINAPI synth_Activate(IDirectMusicSynth8 *iface, BOOL enable) @@ -460,28 +451,20 @@ static HRESULT WINAPI synth_SetSynthSink(IDirectMusicSynth8 *iface, IDirectMusicSynthSink *sink) { struct synth *This = impl_from_IDirectMusicSynth8(iface); - HRESULT hr; TRACE("(%p)->(%p)\n", iface, sink); if (sink == This->sink) return S_OK; - if (!sink || This->sink) { - /* Disconnect the sink */ - if (This->latency_clock) - IReferenceClock_Release(This->latency_clock); - IDirectMusicSynthSink_Release(This->sink); - } + if (!sink || This->sink) IDirectMusicSynthSink_Release(This->sink); This->sink = sink; if (!sink) return S_OK; IDirectMusicSynthSink_AddRef(This->sink); - if (FAILED(hr = IDirectMusicSynthSink_Init(sink, (IDirectMusicSynth *)iface))) - return hr; - return IDirectMusicSynthSink_GetLatencyClock(sink, &This->latency_clock); + return IDirectMusicSynthSink_Init(sink, (IDirectMusicSynth *)iface); } static HRESULT WINAPI synth_Render(IDirectMusicSynth8 *iface, short *buffer, diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 61bfeda072c..bdad58abdcf 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1014,7 +1014,7 @@ static void test_IDirectMusicSynth(void) hr = IDirectMusicSynth_SetSynthSink(synth, sink); ok(hr == S_OK, "got %#lx\n", hr); ref = get_refcount(sink); - todo_wine ok(ref == 2, "got %lu\n", ref); + ok(ref == 2, "got %lu\n", ref); hr = IDirectMusicSynth_Activate(synth, TRUE); todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); @@ -1062,7 +1062,7 @@ static void test_IDirectMusicSynth(void) hr = IDirectMusicSynth_SetSynthSink(synth, sink); ok(hr == S_OK, "got %#lx\n", hr); ref = get_refcount(sink); - todo_wine ok(ref == 2, "got %lu\n", ref); + ok(ref == 2, "got %lu\n", ref); hr = IDirectMusicSynth_Activate(synth, TRUE); todo_wine ok(hr == S_OK, "got %#lx\n", hr); @@ -1145,7 +1145,7 @@ static void test_IDirectMusicSynth(void) IDirectMusicSynth_Release(synth); - if (strcmp(winetest_platform, "wine")) IDirectMusicSynthSink_Release(sink); + IDirectMusicSynthSink_Release(sink); IReferenceClock_Release(clock); IDirectMusic_Release(music); } From b2db3743aa082e3f1b395d7a9a7fb48335e8f1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 29 Aug 2023 14:28:50 +0200 Subject: [PATCH 0811/1148] dmsynth: Return S_FALSE if IDirectMusicSynth_Activate is no-op. (cherry picked from commit f6deca5932d5bc7d0449f508a99f0b413b697944) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 9 ++------- dlls/dmsynth/tests/dmsynth.c | 4 ++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 967de01e95c..a63bfe24f7b 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -425,16 +425,11 @@ static HRESULT WINAPI synth_Activate(IDirectMusicSynth8 *iface, BOOL enable) TRACE("(%p)->(%d)\n", This, enable); + if (enable == This->active) return S_FALSE; + if (!This->sink) return DMUS_E_NOSYNTHSINK; - if (enable == This->active) { - if (enable) - return DMUS_E_SYNTHACTIVE; - else - return S_FALSE; - } - if ((hr = IDirectMusicSynthSink_Activate(This->sink, enable)) != S_OK) { if (hr == DMUS_E_SYNTHACTIVE || hr == S_FALSE) WARN("Synth and sink active state out of sync. Fixing.\n"); diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index bdad58abdcf..5d60edd3b01 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1007,7 +1007,7 @@ static void test_IDirectMusicSynth(void) hr = IDirectMusicSynth_Activate(synth, TRUE); ok(hr == DMUS_E_NOSYNTHSINK, "got %#lx\n", hr); hr = IDirectMusicSynth_Activate(synth, FALSE); - todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + ok(hr == S_FALSE, "got %#lx\n", hr); hr = IDirectMusicSynth_SetSynthSink(synth, NULL); ok(hr == S_OK, "got %#lx\n", hr); @@ -1034,7 +1034,7 @@ static void test_IDirectMusicSynth(void) hr = IDirectMusicSynth_Activate(synth, TRUE); todo_wine ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicSynth_Activate(synth, TRUE); - todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + ok(hr == S_FALSE, "got %#lx\n", hr); /* Close is fine while active */ hr = IDirectMusicSynth_Close(synth); From 3cf92b0d0e33e5c0ba7bc6f90a012acf0f6da884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 13:42:41 +0200 Subject: [PATCH 0812/1148] dmsynth: Return DMUS_E_SYNTHNOTCONFIGURED when sink fails to activate. (cherry picked from commit 54ed994ab1cf2b0dae0857c6f7bbf9c04021ddef) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 15 +++++++++------ dlls/dmsynth/tests/dmsynth.c | 8 ++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index a63bfe24f7b..a93b23133f4 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -428,13 +428,16 @@ static HRESULT WINAPI synth_Activate(IDirectMusicSynth8 *iface, BOOL enable) if (enable == This->active) return S_FALSE; if (!This->sink) - return DMUS_E_NOSYNTHSINK; + { + This->active = FALSE; + return enable ? DMUS_E_NOSYNTHSINK : DMUS_E_SYNTHNOTCONFIGURED; + } - if ((hr = IDirectMusicSynthSink_Activate(This->sink, enable)) != S_OK) { - if (hr == DMUS_E_SYNTHACTIVE || hr == S_FALSE) - WARN("Synth and sink active state out of sync. Fixing.\n"); - else - return hr; + if (FAILED(hr = IDirectMusicSynthSink_Activate(This->sink, enable)) + && hr != DMUS_E_SYNTHACTIVE) + { + This->active = FALSE; + return DMUS_E_SYNTHNOTCONFIGURED; } This->active = enable; diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 5d60edd3b01..46727afe557 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1016,7 +1016,7 @@ static void test_IDirectMusicSynth(void) ref = get_refcount(sink); ok(ref == 2, "got %lu\n", ref); hr = IDirectMusicSynth_Activate(synth, TRUE); - todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); /* SetMasterClock does nothing */ hr = IDirectMusicSynth_SetMasterClock(synth, NULL); @@ -1047,9 +1047,9 @@ static void test_IDirectMusicSynth(void) /* but Activate might fail then */ hr = IDirectMusicSynth_Activate(synth, FALSE); - todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); hr = IDirectMusicSynth_Activate(synth, FALSE); - todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + ok(hr == S_FALSE, "got %#lx\n", hr); /* Test generating some samples */ @@ -1064,7 +1064,7 @@ static void test_IDirectMusicSynth(void) ref = get_refcount(sink); ok(ref == 2, "got %lu\n", ref); hr = IDirectMusicSynth_Activate(synth, TRUE); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); GetTempPathW(MAX_PATH, temp_path); GetTempFileNameW(temp_path, L"synth", 0, temp_file); From b01ddcd9ba50d942ad2b344b17e0a3e6de9ccf5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 10:56:55 +0200 Subject: [PATCH 0813/1148] dmusic: Move constructor parameter checks to class factory. (cherry picked from commit ed4327eef2ad178559e7d317caf1e89ecc9f9c86) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 41 ++++++++++++++---------------------- dlls/dmusic/dmusic.c | 20 ++++++------------ dlls/dmusic/dmusic_main.c | 26 +++++++++++++++-------- dlls/dmusic/dmusic_private.h | 4 ++-- 4 files changed, 41 insertions(+), 50 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 3f1c70b01e3..46939aad65f 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -520,31 +520,22 @@ static const IPersistStreamVtbl persiststream_vtbl = { unimpl_IPersistStream_GetSizeMax }; - -HRESULT DMUSIC_CreateDirectMusicCollectionImpl(REFIID lpcGUID, void **ppobj, IUnknown *pUnkOuter) +HRESULT collection_create(IUnknown **ret_iface) { - IDirectMusicCollectionImpl* obj; - HRESULT hr; - - *ppobj = NULL; - if (pUnkOuter) - return CLASS_E_NOAGGREGATION; - - obj = calloc(1, sizeof(IDirectMusicCollectionImpl)); - if (!obj) - return E_OUTOFMEMORY; - - obj->IDirectMusicCollection_iface.lpVtbl = &DirectMusicCollection_Collection_Vtbl; - obj->ref = 1; - dmobject_init(&obj->dmobj, &CLSID_DirectMusicCollection, - (IUnknown*)&obj->IDirectMusicCollection_iface); - obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; - obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - - list_init (&obj->Instruments); + IDirectMusicCollectionImpl *obj; - hr = IDirectMusicCollection_QueryInterface(&obj->IDirectMusicCollection_iface, lpcGUID, ppobj); - IDirectMusicCollection_Release(&obj->IDirectMusicCollection_iface); - - return hr; + *ret_iface = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; + obj->IDirectMusicCollection_iface.lpVtbl = &DirectMusicCollection_Collection_Vtbl; + obj->ref = 1; + dmobject_init(&obj->dmobj, &CLSID_DirectMusicCollection, + (IUnknown *)&obj->IDirectMusicCollection_iface); + obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; + obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; + + list_init(&obj->Instruments); + + TRACE("Created DirectMusicCollection %p\n", obj); + *ret_iface = (IUnknown *)&obj->IDirectMusicCollection_iface; + return S_OK; } diff --git a/dlls/dmusic/dmusic.c b/dlls/dmusic/dmusic.c index 970fb28ebd2..57a39d1465c 100644 --- a/dlls/dmusic/dmusic.c +++ b/dlls/dmusic/dmusic.c @@ -577,22 +577,15 @@ static void create_system_ports_list(IDirectMusic8Impl* object) object->num_system_ports = nb_ports; } -/* For ClassFactory */ -HRESULT DMUSIC_CreateDirectMusicImpl(REFIID riid, void **ret_iface, IUnknown *unkouter) +HRESULT music_create(IUnknown **ret_iface) { IDirectMusic8Impl *dmusic; HRESULT ret; - TRACE("(%s, %p, %p)\n", debugstr_guid(riid), ret_iface, unkouter); + TRACE("(%p)\n", ret_iface); *ret_iface = NULL; - if (unkouter) - return CLASS_E_NOAGGREGATION; - - dmusic = calloc(1, sizeof(IDirectMusic8Impl)); - if (!dmusic) - return E_OUTOFMEMORY; - + if (!(dmusic = calloc(1, sizeof(*dmusic)))) return E_OUTOFMEMORY; dmusic->IDirectMusic8_iface.lpVtbl = &DirectMusic8_Vtbl; dmusic->ref = 1; ret = master_clock_create(&dmusic->master_clock); @@ -603,8 +596,7 @@ HRESULT DMUSIC_CreateDirectMusicImpl(REFIID riid, void **ret_iface, IUnknown *un create_system_ports_list(dmusic); - ret = IDirectMusic8Impl_QueryInterface(&dmusic->IDirectMusic8_iface, riid, ret_iface); - IDirectMusic8_Release(&dmusic->IDirectMusic8_iface); - - return ret; + TRACE("Created DirectMusic %p\n", dmusic); + *ret_iface = (IUnknown *)&dmusic->IDirectMusic8_iface; + return S_OK; } diff --git a/dlls/dmusic/dmusic_main.c b/dlls/dmusic/dmusic_main.c index 509e80f872b..f0aaca8947a 100644 --- a/dlls/dmusic/dmusic_main.c +++ b/dlls/dmusic/dmusic_main.c @@ -41,7 +41,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmusic); typedef struct { IClassFactory IClassFactory_iface; - HRESULT (*fnCreateInstance)(REFIID riid, void **ppv, IUnknown *pUnkOuter); + HRESULT (*create_instance)(IUnknown **ret_iface); } IClassFactoryImpl; /****************************************************************** @@ -82,14 +82,23 @@ static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) return 1; /* non-heap based object */ } -static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter, - REFIID riid, void **ppv) +static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *unk_outer, REFIID riid, void **ret_iface) { - IClassFactoryImpl *This = impl_from_IClassFactory(iface); + IClassFactoryImpl *This = impl_from_IClassFactory(iface); + IUnknown *object; + HRESULT hr; - TRACE ("(%p, %s, %p)\n", pUnkOuter, debugstr_dmguid(riid), ppv); + TRACE("(%p, %s, %p)\n", unk_outer, debugstr_dmguid(riid), ret_iface); - return This->fnCreateInstance(riid, ppv, pUnkOuter); + *ret_iface = NULL; + if (unk_outer) return CLASS_E_NOAGGREGATION; + if (SUCCEEDED(hr = This->create_instance(&object))) + { + hr = IUnknown_QueryInterface(object, riid, ret_iface); + IUnknown_Release(object); + } + + return hr; } static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock) @@ -106,9 +115,8 @@ static const IClassFactoryVtbl classfactory_vtbl = { ClassFactory_LockServer }; -static IClassFactoryImpl DirectMusic_CF = {{&classfactory_vtbl}, DMUSIC_CreateDirectMusicImpl}; -static IClassFactoryImpl Collection_CF = {{&classfactory_vtbl}, - DMUSIC_CreateDirectMusicCollectionImpl}; +static IClassFactoryImpl DirectMusic_CF = {{&classfactory_vtbl}, music_create}; +static IClassFactoryImpl Collection_CF = {{&classfactory_vtbl}, collection_create}; diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 1eea2f845cb..85b64fb0470 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -94,8 +94,8 @@ typedef struct instrument_articulation { */ /* CLSID */ -extern HRESULT DMUSIC_CreateDirectMusicImpl(REFIID riid, void **ret_iface, IUnknown *pUnkOuter); -extern HRESULT DMUSIC_CreateDirectMusicCollectionImpl(REFIID riid, void **ppobj, IUnknown *pUnkOuter); +extern HRESULT music_create(IUnknown **ret_iface); +extern HRESULT collection_create(IUnknown **ret_iface); /* Internal */ extern HRESULT DMUSIC_CreateDirectMusicBufferImpl(LPDMUS_BUFFERDESC desc, LPVOID* ret_iface); From cca1ef228456ac97d7cf248b5d3075ed1c9386ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 6 Sep 2023 09:17:51 +0200 Subject: [PATCH 0814/1148] dmusic: Rename IDirectMusicCollectionImpl method prefix to collection. (cherry picked from commit 150240104224350f586ec88b79a4ec9a420b3f67) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 49 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 46939aad65f..2ff53a0fe23 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -54,8 +54,7 @@ static inline IDirectMusicCollectionImpl *impl_from_IPersistStream(IPersistStrea return CONTAINING_RECORD(iface, IDirectMusicCollectionImpl, dmobj.IPersistStream_iface); } -/* IDirectMusicCollectionImpl IUnknown part: */ -static HRESULT WINAPI IDirectMusicCollectionImpl_QueryInterface(IDirectMusicCollection *iface, +static HRESULT WINAPI collection_QueryInterface(IDirectMusicCollection *iface, REFIID riid, void **ret_iface) { IDirectMusicCollectionImpl *This = impl_from_IDirectMusicCollection(iface); @@ -80,7 +79,7 @@ static HRESULT WINAPI IDirectMusicCollectionImpl_QueryInterface(IDirectMusicColl return S_OK; } -static ULONG WINAPI IDirectMusicCollectionImpl_AddRef(IDirectMusicCollection *iface) +static ULONG WINAPI collection_AddRef(IDirectMusicCollection *iface) { IDirectMusicCollectionImpl *This = impl_from_IDirectMusicCollection(iface); ULONG ref = InterlockedIncrement(&This->ref); @@ -90,7 +89,7 @@ static ULONG WINAPI IDirectMusicCollectionImpl_AddRef(IDirectMusicCollection *if return ref; } -static ULONG WINAPI IDirectMusicCollectionImpl_Release(IDirectMusicCollection *iface) +static ULONG WINAPI collection_Release(IDirectMusicCollection *iface) { IDirectMusicCollectionImpl *This = impl_from_IDirectMusicCollection(iface); ULONG ref = InterlockedDecrement(&This->ref); @@ -104,8 +103,7 @@ static ULONG WINAPI IDirectMusicCollectionImpl_Release(IDirectMusicCollection *i return ref; } -/* IDirectMusicCollection Interface follows: */ -static HRESULT WINAPI IDirectMusicCollectionImpl_GetInstrument(IDirectMusicCollection *iface, +static HRESULT WINAPI collection_GetInstrument(IDirectMusicCollection *iface, DWORD patch, IDirectMusicInstrument **instrument) { IDirectMusicCollectionImpl *This = impl_from_IDirectMusicCollection(iface); @@ -132,7 +130,7 @@ static HRESULT WINAPI IDirectMusicCollectionImpl_GetInstrument(IDirectMusicColle return DMUS_E_INVALIDPATCH; } -static HRESULT WINAPI IDirectMusicCollectionImpl_EnumInstrument(IDirectMusicCollection *iface, +static HRESULT WINAPI collection_EnumInstrument(IDirectMusicCollection *iface, DWORD index, DWORD *patch, LPWSTR name, DWORD name_length) { IDirectMusicCollectionImpl *This = impl_from_IDirectMusicCollection(iface); @@ -161,16 +159,16 @@ static HRESULT WINAPI IDirectMusicCollectionImpl_EnumInstrument(IDirectMusicColl return S_FALSE; } -static const IDirectMusicCollectionVtbl DirectMusicCollection_Collection_Vtbl = { - IDirectMusicCollectionImpl_QueryInterface, - IDirectMusicCollectionImpl_AddRef, - IDirectMusicCollectionImpl_Release, - IDirectMusicCollectionImpl_GetInstrument, - IDirectMusicCollectionImpl_EnumInstrument +static const IDirectMusicCollectionVtbl collection_vtbl = +{ + collection_QueryInterface, + collection_AddRef, + collection_Release, + collection_GetInstrument, + collection_EnumInstrument, }; -/* IDirectMusicCollectionImpl IDirectMusicObject part: */ -static HRESULT WINAPI col_IDirectMusicObject_ParseDescriptor(IDirectMusicObject *iface, +static HRESULT WINAPI collection_object_ParseDescriptor(IDirectMusicObject *iface, IStream *stream, DMUS_OBJECTDESC *desc) { struct chunk_entry riff = {0}; @@ -201,17 +199,17 @@ static HRESULT WINAPI col_IDirectMusicObject_ParseDescriptor(IDirectMusicObject return S_OK; } -static const IDirectMusicObjectVtbl dmobject_vtbl = { +static const IDirectMusicObjectVtbl collection_object_vtbl = +{ dmobj_IDirectMusicObject_QueryInterface, dmobj_IDirectMusicObject_AddRef, dmobj_IDirectMusicObject_Release, dmobj_IDirectMusicObject_GetDescriptor, dmobj_IDirectMusicObject_SetDescriptor, - col_IDirectMusicObject_ParseDescriptor + collection_object_ParseDescriptor, }; -/* IDirectMusicCollectionImpl IPersistStream part: */ -static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, +static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *stream) { IDirectMusicCollectionImpl *This = impl_from_IPersistStream(iface); @@ -509,15 +507,16 @@ static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, return S_OK; } -static const IPersistStreamVtbl persiststream_vtbl = { +static const IPersistStreamVtbl collection_stream_vtbl = +{ dmobj_IPersistStream_QueryInterface, dmobj_IPersistStream_AddRef, dmobj_IPersistStream_Release, unimpl_IPersistStream_GetClassID, unimpl_IPersistStream_IsDirty, - IPersistStreamImpl_Load, + collection_stream_Load, unimpl_IPersistStream_Save, - unimpl_IPersistStream_GetSizeMax + unimpl_IPersistStream_GetSizeMax, }; HRESULT collection_create(IUnknown **ret_iface) @@ -526,12 +525,12 @@ HRESULT collection_create(IUnknown **ret_iface) *ret_iface = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->IDirectMusicCollection_iface.lpVtbl = &DirectMusicCollection_Collection_Vtbl; + obj->IDirectMusicCollection_iface.lpVtbl = &collection_vtbl; obj->ref = 1; dmobject_init(&obj->dmobj, &CLSID_DirectMusicCollection, (IUnknown *)&obj->IDirectMusicCollection_iface); - obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; - obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; + obj->dmobj.IDirectMusicObject_iface.lpVtbl = &collection_object_vtbl; + obj->dmobj.IPersistStream_iface.lpVtbl = &collection_stream_vtbl; list_init(&obj->Instruments); From 5043ebf907ddee84bfb730248ec23feccfa77614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 6 Sep 2023 09:19:52 +0200 Subject: [PATCH 0815/1148] dmusic: Get rid of the IDirectMusicCollectionImpl typedef. (cherry picked from commit 849c7a6ae15942c1efb10ef5e81c6191bee918bb) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 56 +++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 2ff53a0fe23..7223aa23bdb 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -24,14 +24,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmusic); WINE_DECLARE_DEBUG_CHANNEL(dmfile); -/***************************************************************************** - * IDirectMusicCollectionImpl implementation - */ -typedef struct IDirectMusicCollectionImpl { +struct collection +{ IDirectMusicCollection IDirectMusicCollection_iface; struct dmobject dmobj; LONG ref; - /* IDirectMusicCollectionImpl fields */ + IStream *pStm; /* stream from which we load collection and later instruments */ LARGE_INTEGER liCollectionPosition; /* offset in a stream where collection was loaded from */ LARGE_INTEGER liWavePoolTablePosition; /* offset in a stream where wave pool table can be found */ @@ -42,22 +40,22 @@ typedef struct IDirectMusicCollectionImpl { POOLCUE *pPoolCues; /* instruments */ struct list Instruments; -} IDirectMusicCollectionImpl; +}; -static inline IDirectMusicCollectionImpl *impl_from_IDirectMusicCollection(IDirectMusicCollection *iface) +static inline struct collection *impl_from_IDirectMusicCollection(IDirectMusicCollection *iface) { - return CONTAINING_RECORD(iface, IDirectMusicCollectionImpl, IDirectMusicCollection_iface); + return CONTAINING_RECORD(iface, struct collection, IDirectMusicCollection_iface); } -static inline IDirectMusicCollectionImpl *impl_from_IPersistStream(IPersistStream *iface) +static inline struct collection *impl_from_IPersistStream(IPersistStream *iface) { - return CONTAINING_RECORD(iface, IDirectMusicCollectionImpl, dmobj.IPersistStream_iface); + return CONTAINING_RECORD(iface, struct collection, dmobj.IPersistStream_iface); } static HRESULT WINAPI collection_QueryInterface(IDirectMusicCollection *iface, REFIID riid, void **ret_iface) { - IDirectMusicCollectionImpl *This = impl_from_IDirectMusicCollection(iface); + struct collection *This = impl_from_IDirectMusicCollection(iface); TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); @@ -81,7 +79,7 @@ static HRESULT WINAPI collection_QueryInterface(IDirectMusicCollection *iface, static ULONG WINAPI collection_AddRef(IDirectMusicCollection *iface) { - IDirectMusicCollectionImpl *This = impl_from_IDirectMusicCollection(iface); + struct collection *This = impl_from_IDirectMusicCollection(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p): new ref = %lu\n", iface, ref); @@ -91,7 +89,7 @@ static ULONG WINAPI collection_AddRef(IDirectMusicCollection *iface) static ULONG WINAPI collection_Release(IDirectMusicCollection *iface) { - IDirectMusicCollectionImpl *This = impl_from_IDirectMusicCollection(iface); + struct collection *This = impl_from_IDirectMusicCollection(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p): new ref = %lu\n", iface, ref); @@ -106,7 +104,7 @@ static ULONG WINAPI collection_Release(IDirectMusicCollection *iface) static HRESULT WINAPI collection_GetInstrument(IDirectMusicCollection *iface, DWORD patch, IDirectMusicInstrument **instrument) { - IDirectMusicCollectionImpl *This = impl_from_IDirectMusicCollection(iface); + struct collection *This = impl_from_IDirectMusicCollection(iface); DMUS_PRIVATE_INSTRUMENTENTRY *inst_entry; struct list *list_entry; DWORD inst_patch; @@ -133,7 +131,7 @@ static HRESULT WINAPI collection_GetInstrument(IDirectMusicCollection *iface, static HRESULT WINAPI collection_EnumInstrument(IDirectMusicCollection *iface, DWORD index, DWORD *patch, LPWSTR name, DWORD name_length) { - IDirectMusicCollectionImpl *This = impl_from_IDirectMusicCollection(iface); + struct collection *This = impl_from_IDirectMusicCollection(iface); DWORD i = 0; DMUS_PRIVATE_INSTRUMENTENTRY *inst_entry; struct list *list_entry; @@ -212,7 +210,7 @@ static const IDirectMusicObjectVtbl collection_object_vtbl = static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *stream) { - IDirectMusicCollectionImpl *This = impl_from_IPersistStream(iface); + struct collection *This = impl_from_IPersistStream(iface); DMUS_PRIVATE_CHUNK chunk; DWORD StreamSize, StreamCount, ListSize[2], ListCount[2]; LARGE_INTEGER liMove; /* used when skipping chunks */ @@ -521,20 +519,20 @@ static const IPersistStreamVtbl collection_stream_vtbl = HRESULT collection_create(IUnknown **ret_iface) { - IDirectMusicCollectionImpl *obj; + struct collection *collection; *ret_iface = NULL; - if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->IDirectMusicCollection_iface.lpVtbl = &collection_vtbl; - obj->ref = 1; - dmobject_init(&obj->dmobj, &CLSID_DirectMusicCollection, - (IUnknown *)&obj->IDirectMusicCollection_iface); - obj->dmobj.IDirectMusicObject_iface.lpVtbl = &collection_object_vtbl; - obj->dmobj.IPersistStream_iface.lpVtbl = &collection_stream_vtbl; - - list_init(&obj->Instruments); - - TRACE("Created DirectMusicCollection %p\n", obj); - *ret_iface = (IUnknown *)&obj->IDirectMusicCollection_iface; + if (!(collection = calloc(1, sizeof(*collection)))) return E_OUTOFMEMORY; + collection->IDirectMusicCollection_iface.lpVtbl = &collection_vtbl; + collection->ref = 1; + dmobject_init(&collection->dmobj, &CLSID_DirectMusicCollection, + (IUnknown *)&collection->IDirectMusicCollection_iface); + collection->dmobj.IDirectMusicObject_iface.lpVtbl = &collection_object_vtbl; + collection->dmobj.IPersistStream_iface.lpVtbl = &collection_stream_vtbl; + + list_init(&collection->Instruments); + + TRACE("Created DirectMusicCollection %p\n", collection); + *ret_iface = (IUnknown *)&collection->IDirectMusicCollection_iface; return S_OK; } From f2a64b8941d25800b8c2437bd48f44778823a991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 09:47:12 +0200 Subject: [PATCH 0816/1148] dmusic: Get rid of struct collection liCollectionPosition member. (cherry picked from commit a614527524be0263d6605547a25cab3a176852c8) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 7223aa23bdb..ca0f30db80a 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -31,7 +31,6 @@ struct collection LONG ref; IStream *pStm; /* stream from which we load collection and later instruments */ - LARGE_INTEGER liCollectionPosition; /* offset in a stream where collection was loaded from */ LARGE_INTEGER liWavePoolTablePosition; /* offset in a stream where wave pool table can be found */ CHAR *szCopyright; /* FIXME: should probably be placed somewhere else */ DLSHEADER *pHeader; @@ -214,12 +213,9 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, DMUS_PRIVATE_CHUNK chunk; DWORD StreamSize, StreamCount, ListSize[2], ListCount[2]; LARGE_INTEGER liMove; /* used when skipping chunks */ - ULARGE_INTEGER dlibCollectionPosition, dlibInstrumentPosition, dlibWavePoolPosition; + ULARGE_INTEGER dlibInstrumentPosition, dlibWavePoolPosition; IStream_AddRef(stream); /* add count for later references */ - liMove.QuadPart = 0; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, &dlibCollectionPosition); /* store offset, in case it'll be needed later */ - This->liCollectionPosition.QuadPart = dlibCollectionPosition.QuadPart; This->pStm = stream; IStream_Read(stream, &chunk, sizeof(FOURCC) + sizeof(DWORD), NULL); From 59832dbd9648d398b45c2f8cc8891f51e803055b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 09:48:05 +0200 Subject: [PATCH 0817/1148] dmusic: Get rid of struct collection liWavePoolTablePosition member. (cherry picked from commit 4a60c0f64fb8f659e02dde5de00e84a1d9cb03af) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index ca0f30db80a..19fc48ce555 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -31,7 +31,6 @@ struct collection LONG ref; IStream *pStm; /* stream from which we load collection and later instruments */ - LARGE_INTEGER liWavePoolTablePosition; /* offset in a stream where wave pool table can be found */ CHAR *szCopyright; /* FIXME: should probably be placed somewhere else */ DLSHEADER *pHeader; /* pool table */ @@ -213,7 +212,7 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, DMUS_PRIVATE_CHUNK chunk; DWORD StreamSize, StreamCount, ListSize[2], ListCount[2]; LARGE_INTEGER liMove; /* used when skipping chunks */ - ULARGE_INTEGER dlibInstrumentPosition, dlibWavePoolPosition; + ULARGE_INTEGER dlibInstrumentPosition; IStream_AddRef(stream); /* add count for later references */ This->pStm = stream; @@ -357,9 +356,6 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, } case FOURCC_WVPL: { TRACE_(dmfile)(": wave pool list (mark & skip)\n"); - liMove.QuadPart = 0; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, &dlibWavePoolPosition); /* store position */ - This->liWavePoolTablePosition.QuadPart = dlibWavePoolPosition.QuadPart; liMove.QuadPart = chunk.dwSize - sizeof(FOURCC); IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); break; From 319a33646b83ac648dc9df3514f5ab17ae21c33e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 10:56:55 +0200 Subject: [PATCH 0818/1148] dmusic: Simplify and cleanup IDirectMusicInstrument constructor. (cherry picked from commit d6dc41c2efca6084e3e10619acf8758a273ec86c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 2 +- dlls/dmusic/dmusic_private.h | 3 ++- dlls/dmusic/instrument.c | 28 +++++++++++----------------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 19fc48ce555..1331efbd05d 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -377,7 +377,7 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, DMUS_PRIVATE_INSTRUMENTENTRY *new_instrument = calloc(1, sizeof(DMUS_PRIVATE_INSTRUMENTENTRY)); TRACE_(dmfile)(": instrument list\n"); /* Only way to create this one... even M$ does it discretely */ - DMUSIC_CreateDirectMusicInstrumentImpl(&IID_IDirectMusicInstrument, (void**)&new_instrument->pInstrument, NULL); + instrument_create(&new_instrument->pInstrument); { IDirectMusicInstrumentImpl *instrument = impl_from_IDirectMusicInstrument(new_instrument->pInstrument); /* Store offset and length, they will be needed when loading the instrument */ diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 85b64fb0470..2822f4ffb87 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -100,10 +100,11 @@ extern HRESULT collection_create(IUnknown **ret_iface); /* Internal */ extern HRESULT DMUSIC_CreateDirectMusicBufferImpl(LPDMUS_BUFFERDESC desc, LPVOID* ret_iface); extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter); -extern HRESULT DMUSIC_CreateDirectMusicInstrumentImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter); extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface); +extern HRESULT instrument_create(IDirectMusicInstrument **ret_iface); + /***************************************************************************** * IDirectMusic8Impl implementation structure */ diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 767c4419225..3d4d7478be6 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -119,24 +119,18 @@ static const IDirectMusicInstrumentVtbl DirectMusicInstrument_Vtbl = IDirectMusicInstrumentImpl_SetPatch }; -/* for ClassFactory */ -HRESULT DMUSIC_CreateDirectMusicInstrumentImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter) { - IDirectMusicInstrumentImpl* dminst; - HRESULT hr; - - dminst = calloc(1, sizeof(IDirectMusicInstrumentImpl)); - if (NULL == dminst) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } - dminst->IDirectMusicInstrument_iface.lpVtbl = &DirectMusicInstrument_Vtbl; - dminst->ref = 1; - - hr = IDirectMusicInstrument_QueryInterface(&dminst->IDirectMusicInstrument_iface, lpcGUID, - ppobj); - IDirectMusicInstrument_Release(&dminst->IDirectMusicInstrument_iface); +HRESULT instrument_create(IDirectMusicInstrument **ret_iface) +{ + IDirectMusicInstrumentImpl *dminst; - return hr; + *ret_iface = NULL; + if (!(dminst = calloc(1, sizeof(*dminst)))) return E_OUTOFMEMORY; + dminst->IDirectMusicInstrument_iface.lpVtbl = &DirectMusicInstrument_Vtbl; + dminst->ref = 1; + + TRACE("Created DirectMusicInstrument %p\n", dminst); + *ret_iface = &dminst->IDirectMusicInstrument_iface; + return S_OK; } static HRESULT read_from_stream(IStream *stream, void *data, ULONG size) From e1c346bbe1c9b898ecb2dfb7c6c4dbfbf0ecdf40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 09:54:45 +0200 Subject: [PATCH 0819/1148] dmusic: Rename IDirectMusicInstrumentImpl method prefix to instrument. (cherry picked from commit 9abd659efdccfa7f29bd715d4f2a44ac0d62a6b6) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 2 +- dlls/dmusic/dmusic_private.h | 3 +-- dlls/dmusic/instrument.c | 28 +++++++++++++--------------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 1331efbd05d..8ec8cefad95 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -115,7 +115,7 @@ static HRESULT WINAPI collection_GetInstrument(IDirectMusicCollection *iface, if (patch == inst_patch) { *instrument = inst_entry->pInstrument; IDirectMusicInstrument_AddRef(inst_entry->pInstrument); - IDirectMusicInstrumentImpl_CustomLoad(inst_entry->pInstrument, This->pStm); + instrument_load(inst_entry->pInstrument, This->pStm); TRACE(": returning instrument %p\n", *instrument); return S_OK; } diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 2822f4ffb87..8d49c146013 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -204,8 +204,7 @@ static inline IDirectMusicInstrumentImpl *impl_from_IDirectMusicInstrument(IDire return CONTAINING_RECORD(iface, IDirectMusicInstrumentImpl, IDirectMusicInstrument_iface); } -/* custom :) */ -extern HRESULT IDirectMusicInstrumentImpl_CustomLoad(IDirectMusicInstrument *iface, IStream *stream); +extern HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream); /***************************************************************************** * Misc. diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 3d4d7478be6..6a394290ebd 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -25,8 +25,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmusic); static const GUID IID_IDirectMusicInstrumentPRIVATE = { 0xbcb20080, 0xa40c, 0x11d1, { 0x86, 0xbc, 0x00, 0xc0, 0x4f, 0xbf, 0x8f, 0xef } }; -/* IDirectMusicInstrument IUnknown part: */ -static HRESULT WINAPI IDirectMusicInstrumentImpl_QueryInterface(LPDIRECTMUSICINSTRUMENT iface, REFIID riid, LPVOID *ret_iface) +static HRESULT WINAPI instrument_QueryInterface(LPDIRECTMUSICINSTRUMENT iface, REFIID riid, LPVOID *ret_iface) { TRACE("(%p)->(%s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); @@ -56,7 +55,7 @@ static HRESULT WINAPI IDirectMusicInstrumentImpl_QueryInterface(LPDIRECTMUSICINS return E_NOINTERFACE; } -static ULONG WINAPI IDirectMusicInstrumentImpl_AddRef(LPDIRECTMUSICINSTRUMENT iface) +static ULONG WINAPI instrument_AddRef(LPDIRECTMUSICINSTRUMENT iface) { IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); ULONG ref = InterlockedIncrement(&This->ref); @@ -66,7 +65,7 @@ static ULONG WINAPI IDirectMusicInstrumentImpl_AddRef(LPDIRECTMUSICINSTRUMENT if return ref; } -static ULONG WINAPI IDirectMusicInstrumentImpl_Release(LPDIRECTMUSICINSTRUMENT iface) +static ULONG WINAPI instrument_Release(LPDIRECTMUSICINSTRUMENT iface) { IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); ULONG ref = InterlockedDecrement(&This->ref); @@ -87,8 +86,7 @@ static ULONG WINAPI IDirectMusicInstrumentImpl_Release(LPDIRECTMUSICINSTRUMENT i return ref; } -/* IDirectMusicInstrumentImpl IDirectMusicInstrument part: */ -static HRESULT WINAPI IDirectMusicInstrumentImpl_GetPatch(LPDIRECTMUSICINSTRUMENT iface, DWORD* pdwPatch) +static HRESULT WINAPI instrument_GetPatch(LPDIRECTMUSICINSTRUMENT iface, DWORD* pdwPatch) { IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); @@ -99,7 +97,7 @@ static HRESULT WINAPI IDirectMusicInstrumentImpl_GetPatch(LPDIRECTMUSICINSTRUMEN return S_OK; } -static HRESULT WINAPI IDirectMusicInstrumentImpl_SetPatch(LPDIRECTMUSICINSTRUMENT iface, DWORD dwPatch) +static HRESULT WINAPI instrument_SetPatch(LPDIRECTMUSICINSTRUMENT iface, DWORD dwPatch) { IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); @@ -110,13 +108,13 @@ static HRESULT WINAPI IDirectMusicInstrumentImpl_SetPatch(LPDIRECTMUSICINSTRUMEN return S_OK; } -static const IDirectMusicInstrumentVtbl DirectMusicInstrument_Vtbl = +static const IDirectMusicInstrumentVtbl instrument_vtbl = { - IDirectMusicInstrumentImpl_QueryInterface, - IDirectMusicInstrumentImpl_AddRef, - IDirectMusicInstrumentImpl_Release, - IDirectMusicInstrumentImpl_GetPatch, - IDirectMusicInstrumentImpl_SetPatch + instrument_QueryInterface, + instrument_AddRef, + instrument_Release, + instrument_GetPatch, + instrument_SetPatch, }; HRESULT instrument_create(IDirectMusicInstrument **ret_iface) @@ -125,7 +123,7 @@ HRESULT instrument_create(IDirectMusicInstrument **ret_iface) *ret_iface = NULL; if (!(dminst = calloc(1, sizeof(*dminst)))) return E_OUTOFMEMORY; - dminst->IDirectMusicInstrument_iface.lpVtbl = &DirectMusicInstrument_Vtbl; + dminst->IDirectMusicInstrument_iface.lpVtbl = &instrument_vtbl; dminst->ref = 1; TRACE("Created DirectMusicInstrument %p\n", dminst); @@ -278,7 +276,7 @@ static HRESULT load_articulation(IDirectMusicInstrumentImpl *This, IStream *stre } /* Function that loads all instrument data and which is called from IDirectMusicCollection_GetInstrument as in native */ -HRESULT IDirectMusicInstrumentImpl_CustomLoad(IDirectMusicInstrument *iface, IStream *stream) +HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) { IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); HRESULT hr; From ae06fa871c2acec6c22251300cb483dde8a57cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 6 Sep 2023 09:21:24 +0200 Subject: [PATCH 0820/1148] dmusic: Get rid of the IDirectMusicInstrumentImpl typedef. (cherry picked from commit 0110dc24462761ba24b6c7bd9539a1232c11480c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 4 ++-- dlls/dmusic/dmusic_private.h | 14 ++++---------- dlls/dmusic/instrument.c | 26 +++++++++++++------------- dlls/dmusic/port.c | 2 +- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 8ec8cefad95..5332adff084 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -140,7 +140,7 @@ static HRESULT WINAPI collection_EnumInstrument(IDirectMusicCollection *iface, LIST_FOR_EACH(list_entry, &This->Instruments) { inst_entry = LIST_ENTRY(list_entry, DMUS_PRIVATE_INSTRUMENTENTRY, entry); if (i == index) { - IDirectMusicInstrumentImpl *instrument = impl_from_IDirectMusicInstrument(inst_entry->pInstrument); + struct instrument *instrument = impl_from_IDirectMusicInstrument(inst_entry->pInstrument); IDirectMusicInstrument_GetPatch(inst_entry->pInstrument, patch); if (name) { length = min(lstrlenW(instrument->wszName), name_length - 1); @@ -379,7 +379,7 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, /* Only way to create this one... even M$ does it discretely */ instrument_create(&new_instrument->pInstrument); { - IDirectMusicInstrumentImpl *instrument = impl_from_IDirectMusicInstrument(new_instrument->pInstrument); + struct instrument *instrument = impl_from_IDirectMusicInstrument(new_instrument->pInstrument); /* Store offset and length, they will be needed when loading the instrument */ liMove.QuadPart = 0; IStream_Seek(stream, liMove, STREAM_SEEK_CUR, &dlibInstrumentPosition); diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 8d49c146013..5c8f6e1abad 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -52,8 +52,6 @@ typedef struct IDirectMusicBufferImpl IDirectMusicBufferImpl; typedef struct IDirectMusicDownloadedInstrumentImpl IDirectMusicDownloadedInstrumentImpl; typedef struct IReferenceClockImpl IReferenceClockImpl; -typedef struct IDirectMusicInstrumentImpl IDirectMusicInstrumentImpl; - /***************************************************************************** * Some stuff to make my life easier :=) */ @@ -178,15 +176,11 @@ typedef struct _DMUS_PRIVATE_POOLCUE { struct list entry; /* for listing elements */ } DMUS_PRIVATE_POOLCUE, *LPDMUS_PRIVATE_POOLCUE; -/***************************************************************************** - * IDirectMusicInstrumentImpl implementation structure - */ -struct IDirectMusicInstrumentImpl { - /* IUnknown fields */ +struct instrument +{ IDirectMusicInstrument IDirectMusicInstrument_iface; LONG ref; - /* IDirectMusicInstrumentImpl fields */ LARGE_INTEGER liInstrumentPosition; /* offset in a stream where instrument chunk can be found */ ULONG length; /* Length of the instrument in the stream */ GUID id; @@ -199,9 +193,9 @@ struct IDirectMusicInstrumentImpl { instrument_articulation *articulations; }; -static inline IDirectMusicInstrumentImpl *impl_from_IDirectMusicInstrument(IDirectMusicInstrument *iface) +static inline struct instrument *impl_from_IDirectMusicInstrument(IDirectMusicInstrument *iface) { - return CONTAINING_RECORD(iface, IDirectMusicInstrumentImpl, IDirectMusicInstrument_iface); + return CONTAINING_RECORD(iface, struct instrument, IDirectMusicInstrument_iface); } extern HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream); diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 6a394290ebd..5677f128060 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -57,7 +57,7 @@ static HRESULT WINAPI instrument_QueryInterface(LPDIRECTMUSICINSTRUMENT iface, R static ULONG WINAPI instrument_AddRef(LPDIRECTMUSICINSTRUMENT iface) { - IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); + struct instrument *This = impl_from_IDirectMusicInstrument(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p): new ref = %lu\n", iface, ref); @@ -67,7 +67,7 @@ static ULONG WINAPI instrument_AddRef(LPDIRECTMUSICINSTRUMENT iface) static ULONG WINAPI instrument_Release(LPDIRECTMUSICINSTRUMENT iface) { - IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); + struct instrument *This = impl_from_IDirectMusicInstrument(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p): new ref = %lu\n", iface, ref); @@ -88,7 +88,7 @@ static ULONG WINAPI instrument_Release(LPDIRECTMUSICINSTRUMENT iface) static HRESULT WINAPI instrument_GetPatch(LPDIRECTMUSICINSTRUMENT iface, DWORD* pdwPatch) { - IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); + struct instrument *This = impl_from_IDirectMusicInstrument(iface); TRACE("(%p)->(%p)\n", This, pdwPatch); @@ -99,7 +99,7 @@ static HRESULT WINAPI instrument_GetPatch(LPDIRECTMUSICINSTRUMENT iface, DWORD* static HRESULT WINAPI instrument_SetPatch(LPDIRECTMUSICINSTRUMENT iface, DWORD dwPatch) { - IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); + struct instrument *This = impl_from_IDirectMusicInstrument(iface); TRACE("(%p, %ld): stub\n", This, dwPatch); @@ -119,15 +119,15 @@ static const IDirectMusicInstrumentVtbl instrument_vtbl = HRESULT instrument_create(IDirectMusicInstrument **ret_iface) { - IDirectMusicInstrumentImpl *dminst; + struct instrument *instrument; *ret_iface = NULL; - if (!(dminst = calloc(1, sizeof(*dminst)))) return E_OUTOFMEMORY; - dminst->IDirectMusicInstrument_iface.lpVtbl = &instrument_vtbl; - dminst->ref = 1; + if (!(instrument = calloc(1, sizeof(*instrument)))) return E_OUTOFMEMORY; + instrument->IDirectMusicInstrument_iface.lpVtbl = &instrument_vtbl; + instrument->ref = 1; - TRACE("Created DirectMusicInstrument %p\n", dminst); - *ret_iface = &dminst->IDirectMusicInstrument_iface; + TRACE("Created DirectMusicInstrument %p\n", instrument); + *ret_iface = &instrument->IDirectMusicInstrument_iface; return S_OK; } @@ -172,7 +172,7 @@ static inline HRESULT advance_stream(IStream *stream, ULONG bytes) return ret; } -static HRESULT load_region(IDirectMusicInstrumentImpl *This, IStream *stream, instrument_region *region, ULONG length) +static HRESULT load_region(struct instrument *This, IStream *stream, instrument_region *region, ULONG length) { HRESULT ret; DMUS_PRIVATE_CHUNK chunk; @@ -242,7 +242,7 @@ static HRESULT load_region(IDirectMusicInstrumentImpl *This, IStream *stream, in return S_OK; } -static HRESULT load_articulation(IDirectMusicInstrumentImpl *This, IStream *stream, ULONG length) +static HRESULT load_articulation(struct instrument *This, IStream *stream, ULONG length) { HRESULT ret; instrument_articulation *articulation; @@ -278,7 +278,7 @@ static HRESULT load_articulation(IDirectMusicInstrumentImpl *This, IStream *stre /* Function that loads all instrument data and which is called from IDirectMusicCollection_GetInstrument as in native */ HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) { - IDirectMusicInstrumentImpl *This = impl_from_IDirectMusicInstrument(iface); + struct instrument *This = impl_from_IDirectMusicInstrument(iface); HRESULT hr; DMUS_PRIVATE_CHUNK chunk; ULONG i = 0; diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index eaf2bb139f1..95857ea55ac 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -266,7 +266,7 @@ static HRESULT WINAPI synth_port_DownloadInstrument(IDirectMusicPort *iface, IDi IDirectMusicDownloadedInstrument **downloaded_instrument, DMUS_NOTERANGE *note_ranges, DWORD num_note_ranges) { struct synth_port *This = synth_from_IDirectMusicPort(iface); - IDirectMusicInstrumentImpl *instrument_object; + struct instrument *instrument_object; HRESULT ret; BOOL on_heap; HANDLE download; From 87527b404606126269d7e4c8307a9b535506c6f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 23:47:56 +0200 Subject: [PATCH 0821/1148] dmime/tests: Test IDirectMusicGraph interface with a dummy tool. (cherry picked from commit 881e424683eb1f2493c4d4524e65127a6277a3f4) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 285 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 281 insertions(+), 4 deletions(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 8f2bf6f2812..2c84fee05ad 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -27,6 +27,126 @@ #include #include +struct test_tool +{ + IDirectMusicTool IDirectMusicTool_iface; + LONG ref; + + IDirectMusicGraph *graph; +}; + +static struct test_tool *impl_from_IDirectMusicTool(IDirectMusicTool *iface) +{ + return CONTAINING_RECORD(iface, struct test_tool, IDirectMusicTool_iface); +} + +static HRESULT WINAPI test_tool_QueryInterface(IDirectMusicTool *iface, REFIID iid, void **out) +{ + if (IsEqualGUID(iid, &IID_IUnknown) + || IsEqualGUID(iid, &IID_IDirectMusicTool)) + { + IDirectMusicTool_AddRef(iface); + *out = iface; + return S_OK; + } + + ok(IsEqualGUID(iid, &IID_IDirectMusicTool8) || IsEqualGUID(iid, &IID_IPersistStream), + "got iid %s\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_tool_AddRef(IDirectMusicTool *iface) +{ + struct test_tool *tool = impl_from_IDirectMusicTool(iface); + return InterlockedIncrement(&tool->ref); +} + +static ULONG WINAPI test_tool_Release(IDirectMusicTool *iface) +{ + struct test_tool *tool = impl_from_IDirectMusicTool(iface); + ULONG ref = InterlockedDecrement(&tool->ref); + + if (!ref) + { + if (tool->graph) IDirectMusicGraph_Release(tool->graph); + free(tool); + } + + return ref; +} + +static HRESULT WINAPI test_tool_Init(IDirectMusicTool *iface, IDirectMusicGraph *graph) +{ + struct test_tool *tool = impl_from_IDirectMusicTool(iface); + if ((tool->graph = graph)) IDirectMusicGraph_AddRef(tool->graph); + return S_OK; +} + +static HRESULT WINAPI test_tool_GetMsgDeliveryType(IDirectMusicTool *iface, DWORD *type) +{ + *type = DMUS_PMSGF_TOOL_IMMEDIATE; + return S_OK; +} + +static HRESULT WINAPI test_tool_GetMediaTypeArraySize(IDirectMusicTool *iface, DWORD *size) +{ + *size = 0; + return S_OK; +} + +static HRESULT WINAPI test_tool_GetMediaTypes(IDirectMusicTool *iface, DWORD **types, DWORD size) +{ + ok(0, "%#04lx: unexpected %s, types %p, size %lu\n", GetCurrentThreadId(), __func__, types, size); + return S_OK; +} + +static HRESULT WINAPI test_tool_ProcessPMsg(IDirectMusicTool *iface, IDirectMusicPerformance *performance, DMUS_PMSG *msg) +{ + ok(0, "%#04lx: unexpected %s, iface %p, performance %p, msg %p\n", GetCurrentThreadId(), __func__, iface, performance, msg); + return DMUS_S_REQUEUE; +} + +static HRESULT WINAPI test_tool_Flush(IDirectMusicTool *iface, IDirectMusicPerformance *performance, + DMUS_PMSG *msg, REFERENCE_TIME time) +{ + ok(0, "unexpected %s\n", __func__); + return S_OK; +} + +static IDirectMusicToolVtbl test_tool_vtbl = +{ + test_tool_QueryInterface, + test_tool_AddRef, + test_tool_Release, + test_tool_Init, + test_tool_GetMsgDeliveryType, + test_tool_GetMediaTypeArraySize, + test_tool_GetMediaTypes, + test_tool_ProcessPMsg, + test_tool_Flush, +}; + +static HRESULT test_tool_create(IDirectMusicTool **ret_iface) +{ + struct test_tool *tool; + + *ret_iface = NULL; + if (!(tool = calloc(1, sizeof(*tool)))) return E_OUTOFMEMORY; + tool->IDirectMusicTool_iface.lpVtbl = &test_tool_vtbl; + tool->ref = 1; + + *ret_iface = &tool->IDirectMusicTool_iface; + return S_OK; +} + +static HRESULT test_tool_get_graph(IDirectMusicTool *iface, IDirectMusicGraph **graph) +{ + struct test_tool *tool = impl_from_IDirectMusicTool(iface); + if ((*graph = tool->graph)) IDirectMusicGraph_AddRef(tool->graph); + return tool->graph ? S_OK : DMUS_E_NOT_FOUND; +} + static BOOL missing_dmime(void) { IDirectMusicSegment8 *dms; @@ -458,18 +578,20 @@ static void test_audiopathconfig(void) static void test_graph(void) { - IDirectMusicGraph *dmg; + IDirectMusicTool *tool1, *tool2, *tmp_tool; + IDirectMusicGraph *graph, *tmp_graph; IPersistStream *ps; CLSID class = { 0 }; ULARGE_INTEGER size; + DMUS_PMSG msg; HRESULT hr; hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectMusicGraph, (void**)&dmg); + &IID_IDirectMusicGraph, (void**)&graph); ok(hr == S_OK, "DirectMusicGraph create failed: %#lx, expected S_OK\n", hr); /* IPersistStream */ - hr = IDirectMusicGraph_QueryInterface(dmg, &IID_IPersistStream, (void**)&ps); + hr = IDirectMusicGraph_QueryInterface(graph, &IID_IPersistStream, (void**)&ps); ok(hr == S_OK, "QueryInterface for IID_IPersistStream failed: %#lx\n", hr); hr = IPersistStream_GetClassID(ps, &class); ok(hr == S_OK || broken(hr == E_NOTIMPL) /* win2k */, "IPersistStream_GetClassID failed: %#lx\n", hr); @@ -485,7 +607,162 @@ static void test_graph(void) hr = IPersistStream_Save(ps, NULL, TRUE); ok(hr == E_NOTIMPL, "IPersistStream_Save failed: %#lx\n", hr); - while (IDirectMusicGraph_Release(dmg)); + IDirectMusicGraph_Release(graph); + + + hr = test_tool_create(&tool1); + ok(hr == S_OK, "got %#lx\n", hr); + trace("created tool1 %p\n", tool1); + hr = test_tool_create(&tool2); + ok(hr == S_OK, "got %#lx\n", hr); + trace("created tool2 %p\n", tool2); + + hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicGraph, (void **)&graph); + ok(hr == S_OK, "got %#lx\n", hr); + + + hr = IDirectMusicGraph_InsertTool(graph, NULL, NULL, 0, -1); + ok(hr == E_POINTER, "got %#lx\n", hr); + + /* InsertTool initializes the tool */ + hr = IDirectMusicGraph_InsertTool(graph, tool1, NULL, 0, -1); + ok(hr == S_OK, "got %#lx\n", hr); + hr = test_tool_get_graph(tool1, &tmp_graph); + ok(hr == S_OK, "got %#lx\n", hr); + ok(graph == tmp_graph, "got %#lx\n", hr); + IDirectMusicGraph_Release(tmp_graph); + + hr = IDirectMusicGraph_InsertTool(graph, tool2, NULL, 0, 1); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicGraph_GetTool(graph, 0, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicGraph_GetTool(graph, 0, &tmp_tool); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(tool1 == tmp_tool, "got %p\n", tmp_tool); + if (hr == S_OK) IDirectMusicTool_Release(tmp_tool); + hr = IDirectMusicGraph_GetTool(graph, 1, &tmp_tool); + ok(hr == S_OK, "got %#lx\n", hr); + ok(tool2 == tmp_tool, "got %p\n", tmp_tool); + if (hr == S_OK) IDirectMusicTool_Release(tmp_tool); + hr = IDirectMusicGraph_GetTool(graph, 2, &tmp_tool); + ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + + /* cannot insert the tool twice */ + hr = IDirectMusicGraph_InsertTool(graph, tool1, NULL, 0, -1); + ok(hr == DMUS_E_ALREADY_EXISTS, "got %#lx\n", hr); + + /* test removing the first tool */ + hr = IDirectMusicGraph_RemoveTool(graph, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicGraph_RemoveTool(graph, tool1); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_RemoveTool(graph, tool1); + todo_wine ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + + hr = IDirectMusicGraph_GetTool(graph, 0, &tmp_tool); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(tool2 == tmp_tool, "got %p\n", tmp_tool); + if (hr == S_OK) IDirectMusicTool_Release(tmp_tool); + hr = IDirectMusicGraph_GetTool(graph, 1, &tmp_tool); + todo_wine ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + + hr = IDirectMusicGraph_InsertTool(graph, tool1, NULL, 0, -1); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_GetTool(graph, 0, &tmp_tool); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(tool1 == tmp_tool, "got %p\n", tmp_tool); + if (hr == S_OK) IDirectMusicTool_Release(tmp_tool); + + + /* Test basic IDirectMusicGraph_StampPMsg usage */ + hr = IDirectMusicGraph_StampPMsg(graph, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + memset(&msg, 0, sizeof(msg)); + hr = IDirectMusicGraph_StampPMsg(graph, &msg); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + todo_wine ok(msg.pTool == tool1, "got %p\n", msg.pTool); + + ok(!msg.dwSize, "got %ld\n", msg.dwSize); + ok(!msg.rtTime, "got %I64d\n", msg.rtTime); + ok(!msg.mtTime, "got %ld\n", msg.mtTime); + todo_wine ok(msg.dwFlags == DMUS_PMSGF_TOOL_IMMEDIATE, "got %#lx\n", msg.dwFlags); + ok(!msg.dwPChannel, "got %ld\n", msg.dwPChannel); + ok(!msg.dwVirtualTrackID, "got %ld\n", msg.dwVirtualTrackID); + ok(!msg.dwType, "got %#lx\n", msg.dwType); + ok(!msg.dwVoiceID, "got %ld\n", msg.dwVoiceID); + ok(!msg.dwGroupID, "got %ld\n", msg.dwGroupID); + ok(!msg.punkUser, "got %p\n", msg.punkUser); + + hr = IDirectMusicGraph_StampPMsg(graph, &msg); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + todo_wine ok(msg.pTool == tool2, "got %p\n", msg.pTool); + hr = IDirectMusicGraph_StampPMsg(graph, &msg); + todo_wine ok(hr == DMUS_S_LAST_TOOL, "got %#lx\n", hr); + todo_wine ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + ok(!msg.pTool, "got %p\n", msg.pTool); + hr = IDirectMusicGraph_StampPMsg(graph, &msg); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + todo_wine ok(msg.pTool == tool1, "got %p\n", msg.pTool); + if (msg.pGraph) IDirectMusicGraph_Release(msg.pGraph); + msg.pGraph = NULL; + if (msg.pTool) IDirectMusicGraph_Release(msg.pTool); + msg.pTool = NULL; + + + /* test StampPMsg with the wrong graph or innexistant tools */ + hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicGraph, (void **)&tmp_graph); + ok(hr == S_OK, "got %#lx\n", hr); + + msg.pGraph = tmp_graph; + IDirectMusicGraph_AddRef(msg.pGraph); + msg.pTool = tool1; + IDirectMusicTool_AddRef(msg.pTool); + hr = IDirectMusicGraph_StampPMsg(graph, &msg); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg.pGraph == tmp_graph, "got %p\n", msg.pGraph); + todo_wine ok(msg.pTool == tool2, "got %p\n", msg.pTool); + if (msg.pGraph) IDirectMusicGraph_Release(msg.pGraph); + msg.pGraph = NULL; + + msg.pGraph = graph; + IDirectMusicGraph_AddRef(msg.pGraph); + hr = IDirectMusicGraph_StampPMsg(tmp_graph, &msg); + todo_wine ok(hr == DMUS_S_LAST_TOOL, "got %#lx\n", hr); + ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + todo_wine ok(msg.pTool == NULL, "got %p\n", msg.pTool); + + msg.pTool = tool2; + IDirectMusicTool_AddRef(msg.pTool); + hr = IDirectMusicGraph_InsertTool(tmp_graph, tool1, NULL, 0, 0); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_InsertTool(tmp_graph, tool2, NULL, 0, 0); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_StampPMsg(tmp_graph, &msg); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + todo_wine ok(msg.pTool == tool1, "got %p\n", msg.pTool); + if (msg.pGraph) IDirectMusicGraph_Release(msg.pGraph); + msg.pGraph = NULL; + + hr = IDirectMusicGraph_RemoveTool(graph, tool1); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_StampPMsg(tmp_graph, &msg); + todo_wine ok(hr == DMUS_S_LAST_TOOL, "got %#lx\n", hr); + ok(msg.pGraph == NULL, "got %p\n", msg.pGraph); + todo_wine ok(msg.pTool == NULL, "got %p\n", msg.pTool); + + IDirectMusicGraph_Release(tmp_graph); + + + IDirectMusicGraph_Release(graph); + IDirectMusicTool_Release(tool2); + IDirectMusicTool_Release(tool1); } static void test_segment(void) From 519840b4b38e14f7f0830bd209146738d75cb8a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 08:32:42 +0200 Subject: [PATCH 0822/1148] dmime/tests: Test performance IDirectMusicTool interface. (cherry picked from commit 5efaae40deaf4c33780832bb349fb61e1f5d0123) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 74 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 2c84fee05ad..da1b4b4d8b8 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -27,6 +27,34 @@ #include #include +static ULONG get_refcount(void *iface) +{ + IUnknown *unknown = iface; + IUnknown_AddRef(unknown); + return IUnknown_Release(unknown); +} + +#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) +static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) +{ + ULONG expect_ref = get_refcount(iface_ptr); + IUnknown *iface = iface_ptr; + HRESULT hr, expected; + IUnknown *unk; + + expected = supported ? S_OK : E_NOINTERFACE; + hr = IUnknown_QueryInterface(iface, iid, (void **)&unk); + ok_(__FILE__, line)(hr == expected, "got hr %#lx, expected %#lx.\n", hr, expected); + if (SUCCEEDED(hr)) + { + LONG ref = get_refcount(unk); + ok_(__FILE__, line)(ref == expect_ref + 1, "got %ld\n", ref); + IUnknown_Release(unk); + ref = get_refcount(iface_ptr); + ok_(__FILE__, line)(ref == expect_ref, "got %ld\n", ref); + } +} + struct test_tool { IDirectMusicTool IDirectMusicTool_iface; @@ -1405,6 +1433,51 @@ static void test_parsedescriptor(void) } } +static void test_performance_tool(void) +{ + IDirectMusicPerformance *performance; + IDirectMusicGraph *graph; + IDirectMusicTool *tool; + DWORD value, types[1]; + DMUS_PMSG msg = {0}; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void **)&performance); + ok(hr == S_OK, "got %#lx\n", hr); + + check_interface(performance, &IID_IDirectMusicTool8, FALSE); + + hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicTool, (void **)&tool); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (hr != S_OK) goto skip_tool; + hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicTool_Init(tool, graph); + ok(hr == E_NOTIMPL, "got %#lx\n", hr); + value = 0xdeadbeef; + hr = IDirectMusicTool_GetMsgDeliveryType(tool, &value); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(value == DMUS_PMSGF_TOOL_IMMEDIATE, "got %#lx\n", value); + value = 0xdeadbeef; + hr = IDirectMusicTool_GetMediaTypeArraySize(tool, &value); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(value == 0, "got %#lx\n", value); + hr = IDirectMusicTool_GetMediaTypes(tool, (DWORD **)&types, 64); + ok(hr == E_NOTIMPL, "got %#lx\n", hr); + hr = IDirectMusicTool_ProcessPMsg(tool, performance, &msg); + todo_wine ok(hr == DMUS_S_FREE, "got %#lx\n", hr); + hr = IDirectMusicTool_Flush(tool, performance, &msg, 0); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + + IDirectMusicGraph_Release(graph); + IDirectMusicTool_Release(tool); + +skip_tool: + IDirectMusicPerformance_Release(performance); +} + START_TEST(dmime) { CoInitialize(NULL); @@ -1428,6 +1501,7 @@ START_TEST(dmime) test_segment_param(); test_track(); test_parsedescriptor(); + test_performance_tool(); CoUninitialize(); } From 57e99ec9b771dc8a795ad8999395bdaa1281b280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 08:29:57 +0200 Subject: [PATCH 0823/1148] dmime/tests: Test performance IDirectMusicGraph interface. (cherry picked from commit cd5c9b800d275ba91899c2ad9a8739bcad0c6be0) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 124 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index da1b4b4d8b8..42eb4b1f47e 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1478,6 +1478,129 @@ static void test_performance_tool(void) IDirectMusicPerformance_Release(performance); } +static void test_performance_graph(void) +{ + IDirectMusicPerformance *performance; + IDirectMusicGraph *graph, *tmp_graph; + IDirectMusicTool *tool, *tmp_tool; + DMUS_PMSG msg; + HRESULT hr; + + hr = test_tool_create(&tool); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void **)&performance); + ok(hr == S_OK, "got %#lx\n", hr); + + + /* performance exposes a graph interface but it's not an actual toolgraph */ + hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (hr != S_OK) goto skip_graph; + hr = IDirectMusicGraph_InsertTool(graph, (IDirectMusicTool *)tool, NULL, 0, -1); + ok(hr == E_NOTIMPL, "got %#lx\n", hr); + hr = IDirectMusicGraph_GetTool(graph, 0, &tmp_tool); + ok(hr == E_NOTIMPL, "got %#lx\n", hr); + hr = IDirectMusicGraph_RemoveTool(graph, tool); + ok(hr == E_NOTIMPL, "got %#lx\n", hr); + + /* test IDirectMusicGraph_StampPMsg usage */ + hr = IDirectMusicGraph_StampPMsg(graph, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + memset(&msg, 0, sizeof(msg)); + hr = IDirectMusicGraph_StampPMsg(graph, &msg); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(msg.pGraph == NULL, "got %p\n", msg.pGraph); + todo_wine ok(msg.pTool != NULL, "got %p\n", msg.pTool); + if (msg.pTool) check_interface(msg.pTool, &IID_IDirectMusicPerformance, TRUE); + + ok(!msg.dwSize, "got %ld\n", msg.dwSize); + ok(!msg.rtTime, "got %I64d\n", msg.rtTime); + ok(!msg.mtTime, "got %ld\n", msg.mtTime); + todo_wine ok(msg.dwFlags == DMUS_PMSGF_TOOL_QUEUE, "got %#lx\n", msg.dwFlags); + ok(!msg.dwPChannel, "got %ld\n", msg.dwPChannel); + ok(!msg.dwVirtualTrackID, "got %ld\n", msg.dwVirtualTrackID); + ok(!msg.dwType, "got %#lx\n", msg.dwType); + ok(!msg.dwVoiceID, "got %ld\n", msg.dwVoiceID); + ok(!msg.dwGroupID, "got %ld\n", msg.dwGroupID); + ok(!msg.punkUser, "got %p\n", msg.punkUser); + + hr = IDirectMusicGraph_StampPMsg(graph, &msg); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(msg.pGraph == NULL, "got %p\n", msg.pGraph); + todo_wine ok(msg.pTool != NULL, "got %p\n", msg.pTool); + if (msg.pTool) check_interface(msg.pTool, &IID_IDirectMusicPerformance, TRUE); + + if (msg.pTool) IDirectMusicTool_Release(msg.pTool); + msg.pTool = NULL; + + IDirectMusicGraph_Release(graph); + + +skip_graph: + /* performance doesn't have a default embedded toolgraph */ + hr = IDirectMusicPerformance_GetGraph(performance, &graph); + ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + + + /* test adding a graph to the performance */ + hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicGraph, (void **)&graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SetGraph(performance, graph); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_GetGraph(performance, &tmp_graph); + ok(hr == S_OK, "got %#lx\n", hr); + ok(tmp_graph == graph, "got %p\n", graph); + IDirectMusicGraph_Release(tmp_graph); + + hr = IDirectMusicGraph_InsertTool(graph, (IDirectMusicTool *)tool, NULL, 0, -1); + ok(hr == S_OK, "got %#lx\n", hr); + + IDirectMusicGraph_Release(graph); + + + /* test IDirectMusicGraph_StampPMsg usage */ + hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (hr != S_OK) goto skip_graph2; + + memset(&msg, 0, sizeof(msg)); + hr = IDirectMusicGraph_StampPMsg(graph, &msg); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + todo_wine ok(msg.pTool == tool, "got %p\n", msg.pTool); + + ok(!msg.dwSize, "got %ld\n", msg.dwSize); + ok(!msg.rtTime, "got %I64d\n", msg.rtTime); + ok(!msg.mtTime, "got %ld\n", msg.mtTime); + todo_wine ok(msg.dwFlags == DMUS_PMSGF_TOOL_IMMEDIATE, "got %#lx\n", msg.dwFlags); + ok(!msg.dwPChannel, "got %ld\n", msg.dwPChannel); + ok(!msg.dwVirtualTrackID, "got %ld\n", msg.dwVirtualTrackID); + ok(!msg.dwType, "got %#lx\n", msg.dwType); + ok(!msg.dwVoiceID, "got %ld\n", msg.dwVoiceID); + ok(!msg.dwGroupID, "got %ld\n", msg.dwGroupID); + ok(!msg.punkUser, "got %p\n", msg.punkUser); + + hr = IDirectMusicGraph_StampPMsg(graph, &msg); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(msg.pGraph == NULL, "got %p\n", msg.pGraph); + todo_wine ok(msg.pTool != NULL, "got %p\n", msg.pTool); + if (msg.pTool) check_interface(msg.pTool, &IID_IDirectMusicPerformance, TRUE); + + if (msg.pTool) IDirectMusicTool_Release(msg.pTool); + msg.pTool = NULL; + + IDirectMusicGraph_Release(graph); + + +skip_graph2: + IDirectMusicPerformance_Release(performance); + IDirectMusicTool_Release(tool); +} + START_TEST(dmime) { CoInitialize(NULL); @@ -1502,6 +1625,7 @@ START_TEST(dmime) test_track(); test_parsedescriptor(); test_performance_tool(); + test_performance_graph(); CoUninitialize(); } From d4234daf1443a335e7bc83ad218feebe7977b7ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 16:27:17 +0200 Subject: [PATCH 0824/1148] dmime/tests: Test IDirectMusicPerformance time conversion. (cherry picked from commit a874f60cae59f5cda98e6d02acbf1ead343141ae) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 91 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 42eb4b1f47e..7fa832ebfd5 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1601,6 +1601,96 @@ static void test_performance_graph(void) IDirectMusicTool_Release(tool); } +static void test_performance_time(void) +{ + IDirectMusicPerformance *performance; + REFERENCE_TIME init_time, time; + IReferenceClock *clock; + MUSIC_TIME music_time; + IDirectMusic *dmusic; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void **)&performance); + ok(hr == S_OK, "got %#lx\n", hr); + + + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 0, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 0, &time); + todo_wine ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); + todo_wine ok(time == 0, "got %I64d\n", time); + + hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, 0, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + music_time = 0xdeadbeef; + hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, 0, &music_time); + todo_wine ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); + todo_wine ok(music_time == 0, "got %ld\n", music_time); + + + dmusic = NULL; + hr = IDirectMusicPerformance_Init(performance, &dmusic, NULL, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusic_GetMasterClock(dmusic, NULL, &clock); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusic_Release(dmusic); + hr = IReferenceClock_GetTime(clock, &init_time); + ok(hr == S_OK, "got %#lx\n", hr); + IReferenceClock_Release(clock); + + + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 0, &time); + ok(hr == S_OK, "got %#lx\n", hr); + ok(time - init_time <= 100 * 10000, "got %I64d\n", time - init_time); + init_time = time; + + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 1, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time - init_time >= 6505, "got %I64d\n", time - init_time); + ok(time - init_time <= 6515, "got %I64d\n", time - init_time); + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 1000, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time - init_time >= 1000 * 6505, "got %I64d\n", time - init_time); + ok(time - init_time <= 1000 * 6515, "got %I64d\n", time - init_time); + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 2000, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time - init_time >= 2000 * 6505, "got %I64d\n", time - init_time); + ok(time - init_time <= 2000 * 6515, "got %I64d\n", time - init_time); + + music_time = 0xdeadbeef; + hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time, &music_time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(music_time == 0, "got %ld\n", music_time); + music_time = 0xdeadbeef; + hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + 1000 * 6510, &music_time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(music_time == 1000, "got %ld\n", music_time); + music_time = 0xdeadbeef; + hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + 2000 * 6510, &music_time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(music_time == 2000, "got %ld\n", music_time); + + time = 0xdeadbeef; + music_time = 0xdeadbeef; + hr = IDirectMusicPerformance_GetTime(performance, &time, &music_time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time - init_time <= 200 * 10000, "got %I64d\n", time - init_time); + todo_wine ok(music_time == (time - init_time) / 6510, "got %ld\n", music_time); + + + hr = IDirectMusicPerformance_CloseDown(performance); + ok(hr == S_OK, "got %#lx\n", hr); + + IDirectMusicPerformance_Release(performance); + +} + START_TEST(dmime) { CoInitialize(NULL); @@ -1626,6 +1716,7 @@ START_TEST(dmime) test_parsedescriptor(); test_performance_tool(); test_performance_graph(); + test_performance_time(); CoUninitialize(); } From b727baa13d280a64678f5078e09050e8364e3bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 16:27:41 +0200 Subject: [PATCH 0825/1148] dmime/tests: Test IDirectMusicPerformance_*PMsg methods. (cherry picked from commit e80c1d90f1c08c1d2ec2a84949d69ca3219b3d1d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 124 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 7fa832ebfd5..46751339994 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1691,6 +1691,129 @@ static void test_performance_time(void) } +static void test_performance_pmsg(void) +{ + IDirectMusicPerformance *performance; + IDirectMusicGraph *graph; + IDirectMusicTool *tool; + DMUS_PMSG *msg, *clone; + REFERENCE_TIME time; + HRESULT hr; + + hr = test_tool_create(&tool); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void **)&performance); + ok(hr == S_OK, "got %#lx\n", hr); + + + hr = IDirectMusicPerformance_AllocPMsg(performance, 0, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(DMUS_PMSG) - 1, &msg); + ok(hr == E_INVALIDARG, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(DMUS_PMSG), &msg); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); + ok(!msg->rtTime, "got %I64d\n", msg->rtTime); + ok(!msg->mtTime, "got %ld\n", msg->mtTime); + ok(!msg->dwFlags, "got %#lx\n", msg->dwFlags); + ok(!msg->dwPChannel, "got %ld\n", msg->dwPChannel); + ok(!msg->dwVirtualTrackID, "got %ld\n", msg->dwVirtualTrackID); + ok(!msg->dwType, "got %#lx\n", msg->dwType); + ok(!msg->dwVoiceID, "got %ld\n", msg->dwVoiceID); + ok(!msg->dwGroupID, "got %ld\n", msg->dwGroupID); + ok(!msg->punkUser, "got %p\n", msg->punkUser); + + hr = IDirectMusicPerformance_SendPMsg(performance, NULL); + ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SendPMsg(performance, msg); + todo_wine ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_FreePMsg(performance, NULL); + ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + + + hr = IDirectMusicPerformance_Init(performance, NULL, 0, 0); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicGraph, (void **)&graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SetGraph(performance, graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_InsertTool(graph, (IDirectMusicTool *)tool, NULL, 0, -1); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicGraph_Release(graph); + + hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(DMUS_PMSG), &msg); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); + ok(!msg->rtTime, "got %I64d\n", msg->rtTime); + ok(!msg->mtTime, "got %ld\n", msg->mtTime); + ok(!msg->dwFlags, "got %#lx\n", msg->dwFlags); + ok(!msg->dwPChannel, "got %ld\n", msg->dwPChannel); + ok(!msg->dwVirtualTrackID, "got %ld\n", msg->dwVirtualTrackID); + ok(!msg->pTool, "got %p\n", msg->pTool); + ok(!msg->pGraph, "got %p\n", msg->pGraph); + ok(!msg->dwType, "got %#lx\n", msg->dwType); + ok(!msg->dwVoiceID, "got %ld\n", msg->dwVoiceID); + ok(!msg->dwGroupID, "got %ld\n", msg->dwGroupID); + ok(!msg->punkUser, "got %p\n", msg->punkUser); + hr = IDirectMusicPerformance_SendPMsg(performance, msg); + todo_wine ok(hr == E_INVALIDARG, "got %#lx\n", hr); + + hr = IDirectMusicPerformance8_ClonePMsg((IDirectMusicPerformance8 *)performance, msg, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicPerformance8_ClonePMsg((IDirectMusicPerformance8 *)performance, NULL, &clone); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + clone = NULL; + hr = IDirectMusicPerformance8_ClonePMsg((IDirectMusicPerformance8 *)performance, msg, &clone); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(clone != NULL, "got %p\n", clone); + + msg->mtTime = 500; + msg->dwFlags = DMUS_PMSGF_MUSICTIME; + hr = IDirectMusicPerformance_SendPMsg(performance, msg); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SendPMsg(performance, msg); + ok(hr == DMUS_E_ALREADY_SENT, "got %#lx\n", hr); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == DMUS_E_CANNOT_FREE, "got %#lx\n", hr); + + if (!clone) hr = S_OK; + else hr = IDirectMusicPerformance_FreePMsg(performance, clone); + ok(hr == S_OK, "got %#lx\n", hr); + + + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, msg->mtTime, &time); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); + todo_wine ok(msg->rtTime == time, "got %I64d\n", msg->rtTime); + ok(msg->mtTime == 500, "got %ld\n", msg->mtTime); + todo_wine ok(msg->dwFlags & DMUS_PMSGF_REFTIME, "got %#lx\n", msg->dwFlags); + todo_wine ok(msg->dwFlags & (DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_IMMEDIATE), "got %#lx\n", msg->dwFlags); + ok(!msg->dwPChannel, "got %ld\n", msg->dwPChannel); + ok(!msg->dwVirtualTrackID, "got %ld\n", msg->dwVirtualTrackID); + ok(!msg->pTool, "got %p\n", msg->pTool); + ok(!msg->pGraph, "got %p\n", msg->pGraph); + ok(!msg->dwType, "got %#lx\n", msg->dwType); + ok(!msg->dwVoiceID, "got %ld\n", msg->dwVoiceID); + ok(!msg->dwGroupID, "got %ld\n", msg->dwGroupID); + ok(!msg->punkUser, "got %p\n", msg->punkUser); + + + hr = IDirectMusicPerformance_CloseDown(performance); + ok(hr == S_OK, "got %#lx\n", hr); + + + IDirectMusicPerformance_Release(performance); +} + START_TEST(dmime) { CoInitialize(NULL); @@ -1717,6 +1840,7 @@ START_TEST(dmime) test_performance_tool(); test_performance_graph(); test_performance_time(); + test_performance_pmsg(); CoUninitialize(); } From 12c571c97bee5a5a2767902fbb05c9b3287b9cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 18:37:02 +0200 Subject: [PATCH 0826/1148] dmime/tests: Test IDirectMusicTool message reception filter. (cherry picked from commit 2ac84e2b94342618bcffb4ab7ba6ec5ee5703f24) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 99 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 8 deletions(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 46751339994..a259ea599b8 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -61,6 +61,11 @@ struct test_tool LONG ref; IDirectMusicGraph *graph; + const DWORD *types; + DWORD types_count; + + HANDLE message_event; + DMUS_PMSG *message; }; static struct test_tool *impl_from_IDirectMusicTool(IDirectMusicTool *iface) @@ -98,6 +103,8 @@ static ULONG WINAPI test_tool_Release(IDirectMusicTool *iface) if (!ref) { if (tool->graph) IDirectMusicGraph_Release(tool->graph); + ok(!tool->message, "got %p\n", tool->message); + CloseHandle(tool->message_event); free(tool); } @@ -119,19 +126,35 @@ static HRESULT WINAPI test_tool_GetMsgDeliveryType(IDirectMusicTool *iface, DWOR static HRESULT WINAPI test_tool_GetMediaTypeArraySize(IDirectMusicTool *iface, DWORD *size) { - *size = 0; + struct test_tool *tool = impl_from_IDirectMusicTool(iface); + *size = tool->types_count; return S_OK; } static HRESULT WINAPI test_tool_GetMediaTypes(IDirectMusicTool *iface, DWORD **types, DWORD size) { - ok(0, "%#04lx: unexpected %s, types %p, size %lu\n", GetCurrentThreadId(), __func__, types, size); + struct test_tool *tool = impl_from_IDirectMusicTool(iface); + UINT i; + for (i = 0; i < tool->types_count; i++) (*types)[i] = tool->types[i]; return S_OK; } static HRESULT WINAPI test_tool_ProcessPMsg(IDirectMusicTool *iface, IDirectMusicPerformance *performance, DMUS_PMSG *msg) { - ok(0, "%#04lx: unexpected %s, iface %p, performance %p, msg %p\n", GetCurrentThreadId(), __func__, iface, performance, msg); + struct test_tool *tool = impl_from_IDirectMusicTool(iface); + DMUS_PMSG *clone; + HRESULT hr; + + hr = IDirectMusicPerformance8_ClonePMsg((IDirectMusicPerformance8 *)performance, msg, &clone); + ok(hr == S_OK, "got %#lx\n", hr); + + clone = InterlockedExchangePointer((void **)&tool->message, clone); + ok(!clone, "got %p\n", clone); + SetEvent(tool->message_event); + + hr = IDirectMusicGraph_StampPMsg(msg->pGraph, msg); + ok(hr == S_OK, "got %#lx\n", hr); + return DMUS_S_REQUEUE; } @@ -155,7 +178,8 @@ static IDirectMusicToolVtbl test_tool_vtbl = test_tool_Flush, }; -static HRESULT test_tool_create(IDirectMusicTool **ret_iface) +static HRESULT test_tool_create(const DWORD *types, DWORD types_count, + IDirectMusicTool **ret_iface) { struct test_tool *tool; @@ -164,6 +188,11 @@ static HRESULT test_tool_create(IDirectMusicTool **ret_iface) tool->IDirectMusicTool_iface.lpVtbl = &test_tool_vtbl; tool->ref = 1; + tool->types = types; + tool->types_count = types_count; + tool->message_event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!tool->message_event, "CreateEventW failed, error %lu\n", GetLastError()); + *ret_iface = &tool->IDirectMusicTool_iface; return S_OK; } @@ -175,6 +204,17 @@ static HRESULT test_tool_get_graph(IDirectMusicTool *iface, IDirectMusicGraph ** return tool->graph ? S_OK : DMUS_E_NOT_FOUND; } +static DWORD test_tool_wait_message(IDirectMusicTool *iface, DWORD timeout, DMUS_PMSG **msg) +{ + struct test_tool *tool = impl_from_IDirectMusicTool(iface); + DWORD ret; + + ret = WaitForSingleObject(tool->message_event, timeout); + *msg = InterlockedExchangePointer((void **)&tool->message, NULL); + + return ret; +} + static BOOL missing_dmime(void) { IDirectMusicSegment8 *dms; @@ -638,10 +678,10 @@ static void test_graph(void) IDirectMusicGraph_Release(graph); - hr = test_tool_create(&tool1); + hr = test_tool_create(NULL, 0, &tool1); ok(hr == S_OK, "got %#lx\n", hr); trace("created tool1 %p\n", tool1); - hr = test_tool_create(&tool2); + hr = test_tool_create(NULL, 0, &tool2); ok(hr == S_OK, "got %#lx\n", hr); trace("created tool2 %p\n", tool2); @@ -1486,7 +1526,7 @@ static void test_performance_graph(void) DMUS_PMSG msg; HRESULT hr; - hr = test_tool_create(&tool); + hr = test_tool_create(NULL, 0, &tool); ok(hr == S_OK, "got %#lx\n", hr); hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, @@ -1693,14 +1733,16 @@ static void test_performance_time(void) static void test_performance_pmsg(void) { + static const DWORD message_types[] = {DMUS_PMSGT_MIDI, DMUS_PMSGT_USER}; IDirectMusicPerformance *performance; IDirectMusicGraph *graph; IDirectMusicTool *tool; DMUS_PMSG *msg, *clone; REFERENCE_TIME time; HRESULT hr; + DWORD ret; - hr = test_tool_create(&tool); + hr = test_tool_create(message_types, ARRAY_SIZE(message_types), &tool); ok(hr == S_OK, "got %#lx\n", hr); hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, @@ -1807,6 +1849,47 @@ static void test_performance_pmsg(void) ok(!msg->punkUser, "got %p\n", msg->punkUser); + /* SendPMsg skips all the tools unless messages are stamped beforehand */ + + hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(DMUS_PMSG), &msg); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); + msg->rtTime = time; + msg->dwFlags = DMUS_PMSGF_REFTIME; + msg->dwType = DMUS_PMSGT_USER; + hr = IDirectMusicPerformance_SendPMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 10, &msg); + ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); + ok(!msg, "got %p\n", msg); + + + hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(DMUS_PMSG), &msg); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); + msg->rtTime = time; + msg->dwFlags = DMUS_PMSGF_REFTIME; + msg->dwType = DMUS_PMSGT_USER; + + graph = NULL; + hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (graph) hr = IDirectMusicGraph_StampPMsg(graph, msg); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (graph) IDirectMusicGraph_Release(graph); + + hr = IDirectMusicPerformance_SendPMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 50, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + todo_wine ok(msg != NULL, "got %p\n", msg); + if (!msg) hr = S_OK; + else hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_CloseDown(performance); ok(hr == S_OK, "got %#lx\n", hr); From 769ee61e45bc5edb15e9529d39838cd30c9cd75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 7 Sep 2023 10:21:31 +0200 Subject: [PATCH 0827/1148] dmime/tests: Test IDirectMusicTool message reception delay. (cherry picked from commit 5e68fa63944fbea2943de9aa651fb319eac8e613) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index a259ea599b8..f00b5073b48 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1733,6 +1733,7 @@ static void test_performance_time(void) static void test_performance_pmsg(void) { + static const DWORD delivery_flags[] = {DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGF_TOOL_QUEUE, DMUS_PMSGF_TOOL_ATTIME}; static const DWORD message_types[] = {DMUS_PMSGT_MIDI, DMUS_PMSGT_USER}; IDirectMusicPerformance *performance; IDirectMusicGraph *graph; @@ -1741,6 +1742,7 @@ static void test_performance_pmsg(void) REFERENCE_TIME time; HRESULT hr; DWORD ret; + UINT i; hr = test_tool_create(message_types, ARRAY_SIZE(message_types), &tool); ok(hr == S_OK, "got %#lx\n", hr); @@ -1890,6 +1892,50 @@ static void test_performance_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); + for (i = 0; i < ARRAY_SIZE(delivery_flags); i++) + { + DWORD duration = 0; + + hr = IDirectMusicPerformance_GetTime(performance, &time, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(DMUS_PMSG), &msg); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); + msg->rtTime = time + 150 * 10000; + msg->dwFlags = DMUS_PMSGF_REFTIME; + msg->dwType = DMUS_PMSGT_USER; + + hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (!graph) hr = S_OK; + else hr = IDirectMusicGraph_StampPMsg(graph, msg); + ok(hr == S_OK, "got %#lx\n", hr); + if (graph) IDirectMusicGraph_Release(graph); + + msg->dwFlags &= ~(DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME); + msg->dwFlags |= delivery_flags[i]; + + duration -= GetTickCount(); + hr = IDirectMusicPerformance_SendPMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + msg = NULL; + ret = test_tool_wait_message(tool, 1000, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + todo_wine ok(msg != NULL, "got %p\n", msg); + duration += GetTickCount(); + + if (msg) hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + + switch (delivery_flags[i]) + { + case DMUS_PMSGF_TOOL_IMMEDIATE: todo_wine ok(duration <= 50, "got %lu\n", duration); break; + case DMUS_PMSGF_TOOL_QUEUE: todo_wine ok(duration >= 50 && duration <= 100, "got %lu\n", duration); break; + case DMUS_PMSGF_TOOL_ATTIME: todo_wine ok(duration >= 150 && duration <= 500, "got %lu\n", duration); break; + } + } + + hr = IDirectMusicPerformance_CloseDown(performance); ok(hr == S_OK, "got %#lx\n", hr); From 64696f0f6136b2a44e991f03f1cad67b0449b516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 7 Sep 2023 11:14:35 +0200 Subject: [PATCH 0828/1148] dmsynth/tests: Test IDirectMusicSynth_Unload. (cherry picked from commit 971d7072738f3394aae42fc4ea6b7e1d8b0c938c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/tests/dmsynth.c | 47 ++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 46727afe557..e696ca34513 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -830,6 +830,22 @@ static void test_sink_render(IDirectMusicSynthSink *iface, void *buffer, DWORD b } } +static BOOL unload_called; + +static HRESULT CALLBACK test_unload_callback(HANDLE handle, HANDLE user_data) +{ + ok(!!handle, "got %p\n", handle); + ok(user_data == (HANDLE)0xdeadbeef, "got %p\n", user_data); + unload_called = TRUE; + return E_FAIL; +} + +static HRESULT CALLBACK test_unload_no_callback(HANDLE handle, HANDLE user_data) +{ + ok(0, "unexpected %s\n", __func__); + return E_FAIL; +} + static void test_IDirectMusicSynth(void) { static const UINT RENDER_ITERATIONS = 8; @@ -943,7 +959,7 @@ static void test_IDirectMusicSynth(void) IDirectMusicBuffer *buffer; DWORD format_size, written; IDirectMusicSynth *synth; - HANDLE wave_file, handle; + HANDLE wave_file, wave_handle, instrument_handle; IReferenceClock *clock; BOOL can_free = FALSE; REFERENCE_TIME time; @@ -1095,17 +1111,17 @@ static void test_IDirectMusicSynth(void) wave_download.samples[i] = i; can_free = 0xdeadbeef; - handle = (HANDLE)0xdeadbeef; - hr = IDirectMusicSynth_Download(synth, &handle, &wave_download, &can_free); + wave_handle = NULL; + hr = IDirectMusicSynth_Download(synth, &wave_handle, &wave_download, &can_free); ok(hr == S_OK, "got %#lx\n", hr); - ok(handle != 0, "got %p\n", handle); + todo_wine ok(!!wave_handle, "got %p\n", wave_handle); todo_wine ok(can_free == FALSE, "got %u\n", can_free); can_free = 0xdeadbeef; - handle = (HANDLE)0xdeadbeef; - hr = IDirectMusicSynth_Download(synth, &handle, &instrument_download, &can_free); + instrument_handle = NULL; + hr = IDirectMusicSynth_Download(synth, &instrument_handle, &instrument_download, &can_free); ok(hr == S_OK, "got %#lx\n", hr); - ok(handle != 0, "got %p\n", handle); + todo_wine ok(!!instrument_handle, "got %p\n", instrument_handle); todo_wine ok(can_free == TRUE, "got %u\n", can_free); /* add a MIDI note to a buffer and play it */ @@ -1133,15 +1149,32 @@ static void test_IDirectMusicSynth(void) CloseHandle(wave_file); trace("Rendered samples to %s\n", debugstr_w(temp_file)); + hr = IDirectMusicSynth_Activate(synth, FALSE); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicSynth_SetSynthSink(synth, NULL); ok(hr == S_OK, "got %#lx\n", hr); ref = get_refcount(sink); ok(ref == 1, "got %lu\n", ref); + + hr = IDirectMusicSynth_Unload(synth, 0, NULL, NULL); + todo_wine ok(hr == E_FAIL, "got %#lx\n", hr); + hr = IDirectMusicSynth_Unload(synth, (HANDLE)0xdeadbeef, test_unload_no_callback, (HANDLE)0xdeadbeef); + todo_wine ok(hr == E_FAIL, "got %#lx\n", hr); + hr = IDirectMusicSynth_Unload(synth, wave_handle, test_unload_callback, (HANDLE)0xdeadbeef); + ok(hr == S_OK, "got %#lx\n", hr); + ok(!unload_called, "callback called\n"); + hr = IDirectMusicSynth_Unload(synth, instrument_handle, NULL, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(unload_called, "callback not called\n"); + hr = IDirectMusicSynth_Close(synth); ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSynth_Unload(synth, 0, NULL, NULL); + todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + + IDirectMusicSynth_Release(synth); From 54409b2d468a75e44793fce35d97e8f8ce6aaa00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 11:38:09 +0200 Subject: [PATCH 0829/1148] dmusic: Avoid passing invalid handle to IDirectMusicSynth_Unload. (cherry picked from commit 82436b1ee8f7b893b91c7cc6c276a47bba1ee25f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/port.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index 95857ea55ac..2f8556d149b 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -707,6 +707,7 @@ static HRESULT WINAPI synth_port_download_Unload(IDirectMusicPortDownload *iface } } + if (!handle) return S_OK; return IDirectMusicSynth_Unload(This->synth, handle, NULL, NULL); } From 47bf418f0a6b20815ce1ffdeb1dfc16deba72373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 11:38:09 +0200 Subject: [PATCH 0830/1148] dmsynth: Implement IDirectMusicSynth_(Download|Unload) for instruments. (cherry picked from commit 56fc711a936781837165d14794fa0e9086e9641e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 229 ++++++++++++++++++----------------- dlls/dmsynth/tests/dmsynth.c | 10 +- 2 files changed, 121 insertions(+), 118 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index a93b23133f4..794e341046a 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -31,6 +31,35 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); +static void dump_dmus_instrument(DMUS_INSTRUMENT *instrument) +{ + TRACE("DMUS_INSTRUMENT:\n"); + TRACE(" - ulPatch = %lu\n", instrument->ulPatch); + TRACE(" - ulFirstRegionIdx = %lu\n", instrument->ulFirstRegionIdx); + TRACE(" - ulGlobalArtIdx = %lu\n", instrument->ulGlobalArtIdx); + TRACE(" - ulFirstExtCkIdx = %lu\n", instrument->ulFirstExtCkIdx); + TRACE(" - ulCopyrightIdx = %lu\n", instrument->ulCopyrightIdx); + TRACE(" - ulFlags = %lu\n", instrument->ulFlags); +} + +struct instrument +{ + struct list entry; + LONG ref; + UINT id; + + UINT patch; + UINT flags; + + struct synth *synth; +}; + +static void instrument_release(struct instrument *instrument) +{ + ULONG ref = InterlockedDecrement(&instrument->ref); + if (!ref) free(instrument); +} + struct synth { IDirectMusicSynth8 IDirectMusicSynth8_iface; @@ -42,6 +71,8 @@ struct synth BOOL active; BOOL open; IDirectMusicSynthSink *sink; + + struct list instruments; }; static inline struct synth *impl_from_IDirectMusicSynth8(IDirectMusicSynth8 *iface) @@ -95,7 +126,19 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) TRACE("(%p): new ref = %lu\n", This, ref); - if (!ref) free(This); + if (!ref) + { + struct instrument *instrument; + void *next; + + LIST_FOR_EACH_ENTRY_SAFE(instrument, next, &This->instruments, struct instrument, entry) + { + list_remove(&instrument->entry); + instrument_release(instrument); + } + + free(This); + } return ref; } @@ -210,134 +253,80 @@ static HRESULT WINAPI synth_SetNumChannelGroups(IDirectMusicSynth8 *iface, return S_OK; } -static HRESULT WINAPI synth_Download(IDirectMusicSynth8 *iface, HANDLE *hDownload, - void *data, BOOL *free) +static HRESULT synth_download_instrument(struct synth *This, DMUS_DOWNLOADINFO *info, ULONG *offsets, + BYTE *data, HANDLE *ret_handle) { - struct synth *This = impl_from_IDirectMusicSynth8(iface); - LPBYTE buffer = data; - DMUS_DOWNLOADINFO *info = (DMUS_DOWNLOADINFO*)buffer; - ULONG *offsets = ((DMUS_OFFSETTABLE*)(buffer + sizeof(DMUS_DOWNLOADINFO)))->ulOffsetTable; - LPBYTE object = buffer + sizeof(DMUS_DOWNLOADINFO) + info->dwNumOffsetTableEntries * sizeof(ULONG); - - FIXME("(%p)->(%p, %p, %p): stub\n", This, hDownload, data, free); - - /* FIXME: Currently we only dump data which is very useful to known how native dmusic behave and debug builtin dmusic */ - - if (!hDownload || !free) - return E_POINTER; + DMUS_INSTRUMENT *instrument_info = (DMUS_INSTRUMENT *)(data + offsets[0]); + struct instrument *instrument; if (TRACE_ON(dmsynth)) { - TRACE("Dump DMUS_DOWNLOADINFO struct:\n"); - TRACE(" - dwDLType = %lu\n", info->dwDLType); - TRACE(" - dwDLId = %lu\n", info->dwDLId); - TRACE(" - dwNumOffsetTableEntries = %lu\n", info->dwNumOffsetTableEntries); - TRACE(" - cbSize = %lu\n", info->cbSize); - } + dump_dmus_instrument(instrument_info); - /* The struct should have at least one offset corresponding to the download object itself */ - if (!info->dwNumOffsetTableEntries) - { - FIXME("Offset table is empty\n"); - return DMUS_E_BADOFFSETTABLE; - } - - /* First offset should point to the download object */ - if ((buffer + offsets[0]) != object) - { - FIXME("Object is not at the beginning\n"); - return DMUS_E_BADOFFSETTABLE; + if (instrument_info->ulCopyrightIdx) + { + DMUS_COPYRIGHT *copyright = (DMUS_COPYRIGHT *)(data + offsets[instrument_info->ulCopyrightIdx]); + TRACE("Copyright = '%s'\n", (char *)copyright->byCopyright); + } } - if (info->dwDLType == DMUS_DOWNLOADINFO_INSTRUMENT) - { - FIXME("Download type DMUS_DOWNLOADINFO_INSTRUMENT not yet supported\n"); - } - else if (info->dwDLType == DMUS_DOWNLOADINFO_WAVE) - { - DMUS_WAVE *wave = (DMUS_WAVE*)object; - DMUS_WAVEDATA *wave_data; + if (instrument_info->ulFirstExtCkIdx) FIXME("Instrument extensions not implemented\n"); - TRACE("Processing download type DMUS_DOWNLOADINFO_WAVE\n"); + if (!(instrument = calloc(1, sizeof(*instrument)))) return E_OUTOFMEMORY; + instrument->ref = 1; + instrument->id = info->dwDLId; + instrument->patch = instrument_info->ulPatch; + instrument->flags = instrument_info->ulFlags; + instrument->synth = This; - if (TRACE_ON(dmsynth)) - { - TRACE("Dump DMUS_WAVE struct\n"); - TRACE(" - ulFirstExtCkIdx = %lu\n", wave->ulFirstExtCkIdx); - TRACE(" - ulCopyrightIdx = %lu\n", wave->ulCopyrightIdx); - TRACE(" - ulWaveDataIdx = %lu\n", wave->ulWaveDataIdx); - TRACE(" - WaveformatEx:\n"); - TRACE(" - wFormatTag = %u\n", wave->WaveformatEx.wFormatTag); - TRACE(" - nChannels = %u\n", wave->WaveformatEx.nChannels); - TRACE(" - nSamplesPerSec = %lu\n", wave->WaveformatEx.nSamplesPerSec); - TRACE(" - nAvgBytesPerSec = %lu\n", wave->WaveformatEx.nAvgBytesPerSec); - TRACE(" - nBlockAlign = %u\n", wave->WaveformatEx.nBlockAlign); - TRACE(" - wBitsPerSample = %u\n", wave->WaveformatEx.wBitsPerSample); - TRACE(" - cbSize = %u\n", wave->WaveformatEx.cbSize); - - if (wave->ulCopyrightIdx) - { - DMUS_COPYRIGHT *copyright = (DMUS_COPYRIGHT*)(buffer + offsets[wave->ulCopyrightIdx]); - TRACE("Copyright = '%s'\n", (char*)copyright->byCopyright); - } + list_add_tail(&This->instruments, &instrument->entry); + *ret_handle = instrument; - wave_data = (DMUS_WAVEDATA*)(buffer + offsets[wave->ulWaveDataIdx]); - TRACE("Found %lu bytes of wave data\n", wave_data->cbSize); - } - } - else if (info->dwDLType == DMUS_DOWNLOADINFO_INSTRUMENT2) - { - DMUS_INSTRUMENT *instrument = (DMUS_INSTRUMENT*)object; - ULONG nb_regions = 0; + return S_OK; +} - TRACE("Processing download type DMUS_DOWNLOADINFO_INSTRUMENT2\n"); +static HRESULT WINAPI synth_Download(IDirectMusicSynth8 *iface, HANDLE *ret_handle, void *data, BOOL *ret_free) +{ + struct synth *This = impl_from_IDirectMusicSynth8(iface); + DMUS_DOWNLOADINFO *info = data; + ULONG *offsets = (ULONG *)(info + 1); - if (TRACE_ON(dmsynth)) - { - TRACE("Dump DMUS_INSTRUMENT struct\n"); - TRACE(" - ulPatch = %lu\n", instrument->ulPatch); - TRACE(" - ulFirstRegionIdx = %lu\n", instrument->ulFirstRegionIdx); - TRACE(" - ulGlobalArtIdx = %lu\n", instrument->ulGlobalArtIdx); - TRACE(" - ulFirstExtCkIdx = %lu\n", instrument->ulFirstExtCkIdx); - TRACE(" - ulCopyrightIdx = %lu\n", instrument->ulCopyrightIdx); - TRACE(" - ulFlags = %lu\n", instrument->ulFlags); - - if (instrument->ulCopyrightIdx) - { - DMUS_COPYRIGHT *copyright = (DMUS_COPYRIGHT*)(buffer + offsets[instrument->ulCopyrightIdx]); - TRACE("Copyright = '%s'\n", (char*)copyright->byCopyright); - } - } + FIXME("(%p)->(%p, %p, %p): stub\n", This, ret_handle, data, free); - if (instrument->ulFirstRegionIdx) - { - ULONG region_idx = instrument->ulFirstRegionIdx; + if (!ret_handle || !data || !ret_free) return E_POINTER; + *ret_handle = 0; + *ret_free = TRUE; - while (region_idx) - { - DMUS_REGION *region = (DMUS_REGION*)(buffer + offsets[region_idx]); + if (TRACE_ON(dmsynth)) + { + TRACE("Dump DMUS_DOWNLOADINFO struct:\n"); + TRACE(" - dwDLType = %lu\n", info->dwDLType); + TRACE(" - dwDLId = %lu\n", info->dwDLId); + TRACE(" - dwNumOffsetTableEntries = %lu\n", info->dwNumOffsetTableEntries); + TRACE(" - cbSize = %lu\n", info->cbSize); + } - region_idx = region->ulNextRegionIdx; - nb_regions++; - } - } + if (!info->dwNumOffsetTableEntries) return DMUS_E_BADOFFSETTABLE; + if (((BYTE *)data + offsets[0]) != (BYTE *)(offsets + info->dwNumOffsetTableEntries)) return DMUS_E_BADOFFSETTABLE; - TRACE("Number of regions = %lu\n", nb_regions); - } - else if (info->dwDLType == DMUS_DOWNLOADINFO_WAVEARTICULATION) + switch (info->dwDLType) { + case DMUS_DOWNLOADINFO_INSTRUMENT: + case DMUS_DOWNLOADINFO_INSTRUMENT2: + return synth_download_instrument(This, info, offsets, data, ret_handle); + case DMUS_DOWNLOADINFO_WAVE: + FIXME("Download type DMUS_DOWNLOADINFO_WAVE not yet supported\n"); + return S_OK; + case DMUS_DOWNLOADINFO_WAVEARTICULATION: FIXME("Download type DMUS_DOWNLOADINFO_WAVEARTICULATION not yet supported\n"); - } - else if (info->dwDLType == DMUS_DOWNLOADINFO_STREAMINGWAVE) - { + return E_NOTIMPL; + case DMUS_DOWNLOADINFO_STREAMINGWAVE: FIXME("Download type DMUS_DOWNLOADINFO_STREAMINGWAVE not yet supported\n"); - } - else if (info->dwDLType == DMUS_DOWNLOADINFO_ONESHOTWAVE) - { + return E_NOTIMPL; + case DMUS_DOWNLOADINFO_ONESHOTWAVE: FIXME("Download type DMUS_DOWNLOADINFO_ONESHOTWAVE not yet supported\n"); - } - else - { + return E_NOTIMPL; + default: WARN("Unknown download type %lu\n", info->dwDLType); return DMUS_E_UNKNOWNDOWNLOAD; } @@ -345,14 +334,26 @@ static HRESULT WINAPI synth_Download(IDirectMusicSynth8 *iface, HANDLE *hDownloa return S_OK; } -static HRESULT WINAPI synth_Unload(IDirectMusicSynth8 *iface, HANDLE hDownload, - HRESULT (CALLBACK *lpFreeHandle)(HANDLE,HANDLE), HANDLE hUserData) +static HRESULT WINAPI synth_Unload(IDirectMusicSynth8 *iface, HANDLE handle, + HRESULT (CALLBACK *callback)(HANDLE, HANDLE), HANDLE user_data) { struct synth *This = impl_from_IDirectMusicSynth8(iface); + struct instrument *instrument; - FIXME("(%p)->(%p, %p, %p): stub\n", This, hDownload, lpFreeHandle, hUserData); + TRACE("(%p)->(%p, %p, %p)\n", This, handle, callback, user_data); + if (callback) FIXME("Unload callbacks not implemented\n"); - return S_OK; + LIST_FOR_EACH_ENTRY(instrument, &This->instruments, struct instrument, entry) + { + if (instrument == handle) + { + list_remove(&instrument->entry); + instrument_release(instrument); + return S_OK; + } + } + + return E_FAIL; } static HRESULT WINAPI synth_PlayBuffer(IDirectMusicSynth8 *iface, @@ -745,6 +746,8 @@ HRESULT synth_create(IUnknown **ret_iface) obj->caps.dwEffectFlags = DMUS_EFFECT_REVERB; lstrcpyW(obj->caps.wszDescription, L"Microsoft Synthesizer"); + list_init(&obj->instruments); + TRACE("Created DirectMusicSynth %p\n", obj); *ret_iface = (IUnknown *)&obj->IDirectMusicSynth8_iface; return S_OK; diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index e696ca34513..7120d1e06c2 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1121,8 +1121,8 @@ static void test_IDirectMusicSynth(void) instrument_handle = NULL; hr = IDirectMusicSynth_Download(synth, &instrument_handle, &instrument_download, &can_free); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(!!instrument_handle, "got %p\n", instrument_handle); - todo_wine ok(can_free == TRUE, "got %u\n", can_free); + ok(!!instrument_handle, "got %p\n", instrument_handle); + ok(can_free == TRUE, "got %u\n", can_free); /* add a MIDI note to a buffer and play it */ hr = IDirectMusicSynth_GetLatencyClock(synth, &latency_clock); @@ -1158,11 +1158,11 @@ static void test_IDirectMusicSynth(void) ok(ref == 1, "got %lu\n", ref); hr = IDirectMusicSynth_Unload(synth, 0, NULL, NULL); - todo_wine ok(hr == E_FAIL, "got %#lx\n", hr); + ok(hr == E_FAIL, "got %#lx\n", hr); hr = IDirectMusicSynth_Unload(synth, (HANDLE)0xdeadbeef, test_unload_no_callback, (HANDLE)0xdeadbeef); - todo_wine ok(hr == E_FAIL, "got %#lx\n", hr); + ok(hr == E_FAIL, "got %#lx\n", hr); hr = IDirectMusicSynth_Unload(synth, wave_handle, test_unload_callback, (HANDLE)0xdeadbeef); - ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); ok(!unload_called, "callback called\n"); hr = IDirectMusicSynth_Unload(synth, instrument_handle, NULL, NULL); ok(hr == S_OK, "got %#lx\n", hr); From 73e39ac60c052929b22618cb39340fa01104cfe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 8 Sep 2023 08:26:01 +0200 Subject: [PATCH 0831/1148] dmsynth: Implement IDirectMusicSynth_(Download|Unload) for waves. (cherry picked from commit 22f85dd9b8b5d6891f2b7bd1027b8dcb6b1e722e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 120 ++++++++++++++++++++++++++++++++++- dlls/dmsynth/tests/dmsynth.c | 4 +- 2 files changed, 120 insertions(+), 4 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 794e341046a..e6f47b660c5 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -42,6 +42,41 @@ static void dump_dmus_instrument(DMUS_INSTRUMENT *instrument) TRACE(" - ulFlags = %lu\n", instrument->ulFlags); } +static void dump_dmus_wave(DMUS_WAVE *wave) +{ + TRACE("DMUS_WAVE:\n"); + TRACE(" - ulFirstExtCkIdx = %lu\n", wave->ulFirstExtCkIdx); + TRACE(" - ulCopyrightIdx = %lu\n", wave->ulCopyrightIdx); + TRACE(" - ulWaveDataIdx = %lu\n", wave->ulWaveDataIdx); + TRACE(" - WaveformatEx:\n"); + TRACE(" - wFormatTag = %u\n", wave->WaveformatEx.wFormatTag); + TRACE(" - nChannels = %u\n", wave->WaveformatEx.nChannels); + TRACE(" - nSamplesPerSec = %lu\n", wave->WaveformatEx.nSamplesPerSec); + TRACE(" - nAvgBytesPerSec = %lu\n", wave->WaveformatEx.nAvgBytesPerSec); + TRACE(" - nBlockAlign = %u\n", wave->WaveformatEx.nBlockAlign); + TRACE(" - wBitsPerSample = %u\n", wave->WaveformatEx.wBitsPerSample); + TRACE(" - cbSize = %u\n", wave->WaveformatEx.cbSize); +} + +struct wave +{ + struct list entry; + LONG ref; + UINT id; + + WAVEFORMATEX format; + UINT sample_count; + short samples[]; +}; + +C_ASSERT(sizeof(struct wave) == offsetof(struct wave, samples[0])); + +static void wave_release(struct wave *wave) +{ + ULONG ref = InterlockedDecrement(&wave->ref); + if (!ref) free(wave); +} + struct instrument { struct list entry; @@ -73,6 +108,7 @@ struct synth IDirectMusicSynthSink *sink; struct list instruments; + struct list waves; }; static inline struct synth *impl_from_IDirectMusicSynth8(IDirectMusicSynth8 *iface) @@ -129,6 +165,7 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) if (!ref) { struct instrument *instrument; + struct wave *wave; void *next; LIST_FOR_EACH_ENTRY_SAFE(instrument, next, &This->instruments, struct instrument, entry) @@ -137,6 +174,12 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) instrument_release(instrument); } + LIST_FOR_EACH_ENTRY_SAFE(wave, next, &This->waves, struct wave, entry) + { + list_remove(&wave->entry); + wave_release(wave); + } + free(This); } @@ -285,6 +328,68 @@ static HRESULT synth_download_instrument(struct synth *This, DMUS_DOWNLOADINFO * return S_OK; } +static HRESULT synth_download_wave(struct synth *This, DMUS_DOWNLOADINFO *info, ULONG *offsets, + BYTE *data, HANDLE *ret_handle) +{ + DMUS_WAVE *wave_info = (DMUS_WAVE *)(data + offsets[0]); + DMUS_WAVEDATA *wave_data = (DMUS_WAVEDATA *)(data + offsets[wave_info->ulWaveDataIdx]); + struct wave *wave; + UINT sample_count; + + if (TRACE_ON(dmsynth)) + { + dump_dmus_wave(wave_info); + + if (wave_info->ulCopyrightIdx) + { + DMUS_COPYRIGHT *copyright = (DMUS_COPYRIGHT *)(data + offsets[wave_info->ulCopyrightIdx]); + TRACE("Copyright = '%s'\n", (char *)copyright->byCopyright); + } + + TRACE("Found %lu bytes of wave data\n", wave_data->cbSize); + } + + if (wave_info->ulFirstExtCkIdx) FIXME("Wave extensions not implemented\n"); + if (wave_info->WaveformatEx.wFormatTag != WAVE_FORMAT_PCM) return DMUS_E_NOTPCM; + + sample_count = wave_data->cbSize / wave_info->WaveformatEx.nBlockAlign; + if (!(wave = calloc(1, offsetof(struct wave, samples[sample_count])))) return E_OUTOFMEMORY; + wave->ref = 1; + wave->id = info->dwDLId; + wave->format = wave_info->WaveformatEx; + wave->sample_count = sample_count; + + if (wave_info->WaveformatEx.nBlockAlign == 1) + { + while (sample_count--) + { + short sample = (wave_data->byData[sample_count] - 0x80) << 8; + wave->samples[sample_count] = sample; + } + } + else if (wave_info->WaveformatEx.nBlockAlign == 2) + { + while (sample_count--) + { + short sample = ((short *)wave_data->byData)[sample_count]; + wave->samples[sample_count] = sample; + } + } + else if (wave_info->WaveformatEx.nBlockAlign == 4) + { + while (sample_count--) + { + short sample = ((UINT *)wave_data->byData)[sample_count] >> 16; + wave->samples[sample_count] = sample; + } + } + + list_add_tail(&This->waves, &wave->entry); + *ret_handle = wave; + + return S_OK; +} + static HRESULT WINAPI synth_Download(IDirectMusicSynth8 *iface, HANDLE *ret_handle, void *data, BOOL *ret_free) { struct synth *This = impl_from_IDirectMusicSynth8(iface); @@ -315,8 +420,7 @@ static HRESULT WINAPI synth_Download(IDirectMusicSynth8 *iface, HANDLE *ret_hand case DMUS_DOWNLOADINFO_INSTRUMENT2: return synth_download_instrument(This, info, offsets, data, ret_handle); case DMUS_DOWNLOADINFO_WAVE: - FIXME("Download type DMUS_DOWNLOADINFO_WAVE not yet supported\n"); - return S_OK; + return synth_download_wave(This, info, offsets, data, ret_handle); case DMUS_DOWNLOADINFO_WAVEARTICULATION: FIXME("Download type DMUS_DOWNLOADINFO_WAVEARTICULATION not yet supported\n"); return E_NOTIMPL; @@ -339,6 +443,7 @@ static HRESULT WINAPI synth_Unload(IDirectMusicSynth8 *iface, HANDLE handle, { struct synth *This = impl_from_IDirectMusicSynth8(iface); struct instrument *instrument; + struct wave *wave; TRACE("(%p)->(%p, %p, %p)\n", This, handle, callback, user_data); if (callback) FIXME("Unload callbacks not implemented\n"); @@ -353,6 +458,16 @@ static HRESULT WINAPI synth_Unload(IDirectMusicSynth8 *iface, HANDLE handle, } } + LIST_FOR_EACH_ENTRY(wave, &This->waves, struct wave, entry) + { + if (wave == handle) + { + list_remove(&wave->entry); + wave_release(wave); + return S_OK; + } + } + return E_FAIL; } @@ -747,6 +862,7 @@ HRESULT synth_create(IUnknown **ret_iface) lstrcpyW(obj->caps.wszDescription, L"Microsoft Synthesizer"); list_init(&obj->instruments); + list_init(&obj->waves); TRACE("Created DirectMusicSynth %p\n", obj); *ret_iface = (IUnknown *)&obj->IDirectMusicSynth8_iface; diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 7120d1e06c2..51504d68a17 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1114,7 +1114,7 @@ static void test_IDirectMusicSynth(void) wave_handle = NULL; hr = IDirectMusicSynth_Download(synth, &wave_handle, &wave_download, &can_free); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(!!wave_handle, "got %p\n", wave_handle); + ok(!!wave_handle, "got %p\n", wave_handle); todo_wine ok(can_free == FALSE, "got %u\n", can_free); can_free = 0xdeadbeef; @@ -1162,7 +1162,7 @@ static void test_IDirectMusicSynth(void) hr = IDirectMusicSynth_Unload(synth, (HANDLE)0xdeadbeef, test_unload_no_callback, (HANDLE)0xdeadbeef); ok(hr == E_FAIL, "got %#lx\n", hr); hr = IDirectMusicSynth_Unload(synth, wave_handle, test_unload_callback, (HANDLE)0xdeadbeef); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); ok(!unload_called, "callback called\n"); hr = IDirectMusicSynth_Unload(synth, instrument_handle, NULL, NULL); ok(hr == S_OK, "got %#lx\n", hr); From 4af08dd2d47966f5f5ec4ddf6e5f135712cd5ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 7 Sep 2023 12:07:59 +0200 Subject: [PATCH 0832/1148] dmsynth: Implement IDirectMusicSynth_Download for instrument regions. (cherry picked from commit 4fa0978394066d10ca3b1015b929ff0febf27f61) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 126 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index e6f47b660c5..c3b99063834 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -42,6 +42,40 @@ static void dump_dmus_instrument(DMUS_INSTRUMENT *instrument) TRACE(" - ulFlags = %lu\n", instrument->ulFlags); } +static void dump_dmus_region(DMUS_REGION *region) +{ + UINT i; + + TRACE("DMUS_REGION:\n"); + TRACE(" - RangeKey = %u - %u\n", region->RangeKey.usLow, region->RangeKey.usHigh); + TRACE(" - RangeVelocity = %u - %u\n", region->RangeVelocity.usLow, region->RangeVelocity.usHigh); + TRACE(" - fusOptions = %#x\n", region->fusOptions); + TRACE(" - usKeyGroup = %u\n", region->usKeyGroup); + TRACE(" - ulRegionArtIdx = %lu\n", region->ulRegionArtIdx); + TRACE(" - ulNextRegionIdx = %lu\n", region->ulNextRegionIdx); + TRACE(" - ulFirstExtCkIdx = %lu\n", region->ulFirstExtCkIdx); + TRACE(" - WaveLink:\n"); + TRACE(" - fusOptions = %#x\n", region->WaveLink.fusOptions); + TRACE(" - usPhaseGroup = %u\n", region->WaveLink.usPhaseGroup); + TRACE(" - ulChannel = %lu\n", region->WaveLink.ulChannel); + TRACE(" - ulTableIndex = %lu\n", region->WaveLink.ulTableIndex); + TRACE(" - WSMP:\n"); + TRACE(" - cbSize = %lu\n", region->WSMP.cbSize); + TRACE(" - usUnityNote = %u\n", region->WSMP.usUnityNote); + TRACE(" - sFineTune = %u\n", region->WSMP.sFineTune); + TRACE(" - lAttenuation = %lu\n", region->WSMP.lAttenuation); + TRACE(" - fulOptions = %#lx\n", region->WSMP.fulOptions); + TRACE(" - cSampleLoops = %lu\n", region->WSMP.cSampleLoops); + for (i = 0; i < region->WSMP.cSampleLoops; i++) + { + TRACE(" - WLOOP[%u]:\n", i); + TRACE(" - cbSize = %lu\n", region->WLOOP[i].cbSize); + TRACE(" - ulType = %#lx\n", region->WLOOP[i].ulType); + TRACE(" - ulStart = %lu\n", region->WLOOP[i].ulStart); + TRACE(" - ulLength = %lu\n", region->WLOOP[i].ulLength); + } +} + static void dump_dmus_wave(DMUS_WAVE *wave) { TRACE("DMUS_WAVE:\n"); @@ -71,12 +105,38 @@ struct wave C_ASSERT(sizeof(struct wave) == offsetof(struct wave, samples[0])); +static void wave_addref(struct wave *wave) +{ + InterlockedIncrement(&wave->ref); +} + static void wave_release(struct wave *wave) { ULONG ref = InterlockedDecrement(&wave->ref); if (!ref) free(wave); } +struct region +{ + struct list entry; + + RGNRANGE key_range; + RGNRANGE vel_range; + UINT flags; + UINT group; + + struct wave *wave; + WAVELINK wave_link; + WSMPL wave_sample; + WLOOP wave_loops[]; +}; + +static void region_destroy(struct region *region) +{ + wave_release(region->wave); + free(region); +} + struct instrument { struct list entry; @@ -85,6 +145,7 @@ struct instrument UINT patch; UINT flags; + struct list regions; struct synth *synth; }; @@ -92,7 +153,20 @@ struct instrument static void instrument_release(struct instrument *instrument) { ULONG ref = InterlockedDecrement(&instrument->ref); - if (!ref) free(instrument); + + if (!ref) + { + struct region *region; + void *next; + + LIST_FOR_EACH_ENTRY_SAFE(region, next, &instrument->regions, struct region, entry) + { + list_remove(®ion->entry); + region_destroy(region); + } + + free(instrument); + } } struct synth @@ -296,11 +370,31 @@ static HRESULT WINAPI synth_SetNumChannelGroups(IDirectMusicSynth8 *iface, return S_OK; } +static struct wave *synth_find_wave_from_id(struct synth *This, DWORD id) +{ + struct wave *wave; + + LIST_FOR_EACH_ENTRY(wave, &This->waves, struct wave, entry) + { + if (wave->id == id) + { + wave_addref(wave); + return wave; + } + } + + WARN("Failed to find wave with id %#lx\n", id); + return NULL; +} + static HRESULT synth_download_instrument(struct synth *This, DMUS_DOWNLOADINFO *info, ULONG *offsets, BYTE *data, HANDLE *ret_handle) { DMUS_INSTRUMENT *instrument_info = (DMUS_INSTRUMENT *)(data + offsets[0]); struct instrument *instrument; + DMUS_REGION *region_info; + struct region *region; + ULONG index; if (TRACE_ON(dmsynth)) { @@ -320,12 +414,42 @@ static HRESULT synth_download_instrument(struct synth *This, DMUS_DOWNLOADINFO * instrument->id = info->dwDLId; instrument->patch = instrument_info->ulPatch; instrument->flags = instrument_info->ulFlags; + list_init(&instrument->regions); instrument->synth = This; + for (index = instrument_info->ulFirstRegionIdx; index; index = region_info->ulNextRegionIdx) + { + region_info = (DMUS_REGION *)(data + offsets[index]); + if (TRACE_ON(dmsynth)) dump_dmus_region(region_info); + if (region_info->ulFirstExtCkIdx) FIXME("Region extensions not implemented\n"); + + if (!(region = calloc(1, offsetof(struct region, wave_loops[region_info->WSMP.cSampleLoops])))) goto error; + region->key_range = region_info->RangeKey; + region->vel_range = region_info->RangeVelocity; + region->flags = region_info->fusOptions; + region->group = region_info->usKeyGroup; + region->wave_link = region_info->WaveLink; + region->wave_sample = region_info->WSMP; + memcpy(region->wave_loops, region_info->WLOOP, region_info->WSMP.cSampleLoops * sizeof(WLOOP)); + + if (!(region->wave = synth_find_wave_from_id(This, region->wave_link.ulTableIndex))) + { + free(region); + instrument_release(instrument); + return DMUS_E_BADWAVELINK; + } + + list_add_tail(&instrument->regions, ®ion->entry); + } + list_add_tail(&This->instruments, &instrument->entry); *ret_handle = instrument; return S_OK; + +error: + instrument_release(instrument); + return E_OUTOFMEMORY; } static HRESULT synth_download_wave(struct synth *This, DMUS_DOWNLOADINFO *info, ULONG *offsets, From 3290edce10ddb0056c4dc6b5dff7ce4333b3da0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 7 Sep 2023 11:46:19 +0200 Subject: [PATCH 0833/1148] dmsynth: Implement IDirectMusicSynth_Download for instrument articulations. (cherry picked from commit 223ba990b1d8914c5ec6a1b57e28e1299ee653a8) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 101 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index c3b99063834..d87afd245e9 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -76,6 +76,26 @@ static void dump_dmus_region(DMUS_REGION *region) } } +static void dump_connectionlist(CONNECTIONLIST *list) +{ + CONNECTION *connections = (CONNECTION *)(list + 1); + UINT i; + + TRACE("CONNECTIONLIST:\n"); + TRACE(" - cbSize = %lu", list->cbSize); + TRACE(" - cConnections = %lu", list->cConnections); + + for (i = 0; i < list->cConnections; i++) + { + TRACE("- CONNECTION[%u]:\n", i); + TRACE(" - usSource = %u\n", connections[i].usSource); + TRACE(" - usControl = %u\n", connections[i].usControl); + TRACE(" - usDestination = %u\n", connections[i].usDestination); + TRACE(" - usTransform = %u\n", connections[i].usTransform); + TRACE(" - lScale = %lu\n", connections[i].lScale); + } +} + static void dump_dmus_wave(DMUS_WAVE *wave) { TRACE("DMUS_WAVE:\n"); @@ -116,6 +136,15 @@ static void wave_release(struct wave *wave) if (!ref) free(wave); } +struct articulation +{ + struct list entry; + CONNECTIONLIST list; + CONNECTION connections[]; +}; + +C_ASSERT(sizeof(struct articulation) == offsetof(struct articulation, connections[0])); + struct region { struct list entry; @@ -125,6 +154,8 @@ struct region UINT flags; UINT group; + struct list articulations; + struct wave *wave; WAVELINK wave_link; WSMPL wave_sample; @@ -133,6 +164,15 @@ struct region static void region_destroy(struct region *region) { + struct articulation *articulation; + void *next; + + LIST_FOR_EACH_ENTRY_SAFE(articulation, next, ®ion->articulations, struct articulation, entry) + { + list_remove(&articulation->entry); + free(articulation); + } + wave_release(region->wave); free(region); } @@ -146,6 +186,7 @@ struct instrument UINT patch; UINT flags; struct list regions; + struct list articulations; struct synth *synth; }; @@ -156,6 +197,7 @@ static void instrument_release(struct instrument *instrument) if (!ref) { + struct articulation *articulation; struct region *region; void *next; @@ -165,6 +207,12 @@ static void instrument_release(struct instrument *instrument) region_destroy(region); } + LIST_FOR_EACH_ENTRY_SAFE(articulation, next, &instrument->articulations, struct articulation, entry) + { + list_remove(&articulation->entry); + free(articulation); + } + free(instrument); } } @@ -370,6 +418,47 @@ static HRESULT WINAPI synth_SetNumChannelGroups(IDirectMusicSynth8 *iface, return S_OK; } +static HRESULT synth_download_articulation2(struct synth *This, ULONG *offsets, BYTE *data, + UINT index, struct list *articulations) +{ + DMUS_ARTICULATION2 *articulation_info; + struct articulation *articulation; + CONNECTION *connections; + CONNECTIONLIST *list; + UINT size; + + for (; index; index = articulation_info->ulNextArtIdx) + { + articulation_info = (DMUS_ARTICULATION2 *)(data + offsets[index]); + list = (CONNECTIONLIST *)(data + offsets[articulation_info->ulArtIdx]); + connections = (CONNECTION *)list + 1; + + if (TRACE_ON(dmsynth)) dump_connectionlist(list); + if (articulation_info->ulFirstExtCkIdx) FIXME("Articulation extensions not implemented\n"); + if (list->cbSize != sizeof(*list)) return DMUS_E_BADARTICULATION; + + size = offsetof(struct articulation, connections[list->cConnections]); + if (!(articulation = calloc(1, size))) return E_OUTOFMEMORY; + articulation->list = *list; + memcpy(articulation->connections, connections, list->cConnections * sizeof(*connections)); + list_add_tail(articulations, &articulation->entry); + } + + return S_OK; +} + +static HRESULT synth_download_articulation(struct synth *This, DMUS_DOWNLOADINFO *info, ULONG *offsets, BYTE *data, + UINT index, struct list *list) +{ + if (info->dwDLType == DMUS_DOWNLOADINFO_INSTRUMENT2) + return synth_download_articulation2(This, offsets, data, index, list); + else + { + FIXME("DMUS_ARTICPARAMS support not implemented\n"); + return S_OK; + } +} + static struct wave *synth_find_wave_from_id(struct synth *This, DWORD id) { struct wave *wave; @@ -415,6 +504,7 @@ static HRESULT synth_download_instrument(struct synth *This, DMUS_DOWNLOADINFO * instrument->patch = instrument_info->ulPatch; instrument->flags = instrument_info->ulFlags; list_init(&instrument->regions); + list_init(&instrument->articulations); instrument->synth = This; for (index = instrument_info->ulFirstRegionIdx; index; index = region_info->ulNextRegionIdx) @@ -431,6 +521,7 @@ static HRESULT synth_download_instrument(struct synth *This, DMUS_DOWNLOADINFO * region->wave_link = region_info->WaveLink; region->wave_sample = region_info->WSMP; memcpy(region->wave_loops, region_info->WLOOP, region_info->WSMP.cSampleLoops * sizeof(WLOOP)); + list_init(®ion->articulations); if (!(region->wave = synth_find_wave_from_id(This, region->wave_link.ulTableIndex))) { @@ -440,8 +531,16 @@ static HRESULT synth_download_instrument(struct synth *This, DMUS_DOWNLOADINFO * } list_add_tail(&instrument->regions, ®ion->entry); + + if (region_info->ulRegionArtIdx && FAILED(synth_download_articulation(This, info, offsets, data, + region_info->ulRegionArtIdx, ®ion->articulations))) + goto error; } + if (FAILED(synth_download_articulation(This, info, offsets, data, + instrument_info->ulGlobalArtIdx, &instrument->articulations))) + goto error; + list_add_tail(&This->instruments, &instrument->entry); *ret_handle = instrument; @@ -520,7 +619,7 @@ static HRESULT WINAPI synth_Download(IDirectMusicSynth8 *iface, HANDLE *ret_hand DMUS_DOWNLOADINFO *info = data; ULONG *offsets = (ULONG *)(info + 1); - FIXME("(%p)->(%p, %p, %p): stub\n", This, ret_handle, data, free); + TRACE("(%p)->(%p, %p, %p)\n", This, ret_handle, data, free); if (!ret_handle || !data || !ret_free) return E_POINTER; *ret_handle = 0; From f03e07f11770ee6d1e507909f366180b20ff4bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 21:32:06 +0200 Subject: [PATCH 0834/1148] dmusic: Use a struct list for instrument articulations. (cherry picked from commit febe6a78102ad30c425c9fd1ce328fe32f84e35e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/dmusic_private.h | 9 ++---- dlls/dmusic/instrument.c | 60 ++++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 5c8f6e1abad..e643f0a6770 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -82,11 +82,6 @@ typedef struct instrument_region { BOOL loop_present; } instrument_region; -typedef struct instrument_articulation { - CONNECTIONLIST connections_list; - CONNECTION *connections; -} instrument_articulation; - /***************************************************************************** * ClassFactory */ @@ -189,8 +184,8 @@ struct instrument /* instrument data */ BOOL loaded; instrument_region *regions; - ULONG nb_articulations; - instrument_articulation *articulations; + + struct list articulations; }; static inline struct instrument *impl_from_IDirectMusicInstrument(IDirectMusicInstrument *iface) diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 5677f128060..a65826976f3 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -25,6 +25,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmusic); static const GUID IID_IDirectMusicInstrumentPRIVATE = { 0xbcb20080, 0xa40c, 0x11d1, { 0x86, 0xbc, 0x00, 0xc0, 0x4f, 0xbf, 0x8f, 0xef } }; +struct articulation +{ + struct list entry; + CONNECTIONLIST list; + CONNECTION connections[]; +}; + +C_ASSERT(sizeof(struct articulation) == offsetof(struct articulation, connections[0])); + static HRESULT WINAPI instrument_QueryInterface(LPDIRECTMUSICINSTRUMENT iface, REFIID riid, LPVOID *ret_iface) { TRACE("(%p)->(%s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); @@ -74,12 +83,16 @@ static ULONG WINAPI instrument_Release(LPDIRECTMUSICINSTRUMENT iface) if (!ref) { - ULONG i; + struct articulation *articulation, *next_articulation; free(This->regions); - for (i = 0; i < This->nb_articulations; i++) - free(This->articulations->connections); - free(This->articulations); + + LIST_FOR_EACH_ENTRY_SAFE(articulation, next_articulation, &This->articulations, struct articulation, entry) + { + list_remove(&articulation->entry); + free(articulation); + } + free(This); } @@ -125,6 +138,7 @@ HRESULT instrument_create(IDirectMusicInstrument **ret_iface) if (!(instrument = calloc(1, sizeof(*instrument)))) return E_OUTOFMEMORY; instrument->IDirectMusicInstrument_iface.lpVtbl = &instrument_vtbl; instrument->ref = 1; + list_init(&instrument->articulations); TRACE("Created DirectMusicInstrument %p\n", instrument); *ret_iface = &instrument->IDirectMusicInstrument_iface; @@ -244,35 +258,27 @@ static HRESULT load_region(struct instrument *This, IStream *stream, instrument_ static HRESULT load_articulation(struct instrument *This, IStream *stream, ULONG length) { - HRESULT ret; - instrument_articulation *articulation; - - This->articulations = realloc(This->articulations, sizeof(*This->articulations) * (This->nb_articulations + 1)); - if (!This->articulations) - return E_OUTOFMEMORY; + struct articulation *articulation; + CONNECTIONLIST list; + HRESULT hr; + UINT size; - articulation = &This->articulations[This->nb_articulations]; + if (FAILED(hr = read_from_stream(stream, &list, sizeof(list)))) return hr; + if (list.cbSize != sizeof(list)) return E_INVALIDARG; - ret = read_from_stream(stream, &articulation->connections_list, sizeof(CONNECTIONLIST)); - if (FAILED(ret)) - return ret; - - articulation->connections = malloc(sizeof(CONNECTION) * articulation->connections_list.cConnections); - if (!articulation->connections) - return E_OUTOFMEMORY; + size = offsetof(struct articulation, connections[list.cConnections]); + if (!(articulation = malloc(size))) return E_OUTOFMEMORY; + articulation->list = list; - ret = read_from_stream(stream, articulation->connections, sizeof(CONNECTION) * articulation->connections_list.cConnections); - if (FAILED(ret)) + size = sizeof(CONNECTION) * list.cConnections; + if (FAILED(hr = read_from_stream(stream, articulation->connections, size))) free(articulation); + else { - free(articulation->connections); - return ret; + subtract_bytes(length, sizeof(list) + sizeof(CONNECTION) * list.cConnections); + list_add_tail(&This->articulations, &articulation->entry); } - subtract_bytes(length, sizeof(CONNECTIONLIST) + sizeof(CONNECTION) * articulation->connections_list.cConnections); - - This->nb_articulations++; - - return S_OK; + return hr; } /* Function that loads all instrument data and which is called from IDirectMusicCollection_GetInstrument as in native */ From 0e24083294034010855b0fe15f81948b6b7eec13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 21:36:18 +0200 Subject: [PATCH 0835/1148] dmusic: Use a struct list for instrument regions. (cherry picked from commit 52d3f7ab09f8b4920c71eaa0c385f13f4bf1fa2a) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/dmusic_private.h | 8 +++--- dlls/dmusic/instrument.c | 50 ++++++++++++++++++------------------ dlls/dmusic/port.c | 19 ++++++++------ 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index e643f0a6770..331aa683f4b 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -74,13 +74,15 @@ typedef struct port_info { ULONG device; } port_info; -typedef struct instrument_region { +struct region +{ + struct list entry; RGNHEADER header; WAVELINK wave_link; WSMPL wave_sample; WLOOP wave_loop; BOOL loop_present; -} instrument_region; +}; /***************************************************************************** * ClassFactory @@ -183,9 +185,9 @@ struct instrument WCHAR wszName[DMUS_MAX_NAME]; /* instrument data */ BOOL loaded; - instrument_region *regions; struct list articulations; + struct list regions; }; static inline struct instrument *impl_from_IDirectMusicInstrument(IDirectMusicInstrument *iface) diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index a65826976f3..bcd2ef58265 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -84,8 +84,7 @@ static ULONG WINAPI instrument_Release(LPDIRECTMUSICINSTRUMENT iface) if (!ref) { struct articulation *articulation, *next_articulation; - - free(This->regions); + struct region *region, *next_region; LIST_FOR_EACH_ENTRY_SAFE(articulation, next_articulation, &This->articulations, struct articulation, entry) { @@ -93,6 +92,12 @@ static ULONG WINAPI instrument_Release(LPDIRECTMUSICINSTRUMENT iface) free(articulation); } + LIST_FOR_EACH_ENTRY_SAFE(region, next_region, &This->regions, struct region, entry) + { + list_remove(®ion->entry); + free(region); + } + free(This); } @@ -139,6 +144,7 @@ HRESULT instrument_create(IDirectMusicInstrument **ret_iface) instrument->IDirectMusicInstrument_iface.lpVtbl = &instrument_vtbl; instrument->ref = 1; list_init(&instrument->articulations); + list_init(&instrument->regions); TRACE("Created DirectMusicInstrument %p\n", instrument); *ret_iface = &instrument->IDirectMusicInstrument_iface; @@ -186,18 +192,20 @@ static inline HRESULT advance_stream(IStream *stream, ULONG bytes) return ret; } -static HRESULT load_region(struct instrument *This, IStream *stream, instrument_region *region, ULONG length) +static HRESULT load_region(struct instrument *This, IStream *stream, ULONG length) { + struct region *region; HRESULT ret; DMUS_PRIVATE_CHUNK chunk; - TRACE("(%p, %p, %p, %lu)\n", This, stream, region, length); + TRACE("(%p, %p, %lu)\n", This, stream, length); + + if (!(region = malloc(sizeof(*region)))) return E_OUTOFMEMORY; while (length) { ret = read_from_stream(stream, &chunk, sizeof(chunk)); - if (FAILED(ret)) - return ret; + if (FAILED(ret)) goto failed; length = subtract_bytes(length, sizeof(chunk)); @@ -207,8 +215,7 @@ static HRESULT load_region(struct instrument *This, IStream *stream, instrument_ TRACE("RGNH chunk (region header): %lu bytes\n", chunk.dwSize); ret = read_from_stream(stream, ®ion->header, sizeof(region->header)); - if (FAILED(ret)) - return ret; + if (FAILED(ret)) goto failed; length = subtract_bytes(length, sizeof(region->header)); break; @@ -217,16 +224,14 @@ static HRESULT load_region(struct instrument *This, IStream *stream, instrument_ TRACE("WSMP chunk (wave sample): %lu bytes\n", chunk.dwSize); ret = read_from_stream(stream, ®ion->wave_sample, sizeof(region->wave_sample)); - if (FAILED(ret)) - return ret; + if (FAILED(ret)) goto failed; length = subtract_bytes(length, sizeof(region->wave_sample)); if (!(region->loop_present = (chunk.dwSize != sizeof(region->wave_sample)))) break; ret = read_from_stream(stream, ®ion->wave_loop, sizeof(region->wave_loop)); - if (FAILED(ret)) - return ret; + if (FAILED(ret)) goto failed; length = subtract_bytes(length, sizeof(region->wave_loop)); break; @@ -235,8 +240,7 @@ static HRESULT load_region(struct instrument *This, IStream *stream, instrument_ TRACE("WLNK chunk (wave link): %lu bytes\n", chunk.dwSize); ret = read_from_stream(stream, ®ion->wave_link, sizeof(region->wave_link)); - if (FAILED(ret)) - return ret; + if (FAILED(ret)) goto failed; length = subtract_bytes(length, sizeof(region->wave_link)); break; @@ -245,15 +249,19 @@ static HRESULT load_region(struct instrument *This, IStream *stream, instrument_ TRACE("Unknown chunk %s (skipping): %lu bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); ret = advance_stream(stream, chunk.dwSize); - if (FAILED(ret)) - return ret; + if (FAILED(ret)) goto failed; length = subtract_bytes(length, chunk.dwSize); break; } } + list_add_tail(&This->regions, ®ion->entry); return S_OK; + +failed: + free(region); + return ret; } static HRESULT load_articulation(struct instrument *This, IStream *stream, ULONG length) @@ -287,7 +295,6 @@ HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) struct instrument *This = impl_from_IDirectMusicInstrument(iface); HRESULT hr; DMUS_PRIVATE_CHUNK chunk; - ULONG i = 0; ULONG length = This->length; TRACE("(%p, %p): offset = 0x%s, length = %lu)\n", This, stream, wine_dbgstr_longlong(This->liInstrumentPosition.QuadPart), This->length); @@ -302,10 +309,6 @@ HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) return DMUS_E_UNSUPPORTED_STREAM; } - This->regions = malloc(sizeof(*This->regions) * This->header.cRegions); - if (!This->regions) - return E_OUTOFMEMORY; - while (length) { hr = read_from_stream(stream, &chunk, sizeof(chunk)); @@ -362,7 +365,7 @@ HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) if (chunk.fccID == FOURCC_RGN) { TRACE("RGN chunk (region): %lu bytes\n", chunk.dwSize); - hr = load_region(This, stream, &This->regions[i++], chunk.dwSize - sizeof(chunk.fccID)); + hr = load_region(This, stream, chunk.dwSize - sizeof(chunk.fccID)); } else { @@ -431,8 +434,5 @@ HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) return S_OK; error: - free(This->regions); - This->regions = NULL; - return DMUS_E_UNSUPPORTED_STREAM; } diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index 2f8556d149b..27e20b1cf81 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -267,6 +267,7 @@ static HRESULT WINAPI synth_port_DownloadInstrument(IDirectMusicPort *iface, IDi { struct synth_port *This = synth_from_IDirectMusicPort(iface); struct instrument *instrument_object; + struct region *instrument_region; HRESULT ret; BOOL on_heap; HANDLE download; @@ -312,22 +313,24 @@ static HRESULT WINAPI synth_port_DownloadInstrument(IDirectMusicPort *iface, IDi instrument_info->ulCopyrightIdx = 0; /* FIXME */ instrument_info->ulFlags = 0; /* FIXME */ - for (i = 0; i < nb_regions; i++) + i = 0; + LIST_FOR_EACH_ENTRY(instrument_region, &instrument_object->regions, struct region, entry) { DMUS_REGION *region = (DMUS_REGION*)(data + offset); offset_table->ulOffsetTable[1 + i] = offset; offset += sizeof(DMUS_REGION); - region->RangeKey = instrument_object->regions[i].header.RangeKey; - region->RangeVelocity = instrument_object->regions[i].header.RangeVelocity; - region->fusOptions = instrument_object->regions[i].header.fusOptions; - region->usKeyGroup = instrument_object->regions[i].header.usKeyGroup; + region->RangeKey = instrument_region->header.RangeKey; + region->RangeVelocity = instrument_region->header.RangeVelocity; + region->fusOptions = instrument_region->header.fusOptions; + region->usKeyGroup = instrument_region->header.usKeyGroup; region->ulRegionArtIdx = 0; /* FIXME */ region->ulNextRegionIdx = i != (nb_regions - 1) ? (i + 2) : 0; region->ulFirstExtCkIdx = 0; /* FIXME */ - region->WaveLink = instrument_object->regions[i].wave_link; - region->WSMP = instrument_object->regions[i].wave_sample; - region->WLOOP[0] = instrument_object->regions[i].wave_loop; + region->WaveLink = instrument_region->wave_link; + region->WSMP = instrument_region->wave_sample; + region->WLOOP[0] = instrument_region->wave_loop; + i++; } ret = IDirectMusicSynth8_Download(This->synth, &download, (void*)data, &on_heap); From 3e77b60f226f905fff6e6276937d8049e15e93b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 10:04:57 +0200 Subject: [PATCH 0836/1148] dmusic: Reset riff chunk type to 0 when it is invalid. (cherry picked from commit 7684880546bf0b7a31aae917b7914ce493b47551) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/dmobject.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dlls/dmusic/dmobject.c b/dlls/dmusic/dmobject.c index 07d887a376c..a08db8cba58 100644 --- a/dlls/dmusic/dmobject.c +++ b/dlls/dmusic/dmobject.c @@ -338,11 +338,10 @@ HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) } } - if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { - hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); - if (hr != S_OK) - return hr != S_FALSE ? hr : E_FAIL; - } + if (chunk->id != FOURCC_LIST && chunk->id != FOURCC_RIFF) + chunk->type = 0; + else if ((hr = stream_read(stream, &chunk->type, sizeof(FOURCC))) != S_OK) + return hr != S_FALSE ? hr : E_FAIL; TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); From aba4a2dbba5e559e739fa61f79dc48b732a042bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 7 Sep 2023 09:58:34 +0200 Subject: [PATCH 0837/1148] dmusic: Rewrite instrument lrgn list parsing. (cherry picked from commit 9e9b5d2980c28d1a7c126f3ba59257c7cdd2f9d3) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/dmobject.c | 2 +- dlls/dmusic/dmobject.h | 2 + dlls/dmusic/instrument.c | 176 ++++++++++++++++----------------------- 3 files changed, 77 insertions(+), 103 deletions(-) diff --git a/dlls/dmusic/dmobject.c b/dlls/dmusic/dmobject.c index a08db8cba58..2bef5511851 100644 --- a/dlls/dmusic/dmobject.c +++ b/dlls/dmusic/dmobject.c @@ -287,7 +287,7 @@ const char *debugstr_chunk(const struct chunk_entry *chunk) return wine_dbg_sprintf("%s chunk, %ssize %lu", debugstr_fourcc(chunk->id), type, chunk->size); } -static HRESULT stream_read(IStream *stream, void *data, ULONG size) +HRESULT stream_read(IStream *stream, void *data, ULONG size) { ULONG read; HRESULT hr; diff --git a/dlls/dmusic/dmobject.h b/dlls/dmusic/dmobject.h index 772be015c80..98069de8cc4 100644 --- a/dlls/dmusic/dmobject.h +++ b/dlls/dmusic/dmobject.h @@ -30,7 +30,9 @@ struct chunk_entry { ULARGE_INTEGER offset; /* chunk offset from start of stream */ const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ }; +#define MAKE_IDTYPE(id, type) (((UINT64)type << 32) | (UINT64)id) +HRESULT stream_read(IStream *stream, void *data, ULONG size); HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index bcd2ef58265..5941042caa1 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -192,98 +192,95 @@ static inline HRESULT advance_stream(IStream *stream, ULONG bytes) return ret; } -static HRESULT load_region(struct instrument *This, IStream *stream, ULONG length) +static HRESULT load_articulation(struct instrument *This, IStream *stream, ULONG length) { - struct region *region; - HRESULT ret; - DMUS_PRIVATE_CHUNK chunk; + struct articulation *articulation; + CONNECTIONLIST list; + HRESULT hr; + UINT size; - TRACE("(%p, %p, %lu)\n", This, stream, length); + if (FAILED(hr = read_from_stream(stream, &list, sizeof(list)))) return hr; + if (list.cbSize != sizeof(list)) return E_INVALIDARG; - if (!(region = malloc(sizeof(*region)))) return E_OUTOFMEMORY; + size = offsetof(struct articulation, connections[list.cConnections]); + if (!(articulation = malloc(size))) return E_OUTOFMEMORY; + articulation->list = list; - while (length) + size = sizeof(CONNECTION) * list.cConnections; + if (FAILED(hr = read_from_stream(stream, articulation->connections, size))) free(articulation); + else { - ret = read_from_stream(stream, &chunk, sizeof(chunk)); - if (FAILED(ret)) goto failed; - - length = subtract_bytes(length, sizeof(chunk)); - - switch (chunk.fccID) - { - case FOURCC_RGNH: - TRACE("RGNH chunk (region header): %lu bytes\n", chunk.dwSize); - - ret = read_from_stream(stream, ®ion->header, sizeof(region->header)); - if (FAILED(ret)) goto failed; - - length = subtract_bytes(length, sizeof(region->header)); - break; - - case FOURCC_WSMP: - TRACE("WSMP chunk (wave sample): %lu bytes\n", chunk.dwSize); - - ret = read_from_stream(stream, ®ion->wave_sample, sizeof(region->wave_sample)); - if (FAILED(ret)) goto failed; - length = subtract_bytes(length, sizeof(region->wave_sample)); - - if (!(region->loop_present = (chunk.dwSize != sizeof(region->wave_sample)))) - break; - - ret = read_from_stream(stream, ®ion->wave_loop, sizeof(region->wave_loop)); - if (FAILED(ret)) goto failed; - - length = subtract_bytes(length, sizeof(region->wave_loop)); - break; + subtract_bytes(length, sizeof(list) + sizeof(CONNECTION) * list.cConnections); + list_add_tail(&This->articulations, &articulation->entry); + } - case FOURCC_WLNK: - TRACE("WLNK chunk (wave link): %lu bytes\n", chunk.dwSize); + return hr; +} - ret = read_from_stream(stream, ®ion->wave_link, sizeof(region->wave_link)); - if (FAILED(ret)) goto failed; +static HRESULT parse_rgn_chunk(struct instrument *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + struct region *region; + HRESULT hr; - length = subtract_bytes(length, sizeof(region->wave_link)); - break; + if (!(region = malloc(sizeof(*region)))) return E_OUTOFMEMORY; - default: - TRACE("Unknown chunk %s (skipping): %lu bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case FOURCC_RGNH: + hr = stream_chunk_get_data(stream, &chunk, ®ion->header, sizeof(region->header)); + break; + + case FOURCC_WSMP: + if (chunk.size < sizeof(region->wave_sample)) hr = E_INVALIDARG; + else hr = stream_read(stream, ®ion->wave_sample, sizeof(region->wave_sample)); + if (SUCCEEDED(hr) && region->wave_sample.cSampleLoops) + { + if (region->wave_sample.cSampleLoops > 1) FIXME("More than one wave loop is not implemented\n"); + if (chunk.size != sizeof(WSMPL) + region->wave_sample.cSampleLoops * sizeof(WLOOP)) hr = E_INVALIDARG; + else hr = stream_read(stream, ®ion->wave_loop, sizeof(region->wave_loop)); + } + break; - ret = advance_stream(stream, chunk.dwSize); - if (FAILED(ret)) goto failed; + case FOURCC_WLNK: + hr = stream_chunk_get_data(stream, &chunk, ®ion->wave_link, sizeof(region->wave_link)); + break; - length = subtract_bytes(length, chunk.dwSize); - break; + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; } + + if (FAILED(hr)) break; } - list_add_tail(&This->regions, ®ion->entry); - return S_OK; + if (FAILED(hr)) free(region); + else list_add_tail(&This->regions, ®ion->entry); -failed: - free(region); - return ret; + return hr; } -static HRESULT load_articulation(struct instrument *This, IStream *stream, ULONG length) +static HRESULT parse_lrgn_list(struct instrument *This, IStream *stream, struct chunk_entry *parent) { - struct articulation *articulation; - CONNECTIONLIST list; + struct chunk_entry chunk = {.parent = parent}; HRESULT hr; - UINT size; - if (FAILED(hr = read_from_stream(stream, &list, sizeof(list)))) return hr; - if (list.cbSize != sizeof(list)) return E_INVALIDARG; + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_LIST, FOURCC_RGN): + hr = parse_rgn_chunk(This, stream, &chunk); + break; - size = offsetof(struct articulation, connections[list.cConnections]); - if (!(articulation = malloc(size))) return E_OUTOFMEMORY; - articulation->list = list; + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } - size = sizeof(CONNECTION) * list.cConnections; - if (FAILED(hr = read_from_stream(stream, articulation->connections, size))) free(articulation); - else - { - subtract_bytes(length, sizeof(list) + sizeof(CONNECTION) * list.cConnections); - list_add_tail(&This->articulations, &articulation->entry); + if (FAILED(hr)) break; } return hr; @@ -344,40 +341,15 @@ HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) switch (chunk.fccID) { case FOURCC_LRGN: + { + static const LARGE_INTEGER zero = {0}; + struct chunk_entry list_chunk = {.id = FOURCC_LIST, .size = chunk.dwSize, .type = chunk.fccID}; TRACE("LRGN chunk (regions list): %lu bytes\n", size); - - while (size) - { - hr = read_from_stream(stream, &chunk, sizeof(chunk)); - if (FAILED(hr)) - goto error; - - if (chunk.fccID != FOURCC_LIST) - { - TRACE("Unknown chunk %s: %lu bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); - goto error; - } - - hr = read_from_stream(stream, &chunk.fccID, sizeof(chunk.fccID)); - if (FAILED(hr)) - goto error; - - if (chunk.fccID == FOURCC_RGN) - { - TRACE("RGN chunk (region): %lu bytes\n", chunk.dwSize); - hr = load_region(This, stream, chunk.dwSize - sizeof(chunk.fccID)); - } - else - { - TRACE("Unknown chunk %s: %lu bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); - hr = advance_stream(stream, chunk.dwSize - sizeof(chunk.fccID)); - } - if (FAILED(hr)) - goto error; - - size = subtract_bytes(size, chunk.dwSize + sizeof(chunk)); - } + IStream_Seek(stream, zero, STREAM_SEEK_CUR, &list_chunk.offset); + list_chunk.offset.QuadPart -= 12; + hr = parse_lrgn_list(This, stream, &list_chunk); break; + } case FOURCC_LART: TRACE("LART chunk (articulations list): %lu bytes\n", size); From 0ddbaf41054417568d3f6d71f9dee02978007ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 7 Sep 2023 10:10:40 +0200 Subject: [PATCH 0838/1148] dmusic: Rewrite instrument lart list parsing. (cherry picked from commit b5fd6187198f9b15e2ce3795c6dce798e7f0f3d0) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/instrument.c | 63 ++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 5941042caa1..1e171048008 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -192,14 +192,16 @@ static inline HRESULT advance_stream(IStream *stream, ULONG bytes) return ret; } -static HRESULT load_articulation(struct instrument *This, IStream *stream, ULONG length) +static HRESULT parse_art1_chunk(struct instrument *This, IStream *stream, struct chunk_entry *chunk) { struct articulation *articulation; CONNECTIONLIST list; HRESULT hr; UINT size; - if (FAILED(hr = read_from_stream(stream, &list, sizeof(list)))) return hr; + if (chunk->size < sizeof(list)) return E_INVALIDARG; + if (FAILED(hr = stream_read(stream, &list, sizeof(list)))) return hr; + if (chunk->size != list.cbSize + sizeof(CONNECTION) * list.cConnections) return E_INVALIDARG; if (list.cbSize != sizeof(list)) return E_INVALIDARG; size = offsetof(struct articulation, connections[list.cConnections]); @@ -207,11 +209,31 @@ static HRESULT load_articulation(struct instrument *This, IStream *stream, ULONG articulation->list = list; size = sizeof(CONNECTION) * list.cConnections; - if (FAILED(hr = read_from_stream(stream, articulation->connections, size))) free(articulation); - else + if (FAILED(hr = stream_read(stream, articulation->connections, size))) free(articulation); + else list_add_tail(&This->articulations, &articulation->entry); + + return hr; +} + +static HRESULT parse_lart_list(struct instrument *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { - subtract_bytes(length, sizeof(list) + sizeof(CONNECTION) * list.cConnections); - list_add_tail(&This->articulations, &articulation->entry); + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case FOURCC_ART1: + hr = parse_art1_chunk(This, stream, &chunk); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; } return hr; @@ -352,30 +374,15 @@ HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) } case FOURCC_LART: + { + static const LARGE_INTEGER zero = {0}; + struct chunk_entry list_chunk = {.id = FOURCC_LIST, .size = chunk.dwSize, .type = chunk.fccID}; TRACE("LART chunk (articulations list): %lu bytes\n", size); - - while (size) - { - hr = read_from_stream(stream, &chunk, sizeof(chunk)); - if (FAILED(hr)) - goto error; - - if (chunk.fccID == FOURCC_ART1) - { - TRACE("ART1 chunk (level 1 articulation): %lu bytes\n", chunk.dwSize); - hr = load_articulation(This, stream, chunk.dwSize); - } - else - { - TRACE("Unknown chunk %s: %lu bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); - hr = advance_stream(stream, chunk.dwSize); - } - if (FAILED(hr)) - goto error; - - size = subtract_bytes(size, chunk.dwSize + sizeof(chunk)); - } + IStream_Seek(stream, zero, STREAM_SEEK_CUR, &list_chunk.offset); + list_chunk.offset.QuadPart -= 12; + hr = parse_lart_list(This, stream, &list_chunk); break; + } default: TRACE("Unknown chunk %s: %lu bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); From 0a4d8ecb95f447140ca7edaad906a9a3f4a84364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 21:58:49 +0200 Subject: [PATCH 0839/1148] dmusic: Rewrite instrument ins chunk parsing. (cherry picked from commit 01127ce4746d3960241a05db53e73f92d828e3ef) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/instrument.c | 163 +++++++++++---------------------------- 1 file changed, 45 insertions(+), 118 deletions(-) diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 1e171048008..cbcebcb5902 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -151,47 +151,6 @@ HRESULT instrument_create(IDirectMusicInstrument **ret_iface) return S_OK; } -static HRESULT read_from_stream(IStream *stream, void *data, ULONG size) -{ - ULONG bytes_read; - HRESULT hr; - - hr = IStream_Read(stream, data, size, &bytes_read); - if(FAILED(hr)){ - TRACE("IStream_Read failed: %08lx\n", hr); - return hr; - } - if (bytes_read < size) { - TRACE("Didn't read full chunk: %lu < %lu\n", bytes_read, size); - return E_FAIL; - } - - return S_OK; -} - -static inline DWORD subtract_bytes(DWORD len, DWORD bytes) -{ - if(bytes > len){ - TRACE("Apparent mismatch in chunk lengths? %lu bytes remaining, %lu bytes read\n", len, bytes); - return 0; - } - return len - bytes; -} - -static inline HRESULT advance_stream(IStream *stream, ULONG bytes) -{ - LARGE_INTEGER move; - HRESULT ret; - - move.QuadPart = bytes; - - ret = IStream_Seek(stream, move, STREAM_SEEK_CUR, NULL); - if (FAILED(ret)) - WARN("IStream_Seek failed: %08lx\n", ret); - - return ret; -} - static HRESULT parse_art1_chunk(struct instrument *This, IStream *stream, struct chunk_entry *chunk) { struct articulation *articulation; @@ -308,13 +267,45 @@ static HRESULT parse_lrgn_list(struct instrument *This, IStream *stream, struct return hr; } +static HRESULT parse_ins_chunk(struct instrument *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case FOURCC_INSH: + case FOURCC_DLID: + /* Instrument header and id are already set so just skip */ + break; + + case MAKE_IDTYPE(FOURCC_LIST, FOURCC_LRGN): + hr = parse_lrgn_list(This, stream, &chunk); + break; + + case MAKE_IDTYPE(FOURCC_LIST, FOURCC_LART): + hr = parse_lart_list(This, stream, &chunk); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + return hr; +} + /* Function that loads all instrument data and which is called from IDirectMusicCollection_GetInstrument as in native */ HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) { struct instrument *This = impl_from_IDirectMusicInstrument(iface); + struct chunk_entry chunk = {0}; HRESULT hr; - DMUS_PRIVATE_CHUNK chunk; - ULONG length = This->length; TRACE("(%p, %p): offset = 0x%s, length = %lu)\n", This, stream, wine_dbgstr_longlong(This->liInstrumentPosition.QuadPart), This->length); @@ -328,90 +319,26 @@ HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) return DMUS_E_UNSUPPORTED_STREAM; } - while (length) + if ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { - hr = read_from_stream(stream, &chunk, sizeof(chunk)); - if (FAILED(hr)) - goto error; - - length = subtract_bytes(length, sizeof(chunk) + chunk.dwSize); - - switch (chunk.fccID) + switch (MAKE_IDTYPE(chunk.id, chunk.type)) { - case FOURCC_INSH: - case FOURCC_DLID: - TRACE("Chunk %s: %lu bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); - - /* Instrument header and id are already set so just skip */ - hr = advance_stream(stream, chunk.dwSize); - if (FAILED(hr)) - goto error; - - break; - - case FOURCC_LIST: { - DWORD size = chunk.dwSize; - - TRACE("LIST chunk: %lu bytes\n", chunk.dwSize); - - hr = read_from_stream(stream, &chunk.fccID, sizeof(chunk.fccID)); - if (FAILED(hr)) - goto error; - - size = subtract_bytes(size, sizeof(chunk.fccID)); - - switch (chunk.fccID) - { - case FOURCC_LRGN: - { - static const LARGE_INTEGER zero = {0}; - struct chunk_entry list_chunk = {.id = FOURCC_LIST, .size = chunk.dwSize, .type = chunk.fccID}; - TRACE("LRGN chunk (regions list): %lu bytes\n", size); - IStream_Seek(stream, zero, STREAM_SEEK_CUR, &list_chunk.offset); - list_chunk.offset.QuadPart -= 12; - hr = parse_lrgn_list(This, stream, &list_chunk); - break; - } - - case FOURCC_LART: - { - static const LARGE_INTEGER zero = {0}; - struct chunk_entry list_chunk = {.id = FOURCC_LIST, .size = chunk.dwSize, .type = chunk.fccID}; - TRACE("LART chunk (articulations list): %lu bytes\n", size); - IStream_Seek(stream, zero, STREAM_SEEK_CUR, &list_chunk.offset); - list_chunk.offset.QuadPart -= 12; - hr = parse_lart_list(This, stream, &list_chunk); - break; - } - - default: - TRACE("Unknown chunk %s: %lu bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); - - hr = advance_stream(stream, chunk.dwSize - sizeof(chunk.fccID)); - if (FAILED(hr)) - goto error; - - size = subtract_bytes(size, chunk.dwSize - sizeof(chunk.fccID)); - break; - } - break; - } - - default: - TRACE("Unknown chunk %s: %lu bytes\n", debugstr_fourcc(chunk.fccID), chunk.dwSize); - - hr = advance_stream(stream, chunk.dwSize); - if (FAILED(hr)) - goto error; + case MAKE_IDTYPE(FOURCC_LIST, FOURCC_INS): + hr = parse_ins_chunk(This, stream, &chunk); + break; - break; + default: + WARN("Invalid instrument chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + goto error; } } - This->loaded = TRUE; + if (FAILED(hr)) goto error; + This->loaded = TRUE; return S_OK; error: + stream_skip_chunk(stream, &chunk); return DMUS_E_UNSUPPORTED_STREAM; } From cb077a910ee80293088417c97aab046910ca7cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 7 Sep 2023 10:12:29 +0200 Subject: [PATCH 0840/1148] dmusic: Allocate and parse instruments in a single pass. (cherry picked from commit ff4cb785cbb4f9a5ab2fc16293edc7ea9fb097fc) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 68 +++--------------------------------- dlls/dmusic/dmusic_private.h | 7 +--- dlls/dmusic/instrument.c | 46 +++++++++++++++--------- 3 files changed, 35 insertions(+), 86 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 5332adff084..cca23870f9d 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -115,7 +115,6 @@ static HRESULT WINAPI collection_GetInstrument(IDirectMusicCollection *iface, if (patch == inst_patch) { *instrument = inst_entry->pInstrument; IDirectMusicInstrument_AddRef(inst_entry->pInstrument); - instrument_load(inst_entry->pInstrument, This->pStm); TRACE(": returning instrument %p\n", *instrument); return S_OK; } @@ -212,7 +211,6 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, DMUS_PRIVATE_CHUNK chunk; DWORD StreamSize, StreamCount, ListSize[2], ListCount[2]; LARGE_INTEGER liMove; /* used when skipping chunks */ - ULARGE_INTEGER dlibInstrumentPosition; IStream_AddRef(stream); /* add count for later references */ This->pStm = stream; @@ -374,68 +372,12 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, ListCount[1] = 0; switch (chunk.fccID) { case FOURCC_INS: { - DMUS_PRIVATE_INSTRUMENTENTRY *new_instrument = calloc(1, sizeof(DMUS_PRIVATE_INSTRUMENTENTRY)); + DMUS_PRIVATE_INSTRUMENTENTRY *entry = calloc(1, sizeof(DMUS_PRIVATE_INSTRUMENTENTRY)); TRACE_(dmfile)(": instrument list\n"); - /* Only way to create this one... even M$ does it discretely */ - instrument_create(&new_instrument->pInstrument); - { - struct instrument *instrument = impl_from_IDirectMusicInstrument(new_instrument->pInstrument); - /* Store offset and length, they will be needed when loading the instrument */ - liMove.QuadPart = 0; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, &dlibInstrumentPosition); - instrument->liInstrumentPosition.QuadPart = dlibInstrumentPosition.QuadPart; - instrument->length = ListSize[1]; - do { - IStream_Read(stream, &chunk, sizeof(FOURCC) + sizeof(DWORD), NULL); - ListCount[1] += sizeof(FOURCC) + sizeof(DWORD) + chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %#04lx)", debugstr_fourcc(chunk.fccID), chunk.dwSize); - switch (chunk.fccID) { - case FOURCC_INSH: { - TRACE_(dmfile)(": instrument header chunk\n"); - IStream_Read(stream, &instrument->header, chunk.dwSize, NULL); - break; - } - case FOURCC_DLID: { - TRACE_(dmfile)(": DLID (GUID) chunk\n"); - IStream_Read(stream, &instrument->id, chunk.dwSize, NULL); - break; - } - case FOURCC_LIST: { - IStream_Read(stream, &chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": LIST chunk of type %s", debugstr_fourcc(chunk.fccID)); - switch (chunk.fccID) { - default: { - TRACE_(dmfile)(": unknown (skipping)\n"); - liMove.QuadPart = chunk.dwSize - sizeof(FOURCC); - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = chunk.dwSize; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - TRACE_(dmfile)(": ListCount[1] = %ld < ListSize[1] = %ld\n", ListCount[1], ListSize[1]); - } while (ListCount[1] < ListSize[1]); - /* DEBUG: dumps whole instrument object tree: */ - if (TRACE_ON(dmusic)) { - TRACE("*** IDirectMusicInstrument (%p) ***\n", instrument); - if (!IsEqualGUID(&instrument->id, &GUID_NULL)) - TRACE(" - GUID = %s\n", debugstr_dmguid(&instrument->id)); - TRACE(" - Instrument header:\n"); - TRACE(" - cRegions: %ld\n", instrument->header.cRegions); - TRACE(" - Locale:\n"); - TRACE(" - ulBank: %ld\n", instrument->header.Locale.ulBank); - TRACE(" - ulInstrument: %ld\n", instrument->header.Locale.ulInstrument); - TRACE(" => dwPatch: %ld\n", MIDILOCALE2Patch(&instrument->header.Locale)); - } - list_add_tail(&This->Instruments, &new_instrument->entry); - } + liMove.QuadPart = -12; + IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); + if (FAILED(instrument_create_from_stream(stream, &entry->pInstrument))) free(entry); + else list_add_tail(&This->Instruments, &entry->entry); break; } } diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 331aa683f4b..fca76105b20 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -98,7 +98,7 @@ extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface); -extern HRESULT instrument_create(IDirectMusicInstrument **ret_iface); +extern HRESULT instrument_create_from_stream(IStream *stream, IDirectMusicInstrument **ret_iface); /***************************************************************************** * IDirectMusic8Impl implementation structure @@ -178,13 +178,10 @@ struct instrument IDirectMusicInstrument IDirectMusicInstrument_iface; LONG ref; - LARGE_INTEGER liInstrumentPosition; /* offset in a stream where instrument chunk can be found */ - ULONG length; /* Length of the instrument in the stream */ GUID id; INSTHEADER header; WCHAR wszName[DMUS_MAX_NAME]; /* instrument data */ - BOOL loaded; struct list articulations; struct list regions; @@ -195,8 +192,6 @@ static inline struct instrument *impl_from_IDirectMusicInstrument(IDirectMusicIn return CONTAINING_RECORD(iface, struct instrument, IDirectMusicInstrument_iface); } -extern HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream); - /***************************************************************************** * Misc. */ diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index cbcebcb5902..b62b053c542 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -135,7 +135,7 @@ static const IDirectMusicInstrumentVtbl instrument_vtbl = instrument_SetPatch, }; -HRESULT instrument_create(IDirectMusicInstrument **ret_iface) +static HRESULT instrument_create(IDirectMusicInstrument **ret_iface) { struct instrument *instrument; @@ -277,8 +277,11 @@ static HRESULT parse_ins_chunk(struct instrument *This, IStream *stream, struct switch (MAKE_IDTYPE(chunk.id, chunk.type)) { case FOURCC_INSH: + hr = stream_chunk_get_data(stream, &chunk, &This->header, sizeof(This->header)); + break; + case FOURCC_DLID: - /* Instrument header and id are already set so just skip */ + hr = stream_chunk_get_data(stream, &chunk, &This->id, sizeof(This->id)); break; case MAKE_IDTYPE(FOURCC_LIST, FOURCC_LRGN): @@ -300,24 +303,17 @@ static HRESULT parse_ins_chunk(struct instrument *This, IStream *stream, struct return hr; } -/* Function that loads all instrument data and which is called from IDirectMusicCollection_GetInstrument as in native */ -HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) +HRESULT instrument_create_from_stream(IStream *stream, IDirectMusicInstrument **ret_iface) { - struct instrument *This = impl_from_IDirectMusicInstrument(iface); struct chunk_entry chunk = {0}; + IDirectMusicInstrument *iface; + struct instrument *This; HRESULT hr; - TRACE("(%p, %p): offset = 0x%s, length = %lu)\n", This, stream, wine_dbgstr_longlong(This->liInstrumentPosition.QuadPart), This->length); + TRACE("(%p, %p)\n", stream, ret_iface); - if (This->loaded) - return S_OK; - - hr = IStream_Seek(stream, This->liInstrumentPosition, STREAM_SEEK_SET, NULL); - if (FAILED(hr)) - { - WARN("IStream_Seek failed: %08lx\n", hr); - return DMUS_E_UNSUPPORTED_STREAM; - } + if (FAILED(hr = instrument_create(&iface))) return hr; + This = impl_from_IDirectMusicInstrument(iface); if ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { @@ -329,16 +325,32 @@ HRESULT instrument_load(IDirectMusicInstrument *iface, IStream *stream) default: WARN("Invalid instrument chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); - goto error; + hr = E_INVALIDARG; + break; } } if (FAILED(hr)) goto error; - This->loaded = TRUE; + if (TRACE_ON(dmusic)) + { + TRACE("Created DirectMusicInstrument (%p) ***\n", This); + if (!IsEqualGUID(&This->id, &GUID_NULL)) + TRACE(" - GUID = %s\n", debugstr_dmguid(&This->id)); + TRACE(" - Instrument header:\n"); + TRACE(" - cRegions: %ld\n", This->header.cRegions); + TRACE(" - Locale:\n"); + TRACE(" - ulBank: %ld\n", This->header.Locale.ulBank); + TRACE(" - ulInstrument: %ld\n", This->header.Locale.ulInstrument); + TRACE(" => dwPatch: %ld\n", MIDILOCALE2Patch(&This->header.Locale)); + } + + *ret_iface = iface; return S_OK; error: + IDirectMusicInstrument_Release(iface); + stream_skip_chunk(stream, &chunk); return DMUS_E_UNSUPPORTED_STREAM; } From 857981af5f6ef992925956b7f0076485db6fa6a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 13:27:36 +0200 Subject: [PATCH 0841/1148] dmusic: Cleanup collection instrument iteration loops. (cherry picked from commit 572125edf622d1cf00025e47a2f487ef42b6bb15) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 83 ++++++++++++++++++------------------ dlls/dmusic/dmusic_private.h | 5 --- 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index cca23870f9d..0b4e23b34f4 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -24,6 +24,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmusic); WINE_DECLARE_DEBUG_CHANNEL(dmfile); +struct instrument_entry +{ + struct list entry; + IDirectMusicInstrument *instrument; +}; + struct collection { IDirectMusicCollection IDirectMusicCollection_iface; @@ -36,8 +42,8 @@ struct collection /* pool table */ POOLTABLE *pPoolTable; POOLCUE *pPoolCues; - /* instruments */ - struct list Instruments; + + struct list instruments; }; static inline struct collection *impl_from_IDirectMusicCollection(IDirectMusicCollection *iface) @@ -103,25 +109,25 @@ static HRESULT WINAPI collection_GetInstrument(IDirectMusicCollection *iface, DWORD patch, IDirectMusicInstrument **instrument) { struct collection *This = impl_from_IDirectMusicCollection(iface); - DMUS_PRIVATE_INSTRUMENTENTRY *inst_entry; - struct list *list_entry; + struct instrument_entry *entry; DWORD inst_patch; + HRESULT hr; TRACE("(%p, %lu, %p)\n", iface, patch, instrument); - LIST_FOR_EACH(list_entry, &This->Instruments) { - inst_entry = LIST_ENTRY(list_entry, DMUS_PRIVATE_INSTRUMENTENTRY, entry); - IDirectMusicInstrument_GetPatch(inst_entry->pInstrument, &inst_patch); - if (patch == inst_patch) { - *instrument = inst_entry->pInstrument; - IDirectMusicInstrument_AddRef(inst_entry->pInstrument); - TRACE(": returning instrument %p\n", *instrument); + LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry) + { + if (FAILED(hr = IDirectMusicInstrument_GetPatch(entry->instrument, &inst_patch))) return hr; + if (patch == inst_patch) + { + *instrument = entry->instrument; + IDirectMusicInstrument_AddRef(entry->instrument); + TRACE(": returning instrument %p\n", entry->instrument); return S_OK; } } TRACE(": instrument not found\n"); - return DMUS_E_INVALIDPATCH; } @@ -129,26 +135,23 @@ static HRESULT WINAPI collection_EnumInstrument(IDirectMusicCollection *iface, DWORD index, DWORD *patch, LPWSTR name, DWORD name_length) { struct collection *This = impl_from_IDirectMusicCollection(iface); - DWORD i = 0; - DMUS_PRIVATE_INSTRUMENTENTRY *inst_entry; - struct list *list_entry; - DWORD length; + struct instrument_entry *entry; + HRESULT hr; TRACE("(%p, %ld, %p, %p, %ld)\n", iface, index, patch, name, name_length); - LIST_FOR_EACH(list_entry, &This->Instruments) { - inst_entry = LIST_ENTRY(list_entry, DMUS_PRIVATE_INSTRUMENTENTRY, entry); - if (i == index) { - struct instrument *instrument = impl_from_IDirectMusicInstrument(inst_entry->pInstrument); - IDirectMusicInstrument_GetPatch(inst_entry->pInstrument, patch); - if (name) { - length = min(lstrlenW(instrument->wszName), name_length - 1); - memcpy(name, instrument->wszName, length * sizeof(WCHAR)); - name[length] = '\0'; - } - return S_OK; + LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry) + { + if (index--) continue; + if (FAILED(hr = IDirectMusicInstrument_GetPatch(entry->instrument, patch))) return hr; + if (name) + { + struct instrument *instrument = impl_from_IDirectMusicInstrument(entry->instrument); + DWORD length = min(lstrlenW(instrument->wszName), name_length - 1); + memcpy(name, instrument->wszName, length * sizeof(WCHAR)); + name[length] = '\0'; } - i++; + return S_OK; } return S_FALSE; @@ -372,12 +375,12 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, ListCount[1] = 0; switch (chunk.fccID) { case FOURCC_INS: { - DMUS_PRIVATE_INSTRUMENTENTRY *entry = calloc(1, sizeof(DMUS_PRIVATE_INSTRUMENTENTRY)); + struct instrument_entry *entry = calloc(1, sizeof(*entry)); TRACE_(dmfile)(": instrument list\n"); liMove.QuadPart = -12; IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - if (FAILED(instrument_create_from_stream(stream, &entry->pInstrument))) free(entry); - else list_add_tail(&This->Instruments, &entry->entry); + if (FAILED(instrument_create_from_stream(stream, &entry->instrument))) free(entry); + else list_add_tail(&This->instruments, &entry->entry); break; } } @@ -417,10 +420,10 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, /* DEBUG: dumps whole collection object tree: */ - if (TRACE_ON(dmusic)) { - int r = 0; - DMUS_PRIVATE_INSTRUMENTENTRY *tmpEntry; - struct list *listEntry; + if (TRACE_ON(dmusic)) + { + struct instrument_entry *entry; + int i = 0; TRACE("*** IDirectMusicCollection (%p) ***\n", &This->IDirectMusicCollection_iface); dump_DMUS_OBJECTDESC(&This->dmobj.desc); @@ -429,11 +432,8 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, TRACE(" - cInstruments: %ld\n", This->pHeader->cInstruments); TRACE(" - Instruments:\n"); - LIST_FOR_EACH(listEntry, &This->Instruments) { - tmpEntry = LIST_ENTRY( listEntry, DMUS_PRIVATE_INSTRUMENTENTRY, entry ); - TRACE(" - Instrument[%i]: %p\n", r, tmpEntry->pInstrument); - r++; - } + LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry) + TRACE(" - Instrument[%i]: %p\n", i++, entry->instrument); } return S_OK; @@ -463,8 +463,7 @@ HRESULT collection_create(IUnknown **ret_iface) (IUnknown *)&collection->IDirectMusicCollection_iface); collection->dmobj.IDirectMusicObject_iface.lpVtbl = &collection_object_vtbl; collection->dmobj.IPersistStream_iface.lpVtbl = &collection_stream_vtbl; - - list_init(&collection->Instruments); + list_init(&collection->instruments); TRACE("Created DirectMusicCollection %p\n", collection); *ret_iface = (IUnknown *)&collection->IDirectMusicCollection_iface; diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index fca76105b20..b668f2f2559 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -164,11 +164,6 @@ struct IReferenceClockImpl { DMUS_CLOCKINFO pClockInfo; }; -typedef struct _DMUS_PRIVATE_INSTRUMENT_ENTRY { - struct list entry; /* for listing elements */ - IDirectMusicInstrument* pInstrument; -} DMUS_PRIVATE_INSTRUMENTENTRY, *LPDMUS_PRIVATE_INSTRUMENTENTRY; - typedef struct _DMUS_PRIVATE_POOLCUE { struct list entry; /* for listing elements */ } DMUS_PRIVATE_POOLCUE, *LPDMUS_PRIVATE_POOLCUE; From 412a4143836f316f3add05e359e8fc0f17d8dd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 14:51:56 +0200 Subject: [PATCH 0842/1148] dmusic: Stop leaking instruments on collection release. (cherry picked from commit 4ae866a0a7f65a4a4d2c95c5c98145b02d63528e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 0b4e23b34f4..2d4a7f855ae 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -98,7 +98,18 @@ static ULONG WINAPI collection_Release(IDirectMusicCollection *iface) TRACE("(%p): new ref = %lu\n", iface, ref); - if (!ref) { + if (!ref) + { + struct instrument_entry *instrument_entry; + void *next; + + LIST_FOR_EACH_ENTRY_SAFE(instrument_entry, next, &This->instruments, struct instrument_entry, entry) + { + list_remove(&instrument_entry->entry); + IDirectMusicInstrument_Release(instrument_entry->instrument); + free(instrument_entry); + } + free(This); } From efa8b7da9113058e9cf1edc00e0f69c72dc2de5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 22 Aug 2023 17:40:22 +0200 Subject: [PATCH 0843/1148] dmime: Always return S_FALSE from DllCanUnloadNow. (cherry picked from commit 6e1e7a88fd5d50dd9852ae6106ddedf5506311bc) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/audiopath.c | 2 -- dlls/dmime/dmime_main.c | 22 ---------------------- dlls/dmime/dmime_private.h | 7 ------- dlls/dmime/graph.c | 2 -- dlls/dmime/lyricstrack.c | 2 -- dlls/dmime/markertrack.c | 2 -- dlls/dmime/paramcontroltrack.c | 2 -- dlls/dmime/performance.c | 4 ---- dlls/dmime/segment.c | 3 --- dlls/dmime/segmentstate.c | 4 ---- dlls/dmime/segtriggertrack.c | 2 -- dlls/dmime/seqtrack.c | 2 -- dlls/dmime/sysextrack.c | 2 -- dlls/dmime/tempotrack.c | 2 -- dlls/dmime/timesigtrack.c | 2 -- dlls/dmime/wavetrack.c | 2 -- 16 files changed, 62 deletions(-) diff --git a/dlls/dmime/audiopath.c b/dlls/dmime/audiopath.c index 0857ed77c48..0d7df17bba6 100644 --- a/dlls/dmime/audiopath.c +++ b/dlls/dmime/audiopath.c @@ -96,7 +96,6 @@ static ULONG WINAPI IDirectMusicAudioPathImpl_AddRef (IDirectMusicAudioPath *ifa TRACE("(%p): ref=%ld\n", This, ref); - DMIME_LockModule(); return ref; } @@ -116,7 +115,6 @@ static ULONG WINAPI IDirectMusicAudioPathImpl_Release (IDirectMusicAudioPath *if HeapFree(GetProcessHeap(), 0, This); } - DMIME_UnlockModule(); return ref; } diff --git a/dlls/dmime/dmime_main.c b/dlls/dmime/dmime_main.c index c24cf944af5..c0aa4e31cfb 100644 --- a/dlls/dmime/dmime_main.c +++ b/dlls/dmime/dmime_main.c @@ -38,8 +38,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmime); -LONG DMIME_refCount = 0; - typedef struct { IClassFactory IClassFactory_iface; HRESULT (*fnCreateInstance)(REFIID riid, void **ret_iface); @@ -75,15 +73,11 @@ static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID r static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) { - DMIME_LockModule(); - return 2; /* non-heap based object */ } static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) { - DMIME_UnlockModule(); - return 1; /* non-heap based object */ } @@ -103,12 +97,6 @@ static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock) { TRACE("(%d)\n", dolock); - - if (dolock) - DMIME_LockModule(); - else - DMIME_UnlockModule(); - return S_OK; } @@ -136,16 +124,6 @@ static IClassFactoryImpl SegTriggerTrack_CF = {{&classfactory_vtbl}, create_dmse static IClassFactoryImpl AudioPath_CF = {{&classfactory_vtbl}, create_dmaudiopath}; static IClassFactoryImpl WaveTrack_CF = {{&classfactory_vtbl}, create_dmwavetrack}; -/****************************************************************** - * DllCanUnloadNow (DMIME.1) - * - * - */ -HRESULT WINAPI DllCanUnloadNow(void) -{ - return DMIME_refCount != 0 ? S_FALSE : S_OK; -} - /****************************************************************** * DllGetClassObject (DMIME.@) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 7cdc1534866..6102fd5ec2f 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -96,13 +96,6 @@ typedef struct _DMUS_PRIVATE_TEMPO_PLAY_STATE { DWORD dummy; } DMUS_PRIVATE_TEMPO_PLAY_STATE, *LPDMUS_PRIVATE_TEMPO_PLAY_STATE; -/********************************************************************** - * Dll lifetime tracking declaration for dmime.dll - */ -extern LONG DMIME_refCount; -static inline void DMIME_LockModule(void) { InterlockedIncrement( &DMIME_refCount ); } -static inline void DMIME_UnlockModule(void) { InterlockedDecrement( &DMIME_refCount ); } - /***************************************************************************** * Misc. */ diff --git a/dlls/dmime/graph.c b/dlls/dmime/graph.c index 6cb719025c1..8b0fb246a74 100644 --- a/dlls/dmime/graph.c +++ b/dlls/dmime/graph.c @@ -75,7 +75,6 @@ static ULONG WINAPI DirectMusicGraph_AddRef(IDirectMusicGraph *iface) TRACE("(%p): %ld\n", This, ref); - DMIME_LockModule(); return ref; } @@ -89,7 +88,6 @@ static ULONG WINAPI DirectMusicGraph_Release(IDirectMusicGraph *iface) if (ref == 0) HeapFree(GetProcessHeap(), 0, This); - DMIME_UnlockModule(); return ref; } diff --git a/dlls/dmime/lyricstrack.c b/dlls/dmime/lyricstrack.c index 4983fe99a2d..ea4529fb4c7 100644 --- a/dlls/dmime/lyricstrack.c +++ b/dlls/dmime/lyricstrack.c @@ -84,7 +84,6 @@ static ULONG WINAPI lyrics_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMIME_UnlockModule(); } return ref; @@ -359,7 +358,6 @@ HRESULT create_dmlyricstrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMIME_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmime/markertrack.c b/dlls/dmime/markertrack.c index 7749d9abb08..8be2b937d1b 100644 --- a/dlls/dmime/markertrack.c +++ b/dlls/dmime/markertrack.c @@ -78,7 +78,6 @@ static ULONG WINAPI IDirectMusicTrackImpl_Release(IDirectMusicTrack *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMIME_UnlockModule(); } return ref; @@ -237,7 +236,6 @@ HRESULT create_dmmarkertrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMIME_LockModule(); hr = IDirectMusicTrack_QueryInterface(&track->IDirectMusicTrack_iface, lpcGUID, ppobj); IDirectMusicTrack_Release(&track->IDirectMusicTrack_iface); diff --git a/dlls/dmime/paramcontroltrack.c b/dlls/dmime/paramcontroltrack.c index 641fd73baa3..d6f3844f60c 100644 --- a/dlls/dmime/paramcontroltrack.c +++ b/dlls/dmime/paramcontroltrack.c @@ -79,7 +79,6 @@ static ULONG WINAPI paramcontrol_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMIME_UnlockModule(); } return ref; @@ -273,7 +272,6 @@ HRESULT create_dmparamcontroltrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMIME_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index d69a27540d6..5e80a0d1b54 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -278,8 +278,6 @@ static ULONG WINAPI IDirectMusicPerformance8Impl_AddRef(IDirectMusicPerformance8 TRACE("(%p): ref=%ld\n", This, ref); - DMIME_LockModule(); - return ref; } @@ -297,8 +295,6 @@ static ULONG WINAPI IDirectMusicPerformance8Impl_Release(IDirectMusicPerformance HeapFree(GetProcessHeap(), 0, This); } - DMIME_UnlockModule(); - return ref; } diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index c005d506cad..457b6678759 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -94,7 +94,6 @@ static ULONG WINAPI IDirectMusicSegment8Impl_Release(IDirectMusicSegment8 *iface free(This->wave_data); HeapFree(GetProcessHeap(), 0, This); - DMIME_UnlockModule(); } return ref; @@ -935,8 +934,6 @@ IDirectMusicSegment8Impl *create_segment(void) obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; list_init (&obj->Tracks); - DMIME_LockModule(); - return obj; } diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index 21544fad773..a67079a2daf 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -63,8 +63,6 @@ static ULONG WINAPI DirectMusicSegmentState8_AddRef(IDirectMusicSegmentState8 *i TRACE("(%p): %ld\n", This, ref); - DMIME_LockModule(); - return ref; } @@ -78,8 +76,6 @@ static ULONG WINAPI DirectMusicSegmentState8_Release(IDirectMusicSegmentState8 * if (ref == 0) HeapFree(GetProcessHeap(), 0, This); - DMIME_UnlockModule(); - return ref; } diff --git a/dlls/dmime/segtriggertrack.c b/dlls/dmime/segtriggertrack.c index 1b7fc6d951c..a04561450f6 100644 --- a/dlls/dmime/segtriggertrack.c +++ b/dlls/dmime/segtriggertrack.c @@ -102,7 +102,6 @@ static ULONG WINAPI segment_track_Release(IDirectMusicTrack8 *iface) } heap_free(This); - DMIME_UnlockModule(); } return ref; @@ -399,7 +398,6 @@ HRESULT create_dmsegtriggertrack(REFIID lpcGUID, void **ppobj) track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; list_init(&track->Items); - DMIME_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmime/seqtrack.c b/dlls/dmime/seqtrack.c index 743be10e5c6..2fae8bd5dc1 100644 --- a/dlls/dmime/seqtrack.c +++ b/dlls/dmime/seqtrack.c @@ -79,7 +79,6 @@ static ULONG WINAPI sequence_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMIME_UnlockModule(); } return ref; @@ -273,7 +272,6 @@ HRESULT create_dmseqtrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMIME_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmime/sysextrack.c b/dlls/dmime/sysextrack.c index d3ff9a051b8..ed44ba50a48 100644 --- a/dlls/dmime/sysextrack.c +++ b/dlls/dmime/sysextrack.c @@ -79,7 +79,6 @@ static ULONG WINAPI sysex_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMIME_UnlockModule(); } return ref; @@ -272,7 +271,6 @@ HRESULT create_dmsysextrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMIME_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmime/tempotrack.c b/dlls/dmime/tempotrack.c index a3cbffc341a..2bfac9ba059 100644 --- a/dlls/dmime/tempotrack.c +++ b/dlls/dmime/tempotrack.c @@ -87,7 +87,6 @@ static ULONG WINAPI tempo_track_Release(IDirectMusicTrack8 *iface) if (!ref) { heap_free(This->items); heap_free(This); - DMIME_UnlockModule(); } return ref; @@ -386,7 +385,6 @@ HRESULT create_dmtempotrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMIME_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmime/timesigtrack.c b/dlls/dmime/timesigtrack.c index 1f4c0dbf187..3e16895eddf 100644 --- a/dlls/dmime/timesigtrack.c +++ b/dlls/dmime/timesigtrack.c @@ -88,7 +88,6 @@ static ULONG WINAPI IDirectMusicTrackImpl_Release(IDirectMusicTrack *iface) if (!ref) { heap_free(This->items); HeapFree(GetProcessHeap(), 0, This); - DMIME_UnlockModule(); } return ref; @@ -301,7 +300,6 @@ HRESULT create_dmtimesigtrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMIME_LockModule(); hr = IDirectMusicTrack_QueryInterface(&track->IDirectMusicTrack_iface, lpcGUID, ppobj); IDirectMusicTrack_Release(&track->IDirectMusicTrack_iface); diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index d87d16fcdab..b532ace3ce9 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -113,7 +113,6 @@ static ULONG WINAPI wave_track_Release(IDirectMusicTrack8 *iface) } heap_free(This); - DMIME_UnlockModule(); } return ref; @@ -488,7 +487,6 @@ HRESULT create_dmwavetrack(REFIID lpcGUID, void **ppobj) track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; list_init(&track->parts); - DMIME_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); From 1653fb1f28db857bd1017d7cf0d8f2a31ad701d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 21:29:38 +0200 Subject: [PATCH 0844/1148] dmime: Use CRT allocation functions. (cherry picked from commit 0feb0cda7498c7fd1c658782eb9ff1f2966204a5) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/audiopath.c | 9 +++------ dlls/dmime/dmobject.c | 8 +++----- dlls/dmime/graph.c | 11 +++-------- dlls/dmime/lyricstrack.c | 9 +++------ dlls/dmime/markertrack.c | 9 +++------ dlls/dmime/paramcontroltrack.c | 9 +++------ dlls/dmime/performance.c | 28 ++++++++++------------------ dlls/dmime/segment.c | 18 +++++++++--------- dlls/dmime/segmentstate.c | 9 ++------- dlls/dmime/segtriggertrack.c | 18 ++++++------------ dlls/dmime/seqtrack.c | 9 +++------ dlls/dmime/sysextrack.c | 9 +++------ dlls/dmime/tempotrack.c | 19 ++++++------------- dlls/dmime/timesigtrack.c | 12 ++++-------- dlls/dmime/wavetrack.c | 24 +++++++++--------------- 15 files changed, 70 insertions(+), 131 deletions(-) diff --git a/dlls/dmime/audiopath.c b/dlls/dmime/audiopath.c index 0d7df17bba6..013939f9dfa 100644 --- a/dlls/dmime/audiopath.c +++ b/dlls/dmime/audiopath.c @@ -112,7 +112,7 @@ static ULONG WINAPI IDirectMusicAudioPathImpl_Release (IDirectMusicAudioPath *if if (This->pDSBuffer) IDirectSoundBuffer_Release(This->pDSBuffer); This->pPerf = NULL; - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -328,11 +328,8 @@ HRESULT create_dmaudiopath(REFIID riid, void **ppobj) IDirectMusicAudioPathImpl* obj; HRESULT hr; - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicAudioPathImpl)); - if (NULL == obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicAudioPath_iface.lpVtbl = &DirectMusicAudioPathVtbl; obj->ref = 1; dmobject_init(&obj->dmobj, &CLSID_DirectMusicAudioPathConfig, diff --git a/dlls/dmime/dmobject.c b/dlls/dmime/dmobject.c index b526b23d031..e080211dd11 100644 --- a/dlls/dmime/dmobject.c +++ b/dlls/dmime/dmobject.c @@ -28,7 +28,6 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -375,7 +374,7 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) /* Reads chunk data of the form: DWORD - size of array element element[] - Array of elements - The caller needs to heap_free() the array. + The caller needs to free() the array. */ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, unsigned int *count, DWORD elem_size) @@ -400,10 +399,9 @@ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, *count = (chunk->size - sizeof(DWORD)) / elem_size; size = *count * elem_size; - if (!(*array = heap_alloc(size))) - return E_OUTOFMEMORY; + if (!(*array = malloc(size))) return E_OUTOFMEMORY; if (FAILED(hr = stream_read(stream, *array, size))) { - heap_free(*array); + free(*array); *array = NULL; return hr; } diff --git a/dlls/dmime/graph.c b/dlls/dmime/graph.c index 8b0fb246a74..8ac037e43bc 100644 --- a/dlls/dmime/graph.c +++ b/dlls/dmime/graph.c @@ -85,8 +85,7 @@ static ULONG WINAPI DirectMusicGraph_Release(IDirectMusicGraph *iface) TRACE("(%p): %ld\n", This, ref); - if (ref == 0) - HeapFree(GetProcessHeap(), 0, This); + if (!ref) free(This); return ref; } @@ -127,7 +126,7 @@ static HRESULT WINAPI DirectMusicGraph_InsertTool(IDirectMusicGraph *iface, IDir } ++This->num_tools; - pNewTool = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_GRAPH_TOOL)); + pNewTool = calloc(1, sizeof(*pNewTool)); pNewTool->pTool = pTool; pNewTool->dwIndex = lIndex; IDirectMusicTool8_AddRef(pTool); @@ -255,11 +254,7 @@ HRESULT create_dmgraph(REFIID riid, void **ret_iface) HRESULT hr; *ret_iface = NULL; - - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicGraphImpl)); - if (!obj) - return E_OUTOFMEMORY; - + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicGraph_iface.lpVtbl = &DirectMusicGraphVtbl; obj->ref = 1; dmobject_init(&obj->dmobj, &CLSID_DirectMusicGraph, (IUnknown *)&obj->IDirectMusicGraph_iface); diff --git a/dlls/dmime/lyricstrack.c b/dlls/dmime/lyricstrack.c index ea4529fb4c7..a1b299e9a7c 100644 --- a/dlls/dmime/lyricstrack.c +++ b/dlls/dmime/lyricstrack.c @@ -83,7 +83,7 @@ static ULONG WINAPI lyrics_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); if (!ref) { - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -347,11 +347,8 @@ HRESULT create_dmlyricstrack(REFIID lpcGUID, void **ppobj) IDirectMusicLyricsTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicLyricsTrack, diff --git a/dlls/dmime/markertrack.c b/dlls/dmime/markertrack.c index 8be2b937d1b..460e708bc5d 100644 --- a/dlls/dmime/markertrack.c +++ b/dlls/dmime/markertrack.c @@ -77,7 +77,7 @@ static ULONG WINAPI IDirectMusicTrackImpl_Release(IDirectMusicTrack *iface) TRACE("(%p) ref=%ld\n", This, ref); if (!ref) { - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -225,11 +225,8 @@ HRESULT create_dmmarkertrack(REFIID lpcGUID, void **ppobj) IDirectMusicMarkerTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack_iface.lpVtbl = &dmtrack_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicMarkerTrack, diff --git a/dlls/dmime/paramcontroltrack.c b/dlls/dmime/paramcontroltrack.c index d6f3844f60c..9d32484bfd7 100644 --- a/dlls/dmime/paramcontroltrack.c +++ b/dlls/dmime/paramcontroltrack.c @@ -78,7 +78,7 @@ static ULONG WINAPI paramcontrol_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); if (!ref) { - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -261,11 +261,8 @@ HRESULT create_dmparamcontroltrack(REFIID lpcGUID, void **ppobj) IDirectMusicParamControlTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicParamControlTrack, diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 5e80a0d1b54..a1acc6d97cc 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -19,7 +19,6 @@ */ #include "dmime_private.h" -#include "wine/heap.h" #include "wine/rbtree.h" #include "dmobject.h" @@ -133,15 +132,15 @@ static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) { for (it = This->imm_head; NULL != it; ) { it_next = it->next; - cur = ProceedMsg(This, it); - HeapFree(GetProcessHeap(), 0, cur); + cur = ProceedMsg(This, it); + free(cur); it = it_next; } for (it = This->head; NULL != it && it->rtItemTime < rtCurTime + dwDec; ) { it_next = it->next; cur = ProceedMsg(This, it); - HeapFree(GetProcessHeap(), 0, cur); + free(cur); it = it_next; } if (NULL != it) { @@ -215,7 +214,7 @@ static void pchannel_block_free(struct wine_rb_entry *entry, void *context) { struct pchannel_block *b = WINE_RB_ENTRY_VALUE(entry, struct pchannel_block, entry); - heap_free(b); + free(b); } static struct pchannel_block *pchannel_block_set(struct wine_rb_tree *tree, DWORD block_num, @@ -231,8 +230,7 @@ static struct pchannel_block *pchannel_block_set(struct wine_rb_tree *tree, DWOR if (only_set_new) return block; } else { - if (!(block = heap_alloc(sizeof(*block)))) - return NULL; + if (!(block = malloc(sizeof(*block)))) return NULL; block->block_num = block_num; } @@ -292,7 +290,7 @@ static ULONG WINAPI IDirectMusicPerformance8Impl_Release(IDirectMusicPerformance wine_rb_destroy(&This->pchannels, pchannel_block_free, NULL); This->safe.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->safe); - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -501,10 +499,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_AllocPMsg(IDirectMusicPerform if (NULL == ppPMSG) { return E_POINTER; } - pItem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb - sizeof(DMUS_PMSG) + sizeof(DMUS_PMSGItem)); - if (NULL == pItem) { - return E_OUTOFMEMORY; - } + if (!(pItem = calloc(1, cb - sizeof(DMUS_PMSG) + sizeof(DMUS_PMSGItem)))) return E_OUTOFMEMORY; pItem->pMsg.dwSize = cb; *ppPMSG = DMUS_ItemToPMSG(pItem); return S_OK; @@ -540,7 +535,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_FreePMsg(IDirectMusicPerforma if (pPMSG->punkUser) IUnknown_Release(pPMSG->punkUser); - HeapFree(GetProcessHeap(), 0, pItem); + free(pItem); return S_OK; } @@ -1286,11 +1281,8 @@ HRESULT create_dmperformance(REFIID lpcGUID, void **ppobj) TRACE("(%s, %p)\n", debugstr_guid(lpcGUID), ppobj); - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicPerformance8Impl)); - if (NULL == obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicPerformance8_iface.lpVtbl = &DirectMusicPerformance8_Vtbl; obj->ref = 0; /* will be inited by QueryInterface */ obj->pDefaultPath = NULL; diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 457b6678759..39a5333fbbb 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -93,7 +93,7 @@ static ULONG WINAPI IDirectMusicSegment8Impl_Release(IDirectMusicSegment8 *iface if (This->wave_data) free(This->wave_data); - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -266,9 +266,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_InsertTrack(IDirectMusicSegment8 } } - pNewSegTrack = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_SEGMENT_TRACK)); - if (NULL == pNewSegTrack) - return E_OUTOFMEMORY; + if (!(pNewSegTrack = calloc(1, sizeof(*pNewSegTrack)))) return E_OUTOFMEMORY; pNewSegTrack->dwGroupBits = group; pNewSegTrack->pTrack = pTrack; @@ -296,7 +294,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_RemoveTrack(IDirectMusicSegment8 list_remove(&pIt->entry); IDirectMusicTrack_Init(pIt->pTrack, NULL); IDirectMusicTrack_Release(pIt->pTrack); - HeapFree(GetProcessHeap(), 0, pIt); + free(pIt); return S_OK; } @@ -445,13 +443,16 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_Clone(IDirectMusicSegment8 *iface LIST_FOR_EACH_ENTRY(track_item, &This->Tracks, DMUS_PRIVATE_SEGMENT_TRACK, entry) { if (SUCCEEDED(hr = IDirectMusicTrack_Clone(track_item->pTrack, start, end, &track))) { - if ((cloned_item = HeapAlloc(GetProcessHeap(), 0, sizeof(*cloned_item)))) { + if ((cloned_item = malloc(sizeof(*cloned_item)))) + { cloned_item->dwGroupBits = track_item->dwGroupBits; cloned_item->flags = track_item->flags; cloned_item->pTrack = track; list_add_tail(&clone->Tracks, &cloned_item->entry); continue; - } else { + } + else + { IDirectMusicTrack_Release(track); } } @@ -924,8 +925,7 @@ IDirectMusicSegment8Impl *create_segment(void) { IDirectMusicSegment8Impl *obj; - if (!(obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj)))) - return NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return NULL; obj->IDirectMusicSegment8_iface.lpVtbl = &dmsegment8_vtbl; obj->ref = 1; diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index a67079a2daf..1a0b63419f0 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -73,8 +73,7 @@ static ULONG WINAPI DirectMusicSegmentState8_Release(IDirectMusicSegmentState8 * TRACE("(%p): %ld\n", This, ref); - if (ref == 0) - HeapFree(GetProcessHeap(), 0, This); + if (!ref) free(This); return ref; } @@ -146,11 +145,7 @@ HRESULT create_dmsegmentstate(REFIID riid, void **ret_iface) HRESULT hr; *ret_iface = NULL; - - obj = HeapAlloc (GetProcessHeap(), 0, sizeof(IDirectMusicSegmentState8Impl)); - if (!obj) - return E_OUTOFMEMORY; - + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicSegmentState8_iface.lpVtbl = &DirectMusicSegmentState8Vtbl; obj->ref = 1; diff --git a/dlls/dmime/segtriggertrack.c b/dlls/dmime/segtriggertrack.c index a04561450f6..dc29ede4963 100644 --- a/dlls/dmime/segtriggertrack.c +++ b/dlls/dmime/segtriggertrack.c @@ -21,8 +21,6 @@ #include "dmime_private.h" #include "dmobject.h" -#include "wine/heap.h" - WINE_DEFAULT_DEBUG_CHANNEL(dmime); /***************************************************************************** @@ -98,10 +96,10 @@ static ULONG WINAPI segment_track_Release(IDirectMusicTrack8 *iface) if (item->dmobj) IDirectMusicObject_Release(item->dmobj); - heap_free(item); + free(item); } - heap_free(This); + free(This); } return ref; @@ -274,8 +272,7 @@ static HRESULT parse_segment_item(IDirectMusicSegTriggerTrack *This, IStream *st /* First chunk is a header */ if (stream_get_chunk(stream, &chunk) != S_OK || chunk.id != DMUS_FOURCC_SEGMENTITEM_CHUNK) return DMUS_E_TRACK_HDR_NOT_FIRST_CK; - if (!(item = heap_alloc_zero(sizeof(*item)))) - return E_OUTOFMEMORY; + if (!(item = calloc(1, sizeof(*item)))) return E_OUTOFMEMORY; hr = stream_chunk_get_data(stream, &chunk, &item->header, sizeof(DMUS_IO_SEGMENT_ITEM_HEADER)); if (FAILED(hr)) goto error; @@ -311,7 +308,7 @@ static HRESULT parse_segment_item(IDirectMusicSegTriggerTrack *This, IStream *st return S_OK; error: - heap_free(item); + free(item); return hr; } @@ -386,11 +383,8 @@ HRESULT create_dmsegtriggertrack(REFIID lpcGUID, void **ppobj) IDirectMusicSegTriggerTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicSegTriggerTrack, diff --git a/dlls/dmime/seqtrack.c b/dlls/dmime/seqtrack.c index 2fae8bd5dc1..0e496a5131c 100644 --- a/dlls/dmime/seqtrack.c +++ b/dlls/dmime/seqtrack.c @@ -78,7 +78,7 @@ static ULONG WINAPI sequence_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); if (!ref) { - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -261,11 +261,8 @@ HRESULT create_dmseqtrack(REFIID lpcGUID, void **ppobj) IDirectMusicSeqTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicSeqTrack, diff --git a/dlls/dmime/sysextrack.c b/dlls/dmime/sysextrack.c index ed44ba50a48..f9c71abf08a 100644 --- a/dlls/dmime/sysextrack.c +++ b/dlls/dmime/sysextrack.c @@ -78,7 +78,7 @@ static ULONG WINAPI sysex_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); if (!ref) { - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -260,11 +260,8 @@ HRESULT create_dmsysextrack(REFIID lpcGUID, void **ppobj) IDirectMusicSysExTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicSysExTrack, diff --git a/dlls/dmime/tempotrack.c b/dlls/dmime/tempotrack.c index 2bfac9ba059..6704448b71e 100644 --- a/dlls/dmime/tempotrack.c +++ b/dlls/dmime/tempotrack.c @@ -21,8 +21,6 @@ #include "dmime_private.h" #include "dmobject.h" -#include "wine/heap.h" - WINE_DEFAULT_DEBUG_CHANNEL(dmime); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -85,8 +83,8 @@ static ULONG WINAPI tempo_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); if (!ref) { - heap_free(This->items); - heap_free(This); + free(This->items); + free(This); } return ref; @@ -109,9 +107,7 @@ static HRESULT WINAPI tempo_track_InitPlay(IDirectMusicTrack8 *iface, FIXME("(%p, %p, %p, %p, %ld, %ld): semi-stub\n", This, pSegmentState, pPerformance, ppStateData, dwVirtualTrack8ID, dwFlags); - pState = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_TEMPO_PLAY_STATE)); - if (NULL == pState) - return E_OUTOFMEMORY; + if (!(pState = calloc(1, sizeof(*pState)))) return E_OUTOFMEMORY; /** TODO real fill useful data */ pState->dummy = 0; @@ -131,7 +127,7 @@ static HRESULT WINAPI tempo_track_EndPlay(IDirectMusicTrack8 *iface, void *pStat return E_POINTER; } /** TODO real clean up */ - HeapFree(GetProcessHeap(), 0, pState); + free(pState); return S_OK; } @@ -374,11 +370,8 @@ HRESULT create_dmtempotrack(REFIID lpcGUID, void **ppobj) IDirectMusicTempoTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicTempoTrack, diff --git a/dlls/dmime/timesigtrack.c b/dlls/dmime/timesigtrack.c index 3e16895eddf..e98807b7503 100644 --- a/dlls/dmime/timesigtrack.c +++ b/dlls/dmime/timesigtrack.c @@ -19,7 +19,6 @@ #include "dmime_private.h" #include "dmobject.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); @@ -86,8 +85,8 @@ static ULONG WINAPI IDirectMusicTrackImpl_Release(IDirectMusicTrack *iface) TRACE("(%p) ref=%ld\n", This, ref); if (!ref) { - heap_free(This->items); - HeapFree(GetProcessHeap(), 0, This); + free(This->items); + free(This); } return ref; @@ -289,11 +288,8 @@ HRESULT create_dmtimesigtrack(REFIID lpcGUID, void **ppobj) IDirectMusicTimeSigTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack_iface.lpVtbl = &dmtack_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicTimeSigTrack, diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index b532ace3ce9..4150ea3bcd2 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -19,7 +19,6 @@ #include "dmime_private.h" #include "dmobject.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); @@ -107,12 +106,12 @@ static ULONG WINAPI wave_track_Release(IDirectMusicTrack8 *iface) list_remove(&item->entry); if (item->object) IDirectMusicObject_Release(item->object); - heap_free(item); + free(item); } - heap_free(part); + free(part); } - heap_free(This); + free(This); } return ref; @@ -323,8 +322,7 @@ static HRESULT parse_wave_item(struct wave_part *part, IStream *stream, struct c if (wave.id != FOURCC_LIST || wave.type != DMUS_FOURCC_WAVE_LIST) return DMUS_E_UNSUPPORTED_STREAM; - if (!(item = heap_alloc_zero(sizeof(*item)))) - return E_OUTOFMEMORY; + if (!(item = calloc(1, sizeof(*item)))) return E_OUTOFMEMORY; /* Wave item header chunk */ if (FAILED(hr = stream_next_chunk(stream, &chunk))) @@ -366,7 +364,7 @@ static HRESULT parse_wave_item(struct wave_part *part, IStream *stream, struct c return S_OK; error: - heap_free(item); + free(item); return hr; } @@ -383,8 +381,7 @@ static HRESULT parse_wave_part(IDirectMusicWaveTrack *This, IStream *stream, if (chunk.id != DMUS_FOURCC_WAVEPART_CHUNK) return DMUS_E_UNSUPPORTED_STREAM; - if (!(part = heap_alloc_zero(sizeof(*part)))) - return E_OUTOFMEMORY; + if (!(part = calloc(1, sizeof(*part)))) return E_OUTOFMEMORY; list_init(&part->items); if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &part->header, sizeof(part->header)))) { @@ -414,7 +411,7 @@ static HRESULT parse_wave_part(IDirectMusicWaveTrack *This, IStream *stream, return S_OK; error: - heap_free(part); + free(part); return hr; } @@ -475,11 +472,8 @@ HRESULT create_dmwavetrack(REFIID lpcGUID, void **ppobj) IDirectMusicWaveTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicWaveTrack, From a338edcc53557d4152750f6f5f86b1a94007f574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 13:19:25 +0200 Subject: [PATCH 0845/1148] dmime: Use the correct interface methods. (cherry picked from commit 2dc15d394374ac2a29c482d8e74994ab903bd849) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/graph.c | 4 ++-- dlls/dmime/performance.c | 2 +- dlls/dmime/tests/dmime.c | 8 ++++---- dlls/dmime/tests/performance.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dlls/dmime/graph.c b/dlls/dmime/graph.c index 8ac037e43bc..159923b6a70 100644 --- a/dlls/dmime/graph.c +++ b/dlls/dmime/graph.c @@ -129,8 +129,8 @@ static HRESULT WINAPI DirectMusicGraph_InsertTool(IDirectMusicGraph *iface, IDir pNewTool = calloc(1, sizeof(*pNewTool)); pNewTool->pTool = pTool; pNewTool->dwIndex = lIndex; - IDirectMusicTool8_AddRef(pTool); - IDirectMusicTool8_Init(pTool, iface); + IDirectMusicTool_AddRef(pTool); + IDirectMusicTool_Init(pTool, iface); list_add_tail (pPrevEntry->next, &pNewTool->entry); #if 0 diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index a1acc6d97cc..f6eb53e365f 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -879,7 +879,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_CloseDown(IDirectMusicPerform This->dsound = NULL; } if (This->dmusic) { - IDirectMusic_SetDirectSound(This->dmusic, NULL, NULL); + IDirectMusic8_SetDirectSound(This->dmusic, NULL, NULL); IDirectMusic8_Release(This->dmusic); This->dmusic = NULL; } diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index f00b5073b48..e95a1c02427 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -223,7 +223,7 @@ static BOOL missing_dmime(void) if (hr == S_OK && dms) { - IDirectMusicSegment_Release(dms); + IDirectMusicSegment8_Release(dms); return FALSE; } return TRUE; @@ -1015,7 +1015,7 @@ static void expect_getparam(IDirectMusicTrack *track, REFGUID type, const char * HRESULT hr; char buf[64] = { 0 }; - hr = IDirectMusicTrack8_GetParam(track, type, 0, NULL, buf); + hr = IDirectMusicTrack_GetParam(track, type, 0, NULL, buf); ok(hr == expect, "GetParam(%s) failed: %#lx, expected %#lx\n", name, hr, expect); } @@ -1025,7 +1025,7 @@ static void expect_setparam(IDirectMusicTrack *track, REFGUID type, const char * HRESULT hr; char buf[64] = { 0 }; - hr = IDirectMusicTrack8_SetParam(track, type, 0, buf); + hr = IDirectMusicTrack_SetParam(track, type, 0, buf); ok(hr == expect, "SetParam(%s) failed: %#lx, expected %#lx\n", name, hr, expect); } @@ -1103,7 +1103,7 @@ static void test_track(void) /* IDirectMusicTrack */ if (class[i].has_params != ~0) { for (j = 0; j < ARRAY_SIZE(param_types); j++) { - hr = IDirectMusicTrack8_IsParamSupported(dmt, param_types[j].type); + hr = IDirectMusicTrack_IsParamSupported(dmt, param_types[j].type); if (class[i].has_params & (1 << j)) { ok(hr == S_OK, "IsParamSupported(%s) failed: %#lx, expected S_OK\n", param_types[j].name, hr); diff --git a/dlls/dmime/tests/performance.c b/dlls/dmime/tests/performance.c index 100d8e40be0..4af91fc3130 100644 --- a/dlls/dmime/tests/performance.c +++ b/dlls/dmime/tests/performance.c @@ -334,7 +334,7 @@ static void test_createport(void) ok(hr == S_OK, "CloseDown failed: %#lx\n", hr); IDirectMusic_Release(music); - IDirectMusicPerformance_Release(perf); + IDirectMusicPerformance8_Release(perf); } static void test_pchannel(void) From f7c3e92b9d5902d13aaa5fa15ca02541305c20ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 21:36:44 +0200 Subject: [PATCH 0846/1148] dmime: Fix indentation in DirectMusicPerformance class constructor. (cherry picked from commit 698605ce41616a7777100a75fefaeba62eed8354) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index f6eb53e365f..cd3d800e267 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1275,24 +1275,28 @@ static const IDirectMusicPerformance8Vtbl DirectMusicPerformance8_Vtbl = { }; /* for ClassFactory */ -HRESULT create_dmperformance(REFIID lpcGUID, void **ppobj) +HRESULT create_dmperformance(REFIID iid, void **ret_iface) { - IDirectMusicPerformance8Impl *obj; + IDirectMusicPerformance8Impl *obj; + HRESULT hr; - TRACE("(%s, %p)\n", debugstr_guid(lpcGUID), ppobj); + TRACE("(%s, %p)\n", debugstr_guid(iid), ret_iface); - *ppobj = NULL; + *ret_iface = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->IDirectMusicPerformance8_iface.lpVtbl = &DirectMusicPerformance8_Vtbl; - obj->ref = 0; /* will be inited by QueryInterface */ - obj->pDefaultPath = NULL; - InitializeCriticalSection(&obj->safe); - obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IDirectMusicPerformance8Impl*->safe"); - wine_rb_init(&obj->pchannels, pchannel_block_compare); - - obj->rtLatencyTime = 100; /* 100 ms TO FIX */ - obj->dwBumperLength = 50; /* 50 ms default */ - obj->dwPrepareTime = 1000; /* 1000 ms default */ - return IDirectMusicPerformance8Impl_QueryInterface(&obj->IDirectMusicPerformance8_iface, - lpcGUID, ppobj); + obj->IDirectMusicPerformance8_iface.lpVtbl = &DirectMusicPerformance8_Vtbl; + obj->ref = 1; + + obj->pDefaultPath = NULL; + InitializeCriticalSection(&obj->safe); + obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IDirectMusicPerformance8Impl*->safe"); + wine_rb_init(&obj->pchannels, pchannel_block_compare); + + obj->rtLatencyTime = 100; /* 100 ms TO FIX */ + obj->dwBumperLength = 50; /* 50 ms default */ + obj->dwPrepareTime = 1000; /* 1000 ms default */ + + hr = IDirectMusicPerformance8_QueryInterface(&obj->IDirectMusicPerformance8_iface, iid, ret_iface); + IDirectMusicPerformance_Release(&obj->IDirectMusicPerformance8_iface); + return hr; } From d2a0c8d99f83504fc185fbc543c6e0d66af50a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 21:37:26 +0200 Subject: [PATCH 0847/1148] dmime: Rename IDirectMusicPerformance8Impl method prefix to performance. (cherry picked from commit 52f7ae49326e97fd197521df5f296d3be7c90f25) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 286 ++++++++++++++++++--------------------- 1 file changed, 129 insertions(+), 157 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index cd3d800e267..7617b92309d 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -251,8 +251,7 @@ static inline IDirectMusicPerformance8Impl *impl_from_IDirectMusicPerformance8(I } /* IDirectMusicPerformance8 IUnknown part: */ -static HRESULT WINAPI IDirectMusicPerformance8Impl_QueryInterface(IDirectMusicPerformance8 *iface, - REFIID riid, void **ppv) +static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ppv) { TRACE("(%p, %s,%p)\n", iface, debugstr_dmguid(riid), ppv); @@ -269,7 +268,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_QueryInterface(IDirectMusicPe return E_NOINTERFACE; } -static ULONG WINAPI IDirectMusicPerformance8Impl_AddRef(IDirectMusicPerformance8 *iface) +static ULONG WINAPI performance_AddRef(IDirectMusicPerformance8 *iface) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); ULONG ref = InterlockedIncrement(&This->ref); @@ -279,7 +278,7 @@ static ULONG WINAPI IDirectMusicPerformance8Impl_AddRef(IDirectMusicPerformance8 return ref; } -static ULONG WINAPI IDirectMusicPerformance8Impl_Release(IDirectMusicPerformance8 *iface) +static ULONG WINAPI performance_Release(IDirectMusicPerformance8 *iface) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); ULONG ref = InterlockedDecrement(&This->ref); @@ -297,8 +296,8 @@ static ULONG WINAPI IDirectMusicPerformance8Impl_Release(IDirectMusicPerformance } /* IDirectMusicPerformanceImpl IDirectMusicPerformance Interface part: */ -static HRESULT WINAPI IDirectMusicPerformance8Impl_Init(IDirectMusicPerformance8 *iface, - IDirectMusic **dmusic, IDirectSound *dsound, HWND hwnd) +static HRESULT WINAPI performance_Init(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic, + IDirectSound *dsound, HWND hwnd) { TRACE("(%p, %p, %p, %p)\n", iface, dmusic, dsound, hwnd); @@ -306,9 +305,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_Init(IDirectMusicPerformance8 0, NULL); } -static HRESULT WINAPI IDirectMusicPerformance8Impl_PlaySegment(IDirectMusicPerformance8 *iface, - IDirectMusicSegment *pSegment, DWORD dwFlags, __int64 i64StartTime, - IDirectMusicSegmentState **ppSegmentState) +static HRESULT WINAPI performance_PlaySegment(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, + DWORD dwFlags, __int64 i64StartTime, IDirectMusicSegmentState **ppSegmentState) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -319,9 +317,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_PlaySegment(IDirectMusicPerfo return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_Stop(IDirectMusicPerformance8 *iface, - IDirectMusicSegment *pSegment, IDirectMusicSegmentState *pSegmentState, MUSIC_TIME mtTime, - DWORD dwFlags) +static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, + IDirectMusicSegmentState *pSegmentState, MUSIC_TIME mtTime, DWORD dwFlags) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -329,7 +326,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_Stop(IDirectMusicPerformance8 return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetSegmentState(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_GetSegmentState(IDirectMusicPerformance8 *iface, IDirectMusicSegmentState **ppSegmentState, MUSIC_TIME mtTime) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -338,8 +335,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetSegmentState(IDirectMusicP return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_SetPrepareTime(IDirectMusicPerformance8 *iface, - DWORD dwMilliSeconds) +static HRESULT WINAPI performance_SetPrepareTime(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -348,8 +344,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_SetPrepareTime(IDirectMusicPe return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetPrepareTime(IDirectMusicPerformance8 *iface, - DWORD *pdwMilliSeconds) +static HRESULT WINAPI performance_GetPrepareTime(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -361,8 +356,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetPrepareTime(IDirectMusicPe return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_SetBumperLength(IDirectMusicPerformance8 *iface, - DWORD dwMilliSeconds) +static HRESULT WINAPI performance_SetBumperLength(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -371,8 +365,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_SetBumperLength(IDirectMusicP return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetBumperLength(IDirectMusicPerformance8 *iface, - DWORD *pdwMilliSeconds) +static HRESULT WINAPI performance_GetBumperLength(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -384,8 +377,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetBumperLength(IDirectMusicP return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_SendPMsg(IDirectMusicPerformance8 *iface, - DMUS_PMSG *pPMSG) +static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *pPMSG) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); DMUS_PMSGItem* pItem = NULL; @@ -436,7 +428,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_SendPMsg(IDirectMusicPerforma return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_MusicToReferenceTime(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, REFERENCE_TIME *prtTime) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -445,7 +437,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_MusicToReferenceTime(IDirectM return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_ReferenceToMusicTime(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_ReferenceToMusicTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME rtTime, MUSIC_TIME *pmtTime) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -454,7 +446,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_ReferenceToMusicTime(IDirectM return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_IsPlaying(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_IsPlaying(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, IDirectMusicSegmentState *pSegState) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -463,8 +455,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_IsPlaying(IDirectMusicPerform return S_FALSE; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetTime(IDirectMusicPerformance8 *iface, - REFERENCE_TIME *prtNow, MUSIC_TIME *pmtNow) +static HRESULT WINAPI performance_GetTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtNow, MUSIC_TIME *pmtNow) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr = S_OK; @@ -485,8 +476,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetTime(IDirectMusicPerforman return hr; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_AllocPMsg(IDirectMusicPerformance8 *iface, - ULONG cb, DMUS_PMSG **ppPMSG) +static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULONG cb, DMUS_PMSG **ppPMSG) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); DMUS_PMSGItem* pItem = NULL; @@ -505,8 +495,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_AllocPMsg(IDirectMusicPerform return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_FreePMsg(IDirectMusicPerformance8 *iface, - DMUS_PMSG *pPMSG) +static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *pPMSG) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); DMUS_PMSGItem* pItem = NULL; @@ -539,8 +528,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_FreePMsg(IDirectMusicPerforma return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetGraph(IDirectMusicPerformance8 *iface, - IDirectMusicGraph **graph) +static HRESULT WINAPI performance_GetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph **graph) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -557,8 +545,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetGraph(IDirectMusicPerforma return *graph ? S_OK : DMUS_E_NOT_FOUND; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_SetGraph(IDirectMusicPerformance8 *iface, - IDirectMusicGraph *pGraph) +static HRESULT WINAPI performance_SetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph *pGraph) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -575,7 +562,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_SetGraph(IDirectMusicPerforma return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_SetNotificationHandle(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_SetNotificationHandle(IDirectMusicPerformance8 *iface, HANDLE hNotification, REFERENCE_TIME rtMinimum) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -590,7 +577,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_SetNotificationHandle(IDirect return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetNotificationPMsg(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_GetNotificationPMsg(IDirectMusicPerformance8 *iface, DMUS_NOTIFICATION_PMSG **ppNotificationPMsg) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -606,8 +593,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetNotificationPMsg(IDirectMu /*return S_OK;*/ } -static HRESULT WINAPI IDirectMusicPerformance8Impl_AddNotificationType(IDirectMusicPerformance8 *iface, - REFGUID rguidNotificationType) +static HRESULT WINAPI performance_AddNotificationType(IDirectMusicPerformance8 *iface, REFGUID rguidNotificationType) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -615,8 +601,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_AddNotificationType(IDirectMu return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_RemoveNotificationType(IDirectMusicPerformance8 *iface, - REFGUID rguidNotificationType) +static HRESULT WINAPI performance_RemoveNotificationType(IDirectMusicPerformance8 *iface, REFGUID rguidNotificationType) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -646,8 +631,7 @@ static HRESULT perf_dmport_create(IDirectMusicPerformance8Impl *perf, DMUS_PORTP return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_AddPort(IDirectMusicPerformance8 *iface, - IDirectMusicPort *port) +static HRESULT WINAPI performance_AddPort(IDirectMusicPerformance8 *iface, IDirectMusicPort *port) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -674,8 +658,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_AddPort(IDirectMusicPerforman return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_RemovePort(IDirectMusicPerformance8 *iface, - IDirectMusicPort *pPort) +static HRESULT WINAPI performance_RemovePort(IDirectMusicPerformance8 *iface, IDirectMusicPort *pPort) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -684,7 +667,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_RemovePort(IDirectMusicPerfor return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_AssignPChannelBlock(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 *iface, DWORD block_num, IDirectMusicPort *port, DWORD group) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -701,8 +684,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_AssignPChannelBlock(IDirectMu return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_AssignPChannel(IDirectMusicPerformance8 *iface, - DWORD pchannel, IDirectMusicPort *port, DWORD group, DWORD channel) +static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface, DWORD pchannel, + IDirectMusicPort *port, DWORD group, DWORD channel) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); struct pchannel_block *block; @@ -721,8 +704,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_AssignPChannel(IDirectMusicPe return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_PChannelInfo(IDirectMusicPerformance8 *iface, - DWORD pchannel, IDirectMusicPort **port, DWORD *group, DWORD *channel) +static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, DWORD pchannel, + IDirectMusicPort **port, DWORD *group, DWORD *channel) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); struct pchannel_block *block; @@ -749,7 +732,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_PChannelInfo(IDirectMusicPerf return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_DownloadInstrument(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_DownloadInstrument(IDirectMusicPerformance8 *iface, IDirectMusicInstrument *pInst, DWORD dwPChannel, IDirectMusicDownloadedInstrument **ppDownInst, DMUS_NOTERANGE *pNoteRanges, DWORD dwNumNoteRanges, IDirectMusicPort **ppPort, DWORD *pdwGroup, DWORD *pdwMChannel) @@ -760,8 +743,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_DownloadInstrument(IDirectMus return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_Invalidate(IDirectMusicPerformance8 *iface, - MUSIC_TIME mtTime, DWORD dwFlags) +static HRESULT WINAPI performance_Invalidate(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, DWORD dwFlags) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -769,9 +751,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_Invalidate(IDirectMusicPerfor return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetParam(IDirectMusicPerformance8 *iface, - REFGUID rguidType, DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, - MUSIC_TIME *pmtNext, void *pParam) +static HRESULT WINAPI performance_GetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, + DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -779,8 +760,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetParam(IDirectMusicPerforma return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_SetParam(IDirectMusicPerformance8 *iface, - REFGUID rguidType, DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, void *pParam) +static HRESULT WINAPI performance_SetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, + DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, void *pParam) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -788,8 +769,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_SetParam(IDirectMusicPerforma return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetGlobalParam(IDirectMusicPerformance8 *iface, - REFGUID rguidType, void *pParam, DWORD dwSize) +static HRESULT WINAPI performance_GetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, + void *pParam, DWORD dwSize) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -807,8 +788,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetGlobalParam(IDirectMusicPe return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_SetGlobalParam(IDirectMusicPerformance8 *iface, - REFGUID rguidType, void *pParam, DWORD dwSize) +static HRESULT WINAPI performance_SetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, + void *pParam, DWORD dwSize) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -834,8 +815,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_SetGlobalParam(IDirectMusicPe return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetLatencyTime(IDirectMusicPerformance8 *iface, - REFERENCE_TIME *prtTime) +static HRESULT WINAPI performance_GetLatencyTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtTime) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -844,8 +824,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetLatencyTime(IDirectMusicPe return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetQueueTime(IDirectMusicPerformance8 *iface, - REFERENCE_TIME *prtTime) +static HRESULT WINAPI performance_GetQueueTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtTime) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -854,8 +833,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetQueueTime(IDirectMusicPerf return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_AdjustTime(IDirectMusicPerformance8 *iface, - REFERENCE_TIME rtAmount) +static HRESULT WINAPI performance_AdjustTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME rtAmount) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -863,7 +841,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_AdjustTime(IDirectMusicPerfor return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_CloseDown(IDirectMusicPerformance8 *iface) +static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -886,7 +864,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_CloseDown(IDirectMusicPerform return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetResolvedTime(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_GetResolvedTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME rtTime, REFERENCE_TIME *prtResolved, DWORD dwTimeResolveFlags) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -896,9 +874,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetResolvedTime(IDirectMusicP return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_MIDIToMusic(IDirectMusicPerformance8 *iface, - BYTE bMIDIValue, DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, - WORD *pwMusicValue) +static HRESULT WINAPI performance_MIDIToMusic(IDirectMusicPerformance8 *iface, BYTE bMIDIValue, + DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, WORD *pwMusicValue) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -906,9 +883,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_MIDIToMusic(IDirectMusicPerfo return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_MusicToMIDI(IDirectMusicPerformance8 *iface, - WORD wMusicValue, DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, - BYTE *pbMIDIValue) +static HRESULT WINAPI performance_MusicToMIDI(IDirectMusicPerformance8 *iface, WORD wMusicValue, + DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, BYTE *pbMIDIValue) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -916,9 +892,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_MusicToMIDI(IDirectMusicPerfo return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_TimeToRhythm(IDirectMusicPerformance8 *iface, - MUSIC_TIME mtTime, DMUS_TIMESIGNATURE *pTimeSig, WORD *pwMeasure, BYTE *pbBeat, - BYTE *pbGrid, short *pnOffset) +static HRESULT WINAPI performance_TimeToRhythm(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, + DMUS_TIMESIGNATURE *pTimeSig, WORD *pwMeasure, BYTE *pbBeat, BYTE *pbGrid, short *pnOffset) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -926,9 +901,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_TimeToRhythm(IDirectMusicPerf return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_RhythmToTime(IDirectMusicPerformance8 *iface, - WORD wMeasure, BYTE bBeat, BYTE bGrid, short nOffset, DMUS_TIMESIGNATURE *pTimeSig, - MUSIC_TIME *pmtTime) +static HRESULT WINAPI performance_RhythmToTime(IDirectMusicPerformance8 *iface, WORD wMeasure, + BYTE bBeat, BYTE bGrid, short nOffset, DMUS_TIMESIGNATURE *pTimeSig, MUSIC_TIME *pmtTime) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -937,9 +911,9 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_RhythmToTime(IDirectMusicPerf } /* IDirectMusicPerformance8 Interface part follow: */ -static HRESULT WINAPI IDirectMusicPerformance8Impl_InitAudio(IDirectMusicPerformance8 *iface, - IDirectMusic **dmusic, IDirectSound **dsound, HWND hwnd, DWORD default_path_type, - DWORD num_channels, DWORD flags, DMUS_AUDIOPARAMS *params) +static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic, + IDirectSound **dsound, HWND hwnd, DWORD default_path_type, DWORD num_channels, DWORD flags, + DMUS_AUDIOPARAMS *params) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr = S_OK; @@ -1022,10 +996,9 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_InitAudio(IDirectMusicPerform return hr; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_PlaySegmentEx(IDirectMusicPerformance8 *iface, - IUnknown *pSource, WCHAR *pwzSegmentName, IUnknown *pTransition, DWORD dwFlags, - __int64 i64StartTime, IDirectMusicSegmentState **ppSegmentState, IUnknown *pFrom, - IUnknown *pAudioPath) +static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, IUnknown *pSource, + WCHAR *pwzSegmentName, IUnknown *pTransition, DWORD dwFlags, __int64 i64StartTime, + IDirectMusicSegmentState **ppSegmentState, IUnknown *pFrom, IUnknown *pAudioPath) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -1036,8 +1009,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_PlaySegmentEx(IDirectMusicPer return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_StopEx(IDirectMusicPerformance8 *iface, - IUnknown *pObjectToStop, __int64 i64StopTime, DWORD dwFlags) +static HRESULT WINAPI performance_StopEx(IDirectMusicPerformance8 *iface, IUnknown *pObjectToStop, + __int64 i64StopTime, DWORD dwFlags) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -1046,8 +1019,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_StopEx(IDirectMusicPerformanc return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_ClonePMsg(IDirectMusicPerformance8 *iface, - DMUS_PMSG *pSourcePMSG, DMUS_PMSG **ppCopyPMSG) +static HRESULT WINAPI performance_ClonePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *pSourcePMSG, + DMUS_PMSG **ppCopyPMSG) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -1055,7 +1028,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_ClonePMsg(IDirectMusicPerform return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_CreateAudioPath(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_CreateAudioPath(IDirectMusicPerformance8 *iface, IUnknown *pSourceConfig, BOOL fActivate, IDirectMusicAudioPath **ppNewPath) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -1077,7 +1050,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_CreateAudioPath(IDirectMusicP return IDirectMusicAudioPath_Activate(*ppNewPath, fActivate); } -static HRESULT WINAPI IDirectMusicPerformance8Impl_CreateStandardAudioPath(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_CreateStandardAudioPath(IDirectMusicPerformance8 *iface, DWORD dwType, DWORD pchannel_count, BOOL fActivate, IDirectMusicAudioPath **ppNewPath) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -1171,8 +1144,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_CreateStandardAudioPath(IDire return IDirectMusicAudioPath_Activate(*ppNewPath, fActivate); } -static HRESULT WINAPI IDirectMusicPerformance8Impl_SetDefaultAudioPath(IDirectMusicPerformance8 *iface, - IDirectMusicAudioPath *pAudioPath) +static HRESULT WINAPI performance_SetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath *pAudioPath) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -1191,7 +1163,7 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_SetDefaultAudioPath(IDirectMu return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetDefaultAudioPath(IDirectMusicPerformance8 *iface, +static HRESULT WINAPI performance_GetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath **ppAudioPath) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -1207,9 +1179,8 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetDefaultAudioPath(IDirectMu return S_OK; } -static HRESULT WINAPI IDirectMusicPerformance8Impl_GetParamEx(IDirectMusicPerformance8 *iface, - REFGUID rguidType, DWORD dwTrackID, DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, - MUSIC_TIME *pmtNext, void *pParam) +static HRESULT WINAPI performance_GetParamEx(IDirectMusicPerformance8 *iface, REFGUID rguidType, DWORD dwTrackID, + DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam) { IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); @@ -1218,60 +1189,61 @@ static HRESULT WINAPI IDirectMusicPerformance8Impl_GetParamEx(IDirectMusicPerfor return S_OK; } -static const IDirectMusicPerformance8Vtbl DirectMusicPerformance8_Vtbl = { - IDirectMusicPerformance8Impl_QueryInterface, - IDirectMusicPerformance8Impl_AddRef, - IDirectMusicPerformance8Impl_Release, - IDirectMusicPerformance8Impl_Init, - IDirectMusicPerformance8Impl_PlaySegment, - IDirectMusicPerformance8Impl_Stop, - IDirectMusicPerformance8Impl_GetSegmentState, - IDirectMusicPerformance8Impl_SetPrepareTime, - IDirectMusicPerformance8Impl_GetPrepareTime, - IDirectMusicPerformance8Impl_SetBumperLength, - IDirectMusicPerformance8Impl_GetBumperLength, - IDirectMusicPerformance8Impl_SendPMsg, - IDirectMusicPerformance8Impl_MusicToReferenceTime, - IDirectMusicPerformance8Impl_ReferenceToMusicTime, - IDirectMusicPerformance8Impl_IsPlaying, - IDirectMusicPerformance8Impl_GetTime, - IDirectMusicPerformance8Impl_AllocPMsg, - IDirectMusicPerformance8Impl_FreePMsg, - IDirectMusicPerformance8Impl_GetGraph, - IDirectMusicPerformance8Impl_SetGraph, - IDirectMusicPerformance8Impl_SetNotificationHandle, - IDirectMusicPerformance8Impl_GetNotificationPMsg, - IDirectMusicPerformance8Impl_AddNotificationType, - IDirectMusicPerformance8Impl_RemoveNotificationType, - IDirectMusicPerformance8Impl_AddPort, - IDirectMusicPerformance8Impl_RemovePort, - IDirectMusicPerformance8Impl_AssignPChannelBlock, - IDirectMusicPerformance8Impl_AssignPChannel, - IDirectMusicPerformance8Impl_PChannelInfo, - IDirectMusicPerformance8Impl_DownloadInstrument, - IDirectMusicPerformance8Impl_Invalidate, - IDirectMusicPerformance8Impl_GetParam, - IDirectMusicPerformance8Impl_SetParam, - IDirectMusicPerformance8Impl_GetGlobalParam, - IDirectMusicPerformance8Impl_SetGlobalParam, - IDirectMusicPerformance8Impl_GetLatencyTime, - IDirectMusicPerformance8Impl_GetQueueTime, - IDirectMusicPerformance8Impl_AdjustTime, - IDirectMusicPerformance8Impl_CloseDown, - IDirectMusicPerformance8Impl_GetResolvedTime, - IDirectMusicPerformance8Impl_MIDIToMusic, - IDirectMusicPerformance8Impl_MusicToMIDI, - IDirectMusicPerformance8Impl_TimeToRhythm, - IDirectMusicPerformance8Impl_RhythmToTime, - IDirectMusicPerformance8Impl_InitAudio, - IDirectMusicPerformance8Impl_PlaySegmentEx, - IDirectMusicPerformance8Impl_StopEx, - IDirectMusicPerformance8Impl_ClonePMsg, - IDirectMusicPerformance8Impl_CreateAudioPath, - IDirectMusicPerformance8Impl_CreateStandardAudioPath, - IDirectMusicPerformance8Impl_SetDefaultAudioPath, - IDirectMusicPerformance8Impl_GetDefaultAudioPath, - IDirectMusicPerformance8Impl_GetParamEx +static const IDirectMusicPerformance8Vtbl performance_vtbl = +{ + performance_QueryInterface, + performance_AddRef, + performance_Release, + performance_Init, + performance_PlaySegment, + performance_Stop, + performance_GetSegmentState, + performance_SetPrepareTime, + performance_GetPrepareTime, + performance_SetBumperLength, + performance_GetBumperLength, + performance_SendPMsg, + performance_MusicToReferenceTime, + performance_ReferenceToMusicTime, + performance_IsPlaying, + performance_GetTime, + performance_AllocPMsg, + performance_FreePMsg, + performance_GetGraph, + performance_SetGraph, + performance_SetNotificationHandle, + performance_GetNotificationPMsg, + performance_AddNotificationType, + performance_RemoveNotificationType, + performance_AddPort, + performance_RemovePort, + performance_AssignPChannelBlock, + performance_AssignPChannel, + performance_PChannelInfo, + performance_DownloadInstrument, + performance_Invalidate, + performance_GetParam, + performance_SetParam, + performance_GetGlobalParam, + performance_SetGlobalParam, + performance_GetLatencyTime, + performance_GetQueueTime, + performance_AdjustTime, + performance_CloseDown, + performance_GetResolvedTime, + performance_MIDIToMusic, + performance_MusicToMIDI, + performance_TimeToRhythm, + performance_RhythmToTime, + performance_InitAudio, + performance_PlaySegmentEx, + performance_StopEx, + performance_ClonePMsg, + performance_CreateAudioPath, + performance_CreateStandardAudioPath, + performance_SetDefaultAudioPath, + performance_GetDefaultAudioPath, + performance_GetParamEx, }; /* for ClassFactory */ @@ -1284,7 +1256,7 @@ HRESULT create_dmperformance(REFIID iid, void **ret_iface) *ret_iface = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->IDirectMusicPerformance8_iface.lpVtbl = &DirectMusicPerformance8_Vtbl; + obj->IDirectMusicPerformance8_iface.lpVtbl = &performance_vtbl; obj->ref = 1; obj->pDefaultPath = NULL; From ff04d09b5e58f22c44be7738e9e3de7869fda90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 21:45:31 +0200 Subject: [PATCH 0848/1148] dmime: Get rid of IDirectMusicPerformance8Impl typedef. (cherry picked from commit c927967a7623921b8f2eaa946b72342d4ec81b5d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 125 ++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 7617b92309d..d6f7e76686e 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -34,7 +34,8 @@ struct pchannel_block { struct wine_rb_entry entry; }; -typedef struct IDirectMusicPerformance8Impl { +struct performance +{ IDirectMusicPerformance8 IDirectMusicPerformance8_iface; LONG ref; IDirectMusic8 *dmusic; @@ -47,7 +48,7 @@ typedef struct IDirectMusicPerformance8Impl { long lMasterVolume; /* performance channels */ struct wine_rb_tree pchannels; - /* IDirectMusicPerformance8Impl fields */ + IDirectMusicAudioPath *pDefaultPath; HANDLE hNotification; REFERENCE_TIME rtMinimum; @@ -62,7 +63,7 @@ typedef struct IDirectMusicPerformance8Impl { CRITICAL_SECTION safe; struct DMUS_PMSGItem *head; struct DMUS_PMSGItem *imm_head; -} IDirectMusicPerformance8Impl; +}; typedef struct DMUS_PMSGItem DMUS_PMSGItem; struct DMUS_PMSGItem { @@ -92,7 +93,7 @@ struct DMUS_PMSGItem { #define PROCESSMSG_ADD (WM_APP + 4) -static DMUS_PMSGItem* ProceedMsg(IDirectMusicPerformance8Impl* This, DMUS_PMSGItem* cur) { +static DMUS_PMSGItem* ProceedMsg(struct performance *This, DMUS_PMSGItem* cur) { if (cur->pMsg.dwType == DMUS_PMSGT_NOTIFICATION) { SetEvent(This->hNotification); } @@ -109,7 +110,7 @@ static DMUS_PMSGItem* ProceedMsg(IDirectMusicPerformance8Impl* This, DMUS_PMSGIt } static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) { - IDirectMusicPerformance8Impl* This = lpParam; + struct performance *This = lpParam; DWORD timeOut = INFINITE; MSG msg; HRESULT hr; @@ -183,7 +184,7 @@ static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) { return 0; } -static BOOL PostMessageToProcessMsgThread(IDirectMusicPerformance8Impl* This, UINT iMsg) { +static BOOL PostMessageToProcessMsgThread(struct performance *This, UINT iMsg) { if (FALSE == This->procThreadTicStarted && PROCESSMSG_EXIT != iMsg) { BOOL res; This->procThread = CreateThread(NULL, 0, ProcessMsgThread, This, 0, &This->procThreadId); @@ -245,9 +246,9 @@ static struct pchannel_block *pchannel_block_set(struct wine_rb_tree *tree, DWOR return block; } -static inline IDirectMusicPerformance8Impl *impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface) +static inline struct performance *impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface) { - return CONTAINING_RECORD(iface, IDirectMusicPerformance8Impl, IDirectMusicPerformance8_iface); + return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface); } /* IDirectMusicPerformance8 IUnknown part: */ @@ -270,7 +271,7 @@ static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface static ULONG WINAPI performance_AddRef(IDirectMusicPerformance8 *iface) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p): ref=%ld\n", This, ref); @@ -280,7 +281,7 @@ static ULONG WINAPI performance_AddRef(IDirectMusicPerformance8 *iface) static ULONG WINAPI performance_Release(IDirectMusicPerformance8 *iface) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p): ref=%ld\n", This, ref); @@ -308,7 +309,7 @@ static HRESULT WINAPI performance_Init(IDirectMusicPerformance8 *iface, IDirectM static HRESULT WINAPI performance_PlaySegment(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, DWORD dwFlags, __int64 i64StartTime, IDirectMusicSegmentState **ppSegmentState) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p, %ld, 0x%s, %p): stub\n", This, pSegment, dwFlags, wine_dbgstr_longlong(i64StartTime), ppSegmentState); @@ -320,7 +321,7 @@ static HRESULT WINAPI performance_PlaySegment(IDirectMusicPerformance8 *iface, I static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, IDirectMusicSegmentState *pSegmentState, MUSIC_TIME mtTime, DWORD dwFlags) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p, %p, %ld, %ld): stub\n", This, pSegment, pSegmentState, mtTime, dwFlags); return S_OK; @@ -329,7 +330,7 @@ static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectM static HRESULT WINAPI performance_GetSegmentState(IDirectMusicPerformance8 *iface, IDirectMusicSegmentState **ppSegmentState, MUSIC_TIME mtTime) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p,%p, %ld): stub\n", This, ppSegmentState, mtTime); return S_OK; @@ -337,7 +338,7 @@ static HRESULT WINAPI performance_GetSegmentState(IDirectMusicPerformance8 *ifac static HRESULT WINAPI performance_SetPrepareTime(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %ld)\n", This, dwMilliSeconds); This->dwPrepareTime = dwMilliSeconds; @@ -346,7 +347,7 @@ static HRESULT WINAPI performance_SetPrepareTime(IDirectMusicPerformance8 *iface static HRESULT WINAPI performance_GetPrepareTime(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %p)\n", This, pdwMilliSeconds); if (NULL == pdwMilliSeconds) { @@ -358,7 +359,7 @@ static HRESULT WINAPI performance_GetPrepareTime(IDirectMusicPerformance8 *iface static HRESULT WINAPI performance_SetBumperLength(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %ld)\n", This, dwMilliSeconds); This->dwBumperLength = dwMilliSeconds; @@ -367,7 +368,7 @@ static HRESULT WINAPI performance_SetBumperLength(IDirectMusicPerformance8 *ifac static HRESULT WINAPI performance_GetBumperLength(IDirectMusicPerformance8 *iface, DWORD *pdwMilliSeconds) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %p)\n", This, pdwMilliSeconds); if (NULL == pdwMilliSeconds) { @@ -379,7 +380,7 @@ static HRESULT WINAPI performance_GetBumperLength(IDirectMusicPerformance8 *ifac static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *pPMSG) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); DMUS_PMSGItem* pItem = NULL; DMUS_PMSGItem* it = NULL; DMUS_PMSGItem* prev_it = NULL; @@ -431,7 +432,7 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, REFERENCE_TIME *prtTime) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %ld, %p): stub\n", This, mtTime, prtTime); return S_OK; @@ -440,7 +441,7 @@ static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 static HRESULT WINAPI performance_ReferenceToMusicTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME rtTime, MUSIC_TIME *pmtTime) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, 0x%s, %p): stub\n", This, wine_dbgstr_longlong(rtTime), pmtTime); return S_OK; @@ -449,7 +450,7 @@ static HRESULT WINAPI performance_ReferenceToMusicTime(IDirectMusicPerformance8 static HRESULT WINAPI performance_IsPlaying(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, IDirectMusicSegmentState *pSegState) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p, %p): stub\n", This, pSegment, pSegState); return S_FALSE; @@ -457,7 +458,7 @@ static HRESULT WINAPI performance_IsPlaying(IDirectMusicPerformance8 *iface, static HRESULT WINAPI performance_GetTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtNow, MUSIC_TIME *pmtNow) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr = S_OK; REFERENCE_TIME rtCur = 0; @@ -478,7 +479,7 @@ static HRESULT WINAPI performance_GetTime(IDirectMusicPerformance8 *iface, REFER static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULONG cb, DMUS_PMSG **ppPMSG) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); DMUS_PMSGItem* pItem = NULL; FIXME("(%p, %ld, %p): stub\n", This, cb, ppPMSG); @@ -497,7 +498,7 @@ static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULO static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *pPMSG) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); DMUS_PMSGItem* pItem = NULL; FIXME("(%p, %p): stub\n", This, pPMSG); @@ -530,7 +531,7 @@ static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS static HRESULT WINAPI performance_GetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph **graph) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %p)\n", This, graph); @@ -547,7 +548,7 @@ static HRESULT WINAPI performance_GetGraph(IDirectMusicPerformance8 *iface, IDir static HRESULT WINAPI performance_SetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph *pGraph) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): to check\n", This, pGraph); @@ -565,7 +566,7 @@ static HRESULT WINAPI performance_SetGraph(IDirectMusicPerformance8 *iface, IDir static HRESULT WINAPI performance_SetNotificationHandle(IDirectMusicPerformance8 *iface, HANDLE hNotification, REFERENCE_TIME rtMinimum) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %p, 0x%s)\n", This, hNotification, wine_dbgstr_longlong(rtMinimum)); @@ -580,7 +581,7 @@ static HRESULT WINAPI performance_SetNotificationHandle(IDirectMusicPerformance8 static HRESULT WINAPI performance_GetNotificationPMsg(IDirectMusicPerformance8 *iface, DMUS_NOTIFICATION_PMSG **ppNotificationPMsg) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): stub\n", This, ppNotificationPMsg); if (NULL == ppNotificationPMsg) { @@ -595,7 +596,7 @@ static HRESULT WINAPI performance_GetNotificationPMsg(IDirectMusicPerformance8 * static HRESULT WINAPI performance_AddNotificationType(IDirectMusicPerformance8 *iface, REFGUID rguidNotificationType) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %s): stub\n", This, debugstr_dmguid(rguidNotificationType)); return S_OK; @@ -603,13 +604,13 @@ static HRESULT WINAPI performance_AddNotificationType(IDirectMusicPerformance8 * static HRESULT WINAPI performance_RemoveNotificationType(IDirectMusicPerformance8 *iface, REFGUID rguidNotificationType) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %s): stub\n", This, debugstr_dmguid(rguidNotificationType)); return S_OK; } -static HRESULT perf_dmport_create(IDirectMusicPerformance8Impl *perf, DMUS_PORTPARAMS *params) +static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *params) { IDirectMusicPort *port; GUID guid; @@ -633,7 +634,7 @@ static HRESULT perf_dmport_create(IDirectMusicPerformance8Impl *perf, DMUS_PORTP static HRESULT WINAPI performance_AddPort(IDirectMusicPerformance8 *iface, IDirectMusicPort *port) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): semi-stub\n", This, port); @@ -660,7 +661,7 @@ static HRESULT WINAPI performance_AddPort(IDirectMusicPerformance8 *iface, IDire static HRESULT WINAPI performance_RemovePort(IDirectMusicPerformance8 *iface, IDirectMusicPort *pPort) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): stub\n", This, pPort); IDirectMusicPort_Release (pPort); @@ -670,7 +671,7 @@ static HRESULT WINAPI performance_RemovePort(IDirectMusicPerformance8 *iface, ID static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 *iface, DWORD block_num, IDirectMusicPort *port, DWORD group) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %ld, %p, %ld): semi-stub\n", This, block_num, port, group); @@ -687,7 +688,7 @@ static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 * static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface, DWORD pchannel, IDirectMusicPort *port, DWORD group, DWORD channel) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct pchannel_block *block; FIXME("(%p)->(%ld, %p, %ld, %ld) semi-stub\n", This, pchannel, port, group, channel); @@ -707,7 +708,7 @@ static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, DWORD pchannel, IDirectMusicPort **port, DWORD *group, DWORD *channel) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct pchannel_block *block; struct wine_rb_entry *entry; DWORD block_num = pchannel / 16; @@ -737,7 +738,7 @@ static HRESULT WINAPI performance_DownloadInstrument(IDirectMusicPerformance8 *i IDirectMusicDownloadedInstrument **ppDownInst, DMUS_NOTERANGE *pNoteRanges, DWORD dwNumNoteRanges, IDirectMusicPort **ppPort, DWORD *pdwGroup, DWORD *pdwMChannel) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p, %ld, %p, %p, %ld, %p, %p, %p): stub\n", This, pInst, dwPChannel, ppDownInst, pNoteRanges, dwNumNoteRanges, ppPort, pdwGroup, pdwMChannel); return S_OK; @@ -745,7 +746,7 @@ static HRESULT WINAPI performance_DownloadInstrument(IDirectMusicPerformance8 *i static HRESULT WINAPI performance_Invalidate(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, DWORD dwFlags) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %ld, %ld): stub\n", This, mtTime, dwFlags); return S_OK; @@ -754,7 +755,7 @@ static HRESULT WINAPI performance_Invalidate(IDirectMusicPerformance8 *iface, MU static HRESULT WINAPI performance_GetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %s, %ld, %ld, %ld, %p, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, mtTime, pmtNext, pParam); return S_OK; @@ -763,7 +764,7 @@ static HRESULT WINAPI performance_GetParam(IDirectMusicPerformance8 *iface, REFG static HRESULT WINAPI performance_SetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, void *pParam) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %s, %ld, %ld, %ld, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, mtTime, pParam); return S_OK; @@ -772,7 +773,7 @@ static HRESULT WINAPI performance_SetParam(IDirectMusicPerformance8 *iface, REFG static HRESULT WINAPI performance_GetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, void *pParam, DWORD dwSize) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %s, %p, %ld): stub\n", This, debugstr_dmguid(rguidType), pParam, dwSize); @@ -791,7 +792,7 @@ static HRESULT WINAPI performance_GetGlobalParam(IDirectMusicPerformance8 *iface static HRESULT WINAPI performance_SetGlobalParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, void *pParam, DWORD dwSize) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %s, %p, %ld)\n", This, debugstr_dmguid(rguidType), pParam, dwSize); @@ -817,7 +818,7 @@ static HRESULT WINAPI performance_SetGlobalParam(IDirectMusicPerformance8 *iface static HRESULT WINAPI performance_GetLatencyTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtTime) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); TRACE("(%p, %p): stub\n", This, prtTime); *prtTime = This->rtLatencyTime; @@ -827,7 +828,7 @@ static HRESULT WINAPI performance_GetLatencyTime(IDirectMusicPerformance8 *iface static HRESULT WINAPI performance_GetQueueTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtTime) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): stub\n", This, prtTime); return S_OK; @@ -835,7 +836,7 @@ static HRESULT WINAPI performance_GetQueueTime(IDirectMusicPerformance8 *iface, static HRESULT WINAPI performance_AdjustTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME rtAmount) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, 0x%s): stub\n", This, wine_dbgstr_longlong(rtAmount)); return S_OK; @@ -843,7 +844,7 @@ static HRESULT WINAPI performance_AdjustTime(IDirectMusicPerformance8 *iface, RE static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p): semi-stub\n", This); @@ -867,7 +868,7 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) static HRESULT WINAPI performance_GetResolvedTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME rtTime, REFERENCE_TIME *prtResolved, DWORD dwTimeResolveFlags) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, 0x%s, %p, %ld): stub\n", This, wine_dbgstr_longlong(rtTime), prtResolved, dwTimeResolveFlags); @@ -877,7 +878,7 @@ static HRESULT WINAPI performance_GetResolvedTime(IDirectMusicPerformance8 *ifac static HRESULT WINAPI performance_MIDIToMusic(IDirectMusicPerformance8 *iface, BYTE bMIDIValue, DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, WORD *pwMusicValue) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %d, %p, %d, %d, %p): stub\n", This, bMIDIValue, pChord, bPlayMode, bChordLevel, pwMusicValue); return S_OK; @@ -886,7 +887,7 @@ static HRESULT WINAPI performance_MIDIToMusic(IDirectMusicPerformance8 *iface, B static HRESULT WINAPI performance_MusicToMIDI(IDirectMusicPerformance8 *iface, WORD wMusicValue, DMUS_CHORD_KEY *pChord, BYTE bPlayMode, BYTE bChordLevel, BYTE *pbMIDIValue) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %d, %p, %d, %d, %p): stub\n", This, wMusicValue, pChord, bPlayMode, bChordLevel, pbMIDIValue); return S_OK; @@ -895,7 +896,7 @@ static HRESULT WINAPI performance_MusicToMIDI(IDirectMusicPerformance8 *iface, W static HRESULT WINAPI performance_TimeToRhythm(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, DMUS_TIMESIGNATURE *pTimeSig, WORD *pwMeasure, BYTE *pbBeat, BYTE *pbGrid, short *pnOffset) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %ld, %p, %p, %p, %p, %p): stub\n", This, mtTime, pTimeSig, pwMeasure, pbBeat, pbGrid, pnOffset); return S_OK; @@ -904,7 +905,7 @@ static HRESULT WINAPI performance_TimeToRhythm(IDirectMusicPerformance8 *iface, static HRESULT WINAPI performance_RhythmToTime(IDirectMusicPerformance8 *iface, WORD wMeasure, BYTE bBeat, BYTE bGrid, short nOffset, DMUS_TIMESIGNATURE *pTimeSig, MUSIC_TIME *pmtTime) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %d, %d, %d, %i, %p, %p): stub\n", This, wMeasure, bBeat, bGrid, nOffset, pTimeSig, pmtTime); return S_OK; @@ -915,7 +916,7 @@ static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDi IDirectSound **dsound, HWND hwnd, DWORD default_path_type, DWORD num_channels, DWORD flags, DMUS_AUDIOPARAMS *params) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr = S_OK; TRACE("(%p, %p, %p, %p, %lx, %lu, %lx, %p)\n", This, dmusic, dsound, hwnd, default_path_type, @@ -1000,7 +1001,7 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, WCHAR *pwzSegmentName, IUnknown *pTransition, DWORD dwFlags, __int64 i64StartTime, IDirectMusicSegmentState **ppSegmentState, IUnknown *pFrom, IUnknown *pAudioPath) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); 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); @@ -1012,7 +1013,7 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, static HRESULT WINAPI performance_StopEx(IDirectMusicPerformance8 *iface, IUnknown *pObjectToStop, __int64 i64StopTime, DWORD dwFlags) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p, 0x%s, %ld): stub\n", This, pObjectToStop, wine_dbgstr_longlong(i64StopTime), dwFlags); @@ -1022,7 +1023,7 @@ static HRESULT WINAPI performance_StopEx(IDirectMusicPerformance8 *iface, IUnkno static HRESULT WINAPI performance_ClonePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *pSourcePMSG, DMUS_PMSG **ppCopyPMSG) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p, %p): stub\n", This, pSourcePMSG, ppCopyPMSG); return S_OK; @@ -1031,7 +1032,7 @@ static HRESULT WINAPI performance_ClonePMsg(IDirectMusicPerformance8 *iface, DMU static HRESULT WINAPI performance_CreateAudioPath(IDirectMusicPerformance8 *iface, IUnknown *pSourceConfig, BOOL fActivate, IDirectMusicAudioPath **ppNewPath) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); IDirectMusicAudioPath *pPath; FIXME("(%p, %p, %d, %p): stub\n", This, pSourceConfig, fActivate, ppNewPath); @@ -1053,7 +1054,7 @@ static HRESULT WINAPI performance_CreateAudioPath(IDirectMusicPerformance8 *ifac static HRESULT WINAPI performance_CreateStandardAudioPath(IDirectMusicPerformance8 *iface, DWORD dwType, DWORD pchannel_count, BOOL fActivate, IDirectMusicAudioPath **ppNewPath) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); IDirectMusicAudioPath *pPath; DSBUFFERDESC desc; WAVEFORMATEX format; @@ -1146,7 +1147,7 @@ static HRESULT WINAPI performance_CreateStandardAudioPath(IDirectMusicPerformanc static HRESULT WINAPI performance_SetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath *pAudioPath) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): semi-stub\n", This, pAudioPath); @@ -1166,7 +1167,7 @@ static HRESULT WINAPI performance_SetDefaultAudioPath(IDirectMusicPerformance8 * static HRESULT WINAPI performance_GetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath **ppAudioPath) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %p): semi-stub (%p)\n", This, ppAudioPath, This->pDefaultPath); @@ -1182,7 +1183,7 @@ static HRESULT WINAPI performance_GetDefaultAudioPath(IDirectMusicPerformance8 * static HRESULT WINAPI performance_GetParamEx(IDirectMusicPerformance8 *iface, REFGUID rguidType, DWORD dwTrackID, DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam) { - IDirectMusicPerformance8Impl *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); FIXME("(%p, %s, %ld, %ld, %ld, %ld, %p, %p): stub\n", This, debugstr_dmguid(rguidType), dwTrackID, dwGroupBits, dwIndex, mtTime, pmtNext, pParam); @@ -1249,7 +1250,7 @@ static const IDirectMusicPerformance8Vtbl performance_vtbl = /* for ClassFactory */ HRESULT create_dmperformance(REFIID iid, void **ret_iface) { - IDirectMusicPerformance8Impl *obj; + struct performance *obj; HRESULT hr; TRACE("(%s, %p)\n", debugstr_guid(iid), ret_iface); @@ -1261,7 +1262,7 @@ HRESULT create_dmperformance(REFIID iid, void **ret_iface) obj->pDefaultPath = NULL; InitializeCriticalSection(&obj->safe); - obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IDirectMusicPerformance8Impl*->safe"); + obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": performance->safe"); wine_rb_init(&obj->pchannels, pchannel_block_compare); obj->rtLatencyTime = 100; /* 100 ms TO FIX */ From 66785e1d7555c5947e0a65729c4ea4561cf52633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 21:59:03 +0200 Subject: [PATCH 0849/1148] dmime: Fix indentation in IDirectMusicPerformance_QueryInterface. (cherry picked from commit e5ae7f90eebc5618385afaf539157be8110a8512) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index d6f7e76686e..a185716761d 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -252,21 +252,23 @@ static inline struct performance *impl_from_IDirectMusicPerformance8(IDirectMusi } /* IDirectMusicPerformance8 IUnknown part: */ -static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ppv) +static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ret_iface) { - TRACE("(%p, %s,%p)\n", iface, debugstr_dmguid(riid), ppv); - - if (IsEqualIID (riid, &IID_IUnknown) || - IsEqualIID (riid, &IID_IDirectMusicPerformance) || - IsEqualIID (riid, &IID_IDirectMusicPerformance2) || - IsEqualIID (riid, &IID_IDirectMusicPerformance8)) { - *ppv = iface; - IUnknown_AddRef(iface); - return S_OK; - } + TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); + + if (IsEqualGUID(riid, &IID_IUnknown) + || IsEqualGUID(riid, &IID_IDirectMusicPerformance) + || IsEqualGUID(riid, &IID_IDirectMusicPerformance2) + || IsEqualGUID(riid, &IID_IDirectMusicPerformance8)) + { + *ret_iface = iface; + IUnknown_AddRef(iface); + return S_OK; + } - WARN("(%p, %s,%p): not found\n", iface, debugstr_dmguid(riid), ppv); - return E_NOINTERFACE; + *ret_iface = NULL; + WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface); + return E_NOINTERFACE; } static ULONG WINAPI performance_AddRef(IDirectMusicPerformance8 *iface) From 21ebd922191dfc889e1456e75b990f0497ebcb18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 21:50:46 +0200 Subject: [PATCH 0850/1148] dmime: Add a IDirectMusicGraph interface to the performance. (cherry picked from commit 2e25af4fd3e7cd618b2080502c429446e35d4666) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 74 ++++++++++++++++++++++++++++++++++ dlls/dmime/tests/dmime.c | 22 ++++------ dlls/dmime/tests/performance.c | 2 +- 3 files changed, 83 insertions(+), 15 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index a185716761d..887d9a7bf16 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -37,6 +37,7 @@ struct pchannel_block { struct performance { IDirectMusicPerformance8 IDirectMusicPerformance8_iface; + IDirectMusicGraph IDirectMusicGraph_iface; LONG ref; IDirectMusic8 *dmusic; IDirectSound *dsound; @@ -254,6 +255,8 @@ static inline struct performance *impl_from_IDirectMusicPerformance8(IDirectMusi /* IDirectMusicPerformance8 IUnknown part: */ static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ret_iface) { + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); if (IsEqualGUID(riid, &IID_IUnknown) @@ -266,6 +269,13 @@ static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface return S_OK; } + if (IsEqualGUID(riid, &IID_IDirectMusicGraph)) + { + *ret_iface = &This->IDirectMusicGraph_iface; + IDirectMusicGraph_AddRef(&This->IDirectMusicGraph_iface); + return S_OK; + } + *ret_iface = NULL; WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface); return E_NOINTERFACE; @@ -1249,6 +1259,69 @@ static const IDirectMusicPerformance8Vtbl performance_vtbl = performance_GetParamEx, }; +static inline struct performance *impl_from_IDirectMusicGraph(IDirectMusicGraph *iface) +{ + return CONTAINING_RECORD(iface, struct performance, IDirectMusicGraph_iface); +} + +static HRESULT WINAPI performance_graph_QueryInterface(IDirectMusicGraph *iface, REFIID riid, void **ret_iface) +{ + struct performance *This = impl_from_IDirectMusicGraph(iface); + return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface); +} + +static ULONG WINAPI performance_graph_AddRef(IDirectMusicGraph *iface) +{ + struct performance *This = impl_from_IDirectMusicGraph(iface); + return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface); +} + +static ULONG WINAPI performance_graph_Release(IDirectMusicGraph *iface) +{ + struct performance *This = impl_from_IDirectMusicGraph(iface); + return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface); +} + +static HRESULT WINAPI performance_graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg) +{ + struct performance *This = impl_from_IDirectMusicGraph(iface); + FIXME("(%p, %p): stub\n", This, msg); + return E_NOTIMPL; +} + +static HRESULT WINAPI performance_graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool *tool, + DWORD *channels, DWORD channels_count, LONG index) +{ + struct performance *This = impl_from_IDirectMusicGraph(iface); + FIXME("(%p, %p, %p, %lu, %ld): stub\n", This, tool, channels, channels_count, index); + return E_NOTIMPL; +} + +static HRESULT WINAPI performance_graph_GetTool(IDirectMusicGraph *iface, DWORD index, IDirectMusicTool **tool) +{ + struct performance *This = impl_from_IDirectMusicGraph(iface); + FIXME("(%p, %lu, %p): stub\n", This, index, tool); + return E_NOTIMPL; +} + +static HRESULT WINAPI performance_graph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool *tool) +{ + struct performance *This = impl_from_IDirectMusicGraph(iface); + FIXME("(%p, %p): stub\n", This, tool); + return E_NOTIMPL; +} + +static const IDirectMusicGraphVtbl performance_graph_vtbl = +{ + performance_graph_QueryInterface, + performance_graph_AddRef, + performance_graph_Release, + performance_graph_StampPMsg, + performance_graph_InsertTool, + performance_graph_GetTool, + performance_graph_RemoveTool, +}; + /* for ClassFactory */ HRESULT create_dmperformance(REFIID iid, void **ret_iface) { @@ -1260,6 +1333,7 @@ HRESULT create_dmperformance(REFIID iid, void **ret_iface) *ret_iface = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicPerformance8_iface.lpVtbl = &performance_vtbl; + obj->IDirectMusicGraph_iface.lpVtbl = &performance_graph_vtbl; obj->ref = 1; obj->pDefaultPath = NULL; diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index e95a1c02427..6dcb2b820c9 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1536,8 +1536,7 @@ static void test_performance_graph(void) /* performance exposes a graph interface but it's not an actual toolgraph */ hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (hr != S_OK) goto skip_graph; + ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicGraph_InsertTool(graph, (IDirectMusicTool *)tool, NULL, 0, -1); ok(hr == E_NOTIMPL, "got %#lx\n", hr); hr = IDirectMusicGraph_GetTool(graph, 0, &tmp_tool); @@ -1578,7 +1577,6 @@ static void test_performance_graph(void) IDirectMusicGraph_Release(graph); -skip_graph: /* performance doesn't have a default embedded toolgraph */ hr = IDirectMusicPerformance_GetGraph(performance, &graph); ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); @@ -1604,8 +1602,7 @@ static void test_performance_graph(void) /* test IDirectMusicGraph_StampPMsg usage */ hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (hr != S_OK) goto skip_graph2; + ok(hr == S_OK, "got %#lx\n", hr); memset(&msg, 0, sizeof(msg)); hr = IDirectMusicGraph_StampPMsg(graph, &msg); @@ -1636,7 +1633,6 @@ static void test_performance_graph(void) IDirectMusicGraph_Release(graph); -skip_graph2: IDirectMusicPerformance_Release(performance); IDirectMusicTool_Release(tool); } @@ -1874,12 +1870,11 @@ static void test_performance_pmsg(void) msg->dwFlags = DMUS_PMSGF_REFTIME; msg->dwType = DMUS_PMSGT_USER; - graph = NULL; hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_StampPMsg(graph, msg); todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (graph) hr = IDirectMusicGraph_StampPMsg(graph, msg); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (graph) IDirectMusicGraph_Release(graph); + IDirectMusicGraph_Release(graph); hr = IDirectMusicPerformance_SendPMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); @@ -1906,11 +1901,10 @@ static void test_performance_pmsg(void) msg->dwType = DMUS_PMSGT_USER; hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (!graph) hr = S_OK; - else hr = IDirectMusicGraph_StampPMsg(graph, msg); ok(hr == S_OK, "got %#lx\n", hr); - if (graph) IDirectMusicGraph_Release(graph); + hr = IDirectMusicGraph_StampPMsg(graph, msg); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicGraph_Release(graph); msg->dwFlags &= ~(DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME); msg->dwFlags |= delivery_flags[i]; diff --git a/dlls/dmime/tests/performance.c b/dlls/dmime/tests/performance.c index 4af91fc3130..5cb91a78387 100644 --- a/dlls/dmime/tests/performance.c +++ b/dlls/dmime/tests/performance.c @@ -627,7 +627,7 @@ static void test_performance_graph(void) ok(graph2 == NULL, "unexpected pointer.\n"); hr = IDirectMusicPerformance8_QueryInterface(perf, &IID_IDirectMusicGraph, (void**)&graph); - todo_wine ok(hr == S_OK, "Failed: %#lx\n", hr); + ok(hr == S_OK, "Failed: %#lx\n", hr); if (graph) IDirectMusicGraph_Release(graph); From 4e0ce65150c5009ad2bba5d88ccea57c8590878e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 21:56:20 +0200 Subject: [PATCH 0851/1148] dmime: Add a IDirectMusicTool interface to the performance. (cherry picked from commit 428eb3e87c863fd391f2b5b91a8d67036cd21120) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 89 ++++++++++++++++++++++++++++++++++++++++ dlls/dmime/tests/dmime.c | 6 +-- 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 887d9a7bf16..1f8de0837d5 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -38,6 +38,7 @@ struct performance { IDirectMusicPerformance8 IDirectMusicPerformance8_iface; IDirectMusicGraph IDirectMusicGraph_iface; + IDirectMusicTool IDirectMusicTool_iface; LONG ref; IDirectMusic8 *dmusic; IDirectSound *dsound; @@ -276,6 +277,13 @@ static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface return S_OK; } + if (IsEqualGUID(riid, &IID_IDirectMusicTool)) + { + *ret_iface = &This->IDirectMusicTool_iface; + IDirectMusicTool_AddRef(&This->IDirectMusicTool_iface); + return S_OK; + } + *ret_iface = NULL; WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface); return E_NOINTERFACE; @@ -1322,6 +1330,86 @@ static const IDirectMusicGraphVtbl performance_graph_vtbl = performance_graph_RemoveTool, }; +static inline struct performance *impl_from_IDirectMusicTool(IDirectMusicTool *iface) +{ + return CONTAINING_RECORD(iface, struct performance, IDirectMusicTool_iface); +} + +static HRESULT WINAPI performance_tool_QueryInterface(IDirectMusicTool *iface, REFIID riid, void **ret_iface) +{ + struct performance *This = impl_from_IDirectMusicTool(iface); + return IDirectMusicPerformance8_QueryInterface(&This->IDirectMusicPerformance8_iface, riid, ret_iface); +} + +static ULONG WINAPI performance_tool_AddRef(IDirectMusicTool *iface) +{ + struct performance *This = impl_from_IDirectMusicTool(iface); + return IDirectMusicPerformance8_AddRef(&This->IDirectMusicPerformance8_iface); +} + +static ULONG WINAPI performance_tool_Release(IDirectMusicTool *iface) +{ + struct performance *This = impl_from_IDirectMusicTool(iface); + return IDirectMusicPerformance8_Release(&This->IDirectMusicPerformance8_iface); +} + +static HRESULT WINAPI performance_tool_Init(IDirectMusicTool *iface, IDirectMusicGraph *graph) +{ + struct performance *This = impl_from_IDirectMusicTool(iface); + FIXME("(%p, %p): stub\n", This, graph); + return E_NOTIMPL; +} + +static HRESULT WINAPI performance_tool_GetMsgDeliveryType(IDirectMusicTool *iface, DWORD *type) +{ + struct performance *This = impl_from_IDirectMusicTool(iface); + FIXME("(%p, %p): stub\n", This, type); + return E_NOTIMPL; +} + +static HRESULT WINAPI performance_tool_GetMediaTypeArraySize(IDirectMusicTool *iface, DWORD *size) +{ + struct performance *This = impl_from_IDirectMusicTool(iface); + FIXME("(%p, %p): stub\n", This, size); + return E_NOTIMPL; +} + +static HRESULT WINAPI performance_tool_GetMediaTypes(IDirectMusicTool *iface, DWORD **types, DWORD size) +{ + struct performance *This = impl_from_IDirectMusicTool(iface); + FIXME("(%p, %p, %lu): stub\n", This, types, size); + return E_NOTIMPL; +} + +static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, + IDirectMusicPerformance *performance, DMUS_PMSG *msg) +{ + struct performance *This = impl_from_IDirectMusicTool(iface); + FIXME("(%p, %p, %p): stub\n", This, performance, msg); + return E_NOTIMPL; +} + +static HRESULT WINAPI performance_tool_Flush(IDirectMusicTool *iface, + IDirectMusicPerformance *performance, DMUS_PMSG *msg, REFERENCE_TIME time) +{ + struct performance *This = impl_from_IDirectMusicTool(iface); + FIXME("(%p, %p, %p, %I64d): stub\n", This, performance, msg, time); + return E_NOTIMPL; +} + +static const IDirectMusicToolVtbl performance_tool_vtbl = +{ + performance_tool_QueryInterface, + performance_tool_AddRef, + performance_tool_Release, + performance_tool_Init, + performance_tool_GetMsgDeliveryType, + performance_tool_GetMediaTypeArraySize, + performance_tool_GetMediaTypes, + performance_tool_ProcessPMsg, + performance_tool_Flush, +}; + /* for ClassFactory */ HRESULT create_dmperformance(REFIID iid, void **ret_iface) { @@ -1334,6 +1422,7 @@ HRESULT create_dmperformance(REFIID iid, void **ret_iface) if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicPerformance8_iface.lpVtbl = &performance_vtbl; obj->IDirectMusicGraph_iface.lpVtbl = &performance_graph_vtbl; + obj->IDirectMusicTool_iface.lpVtbl = &performance_tool_vtbl; obj->ref = 1; obj->pDefaultPath = NULL; diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 6dcb2b820c9..0aeed24b200 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1489,10 +1489,9 @@ static void test_performance_tool(void) check_interface(performance, &IID_IDirectMusicTool8, FALSE); hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicTool, (void **)&tool); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (hr != S_OK) goto skip_tool; + ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicTool_Init(tool, graph); ok(hr == E_NOTIMPL, "got %#lx\n", hr); @@ -1514,7 +1513,6 @@ static void test_performance_tool(void) IDirectMusicGraph_Release(graph); IDirectMusicTool_Release(tool); -skip_tool: IDirectMusicPerformance_Release(performance); } From 4e3cf4620ada9ba50405dc3768e5a29f387a64d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 22 Aug 2023 17:51:00 +0200 Subject: [PATCH 0852/1148] dmband: Always return S_FALSE from DllCanUnloadNow. (cherry picked from commit c3ebc387f38815748bbc1b01199baa9ea44eb04a) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 2 -- dlls/dmband/bandtrack.c | 2 -- dlls/dmband/dmband_main.c | 22 ---------------------- dlls/dmband/dmband_private.h | 8 -------- 4 files changed, 34 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index d71863a1f9d..907377ebed5 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -83,7 +83,6 @@ static ULONG WINAPI IDirectMusicBandImpl_Release(IDirectMusicBand *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMBAND_UnlockModule(); } return ref; @@ -527,7 +526,6 @@ HRESULT create_dmband(REFIID lpcGUID, void **ppobj) obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; list_init (&obj->Instruments); - DMBAND_LockModule(); hr = IDirectMusicBand_QueryInterface(&obj->IDirectMusicBand_iface, lpcGUID, ppobj); IDirectMusicBand_Release(&obj->IDirectMusicBand_iface); diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index 0142b5b5188..25aca207612 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -82,7 +82,6 @@ static ULONG WINAPI band_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMBAND_UnlockModule(); } return ref; @@ -650,7 +649,6 @@ HRESULT create_dmbandtrack(REFIID lpcGUID, void **ppobj) track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; list_init (&track->Bands); - DMBAND_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmband/dmband_main.c b/dlls/dmband/dmband_main.c index c08f8b0a087..c032a931f31 100644 --- a/dlls/dmband/dmband_main.c +++ b/dlls/dmband/dmband_main.c @@ -23,8 +23,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmband); -LONG DMBAND_refCount = 0; - typedef struct { IClassFactory IClassFactory_iface; HRESULT (*fnCreateInstance)(REFIID riid, void **ret_iface); @@ -60,15 +58,11 @@ static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID r static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) { - DMBAND_LockModule(); - return 2; /* non-heap based object */ } static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) { - DMBAND_UnlockModule(); - return 1; /* non-heap based object */ } @@ -90,12 +84,6 @@ static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock) { TRACE("(%d)\n", dolock); - - if (dolock) - DMBAND_LockModule(); - else - DMBAND_UnlockModule(); - return S_OK; } @@ -110,16 +98,6 @@ static const IClassFactoryVtbl classfactory_vtbl = { static IClassFactoryImpl Band_CF = {{&classfactory_vtbl}, create_dmband}; static IClassFactoryImpl BandTrack_CF = {{&classfactory_vtbl}, create_dmbandtrack}; -/****************************************************************** - * DllCanUnloadNow (DMBAND.@) - * - * - */ -HRESULT WINAPI DllCanUnloadNow(void) -{ - return DMBAND_refCount != 0 ? S_FALSE : S_OK; -} - /****************************************************************** * DllGetClassObject (DMBAND.@) diff --git a/dlls/dmband/dmband_private.h b/dlls/dmband/dmband_private.h index b89dd08dd83..0b1807eaf93 100644 --- a/dlls/dmband/dmband_private.h +++ b/dlls/dmband/dmband_private.h @@ -73,14 +73,6 @@ typedef struct _DMUS_PRIVATE_BAND { IDirectMusicBand *band; } DMUS_PRIVATE_BAND, *LPDMUS_PRIVATE_BAND; - -/********************************************************************** - * Dll lifetime tracking declaration for dmband.dll - */ -extern LONG DMBAND_refCount; -static inline void DMBAND_LockModule(void) { InterlockedIncrement( &DMBAND_refCount ); } -static inline void DMBAND_UnlockModule(void) { InterlockedDecrement( &DMBAND_refCount ); } - /***************************************************************************** * Misc. */ From d01f25ad6b98fbe40146cd28444ec614d3177f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 09:16:50 +0200 Subject: [PATCH 0853/1148] dmband: Use CRT allocation functions. (cherry picked from commit b60ee21fb2d657d2bf848925dfb8464bca6f0d9f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 20 +++++--------------- dlls/dmband/bandtrack.c | 18 +++++------------- dlls/dmband/dmobject.c | 7 +++---- 3 files changed, 13 insertions(+), 32 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index 907377ebed5..02e3573bab9 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -81,9 +81,7 @@ static ULONG WINAPI IDirectMusicBandImpl_Release(IDirectMusicBand *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -277,11 +275,7 @@ static HRESULT parse_instrument(IDirectMusicBandImpl *This, DMUS_PRIVATE_CHUNK * /* * @TODO insert pNewInstrument into This */ - pNewInstrument = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_INSTRUMENT)); - if (NULL == pNewInstrument) { - ERR(": no more memory\n"); - return E_OUTOFMEMORY; - } + if (!(pNewInstrument = calloc(1, sizeof(*pNewInstrument)))) return E_OUTOFMEMORY; memcpy(&pNewInstrument->pInstrument, &inst, sizeof(DMUS_IO_INSTRUMENT)); pNewInstrument->ppReferenceCollection = NULL; if (NULL != pObject) { @@ -289,8 +283,7 @@ static HRESULT parse_instrument(IDirectMusicBandImpl *This, DMUS_PRIVATE_CHUNK * hr = IDirectMusicObject_QueryInterface (pObject, &IID_IDirectMusicCollection, (void**) &pCol); if (FAILED(hr)) { ERR(": failed to get IDirectMusicCollection Interface from DMObject\n"); - HeapFree(GetProcessHeap(), 0, pNewInstrument); - + free(pNewInstrument); return hr; } pNewInstrument->ppReferenceCollection = pCol; @@ -514,11 +507,8 @@ HRESULT create_dmband(REFIID lpcGUID, void **ppobj) IDirectMusicBandImpl* obj; HRESULT hr; - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicBandImpl)); - if (NULL == obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicBand_iface.lpVtbl = &dmband_vtbl; obj->ref = 1; dmobject_init(&obj->dmobj, &CLSID_DirectMusicBand, (IUnknown *)&obj->IDirectMusicBand_iface); diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index 25aca207612..bdb9344b064 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -80,9 +80,7 @@ static ULONG WINAPI band_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -344,11 +342,8 @@ static HRESULT load_band(IDirectMusicBandTrack *This, IStream *pClonedStream, * @TODO insert pBand into This */ if (SUCCEEDED(hr)) { - LPDMUS_PRIVATE_BAND pNewBand = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_BAND)); - if (NULL == pNewBand) { - ERR(": no more memory\n"); - return E_OUTOFMEMORY; - } + LPDMUS_PRIVATE_BAND pNewBand; + if (!(pNewBand = calloc(1, sizeof(*pNewBand)))) return E_OUTOFMEMORY; pNewBand->BandHeader = *pHeader; pNewBand->band = *ppBand; IDirectMusicBand_AddRef(*ppBand); @@ -637,11 +632,8 @@ HRESULT create_dmbandtrack(REFIID lpcGUID, void **ppobj) IDirectMusicBandTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicBandTrack, diff --git a/dlls/dmband/dmobject.c b/dlls/dmband/dmobject.c index b526b23d031..07d887a376c 100644 --- a/dlls/dmband/dmobject.c +++ b/dlls/dmband/dmobject.c @@ -28,7 +28,6 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -375,7 +374,7 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) /* Reads chunk data of the form: DWORD - size of array element element[] - Array of elements - The caller needs to heap_free() the array. + The caller needs to free() the array. */ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, unsigned int *count, DWORD elem_size) @@ -400,10 +399,10 @@ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, *count = (chunk->size - sizeof(DWORD)) / elem_size; size = *count * elem_size; - if (!(*array = heap_alloc(size))) + if (!(*array = malloc(size))) return E_OUTOFMEMORY; if (FAILED(hr = stream_read(stream, *array, size))) { - heap_free(*array); + free(*array); *array = NULL; return hr; } From 8d133674ab8568050c5f601de80c7758dc58f649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 30 Aug 2023 19:57:16 +0200 Subject: [PATCH 0854/1148] dmband: Use PARENTSRC with dmusic. (cherry picked from commit 649de392194c83841b2e6f0c03a34c3b370ebf2a) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/Makefile.in | 1 + dlls/dmband/dmobject.c | 732 ---------------------------------------- dlls/dmband/dmobject.h | 124 ------- 3 files changed, 1 insertion(+), 856 deletions(-) delete mode 100644 dlls/dmband/dmobject.c delete mode 100644 dlls/dmband/dmobject.h diff --git a/dlls/dmband/Makefile.in b/dlls/dmband/Makefile.in index 6fa1502f6b0..ef6b6a44c60 100644 --- a/dlls/dmband/Makefile.in +++ b/dlls/dmband/Makefile.in @@ -1,5 +1,6 @@ MODULE = dmband.dll IMPORTS = dxguid uuid ole32 advapi32 +PARENTSRC = ../dmusic C_SRCS = \ band.c \ diff --git a/dlls/dmband/dmobject.c b/dlls/dmband/dmobject.c deleted file mode 100644 index 07d887a376c..00000000000 --- a/dlls/dmband/dmobject.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.c - * - * Copyright (C) 2003-2004 Rok Mandeljc - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#define COBJMACROS -#include -#include "objbase.h" -#include "dmusici.h" -#include "dmusicf.h" -#include "dmusics.h" -#include "dmobject.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(dmobj); -WINE_DECLARE_DEBUG_CHANNEL(dmfile); - -/* Debugging helpers */ -const char *debugstr_dmguid(const GUID *id) { - unsigned int i; -#define X(guid) { &guid, #guid } - static const struct { - const GUID *guid; - const char *name; - } guids[] = { - /* CLSIDs */ - X(CLSID_AudioVBScript), - X(CLSID_DirectMusic), - X(CLSID_DirectMusicAudioPathConfig), - X(CLSID_DirectMusicAuditionTrack), - X(CLSID_DirectMusicBand), - X(CLSID_DirectMusicBandTrack), - X(CLSID_DirectMusicChordMapTrack), - X(CLSID_DirectMusicChordMap), - X(CLSID_DirectMusicChordTrack), - X(CLSID_DirectMusicCollection), - X(CLSID_DirectMusicCommandTrack), - X(CLSID_DirectMusicComposer), - X(CLSID_DirectMusicContainer), - X(CLSID_DirectMusicGraph), - X(CLSID_DirectMusicLoader), - X(CLSID_DirectMusicLyricsTrack), - X(CLSID_DirectMusicMarkerTrack), - X(CLSID_DirectMusicMelodyFormulationTrack), - X(CLSID_DirectMusicMotifTrack), - X(CLSID_DirectMusicMuteTrack), - X(CLSID_DirectMusicParamControlTrack), - X(CLSID_DirectMusicPatternTrack), - X(CLSID_DirectMusicPerformance), - X(CLSID_DirectMusicScript), - X(CLSID_DirectMusicScriptAutoImpSegment), - X(CLSID_DirectMusicScriptAutoImpPerformance), - X(CLSID_DirectMusicScriptAutoImpSegmentState), - X(CLSID_DirectMusicScriptAutoImpAudioPathConfig), - X(CLSID_DirectMusicScriptAutoImpAudioPath), - X(CLSID_DirectMusicScriptAutoImpSong), - X(CLSID_DirectMusicScriptSourceCodeLoader), - X(CLSID_DirectMusicScriptTrack), - X(CLSID_DirectMusicSection), - X(CLSID_DirectMusicSegment), - X(CLSID_DirectMusicSegmentState), - X(CLSID_DirectMusicSegmentTriggerTrack), - X(CLSID_DirectMusicSegTriggerTrack), - X(CLSID_DirectMusicSeqTrack), - X(CLSID_DirectMusicSignPostTrack), - X(CLSID_DirectMusicSong), - X(CLSID_DirectMusicStyle), - X(CLSID_DirectMusicStyleTrack), - X(CLSID_DirectMusicSynth), - X(CLSID_DirectMusicSynthSink), - X(CLSID_DirectMusicSysExTrack), - X(CLSID_DirectMusicTemplate), - X(CLSID_DirectMusicTempoTrack), - X(CLSID_DirectMusicTimeSigTrack), - X(CLSID_DirectMusicWaveTrack), - X(CLSID_DirectSoundWave), - /* IIDs */ - X(IID_IDirectMusic), - X(IID_IDirectMusic2), - X(IID_IDirectMusic8), - X(IID_IDirectMusicAudioPath), - X(IID_IDirectMusicBand), - X(IID_IDirectMusicBuffer), - X(IID_IDirectMusicChordMap), - X(IID_IDirectMusicCollection), - X(IID_IDirectMusicComposer), - X(IID_IDirectMusicContainer), - X(IID_IDirectMusicDownload), - X(IID_IDirectMusicDownloadedInstrument), - X(IID_IDirectMusicGetLoader), - X(IID_IDirectMusicGraph), - X(IID_IDirectMusicInstrument), - X(IID_IDirectMusicLoader), - X(IID_IDirectMusicLoader8), - X(IID_IDirectMusicObject), - X(IID_IDirectMusicPatternTrack), - X(IID_IDirectMusicPerformance), - X(IID_IDirectMusicPerformance2), - X(IID_IDirectMusicPerformance8), - X(IID_IDirectMusicPort), - X(IID_IDirectMusicPortDownload), - X(IID_IDirectMusicScript), - X(IID_IDirectMusicSegment), - X(IID_IDirectMusicSegment2), - X(IID_IDirectMusicSegment8), - X(IID_IDirectMusicSegmentState), - X(IID_IDirectMusicSegmentState8), - X(IID_IDirectMusicStyle), - X(IID_IDirectMusicStyle8), - X(IID_IDirectMusicSynth), - X(IID_IDirectMusicSynth8), - X(IID_IDirectMusicSynthSink), - X(IID_IDirectMusicThru), - X(IID_IDirectMusicTool), - X(IID_IDirectMusicTool8), - X(IID_IDirectMusicTrack), - X(IID_IDirectMusicTrack8), - X(IID_IUnknown), - X(IID_IPersistStream), - X(IID_IStream), - X(IID_IClassFactory), - /* GUIDs */ - X(GUID_DirectMusicAllTypes), - X(GUID_NOTIFICATION_CHORD), - X(GUID_NOTIFICATION_COMMAND), - X(GUID_NOTIFICATION_MEASUREANDBEAT), - X(GUID_NOTIFICATION_PERFORMANCE), - X(GUID_NOTIFICATION_RECOMPOSE), - X(GUID_NOTIFICATION_SEGMENT), - X(GUID_BandParam), - X(GUID_ChordParam), - X(GUID_CommandParam), - X(GUID_CommandParam2), - X(GUID_CommandParamNext), - X(GUID_IDirectMusicBand), - X(GUID_IDirectMusicChordMap), - X(GUID_IDirectMusicStyle), - X(GUID_MuteParam), - X(GUID_Play_Marker), - X(GUID_RhythmParam), - X(GUID_TempoParam), - X(GUID_TimeSignature), - X(GUID_Valid_Start_Time), - X(GUID_Clear_All_Bands), - X(GUID_ConnectToDLSCollection), - X(GUID_Disable_Auto_Download), - X(GUID_DisableTempo), - X(GUID_DisableTimeSig), - X(GUID_Download), - X(GUID_DownloadToAudioPath), - X(GUID_Enable_Auto_Download), - X(GUID_EnableTempo), - X(GUID_EnableTimeSig), - X(GUID_IgnoreBankSelectForGM), - X(GUID_SeedVariations), - X(GUID_StandardMIDIFile), - X(GUID_Unload), - X(GUID_UnloadFromAudioPath), - X(GUID_Variations), - X(GUID_PerfMasterTempo), - X(GUID_PerfMasterVolume), - X(GUID_PerfMasterGrooveLevel), - X(GUID_PerfAutoDownload), - X(GUID_DefaultGMCollection), - X(GUID_Synth_Default), - X(GUID_Buffer_Reverb), - X(GUID_Buffer_EnvReverb), - X(GUID_Buffer_Stereo), - X(GUID_Buffer_3D_Dry), - X(GUID_Buffer_Mono), - X(GUID_DMUS_PROP_GM_Hardware), - X(GUID_DMUS_PROP_GS_Capable), - X(GUID_DMUS_PROP_GS_Hardware), - X(GUID_DMUS_PROP_DLS1), - X(GUID_DMUS_PROP_DLS2), - X(GUID_DMUS_PROP_Effects), - X(GUID_DMUS_PROP_INSTRUMENT2), - X(GUID_DMUS_PROP_LegacyCaps), - X(GUID_DMUS_PROP_MemorySize), - X(GUID_DMUS_PROP_SampleMemorySize), - X(GUID_DMUS_PROP_SamplePlaybackRate), - X(GUID_DMUS_PROP_SetSynthSink), - X(GUID_DMUS_PROP_SinkUsesDSound), - X(GUID_DMUS_PROP_SynthSink_DSOUND), - X(GUID_DMUS_PROP_SynthSink_WAVE), - X(GUID_DMUS_PROP_Volume), - X(GUID_DMUS_PROP_WavesReverb), - X(GUID_DMUS_PROP_WriteLatency), - X(GUID_DMUS_PROP_WritePeriod), - X(GUID_DMUS_PROP_XG_Capable), - X(GUID_DMUS_PROP_XG_Hardware) - }; -#undef X - - if (!id) - return "(null)"; - - for (i = 0; i < ARRAY_SIZE(guids); i++) - if (IsEqualGUID(id, guids[i].guid)) - return guids[i].name; - - return debugstr_guid(id); -} - -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) -{ - if (!desc || !TRACE_ON(dmfile)) - return; - - TRACE_(dmfile)("DMUS_OBJECTDESC (%p):", desc); - TRACE_(dmfile)(" - dwSize = %lu\n", desc->dwSize); - -#define X(flag) if (desc->dwValidData & flag) TRACE_(dmfile)(#flag " ") - TRACE_(dmfile)(" - dwValidData = %#08lx ( ", desc->dwValidData); - X(DMUS_OBJ_OBJECT); - X(DMUS_OBJ_CLASS); - X(DMUS_OBJ_NAME); - X(DMUS_OBJ_CATEGORY); - X(DMUS_OBJ_FILENAME); - X(DMUS_OBJ_FULLPATH); - X(DMUS_OBJ_URL); - X(DMUS_OBJ_VERSION); - X(DMUS_OBJ_DATE); - X(DMUS_OBJ_LOADED); - X(DMUS_OBJ_MEMORY); - X(DMUS_OBJ_STREAM); - TRACE_(dmfile)(")\n"); -#undef X - - if (desc->dwValidData & DMUS_OBJ_CLASS) - TRACE_(dmfile)(" - guidClass = %s\n", debugstr_dmguid(&desc->guidClass)); - if (desc->dwValidData & DMUS_OBJ_OBJECT) - TRACE_(dmfile)(" - guidObject = %s\n", debugstr_guid(&desc->guidObject)); - - if (desc->dwValidData & DMUS_OBJ_DATE) { - SYSTEMTIME time; - FileTimeToSystemTime(&desc->ftDate, &time); - TRACE_(dmfile)(" - ftDate = \'%04u-%02u-%02u %02u:%02u:%02u\'\n", - time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); - } - if (desc->dwValidData & DMUS_OBJ_VERSION) - TRACE_(dmfile)(" - vVersion = \'%u,%u,%u,%u\'\n", - HIWORD(desc->vVersion.dwVersionMS), LOWORD(desc->vVersion.dwVersionMS), - HIWORD(desc->vVersion.dwVersionLS), LOWORD(desc->vVersion.dwVersionLS)); - if (desc->dwValidData & DMUS_OBJ_NAME) - TRACE_(dmfile)(" - wszName = %s\n", debugstr_w(desc->wszName)); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - TRACE_(dmfile)(" - wszCategory = %s\n", debugstr_w(desc->wszCategory)); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - TRACE_(dmfile)(" - wszFileName = %s\n", debugstr_w(desc->wszFileName)); - if (desc->dwValidData & DMUS_OBJ_MEMORY) - TRACE_(dmfile)(" - llMemLength = 0x%s - pbMemData = %p\n", - wine_dbgstr_longlong(desc->llMemLength), desc->pbMemData); - if (desc->dwValidData & DMUS_OBJ_STREAM) - TRACE_(dmfile)(" - pStream = %p\n", desc->pStream); -} - - -/* RIFF format parsing */ -#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) - -const char *debugstr_chunk(const struct chunk_entry *chunk) -{ - const char *type = ""; - - if (!chunk) - return "(null)"; - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); - return wine_dbg_sprintf("%s chunk, %ssize %lu", debugstr_fourcc(chunk->id), type, chunk->size); -} - -static HRESULT stream_read(IStream *stream, void *data, ULONG size) -{ - ULONG read; - HRESULT hr; - - hr = IStream_Read(stream, data, size, &read); - if (FAILED(hr)) - TRACE_(dmfile)("IStream_Read failed: %#lx\n", hr); - else if (!read && read < size) { - /* All or nothing: Handle a partial read due to end of stream as an error */ - TRACE_(dmfile)("Short read: %lu < %lu\n", read, size); - return E_FAIL; - } - - return hr; -} - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) -{ - static const LARGE_INTEGER zero; - ULONGLONG ck_end = 0, p_end = 0; - HRESULT hr; - - hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); - if (FAILED(hr)) - return hr; - assert(!(chunk->offset.QuadPart & 1)); - if (chunk->parent) { - p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); - if (chunk->offset.QuadPart == p_end) - return S_FALSE; - ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); - if (hr != S_OK) - return hr; - if (chunk->parent) { - ck_end += (chunk->size + 1) & ~1; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { - hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); - if (hr != S_OK) - return hr != S_FALSE ? hr : E_FAIL; - } - - TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); - - return S_OK; -} - -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER end; - - end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; - - return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); -} - -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) -{ - HRESULT hr; - - if (chunk->id) { - hr = stream_skip_chunk(stream, chunk); - if (FAILED(hr)) - return hr; - } - - return stream_get_chunk(stream, chunk); -} - -/* Reads chunk data of the form: - DWORD - size of array element - element[] - Array of elements - The caller needs to free() the array. -*/ -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) -{ - DWORD size; - HRESULT hr; - - *array = NULL; - *count = 0; - - if (chunk->size < sizeof(DWORD)) { - WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); - return E_FAIL; - } - if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) - return hr; - if (size != elem_size) { - WARN_(dmfile)("%s: Array element size mismatch: got %lu, expected %lu\n", - debugstr_chunk(chunk), size, elem_size); - return DMUS_E_UNSUPPORTED_STREAM; - } - - *count = (chunk->size - sizeof(DWORD)) / elem_size; - size = *count * elem_size; - if (!(*array = malloc(size))) - return E_OUTOFMEMORY; - if (FAILED(hr = stream_read(stream, *array, size))) { - free(*array); - *array = NULL; - return hr; - } - - if (chunk->size > size + sizeof(DWORD)) { - WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); - stream_skip_chunk(stream, chunk); - return S_FALSE; - } - return S_OK; -} - -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) -{ - if (chunk->size != size) { - WARN_(dmfile)("Chunk %s (size %lu, offset %s) doesn't contains the expected data size %lu\n", - debugstr_fourcc(chunk->id), chunk->size, - wine_dbgstr_longlong(chunk->offset.QuadPart), size); - return E_FAIL; - } - return stream_read(stream, data, size); -} - -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) -{ - ULONG len; - HRESULT hr; - - hr = IStream_Read(stream, str, min(chunk->size, size), &len); - if (FAILED(hr)) - return hr; - - /* Don't assume the string is properly zero terminated */ - str[min(len, size - 1)] = 0; - - if (len < chunk->size) - return S_FALSE; - return S_OK; -} - - - -/* Generic IDirectMusicObject methods */ -static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IDirectMusicObject_iface); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - - TRACE("(%p/%p)->(%p)\n", iface, This, desc); - - if (!desc) - return E_POINTER; - - memcpy(desc, &This->desc, This->desc.dwSize); - - return S_OK; -} - -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - HRESULT ret = S_OK; - - TRACE("(%p, %p)\n", iface, desc); - - if (!desc) - return E_POINTER; - - /* Immutable property */ - if (desc->dwValidData & DMUS_OBJ_CLASS) - { - desc->dwValidData &= ~DMUS_OBJ_CLASS; - ret = S_FALSE; - } - /* Set only valid fields */ - if (desc->dwValidData & DMUS_OBJ_OBJECT) - This->desc.guidObject = desc->guidObject; - if (desc->dwValidData & DMUS_OBJ_NAME) - lstrcpynW(This->desc.wszName, desc->wszName, DMUS_MAX_NAME); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - lstrcpynW(This->desc.wszCategory, desc->wszCategory, DMUS_MAX_CATEGORY); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - lstrcpynW(This->desc.wszFileName, desc->wszFileName, DMUS_MAX_FILENAME); - if (desc->dwValidData & DMUS_OBJ_VERSION) - This->desc.vVersion = desc->vVersion; - if (desc->dwValidData & DMUS_OBJ_DATE) - This->desc.ftDate = desc->ftDate; - if (desc->dwValidData & DMUS_OBJ_MEMORY) { - This->desc.llMemLength = desc->llMemLength; - memcpy(This->desc.pbMemData, desc->pbMemData, desc->llMemLength); - } - if (desc->dwValidData & DMUS_OBJ_STREAM) - IStream_Clone(desc->pStream, &This->desc.pStream); - - This->desc.dwValidData |= desc->dwValidData; - - return ret; -} - -/* Helper for IDirectMusicObject::ParseDescriptor */ -static inline void info_get_name(IStream *stream, const struct chunk_entry *info, - DMUS_OBJECTDESC *desc) -{ - struct chunk_entry chunk = {.parent = info}; - char name[DMUS_MAX_NAME]; - ULONG len; - HRESULT hr = E_FAIL; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == mmioFOURCC('I','N','A','M')) - hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); - - if (SUCCEEDED(hr)) { - len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); - desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; - desc->dwValidData |= DMUS_OBJ_NAME; - } -} - -static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, - DMUS_OBJECTDESC *desc, BOOL inam) -{ - struct chunk_entry chunk = {.parent = unfo}; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) - if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; -} - -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) -{ - struct chunk_entry chunk = {.parent = riff}; - HRESULT hr; - - TRACE("Looking for %#lx in %p: %s\n", supported, stream, debugstr_chunk(riff)); - - desc->dwValidData = 0; - desc->dwSize = sizeof(*desc); - - while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { - switch (chunk.id) { - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; - break; - case DMUS_FOURCC_DATE_CHUNK: - if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, - &desc->ftDate, sizeof(desc->ftDate)) == S_OK) - desc->dwValidData |= DMUS_OBJ_DATE; - break; - case DMUS_FOURCC_FILE_CHUNK: - if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_FILENAME; - break; - case DMUS_FOURCC_GUID_CHUNK: - if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, - &desc->guidObject, sizeof(desc->guidObject)) == S_OK) - desc->dwValidData |= DMUS_OBJ_OBJECT; - break; - case DMUS_FOURCC_NAME_CHUNK: - if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; - break; - case DMUS_FOURCC_VERSION_CHUNK: - if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, - &desc->vVersion, sizeof(desc->vVersion)) == S_OK) - desc->dwValidData |= DMUS_OBJ_VERSION; - break; - case FOURCC_LIST: - if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) - unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); - else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) - info_get_name(stream, &chunk, desc); - break; - } - } - TRACE("Found %#lx\n", desc->dwValidData); - - return hr; -} - -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) -{ - struct chunk_entry chunk = {.parent = list}; - IDirectMusicGetLoader *getloader; - IDirectMusicLoader *loader; - DMUS_OBJECTDESC desc; - DMUS_IO_REFERENCE reference; - HRESULT hr; - - if (FAILED(hr = stream_next_chunk(stream, &chunk))) - return hr; - if (chunk.id != DMUS_FOURCC_REF_CHUNK) - return DMUS_E_UNSUPPORTED_STREAM; - - if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { - WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); - return hr; - } - TRACE("REFERENCE guidClassID %s, dwValidData %#lx\n", debugstr_dmguid(&reference.guidClassID), - reference.dwValidData); - - if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) - return hr; - desc.guidClass = reference.guidClassID; - desc.dwValidData |= DMUS_OBJ_CLASS; - dump_DMUS_OBJECTDESC(&desc); - - if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) - return hr; - hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); - IDirectMusicGetLoader_Release(getloader); - if (FAILED(hr)) - return hr; - - hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); - IDirectMusicLoader_Release(loader); - - return hr; -} - -/* Generic IPersistStream methods */ -static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IPersistStream_iface); -} - -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - - TRACE("(%p, %p)\n", This, class); - - if (!class) - return E_POINTER; - - *class = This->desc.guidClass; - - return S_OK; -} - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - TRACE("(%p, %p): method not implemented\n", iface, class); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) -{ - TRACE("(%p): method not implemented, always returning S_FALSE\n", iface); - return S_FALSE; -} - -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) -{ - TRACE("(%p, %p, %d): method not implemented\n", iface, stream, clear_dirty); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *size) -{ - TRACE("(%p, %p): method not implemented\n", iface, size); - return E_NOTIMPL; -} - - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) -{ - dmobj->outer_unk = outer_unk; - dmobj->desc.dwSize = sizeof(dmobj->desc); - dmobj->desc.dwValidData = DMUS_OBJ_CLASS; - dmobj->desc.guidClass = *class; -} diff --git a/dlls/dmband/dmobject.h b/dlls/dmband/dmobject.h deleted file mode 100644 index 772be015c80..00000000000 --- a/dlls/dmband/dmobject.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.h - * - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "wine/debug.h" - -/* RIFF stream parsing */ -struct chunk_entry; -struct chunk_entry { - FOURCC id; - DWORD size; - FOURCC type; /* valid only for LIST and RIFF chunks */ - ULARGE_INTEGER offset; /* chunk offset from start of stream */ - const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ -}; - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); - -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size); -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size); -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size); - -static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - offset.QuadPart += sizeof(FOURCC); - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - -static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart; - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - - -/* IDirectMusicObject base object */ -struct dmobject { - IDirectMusicObject IDirectMusicObject_iface; - IPersistStream IPersistStream_iface; - IUnknown *outer_unk; - DMUS_OBJECTDESC desc; -}; - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); - -/* Generic IDirectMusicObject methods */ -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); - -/* Helper for IDirectMusicObject::ParseDescriptor */ -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported); -/* Additional supported flags for dmobj_parsedescriptor. - DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ - -/* 'DMRF' (reference list) helper */ -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj); - -/* Generic IPersistStream methods */ -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class); -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty); -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size); - -/* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk); -const char *debugstr_dmguid(const GUID *id); -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); - -static inline const char *debugstr_fourcc(DWORD fourcc) -{ - if (!fourcc) return "''"; - return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), - (char)(fourcc >> 16), (char)(fourcc >> 24)); -} From 443a8c653ba7c460d71da780bb83e69bfad8c31f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 22 Aug 2023 17:36:49 +0200 Subject: [PATCH 0855/1148] dswave: Always return S_FALSE from DllCanUnloadNow. (cherry picked from commit 65f25a150fe19eb883cae89327960d63a22afab5) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dswave/dswave.c | 2 -- dlls/dswave/dswave_main.c | 23 ----------------------- dlls/dswave/dswave_private.h | 7 ------- 3 files changed, 32 deletions(-) diff --git a/dlls/dswave/dswave.c b/dlls/dswave/dswave.c index e6d85d738fe..8e06fc8d13d 100644 --- a/dlls/dswave/dswave.c +++ b/dlls/dswave/dswave.c @@ -86,7 +86,6 @@ static ULONG WINAPI IUnknownImpl_Release(IUnknown *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DSWAVE_UnlockModule(); } return ref; @@ -191,7 +190,6 @@ HRESULT create_dswave(REFIID lpcGUID, void **ppobj) obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DSWAVE_LockModule(); hr = IUnknown_QueryInterface(&obj->IUnknown_iface, lpcGUID, ppobj); IUnknown_Release(&obj->IUnknown_iface); return hr; diff --git a/dlls/dswave/dswave_main.c b/dlls/dswave/dswave_main.c index 81dcc73de85..f2ce7bb950f 100644 --- a/dlls/dswave/dswave_main.c +++ b/dlls/dswave/dswave_main.c @@ -39,8 +39,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(dswave); -LONG DSWAVE_refCount = 0; - typedef struct { IClassFactory IClassFactory_iface; } IClassFactoryImpl; @@ -70,15 +68,11 @@ static HRESULT WINAPI WaveCF_QueryInterface(IClassFactory * iface, REFIID riid, static ULONG WINAPI WaveCF_AddRef(IClassFactory * iface) { - DSWAVE_LockModule(); - return 2; /* non-heap based object */ } static ULONG WINAPI WaveCF_Release(IClassFactory * iface) { - DSWAVE_UnlockModule(); - return 1; /* non-heap based object */ } @@ -98,12 +92,6 @@ static HRESULT WINAPI WaveCF_CreateInstance(IClassFactory * iface, IUnknown *out static HRESULT WINAPI WaveCF_LockServer(IClassFactory * iface, BOOL dolock) { TRACE("(%d)\n", dolock); - - if (dolock) - DSWAVE_LockModule(); - else - DSWAVE_UnlockModule(); - return S_OK; } @@ -117,17 +105,6 @@ static const IClassFactoryVtbl WaveCF_Vtbl = { static IClassFactoryImpl Wave_CF = {{&WaveCF_Vtbl}}; -/****************************************************************** - * DllCanUnloadNow (DSWAVE.@) - * - * - */ -HRESULT WINAPI DllCanUnloadNow(void) -{ - return DSWAVE_refCount != 0 ? S_FALSE : S_OK; -} - - /****************************************************************** * DllGetClassObject (DSWAVE.@) * diff --git a/dlls/dswave/dswave_private.h b/dlls/dswave/dswave_private.h index a7758a970bc..50406c676c1 100644 --- a/dlls/dswave/dswave_private.h +++ b/dlls/dswave/dswave_private.h @@ -44,11 +44,4 @@ */ extern HRESULT create_dswave(REFIID lpcGUID, void **ret_iface); -/********************************************************************** - * Dll lifetime tracking declaration for dswave.dll - */ -extern LONG DSWAVE_refCount; -static inline void DSWAVE_LockModule(void) { InterlockedIncrement( &DSWAVE_refCount ); } -static inline void DSWAVE_UnlockModule(void) { InterlockedDecrement( &DSWAVE_refCount ); } - #endif /* __WINE_DSWAVE_PRIVATE_H */ From 18b16714dd30f69c18a5b311071abbf584a1b702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 09:30:46 +0200 Subject: [PATCH 0856/1148] dswave: Use CRT allocation functions. (cherry picked from commit 8bcaec25529104d1e53fdd1242cb5ae211db411e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dswave/dswave.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/dlls/dswave/dswave.c b/dlls/dswave/dswave.c index 8e06fc8d13d..5bdd83dfa2c 100644 --- a/dlls/dswave/dswave.c +++ b/dlls/dswave/dswave.c @@ -84,9 +84,7 @@ static ULONG WINAPI IUnknownImpl_Release(IUnknown *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -179,11 +177,8 @@ HRESULT create_dswave(REFIID lpcGUID, void **ppobj) IDirectMusicWaveImpl *obj; HRESULT hr; - obj = HeapAlloc(GetProcessHeap(), 0, sizeof(IDirectMusicWaveImpl)); - if (!obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IUnknown_iface.lpVtbl = &unknown_vtbl; obj->ref = 1; dmobject_init(&obj->dmobj, &CLSID_DirectSoundWave, &obj->IUnknown_iface); From 8dbadb832c66c3925801879aaa0cdcb415cb9a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 30 Aug 2023 19:52:16 +0200 Subject: [PATCH 0857/1148] dswave: Use PARENTSRC with dmusic. (cherry picked from commit b27a036eaf9ca599e24d840f4b3aa0dab9a0040a) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dswave/Makefile.in | 1 + dlls/dswave/dmobject.c | 733 ---------------------------------------- dlls/dswave/dmobject.h | 124 ------- 3 files changed, 1 insertion(+), 857 deletions(-) delete mode 100644 dlls/dswave/dmobject.c delete mode 100644 dlls/dswave/dmobject.h diff --git a/dlls/dswave/Makefile.in b/dlls/dswave/Makefile.in index 9a08518397b..99b1e3ff9dc 100644 --- a/dlls/dswave/Makefile.in +++ b/dlls/dswave/Makefile.in @@ -1,5 +1,6 @@ MODULE = dswave.dll IMPORTS = dxguid uuid ole32 advapi32 +PARENTSRC = ../dmusic C_SRCS = \ dmobject.c \ diff --git a/dlls/dswave/dmobject.c b/dlls/dswave/dmobject.c deleted file mode 100644 index b526b23d031..00000000000 --- a/dlls/dswave/dmobject.c +++ /dev/null @@ -1,733 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.c - * - * Copyright (C) 2003-2004 Rok Mandeljc - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#define COBJMACROS -#include -#include "objbase.h" -#include "dmusici.h" -#include "dmusicf.h" -#include "dmusics.h" -#include "dmobject.h" -#include "wine/debug.h" -#include "wine/heap.h" - -WINE_DEFAULT_DEBUG_CHANNEL(dmobj); -WINE_DECLARE_DEBUG_CHANNEL(dmfile); - -/* Debugging helpers */ -const char *debugstr_dmguid(const GUID *id) { - unsigned int i; -#define X(guid) { &guid, #guid } - static const struct { - const GUID *guid; - const char *name; - } guids[] = { - /* CLSIDs */ - X(CLSID_AudioVBScript), - X(CLSID_DirectMusic), - X(CLSID_DirectMusicAudioPathConfig), - X(CLSID_DirectMusicAuditionTrack), - X(CLSID_DirectMusicBand), - X(CLSID_DirectMusicBandTrack), - X(CLSID_DirectMusicChordMapTrack), - X(CLSID_DirectMusicChordMap), - X(CLSID_DirectMusicChordTrack), - X(CLSID_DirectMusicCollection), - X(CLSID_DirectMusicCommandTrack), - X(CLSID_DirectMusicComposer), - X(CLSID_DirectMusicContainer), - X(CLSID_DirectMusicGraph), - X(CLSID_DirectMusicLoader), - X(CLSID_DirectMusicLyricsTrack), - X(CLSID_DirectMusicMarkerTrack), - X(CLSID_DirectMusicMelodyFormulationTrack), - X(CLSID_DirectMusicMotifTrack), - X(CLSID_DirectMusicMuteTrack), - X(CLSID_DirectMusicParamControlTrack), - X(CLSID_DirectMusicPatternTrack), - X(CLSID_DirectMusicPerformance), - X(CLSID_DirectMusicScript), - X(CLSID_DirectMusicScriptAutoImpSegment), - X(CLSID_DirectMusicScriptAutoImpPerformance), - X(CLSID_DirectMusicScriptAutoImpSegmentState), - X(CLSID_DirectMusicScriptAutoImpAudioPathConfig), - X(CLSID_DirectMusicScriptAutoImpAudioPath), - X(CLSID_DirectMusicScriptAutoImpSong), - X(CLSID_DirectMusicScriptSourceCodeLoader), - X(CLSID_DirectMusicScriptTrack), - X(CLSID_DirectMusicSection), - X(CLSID_DirectMusicSegment), - X(CLSID_DirectMusicSegmentState), - X(CLSID_DirectMusicSegmentTriggerTrack), - X(CLSID_DirectMusicSegTriggerTrack), - X(CLSID_DirectMusicSeqTrack), - X(CLSID_DirectMusicSignPostTrack), - X(CLSID_DirectMusicSong), - X(CLSID_DirectMusicStyle), - X(CLSID_DirectMusicStyleTrack), - X(CLSID_DirectMusicSynth), - X(CLSID_DirectMusicSynthSink), - X(CLSID_DirectMusicSysExTrack), - X(CLSID_DirectMusicTemplate), - X(CLSID_DirectMusicTempoTrack), - X(CLSID_DirectMusicTimeSigTrack), - X(CLSID_DirectMusicWaveTrack), - X(CLSID_DirectSoundWave), - /* IIDs */ - X(IID_IDirectMusic), - X(IID_IDirectMusic2), - X(IID_IDirectMusic8), - X(IID_IDirectMusicAudioPath), - X(IID_IDirectMusicBand), - X(IID_IDirectMusicBuffer), - X(IID_IDirectMusicChordMap), - X(IID_IDirectMusicCollection), - X(IID_IDirectMusicComposer), - X(IID_IDirectMusicContainer), - X(IID_IDirectMusicDownload), - X(IID_IDirectMusicDownloadedInstrument), - X(IID_IDirectMusicGetLoader), - X(IID_IDirectMusicGraph), - X(IID_IDirectMusicInstrument), - X(IID_IDirectMusicLoader), - X(IID_IDirectMusicLoader8), - X(IID_IDirectMusicObject), - X(IID_IDirectMusicPatternTrack), - X(IID_IDirectMusicPerformance), - X(IID_IDirectMusicPerformance2), - X(IID_IDirectMusicPerformance8), - X(IID_IDirectMusicPort), - X(IID_IDirectMusicPortDownload), - X(IID_IDirectMusicScript), - X(IID_IDirectMusicSegment), - X(IID_IDirectMusicSegment2), - X(IID_IDirectMusicSegment8), - X(IID_IDirectMusicSegmentState), - X(IID_IDirectMusicSegmentState8), - X(IID_IDirectMusicStyle), - X(IID_IDirectMusicStyle8), - X(IID_IDirectMusicSynth), - X(IID_IDirectMusicSynth8), - X(IID_IDirectMusicSynthSink), - X(IID_IDirectMusicThru), - X(IID_IDirectMusicTool), - X(IID_IDirectMusicTool8), - X(IID_IDirectMusicTrack), - X(IID_IDirectMusicTrack8), - X(IID_IUnknown), - X(IID_IPersistStream), - X(IID_IStream), - X(IID_IClassFactory), - /* GUIDs */ - X(GUID_DirectMusicAllTypes), - X(GUID_NOTIFICATION_CHORD), - X(GUID_NOTIFICATION_COMMAND), - X(GUID_NOTIFICATION_MEASUREANDBEAT), - X(GUID_NOTIFICATION_PERFORMANCE), - X(GUID_NOTIFICATION_RECOMPOSE), - X(GUID_NOTIFICATION_SEGMENT), - X(GUID_BandParam), - X(GUID_ChordParam), - X(GUID_CommandParam), - X(GUID_CommandParam2), - X(GUID_CommandParamNext), - X(GUID_IDirectMusicBand), - X(GUID_IDirectMusicChordMap), - X(GUID_IDirectMusicStyle), - X(GUID_MuteParam), - X(GUID_Play_Marker), - X(GUID_RhythmParam), - X(GUID_TempoParam), - X(GUID_TimeSignature), - X(GUID_Valid_Start_Time), - X(GUID_Clear_All_Bands), - X(GUID_ConnectToDLSCollection), - X(GUID_Disable_Auto_Download), - X(GUID_DisableTempo), - X(GUID_DisableTimeSig), - X(GUID_Download), - X(GUID_DownloadToAudioPath), - X(GUID_Enable_Auto_Download), - X(GUID_EnableTempo), - X(GUID_EnableTimeSig), - X(GUID_IgnoreBankSelectForGM), - X(GUID_SeedVariations), - X(GUID_StandardMIDIFile), - X(GUID_Unload), - X(GUID_UnloadFromAudioPath), - X(GUID_Variations), - X(GUID_PerfMasterTempo), - X(GUID_PerfMasterVolume), - X(GUID_PerfMasterGrooveLevel), - X(GUID_PerfAutoDownload), - X(GUID_DefaultGMCollection), - X(GUID_Synth_Default), - X(GUID_Buffer_Reverb), - X(GUID_Buffer_EnvReverb), - X(GUID_Buffer_Stereo), - X(GUID_Buffer_3D_Dry), - X(GUID_Buffer_Mono), - X(GUID_DMUS_PROP_GM_Hardware), - X(GUID_DMUS_PROP_GS_Capable), - X(GUID_DMUS_PROP_GS_Hardware), - X(GUID_DMUS_PROP_DLS1), - X(GUID_DMUS_PROP_DLS2), - X(GUID_DMUS_PROP_Effects), - X(GUID_DMUS_PROP_INSTRUMENT2), - X(GUID_DMUS_PROP_LegacyCaps), - X(GUID_DMUS_PROP_MemorySize), - X(GUID_DMUS_PROP_SampleMemorySize), - X(GUID_DMUS_PROP_SamplePlaybackRate), - X(GUID_DMUS_PROP_SetSynthSink), - X(GUID_DMUS_PROP_SinkUsesDSound), - X(GUID_DMUS_PROP_SynthSink_DSOUND), - X(GUID_DMUS_PROP_SynthSink_WAVE), - X(GUID_DMUS_PROP_Volume), - X(GUID_DMUS_PROP_WavesReverb), - X(GUID_DMUS_PROP_WriteLatency), - X(GUID_DMUS_PROP_WritePeriod), - X(GUID_DMUS_PROP_XG_Capable), - X(GUID_DMUS_PROP_XG_Hardware) - }; -#undef X - - if (!id) - return "(null)"; - - for (i = 0; i < ARRAY_SIZE(guids); i++) - if (IsEqualGUID(id, guids[i].guid)) - return guids[i].name; - - return debugstr_guid(id); -} - -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) -{ - if (!desc || !TRACE_ON(dmfile)) - return; - - TRACE_(dmfile)("DMUS_OBJECTDESC (%p):", desc); - TRACE_(dmfile)(" - dwSize = %lu\n", desc->dwSize); - -#define X(flag) if (desc->dwValidData & flag) TRACE_(dmfile)(#flag " ") - TRACE_(dmfile)(" - dwValidData = %#08lx ( ", desc->dwValidData); - X(DMUS_OBJ_OBJECT); - X(DMUS_OBJ_CLASS); - X(DMUS_OBJ_NAME); - X(DMUS_OBJ_CATEGORY); - X(DMUS_OBJ_FILENAME); - X(DMUS_OBJ_FULLPATH); - X(DMUS_OBJ_URL); - X(DMUS_OBJ_VERSION); - X(DMUS_OBJ_DATE); - X(DMUS_OBJ_LOADED); - X(DMUS_OBJ_MEMORY); - X(DMUS_OBJ_STREAM); - TRACE_(dmfile)(")\n"); -#undef X - - if (desc->dwValidData & DMUS_OBJ_CLASS) - TRACE_(dmfile)(" - guidClass = %s\n", debugstr_dmguid(&desc->guidClass)); - if (desc->dwValidData & DMUS_OBJ_OBJECT) - TRACE_(dmfile)(" - guidObject = %s\n", debugstr_guid(&desc->guidObject)); - - if (desc->dwValidData & DMUS_OBJ_DATE) { - SYSTEMTIME time; - FileTimeToSystemTime(&desc->ftDate, &time); - TRACE_(dmfile)(" - ftDate = \'%04u-%02u-%02u %02u:%02u:%02u\'\n", - time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); - } - if (desc->dwValidData & DMUS_OBJ_VERSION) - TRACE_(dmfile)(" - vVersion = \'%u,%u,%u,%u\'\n", - HIWORD(desc->vVersion.dwVersionMS), LOWORD(desc->vVersion.dwVersionMS), - HIWORD(desc->vVersion.dwVersionLS), LOWORD(desc->vVersion.dwVersionLS)); - if (desc->dwValidData & DMUS_OBJ_NAME) - TRACE_(dmfile)(" - wszName = %s\n", debugstr_w(desc->wszName)); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - TRACE_(dmfile)(" - wszCategory = %s\n", debugstr_w(desc->wszCategory)); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - TRACE_(dmfile)(" - wszFileName = %s\n", debugstr_w(desc->wszFileName)); - if (desc->dwValidData & DMUS_OBJ_MEMORY) - TRACE_(dmfile)(" - llMemLength = 0x%s - pbMemData = %p\n", - wine_dbgstr_longlong(desc->llMemLength), desc->pbMemData); - if (desc->dwValidData & DMUS_OBJ_STREAM) - TRACE_(dmfile)(" - pStream = %p\n", desc->pStream); -} - - -/* RIFF format parsing */ -#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) - -const char *debugstr_chunk(const struct chunk_entry *chunk) -{ - const char *type = ""; - - if (!chunk) - return "(null)"; - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); - return wine_dbg_sprintf("%s chunk, %ssize %lu", debugstr_fourcc(chunk->id), type, chunk->size); -} - -static HRESULT stream_read(IStream *stream, void *data, ULONG size) -{ - ULONG read; - HRESULT hr; - - hr = IStream_Read(stream, data, size, &read); - if (FAILED(hr)) - TRACE_(dmfile)("IStream_Read failed: %#lx\n", hr); - else if (!read && read < size) { - /* All or nothing: Handle a partial read due to end of stream as an error */ - TRACE_(dmfile)("Short read: %lu < %lu\n", read, size); - return E_FAIL; - } - - return hr; -} - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) -{ - static const LARGE_INTEGER zero; - ULONGLONG ck_end = 0, p_end = 0; - HRESULT hr; - - hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); - if (FAILED(hr)) - return hr; - assert(!(chunk->offset.QuadPart & 1)); - if (chunk->parent) { - p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); - if (chunk->offset.QuadPart == p_end) - return S_FALSE; - ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); - if (hr != S_OK) - return hr; - if (chunk->parent) { - ck_end += (chunk->size + 1) & ~1; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { - hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); - if (hr != S_OK) - return hr != S_FALSE ? hr : E_FAIL; - } - - TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); - - return S_OK; -} - -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER end; - - end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; - - return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); -} - -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) -{ - HRESULT hr; - - if (chunk->id) { - hr = stream_skip_chunk(stream, chunk); - if (FAILED(hr)) - return hr; - } - - return stream_get_chunk(stream, chunk); -} - -/* Reads chunk data of the form: - DWORD - size of array element - element[] - Array of elements - The caller needs to heap_free() the array. -*/ -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) -{ - DWORD size; - HRESULT hr; - - *array = NULL; - *count = 0; - - if (chunk->size < sizeof(DWORD)) { - WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); - return E_FAIL; - } - if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) - return hr; - if (size != elem_size) { - WARN_(dmfile)("%s: Array element size mismatch: got %lu, expected %lu\n", - debugstr_chunk(chunk), size, elem_size); - return DMUS_E_UNSUPPORTED_STREAM; - } - - *count = (chunk->size - sizeof(DWORD)) / elem_size; - size = *count * elem_size; - if (!(*array = heap_alloc(size))) - return E_OUTOFMEMORY; - if (FAILED(hr = stream_read(stream, *array, size))) { - heap_free(*array); - *array = NULL; - return hr; - } - - if (chunk->size > size + sizeof(DWORD)) { - WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); - stream_skip_chunk(stream, chunk); - return S_FALSE; - } - return S_OK; -} - -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) -{ - if (chunk->size != size) { - WARN_(dmfile)("Chunk %s (size %lu, offset %s) doesn't contains the expected data size %lu\n", - debugstr_fourcc(chunk->id), chunk->size, - wine_dbgstr_longlong(chunk->offset.QuadPart), size); - return E_FAIL; - } - return stream_read(stream, data, size); -} - -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) -{ - ULONG len; - HRESULT hr; - - hr = IStream_Read(stream, str, min(chunk->size, size), &len); - if (FAILED(hr)) - return hr; - - /* Don't assume the string is properly zero terminated */ - str[min(len, size - 1)] = 0; - - if (len < chunk->size) - return S_FALSE; - return S_OK; -} - - - -/* Generic IDirectMusicObject methods */ -static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IDirectMusicObject_iface); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - - TRACE("(%p/%p)->(%p)\n", iface, This, desc); - - if (!desc) - return E_POINTER; - - memcpy(desc, &This->desc, This->desc.dwSize); - - return S_OK; -} - -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - HRESULT ret = S_OK; - - TRACE("(%p, %p)\n", iface, desc); - - if (!desc) - return E_POINTER; - - /* Immutable property */ - if (desc->dwValidData & DMUS_OBJ_CLASS) - { - desc->dwValidData &= ~DMUS_OBJ_CLASS; - ret = S_FALSE; - } - /* Set only valid fields */ - if (desc->dwValidData & DMUS_OBJ_OBJECT) - This->desc.guidObject = desc->guidObject; - if (desc->dwValidData & DMUS_OBJ_NAME) - lstrcpynW(This->desc.wszName, desc->wszName, DMUS_MAX_NAME); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - lstrcpynW(This->desc.wszCategory, desc->wszCategory, DMUS_MAX_CATEGORY); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - lstrcpynW(This->desc.wszFileName, desc->wszFileName, DMUS_MAX_FILENAME); - if (desc->dwValidData & DMUS_OBJ_VERSION) - This->desc.vVersion = desc->vVersion; - if (desc->dwValidData & DMUS_OBJ_DATE) - This->desc.ftDate = desc->ftDate; - if (desc->dwValidData & DMUS_OBJ_MEMORY) { - This->desc.llMemLength = desc->llMemLength; - memcpy(This->desc.pbMemData, desc->pbMemData, desc->llMemLength); - } - if (desc->dwValidData & DMUS_OBJ_STREAM) - IStream_Clone(desc->pStream, &This->desc.pStream); - - This->desc.dwValidData |= desc->dwValidData; - - return ret; -} - -/* Helper for IDirectMusicObject::ParseDescriptor */ -static inline void info_get_name(IStream *stream, const struct chunk_entry *info, - DMUS_OBJECTDESC *desc) -{ - struct chunk_entry chunk = {.parent = info}; - char name[DMUS_MAX_NAME]; - ULONG len; - HRESULT hr = E_FAIL; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == mmioFOURCC('I','N','A','M')) - hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); - - if (SUCCEEDED(hr)) { - len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); - desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; - desc->dwValidData |= DMUS_OBJ_NAME; - } -} - -static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, - DMUS_OBJECTDESC *desc, BOOL inam) -{ - struct chunk_entry chunk = {.parent = unfo}; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) - if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; -} - -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) -{ - struct chunk_entry chunk = {.parent = riff}; - HRESULT hr; - - TRACE("Looking for %#lx in %p: %s\n", supported, stream, debugstr_chunk(riff)); - - desc->dwValidData = 0; - desc->dwSize = sizeof(*desc); - - while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { - switch (chunk.id) { - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; - break; - case DMUS_FOURCC_DATE_CHUNK: - if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, - &desc->ftDate, sizeof(desc->ftDate)) == S_OK) - desc->dwValidData |= DMUS_OBJ_DATE; - break; - case DMUS_FOURCC_FILE_CHUNK: - if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_FILENAME; - break; - case DMUS_FOURCC_GUID_CHUNK: - if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, - &desc->guidObject, sizeof(desc->guidObject)) == S_OK) - desc->dwValidData |= DMUS_OBJ_OBJECT; - break; - case DMUS_FOURCC_NAME_CHUNK: - if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; - break; - case DMUS_FOURCC_VERSION_CHUNK: - if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, - &desc->vVersion, sizeof(desc->vVersion)) == S_OK) - desc->dwValidData |= DMUS_OBJ_VERSION; - break; - case FOURCC_LIST: - if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) - unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); - else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) - info_get_name(stream, &chunk, desc); - break; - } - } - TRACE("Found %#lx\n", desc->dwValidData); - - return hr; -} - -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) -{ - struct chunk_entry chunk = {.parent = list}; - IDirectMusicGetLoader *getloader; - IDirectMusicLoader *loader; - DMUS_OBJECTDESC desc; - DMUS_IO_REFERENCE reference; - HRESULT hr; - - if (FAILED(hr = stream_next_chunk(stream, &chunk))) - return hr; - if (chunk.id != DMUS_FOURCC_REF_CHUNK) - return DMUS_E_UNSUPPORTED_STREAM; - - if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { - WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); - return hr; - } - TRACE("REFERENCE guidClassID %s, dwValidData %#lx\n", debugstr_dmguid(&reference.guidClassID), - reference.dwValidData); - - if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) - return hr; - desc.guidClass = reference.guidClassID; - desc.dwValidData |= DMUS_OBJ_CLASS; - dump_DMUS_OBJECTDESC(&desc); - - if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) - return hr; - hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); - IDirectMusicGetLoader_Release(getloader); - if (FAILED(hr)) - return hr; - - hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); - IDirectMusicLoader_Release(loader); - - return hr; -} - -/* Generic IPersistStream methods */ -static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IPersistStream_iface); -} - -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - - TRACE("(%p, %p)\n", This, class); - - if (!class) - return E_POINTER; - - *class = This->desc.guidClass; - - return S_OK; -} - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - TRACE("(%p, %p): method not implemented\n", iface, class); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) -{ - TRACE("(%p): method not implemented, always returning S_FALSE\n", iface); - return S_FALSE; -} - -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) -{ - TRACE("(%p, %p, %d): method not implemented\n", iface, stream, clear_dirty); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *size) -{ - TRACE("(%p, %p): method not implemented\n", iface, size); - return E_NOTIMPL; -} - - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) -{ - dmobj->outer_unk = outer_unk; - dmobj->desc.dwSize = sizeof(dmobj->desc); - dmobj->desc.dwValidData = DMUS_OBJ_CLASS; - dmobj->desc.guidClass = *class; -} diff --git a/dlls/dswave/dmobject.h b/dlls/dswave/dmobject.h deleted file mode 100644 index 772be015c80..00000000000 --- a/dlls/dswave/dmobject.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.h - * - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "wine/debug.h" - -/* RIFF stream parsing */ -struct chunk_entry; -struct chunk_entry { - FOURCC id; - DWORD size; - FOURCC type; /* valid only for LIST and RIFF chunks */ - ULARGE_INTEGER offset; /* chunk offset from start of stream */ - const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ -}; - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); - -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size); -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size); -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size); - -static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - offset.QuadPart += sizeof(FOURCC); - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - -static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart; - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - - -/* IDirectMusicObject base object */ -struct dmobject { - IDirectMusicObject IDirectMusicObject_iface; - IPersistStream IPersistStream_iface; - IUnknown *outer_unk; - DMUS_OBJECTDESC desc; -}; - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); - -/* Generic IDirectMusicObject methods */ -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); - -/* Helper for IDirectMusicObject::ParseDescriptor */ -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported); -/* Additional supported flags for dmobj_parsedescriptor. - DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ - -/* 'DMRF' (reference list) helper */ -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj); - -/* Generic IPersistStream methods */ -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class); -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty); -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size); - -/* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk); -const char *debugstr_dmguid(const GUID *id); -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); - -static inline const char *debugstr_fourcc(DWORD fourcc) -{ - if (!fourcc) return "''"; - return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), - (char)(fourcc >> 16), (char)(fourcc >> 24)); -} From 25a69ccbc1ff27058bd5f796f075dec4486282fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 22 Aug 2023 17:43:30 +0200 Subject: [PATCH 0858/1148] dmstyle: Awlays return S_FALSE from DllCanUnloadNow. (cherry picked from commit 994bcb48421c94949eed9f931aeae965dad2a9a9) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmstyle/auditiontrack.c | 2 -- dlls/dmstyle/chordtrack.c | 2 -- dlls/dmstyle/commandtrack.c | 2 -- dlls/dmstyle/dmstyle_main.c | 21 --------------------- dlls/dmstyle/dmstyle_private.h | 7 ------- dlls/dmstyle/motiftrack.c | 2 -- dlls/dmstyle/mutetrack.c | 2 -- dlls/dmstyle/style.c | 2 -- dlls/dmstyle/styletrack.c | 2 -- 9 files changed, 42 deletions(-) diff --git a/dlls/dmstyle/auditiontrack.c b/dlls/dmstyle/auditiontrack.c index 0bf1c818f6c..f324d18cecc 100644 --- a/dlls/dmstyle/auditiontrack.c +++ b/dlls/dmstyle/auditiontrack.c @@ -79,7 +79,6 @@ static ULONG WINAPI audition_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMSTYLE_UnlockModule(); } return ref; @@ -332,7 +331,6 @@ HRESULT create_dmauditiontrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMSTYLE_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmstyle/chordtrack.c b/dlls/dmstyle/chordtrack.c index fdaecac1240..01511963a40 100644 --- a/dlls/dmstyle/chordtrack.c +++ b/dlls/dmstyle/chordtrack.c @@ -82,7 +82,6 @@ static ULONG WINAPI chord_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMSTYLE_UnlockModule(); } return ref; @@ -431,7 +430,6 @@ HRESULT create_dmchordtrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMSTYLE_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmstyle/commandtrack.c b/dlls/dmstyle/commandtrack.c index 32e0bbe8c0d..02b32bc41e6 100644 --- a/dlls/dmstyle/commandtrack.c +++ b/dlls/dmstyle/commandtrack.c @@ -81,7 +81,6 @@ static ULONG WINAPI command_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMSTYLE_UnlockModule(); } return ref; @@ -385,7 +384,6 @@ HRESULT create_dmcommandtrack(REFIID lpcGUID, void **ppobj) track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; list_init (&track->Commands); - DMSTYLE_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmstyle/dmstyle_main.c b/dlls/dmstyle/dmstyle_main.c index 8f951ece5ae..9d96ab3930e 100644 --- a/dlls/dmstyle/dmstyle_main.c +++ b/dlls/dmstyle/dmstyle_main.c @@ -37,8 +37,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmstyle); -LONG DMSTYLE_refCount = 0; - typedef struct { IClassFactory IClassFactory_iface; HRESULT (*fnCreateInstance)(REFIID riid, void **ret_iface); @@ -81,15 +79,11 @@ static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID r static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) { - DMSTYLE_LockModule(); - return 2; /* non-heap based object */ } static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) { - DMSTYLE_UnlockModule(); - return 1; /* non-heap based object */ } @@ -111,12 +105,6 @@ static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock) { TRACE("(%d)\n", dolock); - - if (dolock) - DMSTYLE_LockModule(); - else - DMSTYLE_UnlockModule(); - return S_OK; } @@ -137,15 +125,6 @@ static IClassFactoryImpl MotifTrack_CF = {{&classfactory_vtbl}, create_dmmotiftr static IClassFactoryImpl AuditionTrack_CF = {{&classfactory_vtbl}, create_dmauditiontrack}; static IClassFactoryImpl MuteTrack_CF = {{&classfactory_vtbl}, create_dmmutetrack}; -/****************************************************************** - * DllCanUnloadNow (DMSTYLE.1) - * - * - */ -HRESULT WINAPI DllCanUnloadNow(void) { - return DMSTYLE_refCount != 0 ? S_FALSE : S_OK; -} - /****************************************************************** * DllGetClassObject (DMSTYLE.@) diff --git a/dlls/dmstyle/dmstyle_private.h b/dlls/dmstyle/dmstyle_private.h index 2807e98fe61..5237368b44e 100644 --- a/dlls/dmstyle/dmstyle_private.h +++ b/dlls/dmstyle/dmstyle_private.h @@ -61,13 +61,6 @@ typedef struct _DMUS_PRIVATE_COMMAND { IDirectMusicCollection* ppReferenceCollection; } DMUS_PRIVATE_COMMAND, *LPDMUS_PRIVATE_COMMAND; -/********************************************************************** - * Dll lifetime tracking declaration for dmstyle.dll - */ -extern LONG DMSTYLE_refCount; -static inline void DMSTYLE_LockModule(void) { InterlockedIncrement( &DMSTYLE_refCount ); } -static inline void DMSTYLE_UnlockModule(void) { InterlockedDecrement( &DMSTYLE_refCount ); } - /***************************************************************************** * Misc. */ diff --git a/dlls/dmstyle/motiftrack.c b/dlls/dmstyle/motiftrack.c index 5efe90706e0..93bfd821e56 100644 --- a/dlls/dmstyle/motiftrack.c +++ b/dlls/dmstyle/motiftrack.c @@ -79,7 +79,6 @@ static ULONG WINAPI motif_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMSTYLE_UnlockModule(); } return ref; @@ -304,7 +303,6 @@ HRESULT create_dmmotiftrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMSTYLE_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmstyle/mutetrack.c b/dlls/dmstyle/mutetrack.c index 2248d7151dd..3f632709397 100644 --- a/dlls/dmstyle/mutetrack.c +++ b/dlls/dmstyle/mutetrack.c @@ -79,7 +79,6 @@ static ULONG WINAPI mute_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMSTYLE_UnlockModule(); } return ref; @@ -313,7 +312,6 @@ HRESULT create_dmmutetrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMSTYLE_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmstyle/style.c b/dlls/dmstyle/style.c index ae939f8b738..e2c3f812330 100644 --- a/dlls/dmstyle/style.c +++ b/dlls/dmstyle/style.c @@ -127,7 +127,6 @@ static ULONG WINAPI IDirectMusicStyle8Impl_Release(IDirectMusicStyle8 *iface) heap_free(motif); } heap_free(This); - DMSTYLE_UnlockModule(); } return ref; @@ -991,7 +990,6 @@ HRESULT create_dmstyle(REFIID lpcGUID, void **ppobj) list_init(&obj->bands); list_init(&obj->motifs); - DMSTYLE_LockModule(); hr = IDirectMusicStyle8_QueryInterface(&obj->IDirectMusicStyle8_iface, lpcGUID, ppobj); IDirectMusicStyle8_Release(&obj->IDirectMusicStyle8_iface); diff --git a/dlls/dmstyle/styletrack.c b/dlls/dmstyle/styletrack.c index 83b03807ddf..3618483749c 100644 --- a/dlls/dmstyle/styletrack.c +++ b/dlls/dmstyle/styletrack.c @@ -96,7 +96,6 @@ static ULONG WINAPI style_track_Release(IDirectMusicTrack8 *iface) } heap_free(This); - DMSTYLE_UnlockModule(); } return ref; @@ -403,7 +402,6 @@ HRESULT create_dmstyletrack(REFIID lpcGUID, void **ppobj) track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; list_init (&track->Items); - DMSTYLE_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); From ac6b31e8af906645db1cbe7f9d7517b37118dfd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 09:27:19 +0200 Subject: [PATCH 0859/1148] dmstyle: Use CRT allocation functions. (cherry picked from commit d18ec193de842e80b4ca1ab01cb217f782329675) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmstyle/auditiontrack.c | 11 +++-------- dlls/dmstyle/chordtrack.c | 11 +++-------- dlls/dmstyle/commandtrack.c | 13 ++++--------- dlls/dmstyle/dmobject.c | 7 +++---- dlls/dmstyle/motiftrack.c | 11 +++-------- dlls/dmstyle/mutetrack.c | 11 +++-------- dlls/dmstyle/style.c | 34 +++++++++------------------------- dlls/dmstyle/styletrack.c | 11 +++-------- 8 files changed, 31 insertions(+), 78 deletions(-) diff --git a/dlls/dmstyle/auditiontrack.c b/dlls/dmstyle/auditiontrack.c index f324d18cecc..e7615f53278 100644 --- a/dlls/dmstyle/auditiontrack.c +++ b/dlls/dmstyle/auditiontrack.c @@ -77,9 +77,7 @@ static ULONG WINAPI audition_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -320,11 +318,8 @@ HRESULT create_dmauditiontrack(REFIID lpcGUID, void **ppobj) IDirectMusicAuditionTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicAuditionTrack, diff --git a/dlls/dmstyle/chordtrack.c b/dlls/dmstyle/chordtrack.c index 01511963a40..50eb4e7a5ca 100644 --- a/dlls/dmstyle/chordtrack.c +++ b/dlls/dmstyle/chordtrack.c @@ -80,9 +80,7 @@ static ULONG WINAPI chord_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -419,11 +417,8 @@ HRESULT create_dmchordtrack(REFIID lpcGUID, void **ppobj) IDirectMusicChordTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicChordTrack, diff --git a/dlls/dmstyle/commandtrack.c b/dlls/dmstyle/commandtrack.c index 02b32bc41e6..17bb1e442bc 100644 --- a/dlls/dmstyle/commandtrack.c +++ b/dlls/dmstyle/commandtrack.c @@ -79,9 +79,7 @@ static ULONG WINAPI command_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -302,7 +300,7 @@ static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, IStream *pS nrCommands = chunkSize/dwSizeOfStruct; /* and this is the number of commands */ /* load each command separately in new entry */ for (count = 0; count < nrCommands; count++) { - LPDMUS_PRIVATE_COMMAND pNewCommand = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_COMMAND)); + LPDMUS_PRIVATE_COMMAND pNewCommand = calloc(1, sizeof(*pNewCommand)); IStream_Read (pStm, &pNewCommand->pCommand, dwSizeOfStruct, NULL); list_add_tail (&This->Commands, &pNewCommand->entry); } @@ -372,11 +370,8 @@ HRESULT create_dmcommandtrack(REFIID lpcGUID, void **ppobj) IDirectMusicCommandTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicCommandTrack, diff --git a/dlls/dmstyle/dmobject.c b/dlls/dmstyle/dmobject.c index b526b23d031..07d887a376c 100644 --- a/dlls/dmstyle/dmobject.c +++ b/dlls/dmstyle/dmobject.c @@ -28,7 +28,6 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -375,7 +374,7 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) /* Reads chunk data of the form: DWORD - size of array element element[] - Array of elements - The caller needs to heap_free() the array. + The caller needs to free() the array. */ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, unsigned int *count, DWORD elem_size) @@ -400,10 +399,10 @@ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, *count = (chunk->size - sizeof(DWORD)) / elem_size; size = *count * elem_size; - if (!(*array = heap_alloc(size))) + if (!(*array = malloc(size))) return E_OUTOFMEMORY; if (FAILED(hr = stream_read(stream, *array, size))) { - heap_free(*array); + free(*array); *array = NULL; return hr; } diff --git a/dlls/dmstyle/motiftrack.c b/dlls/dmstyle/motiftrack.c index 93bfd821e56..9788a92e0e1 100644 --- a/dlls/dmstyle/motiftrack.c +++ b/dlls/dmstyle/motiftrack.c @@ -77,9 +77,7 @@ static ULONG WINAPI motif_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -292,11 +290,8 @@ HRESULT create_dmmotiftrack(REFIID lpcGUID, void **ppobj) IDirectMusicMotifTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicMotifTrack, diff --git a/dlls/dmstyle/mutetrack.c b/dlls/dmstyle/mutetrack.c index 3f632709397..d6b28a16c72 100644 --- a/dlls/dmstyle/mutetrack.c +++ b/dlls/dmstyle/mutetrack.c @@ -77,9 +77,7 @@ static ULONG WINAPI mute_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -301,11 +299,8 @@ HRESULT create_dmmutetrack(REFIID lpcGUID, void **ppobj) IDirectMusicMuteTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicMuteTrack, diff --git a/dlls/dmstyle/style.c b/dlls/dmstyle/style.c index e2c3f812330..781c371e623 100644 --- a/dlls/dmstyle/style.c +++ b/dlls/dmstyle/style.c @@ -20,7 +20,6 @@ #include "dmstyle_private.h" #include "dmobject.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmstyle); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -116,17 +115,17 @@ static ULONG WINAPI IDirectMusicStyle8Impl_Release(IDirectMusicStyle8 *iface) list_remove(&band->entry); if (band->pBand) IDirectMusicBand_Release(band->pBand); - heap_free(band); + free(band); } LIST_FOR_EACH_ENTRY_SAFE(motif, motif2, &This->motifs, struct style_motif, entry) { list_remove(&motif->entry); LIST_FOR_EACH_ENTRY_SAFE(item, item2, &motif->Items, struct style_partref_item, entry) { list_remove(&item->entry); - heap_free(item); + free(item); } - heap_free(motif); + free(motif); } - heap_free(This); + free(This); } return ref; @@ -405,11 +404,7 @@ static HRESULT parse_part_ref_list(DMUS_PRIVATE_CHUNK *pChunk, IStream *pStm, switch (Chunk.fccID) { case DMUS_FOURCC_PARTREF_CHUNK: { TRACE_(dmfile)(": PartRef chunk\n"); - pNewItem = heap_alloc_zero(sizeof(*pNewItem)); - if (!pNewItem) { - ERR(": no more memory\n"); - return E_OUTOFMEMORY; - } + if (!(pNewItem = calloc(1, sizeof(*pNewItem)))) return E_OUTOFMEMORY; hr = IStream_Read (pStm, &pNewItem->part_ref, sizeof(DMUS_IO_PARTREF), NULL); /*TRACE_(dmfile)(" - sizeof %lu\n", sizeof(DMUS_IO_PARTREF));*/ list_add_tail (&pNewMotif->Items, &pNewItem->entry); @@ -630,11 +625,7 @@ static HRESULT parse_pattern_list(IDirectMusicStyle8Impl *This, DMUS_PRIVATE_CHU case DMUS_FOURCC_PATTERN_CHUNK: { TRACE_(dmfile)(": Pattern chunk\n"); /** alloc new motif entry */ - pNewMotif = heap_alloc_zero(sizeof(*pNewMotif)); - if (NULL == pNewMotif) { - ERR(": no more memory\n"); - return E_OUTOFMEMORY; - } + if (!(pNewMotif = calloc(1, sizeof(*pNewMotif)))) return E_OUTOFMEMORY; list_add_tail(&This->motifs, &pNewMotif->entry); IStream_Read (pStm, &pNewMotif->pattern, Chunk.dwSize, NULL); @@ -826,11 +817,7 @@ static HRESULT parse_style_form(IDirectMusicStyle8Impl *This, DMUS_PRIVATE_CHUNK return hr; } - pNewBand = heap_alloc_zero(sizeof(*pNewBand)); - if (NULL == pNewBand) { - ERR(": no more memory\n"); - return E_OUTOFMEMORY; - } + if (!(pNewBand = calloc(1, sizeof(*pNewBand)))) return E_OUTOFMEMORY; pNewBand->pBand = pBand; IDirectMusicBand_AddRef(pBand); list_add_tail(&This->bands, &pNewBand->entry); @@ -977,11 +964,8 @@ HRESULT create_dmstyle(REFIID lpcGUID, void **ppobj) IDirectMusicStyle8Impl* obj; HRESULT hr; - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicStyle8Impl)); - if (NULL == obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicStyle8_iface.lpVtbl = &dmstyle8_vtbl; obj->ref = 1; dmobject_init(&obj->dmobj, &CLSID_DirectMusicStyle, (IUnknown *)&obj->IDirectMusicStyle8_iface); diff --git a/dlls/dmstyle/styletrack.c b/dlls/dmstyle/styletrack.c index 3618483749c..700b8a1ea1a 100644 --- a/dlls/dmstyle/styletrack.c +++ b/dlls/dmstyle/styletrack.c @@ -20,8 +20,6 @@ #include "dmstyle_private.h" #include "dmobject.h" -#include "wine/heap.h" - WINE_DEFAULT_DEBUG_CHANNEL(dmstyle); /***************************************************************************** @@ -95,7 +93,7 @@ static ULONG WINAPI style_track_Release(IDirectMusicTrack8 *iface) free(item); } - heap_free(This); + free(This); } return ref; @@ -390,11 +388,8 @@ HRESULT create_dmstyletrack(REFIID lpcGUID, void **ppobj) IDirectMusicStyleTrack *track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicStyleTrack, From 6b8a8a60aa7321d4cd66f87c6d5dd77ebbdf61c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 30 Aug 2023 19:53:42 +0200 Subject: [PATCH 0860/1148] dmstyle: Use PARENTSRC with dmusic. (cherry picked from commit 31980e74501b39490d20782bd36693ae2370668f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmstyle/Makefile.in | 1 + dlls/dmstyle/dmobject.c | 732 --------------------------------------- dlls/dmstyle/dmobject.h | 124 ------- 3 files changed, 1 insertion(+), 856 deletions(-) delete mode 100644 dlls/dmstyle/dmobject.c delete mode 100644 dlls/dmstyle/dmobject.h diff --git a/dlls/dmstyle/Makefile.in b/dlls/dmstyle/Makefile.in index a95a524d473..8a6897ca4ca 100644 --- a/dlls/dmstyle/Makefile.in +++ b/dlls/dmstyle/Makefile.in @@ -1,5 +1,6 @@ MODULE = dmstyle.dll IMPORTS = dxguid uuid ole32 advapi32 +PARENTSRC = ../dmusic C_SRCS = \ auditiontrack.c \ diff --git a/dlls/dmstyle/dmobject.c b/dlls/dmstyle/dmobject.c deleted file mode 100644 index 07d887a376c..00000000000 --- a/dlls/dmstyle/dmobject.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.c - * - * Copyright (C) 2003-2004 Rok Mandeljc - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#define COBJMACROS -#include -#include "objbase.h" -#include "dmusici.h" -#include "dmusicf.h" -#include "dmusics.h" -#include "dmobject.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(dmobj); -WINE_DECLARE_DEBUG_CHANNEL(dmfile); - -/* Debugging helpers */ -const char *debugstr_dmguid(const GUID *id) { - unsigned int i; -#define X(guid) { &guid, #guid } - static const struct { - const GUID *guid; - const char *name; - } guids[] = { - /* CLSIDs */ - X(CLSID_AudioVBScript), - X(CLSID_DirectMusic), - X(CLSID_DirectMusicAudioPathConfig), - X(CLSID_DirectMusicAuditionTrack), - X(CLSID_DirectMusicBand), - X(CLSID_DirectMusicBandTrack), - X(CLSID_DirectMusicChordMapTrack), - X(CLSID_DirectMusicChordMap), - X(CLSID_DirectMusicChordTrack), - X(CLSID_DirectMusicCollection), - X(CLSID_DirectMusicCommandTrack), - X(CLSID_DirectMusicComposer), - X(CLSID_DirectMusicContainer), - X(CLSID_DirectMusicGraph), - X(CLSID_DirectMusicLoader), - X(CLSID_DirectMusicLyricsTrack), - X(CLSID_DirectMusicMarkerTrack), - X(CLSID_DirectMusicMelodyFormulationTrack), - X(CLSID_DirectMusicMotifTrack), - X(CLSID_DirectMusicMuteTrack), - X(CLSID_DirectMusicParamControlTrack), - X(CLSID_DirectMusicPatternTrack), - X(CLSID_DirectMusicPerformance), - X(CLSID_DirectMusicScript), - X(CLSID_DirectMusicScriptAutoImpSegment), - X(CLSID_DirectMusicScriptAutoImpPerformance), - X(CLSID_DirectMusicScriptAutoImpSegmentState), - X(CLSID_DirectMusicScriptAutoImpAudioPathConfig), - X(CLSID_DirectMusicScriptAutoImpAudioPath), - X(CLSID_DirectMusicScriptAutoImpSong), - X(CLSID_DirectMusicScriptSourceCodeLoader), - X(CLSID_DirectMusicScriptTrack), - X(CLSID_DirectMusicSection), - X(CLSID_DirectMusicSegment), - X(CLSID_DirectMusicSegmentState), - X(CLSID_DirectMusicSegmentTriggerTrack), - X(CLSID_DirectMusicSegTriggerTrack), - X(CLSID_DirectMusicSeqTrack), - X(CLSID_DirectMusicSignPostTrack), - X(CLSID_DirectMusicSong), - X(CLSID_DirectMusicStyle), - X(CLSID_DirectMusicStyleTrack), - X(CLSID_DirectMusicSynth), - X(CLSID_DirectMusicSynthSink), - X(CLSID_DirectMusicSysExTrack), - X(CLSID_DirectMusicTemplate), - X(CLSID_DirectMusicTempoTrack), - X(CLSID_DirectMusicTimeSigTrack), - X(CLSID_DirectMusicWaveTrack), - X(CLSID_DirectSoundWave), - /* IIDs */ - X(IID_IDirectMusic), - X(IID_IDirectMusic2), - X(IID_IDirectMusic8), - X(IID_IDirectMusicAudioPath), - X(IID_IDirectMusicBand), - X(IID_IDirectMusicBuffer), - X(IID_IDirectMusicChordMap), - X(IID_IDirectMusicCollection), - X(IID_IDirectMusicComposer), - X(IID_IDirectMusicContainer), - X(IID_IDirectMusicDownload), - X(IID_IDirectMusicDownloadedInstrument), - X(IID_IDirectMusicGetLoader), - X(IID_IDirectMusicGraph), - X(IID_IDirectMusicInstrument), - X(IID_IDirectMusicLoader), - X(IID_IDirectMusicLoader8), - X(IID_IDirectMusicObject), - X(IID_IDirectMusicPatternTrack), - X(IID_IDirectMusicPerformance), - X(IID_IDirectMusicPerformance2), - X(IID_IDirectMusicPerformance8), - X(IID_IDirectMusicPort), - X(IID_IDirectMusicPortDownload), - X(IID_IDirectMusicScript), - X(IID_IDirectMusicSegment), - X(IID_IDirectMusicSegment2), - X(IID_IDirectMusicSegment8), - X(IID_IDirectMusicSegmentState), - X(IID_IDirectMusicSegmentState8), - X(IID_IDirectMusicStyle), - X(IID_IDirectMusicStyle8), - X(IID_IDirectMusicSynth), - X(IID_IDirectMusicSynth8), - X(IID_IDirectMusicSynthSink), - X(IID_IDirectMusicThru), - X(IID_IDirectMusicTool), - X(IID_IDirectMusicTool8), - X(IID_IDirectMusicTrack), - X(IID_IDirectMusicTrack8), - X(IID_IUnknown), - X(IID_IPersistStream), - X(IID_IStream), - X(IID_IClassFactory), - /* GUIDs */ - X(GUID_DirectMusicAllTypes), - X(GUID_NOTIFICATION_CHORD), - X(GUID_NOTIFICATION_COMMAND), - X(GUID_NOTIFICATION_MEASUREANDBEAT), - X(GUID_NOTIFICATION_PERFORMANCE), - X(GUID_NOTIFICATION_RECOMPOSE), - X(GUID_NOTIFICATION_SEGMENT), - X(GUID_BandParam), - X(GUID_ChordParam), - X(GUID_CommandParam), - X(GUID_CommandParam2), - X(GUID_CommandParamNext), - X(GUID_IDirectMusicBand), - X(GUID_IDirectMusicChordMap), - X(GUID_IDirectMusicStyle), - X(GUID_MuteParam), - X(GUID_Play_Marker), - X(GUID_RhythmParam), - X(GUID_TempoParam), - X(GUID_TimeSignature), - X(GUID_Valid_Start_Time), - X(GUID_Clear_All_Bands), - X(GUID_ConnectToDLSCollection), - X(GUID_Disable_Auto_Download), - X(GUID_DisableTempo), - X(GUID_DisableTimeSig), - X(GUID_Download), - X(GUID_DownloadToAudioPath), - X(GUID_Enable_Auto_Download), - X(GUID_EnableTempo), - X(GUID_EnableTimeSig), - X(GUID_IgnoreBankSelectForGM), - X(GUID_SeedVariations), - X(GUID_StandardMIDIFile), - X(GUID_Unload), - X(GUID_UnloadFromAudioPath), - X(GUID_Variations), - X(GUID_PerfMasterTempo), - X(GUID_PerfMasterVolume), - X(GUID_PerfMasterGrooveLevel), - X(GUID_PerfAutoDownload), - X(GUID_DefaultGMCollection), - X(GUID_Synth_Default), - X(GUID_Buffer_Reverb), - X(GUID_Buffer_EnvReverb), - X(GUID_Buffer_Stereo), - X(GUID_Buffer_3D_Dry), - X(GUID_Buffer_Mono), - X(GUID_DMUS_PROP_GM_Hardware), - X(GUID_DMUS_PROP_GS_Capable), - X(GUID_DMUS_PROP_GS_Hardware), - X(GUID_DMUS_PROP_DLS1), - X(GUID_DMUS_PROP_DLS2), - X(GUID_DMUS_PROP_Effects), - X(GUID_DMUS_PROP_INSTRUMENT2), - X(GUID_DMUS_PROP_LegacyCaps), - X(GUID_DMUS_PROP_MemorySize), - X(GUID_DMUS_PROP_SampleMemorySize), - X(GUID_DMUS_PROP_SamplePlaybackRate), - X(GUID_DMUS_PROP_SetSynthSink), - X(GUID_DMUS_PROP_SinkUsesDSound), - X(GUID_DMUS_PROP_SynthSink_DSOUND), - X(GUID_DMUS_PROP_SynthSink_WAVE), - X(GUID_DMUS_PROP_Volume), - X(GUID_DMUS_PROP_WavesReverb), - X(GUID_DMUS_PROP_WriteLatency), - X(GUID_DMUS_PROP_WritePeriod), - X(GUID_DMUS_PROP_XG_Capable), - X(GUID_DMUS_PROP_XG_Hardware) - }; -#undef X - - if (!id) - return "(null)"; - - for (i = 0; i < ARRAY_SIZE(guids); i++) - if (IsEqualGUID(id, guids[i].guid)) - return guids[i].name; - - return debugstr_guid(id); -} - -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) -{ - if (!desc || !TRACE_ON(dmfile)) - return; - - TRACE_(dmfile)("DMUS_OBJECTDESC (%p):", desc); - TRACE_(dmfile)(" - dwSize = %lu\n", desc->dwSize); - -#define X(flag) if (desc->dwValidData & flag) TRACE_(dmfile)(#flag " ") - TRACE_(dmfile)(" - dwValidData = %#08lx ( ", desc->dwValidData); - X(DMUS_OBJ_OBJECT); - X(DMUS_OBJ_CLASS); - X(DMUS_OBJ_NAME); - X(DMUS_OBJ_CATEGORY); - X(DMUS_OBJ_FILENAME); - X(DMUS_OBJ_FULLPATH); - X(DMUS_OBJ_URL); - X(DMUS_OBJ_VERSION); - X(DMUS_OBJ_DATE); - X(DMUS_OBJ_LOADED); - X(DMUS_OBJ_MEMORY); - X(DMUS_OBJ_STREAM); - TRACE_(dmfile)(")\n"); -#undef X - - if (desc->dwValidData & DMUS_OBJ_CLASS) - TRACE_(dmfile)(" - guidClass = %s\n", debugstr_dmguid(&desc->guidClass)); - if (desc->dwValidData & DMUS_OBJ_OBJECT) - TRACE_(dmfile)(" - guidObject = %s\n", debugstr_guid(&desc->guidObject)); - - if (desc->dwValidData & DMUS_OBJ_DATE) { - SYSTEMTIME time; - FileTimeToSystemTime(&desc->ftDate, &time); - TRACE_(dmfile)(" - ftDate = \'%04u-%02u-%02u %02u:%02u:%02u\'\n", - time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); - } - if (desc->dwValidData & DMUS_OBJ_VERSION) - TRACE_(dmfile)(" - vVersion = \'%u,%u,%u,%u\'\n", - HIWORD(desc->vVersion.dwVersionMS), LOWORD(desc->vVersion.dwVersionMS), - HIWORD(desc->vVersion.dwVersionLS), LOWORD(desc->vVersion.dwVersionLS)); - if (desc->dwValidData & DMUS_OBJ_NAME) - TRACE_(dmfile)(" - wszName = %s\n", debugstr_w(desc->wszName)); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - TRACE_(dmfile)(" - wszCategory = %s\n", debugstr_w(desc->wszCategory)); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - TRACE_(dmfile)(" - wszFileName = %s\n", debugstr_w(desc->wszFileName)); - if (desc->dwValidData & DMUS_OBJ_MEMORY) - TRACE_(dmfile)(" - llMemLength = 0x%s - pbMemData = %p\n", - wine_dbgstr_longlong(desc->llMemLength), desc->pbMemData); - if (desc->dwValidData & DMUS_OBJ_STREAM) - TRACE_(dmfile)(" - pStream = %p\n", desc->pStream); -} - - -/* RIFF format parsing */ -#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) - -const char *debugstr_chunk(const struct chunk_entry *chunk) -{ - const char *type = ""; - - if (!chunk) - return "(null)"; - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); - return wine_dbg_sprintf("%s chunk, %ssize %lu", debugstr_fourcc(chunk->id), type, chunk->size); -} - -static HRESULT stream_read(IStream *stream, void *data, ULONG size) -{ - ULONG read; - HRESULT hr; - - hr = IStream_Read(stream, data, size, &read); - if (FAILED(hr)) - TRACE_(dmfile)("IStream_Read failed: %#lx\n", hr); - else if (!read && read < size) { - /* All or nothing: Handle a partial read due to end of stream as an error */ - TRACE_(dmfile)("Short read: %lu < %lu\n", read, size); - return E_FAIL; - } - - return hr; -} - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) -{ - static const LARGE_INTEGER zero; - ULONGLONG ck_end = 0, p_end = 0; - HRESULT hr; - - hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); - if (FAILED(hr)) - return hr; - assert(!(chunk->offset.QuadPart & 1)); - if (chunk->parent) { - p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); - if (chunk->offset.QuadPart == p_end) - return S_FALSE; - ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); - if (hr != S_OK) - return hr; - if (chunk->parent) { - ck_end += (chunk->size + 1) & ~1; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { - hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); - if (hr != S_OK) - return hr != S_FALSE ? hr : E_FAIL; - } - - TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); - - return S_OK; -} - -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER end; - - end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; - - return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); -} - -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) -{ - HRESULT hr; - - if (chunk->id) { - hr = stream_skip_chunk(stream, chunk); - if (FAILED(hr)) - return hr; - } - - return stream_get_chunk(stream, chunk); -} - -/* Reads chunk data of the form: - DWORD - size of array element - element[] - Array of elements - The caller needs to free() the array. -*/ -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) -{ - DWORD size; - HRESULT hr; - - *array = NULL; - *count = 0; - - if (chunk->size < sizeof(DWORD)) { - WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); - return E_FAIL; - } - if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) - return hr; - if (size != elem_size) { - WARN_(dmfile)("%s: Array element size mismatch: got %lu, expected %lu\n", - debugstr_chunk(chunk), size, elem_size); - return DMUS_E_UNSUPPORTED_STREAM; - } - - *count = (chunk->size - sizeof(DWORD)) / elem_size; - size = *count * elem_size; - if (!(*array = malloc(size))) - return E_OUTOFMEMORY; - if (FAILED(hr = stream_read(stream, *array, size))) { - free(*array); - *array = NULL; - return hr; - } - - if (chunk->size > size + sizeof(DWORD)) { - WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); - stream_skip_chunk(stream, chunk); - return S_FALSE; - } - return S_OK; -} - -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) -{ - if (chunk->size != size) { - WARN_(dmfile)("Chunk %s (size %lu, offset %s) doesn't contains the expected data size %lu\n", - debugstr_fourcc(chunk->id), chunk->size, - wine_dbgstr_longlong(chunk->offset.QuadPart), size); - return E_FAIL; - } - return stream_read(stream, data, size); -} - -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) -{ - ULONG len; - HRESULT hr; - - hr = IStream_Read(stream, str, min(chunk->size, size), &len); - if (FAILED(hr)) - return hr; - - /* Don't assume the string is properly zero terminated */ - str[min(len, size - 1)] = 0; - - if (len < chunk->size) - return S_FALSE; - return S_OK; -} - - - -/* Generic IDirectMusicObject methods */ -static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IDirectMusicObject_iface); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - - TRACE("(%p/%p)->(%p)\n", iface, This, desc); - - if (!desc) - return E_POINTER; - - memcpy(desc, &This->desc, This->desc.dwSize); - - return S_OK; -} - -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - HRESULT ret = S_OK; - - TRACE("(%p, %p)\n", iface, desc); - - if (!desc) - return E_POINTER; - - /* Immutable property */ - if (desc->dwValidData & DMUS_OBJ_CLASS) - { - desc->dwValidData &= ~DMUS_OBJ_CLASS; - ret = S_FALSE; - } - /* Set only valid fields */ - if (desc->dwValidData & DMUS_OBJ_OBJECT) - This->desc.guidObject = desc->guidObject; - if (desc->dwValidData & DMUS_OBJ_NAME) - lstrcpynW(This->desc.wszName, desc->wszName, DMUS_MAX_NAME); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - lstrcpynW(This->desc.wszCategory, desc->wszCategory, DMUS_MAX_CATEGORY); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - lstrcpynW(This->desc.wszFileName, desc->wszFileName, DMUS_MAX_FILENAME); - if (desc->dwValidData & DMUS_OBJ_VERSION) - This->desc.vVersion = desc->vVersion; - if (desc->dwValidData & DMUS_OBJ_DATE) - This->desc.ftDate = desc->ftDate; - if (desc->dwValidData & DMUS_OBJ_MEMORY) { - This->desc.llMemLength = desc->llMemLength; - memcpy(This->desc.pbMemData, desc->pbMemData, desc->llMemLength); - } - if (desc->dwValidData & DMUS_OBJ_STREAM) - IStream_Clone(desc->pStream, &This->desc.pStream); - - This->desc.dwValidData |= desc->dwValidData; - - return ret; -} - -/* Helper for IDirectMusicObject::ParseDescriptor */ -static inline void info_get_name(IStream *stream, const struct chunk_entry *info, - DMUS_OBJECTDESC *desc) -{ - struct chunk_entry chunk = {.parent = info}; - char name[DMUS_MAX_NAME]; - ULONG len; - HRESULT hr = E_FAIL; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == mmioFOURCC('I','N','A','M')) - hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); - - if (SUCCEEDED(hr)) { - len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); - desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; - desc->dwValidData |= DMUS_OBJ_NAME; - } -} - -static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, - DMUS_OBJECTDESC *desc, BOOL inam) -{ - struct chunk_entry chunk = {.parent = unfo}; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) - if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; -} - -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) -{ - struct chunk_entry chunk = {.parent = riff}; - HRESULT hr; - - TRACE("Looking for %#lx in %p: %s\n", supported, stream, debugstr_chunk(riff)); - - desc->dwValidData = 0; - desc->dwSize = sizeof(*desc); - - while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { - switch (chunk.id) { - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; - break; - case DMUS_FOURCC_DATE_CHUNK: - if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, - &desc->ftDate, sizeof(desc->ftDate)) == S_OK) - desc->dwValidData |= DMUS_OBJ_DATE; - break; - case DMUS_FOURCC_FILE_CHUNK: - if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_FILENAME; - break; - case DMUS_FOURCC_GUID_CHUNK: - if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, - &desc->guidObject, sizeof(desc->guidObject)) == S_OK) - desc->dwValidData |= DMUS_OBJ_OBJECT; - break; - case DMUS_FOURCC_NAME_CHUNK: - if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; - break; - case DMUS_FOURCC_VERSION_CHUNK: - if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, - &desc->vVersion, sizeof(desc->vVersion)) == S_OK) - desc->dwValidData |= DMUS_OBJ_VERSION; - break; - case FOURCC_LIST: - if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) - unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); - else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) - info_get_name(stream, &chunk, desc); - break; - } - } - TRACE("Found %#lx\n", desc->dwValidData); - - return hr; -} - -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) -{ - struct chunk_entry chunk = {.parent = list}; - IDirectMusicGetLoader *getloader; - IDirectMusicLoader *loader; - DMUS_OBJECTDESC desc; - DMUS_IO_REFERENCE reference; - HRESULT hr; - - if (FAILED(hr = stream_next_chunk(stream, &chunk))) - return hr; - if (chunk.id != DMUS_FOURCC_REF_CHUNK) - return DMUS_E_UNSUPPORTED_STREAM; - - if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { - WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); - return hr; - } - TRACE("REFERENCE guidClassID %s, dwValidData %#lx\n", debugstr_dmguid(&reference.guidClassID), - reference.dwValidData); - - if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) - return hr; - desc.guidClass = reference.guidClassID; - desc.dwValidData |= DMUS_OBJ_CLASS; - dump_DMUS_OBJECTDESC(&desc); - - if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) - return hr; - hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); - IDirectMusicGetLoader_Release(getloader); - if (FAILED(hr)) - return hr; - - hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); - IDirectMusicLoader_Release(loader); - - return hr; -} - -/* Generic IPersistStream methods */ -static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IPersistStream_iface); -} - -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - - TRACE("(%p, %p)\n", This, class); - - if (!class) - return E_POINTER; - - *class = This->desc.guidClass; - - return S_OK; -} - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - TRACE("(%p, %p): method not implemented\n", iface, class); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) -{ - TRACE("(%p): method not implemented, always returning S_FALSE\n", iface); - return S_FALSE; -} - -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) -{ - TRACE("(%p, %p, %d): method not implemented\n", iface, stream, clear_dirty); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *size) -{ - TRACE("(%p, %p): method not implemented\n", iface, size); - return E_NOTIMPL; -} - - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) -{ - dmobj->outer_unk = outer_unk; - dmobj->desc.dwSize = sizeof(dmobj->desc); - dmobj->desc.dwValidData = DMUS_OBJ_CLASS; - dmobj->desc.guidClass = *class; -} diff --git a/dlls/dmstyle/dmobject.h b/dlls/dmstyle/dmobject.h deleted file mode 100644 index 772be015c80..00000000000 --- a/dlls/dmstyle/dmobject.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.h - * - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "wine/debug.h" - -/* RIFF stream parsing */ -struct chunk_entry; -struct chunk_entry { - FOURCC id; - DWORD size; - FOURCC type; /* valid only for LIST and RIFF chunks */ - ULARGE_INTEGER offset; /* chunk offset from start of stream */ - const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ -}; - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); - -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size); -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size); -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size); - -static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - offset.QuadPart += sizeof(FOURCC); - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - -static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart; - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - - -/* IDirectMusicObject base object */ -struct dmobject { - IDirectMusicObject IDirectMusicObject_iface; - IPersistStream IPersistStream_iface; - IUnknown *outer_unk; - DMUS_OBJECTDESC desc; -}; - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); - -/* Generic IDirectMusicObject methods */ -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); - -/* Helper for IDirectMusicObject::ParseDescriptor */ -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported); -/* Additional supported flags for dmobj_parsedescriptor. - DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ - -/* 'DMRF' (reference list) helper */ -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj); - -/* Generic IPersistStream methods */ -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class); -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty); -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size); - -/* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk); -const char *debugstr_dmguid(const GUID *id); -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); - -static inline const char *debugstr_fourcc(DWORD fourcc) -{ - if (!fourcc) return "''"; - return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), - (char)(fourcc >> 16), (char)(fourcc >> 24)); -} From b983e6c43f70e010b4caa65b42090b2d24672f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 22:18:52 +0200 Subject: [PATCH 0861/1148] dmime: Remove FIXME from performance IDirectMusicGraph methods. (cherry picked from commit 4cb46f1bed7a08838b70626b5dd2248a9f81cb67) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 1f8de0837d5..fee3590c19f 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1301,21 +1301,21 @@ static HRESULT WINAPI performance_graph_InsertTool(IDirectMusicGraph *iface, IDi DWORD *channels, DWORD channels_count, LONG index) { struct performance *This = impl_from_IDirectMusicGraph(iface); - FIXME("(%p, %p, %p, %lu, %ld): stub\n", This, tool, channels, channels_count, index); + TRACE("(%p, %p, %p, %lu, %ld)\n", This, tool, channels, channels_count, index); return E_NOTIMPL; } static HRESULT WINAPI performance_graph_GetTool(IDirectMusicGraph *iface, DWORD index, IDirectMusicTool **tool) { struct performance *This = impl_from_IDirectMusicGraph(iface); - FIXME("(%p, %lu, %p): stub\n", This, index, tool); + TRACE("(%p, %lu, %p)\n", This, index, tool); return E_NOTIMPL; } static HRESULT WINAPI performance_graph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool *tool) { struct performance *This = impl_from_IDirectMusicGraph(iface); - FIXME("(%p, %p): stub\n", This, tool); + TRACE("(%p, %p)\n", This, tool); return E_NOTIMPL; } From 13fea6ebab1838abc8d442b77ef3507798a84b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 22:11:54 +0200 Subject: [PATCH 0862/1148] dmime: Implement some performance IDirectMusicTool methods. (cherry picked from commit 1e70dbcfc2630bc6e7d8b7cdc297c345269c6aec) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 14 ++++++++------ dlls/dmime/tests/dmime.c | 8 ++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index fee3590c19f..4085f70cc77 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1356,28 +1356,30 @@ static ULONG WINAPI performance_tool_Release(IDirectMusicTool *iface) static HRESULT WINAPI performance_tool_Init(IDirectMusicTool *iface, IDirectMusicGraph *graph) { struct performance *This = impl_from_IDirectMusicTool(iface); - FIXME("(%p, %p): stub\n", This, graph); + TRACE("(%p, %p)\n", This, graph); return E_NOTIMPL; } static HRESULT WINAPI performance_tool_GetMsgDeliveryType(IDirectMusicTool *iface, DWORD *type) { struct performance *This = impl_from_IDirectMusicTool(iface); - FIXME("(%p, %p): stub\n", This, type); - return E_NOTIMPL; + TRACE("(%p, %p)\n", This, type); + *type = DMUS_PMSGF_TOOL_IMMEDIATE; + return S_OK; } static HRESULT WINAPI performance_tool_GetMediaTypeArraySize(IDirectMusicTool *iface, DWORD *size) { struct performance *This = impl_from_IDirectMusicTool(iface); - FIXME("(%p, %p): stub\n", This, size); - return E_NOTIMPL; + TRACE("(%p, %p)\n", This, size); + *size = 0; + return S_OK; } static HRESULT WINAPI performance_tool_GetMediaTypes(IDirectMusicTool *iface, DWORD **types, DWORD size) { struct performance *This = impl_from_IDirectMusicTool(iface); - FIXME("(%p, %p, %lu): stub\n", This, types, size); + TRACE("(%p, %p, %lu)\n", This, types, size); return E_NOTIMPL; } diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 0aeed24b200..3d9eb188fed 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1497,12 +1497,12 @@ static void test_performance_tool(void) ok(hr == E_NOTIMPL, "got %#lx\n", hr); value = 0xdeadbeef; hr = IDirectMusicTool_GetMsgDeliveryType(tool, &value); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(value == DMUS_PMSGF_TOOL_IMMEDIATE, "got %#lx\n", value); + ok(hr == S_OK, "got %#lx\n", hr); + ok(value == DMUS_PMSGF_TOOL_IMMEDIATE, "got %#lx\n", value); value = 0xdeadbeef; hr = IDirectMusicTool_GetMediaTypeArraySize(tool, &value); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(value == 0, "got %#lx\n", value); + ok(hr == S_OK, "got %#lx\n", hr); + ok(value == 0, "got %#lx\n", value); hr = IDirectMusicTool_GetMediaTypes(tool, (DWORD **)&types, 64); ok(hr == E_NOTIMPL, "got %#lx\n", hr); hr = IDirectMusicTool_ProcessPMsg(tool, performance, &msg); From db3ce2eef48a3dd488068726417f10a429810317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 22:27:39 +0200 Subject: [PATCH 0863/1148] dmime: Rewrite IDirectMusicGraph tools iteration. (cherry picked from commit d8bce981d7f320987f32f57168614245314932d1) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 6 -- dlls/dmime/graph.c | 118 ++++++++++++++++++++----------------- dlls/dmime/tests/dmime.c | 22 +++---- 3 files changed, 76 insertions(+), 70 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 6102fd5ec2f..28b0afc9af8 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -86,12 +86,6 @@ typedef struct _DMUS_PRIVATE_TEMPO_ITEM { DMUS_IO_TEMPO_ITEM item; } DMUS_PRIVATE_TEMPO_ITEM, *LPDMUS_PRIVATE_TEMPO_ITEM; -typedef struct _DMUS_PRIVATE_GRAPH_TOOL { - struct list entry; /* for listing elements */ - DWORD dwIndex; - IDirectMusicTool* pTool; -} DMUS_PRIVATE_GRAPH_TOOL, *LPDMUS_PRIVATE_GRAPH_TOOL; - typedef struct _DMUS_PRIVATE_TEMPO_PLAY_STATE { DWORD dummy; } DMUS_PRIVATE_TEMPO_PLAY_STATE, *LPDMUS_PRIVATE_TEMPO_PLAY_STATE; diff --git a/dlls/dmime/graph.c b/dlls/dmime/graph.c index 159923b6a70..20d614b95ff 100644 --- a/dlls/dmime/graph.c +++ b/dlls/dmime/graph.c @@ -22,12 +22,18 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmime); +struct tool_entry +{ + struct list entry; + IDirectMusicTool *tool; +}; + struct IDirectMusicGraphImpl { IDirectMusicGraph IDirectMusicGraph_iface; struct dmobject dmobj; LONG ref; - WORD num_tools; - struct list Tools; + + struct list tools; }; static inline IDirectMusicGraphImpl *impl_from_IDirectMusicGraph(IDirectMusicGraph *iface) @@ -85,7 +91,19 @@ static ULONG WINAPI DirectMusicGraph_Release(IDirectMusicGraph *iface) TRACE("(%p): %ld\n", This, ref); - if (!ref) free(This); + if (!ref) + { + struct tool_entry *entry, *next; + + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &This->tools, struct tool_entry, entry) + { + list_remove(&entry->entry); + IDirectMusicTool_Release(entry->tool); + free(entry); + } + + free(This); + } return ref; } @@ -97,80 +115,74 @@ static HRESULT WINAPI DirectMusicGraph_StampPMsg(IDirectMusicGraph *iface, DMUS_ return S_OK; } -static HRESULT WINAPI DirectMusicGraph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool* pTool, DWORD* pdwPChannels, DWORD cPChannels, LONG lIndex) +static HRESULT WINAPI DirectMusicGraph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool *tool, + DWORD *channels, DWORD channel_count, LONG index) { IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); - struct list* pEntry = NULL; - struct list* pPrevEntry = NULL; - LPDMUS_PRIVATE_GRAPH_TOOL pIt = NULL; - LPDMUS_PRIVATE_GRAPH_TOOL pNewTool = NULL; - - FIXME("(%p, %p, %p, %ld, %li): use of pdwPChannels\n", This, pTool, pdwPChannels, cPChannels, lIndex); - - if (!pTool) - return E_POINTER; + struct tool_entry *entry, *next; - if (lIndex < 0) - lIndex = This->num_tools + lIndex; + TRACE("(%p, %p, %p, %ld, %li)\n", This, tool, channels, channel_count, index); - pPrevEntry = &This->Tools; - LIST_FOR_EACH(pEntry, &This->Tools) - { - pIt = LIST_ENTRY(pEntry, DMUS_PRIVATE_GRAPH_TOOL, entry); - if (pIt->dwIndex == lIndex) - return DMUS_E_ALREADY_EXISTS; + if (!tool) return E_POINTER; - if (pIt->dwIndex > lIndex) - break ; - pPrevEntry = pEntry; + LIST_FOR_EACH_ENTRY(next, &This->tools, struct tool_entry, entry) + { + if (next->tool == tool) return DMUS_E_ALREADY_EXISTS; + if (index-- <= 0) break; } - ++This->num_tools; - pNewTool = calloc(1, sizeof(*pNewTool)); - pNewTool->pTool = pTool; - pNewTool->dwIndex = lIndex; - IDirectMusicTool_AddRef(pTool); - IDirectMusicTool_Init(pTool, iface); - list_add_tail (pPrevEntry->next, &pNewTool->entry); - -#if 0 - DWORD dwNum = 0; - IDirectMusicTool8_GetMediaTypes(pTool, &dwNum); -#endif + if (!(entry = calloc(1, sizeof(*entry)))) return E_OUTOFMEMORY; + entry->tool = tool; + IDirectMusicTool_AddRef(tool); + IDirectMusicTool_Init(tool, iface); + list_add_before(&next->entry, &entry->entry); - return DS_OK; + return S_OK; } -static HRESULT WINAPI DirectMusicGraph_GetTool(IDirectMusicGraph *iface, DWORD dwIndex, IDirectMusicTool** ppTool) +static HRESULT WINAPI DirectMusicGraph_GetTool(IDirectMusicGraph *iface, DWORD index, IDirectMusicTool **ret_tool) { IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); - struct list* pEntry = NULL; - LPDMUS_PRIVATE_GRAPH_TOOL pIt = NULL; + struct tool_entry *entry; + + TRACE("(%p, %ld, %p)\n", This, index, ret_tool); - TRACE("(%p, %ld, %p)\n", This, dwIndex, ppTool); + if (!ret_tool) return E_POINTER; - LIST_FOR_EACH (pEntry, &This->Tools) + LIST_FOR_EACH_ENTRY(entry, &This->tools, struct tool_entry, entry) { - pIt = LIST_ENTRY(pEntry, DMUS_PRIVATE_GRAPH_TOOL, entry); - if (pIt->dwIndex == dwIndex) + if (!index--) { - *ppTool = pIt->pTool; - if (*ppTool) - IDirectMusicTool_AddRef(*ppTool); + *ret_tool = entry->tool; + IDirectMusicTool_AddRef(entry->tool); return S_OK; } - if (pIt->dwIndex > dwIndex) - break ; } return DMUS_E_NOT_FOUND; } -static HRESULT WINAPI DirectMusicGraph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool* pTool) +static HRESULT WINAPI DirectMusicGraph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool *tool) { IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); - FIXME("(%p, %p): stub\n", This, pTool); - return S_OK; + struct tool_entry *entry; + + TRACE("(%p, %p)\n", This, tool); + + if (!tool) return E_POINTER; + + LIST_FOR_EACH_ENTRY(entry, &This->tools, struct tool_entry, entry) + { + if (entry->tool == tool) + { + list_remove(&entry->entry); + IDirectMusicTool_Release(entry->tool); + free(entry); + return S_OK; + } + } + + return DMUS_E_NOT_FOUND; } static const IDirectMusicGraphVtbl DirectMusicGraphVtbl = @@ -260,7 +272,7 @@ HRESULT create_dmgraph(REFIID riid, void **ret_iface) dmobject_init(&obj->dmobj, &CLSID_DirectMusicGraph, (IUnknown *)&obj->IDirectMusicGraph_iface); obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - list_init(&obj->Tools); + list_init(&obj->tools); hr = IDirectMusicGraph_QueryInterface(&obj->IDirectMusicGraph_iface, riid, ret_iface); IDirectMusicGraph_Release(&obj->IDirectMusicGraph_iface); diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 3d9eb188fed..605830149e7 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -705,10 +705,10 @@ static void test_graph(void) ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicGraph_GetTool(graph, 0, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicGraph_GetTool(graph, 0, &tmp_tool); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(tool1 == tmp_tool, "got %p\n", tmp_tool); + ok(hr == S_OK, "got %#lx\n", hr); + ok(tool1 == tmp_tool, "got %p\n", tmp_tool); if (hr == S_OK) IDirectMusicTool_Release(tmp_tool); hr = IDirectMusicGraph_GetTool(graph, 1, &tmp_tool); ok(hr == S_OK, "got %#lx\n", hr); @@ -723,24 +723,24 @@ static void test_graph(void) /* test removing the first tool */ hr = IDirectMusicGraph_RemoveTool(graph, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicGraph_RemoveTool(graph, tool1); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicGraph_RemoveTool(graph, tool1); - todo_wine ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); hr = IDirectMusicGraph_GetTool(graph, 0, &tmp_tool); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); ok(tool2 == tmp_tool, "got %p\n", tmp_tool); if (hr == S_OK) IDirectMusicTool_Release(tmp_tool); hr = IDirectMusicGraph_GetTool(graph, 1, &tmp_tool); - todo_wine ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); hr = IDirectMusicGraph_InsertTool(graph, tool1, NULL, 0, -1); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicGraph_GetTool(graph, 0, &tmp_tool); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(tool1 == tmp_tool, "got %p\n", tmp_tool); + ok(hr == S_OK, "got %#lx\n", hr); + ok(tool1 == tmp_tool, "got %p\n", tmp_tool); if (hr == S_OK) IDirectMusicTool_Release(tmp_tool); @@ -810,7 +810,7 @@ static void test_graph(void) hr = IDirectMusicGraph_InsertTool(tmp_graph, tool1, NULL, 0, 0); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicGraph_InsertTool(tmp_graph, tool2, NULL, 0, 0); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicGraph_StampPMsg(tmp_graph, &msg); ok(hr == S_OK, "got %#lx\n", hr); ok(msg.pGraph == graph, "got %p\n", msg.pGraph); From b8e0a4f667a7385eeeb7b463b48c1c623754576e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 12 Sep 2023 10:06:57 +0200 Subject: [PATCH 0864/1148] dmime: Rename DirectMusicGraph method prefix to graph. (cherry picked from commit fec9d2c22e534996a2c48069cb990b7452cdc89e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/graph.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/dlls/dmime/graph.c b/dlls/dmime/graph.c index 20d614b95ff..2e548690a96 100644 --- a/dlls/dmime/graph.c +++ b/dlls/dmime/graph.c @@ -46,7 +46,7 @@ static inline IDirectMusicGraphImpl *impl_from_IPersistStream(IPersistStream *if return CONTAINING_RECORD(iface, IDirectMusicGraphImpl, dmobj.IPersistStream_iface); } -static HRESULT WINAPI DirectMusicGraph_QueryInterface(IDirectMusicGraph *iface, REFIID riid, void **ret_iface) +static HRESULT WINAPI graph_QueryInterface(IDirectMusicGraph *iface, REFIID riid, void **ret_iface) { IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); @@ -74,7 +74,7 @@ static HRESULT WINAPI DirectMusicGraph_QueryInterface(IDirectMusicGraph *iface, return E_NOINTERFACE; } -static ULONG WINAPI DirectMusicGraph_AddRef(IDirectMusicGraph *iface) +static ULONG WINAPI graph_AddRef(IDirectMusicGraph *iface) { IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); ULONG ref = InterlockedIncrement(&This->ref); @@ -84,7 +84,7 @@ static ULONG WINAPI DirectMusicGraph_AddRef(IDirectMusicGraph *iface) return ref; } -static ULONG WINAPI DirectMusicGraph_Release(IDirectMusicGraph *iface) +static ULONG WINAPI graph_Release(IDirectMusicGraph *iface) { IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); ULONG ref = InterlockedDecrement(&This->ref); @@ -108,14 +108,14 @@ static ULONG WINAPI DirectMusicGraph_Release(IDirectMusicGraph *iface) return ref; } -static HRESULT WINAPI DirectMusicGraph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg) +static HRESULT WINAPI graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg) { IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); FIXME("(%p, %p): stub\n", This, msg); return S_OK; } -static HRESULT WINAPI DirectMusicGraph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool *tool, +static HRESULT WINAPI graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool *tool, DWORD *channels, DWORD channel_count, LONG index) { IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); @@ -140,7 +140,7 @@ static HRESULT WINAPI DirectMusicGraph_InsertTool(IDirectMusicGraph *iface, IDir return S_OK; } -static HRESULT WINAPI DirectMusicGraph_GetTool(IDirectMusicGraph *iface, DWORD index, IDirectMusicTool **ret_tool) +static HRESULT WINAPI graph_GetTool(IDirectMusicGraph *iface, DWORD index, IDirectMusicTool **ret_tool) { IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); struct tool_entry *entry; @@ -162,7 +162,7 @@ static HRESULT WINAPI DirectMusicGraph_GetTool(IDirectMusicGraph *iface, DWORD i return DMUS_E_NOT_FOUND; } -static HRESULT WINAPI DirectMusicGraph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool *tool) +static HRESULT WINAPI graph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool *tool) { IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); struct tool_entry *entry; @@ -185,15 +185,15 @@ static HRESULT WINAPI DirectMusicGraph_RemoveTool(IDirectMusicGraph *iface, IDir return DMUS_E_NOT_FOUND; } -static const IDirectMusicGraphVtbl DirectMusicGraphVtbl = +static const IDirectMusicGraphVtbl graph_vtbl = { - DirectMusicGraph_QueryInterface, - DirectMusicGraph_AddRef, - DirectMusicGraph_Release, - DirectMusicGraph_StampPMsg, - DirectMusicGraph_InsertTool, - DirectMusicGraph_GetTool, - DirectMusicGraph_RemoveTool + graph_QueryInterface, + graph_AddRef, + graph_Release, + graph_StampPMsg, + graph_InsertTool, + graph_GetTool, + graph_RemoveTool, }; static HRESULT WINAPI graph_IDirectMusicObject_ParseDescriptor(IDirectMusicObject *iface, @@ -267,7 +267,7 @@ HRESULT create_dmgraph(REFIID riid, void **ret_iface) *ret_iface = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->IDirectMusicGraph_iface.lpVtbl = &DirectMusicGraphVtbl; + obj->IDirectMusicGraph_iface.lpVtbl = &graph_vtbl; obj->ref = 1; dmobject_init(&obj->dmobj, &CLSID_DirectMusicGraph, (IUnknown *)&obj->IDirectMusicGraph_iface); obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; From b8a7401b3d4938a6286b9562bb59eb34f473eba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 22:37:56 +0200 Subject: [PATCH 0865/1148] dmime: Get rid of the IDirectMusicGraphImpl typedef. (cherry picked from commit 5d97483f2252bd37db0762b94af8843c1ba01262) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/audiopath.c | 12 ++++++------ dlls/dmime/dmime_private.h | 1 - dlls/dmime/graph.c | 37 +++++++++++++++++++------------------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/dlls/dmime/audiopath.c b/dlls/dmime/audiopath.c index 013939f9dfa..aef48a5d2db 100644 --- a/dlls/dmime/audiopath.c +++ b/dlls/dmime/audiopath.c @@ -158,11 +158,11 @@ static HRESULT WINAPI IDirectMusicAudioPathImpl_GetObjectInPath (IDirectMusicAud { if (IsEqualIID (iidInterface, &IID_IDirectMusicGraph)) { if (NULL == This->pToolGraph) { - IDirectMusicGraphImpl* pGraph; + IDirectMusicGraph* pGraph; hr = create_dmgraph(&IID_IDirectMusicGraph, (void**)&pGraph); if (FAILED(hr)) return hr; - This->pToolGraph = (IDirectMusicGraph*) pGraph; + This->pToolGraph = pGraph; } *ppObject = This->pToolGraph; IDirectMusicGraph_AddRef((LPDIRECTMUSICGRAPH) *ppObject); @@ -191,14 +191,14 @@ static HRESULT WINAPI IDirectMusicAudioPathImpl_GetObjectInPath (IDirectMusicAud IDirectMusicGraph* pPerfoGraph = NULL; IDirectMusicPerformance8_GetGraph(This->pPerf, &pPerfoGraph); if (NULL == pPerfoGraph) { - IDirectMusicGraphImpl* pGraph = NULL; + IDirectMusicGraph* pGraph = NULL; hr = create_dmgraph(&IID_IDirectMusicGraph, (void**)&pGraph); if (FAILED(hr)) return hr; - IDirectMusicPerformance8_SetGraph(This->pPerf, (IDirectMusicGraph*) pGraph); + IDirectMusicPerformance8_SetGraph(This->pPerf, pGraph); /* we need release as SetGraph do an AddRef */ - IDirectMusicGraph_Release((LPDIRECTMUSICGRAPH) pGraph); - pPerfoGraph = (LPDIRECTMUSICGRAPH) pGraph; + IDirectMusicGraph_Release(pGraph); + pPerfoGraph = pGraph; } *ppObject = pPerfoGraph; return S_OK; diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 28b0afc9af8..be0c463339f 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -45,7 +45,6 @@ /***************************************************************************** * Interfaces */ -typedef struct IDirectMusicGraphImpl IDirectMusicGraphImpl; typedef struct IDirectMusicAudioPathImpl IDirectMusicAudioPathImpl; /***************************************************************************** diff --git a/dlls/dmime/graph.c b/dlls/dmime/graph.c index 2e548690a96..1e6f16b6be0 100644 --- a/dlls/dmime/graph.c +++ b/dlls/dmime/graph.c @@ -28,27 +28,28 @@ struct tool_entry IDirectMusicTool *tool; }; -struct IDirectMusicGraphImpl { - IDirectMusicGraph IDirectMusicGraph_iface; - struct dmobject dmobj; - LONG ref; +struct graph +{ + IDirectMusicGraph IDirectMusicGraph_iface; + struct dmobject dmobj; + LONG ref; - struct list tools; + struct list tools; }; -static inline IDirectMusicGraphImpl *impl_from_IDirectMusicGraph(IDirectMusicGraph *iface) +static inline struct graph *impl_from_IDirectMusicGraph(IDirectMusicGraph *iface) { - return CONTAINING_RECORD(iface, IDirectMusicGraphImpl, IDirectMusicGraph_iface); + return CONTAINING_RECORD(iface, struct graph, IDirectMusicGraph_iface); } -static inline IDirectMusicGraphImpl *impl_from_IPersistStream(IPersistStream *iface) +static inline struct graph *impl_from_IPersistStream(IPersistStream *iface) { - return CONTAINING_RECORD(iface, IDirectMusicGraphImpl, dmobj.IPersistStream_iface); + return CONTAINING_RECORD(iface, struct graph, dmobj.IPersistStream_iface); } static HRESULT WINAPI graph_QueryInterface(IDirectMusicGraph *iface, REFIID riid, void **ret_iface) { - IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); + struct graph *This = impl_from_IDirectMusicGraph(iface); TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ret_iface); @@ -76,7 +77,7 @@ static HRESULT WINAPI graph_QueryInterface(IDirectMusicGraph *iface, REFIID riid static ULONG WINAPI graph_AddRef(IDirectMusicGraph *iface) { - IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); + struct graph *This = impl_from_IDirectMusicGraph(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p): %ld\n", This, ref); @@ -86,7 +87,7 @@ static ULONG WINAPI graph_AddRef(IDirectMusicGraph *iface) static ULONG WINAPI graph_Release(IDirectMusicGraph *iface) { - IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); + struct graph *This = impl_from_IDirectMusicGraph(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p): %ld\n", This, ref); @@ -110,7 +111,7 @@ static ULONG WINAPI graph_Release(IDirectMusicGraph *iface) static HRESULT WINAPI graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg) { - IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); + struct graph *This = impl_from_IDirectMusicGraph(iface); FIXME("(%p, %p): stub\n", This, msg); return S_OK; } @@ -118,7 +119,7 @@ static HRESULT WINAPI graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg) static HRESULT WINAPI graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool *tool, DWORD *channels, DWORD channel_count, LONG index) { - IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); + struct graph *This = impl_from_IDirectMusicGraph(iface); struct tool_entry *entry, *next; TRACE("(%p, %p, %p, %ld, %li)\n", This, tool, channels, channel_count, index); @@ -142,7 +143,7 @@ static HRESULT WINAPI graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicToo static HRESULT WINAPI graph_GetTool(IDirectMusicGraph *iface, DWORD index, IDirectMusicTool **ret_tool) { - IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); + struct graph *This = impl_from_IDirectMusicGraph(iface); struct tool_entry *entry; TRACE("(%p, %ld, %p)\n", This, index, ret_tool); @@ -164,7 +165,7 @@ static HRESULT WINAPI graph_GetTool(IDirectMusicGraph *iface, DWORD index, IDire static HRESULT WINAPI graph_RemoveTool(IDirectMusicGraph *iface, IDirectMusicTool *tool) { - IDirectMusicGraphImpl *This = impl_from_IDirectMusicGraph(iface); + struct graph *This = impl_from_IDirectMusicGraph(iface); struct tool_entry *entry; TRACE("(%p, %p)\n", This, tool); @@ -240,7 +241,7 @@ static const IDirectMusicObjectVtbl dmobject_vtbl = { static HRESULT WINAPI graph_IPersistStream_Load(IPersistStream *iface, IStream *stream) { - IDirectMusicGraphImpl *This = impl_from_IPersistStream(iface); + struct graph *This = impl_from_IPersistStream(iface); FIXME("(%p, %p): Loading not implemented yet\n", This, stream); @@ -262,7 +263,7 @@ static const IPersistStreamVtbl persiststream_vtbl = { /* for ClassFactory */ HRESULT create_dmgraph(REFIID riid, void **ret_iface) { - IDirectMusicGraphImpl* obj; + struct graph *obj; HRESULT hr; *ret_iface = NULL; From 409487672b9d3d3b8603933478473661770a1594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 22:41:39 +0200 Subject: [PATCH 0866/1148] dmime: Implement IDirectMusicGraph_StampPMsg. (cherry picked from commit 98db0c753aa4478d2ae7efdee3e3286ee02a9518) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/graph.c | 29 ++++++++++++++++++++++++++++- dlls/dmime/tests/dmime.c | 38 +++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/dlls/dmime/graph.c b/dlls/dmime/graph.c index 1e6f16b6be0..44996b0303e 100644 --- a/dlls/dmime/graph.c +++ b/dlls/dmime/graph.c @@ -112,7 +112,34 @@ static ULONG WINAPI graph_Release(IDirectMusicGraph *iface) static HRESULT WINAPI graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg) { struct graph *This = impl_from_IDirectMusicGraph(iface); - FIXME("(%p, %p): stub\n", This, msg); + struct tool_entry *entry, *next, *first; + + TRACE("(%p, %p)\n", This, msg); + + if (!msg) return E_POINTER; + + first = LIST_ENTRY(This->tools.next, struct tool_entry, entry); + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &This->tools, struct tool_entry, entry) + if (entry->tool == msg->pTool) break; + if (&entry->entry == &This->tools) next = first; + + if (msg->pTool) + { + IDirectMusicTool_Release(msg->pTool); + msg->pTool = NULL; + } + + if (&next->entry == &This->tools) return DMUS_S_LAST_TOOL; + + if (!msg->pGraph) + { + msg->pGraph = iface; + IDirectMusicGraph_AddRef(msg->pGraph); + } + + msg->pTool = next->tool; + IDirectMusicTool_AddRef(msg->pTool); + return S_OK; } diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 605830149e7..c9c613a40c5 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -746,12 +746,12 @@ static void test_graph(void) /* Test basic IDirectMusicGraph_StampPMsg usage */ hr = IDirectMusicGraph_StampPMsg(graph, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); memset(&msg, 0, sizeof(msg)); hr = IDirectMusicGraph_StampPMsg(graph, &msg); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(msg.pGraph == graph, "got %p\n", msg.pGraph); - todo_wine ok(msg.pTool == tool1, "got %p\n", msg.pTool); + ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + ok(msg.pTool == tool1, "got %p\n", msg.pTool); ok(!msg.dwSize, "got %ld\n", msg.dwSize); ok(!msg.rtTime, "got %I64d\n", msg.rtTime); @@ -766,19 +766,19 @@ static void test_graph(void) hr = IDirectMusicGraph_StampPMsg(graph, &msg); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(msg.pGraph == graph, "got %p\n", msg.pGraph); - todo_wine ok(msg.pTool == tool2, "got %p\n", msg.pTool); + ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + ok(msg.pTool == tool2, "got %p\n", msg.pTool); hr = IDirectMusicGraph_StampPMsg(graph, &msg); - todo_wine ok(hr == DMUS_S_LAST_TOOL, "got %#lx\n", hr); - todo_wine ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + ok(hr == DMUS_S_LAST_TOOL, "got %#lx\n", hr); + ok(msg.pGraph == graph, "got %p\n", msg.pGraph); ok(!msg.pTool, "got %p\n", msg.pTool); hr = IDirectMusicGraph_StampPMsg(graph, &msg); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(msg.pGraph == graph, "got %p\n", msg.pGraph); - todo_wine ok(msg.pTool == tool1, "got %p\n", msg.pTool); - if (msg.pGraph) IDirectMusicGraph_Release(msg.pGraph); + ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + ok(msg.pTool == tool1, "got %p\n", msg.pTool); + IDirectMusicGraph_Release(msg.pGraph); msg.pGraph = NULL; - if (msg.pTool) IDirectMusicGraph_Release(msg.pTool); + IDirectMusicGraph_Release(msg.pTool); msg.pTool = NULL; @@ -794,16 +794,16 @@ static void test_graph(void) hr = IDirectMusicGraph_StampPMsg(graph, &msg); ok(hr == S_OK, "got %#lx\n", hr); ok(msg.pGraph == tmp_graph, "got %p\n", msg.pGraph); - todo_wine ok(msg.pTool == tool2, "got %p\n", msg.pTool); - if (msg.pGraph) IDirectMusicGraph_Release(msg.pGraph); + ok(msg.pTool == tool2, "got %p\n", msg.pTool); + IDirectMusicGraph_Release(msg.pGraph); msg.pGraph = NULL; msg.pGraph = graph; IDirectMusicGraph_AddRef(msg.pGraph); hr = IDirectMusicGraph_StampPMsg(tmp_graph, &msg); - todo_wine ok(hr == DMUS_S_LAST_TOOL, "got %#lx\n", hr); + ok(hr == DMUS_S_LAST_TOOL, "got %#lx\n", hr); ok(msg.pGraph == graph, "got %p\n", msg.pGraph); - todo_wine ok(msg.pTool == NULL, "got %p\n", msg.pTool); + ok(msg.pTool == NULL, "got %p\n", msg.pTool); msg.pTool = tool2; IDirectMusicTool_AddRef(msg.pTool); @@ -814,16 +814,16 @@ static void test_graph(void) hr = IDirectMusicGraph_StampPMsg(tmp_graph, &msg); ok(hr == S_OK, "got %#lx\n", hr); ok(msg.pGraph == graph, "got %p\n", msg.pGraph); - todo_wine ok(msg.pTool == tool1, "got %p\n", msg.pTool); - if (msg.pGraph) IDirectMusicGraph_Release(msg.pGraph); + ok(msg.pTool == tool1, "got %p\n", msg.pTool); + IDirectMusicGraph_Release(msg.pGraph); msg.pGraph = NULL; hr = IDirectMusicGraph_RemoveTool(graph, tool1); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicGraph_StampPMsg(tmp_graph, &msg); - todo_wine ok(hr == DMUS_S_LAST_TOOL, "got %#lx\n", hr); + ok(hr == DMUS_S_LAST_TOOL, "got %#lx\n", hr); ok(msg.pGraph == NULL, "got %p\n", msg.pGraph); - todo_wine ok(msg.pTool == NULL, "got %p\n", msg.pTool); + ok(msg.pTool == NULL, "got %p\n", msg.pTool); IDirectMusicGraph_Release(tmp_graph); From a672db3a6162bf11fbb2259099e6b9bf06f38505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 22:58:08 +0200 Subject: [PATCH 0867/1148] dmime: Implement performance IDirectMusicGraph_StampPMsg. (cherry picked from commit 09dc4a3f1739c2184695138a4c82b20954046c0c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 33 +++++++++++++++++++++++++++++++-- dlls/dmime/tests/dmime.c | 34 +++++++++++++++++----------------- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 4085f70cc77..904008954a2 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1293,8 +1293,37 @@ static ULONG WINAPI performance_graph_Release(IDirectMusicGraph *iface) static HRESULT WINAPI performance_graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg) { struct performance *This = impl_from_IDirectMusicGraph(iface); - FIXME("(%p, %p): stub\n", This, msg); - return E_NOTIMPL; + HRESULT hr; + + TRACE("(%p, %p)\n", This, msg); + + if (!msg) return E_POINTER; + + /* FIXME: Implement segment and audio path graphs support */ + if (!This->pToolGraph) hr = DMUS_S_LAST_TOOL; + else if (FAILED(hr = IDirectMusicGraph_StampPMsg(This->pToolGraph, msg))) return hr; + + if (msg->pGraph) + { + IDirectMusicTool_Release(msg->pGraph); + msg->pGraph = NULL; + } + + if (hr == DMUS_S_LAST_TOOL) + { + if (msg->pTool) IDirectMusicTool_Release(msg->pTool); + msg->pTool = &This->IDirectMusicTool_iface; + IDirectMusicTool_AddRef(msg->pTool); + return S_OK; + } + + if (SUCCEEDED(hr)) + { + msg->pGraph = &This->IDirectMusicGraph_iface; + IDirectMusicTool_AddRef(msg->pGraph); + } + + return hr; } static HRESULT WINAPI performance_graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicTool *tool, diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index c9c613a40c5..17f11daf5ac 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1544,13 +1544,13 @@ static void test_performance_graph(void) /* test IDirectMusicGraph_StampPMsg usage */ hr = IDirectMusicGraph_StampPMsg(graph, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); memset(&msg, 0, sizeof(msg)); hr = IDirectMusicGraph_StampPMsg(graph, &msg); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); ok(msg.pGraph == NULL, "got %p\n", msg.pGraph); - todo_wine ok(msg.pTool != NULL, "got %p\n", msg.pTool); - if (msg.pTool) check_interface(msg.pTool, &IID_IDirectMusicPerformance, TRUE); + ok(msg.pTool != NULL, "got %p\n", msg.pTool); + check_interface(msg.pTool, &IID_IDirectMusicPerformance, TRUE); ok(!msg.dwSize, "got %ld\n", msg.dwSize); ok(!msg.rtTime, "got %I64d\n", msg.rtTime); @@ -1564,12 +1564,12 @@ static void test_performance_graph(void) ok(!msg.punkUser, "got %p\n", msg.punkUser); hr = IDirectMusicGraph_StampPMsg(graph, &msg); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); ok(msg.pGraph == NULL, "got %p\n", msg.pGraph); - todo_wine ok(msg.pTool != NULL, "got %p\n", msg.pTool); - if (msg.pTool) check_interface(msg.pTool, &IID_IDirectMusicPerformance, TRUE); + ok(msg.pTool != NULL, "got %p\n", msg.pTool); + check_interface(msg.pTool, &IID_IDirectMusicPerformance, TRUE); - if (msg.pTool) IDirectMusicTool_Release(msg.pTool); + IDirectMusicTool_Release(msg.pTool); msg.pTool = NULL; IDirectMusicGraph_Release(graph); @@ -1604,9 +1604,9 @@ static void test_performance_graph(void) memset(&msg, 0, sizeof(msg)); hr = IDirectMusicGraph_StampPMsg(graph, &msg); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(msg.pGraph == graph, "got %p\n", msg.pGraph); - todo_wine ok(msg.pTool == tool, "got %p\n", msg.pTool); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg.pGraph == graph, "got %p\n", msg.pGraph); + ok(msg.pTool == tool, "got %p\n", msg.pTool); ok(!msg.dwSize, "got %ld\n", msg.dwSize); ok(!msg.rtTime, "got %I64d\n", msg.rtTime); @@ -1620,12 +1620,12 @@ static void test_performance_graph(void) ok(!msg.punkUser, "got %p\n", msg.punkUser); hr = IDirectMusicGraph_StampPMsg(graph, &msg); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); ok(msg.pGraph == NULL, "got %p\n", msg.pGraph); - todo_wine ok(msg.pTool != NULL, "got %p\n", msg.pTool); - if (msg.pTool) check_interface(msg.pTool, &IID_IDirectMusicPerformance, TRUE); + ok(msg.pTool != NULL, "got %p\n", msg.pTool); + check_interface(msg.pTool, &IID_IDirectMusicPerformance, TRUE); - if (msg.pTool) IDirectMusicTool_Release(msg.pTool); + IDirectMusicTool_Release(msg.pTool); msg.pTool = NULL; IDirectMusicGraph_Release(graph); @@ -1871,7 +1871,7 @@ static void test_performance_pmsg(void) hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicGraph_StampPMsg(graph, msg); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); IDirectMusicGraph_Release(graph); hr = IDirectMusicPerformance_SendPMsg(performance, msg); @@ -1901,7 +1901,7 @@ static void test_performance_pmsg(void) hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicGraph_StampPMsg(graph, msg); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); IDirectMusicGraph_Release(graph); msg->dwFlags &= ~(DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME); From 7b287ed0220cab219366b72ad1bd67e2a5fc6c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 12 Sep 2023 10:11:38 +0200 Subject: [PATCH 0868/1148] dmime: Set the tool delivery type on the messages flags. (cherry picked from commit 1f84523800671046076aac142babea56c9a1b79d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/graph.c | 11 +++++++++++ dlls/dmime/performance.c | 4 ++++ dlls/dmime/tests/dmime.c | 6 +++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/dlls/dmime/graph.c b/dlls/dmime/graph.c index 44996b0303e..d8f6bb88701 100644 --- a/dlls/dmime/graph.c +++ b/dlls/dmime/graph.c @@ -26,6 +26,7 @@ struct tool_entry { struct list entry; IDirectMusicTool *tool; + DWORD delivery; }; struct graph @@ -111,6 +112,7 @@ static ULONG WINAPI graph_Release(IDirectMusicGraph *iface) static HRESULT WINAPI graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg) { + const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME; struct graph *This = impl_from_IDirectMusicGraph(iface); struct tool_entry *entry, *next, *first; @@ -140,6 +142,9 @@ static HRESULT WINAPI graph_StampPMsg(IDirectMusicGraph *iface, DMUS_PMSG *msg) msg->pTool = next->tool; IDirectMusicTool_AddRef(msg->pTool); + msg->dwFlags &= ~delivery_flags; + msg->dwFlags |= next->delivery; + return S_OK; } @@ -148,6 +153,7 @@ static HRESULT WINAPI graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicToo { struct graph *This = impl_from_IDirectMusicGraph(iface); struct tool_entry *entry, *next; + HRESULT hr; TRACE("(%p, %p, %p, %ld, %li)\n", This, tool, channels, channel_count, index); @@ -163,6 +169,11 @@ static HRESULT WINAPI graph_InsertTool(IDirectMusicGraph *iface, IDirectMusicToo entry->tool = tool; IDirectMusicTool_AddRef(tool); IDirectMusicTool_Init(tool, iface); + if (FAILED(hr = IDirectMusicTool_GetMsgDeliveryType(tool, &entry->delivery))) + { + WARN("Failed to get delivery type from tool %p, hr %#lx\n", tool, hr); + entry->delivery = DMUS_PMSGF_TOOL_IMMEDIATE; + } list_add_before(&next->entry, &entry->entry); return S_OK; diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 904008954a2..d277c5de043 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1311,6 +1311,10 @@ static HRESULT WINAPI performance_graph_StampPMsg(IDirectMusicGraph *iface, DMUS if (hr == DMUS_S_LAST_TOOL) { + const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME; + msg->dwFlags &= ~delivery_flags; + msg->dwFlags |= DMUS_PMSGF_TOOL_QUEUE; + if (msg->pTool) IDirectMusicTool_Release(msg->pTool); msg->pTool = &This->IDirectMusicTool_iface; IDirectMusicTool_AddRef(msg->pTool); diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 17f11daf5ac..e178960b18f 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -756,7 +756,7 @@ static void test_graph(void) ok(!msg.dwSize, "got %ld\n", msg.dwSize); ok(!msg.rtTime, "got %I64d\n", msg.rtTime); ok(!msg.mtTime, "got %ld\n", msg.mtTime); - todo_wine ok(msg.dwFlags == DMUS_PMSGF_TOOL_IMMEDIATE, "got %#lx\n", msg.dwFlags); + ok(msg.dwFlags == DMUS_PMSGF_TOOL_IMMEDIATE, "got %#lx\n", msg.dwFlags); ok(!msg.dwPChannel, "got %ld\n", msg.dwPChannel); ok(!msg.dwVirtualTrackID, "got %ld\n", msg.dwVirtualTrackID); ok(!msg.dwType, "got %#lx\n", msg.dwType); @@ -1555,7 +1555,7 @@ static void test_performance_graph(void) ok(!msg.dwSize, "got %ld\n", msg.dwSize); ok(!msg.rtTime, "got %I64d\n", msg.rtTime); ok(!msg.mtTime, "got %ld\n", msg.mtTime); - todo_wine ok(msg.dwFlags == DMUS_PMSGF_TOOL_QUEUE, "got %#lx\n", msg.dwFlags); + ok(msg.dwFlags == DMUS_PMSGF_TOOL_QUEUE, "got %#lx\n", msg.dwFlags); ok(!msg.dwPChannel, "got %ld\n", msg.dwPChannel); ok(!msg.dwVirtualTrackID, "got %ld\n", msg.dwVirtualTrackID); ok(!msg.dwType, "got %#lx\n", msg.dwType); @@ -1611,7 +1611,7 @@ static void test_performance_graph(void) ok(!msg.dwSize, "got %ld\n", msg.dwSize); ok(!msg.rtTime, "got %I64d\n", msg.rtTime); ok(!msg.mtTime, "got %ld\n", msg.mtTime); - todo_wine ok(msg.dwFlags == DMUS_PMSGF_TOOL_IMMEDIATE, "got %#lx\n", msg.dwFlags); + ok(msg.dwFlags == DMUS_PMSGF_TOOL_IMMEDIATE, "got %#lx\n", msg.dwFlags); ok(!msg.dwPChannel, "got %ld\n", msg.dwPChannel); ok(!msg.dwVirtualTrackID, "got %ld\n", msg.dwVirtualTrackID); ok(!msg.dwType, "got %#lx\n", msg.dwType); From fa8cb4189781eb83939763d672be8664eff15c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 22 Aug 2023 17:38:20 +0200 Subject: [PATCH 0869/1148] dmcompos: Always return S_FALSE from DllCanUnloadNow. (cherry picked from commit 7d94983c735b9fd960ac6ad9d1618f1183f5d703) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmcompos/chordmap.c | 2 -- dlls/dmcompos/chordmaptrack.c | 2 -- dlls/dmcompos/composer.c | 2 -- dlls/dmcompos/dmcompos_main.c | 21 --------------------- dlls/dmcompos/dmcompos_private.h | 7 ------- dlls/dmcompos/signposttrack.c | 2 -- 6 files changed, 36 deletions(-) diff --git a/dlls/dmcompos/chordmap.c b/dlls/dmcompos/chordmap.c index adc17402f39..86134451846 100644 --- a/dlls/dmcompos/chordmap.c +++ b/dlls/dmcompos/chordmap.c @@ -81,7 +81,6 @@ static ULONG WINAPI IDirectMusicChordMapImpl_Release(IDirectMusicChordMap *iface if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMCOMPOS_UnlockModule(); } return ref; @@ -326,7 +325,6 @@ HRESULT create_dmchordmap(REFIID lpcGUID, void **ppobj) obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMCOMPOS_LockModule(); hr = IDirectMusicChordMap_QueryInterface(&obj->IDirectMusicChordMap_iface, lpcGUID, ppobj); IDirectMusicChordMap_Release(&obj->IDirectMusicChordMap_iface); diff --git a/dlls/dmcompos/chordmaptrack.c b/dlls/dmcompos/chordmaptrack.c index 66517340d70..35c64aab4a6 100644 --- a/dlls/dmcompos/chordmaptrack.c +++ b/dlls/dmcompos/chordmaptrack.c @@ -79,7 +79,6 @@ static ULONG WINAPI chordmap_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMCOMPOS_UnlockModule(); } return ref; @@ -301,7 +300,6 @@ HRESULT create_dmchordmaptrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMCOMPOS_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmcompos/composer.c b/dlls/dmcompos/composer.c index a2a58396ee5..47e12358201 100644 --- a/dlls/dmcompos/composer.c +++ b/dlls/dmcompos/composer.c @@ -69,7 +69,6 @@ static ULONG WINAPI IDirectMusicComposerImpl_Release(IDirectMusicComposer *iface if (ref == 0) { HeapFree(GetProcessHeap(), 0, This); - DMCOMPOS_UnlockModule(); } return ref; @@ -183,7 +182,6 @@ HRESULT create_dmcomposer(REFIID riid, void **ret_iface) obj->IDirectMusicComposer_iface.lpVtbl = &dmcomposer_vtbl; obj->ref = 1; - DMCOMPOS_LockModule(); hr = IDirectMusicComposer_QueryInterface(&obj->IDirectMusicComposer_iface, riid, ret_iface); IDirectMusicComposer_Release(&obj->IDirectMusicComposer_iface); diff --git a/dlls/dmcompos/dmcompos_main.c b/dlls/dmcompos/dmcompos_main.c index dff77aa4d32..f7b5755683e 100644 --- a/dlls/dmcompos/dmcompos_main.c +++ b/dlls/dmcompos/dmcompos_main.c @@ -38,8 +38,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmcompos); -LONG DMCOMPOS_refCount = 0; - typedef struct { IClassFactory IClassFactory_iface; HRESULT (*fnCreateInstance)(REFIID riid, void **ret_iface); @@ -82,15 +80,11 @@ static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID r static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) { - DMCOMPOS_LockModule(); - return 2; /* non-heap based object */ } static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) { - DMCOMPOS_UnlockModule(); - return 1; /* non-heap based object */ } @@ -112,12 +106,6 @@ static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock) { TRACE("(%d)\n", dolock); - - if (dolock) - DMCOMPOS_LockModule(); - else - DMCOMPOS_UnlockModule(); - return S_OK; } @@ -135,15 +123,6 @@ static IClassFactoryImpl ChordMapTrack_CF = {{&classfactory_vtbl}, create_dmchor static IClassFactoryImpl Template_CF = {{&classfactory_vtbl}, create_direct_music_template}; static IClassFactoryImpl SignPostTrack_CF = {{&classfactory_vtbl}, create_dmsignposttrack}; -/****************************************************************** - * DllCanUnloadNow (DMCOMPOS.@) - * - * - */ -HRESULT WINAPI DllCanUnloadNow(void) { - return DMCOMPOS_refCount != 0 ? S_FALSE : S_OK; -} - /****************************************************************** * DllGetClassObject (DMCOMPOS.@) diff --git a/dlls/dmcompos/dmcompos_private.h b/dlls/dmcompos/dmcompos_private.h index c04d7da6060..928b5a5a993 100644 --- a/dlls/dmcompos/dmcompos_private.h +++ b/dlls/dmcompos/dmcompos_private.h @@ -49,11 +49,4 @@ extern HRESULT create_dmcomposer(REFIID riid, void **ret_iface); extern HRESULT create_dmchordmaptrack(REFIID riid, void **ret_iface); extern HRESULT create_dmsignposttrack(REFIID riid, void **ret_iface); -/********************************************************************** - * Dll lifetime tracking declaration for dmcompos.dll - */ -extern LONG DMCOMPOS_refCount; -static inline void DMCOMPOS_LockModule(void) { InterlockedIncrement( &DMCOMPOS_refCount ); } -static inline void DMCOMPOS_UnlockModule(void) { InterlockedDecrement( &DMCOMPOS_refCount ); } - #endif /* __WINE_DMCOMPOS_PRIVATE_H */ diff --git a/dlls/dmcompos/signposttrack.c b/dlls/dmcompos/signposttrack.c index 249f37b7711..23ed67acebe 100644 --- a/dlls/dmcompos/signposttrack.c +++ b/dlls/dmcompos/signposttrack.c @@ -79,7 +79,6 @@ static ULONG WINAPI signpost_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMCOMPOS_UnlockModule(); } return ref; @@ -289,7 +288,6 @@ HRESULT create_dmsignposttrack(REFIID lpcGUID, void **ppobj) (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMCOMPOS_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); From b2d6d7dc650a9d55dfe721842983b2d68d938d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 09:19:08 +0200 Subject: [PATCH 0870/1148] dmcompos: Use CRT allocation functions. (cherry picked from commit 8a02434cf022f5e3644faee8329337ffe1cedaed) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmcompos/chordmap.c | 33 ++++++++++++++------------------- dlls/dmcompos/chordmaptrack.c | 11 +++-------- dlls/dmcompos/composer.c | 11 +++-------- dlls/dmcompos/dmobject.c | 7 +++---- dlls/dmcompos/signposttrack.c | 11 +++-------- 5 files changed, 26 insertions(+), 47 deletions(-) diff --git a/dlls/dmcompos/chordmap.c b/dlls/dmcompos/chordmap.c index 86134451846..2ddddd3fd57 100644 --- a/dlls/dmcompos/chordmap.c +++ b/dlls/dmcompos/chordmap.c @@ -79,9 +79,7 @@ static ULONG WINAPI IDirectMusicChordMapImpl_Release(IDirectMusicChordMap *iface TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -310,23 +308,20 @@ static const IPersistStreamVtbl persiststream_vtbl = { /* for ClassFactory */ HRESULT create_dmchordmap(REFIID lpcGUID, void **ppobj) { - IDirectMusicChordMapImpl* obj; - HRESULT hr; + IDirectMusicChordMapImpl* obj; + HRESULT hr; - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicChordMapImpl)); - if (NULL == obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } - obj->IDirectMusicChordMap_iface.lpVtbl = &dmchordmap_vtbl; - obj->ref = 1; - dmobject_init(&obj->dmobj, &CLSID_DirectMusicChordMap, - (IUnknown *)&obj->IDirectMusicChordMap_iface); - obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; - obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; + obj->IDirectMusicChordMap_iface.lpVtbl = &dmchordmap_vtbl; + obj->ref = 1; + dmobject_init(&obj->dmobj, &CLSID_DirectMusicChordMap, + (IUnknown *)&obj->IDirectMusicChordMap_iface); + obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; + obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - hr = IDirectMusicChordMap_QueryInterface(&obj->IDirectMusicChordMap_iface, lpcGUID, ppobj); - IDirectMusicChordMap_Release(&obj->IDirectMusicChordMap_iface); + hr = IDirectMusicChordMap_QueryInterface(&obj->IDirectMusicChordMap_iface, lpcGUID, ppobj); + IDirectMusicChordMap_Release(&obj->IDirectMusicChordMap_iface); - return hr; + return hr; } diff --git a/dlls/dmcompos/chordmaptrack.c b/dlls/dmcompos/chordmaptrack.c index 35c64aab4a6..36079ef177a 100644 --- a/dlls/dmcompos/chordmaptrack.c +++ b/dlls/dmcompos/chordmaptrack.c @@ -77,9 +77,7 @@ static ULONG WINAPI chordmap_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -289,11 +287,8 @@ HRESULT create_dmchordmaptrack(REFIID lpcGUID, void **ppobj) IDirectMusicChordMapTrack* track; HRESULT hr; - track = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicChordMapTrack, diff --git a/dlls/dmcompos/composer.c b/dlls/dmcompos/composer.c index 47e12358201..3451f2d4203 100644 --- a/dlls/dmcompos/composer.c +++ b/dlls/dmcompos/composer.c @@ -67,9 +67,7 @@ static ULONG WINAPI IDirectMusicComposerImpl_Release(IDirectMusicComposer *iface TRACE("(%p) ref=%ld\n", This, ref); - if (ref == 0) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -174,11 +172,8 @@ HRESULT create_dmcomposer(REFIID riid, void **ret_iface) IDirectMusicComposerImpl *obj; HRESULT hr; - obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj)); - if (!obj) { - *ret_iface = NULL; - return E_OUTOFMEMORY; - } + *ret_iface = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicComposer_iface.lpVtbl = &dmcomposer_vtbl; obj->ref = 1; diff --git a/dlls/dmcompos/dmobject.c b/dlls/dmcompos/dmobject.c index b526b23d031..07d887a376c 100644 --- a/dlls/dmcompos/dmobject.c +++ b/dlls/dmcompos/dmobject.c @@ -28,7 +28,6 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -375,7 +374,7 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) /* Reads chunk data of the form: DWORD - size of array element element[] - Array of elements - The caller needs to heap_free() the array. + The caller needs to free() the array. */ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, unsigned int *count, DWORD elem_size) @@ -400,10 +399,10 @@ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, *count = (chunk->size - sizeof(DWORD)) / elem_size; size = *count * elem_size; - if (!(*array = heap_alloc(size))) + if (!(*array = malloc(size))) return E_OUTOFMEMORY; if (FAILED(hr = stream_read(stream, *array, size))) { - heap_free(*array); + free(*array); *array = NULL; return hr; } diff --git a/dlls/dmcompos/signposttrack.c b/dlls/dmcompos/signposttrack.c index 23ed67acebe..eccf87a4893 100644 --- a/dlls/dmcompos/signposttrack.c +++ b/dlls/dmcompos/signposttrack.c @@ -77,9 +77,7 @@ static ULONG WINAPI signpost_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -277,11 +275,8 @@ HRESULT create_dmsignposttrack(REFIID lpcGUID, void **ppobj) IDirectMusicSignPostTrack *track; HRESULT hr; - track = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicSignPostTrack, From 8479b3a1dc5e3cde53db700b82dbc0c0ba50946c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 30 Aug 2023 19:56:53 +0200 Subject: [PATCH 0871/1148] dmcompos: Use PARENTSRC with dmusic. (cherry picked from commit 6798b73452f84d31c936cf48417b3df10bfacab0) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmcompos/Makefile.in | 1 + dlls/dmcompos/dmobject.c | 732 -------------------------------------- dlls/dmcompos/dmobject.h | 124 ------- 3 files changed, 1 insertion(+), 856 deletions(-) delete mode 100644 dlls/dmcompos/dmobject.c delete mode 100644 dlls/dmcompos/dmobject.h diff --git a/dlls/dmcompos/Makefile.in b/dlls/dmcompos/Makefile.in index d1e9b5e5b45..af5f2134152 100644 --- a/dlls/dmcompos/Makefile.in +++ b/dlls/dmcompos/Makefile.in @@ -1,5 +1,6 @@ MODULE = dmcompos.dll IMPORTS = dxguid uuid ole32 advapi32 +PARENTSRC = ../dmusic C_SRCS = \ chordmap.c \ diff --git a/dlls/dmcompos/dmobject.c b/dlls/dmcompos/dmobject.c deleted file mode 100644 index 07d887a376c..00000000000 --- a/dlls/dmcompos/dmobject.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.c - * - * Copyright (C) 2003-2004 Rok Mandeljc - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#define COBJMACROS -#include -#include "objbase.h" -#include "dmusici.h" -#include "dmusicf.h" -#include "dmusics.h" -#include "dmobject.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(dmobj); -WINE_DECLARE_DEBUG_CHANNEL(dmfile); - -/* Debugging helpers */ -const char *debugstr_dmguid(const GUID *id) { - unsigned int i; -#define X(guid) { &guid, #guid } - static const struct { - const GUID *guid; - const char *name; - } guids[] = { - /* CLSIDs */ - X(CLSID_AudioVBScript), - X(CLSID_DirectMusic), - X(CLSID_DirectMusicAudioPathConfig), - X(CLSID_DirectMusicAuditionTrack), - X(CLSID_DirectMusicBand), - X(CLSID_DirectMusicBandTrack), - X(CLSID_DirectMusicChordMapTrack), - X(CLSID_DirectMusicChordMap), - X(CLSID_DirectMusicChordTrack), - X(CLSID_DirectMusicCollection), - X(CLSID_DirectMusicCommandTrack), - X(CLSID_DirectMusicComposer), - X(CLSID_DirectMusicContainer), - X(CLSID_DirectMusicGraph), - X(CLSID_DirectMusicLoader), - X(CLSID_DirectMusicLyricsTrack), - X(CLSID_DirectMusicMarkerTrack), - X(CLSID_DirectMusicMelodyFormulationTrack), - X(CLSID_DirectMusicMotifTrack), - X(CLSID_DirectMusicMuteTrack), - X(CLSID_DirectMusicParamControlTrack), - X(CLSID_DirectMusicPatternTrack), - X(CLSID_DirectMusicPerformance), - X(CLSID_DirectMusicScript), - X(CLSID_DirectMusicScriptAutoImpSegment), - X(CLSID_DirectMusicScriptAutoImpPerformance), - X(CLSID_DirectMusicScriptAutoImpSegmentState), - X(CLSID_DirectMusicScriptAutoImpAudioPathConfig), - X(CLSID_DirectMusicScriptAutoImpAudioPath), - X(CLSID_DirectMusicScriptAutoImpSong), - X(CLSID_DirectMusicScriptSourceCodeLoader), - X(CLSID_DirectMusicScriptTrack), - X(CLSID_DirectMusicSection), - X(CLSID_DirectMusicSegment), - X(CLSID_DirectMusicSegmentState), - X(CLSID_DirectMusicSegmentTriggerTrack), - X(CLSID_DirectMusicSegTriggerTrack), - X(CLSID_DirectMusicSeqTrack), - X(CLSID_DirectMusicSignPostTrack), - X(CLSID_DirectMusicSong), - X(CLSID_DirectMusicStyle), - X(CLSID_DirectMusicStyleTrack), - X(CLSID_DirectMusicSynth), - X(CLSID_DirectMusicSynthSink), - X(CLSID_DirectMusicSysExTrack), - X(CLSID_DirectMusicTemplate), - X(CLSID_DirectMusicTempoTrack), - X(CLSID_DirectMusicTimeSigTrack), - X(CLSID_DirectMusicWaveTrack), - X(CLSID_DirectSoundWave), - /* IIDs */ - X(IID_IDirectMusic), - X(IID_IDirectMusic2), - X(IID_IDirectMusic8), - X(IID_IDirectMusicAudioPath), - X(IID_IDirectMusicBand), - X(IID_IDirectMusicBuffer), - X(IID_IDirectMusicChordMap), - X(IID_IDirectMusicCollection), - X(IID_IDirectMusicComposer), - X(IID_IDirectMusicContainer), - X(IID_IDirectMusicDownload), - X(IID_IDirectMusicDownloadedInstrument), - X(IID_IDirectMusicGetLoader), - X(IID_IDirectMusicGraph), - X(IID_IDirectMusicInstrument), - X(IID_IDirectMusicLoader), - X(IID_IDirectMusicLoader8), - X(IID_IDirectMusicObject), - X(IID_IDirectMusicPatternTrack), - X(IID_IDirectMusicPerformance), - X(IID_IDirectMusicPerformance2), - X(IID_IDirectMusicPerformance8), - X(IID_IDirectMusicPort), - X(IID_IDirectMusicPortDownload), - X(IID_IDirectMusicScript), - X(IID_IDirectMusicSegment), - X(IID_IDirectMusicSegment2), - X(IID_IDirectMusicSegment8), - X(IID_IDirectMusicSegmentState), - X(IID_IDirectMusicSegmentState8), - X(IID_IDirectMusicStyle), - X(IID_IDirectMusicStyle8), - X(IID_IDirectMusicSynth), - X(IID_IDirectMusicSynth8), - X(IID_IDirectMusicSynthSink), - X(IID_IDirectMusicThru), - X(IID_IDirectMusicTool), - X(IID_IDirectMusicTool8), - X(IID_IDirectMusicTrack), - X(IID_IDirectMusicTrack8), - X(IID_IUnknown), - X(IID_IPersistStream), - X(IID_IStream), - X(IID_IClassFactory), - /* GUIDs */ - X(GUID_DirectMusicAllTypes), - X(GUID_NOTIFICATION_CHORD), - X(GUID_NOTIFICATION_COMMAND), - X(GUID_NOTIFICATION_MEASUREANDBEAT), - X(GUID_NOTIFICATION_PERFORMANCE), - X(GUID_NOTIFICATION_RECOMPOSE), - X(GUID_NOTIFICATION_SEGMENT), - X(GUID_BandParam), - X(GUID_ChordParam), - X(GUID_CommandParam), - X(GUID_CommandParam2), - X(GUID_CommandParamNext), - X(GUID_IDirectMusicBand), - X(GUID_IDirectMusicChordMap), - X(GUID_IDirectMusicStyle), - X(GUID_MuteParam), - X(GUID_Play_Marker), - X(GUID_RhythmParam), - X(GUID_TempoParam), - X(GUID_TimeSignature), - X(GUID_Valid_Start_Time), - X(GUID_Clear_All_Bands), - X(GUID_ConnectToDLSCollection), - X(GUID_Disable_Auto_Download), - X(GUID_DisableTempo), - X(GUID_DisableTimeSig), - X(GUID_Download), - X(GUID_DownloadToAudioPath), - X(GUID_Enable_Auto_Download), - X(GUID_EnableTempo), - X(GUID_EnableTimeSig), - X(GUID_IgnoreBankSelectForGM), - X(GUID_SeedVariations), - X(GUID_StandardMIDIFile), - X(GUID_Unload), - X(GUID_UnloadFromAudioPath), - X(GUID_Variations), - X(GUID_PerfMasterTempo), - X(GUID_PerfMasterVolume), - X(GUID_PerfMasterGrooveLevel), - X(GUID_PerfAutoDownload), - X(GUID_DefaultGMCollection), - X(GUID_Synth_Default), - X(GUID_Buffer_Reverb), - X(GUID_Buffer_EnvReverb), - X(GUID_Buffer_Stereo), - X(GUID_Buffer_3D_Dry), - X(GUID_Buffer_Mono), - X(GUID_DMUS_PROP_GM_Hardware), - X(GUID_DMUS_PROP_GS_Capable), - X(GUID_DMUS_PROP_GS_Hardware), - X(GUID_DMUS_PROP_DLS1), - X(GUID_DMUS_PROP_DLS2), - X(GUID_DMUS_PROP_Effects), - X(GUID_DMUS_PROP_INSTRUMENT2), - X(GUID_DMUS_PROP_LegacyCaps), - X(GUID_DMUS_PROP_MemorySize), - X(GUID_DMUS_PROP_SampleMemorySize), - X(GUID_DMUS_PROP_SamplePlaybackRate), - X(GUID_DMUS_PROP_SetSynthSink), - X(GUID_DMUS_PROP_SinkUsesDSound), - X(GUID_DMUS_PROP_SynthSink_DSOUND), - X(GUID_DMUS_PROP_SynthSink_WAVE), - X(GUID_DMUS_PROP_Volume), - X(GUID_DMUS_PROP_WavesReverb), - X(GUID_DMUS_PROP_WriteLatency), - X(GUID_DMUS_PROP_WritePeriod), - X(GUID_DMUS_PROP_XG_Capable), - X(GUID_DMUS_PROP_XG_Hardware) - }; -#undef X - - if (!id) - return "(null)"; - - for (i = 0; i < ARRAY_SIZE(guids); i++) - if (IsEqualGUID(id, guids[i].guid)) - return guids[i].name; - - return debugstr_guid(id); -} - -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) -{ - if (!desc || !TRACE_ON(dmfile)) - return; - - TRACE_(dmfile)("DMUS_OBJECTDESC (%p):", desc); - TRACE_(dmfile)(" - dwSize = %lu\n", desc->dwSize); - -#define X(flag) if (desc->dwValidData & flag) TRACE_(dmfile)(#flag " ") - TRACE_(dmfile)(" - dwValidData = %#08lx ( ", desc->dwValidData); - X(DMUS_OBJ_OBJECT); - X(DMUS_OBJ_CLASS); - X(DMUS_OBJ_NAME); - X(DMUS_OBJ_CATEGORY); - X(DMUS_OBJ_FILENAME); - X(DMUS_OBJ_FULLPATH); - X(DMUS_OBJ_URL); - X(DMUS_OBJ_VERSION); - X(DMUS_OBJ_DATE); - X(DMUS_OBJ_LOADED); - X(DMUS_OBJ_MEMORY); - X(DMUS_OBJ_STREAM); - TRACE_(dmfile)(")\n"); -#undef X - - if (desc->dwValidData & DMUS_OBJ_CLASS) - TRACE_(dmfile)(" - guidClass = %s\n", debugstr_dmguid(&desc->guidClass)); - if (desc->dwValidData & DMUS_OBJ_OBJECT) - TRACE_(dmfile)(" - guidObject = %s\n", debugstr_guid(&desc->guidObject)); - - if (desc->dwValidData & DMUS_OBJ_DATE) { - SYSTEMTIME time; - FileTimeToSystemTime(&desc->ftDate, &time); - TRACE_(dmfile)(" - ftDate = \'%04u-%02u-%02u %02u:%02u:%02u\'\n", - time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); - } - if (desc->dwValidData & DMUS_OBJ_VERSION) - TRACE_(dmfile)(" - vVersion = \'%u,%u,%u,%u\'\n", - HIWORD(desc->vVersion.dwVersionMS), LOWORD(desc->vVersion.dwVersionMS), - HIWORD(desc->vVersion.dwVersionLS), LOWORD(desc->vVersion.dwVersionLS)); - if (desc->dwValidData & DMUS_OBJ_NAME) - TRACE_(dmfile)(" - wszName = %s\n", debugstr_w(desc->wszName)); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - TRACE_(dmfile)(" - wszCategory = %s\n", debugstr_w(desc->wszCategory)); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - TRACE_(dmfile)(" - wszFileName = %s\n", debugstr_w(desc->wszFileName)); - if (desc->dwValidData & DMUS_OBJ_MEMORY) - TRACE_(dmfile)(" - llMemLength = 0x%s - pbMemData = %p\n", - wine_dbgstr_longlong(desc->llMemLength), desc->pbMemData); - if (desc->dwValidData & DMUS_OBJ_STREAM) - TRACE_(dmfile)(" - pStream = %p\n", desc->pStream); -} - - -/* RIFF format parsing */ -#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) - -const char *debugstr_chunk(const struct chunk_entry *chunk) -{ - const char *type = ""; - - if (!chunk) - return "(null)"; - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); - return wine_dbg_sprintf("%s chunk, %ssize %lu", debugstr_fourcc(chunk->id), type, chunk->size); -} - -static HRESULT stream_read(IStream *stream, void *data, ULONG size) -{ - ULONG read; - HRESULT hr; - - hr = IStream_Read(stream, data, size, &read); - if (FAILED(hr)) - TRACE_(dmfile)("IStream_Read failed: %#lx\n", hr); - else if (!read && read < size) { - /* All or nothing: Handle a partial read due to end of stream as an error */ - TRACE_(dmfile)("Short read: %lu < %lu\n", read, size); - return E_FAIL; - } - - return hr; -} - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) -{ - static const LARGE_INTEGER zero; - ULONGLONG ck_end = 0, p_end = 0; - HRESULT hr; - - hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); - if (FAILED(hr)) - return hr; - assert(!(chunk->offset.QuadPart & 1)); - if (chunk->parent) { - p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); - if (chunk->offset.QuadPart == p_end) - return S_FALSE; - ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); - if (hr != S_OK) - return hr; - if (chunk->parent) { - ck_end += (chunk->size + 1) & ~1; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { - hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); - if (hr != S_OK) - return hr != S_FALSE ? hr : E_FAIL; - } - - TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); - - return S_OK; -} - -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER end; - - end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; - - return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); -} - -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) -{ - HRESULT hr; - - if (chunk->id) { - hr = stream_skip_chunk(stream, chunk); - if (FAILED(hr)) - return hr; - } - - return stream_get_chunk(stream, chunk); -} - -/* Reads chunk data of the form: - DWORD - size of array element - element[] - Array of elements - The caller needs to free() the array. -*/ -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) -{ - DWORD size; - HRESULT hr; - - *array = NULL; - *count = 0; - - if (chunk->size < sizeof(DWORD)) { - WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); - return E_FAIL; - } - if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) - return hr; - if (size != elem_size) { - WARN_(dmfile)("%s: Array element size mismatch: got %lu, expected %lu\n", - debugstr_chunk(chunk), size, elem_size); - return DMUS_E_UNSUPPORTED_STREAM; - } - - *count = (chunk->size - sizeof(DWORD)) / elem_size; - size = *count * elem_size; - if (!(*array = malloc(size))) - return E_OUTOFMEMORY; - if (FAILED(hr = stream_read(stream, *array, size))) { - free(*array); - *array = NULL; - return hr; - } - - if (chunk->size > size + sizeof(DWORD)) { - WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); - stream_skip_chunk(stream, chunk); - return S_FALSE; - } - return S_OK; -} - -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) -{ - if (chunk->size != size) { - WARN_(dmfile)("Chunk %s (size %lu, offset %s) doesn't contains the expected data size %lu\n", - debugstr_fourcc(chunk->id), chunk->size, - wine_dbgstr_longlong(chunk->offset.QuadPart), size); - return E_FAIL; - } - return stream_read(stream, data, size); -} - -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) -{ - ULONG len; - HRESULT hr; - - hr = IStream_Read(stream, str, min(chunk->size, size), &len); - if (FAILED(hr)) - return hr; - - /* Don't assume the string is properly zero terminated */ - str[min(len, size - 1)] = 0; - - if (len < chunk->size) - return S_FALSE; - return S_OK; -} - - - -/* Generic IDirectMusicObject methods */ -static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IDirectMusicObject_iface); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - - TRACE("(%p/%p)->(%p)\n", iface, This, desc); - - if (!desc) - return E_POINTER; - - memcpy(desc, &This->desc, This->desc.dwSize); - - return S_OK; -} - -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - HRESULT ret = S_OK; - - TRACE("(%p, %p)\n", iface, desc); - - if (!desc) - return E_POINTER; - - /* Immutable property */ - if (desc->dwValidData & DMUS_OBJ_CLASS) - { - desc->dwValidData &= ~DMUS_OBJ_CLASS; - ret = S_FALSE; - } - /* Set only valid fields */ - if (desc->dwValidData & DMUS_OBJ_OBJECT) - This->desc.guidObject = desc->guidObject; - if (desc->dwValidData & DMUS_OBJ_NAME) - lstrcpynW(This->desc.wszName, desc->wszName, DMUS_MAX_NAME); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - lstrcpynW(This->desc.wszCategory, desc->wszCategory, DMUS_MAX_CATEGORY); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - lstrcpynW(This->desc.wszFileName, desc->wszFileName, DMUS_MAX_FILENAME); - if (desc->dwValidData & DMUS_OBJ_VERSION) - This->desc.vVersion = desc->vVersion; - if (desc->dwValidData & DMUS_OBJ_DATE) - This->desc.ftDate = desc->ftDate; - if (desc->dwValidData & DMUS_OBJ_MEMORY) { - This->desc.llMemLength = desc->llMemLength; - memcpy(This->desc.pbMemData, desc->pbMemData, desc->llMemLength); - } - if (desc->dwValidData & DMUS_OBJ_STREAM) - IStream_Clone(desc->pStream, &This->desc.pStream); - - This->desc.dwValidData |= desc->dwValidData; - - return ret; -} - -/* Helper for IDirectMusicObject::ParseDescriptor */ -static inline void info_get_name(IStream *stream, const struct chunk_entry *info, - DMUS_OBJECTDESC *desc) -{ - struct chunk_entry chunk = {.parent = info}; - char name[DMUS_MAX_NAME]; - ULONG len; - HRESULT hr = E_FAIL; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == mmioFOURCC('I','N','A','M')) - hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); - - if (SUCCEEDED(hr)) { - len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); - desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; - desc->dwValidData |= DMUS_OBJ_NAME; - } -} - -static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, - DMUS_OBJECTDESC *desc, BOOL inam) -{ - struct chunk_entry chunk = {.parent = unfo}; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) - if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; -} - -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) -{ - struct chunk_entry chunk = {.parent = riff}; - HRESULT hr; - - TRACE("Looking for %#lx in %p: %s\n", supported, stream, debugstr_chunk(riff)); - - desc->dwValidData = 0; - desc->dwSize = sizeof(*desc); - - while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { - switch (chunk.id) { - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; - break; - case DMUS_FOURCC_DATE_CHUNK: - if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, - &desc->ftDate, sizeof(desc->ftDate)) == S_OK) - desc->dwValidData |= DMUS_OBJ_DATE; - break; - case DMUS_FOURCC_FILE_CHUNK: - if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_FILENAME; - break; - case DMUS_FOURCC_GUID_CHUNK: - if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, - &desc->guidObject, sizeof(desc->guidObject)) == S_OK) - desc->dwValidData |= DMUS_OBJ_OBJECT; - break; - case DMUS_FOURCC_NAME_CHUNK: - if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; - break; - case DMUS_FOURCC_VERSION_CHUNK: - if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, - &desc->vVersion, sizeof(desc->vVersion)) == S_OK) - desc->dwValidData |= DMUS_OBJ_VERSION; - break; - case FOURCC_LIST: - if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) - unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); - else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) - info_get_name(stream, &chunk, desc); - break; - } - } - TRACE("Found %#lx\n", desc->dwValidData); - - return hr; -} - -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) -{ - struct chunk_entry chunk = {.parent = list}; - IDirectMusicGetLoader *getloader; - IDirectMusicLoader *loader; - DMUS_OBJECTDESC desc; - DMUS_IO_REFERENCE reference; - HRESULT hr; - - if (FAILED(hr = stream_next_chunk(stream, &chunk))) - return hr; - if (chunk.id != DMUS_FOURCC_REF_CHUNK) - return DMUS_E_UNSUPPORTED_STREAM; - - if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { - WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); - return hr; - } - TRACE("REFERENCE guidClassID %s, dwValidData %#lx\n", debugstr_dmguid(&reference.guidClassID), - reference.dwValidData); - - if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) - return hr; - desc.guidClass = reference.guidClassID; - desc.dwValidData |= DMUS_OBJ_CLASS; - dump_DMUS_OBJECTDESC(&desc); - - if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) - return hr; - hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); - IDirectMusicGetLoader_Release(getloader); - if (FAILED(hr)) - return hr; - - hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); - IDirectMusicLoader_Release(loader); - - return hr; -} - -/* Generic IPersistStream methods */ -static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IPersistStream_iface); -} - -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - - TRACE("(%p, %p)\n", This, class); - - if (!class) - return E_POINTER; - - *class = This->desc.guidClass; - - return S_OK; -} - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - TRACE("(%p, %p): method not implemented\n", iface, class); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) -{ - TRACE("(%p): method not implemented, always returning S_FALSE\n", iface); - return S_FALSE; -} - -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) -{ - TRACE("(%p, %p, %d): method not implemented\n", iface, stream, clear_dirty); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *size) -{ - TRACE("(%p, %p): method not implemented\n", iface, size); - return E_NOTIMPL; -} - - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) -{ - dmobj->outer_unk = outer_unk; - dmobj->desc.dwSize = sizeof(dmobj->desc); - dmobj->desc.dwValidData = DMUS_OBJ_CLASS; - dmobj->desc.guidClass = *class; -} diff --git a/dlls/dmcompos/dmobject.h b/dlls/dmcompos/dmobject.h deleted file mode 100644 index 772be015c80..00000000000 --- a/dlls/dmcompos/dmobject.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.h - * - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "wine/debug.h" - -/* RIFF stream parsing */ -struct chunk_entry; -struct chunk_entry { - FOURCC id; - DWORD size; - FOURCC type; /* valid only for LIST and RIFF chunks */ - ULARGE_INTEGER offset; /* chunk offset from start of stream */ - const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ -}; - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); - -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size); -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size); -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size); - -static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - offset.QuadPart += sizeof(FOURCC); - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - -static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart; - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - - -/* IDirectMusicObject base object */ -struct dmobject { - IDirectMusicObject IDirectMusicObject_iface; - IPersistStream IPersistStream_iface; - IUnknown *outer_unk; - DMUS_OBJECTDESC desc; -}; - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); - -/* Generic IDirectMusicObject methods */ -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); - -/* Helper for IDirectMusicObject::ParseDescriptor */ -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported); -/* Additional supported flags for dmobj_parsedescriptor. - DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ - -/* 'DMRF' (reference list) helper */ -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj); - -/* Generic IPersistStream methods */ -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class); -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty); -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size); - -/* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk); -const char *debugstr_dmguid(const GUID *id); -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); - -static inline const char *debugstr_fourcc(DWORD fourcc) -{ - if (!fourcc) return "''"; - return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), - (char)(fourcc >> 16), (char)(fourcc >> 24)); -} From 10b5af4c2749820b4289294901e274d393d37e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 22 Aug 2023 17:41:58 +0200 Subject: [PATCH 0872/1148] dmloader: Always return S_FALSE from DllCanUnloadNow. (cherry picked from commit 30e5892c98cc3b7aa0754b542cb19e14d28ac5c2) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/container.c | 3 --- dlls/dmloader/dmloader_main.c | 21 --------------------- dlls/dmloader/dmloader_private.h | 5 ----- dlls/dmloader/loader.c | 3 --- 4 files changed, 32 deletions(-) diff --git a/dlls/dmloader/container.c b/dlls/dmloader/container.c index f2b284e1cb3..747c2a07341 100644 --- a/dlls/dmloader/container.c +++ b/dlls/dmloader/container.c @@ -137,7 +137,6 @@ static ULONG WINAPI IDirectMusicContainerImpl_Release(IDirectMusicContainer *ifa if (This->pStream) destroy_dmcontainer(This); HeapFree(GetProcessHeap(), 0, This); - unlock_module(); } return ref; @@ -666,8 +665,6 @@ HRESULT create_dmcontainer(REFIID lpcGUID, void **ppobj) obj->pContainedObjects = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(struct list)); list_init (obj->pContainedObjects); - lock_module(); - hr = IDirectMusicContainer_QueryInterface(&obj->IDirectMusicContainer_iface, lpcGUID, ppobj); IDirectMusicContainer_Release(&obj->IDirectMusicContainer_iface); diff --git a/dlls/dmloader/dmloader_main.c b/dlls/dmloader/dmloader_main.c index 512e6102d86..96d841e2b2d 100644 --- a/dlls/dmloader/dmloader_main.c +++ b/dlls/dmloader/dmloader_main.c @@ -36,8 +36,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmloader); -LONG module_ref = 0; - typedef struct { IClassFactory IClassFactory_iface; HRESULT (*fnCreateInstance)(REFIID riid, void **ppv); @@ -73,15 +71,11 @@ static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID r static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) { - lock_module(); - return 2; /* non-heap based object */ } static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) { - unlock_module(); - return 1; /* non-heap based object */ } @@ -103,12 +97,6 @@ static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock) { TRACE("(%d)\n", dolock); - - if (dolock) - lock_module(); - else - unlock_module(); - return S_OK; } @@ -123,15 +111,6 @@ static const IClassFactoryVtbl classfactory_vtbl = { static IClassFactoryImpl dm_loader_CF = {{&classfactory_vtbl}, create_dmloader}; static IClassFactoryImpl dm_container_CF = {{&classfactory_vtbl}, create_dmcontainer}; -/****************************************************************** - * DllCanUnloadNow (DMLOADER.@) - */ -HRESULT WINAPI DllCanUnloadNow (void) -{ - TRACE("() ref=%ld\n", module_ref); - - return module_ref ? S_FALSE : S_OK; -} /****************************************************************** * DllGetClassObject (DMLOADER.@) diff --git a/dlls/dmloader/dmloader_private.h b/dlls/dmloader/dmloader_private.h index 204c9689ffb..0aea1ba8499 100644 --- a/dlls/dmloader/dmloader_private.h +++ b/dlls/dmloader/dmloader_private.h @@ -44,11 +44,6 @@ #define ICOM_THIS_MULTI(impl,field,iface) impl* const This=(impl*)((char*)(iface) - offsetof(impl,field)) -/* dmloader.dll global (for DllCanUnloadNow) */ -extern LONG module_ref; -static inline void lock_module(void) { InterlockedIncrement( &module_ref ); } -static inline void unlock_module(void) { InterlockedDecrement( &module_ref ); } - /***************************************************************************** * Interfaces */ diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index 6c4b29ab5ea..f303f93baaa 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -157,7 +157,6 @@ static ULONG WINAPI IDirectMusicLoaderImpl_Release(IDirectMusicLoader8 *iface) for (i = 0; i < ARRAY_SIZE(classes); i++) HeapFree(GetProcessHeap(), 0, This->search_paths[i]); HeapFree(GetProcessHeap(), 0, This); - unlock_module(); } return ref; @@ -940,7 +939,5 @@ HRESULT create_dmloader(REFIID lpcGUID, void **ppobj) dls->bInvalidDefaultDLS = TRUE; } - lock_module(); - return IDirectMusicLoader_QueryInterface(&obj->IDirectMusicLoader8_iface, lpcGUID, ppobj); } From 5a24dac427dfb206df69d660ad0889c703362d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 09:22:04 +0200 Subject: [PATCH 0873/1148] dmloader: Use CRT allocation functions. (cherry picked from commit 307b13bcf56f28f8e891c1b8d1bbf26d4ec4d2df) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/container.c | 33 +++++++++++++++------------------ dlls/dmloader/dmobject.c | 7 +++---- dlls/dmloader/loader.c | 19 ++++++++----------- dlls/dmloader/loaderstream.c | 28 +++++++++++----------------- 4 files changed, 37 insertions(+), 50 deletions(-) diff --git a/dlls/dmloader/container.c b/dlls/dmloader/container.c index 747c2a07341..66049e68907 100644 --- a/dlls/dmloader/container.c +++ b/dlls/dmloader/container.c @@ -136,7 +136,7 @@ static ULONG WINAPI IDirectMusicContainerImpl_Release(IDirectMusicContainer *ifa if (!ref) { if (This->pStream) destroy_dmcontainer(This); - HeapFree(GetProcessHeap(), 0, This); + free(This); } return ref; @@ -388,7 +388,7 @@ static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, IStream *pS case DMUS_FOURCC_CONTAINED_OBJECT_LIST: { LPWINE_CONTAINER_ENTRY pNewEntry; TRACE_(dmfile)(": contained object list\n"); - pNewEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_CONTAINER_ENTRY)); + pNewEntry = calloc(1, sizeof(*pNewEntry)); DM_STRUCT_INIT(&pNewEntry->Desc); do { IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); @@ -397,7 +397,7 @@ static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, IStream *pS switch (Chunk.fccID) { case DMUS_FOURCC_CONTAINED_ALIAS_CHUNK: { TRACE_(dmfile)(": alias chunk\n"); - pNewEntry->wszAlias = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, Chunk.dwSize); + pNewEntry->wszAlias = calloc(1, Chunk.dwSize); IStream_Read (pStm, pNewEntry->wszAlias, Chunk.dwSize, NULL); TRACE_(dmfile)(": alias: %s\n", debugstr_w(pNewEntry->wszAlias)); break; @@ -649,24 +649,21 @@ static const IPersistStreamVtbl persiststream_vtbl = { HRESULT create_dmcontainer(REFIID lpcGUID, void **ppobj) { IDirectMusicContainerImpl* obj; - HRESULT hr; + HRESULT hr; - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicContainerImpl)); - if (NULL == obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } - obj->IDirectMusicContainer_iface.lpVtbl = &dmcontainer_vtbl; - obj->ref = 1; - dmobject_init(&obj->dmobj, &CLSID_DirectMusicContainer, - (IUnknown*)&obj->IDirectMusicContainer_iface); - obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; - obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; + obj->IDirectMusicContainer_iface.lpVtbl = &dmcontainer_vtbl; + obj->ref = 1; + dmobject_init(&obj->dmobj, &CLSID_DirectMusicContainer, + (IUnknown*)&obj->IDirectMusicContainer_iface); + obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; + obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; obj->pContainedObjects = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(struct list)); list_init (obj->pContainedObjects); - hr = IDirectMusicContainer_QueryInterface(&obj->IDirectMusicContainer_iface, lpcGUID, ppobj); - IDirectMusicContainer_Release(&obj->IDirectMusicContainer_iface); + hr = IDirectMusicContainer_QueryInterface(&obj->IDirectMusicContainer_iface, lpcGUID, ppobj); + IDirectMusicContainer_Release(&obj->IDirectMusicContainer_iface); - return hr; + return hr; } diff --git a/dlls/dmloader/dmobject.c b/dlls/dmloader/dmobject.c index b526b23d031..07d887a376c 100644 --- a/dlls/dmloader/dmobject.c +++ b/dlls/dmloader/dmobject.c @@ -28,7 +28,6 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -375,7 +374,7 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) /* Reads chunk data of the form: DWORD - size of array element element[] - Array of elements - The caller needs to heap_free() the array. + The caller needs to free() the array. */ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, unsigned int *count, DWORD elem_size) @@ -400,10 +399,10 @@ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, *count = (chunk->size - sizeof(DWORD)) / elem_size; size = *count * elem_size; - if (!(*array = heap_alloc(size))) + if (!(*array = malloc(size))) return E_OUTOFMEMORY; if (FAILED(hr = stream_read(stream, *array, size))) { - heap_free(*array); + free(*array); *array = NULL; return hr; } diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index f303f93baaa..7b1fb8d04a5 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -155,8 +155,8 @@ static ULONG WINAPI IDirectMusicLoaderImpl_Release(IDirectMusicLoader8 *iface) IDirectMusicLoader8_ClearCache(iface, &GUID_DirectMusicAllTypes); for (i = 0; i < ARRAY_SIZE(classes); i++) - HeapFree(GetProcessHeap(), 0, This->search_paths[i]); - HeapFree(GetProcessHeap(), 0, This); + free(This->search_paths[i]); + free(This); } return ref; @@ -427,7 +427,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_GetObject(IDirectMusicLoader8 *ifac bCache = is_cache_enabled(This, &pDesc->guidClass); if (bCache) { if (!pObjectEntry) { - pObjectEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(*pObjectEntry)); + pObjectEntry = calloc(1, sizeof(*pObjectEntry)); DM_STRUCT_INIT(&pObjectEntry->Desc); DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); pObjectEntry->pObject = pObject; @@ -566,7 +566,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_SetObject(IDirectMusicLoader8 *ifac TRACE(": adding alias entry with following info:\n"); if (TRACE_ON(dmloader)) dump_DMUS_OBJECTDESC(pDesc); - pNewEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(*pNewEntry)); + pNewEntry = calloc(1, sizeof(*pNewEntry)); /* use this function instead of pure memcpy due to streams (memcpy just copies pointer), which is basically used further by app that called SetDescriptor... better safety than exception */ DMUSIC_CopyDescriptor (&pNewEntry->Desc, pDesc); @@ -601,7 +601,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_SetSearchDirectory(IDirectMusicLoad return S_OK; if (!This->search_paths[index]) - This->search_paths[index] = HeapAlloc(GetProcessHeap(), 0, MAX_PATH); + This->search_paths[index] = malloc(MAX_PATH); else if (!wcsncmp(This->search_paths[index], path, MAX_PATH)) return S_FALSE; @@ -743,7 +743,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_ClearCache(IDirectMusicLoader8 *ifa /* basically, wrap to ReleaseObject for each object found */ IDirectMusicLoader8_ReleaseObject(iface, obj->pObject); list_remove(&obj->entry); - HeapFree(GetProcessHeap(), 0, obj); + free(obj); } } @@ -910,11 +910,8 @@ HRESULT create_dmloader(REFIID lpcGUID, void **ppobj) struct list *pEntry; TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID), ppobj); - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoaderImpl)); - if (NULL == obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicLoader8_iface.lpVtbl = &DirectMusicLoader_Loader_Vtbl; obj->ref = 0; /* Will be inited with QueryInterface */ list_init(&obj->cache); diff --git a/dlls/dmloader/loaderstream.c b/dlls/dmloader/loaderstream.c index 38862c0fec7..c2c5105a5e9 100644 --- a/dlls/dmloader/loaderstream.c +++ b/dlls/dmloader/loaderstream.c @@ -122,7 +122,7 @@ static ULONG WINAPI IDirectMusicLoaderFileStream_IStream_Release (LPSTREAM iface if (dwRef == 0) { if (This->hFile) IDirectMusicLoaderFileStream_Detach (iface); - HeapFree (GetProcessHeap(), 0, This); + free(This); } return dwRef; @@ -292,11 +292,9 @@ HRESULT DMUSIC_CreateDirectMusicLoaderFileStream (void** ppobj) { IDirectMusicLoaderFileStream *obj; TRACE("(%p)\n", ppobj); - obj = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoaderFileStream)); - if (NULL == obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->StreamVtbl = &DirectMusicLoaderFileStream_Stream_Vtbl; obj->GetLoaderVtbl = &DirectMusicLoaderFileStream_GetLoader_Vtbl; obj->dwRef = 0; /* will be inited with QueryInterface */ @@ -369,7 +367,7 @@ static ULONG WINAPI IDirectMusicLoaderResourceStream_IStream_Release (LPSTREAM i TRACE("(%p): ReleaseRef to %ld\n", This, dwRef); if (dwRef == 0) { IDirectMusicLoaderResourceStream_Detach (iface); - HeapFree (GetProcessHeap(), 0, This); + free(This); } return dwRef; @@ -549,11 +547,9 @@ HRESULT DMUSIC_CreateDirectMusicLoaderResourceStream (void** ppobj) { IDirectMusicLoaderResourceStream *obj; TRACE("(%p)\n", ppobj); - obj = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoaderResourceStream)); - if (NULL == obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->StreamVtbl = &DirectMusicLoaderResourceStream_Stream_Vtbl; obj->GetLoaderVtbl = &DirectMusicLoaderResourceStream_GetLoader_Vtbl; obj->dwRef = 0; /* will be inited with QueryInterface */ @@ -801,11 +797,9 @@ HRESULT DMUSIC_CreateDirectMusicLoaderGenericStream (void** ppobj) { IDirectMusicLoaderGenericStream *obj; TRACE("(%p)\n", ppobj); - obj = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoaderGenericStream)); - if (NULL == obj) { - *ppobj = NULL; - return E_OUTOFMEMORY; - } + + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->StreamVtbl = &DirectMusicLoaderGenericStream_Stream_Vtbl; obj->GetLoaderVtbl = &DirectMusicLoaderGenericStream_GetLoader_Vtbl; obj->dwRef = 0; /* will be inited with QueryInterface */ From b67fc987a8483b64f5dedd1ebc739015192503ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 30 Aug 2023 19:55:51 +0200 Subject: [PATCH 0874/1148] dmloader: Use PARENTSRC with dmusic. (cherry picked from commit da8a8bbf9a7747f99d50beecfeea012067b2925b) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/Makefile.in | 1 + dlls/dmloader/dmobject.c | 732 -------------------------------------- dlls/dmloader/dmobject.h | 124 ------- 3 files changed, 1 insertion(+), 856 deletions(-) delete mode 100644 dlls/dmloader/dmobject.c delete mode 100644 dlls/dmloader/dmobject.h diff --git a/dlls/dmloader/Makefile.in b/dlls/dmloader/Makefile.in index 4aa80c6dfb3..a8d3698ab6c 100644 --- a/dlls/dmloader/Makefile.in +++ b/dlls/dmloader/Makefile.in @@ -1,5 +1,6 @@ MODULE = dmloader.dll IMPORTS = dxguid uuid ole32 advapi32 +PARENTSRC = ../dmusic C_SRCS = \ container.c \ diff --git a/dlls/dmloader/dmobject.c b/dlls/dmloader/dmobject.c deleted file mode 100644 index 07d887a376c..00000000000 --- a/dlls/dmloader/dmobject.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.c - * - * Copyright (C) 2003-2004 Rok Mandeljc - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#define COBJMACROS -#include -#include "objbase.h" -#include "dmusici.h" -#include "dmusicf.h" -#include "dmusics.h" -#include "dmobject.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(dmobj); -WINE_DECLARE_DEBUG_CHANNEL(dmfile); - -/* Debugging helpers */ -const char *debugstr_dmguid(const GUID *id) { - unsigned int i; -#define X(guid) { &guid, #guid } - static const struct { - const GUID *guid; - const char *name; - } guids[] = { - /* CLSIDs */ - X(CLSID_AudioVBScript), - X(CLSID_DirectMusic), - X(CLSID_DirectMusicAudioPathConfig), - X(CLSID_DirectMusicAuditionTrack), - X(CLSID_DirectMusicBand), - X(CLSID_DirectMusicBandTrack), - X(CLSID_DirectMusicChordMapTrack), - X(CLSID_DirectMusicChordMap), - X(CLSID_DirectMusicChordTrack), - X(CLSID_DirectMusicCollection), - X(CLSID_DirectMusicCommandTrack), - X(CLSID_DirectMusicComposer), - X(CLSID_DirectMusicContainer), - X(CLSID_DirectMusicGraph), - X(CLSID_DirectMusicLoader), - X(CLSID_DirectMusicLyricsTrack), - X(CLSID_DirectMusicMarkerTrack), - X(CLSID_DirectMusicMelodyFormulationTrack), - X(CLSID_DirectMusicMotifTrack), - X(CLSID_DirectMusicMuteTrack), - X(CLSID_DirectMusicParamControlTrack), - X(CLSID_DirectMusicPatternTrack), - X(CLSID_DirectMusicPerformance), - X(CLSID_DirectMusicScript), - X(CLSID_DirectMusicScriptAutoImpSegment), - X(CLSID_DirectMusicScriptAutoImpPerformance), - X(CLSID_DirectMusicScriptAutoImpSegmentState), - X(CLSID_DirectMusicScriptAutoImpAudioPathConfig), - X(CLSID_DirectMusicScriptAutoImpAudioPath), - X(CLSID_DirectMusicScriptAutoImpSong), - X(CLSID_DirectMusicScriptSourceCodeLoader), - X(CLSID_DirectMusicScriptTrack), - X(CLSID_DirectMusicSection), - X(CLSID_DirectMusicSegment), - X(CLSID_DirectMusicSegmentState), - X(CLSID_DirectMusicSegmentTriggerTrack), - X(CLSID_DirectMusicSegTriggerTrack), - X(CLSID_DirectMusicSeqTrack), - X(CLSID_DirectMusicSignPostTrack), - X(CLSID_DirectMusicSong), - X(CLSID_DirectMusicStyle), - X(CLSID_DirectMusicStyleTrack), - X(CLSID_DirectMusicSynth), - X(CLSID_DirectMusicSynthSink), - X(CLSID_DirectMusicSysExTrack), - X(CLSID_DirectMusicTemplate), - X(CLSID_DirectMusicTempoTrack), - X(CLSID_DirectMusicTimeSigTrack), - X(CLSID_DirectMusicWaveTrack), - X(CLSID_DirectSoundWave), - /* IIDs */ - X(IID_IDirectMusic), - X(IID_IDirectMusic2), - X(IID_IDirectMusic8), - X(IID_IDirectMusicAudioPath), - X(IID_IDirectMusicBand), - X(IID_IDirectMusicBuffer), - X(IID_IDirectMusicChordMap), - X(IID_IDirectMusicCollection), - X(IID_IDirectMusicComposer), - X(IID_IDirectMusicContainer), - X(IID_IDirectMusicDownload), - X(IID_IDirectMusicDownloadedInstrument), - X(IID_IDirectMusicGetLoader), - X(IID_IDirectMusicGraph), - X(IID_IDirectMusicInstrument), - X(IID_IDirectMusicLoader), - X(IID_IDirectMusicLoader8), - X(IID_IDirectMusicObject), - X(IID_IDirectMusicPatternTrack), - X(IID_IDirectMusicPerformance), - X(IID_IDirectMusicPerformance2), - X(IID_IDirectMusicPerformance8), - X(IID_IDirectMusicPort), - X(IID_IDirectMusicPortDownload), - X(IID_IDirectMusicScript), - X(IID_IDirectMusicSegment), - X(IID_IDirectMusicSegment2), - X(IID_IDirectMusicSegment8), - X(IID_IDirectMusicSegmentState), - X(IID_IDirectMusicSegmentState8), - X(IID_IDirectMusicStyle), - X(IID_IDirectMusicStyle8), - X(IID_IDirectMusicSynth), - X(IID_IDirectMusicSynth8), - X(IID_IDirectMusicSynthSink), - X(IID_IDirectMusicThru), - X(IID_IDirectMusicTool), - X(IID_IDirectMusicTool8), - X(IID_IDirectMusicTrack), - X(IID_IDirectMusicTrack8), - X(IID_IUnknown), - X(IID_IPersistStream), - X(IID_IStream), - X(IID_IClassFactory), - /* GUIDs */ - X(GUID_DirectMusicAllTypes), - X(GUID_NOTIFICATION_CHORD), - X(GUID_NOTIFICATION_COMMAND), - X(GUID_NOTIFICATION_MEASUREANDBEAT), - X(GUID_NOTIFICATION_PERFORMANCE), - X(GUID_NOTIFICATION_RECOMPOSE), - X(GUID_NOTIFICATION_SEGMENT), - X(GUID_BandParam), - X(GUID_ChordParam), - X(GUID_CommandParam), - X(GUID_CommandParam2), - X(GUID_CommandParamNext), - X(GUID_IDirectMusicBand), - X(GUID_IDirectMusicChordMap), - X(GUID_IDirectMusicStyle), - X(GUID_MuteParam), - X(GUID_Play_Marker), - X(GUID_RhythmParam), - X(GUID_TempoParam), - X(GUID_TimeSignature), - X(GUID_Valid_Start_Time), - X(GUID_Clear_All_Bands), - X(GUID_ConnectToDLSCollection), - X(GUID_Disable_Auto_Download), - X(GUID_DisableTempo), - X(GUID_DisableTimeSig), - X(GUID_Download), - X(GUID_DownloadToAudioPath), - X(GUID_Enable_Auto_Download), - X(GUID_EnableTempo), - X(GUID_EnableTimeSig), - X(GUID_IgnoreBankSelectForGM), - X(GUID_SeedVariations), - X(GUID_StandardMIDIFile), - X(GUID_Unload), - X(GUID_UnloadFromAudioPath), - X(GUID_Variations), - X(GUID_PerfMasterTempo), - X(GUID_PerfMasterVolume), - X(GUID_PerfMasterGrooveLevel), - X(GUID_PerfAutoDownload), - X(GUID_DefaultGMCollection), - X(GUID_Synth_Default), - X(GUID_Buffer_Reverb), - X(GUID_Buffer_EnvReverb), - X(GUID_Buffer_Stereo), - X(GUID_Buffer_3D_Dry), - X(GUID_Buffer_Mono), - X(GUID_DMUS_PROP_GM_Hardware), - X(GUID_DMUS_PROP_GS_Capable), - X(GUID_DMUS_PROP_GS_Hardware), - X(GUID_DMUS_PROP_DLS1), - X(GUID_DMUS_PROP_DLS2), - X(GUID_DMUS_PROP_Effects), - X(GUID_DMUS_PROP_INSTRUMENT2), - X(GUID_DMUS_PROP_LegacyCaps), - X(GUID_DMUS_PROP_MemorySize), - X(GUID_DMUS_PROP_SampleMemorySize), - X(GUID_DMUS_PROP_SamplePlaybackRate), - X(GUID_DMUS_PROP_SetSynthSink), - X(GUID_DMUS_PROP_SinkUsesDSound), - X(GUID_DMUS_PROP_SynthSink_DSOUND), - X(GUID_DMUS_PROP_SynthSink_WAVE), - X(GUID_DMUS_PROP_Volume), - X(GUID_DMUS_PROP_WavesReverb), - X(GUID_DMUS_PROP_WriteLatency), - X(GUID_DMUS_PROP_WritePeriod), - X(GUID_DMUS_PROP_XG_Capable), - X(GUID_DMUS_PROP_XG_Hardware) - }; -#undef X - - if (!id) - return "(null)"; - - for (i = 0; i < ARRAY_SIZE(guids); i++) - if (IsEqualGUID(id, guids[i].guid)) - return guids[i].name; - - return debugstr_guid(id); -} - -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) -{ - if (!desc || !TRACE_ON(dmfile)) - return; - - TRACE_(dmfile)("DMUS_OBJECTDESC (%p):", desc); - TRACE_(dmfile)(" - dwSize = %lu\n", desc->dwSize); - -#define X(flag) if (desc->dwValidData & flag) TRACE_(dmfile)(#flag " ") - TRACE_(dmfile)(" - dwValidData = %#08lx ( ", desc->dwValidData); - X(DMUS_OBJ_OBJECT); - X(DMUS_OBJ_CLASS); - X(DMUS_OBJ_NAME); - X(DMUS_OBJ_CATEGORY); - X(DMUS_OBJ_FILENAME); - X(DMUS_OBJ_FULLPATH); - X(DMUS_OBJ_URL); - X(DMUS_OBJ_VERSION); - X(DMUS_OBJ_DATE); - X(DMUS_OBJ_LOADED); - X(DMUS_OBJ_MEMORY); - X(DMUS_OBJ_STREAM); - TRACE_(dmfile)(")\n"); -#undef X - - if (desc->dwValidData & DMUS_OBJ_CLASS) - TRACE_(dmfile)(" - guidClass = %s\n", debugstr_dmguid(&desc->guidClass)); - if (desc->dwValidData & DMUS_OBJ_OBJECT) - TRACE_(dmfile)(" - guidObject = %s\n", debugstr_guid(&desc->guidObject)); - - if (desc->dwValidData & DMUS_OBJ_DATE) { - SYSTEMTIME time; - FileTimeToSystemTime(&desc->ftDate, &time); - TRACE_(dmfile)(" - ftDate = \'%04u-%02u-%02u %02u:%02u:%02u\'\n", - time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); - } - if (desc->dwValidData & DMUS_OBJ_VERSION) - TRACE_(dmfile)(" - vVersion = \'%u,%u,%u,%u\'\n", - HIWORD(desc->vVersion.dwVersionMS), LOWORD(desc->vVersion.dwVersionMS), - HIWORD(desc->vVersion.dwVersionLS), LOWORD(desc->vVersion.dwVersionLS)); - if (desc->dwValidData & DMUS_OBJ_NAME) - TRACE_(dmfile)(" - wszName = %s\n", debugstr_w(desc->wszName)); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - TRACE_(dmfile)(" - wszCategory = %s\n", debugstr_w(desc->wszCategory)); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - TRACE_(dmfile)(" - wszFileName = %s\n", debugstr_w(desc->wszFileName)); - if (desc->dwValidData & DMUS_OBJ_MEMORY) - TRACE_(dmfile)(" - llMemLength = 0x%s - pbMemData = %p\n", - wine_dbgstr_longlong(desc->llMemLength), desc->pbMemData); - if (desc->dwValidData & DMUS_OBJ_STREAM) - TRACE_(dmfile)(" - pStream = %p\n", desc->pStream); -} - - -/* RIFF format parsing */ -#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) - -const char *debugstr_chunk(const struct chunk_entry *chunk) -{ - const char *type = ""; - - if (!chunk) - return "(null)"; - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); - return wine_dbg_sprintf("%s chunk, %ssize %lu", debugstr_fourcc(chunk->id), type, chunk->size); -} - -static HRESULT stream_read(IStream *stream, void *data, ULONG size) -{ - ULONG read; - HRESULT hr; - - hr = IStream_Read(stream, data, size, &read); - if (FAILED(hr)) - TRACE_(dmfile)("IStream_Read failed: %#lx\n", hr); - else if (!read && read < size) { - /* All or nothing: Handle a partial read due to end of stream as an error */ - TRACE_(dmfile)("Short read: %lu < %lu\n", read, size); - return E_FAIL; - } - - return hr; -} - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) -{ - static const LARGE_INTEGER zero; - ULONGLONG ck_end = 0, p_end = 0; - HRESULT hr; - - hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); - if (FAILED(hr)) - return hr; - assert(!(chunk->offset.QuadPart & 1)); - if (chunk->parent) { - p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); - if (chunk->offset.QuadPart == p_end) - return S_FALSE; - ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); - if (hr != S_OK) - return hr; - if (chunk->parent) { - ck_end += (chunk->size + 1) & ~1; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { - hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); - if (hr != S_OK) - return hr != S_FALSE ? hr : E_FAIL; - } - - TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); - - return S_OK; -} - -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER end; - - end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; - - return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); -} - -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) -{ - HRESULT hr; - - if (chunk->id) { - hr = stream_skip_chunk(stream, chunk); - if (FAILED(hr)) - return hr; - } - - return stream_get_chunk(stream, chunk); -} - -/* Reads chunk data of the form: - DWORD - size of array element - element[] - Array of elements - The caller needs to free() the array. -*/ -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) -{ - DWORD size; - HRESULT hr; - - *array = NULL; - *count = 0; - - if (chunk->size < sizeof(DWORD)) { - WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); - return E_FAIL; - } - if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) - return hr; - if (size != elem_size) { - WARN_(dmfile)("%s: Array element size mismatch: got %lu, expected %lu\n", - debugstr_chunk(chunk), size, elem_size); - return DMUS_E_UNSUPPORTED_STREAM; - } - - *count = (chunk->size - sizeof(DWORD)) / elem_size; - size = *count * elem_size; - if (!(*array = malloc(size))) - return E_OUTOFMEMORY; - if (FAILED(hr = stream_read(stream, *array, size))) { - free(*array); - *array = NULL; - return hr; - } - - if (chunk->size > size + sizeof(DWORD)) { - WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); - stream_skip_chunk(stream, chunk); - return S_FALSE; - } - return S_OK; -} - -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) -{ - if (chunk->size != size) { - WARN_(dmfile)("Chunk %s (size %lu, offset %s) doesn't contains the expected data size %lu\n", - debugstr_fourcc(chunk->id), chunk->size, - wine_dbgstr_longlong(chunk->offset.QuadPart), size); - return E_FAIL; - } - return stream_read(stream, data, size); -} - -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) -{ - ULONG len; - HRESULT hr; - - hr = IStream_Read(stream, str, min(chunk->size, size), &len); - if (FAILED(hr)) - return hr; - - /* Don't assume the string is properly zero terminated */ - str[min(len, size - 1)] = 0; - - if (len < chunk->size) - return S_FALSE; - return S_OK; -} - - - -/* Generic IDirectMusicObject methods */ -static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IDirectMusicObject_iface); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - - TRACE("(%p/%p)->(%p)\n", iface, This, desc); - - if (!desc) - return E_POINTER; - - memcpy(desc, &This->desc, This->desc.dwSize); - - return S_OK; -} - -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - HRESULT ret = S_OK; - - TRACE("(%p, %p)\n", iface, desc); - - if (!desc) - return E_POINTER; - - /* Immutable property */ - if (desc->dwValidData & DMUS_OBJ_CLASS) - { - desc->dwValidData &= ~DMUS_OBJ_CLASS; - ret = S_FALSE; - } - /* Set only valid fields */ - if (desc->dwValidData & DMUS_OBJ_OBJECT) - This->desc.guidObject = desc->guidObject; - if (desc->dwValidData & DMUS_OBJ_NAME) - lstrcpynW(This->desc.wszName, desc->wszName, DMUS_MAX_NAME); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - lstrcpynW(This->desc.wszCategory, desc->wszCategory, DMUS_MAX_CATEGORY); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - lstrcpynW(This->desc.wszFileName, desc->wszFileName, DMUS_MAX_FILENAME); - if (desc->dwValidData & DMUS_OBJ_VERSION) - This->desc.vVersion = desc->vVersion; - if (desc->dwValidData & DMUS_OBJ_DATE) - This->desc.ftDate = desc->ftDate; - if (desc->dwValidData & DMUS_OBJ_MEMORY) { - This->desc.llMemLength = desc->llMemLength; - memcpy(This->desc.pbMemData, desc->pbMemData, desc->llMemLength); - } - if (desc->dwValidData & DMUS_OBJ_STREAM) - IStream_Clone(desc->pStream, &This->desc.pStream); - - This->desc.dwValidData |= desc->dwValidData; - - return ret; -} - -/* Helper for IDirectMusicObject::ParseDescriptor */ -static inline void info_get_name(IStream *stream, const struct chunk_entry *info, - DMUS_OBJECTDESC *desc) -{ - struct chunk_entry chunk = {.parent = info}; - char name[DMUS_MAX_NAME]; - ULONG len; - HRESULT hr = E_FAIL; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == mmioFOURCC('I','N','A','M')) - hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); - - if (SUCCEEDED(hr)) { - len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); - desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; - desc->dwValidData |= DMUS_OBJ_NAME; - } -} - -static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, - DMUS_OBJECTDESC *desc, BOOL inam) -{ - struct chunk_entry chunk = {.parent = unfo}; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) - if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; -} - -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) -{ - struct chunk_entry chunk = {.parent = riff}; - HRESULT hr; - - TRACE("Looking for %#lx in %p: %s\n", supported, stream, debugstr_chunk(riff)); - - desc->dwValidData = 0; - desc->dwSize = sizeof(*desc); - - while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { - switch (chunk.id) { - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; - break; - case DMUS_FOURCC_DATE_CHUNK: - if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, - &desc->ftDate, sizeof(desc->ftDate)) == S_OK) - desc->dwValidData |= DMUS_OBJ_DATE; - break; - case DMUS_FOURCC_FILE_CHUNK: - if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_FILENAME; - break; - case DMUS_FOURCC_GUID_CHUNK: - if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, - &desc->guidObject, sizeof(desc->guidObject)) == S_OK) - desc->dwValidData |= DMUS_OBJ_OBJECT; - break; - case DMUS_FOURCC_NAME_CHUNK: - if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; - break; - case DMUS_FOURCC_VERSION_CHUNK: - if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, - &desc->vVersion, sizeof(desc->vVersion)) == S_OK) - desc->dwValidData |= DMUS_OBJ_VERSION; - break; - case FOURCC_LIST: - if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) - unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); - else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) - info_get_name(stream, &chunk, desc); - break; - } - } - TRACE("Found %#lx\n", desc->dwValidData); - - return hr; -} - -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) -{ - struct chunk_entry chunk = {.parent = list}; - IDirectMusicGetLoader *getloader; - IDirectMusicLoader *loader; - DMUS_OBJECTDESC desc; - DMUS_IO_REFERENCE reference; - HRESULT hr; - - if (FAILED(hr = stream_next_chunk(stream, &chunk))) - return hr; - if (chunk.id != DMUS_FOURCC_REF_CHUNK) - return DMUS_E_UNSUPPORTED_STREAM; - - if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { - WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); - return hr; - } - TRACE("REFERENCE guidClassID %s, dwValidData %#lx\n", debugstr_dmguid(&reference.guidClassID), - reference.dwValidData); - - if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) - return hr; - desc.guidClass = reference.guidClassID; - desc.dwValidData |= DMUS_OBJ_CLASS; - dump_DMUS_OBJECTDESC(&desc); - - if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) - return hr; - hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); - IDirectMusicGetLoader_Release(getloader); - if (FAILED(hr)) - return hr; - - hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); - IDirectMusicLoader_Release(loader); - - return hr; -} - -/* Generic IPersistStream methods */ -static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IPersistStream_iface); -} - -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - - TRACE("(%p, %p)\n", This, class); - - if (!class) - return E_POINTER; - - *class = This->desc.guidClass; - - return S_OK; -} - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - TRACE("(%p, %p): method not implemented\n", iface, class); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) -{ - TRACE("(%p): method not implemented, always returning S_FALSE\n", iface); - return S_FALSE; -} - -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) -{ - TRACE("(%p, %p, %d): method not implemented\n", iface, stream, clear_dirty); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *size) -{ - TRACE("(%p, %p): method not implemented\n", iface, size); - return E_NOTIMPL; -} - - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) -{ - dmobj->outer_unk = outer_unk; - dmobj->desc.dwSize = sizeof(dmobj->desc); - dmobj->desc.dwValidData = DMUS_OBJ_CLASS; - dmobj->desc.guidClass = *class; -} diff --git a/dlls/dmloader/dmobject.h b/dlls/dmloader/dmobject.h deleted file mode 100644 index 772be015c80..00000000000 --- a/dlls/dmloader/dmobject.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.h - * - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "wine/debug.h" - -/* RIFF stream parsing */ -struct chunk_entry; -struct chunk_entry { - FOURCC id; - DWORD size; - FOURCC type; /* valid only for LIST and RIFF chunks */ - ULARGE_INTEGER offset; /* chunk offset from start of stream */ - const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ -}; - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); - -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size); -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size); -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size); - -static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - offset.QuadPart += sizeof(FOURCC); - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - -static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart; - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - - -/* IDirectMusicObject base object */ -struct dmobject { - IDirectMusicObject IDirectMusicObject_iface; - IPersistStream IPersistStream_iface; - IUnknown *outer_unk; - DMUS_OBJECTDESC desc; -}; - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); - -/* Generic IDirectMusicObject methods */ -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); - -/* Helper for IDirectMusicObject::ParseDescriptor */ -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported); -/* Additional supported flags for dmobj_parsedescriptor. - DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ - -/* 'DMRF' (reference list) helper */ -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj); - -/* Generic IPersistStream methods */ -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class); -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty); -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size); - -/* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk); -const char *debugstr_dmguid(const GUID *id); -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); - -static inline const char *debugstr_fourcc(DWORD fourcc) -{ - if (!fourcc) return "''"; - return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), - (char)(fourcc >> 16), (char)(fourcc >> 24)); -} From 8407827a6e0ba8fe2c1b697d10e4d82d77565260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 22 Aug 2023 17:42:13 +0200 Subject: [PATCH 0875/1148] dmscript: Always return S_FALSE from DllCanUnloadNow. (cherry picked from commit b02b32f9b7eef9c5ed88ef60886f3b64954b5a47) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmscript/dmscript_main.c | 22 ---------------------- dlls/dmscript/dmscript_private.h | 7 ------- dlls/dmscript/script.c | 2 -- dlls/dmscript/scripttrack.c | 2 -- 4 files changed, 33 deletions(-) diff --git a/dlls/dmscript/dmscript_main.c b/dlls/dmscript/dmscript_main.c index f6785176aec..db5f163b829 100644 --- a/dlls/dmscript/dmscript_main.c +++ b/dlls/dmscript/dmscript_main.c @@ -38,8 +38,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmscript); -LONG DMSCRIPT_refCount = 0; - typedef struct { IClassFactory IClassFactory_iface; HRESULT (*fnCreateInstance)(REFIID riid, void **ppv, IUnknown *pUnkOuter); @@ -82,15 +80,11 @@ static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID r static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) { - DMSCRIPT_LockModule(); - return 2; /* non-heap based object */ } static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) { - DMSCRIPT_UnlockModule(); - return 1; /* non-heap based object */ } @@ -107,12 +101,6 @@ static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock) { TRACE("(%d)\n", dolock); - - if (dolock) - DMSCRIPT_LockModule(); - else - DMSCRIPT_UnlockModule(); - return S_OK; } @@ -140,16 +128,6 @@ static IClassFactoryImpl ScriptAutoImplAudioPath_CF = {{&classfactory_vtbl}, create_unimpl_instance}; static IClassFactoryImpl ScriptAutoImplSong_CF = {{&classfactory_vtbl}, create_unimpl_instance}; -/****************************************************************** - * DllCanUnloadNow (DMSCRIPT.@) - * - * - */ -HRESULT WINAPI DllCanUnloadNow(void) -{ - return DMSCRIPT_refCount != 0 ? S_FALSE : S_OK; -} - /****************************************************************** * DllGetClassObject (DMSCRIPT.@) diff --git a/dlls/dmscript/dmscript_private.h b/dlls/dmscript/dmscript_private.h index 90808d59f00..f715c083523 100644 --- a/dlls/dmscript/dmscript_private.h +++ b/dlls/dmscript/dmscript_private.h @@ -48,13 +48,6 @@ extern HRESULT DMUSIC_CreateDirectMusicScriptImpl(REFIID riid, void **ppobj, IUn extern HRESULT DMUSIC_CreateDirectMusicScriptTrack(REFIID riid, void **ppobj, IUnknown *pUnkOuter); -/********************************************************************** - * Dll lifetime tracking declaration for dmscript.dll - */ -extern LONG DMSCRIPT_refCount; -static inline void DMSCRIPT_LockModule(void) { InterlockedIncrement( &DMSCRIPT_refCount ); } -static inline void DMSCRIPT_UnlockModule(void) { InterlockedDecrement( &DMSCRIPT_refCount ); } - /***************************************************************************** * Misc. */ diff --git a/dlls/dmscript/script.c b/dlls/dmscript/script.c index 375eebf69aa..dbfcdca7d83 100644 --- a/dlls/dmscript/script.c +++ b/dlls/dmscript/script.c @@ -94,7 +94,6 @@ static ULONG WINAPI IDirectMusicScriptImpl_Release(IDirectMusicScript *iface) HeapFree(GetProcessHeap(), 0, This->pwzLanguage); HeapFree(GetProcessHeap(), 0, This->pwzSource); HeapFree(GetProcessHeap(), 0, This); - DMSCRIPT_UnlockModule(); } return ref; @@ -508,7 +507,6 @@ HRESULT DMUSIC_CreateDirectMusicScriptImpl(REFIID lpcGUID, void **ppobj, IUnknow obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - DMSCRIPT_LockModule(); hr = IDirectMusicScript_QueryInterface(&obj->IDirectMusicScript_iface, lpcGUID, ppobj); IDirectMusicScript_Release(&obj->IDirectMusicScript_iface); diff --git a/dlls/dmscript/scripttrack.c b/dlls/dmscript/scripttrack.c index fbe454c09e5..92dd6d15ec6 100644 --- a/dlls/dmscript/scripttrack.c +++ b/dlls/dmscript/scripttrack.c @@ -86,7 +86,6 @@ static ULONG WINAPI script_track_Release(IDirectMusicTrack8 *iface) if (!ref) { HeapFree(GetProcessHeap(), 0, This); - DMSCRIPT_UnlockModule(); } return ref; @@ -338,7 +337,6 @@ HRESULT DMUSIC_CreateDirectMusicScriptTrack(REFIID riid, void **ret_iface, IUnkn track->desc.guidClass = CLSID_DirectMusicScriptTrack; track->ref = 1; - DMSCRIPT_LockModule(); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, riid, ret_iface); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); From 5cb988122b5f0b594d70d568d25ffe1accb11341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 09:23:38 +0200 Subject: [PATCH 0876/1148] dmscript: Use CRT allocation functions. (cherry picked from commit f49b5ca85ec4bae5272ee102aa129a85a20dfd78) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmscript/dmobject.c | 7 +++---- dlls/dmscript/script.c | 27 ++++++++++++--------------- dlls/dmscript/scripttrack.c | 9 ++------- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/dlls/dmscript/dmobject.c b/dlls/dmscript/dmobject.c index b526b23d031..07d887a376c 100644 --- a/dlls/dmscript/dmobject.c +++ b/dlls/dmscript/dmobject.c @@ -28,7 +28,6 @@ #include "dmusics.h" #include "dmobject.h" #include "wine/debug.h" -#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(dmobj); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -375,7 +374,7 @@ HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) /* Reads chunk data of the form: DWORD - size of array element element[] - Array of elements - The caller needs to heap_free() the array. + The caller needs to free() the array. */ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, unsigned int *count, DWORD elem_size) @@ -400,10 +399,10 @@ HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, *count = (chunk->size - sizeof(DWORD)) / elem_size; size = *count * elem_size; - if (!(*array = heap_alloc(size))) + if (!(*array = malloc(size))) return E_OUTOFMEMORY; if (FAILED(hr = stream_read(stream, *array, size))) { - heap_free(*array); + free(*array); *array = NULL; return hr; } diff --git a/dlls/dmscript/script.c b/dlls/dmscript/script.c index dbfcdca7d83..01d4bcd0290 100644 --- a/dlls/dmscript/script.c +++ b/dlls/dmscript/script.c @@ -89,11 +89,11 @@ static ULONG WINAPI IDirectMusicScriptImpl_Release(IDirectMusicScript *iface) TRACE("(%p) ref=%ld\n", This, ref); if (!ref) { - HeapFree(GetProcessHeap(), 0, This->pHeader); - HeapFree(GetProcessHeap(), 0, This->pVersion); - HeapFree(GetProcessHeap(), 0, This->pwzLanguage); - HeapFree(GetProcessHeap(), 0, This->pwzSource); - HeapFree(GetProcessHeap(), 0, This); + free(This->pHeader); + free(This->pVersion); + free(This->pwzLanguage); + free(This->pwzSource); + free(This); } return ref; @@ -275,36 +275,36 @@ static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, IStream *pS switch (Chunk.fccID) { case DMUS_FOURCC_SCRIPT_CHUNK: { TRACE_(dmfile)(": script header chunk\n"); - This->pHeader = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, Chunk.dwSize); + This->pHeader = calloc(1, Chunk.dwSize); IStream_Read (pStm, This->pHeader, Chunk.dwSize, NULL); break; } case DMUS_FOURCC_SCRIPTVERSION_CHUNK: { TRACE_(dmfile)(": script version chunk\n"); - This->pVersion = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, Chunk.dwSize); + This->pVersion = calloc(1, Chunk.dwSize); IStream_Read (pStm, This->pVersion, Chunk.dwSize, NULL); TRACE_(dmfile)("version: 0x%08lx.0x%08lx\n", This->pVersion->dwVersionMS, This->pVersion->dwVersionLS); break; } case DMUS_FOURCC_SCRIPTLANGUAGE_CHUNK: { TRACE_(dmfile)(": script language chunk\n"); - This->pwzLanguage = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, Chunk.dwSize); + This->pwzLanguage = calloc(1, Chunk.dwSize); IStream_Read (pStm, This->pwzLanguage, Chunk.dwSize, NULL); TRACE_(dmfile)("using language: %s\n", debugstr_w(This->pwzLanguage)); break; } case DMUS_FOURCC_SCRIPTSOURCE_CHUNK: { TRACE_(dmfile)(": script source chunk\n"); - This->pwzSource = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, Chunk.dwSize); + This->pwzSource = calloc(1, Chunk.dwSize); IStream_Read (pStm, This->pwzSource, Chunk.dwSize, NULL); if (TRACE_ON(dmscript)) { int count = WideCharToMultiByte(CP_ACP, 0, This->pwzSource, -1, NULL, 0, NULL, NULL); - LPSTR str = HeapAlloc(GetProcessHeap (), 0, count); + LPSTR str = malloc(count); WideCharToMultiByte(CP_ACP, 0, This->pwzSource, -1, str, count, NULL, NULL); str[count-1] = '\n'; TRACE("source:\n"); fwrite( str, 1, count, stderr ); - HeapFree(GetProcessHeap(), 0, str); + free(str); } break; } @@ -497,10 +497,7 @@ HRESULT DMUSIC_CreateDirectMusicScriptImpl(REFIID lpcGUID, void **ppobj, IUnknow if (pUnkOuter) return CLASS_E_NOAGGREGATION; - obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicScriptImpl)); - if (!obj) - return E_OUTOFMEMORY; - + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicScript_iface.lpVtbl = &dmscript_vtbl; obj->ref = 1; dmobject_init(&obj->dmobj, &CLSID_DirectMusicScript, (IUnknown*)&obj->IDirectMusicScript_iface); diff --git a/dlls/dmscript/scripttrack.c b/dlls/dmscript/scripttrack.c index 92dd6d15ec6..74d45afacbe 100644 --- a/dlls/dmscript/scripttrack.c +++ b/dlls/dmscript/scripttrack.c @@ -84,9 +84,7 @@ static ULONG WINAPI script_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) { - HeapFree(GetProcessHeap(), 0, This); - } + if (!ref) free(This); return ref; } @@ -326,10 +324,7 @@ HRESULT DMUSIC_CreateDirectMusicScriptTrack(REFIID riid, void **ret_iface, IUnkn if (pUnkOuter) return CLASS_E_NOAGGREGATION; - track = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*track)); - if (!track) - return E_OUTOFMEMORY; - + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; track->IPersistStream_iface.lpVtbl = &persist_vtbl; track->desc.dwSize = sizeof(track->desc); From 76d73716194377488f1257ac8e794f8c5ab96859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 30 Aug 2023 19:55:16 +0200 Subject: [PATCH 0877/1148] dmscript: Use PARENTSRC with dmusic. (cherry picked from commit d6f049e8a59a606c82a747e896735826eb6052e7) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmscript/Makefile.in | 1 + dlls/dmscript/dmobject.c | 732 -------------------------------------- dlls/dmscript/dmobject.h | 124 ------- 3 files changed, 1 insertion(+), 856 deletions(-) delete mode 100644 dlls/dmscript/dmobject.c delete mode 100644 dlls/dmscript/dmobject.h diff --git a/dlls/dmscript/Makefile.in b/dlls/dmscript/Makefile.in index 55107e116c9..9eac24c2d86 100644 --- a/dlls/dmscript/Makefile.in +++ b/dlls/dmscript/Makefile.in @@ -1,5 +1,6 @@ MODULE = dmscript.dll IMPORTS = dxguid uuid ole32 advapi32 +PARENTSRC = ../dmusic C_SRCS = \ dmobject.c \ diff --git a/dlls/dmscript/dmobject.c b/dlls/dmscript/dmobject.c deleted file mode 100644 index 07d887a376c..00000000000 --- a/dlls/dmscript/dmobject.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.c - * - * Copyright (C) 2003-2004 Rok Mandeljc - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#define COBJMACROS -#include -#include "objbase.h" -#include "dmusici.h" -#include "dmusicf.h" -#include "dmusics.h" -#include "dmobject.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(dmobj); -WINE_DECLARE_DEBUG_CHANNEL(dmfile); - -/* Debugging helpers */ -const char *debugstr_dmguid(const GUID *id) { - unsigned int i; -#define X(guid) { &guid, #guid } - static const struct { - const GUID *guid; - const char *name; - } guids[] = { - /* CLSIDs */ - X(CLSID_AudioVBScript), - X(CLSID_DirectMusic), - X(CLSID_DirectMusicAudioPathConfig), - X(CLSID_DirectMusicAuditionTrack), - X(CLSID_DirectMusicBand), - X(CLSID_DirectMusicBandTrack), - X(CLSID_DirectMusicChordMapTrack), - X(CLSID_DirectMusicChordMap), - X(CLSID_DirectMusicChordTrack), - X(CLSID_DirectMusicCollection), - X(CLSID_DirectMusicCommandTrack), - X(CLSID_DirectMusicComposer), - X(CLSID_DirectMusicContainer), - X(CLSID_DirectMusicGraph), - X(CLSID_DirectMusicLoader), - X(CLSID_DirectMusicLyricsTrack), - X(CLSID_DirectMusicMarkerTrack), - X(CLSID_DirectMusicMelodyFormulationTrack), - X(CLSID_DirectMusicMotifTrack), - X(CLSID_DirectMusicMuteTrack), - X(CLSID_DirectMusicParamControlTrack), - X(CLSID_DirectMusicPatternTrack), - X(CLSID_DirectMusicPerformance), - X(CLSID_DirectMusicScript), - X(CLSID_DirectMusicScriptAutoImpSegment), - X(CLSID_DirectMusicScriptAutoImpPerformance), - X(CLSID_DirectMusicScriptAutoImpSegmentState), - X(CLSID_DirectMusicScriptAutoImpAudioPathConfig), - X(CLSID_DirectMusicScriptAutoImpAudioPath), - X(CLSID_DirectMusicScriptAutoImpSong), - X(CLSID_DirectMusicScriptSourceCodeLoader), - X(CLSID_DirectMusicScriptTrack), - X(CLSID_DirectMusicSection), - X(CLSID_DirectMusicSegment), - X(CLSID_DirectMusicSegmentState), - X(CLSID_DirectMusicSegmentTriggerTrack), - X(CLSID_DirectMusicSegTriggerTrack), - X(CLSID_DirectMusicSeqTrack), - X(CLSID_DirectMusicSignPostTrack), - X(CLSID_DirectMusicSong), - X(CLSID_DirectMusicStyle), - X(CLSID_DirectMusicStyleTrack), - X(CLSID_DirectMusicSynth), - X(CLSID_DirectMusicSynthSink), - X(CLSID_DirectMusicSysExTrack), - X(CLSID_DirectMusicTemplate), - X(CLSID_DirectMusicTempoTrack), - X(CLSID_DirectMusicTimeSigTrack), - X(CLSID_DirectMusicWaveTrack), - X(CLSID_DirectSoundWave), - /* IIDs */ - X(IID_IDirectMusic), - X(IID_IDirectMusic2), - X(IID_IDirectMusic8), - X(IID_IDirectMusicAudioPath), - X(IID_IDirectMusicBand), - X(IID_IDirectMusicBuffer), - X(IID_IDirectMusicChordMap), - X(IID_IDirectMusicCollection), - X(IID_IDirectMusicComposer), - X(IID_IDirectMusicContainer), - X(IID_IDirectMusicDownload), - X(IID_IDirectMusicDownloadedInstrument), - X(IID_IDirectMusicGetLoader), - X(IID_IDirectMusicGraph), - X(IID_IDirectMusicInstrument), - X(IID_IDirectMusicLoader), - X(IID_IDirectMusicLoader8), - X(IID_IDirectMusicObject), - X(IID_IDirectMusicPatternTrack), - X(IID_IDirectMusicPerformance), - X(IID_IDirectMusicPerformance2), - X(IID_IDirectMusicPerformance8), - X(IID_IDirectMusicPort), - X(IID_IDirectMusicPortDownload), - X(IID_IDirectMusicScript), - X(IID_IDirectMusicSegment), - X(IID_IDirectMusicSegment2), - X(IID_IDirectMusicSegment8), - X(IID_IDirectMusicSegmentState), - X(IID_IDirectMusicSegmentState8), - X(IID_IDirectMusicStyle), - X(IID_IDirectMusicStyle8), - X(IID_IDirectMusicSynth), - X(IID_IDirectMusicSynth8), - X(IID_IDirectMusicSynthSink), - X(IID_IDirectMusicThru), - X(IID_IDirectMusicTool), - X(IID_IDirectMusicTool8), - X(IID_IDirectMusicTrack), - X(IID_IDirectMusicTrack8), - X(IID_IUnknown), - X(IID_IPersistStream), - X(IID_IStream), - X(IID_IClassFactory), - /* GUIDs */ - X(GUID_DirectMusicAllTypes), - X(GUID_NOTIFICATION_CHORD), - X(GUID_NOTIFICATION_COMMAND), - X(GUID_NOTIFICATION_MEASUREANDBEAT), - X(GUID_NOTIFICATION_PERFORMANCE), - X(GUID_NOTIFICATION_RECOMPOSE), - X(GUID_NOTIFICATION_SEGMENT), - X(GUID_BandParam), - X(GUID_ChordParam), - X(GUID_CommandParam), - X(GUID_CommandParam2), - X(GUID_CommandParamNext), - X(GUID_IDirectMusicBand), - X(GUID_IDirectMusicChordMap), - X(GUID_IDirectMusicStyle), - X(GUID_MuteParam), - X(GUID_Play_Marker), - X(GUID_RhythmParam), - X(GUID_TempoParam), - X(GUID_TimeSignature), - X(GUID_Valid_Start_Time), - X(GUID_Clear_All_Bands), - X(GUID_ConnectToDLSCollection), - X(GUID_Disable_Auto_Download), - X(GUID_DisableTempo), - X(GUID_DisableTimeSig), - X(GUID_Download), - X(GUID_DownloadToAudioPath), - X(GUID_Enable_Auto_Download), - X(GUID_EnableTempo), - X(GUID_EnableTimeSig), - X(GUID_IgnoreBankSelectForGM), - X(GUID_SeedVariations), - X(GUID_StandardMIDIFile), - X(GUID_Unload), - X(GUID_UnloadFromAudioPath), - X(GUID_Variations), - X(GUID_PerfMasterTempo), - X(GUID_PerfMasterVolume), - X(GUID_PerfMasterGrooveLevel), - X(GUID_PerfAutoDownload), - X(GUID_DefaultGMCollection), - X(GUID_Synth_Default), - X(GUID_Buffer_Reverb), - X(GUID_Buffer_EnvReverb), - X(GUID_Buffer_Stereo), - X(GUID_Buffer_3D_Dry), - X(GUID_Buffer_Mono), - X(GUID_DMUS_PROP_GM_Hardware), - X(GUID_DMUS_PROP_GS_Capable), - X(GUID_DMUS_PROP_GS_Hardware), - X(GUID_DMUS_PROP_DLS1), - X(GUID_DMUS_PROP_DLS2), - X(GUID_DMUS_PROP_Effects), - X(GUID_DMUS_PROP_INSTRUMENT2), - X(GUID_DMUS_PROP_LegacyCaps), - X(GUID_DMUS_PROP_MemorySize), - X(GUID_DMUS_PROP_SampleMemorySize), - X(GUID_DMUS_PROP_SamplePlaybackRate), - X(GUID_DMUS_PROP_SetSynthSink), - X(GUID_DMUS_PROP_SinkUsesDSound), - X(GUID_DMUS_PROP_SynthSink_DSOUND), - X(GUID_DMUS_PROP_SynthSink_WAVE), - X(GUID_DMUS_PROP_Volume), - X(GUID_DMUS_PROP_WavesReverb), - X(GUID_DMUS_PROP_WriteLatency), - X(GUID_DMUS_PROP_WritePeriod), - X(GUID_DMUS_PROP_XG_Capable), - X(GUID_DMUS_PROP_XG_Hardware) - }; -#undef X - - if (!id) - return "(null)"; - - for (i = 0; i < ARRAY_SIZE(guids); i++) - if (IsEqualGUID(id, guids[i].guid)) - return guids[i].name; - - return debugstr_guid(id); -} - -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) -{ - if (!desc || !TRACE_ON(dmfile)) - return; - - TRACE_(dmfile)("DMUS_OBJECTDESC (%p):", desc); - TRACE_(dmfile)(" - dwSize = %lu\n", desc->dwSize); - -#define X(flag) if (desc->dwValidData & flag) TRACE_(dmfile)(#flag " ") - TRACE_(dmfile)(" - dwValidData = %#08lx ( ", desc->dwValidData); - X(DMUS_OBJ_OBJECT); - X(DMUS_OBJ_CLASS); - X(DMUS_OBJ_NAME); - X(DMUS_OBJ_CATEGORY); - X(DMUS_OBJ_FILENAME); - X(DMUS_OBJ_FULLPATH); - X(DMUS_OBJ_URL); - X(DMUS_OBJ_VERSION); - X(DMUS_OBJ_DATE); - X(DMUS_OBJ_LOADED); - X(DMUS_OBJ_MEMORY); - X(DMUS_OBJ_STREAM); - TRACE_(dmfile)(")\n"); -#undef X - - if (desc->dwValidData & DMUS_OBJ_CLASS) - TRACE_(dmfile)(" - guidClass = %s\n", debugstr_dmguid(&desc->guidClass)); - if (desc->dwValidData & DMUS_OBJ_OBJECT) - TRACE_(dmfile)(" - guidObject = %s\n", debugstr_guid(&desc->guidObject)); - - if (desc->dwValidData & DMUS_OBJ_DATE) { - SYSTEMTIME time; - FileTimeToSystemTime(&desc->ftDate, &time); - TRACE_(dmfile)(" - ftDate = \'%04u-%02u-%02u %02u:%02u:%02u\'\n", - time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); - } - if (desc->dwValidData & DMUS_OBJ_VERSION) - TRACE_(dmfile)(" - vVersion = \'%u,%u,%u,%u\'\n", - HIWORD(desc->vVersion.dwVersionMS), LOWORD(desc->vVersion.dwVersionMS), - HIWORD(desc->vVersion.dwVersionLS), LOWORD(desc->vVersion.dwVersionLS)); - if (desc->dwValidData & DMUS_OBJ_NAME) - TRACE_(dmfile)(" - wszName = %s\n", debugstr_w(desc->wszName)); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - TRACE_(dmfile)(" - wszCategory = %s\n", debugstr_w(desc->wszCategory)); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - TRACE_(dmfile)(" - wszFileName = %s\n", debugstr_w(desc->wszFileName)); - if (desc->dwValidData & DMUS_OBJ_MEMORY) - TRACE_(dmfile)(" - llMemLength = 0x%s - pbMemData = %p\n", - wine_dbgstr_longlong(desc->llMemLength), desc->pbMemData); - if (desc->dwValidData & DMUS_OBJ_STREAM) - TRACE_(dmfile)(" - pStream = %p\n", desc->pStream); -} - - -/* RIFF format parsing */ -#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) - -const char *debugstr_chunk(const struct chunk_entry *chunk) -{ - const char *type = ""; - - if (!chunk) - return "(null)"; - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); - return wine_dbg_sprintf("%s chunk, %ssize %lu", debugstr_fourcc(chunk->id), type, chunk->size); -} - -static HRESULT stream_read(IStream *stream, void *data, ULONG size) -{ - ULONG read; - HRESULT hr; - - hr = IStream_Read(stream, data, size, &read); - if (FAILED(hr)) - TRACE_(dmfile)("IStream_Read failed: %#lx\n", hr); - else if (!read && read < size) { - /* All or nothing: Handle a partial read due to end of stream as an error */ - TRACE_(dmfile)("Short read: %lu < %lu\n", read, size); - return E_FAIL; - } - - return hr; -} - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) -{ - static const LARGE_INTEGER zero; - ULONGLONG ck_end = 0, p_end = 0; - HRESULT hr; - - hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); - if (FAILED(hr)) - return hr; - assert(!(chunk->offset.QuadPart & 1)); - if (chunk->parent) { - p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); - if (chunk->offset.QuadPart == p_end) - return S_FALSE; - ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); - if (hr != S_OK) - return hr; - if (chunk->parent) { - ck_end += (chunk->size + 1) & ~1; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { - hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); - if (hr != S_OK) - return hr != S_FALSE ? hr : E_FAIL; - } - - TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); - - return S_OK; -} - -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER end; - - end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; - - return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); -} - -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) -{ - HRESULT hr; - - if (chunk->id) { - hr = stream_skip_chunk(stream, chunk); - if (FAILED(hr)) - return hr; - } - - return stream_get_chunk(stream, chunk); -} - -/* Reads chunk data of the form: - DWORD - size of array element - element[] - Array of elements - The caller needs to free() the array. -*/ -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) -{ - DWORD size; - HRESULT hr; - - *array = NULL; - *count = 0; - - if (chunk->size < sizeof(DWORD)) { - WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); - return E_FAIL; - } - if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) - return hr; - if (size != elem_size) { - WARN_(dmfile)("%s: Array element size mismatch: got %lu, expected %lu\n", - debugstr_chunk(chunk), size, elem_size); - return DMUS_E_UNSUPPORTED_STREAM; - } - - *count = (chunk->size - sizeof(DWORD)) / elem_size; - size = *count * elem_size; - if (!(*array = malloc(size))) - return E_OUTOFMEMORY; - if (FAILED(hr = stream_read(stream, *array, size))) { - free(*array); - *array = NULL; - return hr; - } - - if (chunk->size > size + sizeof(DWORD)) { - WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); - stream_skip_chunk(stream, chunk); - return S_FALSE; - } - return S_OK; -} - -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) -{ - if (chunk->size != size) { - WARN_(dmfile)("Chunk %s (size %lu, offset %s) doesn't contains the expected data size %lu\n", - debugstr_fourcc(chunk->id), chunk->size, - wine_dbgstr_longlong(chunk->offset.QuadPart), size); - return E_FAIL; - } - return stream_read(stream, data, size); -} - -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) -{ - ULONG len; - HRESULT hr; - - hr = IStream_Read(stream, str, min(chunk->size, size), &len); - if (FAILED(hr)) - return hr; - - /* Don't assume the string is properly zero terminated */ - str[min(len, size - 1)] = 0; - - if (len < chunk->size) - return S_FALSE; - return S_OK; -} - - - -/* Generic IDirectMusicObject methods */ -static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IDirectMusicObject_iface); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - - TRACE("(%p/%p)->(%p)\n", iface, This, desc); - - if (!desc) - return E_POINTER; - - memcpy(desc, &This->desc, This->desc.dwSize); - - return S_OK; -} - -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - HRESULT ret = S_OK; - - TRACE("(%p, %p)\n", iface, desc); - - if (!desc) - return E_POINTER; - - /* Immutable property */ - if (desc->dwValidData & DMUS_OBJ_CLASS) - { - desc->dwValidData &= ~DMUS_OBJ_CLASS; - ret = S_FALSE; - } - /* Set only valid fields */ - if (desc->dwValidData & DMUS_OBJ_OBJECT) - This->desc.guidObject = desc->guidObject; - if (desc->dwValidData & DMUS_OBJ_NAME) - lstrcpynW(This->desc.wszName, desc->wszName, DMUS_MAX_NAME); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - lstrcpynW(This->desc.wszCategory, desc->wszCategory, DMUS_MAX_CATEGORY); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - lstrcpynW(This->desc.wszFileName, desc->wszFileName, DMUS_MAX_FILENAME); - if (desc->dwValidData & DMUS_OBJ_VERSION) - This->desc.vVersion = desc->vVersion; - if (desc->dwValidData & DMUS_OBJ_DATE) - This->desc.ftDate = desc->ftDate; - if (desc->dwValidData & DMUS_OBJ_MEMORY) { - This->desc.llMemLength = desc->llMemLength; - memcpy(This->desc.pbMemData, desc->pbMemData, desc->llMemLength); - } - if (desc->dwValidData & DMUS_OBJ_STREAM) - IStream_Clone(desc->pStream, &This->desc.pStream); - - This->desc.dwValidData |= desc->dwValidData; - - return ret; -} - -/* Helper for IDirectMusicObject::ParseDescriptor */ -static inline void info_get_name(IStream *stream, const struct chunk_entry *info, - DMUS_OBJECTDESC *desc) -{ - struct chunk_entry chunk = {.parent = info}; - char name[DMUS_MAX_NAME]; - ULONG len; - HRESULT hr = E_FAIL; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == mmioFOURCC('I','N','A','M')) - hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); - - if (SUCCEEDED(hr)) { - len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); - desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; - desc->dwValidData |= DMUS_OBJ_NAME; - } -} - -static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, - DMUS_OBJECTDESC *desc, BOOL inam) -{ - struct chunk_entry chunk = {.parent = unfo}; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) - if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; -} - -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) -{ - struct chunk_entry chunk = {.parent = riff}; - HRESULT hr; - - TRACE("Looking for %#lx in %p: %s\n", supported, stream, debugstr_chunk(riff)); - - desc->dwValidData = 0; - desc->dwSize = sizeof(*desc); - - while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { - switch (chunk.id) { - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; - break; - case DMUS_FOURCC_DATE_CHUNK: - if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, - &desc->ftDate, sizeof(desc->ftDate)) == S_OK) - desc->dwValidData |= DMUS_OBJ_DATE; - break; - case DMUS_FOURCC_FILE_CHUNK: - if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_FILENAME; - break; - case DMUS_FOURCC_GUID_CHUNK: - if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, - &desc->guidObject, sizeof(desc->guidObject)) == S_OK) - desc->dwValidData |= DMUS_OBJ_OBJECT; - break; - case DMUS_FOURCC_NAME_CHUNK: - if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; - break; - case DMUS_FOURCC_VERSION_CHUNK: - if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, - &desc->vVersion, sizeof(desc->vVersion)) == S_OK) - desc->dwValidData |= DMUS_OBJ_VERSION; - break; - case FOURCC_LIST: - if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) - unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); - else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) - info_get_name(stream, &chunk, desc); - break; - } - } - TRACE("Found %#lx\n", desc->dwValidData); - - return hr; -} - -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) -{ - struct chunk_entry chunk = {.parent = list}; - IDirectMusicGetLoader *getloader; - IDirectMusicLoader *loader; - DMUS_OBJECTDESC desc; - DMUS_IO_REFERENCE reference; - HRESULT hr; - - if (FAILED(hr = stream_next_chunk(stream, &chunk))) - return hr; - if (chunk.id != DMUS_FOURCC_REF_CHUNK) - return DMUS_E_UNSUPPORTED_STREAM; - - if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { - WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); - return hr; - } - TRACE("REFERENCE guidClassID %s, dwValidData %#lx\n", debugstr_dmguid(&reference.guidClassID), - reference.dwValidData); - - if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) - return hr; - desc.guidClass = reference.guidClassID; - desc.dwValidData |= DMUS_OBJ_CLASS; - dump_DMUS_OBJECTDESC(&desc); - - if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) - return hr; - hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); - IDirectMusicGetLoader_Release(getloader); - if (FAILED(hr)) - return hr; - - hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); - IDirectMusicLoader_Release(loader); - - return hr; -} - -/* Generic IPersistStream methods */ -static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IPersistStream_iface); -} - -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - - TRACE("(%p, %p)\n", This, class); - - if (!class) - return E_POINTER; - - *class = This->desc.guidClass; - - return S_OK; -} - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - TRACE("(%p, %p): method not implemented\n", iface, class); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) -{ - TRACE("(%p): method not implemented, always returning S_FALSE\n", iface); - return S_FALSE; -} - -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) -{ - TRACE("(%p, %p, %d): method not implemented\n", iface, stream, clear_dirty); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *size) -{ - TRACE("(%p, %p): method not implemented\n", iface, size); - return E_NOTIMPL; -} - - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) -{ - dmobj->outer_unk = outer_unk; - dmobj->desc.dwSize = sizeof(dmobj->desc); - dmobj->desc.dwValidData = DMUS_OBJ_CLASS; - dmobj->desc.guidClass = *class; -} diff --git a/dlls/dmscript/dmobject.h b/dlls/dmscript/dmobject.h deleted file mode 100644 index 772be015c80..00000000000 --- a/dlls/dmscript/dmobject.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.h - * - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "wine/debug.h" - -/* RIFF stream parsing */ -struct chunk_entry; -struct chunk_entry { - FOURCC id; - DWORD size; - FOURCC type; /* valid only for LIST and RIFF chunks */ - ULARGE_INTEGER offset; /* chunk offset from start of stream */ - const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ -}; - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); - -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size); -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size); -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size); - -static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - offset.QuadPart += sizeof(FOURCC); - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - -static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart; - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - - -/* IDirectMusicObject base object */ -struct dmobject { - IDirectMusicObject IDirectMusicObject_iface; - IPersistStream IPersistStream_iface; - IUnknown *outer_unk; - DMUS_OBJECTDESC desc; -}; - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); - -/* Generic IDirectMusicObject methods */ -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); - -/* Helper for IDirectMusicObject::ParseDescriptor */ -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported); -/* Additional supported flags for dmobj_parsedescriptor. - DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ - -/* 'DMRF' (reference list) helper */ -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj); - -/* Generic IPersistStream methods */ -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class); -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty); -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size); - -/* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk); -const char *debugstr_dmguid(const GUID *id); -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); - -static inline const char *debugstr_fourcc(DWORD fourcc) -{ - if (!fourcc) return "''"; - return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), - (char)(fourcc >> 16), (char)(fourcc >> 24)); -} From 60db04402c784743dbb34b4496171040d228307e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 30 Aug 2023 19:56:28 +0200 Subject: [PATCH 0878/1148] dmime: Use PARENTSRC with dmusic. (cherry picked from commit aeea45946abee7bb673c12c24bd6b590dcb98c72) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/Makefile.in | 1 + dlls/dmime/dmobject.c | 731 ----------------------------------------- dlls/dmime/dmobject.h | 124 ------- 3 files changed, 1 insertion(+), 855 deletions(-) delete mode 100644 dlls/dmime/dmobject.c delete mode 100644 dlls/dmime/dmobject.h diff --git a/dlls/dmime/Makefile.in b/dlls/dmime/Makefile.in index 51d1492585f..ef4d0b9bee4 100644 --- a/dlls/dmime/Makefile.in +++ b/dlls/dmime/Makefile.in @@ -1,5 +1,6 @@ MODULE = dmime.dll IMPORTS = dxguid uuid dsound ole32 user32 advapi32 +PARENTSRC = ../dmusic C_SRCS = \ audiopath.c \ diff --git a/dlls/dmime/dmobject.c b/dlls/dmime/dmobject.c deleted file mode 100644 index e080211dd11..00000000000 --- a/dlls/dmime/dmobject.c +++ /dev/null @@ -1,731 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.c - * - * Copyright (C) 2003-2004 Rok Mandeljc - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#define COBJMACROS -#include -#include "objbase.h" -#include "dmusici.h" -#include "dmusicf.h" -#include "dmusics.h" -#include "dmobject.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(dmobj); -WINE_DECLARE_DEBUG_CHANNEL(dmfile); - -/* Debugging helpers */ -const char *debugstr_dmguid(const GUID *id) { - unsigned int i; -#define X(guid) { &guid, #guid } - static const struct { - const GUID *guid; - const char *name; - } guids[] = { - /* CLSIDs */ - X(CLSID_AudioVBScript), - X(CLSID_DirectMusic), - X(CLSID_DirectMusicAudioPathConfig), - X(CLSID_DirectMusicAuditionTrack), - X(CLSID_DirectMusicBand), - X(CLSID_DirectMusicBandTrack), - X(CLSID_DirectMusicChordMapTrack), - X(CLSID_DirectMusicChordMap), - X(CLSID_DirectMusicChordTrack), - X(CLSID_DirectMusicCollection), - X(CLSID_DirectMusicCommandTrack), - X(CLSID_DirectMusicComposer), - X(CLSID_DirectMusicContainer), - X(CLSID_DirectMusicGraph), - X(CLSID_DirectMusicLoader), - X(CLSID_DirectMusicLyricsTrack), - X(CLSID_DirectMusicMarkerTrack), - X(CLSID_DirectMusicMelodyFormulationTrack), - X(CLSID_DirectMusicMotifTrack), - X(CLSID_DirectMusicMuteTrack), - X(CLSID_DirectMusicParamControlTrack), - X(CLSID_DirectMusicPatternTrack), - X(CLSID_DirectMusicPerformance), - X(CLSID_DirectMusicScript), - X(CLSID_DirectMusicScriptAutoImpSegment), - X(CLSID_DirectMusicScriptAutoImpPerformance), - X(CLSID_DirectMusicScriptAutoImpSegmentState), - X(CLSID_DirectMusicScriptAutoImpAudioPathConfig), - X(CLSID_DirectMusicScriptAutoImpAudioPath), - X(CLSID_DirectMusicScriptAutoImpSong), - X(CLSID_DirectMusicScriptSourceCodeLoader), - X(CLSID_DirectMusicScriptTrack), - X(CLSID_DirectMusicSection), - X(CLSID_DirectMusicSegment), - X(CLSID_DirectMusicSegmentState), - X(CLSID_DirectMusicSegmentTriggerTrack), - X(CLSID_DirectMusicSegTriggerTrack), - X(CLSID_DirectMusicSeqTrack), - X(CLSID_DirectMusicSignPostTrack), - X(CLSID_DirectMusicSong), - X(CLSID_DirectMusicStyle), - X(CLSID_DirectMusicStyleTrack), - X(CLSID_DirectMusicSynth), - X(CLSID_DirectMusicSynthSink), - X(CLSID_DirectMusicSysExTrack), - X(CLSID_DirectMusicTemplate), - X(CLSID_DirectMusicTempoTrack), - X(CLSID_DirectMusicTimeSigTrack), - X(CLSID_DirectMusicWaveTrack), - X(CLSID_DirectSoundWave), - /* IIDs */ - X(IID_IDirectMusic), - X(IID_IDirectMusic2), - X(IID_IDirectMusic8), - X(IID_IDirectMusicAudioPath), - X(IID_IDirectMusicBand), - X(IID_IDirectMusicBuffer), - X(IID_IDirectMusicChordMap), - X(IID_IDirectMusicCollection), - X(IID_IDirectMusicComposer), - X(IID_IDirectMusicContainer), - X(IID_IDirectMusicDownload), - X(IID_IDirectMusicDownloadedInstrument), - X(IID_IDirectMusicGetLoader), - X(IID_IDirectMusicGraph), - X(IID_IDirectMusicInstrument), - X(IID_IDirectMusicLoader), - X(IID_IDirectMusicLoader8), - X(IID_IDirectMusicObject), - X(IID_IDirectMusicPatternTrack), - X(IID_IDirectMusicPerformance), - X(IID_IDirectMusicPerformance2), - X(IID_IDirectMusicPerformance8), - X(IID_IDirectMusicPort), - X(IID_IDirectMusicPortDownload), - X(IID_IDirectMusicScript), - X(IID_IDirectMusicSegment), - X(IID_IDirectMusicSegment2), - X(IID_IDirectMusicSegment8), - X(IID_IDirectMusicSegmentState), - X(IID_IDirectMusicSegmentState8), - X(IID_IDirectMusicStyle), - X(IID_IDirectMusicStyle8), - X(IID_IDirectMusicSynth), - X(IID_IDirectMusicSynth8), - X(IID_IDirectMusicSynthSink), - X(IID_IDirectMusicThru), - X(IID_IDirectMusicTool), - X(IID_IDirectMusicTool8), - X(IID_IDirectMusicTrack), - X(IID_IDirectMusicTrack8), - X(IID_IUnknown), - X(IID_IPersistStream), - X(IID_IStream), - X(IID_IClassFactory), - /* GUIDs */ - X(GUID_DirectMusicAllTypes), - X(GUID_NOTIFICATION_CHORD), - X(GUID_NOTIFICATION_COMMAND), - X(GUID_NOTIFICATION_MEASUREANDBEAT), - X(GUID_NOTIFICATION_PERFORMANCE), - X(GUID_NOTIFICATION_RECOMPOSE), - X(GUID_NOTIFICATION_SEGMENT), - X(GUID_BandParam), - X(GUID_ChordParam), - X(GUID_CommandParam), - X(GUID_CommandParam2), - X(GUID_CommandParamNext), - X(GUID_IDirectMusicBand), - X(GUID_IDirectMusicChordMap), - X(GUID_IDirectMusicStyle), - X(GUID_MuteParam), - X(GUID_Play_Marker), - X(GUID_RhythmParam), - X(GUID_TempoParam), - X(GUID_TimeSignature), - X(GUID_Valid_Start_Time), - X(GUID_Clear_All_Bands), - X(GUID_ConnectToDLSCollection), - X(GUID_Disable_Auto_Download), - X(GUID_DisableTempo), - X(GUID_DisableTimeSig), - X(GUID_Download), - X(GUID_DownloadToAudioPath), - X(GUID_Enable_Auto_Download), - X(GUID_EnableTempo), - X(GUID_EnableTimeSig), - X(GUID_IgnoreBankSelectForGM), - X(GUID_SeedVariations), - X(GUID_StandardMIDIFile), - X(GUID_Unload), - X(GUID_UnloadFromAudioPath), - X(GUID_Variations), - X(GUID_PerfMasterTempo), - X(GUID_PerfMasterVolume), - X(GUID_PerfMasterGrooveLevel), - X(GUID_PerfAutoDownload), - X(GUID_DefaultGMCollection), - X(GUID_Synth_Default), - X(GUID_Buffer_Reverb), - X(GUID_Buffer_EnvReverb), - X(GUID_Buffer_Stereo), - X(GUID_Buffer_3D_Dry), - X(GUID_Buffer_Mono), - X(GUID_DMUS_PROP_GM_Hardware), - X(GUID_DMUS_PROP_GS_Capable), - X(GUID_DMUS_PROP_GS_Hardware), - X(GUID_DMUS_PROP_DLS1), - X(GUID_DMUS_PROP_DLS2), - X(GUID_DMUS_PROP_Effects), - X(GUID_DMUS_PROP_INSTRUMENT2), - X(GUID_DMUS_PROP_LegacyCaps), - X(GUID_DMUS_PROP_MemorySize), - X(GUID_DMUS_PROP_SampleMemorySize), - X(GUID_DMUS_PROP_SamplePlaybackRate), - X(GUID_DMUS_PROP_SetSynthSink), - X(GUID_DMUS_PROP_SinkUsesDSound), - X(GUID_DMUS_PROP_SynthSink_DSOUND), - X(GUID_DMUS_PROP_SynthSink_WAVE), - X(GUID_DMUS_PROP_Volume), - X(GUID_DMUS_PROP_WavesReverb), - X(GUID_DMUS_PROP_WriteLatency), - X(GUID_DMUS_PROP_WritePeriod), - X(GUID_DMUS_PROP_XG_Capable), - X(GUID_DMUS_PROP_XG_Hardware) - }; -#undef X - - if (!id) - return "(null)"; - - for (i = 0; i < ARRAY_SIZE(guids); i++) - if (IsEqualGUID(id, guids[i].guid)) - return guids[i].name; - - return debugstr_guid(id); -} - -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc) -{ - if (!desc || !TRACE_ON(dmfile)) - return; - - TRACE_(dmfile)("DMUS_OBJECTDESC (%p):", desc); - TRACE_(dmfile)(" - dwSize = %lu\n", desc->dwSize); - -#define X(flag) if (desc->dwValidData & flag) TRACE_(dmfile)(#flag " ") - TRACE_(dmfile)(" - dwValidData = %#08lx ( ", desc->dwValidData); - X(DMUS_OBJ_OBJECT); - X(DMUS_OBJ_CLASS); - X(DMUS_OBJ_NAME); - X(DMUS_OBJ_CATEGORY); - X(DMUS_OBJ_FILENAME); - X(DMUS_OBJ_FULLPATH); - X(DMUS_OBJ_URL); - X(DMUS_OBJ_VERSION); - X(DMUS_OBJ_DATE); - X(DMUS_OBJ_LOADED); - X(DMUS_OBJ_MEMORY); - X(DMUS_OBJ_STREAM); - TRACE_(dmfile)(")\n"); -#undef X - - if (desc->dwValidData & DMUS_OBJ_CLASS) - TRACE_(dmfile)(" - guidClass = %s\n", debugstr_dmguid(&desc->guidClass)); - if (desc->dwValidData & DMUS_OBJ_OBJECT) - TRACE_(dmfile)(" - guidObject = %s\n", debugstr_guid(&desc->guidObject)); - - if (desc->dwValidData & DMUS_OBJ_DATE) { - SYSTEMTIME time; - FileTimeToSystemTime(&desc->ftDate, &time); - TRACE_(dmfile)(" - ftDate = \'%04u-%02u-%02u %02u:%02u:%02u\'\n", - time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); - } - if (desc->dwValidData & DMUS_OBJ_VERSION) - TRACE_(dmfile)(" - vVersion = \'%u,%u,%u,%u\'\n", - HIWORD(desc->vVersion.dwVersionMS), LOWORD(desc->vVersion.dwVersionMS), - HIWORD(desc->vVersion.dwVersionLS), LOWORD(desc->vVersion.dwVersionLS)); - if (desc->dwValidData & DMUS_OBJ_NAME) - TRACE_(dmfile)(" - wszName = %s\n", debugstr_w(desc->wszName)); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - TRACE_(dmfile)(" - wszCategory = %s\n", debugstr_w(desc->wszCategory)); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - TRACE_(dmfile)(" - wszFileName = %s\n", debugstr_w(desc->wszFileName)); - if (desc->dwValidData & DMUS_OBJ_MEMORY) - TRACE_(dmfile)(" - llMemLength = 0x%s - pbMemData = %p\n", - wine_dbgstr_longlong(desc->llMemLength), desc->pbMemData); - if (desc->dwValidData & DMUS_OBJ_STREAM) - TRACE_(dmfile)(" - pStream = %p\n", desc->pStream); -} - - -/* RIFF format parsing */ -#define CHUNK_HDR_SIZE (sizeof(FOURCC) + sizeof(DWORD)) - -const char *debugstr_chunk(const struct chunk_entry *chunk) -{ - const char *type = ""; - - if (!chunk) - return "(null)"; - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - type = wine_dbg_sprintf("type %s, ", debugstr_fourcc(chunk->type)); - return wine_dbg_sprintf("%s chunk, %ssize %lu", debugstr_fourcc(chunk->id), type, chunk->size); -} - -static HRESULT stream_read(IStream *stream, void *data, ULONG size) -{ - ULONG read; - HRESULT hr; - - hr = IStream_Read(stream, data, size, &read); - if (FAILED(hr)) - TRACE_(dmfile)("IStream_Read failed: %#lx\n", hr); - else if (!read && read < size) { - /* All or nothing: Handle a partial read due to end of stream as an error */ - TRACE_(dmfile)("Short read: %lu < %lu\n", read, size); - return E_FAIL; - } - - return hr; -} - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk) -{ - static const LARGE_INTEGER zero; - ULONGLONG ck_end = 0, p_end = 0; - HRESULT hr; - - hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &chunk->offset); - if (FAILED(hr)) - return hr; - assert(!(chunk->offset.QuadPart & 1)); - if (chunk->parent) { - p_end = chunk->parent->offset.QuadPart + CHUNK_HDR_SIZE + ((chunk->parent->size + 1) & ~1); - if (chunk->offset.QuadPart == p_end) - return S_FALSE; - ck_end = chunk->offset.QuadPart + CHUNK_HDR_SIZE; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk header in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - hr = stream_read(stream, chunk, CHUNK_HDR_SIZE); - if (hr != S_OK) - return hr; - if (chunk->parent) { - ck_end += (chunk->size + 1) & ~1; - if (ck_end > p_end) { - WARN_(dmfile)("No space for sub-chunk data in parent chunk: ends at offset %s > %s\n", - wine_dbgstr_longlong(ck_end), wine_dbgstr_longlong(p_end)); - return E_FAIL; - } - } - - if (chunk->id == FOURCC_LIST || chunk->id == FOURCC_RIFF) { - hr = stream_read(stream, &chunk->type, sizeof(FOURCC)); - if (hr != S_OK) - return hr != S_FALSE ? hr : E_FAIL; - } - - TRACE_(dmfile)("Returning %s\n", debugstr_chunk(chunk)); - - return S_OK; -} - -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER end; - - end.QuadPart = (chunk->offset.QuadPart + CHUNK_HDR_SIZE + chunk->size + 1) & ~(ULONGLONG)1; - - return IStream_Seek(stream, end, STREAM_SEEK_SET, NULL); -} - -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk) -{ - HRESULT hr; - - if (chunk->id) { - hr = stream_skip_chunk(stream, chunk); - if (FAILED(hr)) - return hr; - } - - return stream_get_chunk(stream, chunk); -} - -/* Reads chunk data of the form: - DWORD - size of array element - element[] - Array of elements - The caller needs to free() the array. -*/ -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size) -{ - DWORD size; - HRESULT hr; - - *array = NULL; - *count = 0; - - if (chunk->size < sizeof(DWORD)) { - WARN_(dmfile)("%s: Too short to read element size\n", debugstr_chunk(chunk)); - return E_FAIL; - } - if (FAILED(hr = stream_read(stream, &size, sizeof(DWORD)))) - return hr; - if (size != elem_size) { - WARN_(dmfile)("%s: Array element size mismatch: got %lu, expected %lu\n", - debugstr_chunk(chunk), size, elem_size); - return DMUS_E_UNSUPPORTED_STREAM; - } - - *count = (chunk->size - sizeof(DWORD)) / elem_size; - size = *count * elem_size; - if (!(*array = malloc(size))) return E_OUTOFMEMORY; - if (FAILED(hr = stream_read(stream, *array, size))) { - free(*array); - *array = NULL; - return hr; - } - - if (chunk->size > size + sizeof(DWORD)) { - WARN_(dmfile)("%s: Extraneous data at end of array\n", debugstr_chunk(chunk)); - stream_skip_chunk(stream, chunk); - return S_FALSE; - } - return S_OK; -} - -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size) -{ - if (chunk->size != size) { - WARN_(dmfile)("Chunk %s (size %lu, offset %s) doesn't contains the expected data size %lu\n", - debugstr_fourcc(chunk->id), chunk->size, - wine_dbgstr_longlong(chunk->offset.QuadPart), size); - return E_FAIL; - } - return stream_read(stream, data, size); -} - -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size) -{ - ULONG len; - HRESULT hr; - - hr = IStream_Read(stream, str, min(chunk->size, size), &len); - if (FAILED(hr)) - return hr; - - /* Don't assume the string is properly zero terminated */ - str[min(len, size - 1)] = 0; - - if (len < chunk->size) - return S_FALSE; - return S_OK; -} - - - -/* Generic IDirectMusicObject methods */ -static inline struct dmobject *impl_from_IDirectMusicObject(IDirectMusicObject *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IDirectMusicObject_iface); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - - TRACE("(%p/%p)->(%p)\n", iface, This, desc); - - if (!desc) - return E_POINTER; - - memcpy(desc, &This->desc, This->desc.dwSize); - - return S_OK; -} - -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc) -{ - struct dmobject *This = impl_from_IDirectMusicObject(iface); - HRESULT ret = S_OK; - - TRACE("(%p, %p)\n", iface, desc); - - if (!desc) - return E_POINTER; - - /* Immutable property */ - if (desc->dwValidData & DMUS_OBJ_CLASS) - { - desc->dwValidData &= ~DMUS_OBJ_CLASS; - ret = S_FALSE; - } - /* Set only valid fields */ - if (desc->dwValidData & DMUS_OBJ_OBJECT) - This->desc.guidObject = desc->guidObject; - if (desc->dwValidData & DMUS_OBJ_NAME) - lstrcpynW(This->desc.wszName, desc->wszName, DMUS_MAX_NAME); - if (desc->dwValidData & DMUS_OBJ_CATEGORY) - lstrcpynW(This->desc.wszCategory, desc->wszCategory, DMUS_MAX_CATEGORY); - if (desc->dwValidData & DMUS_OBJ_FILENAME) - lstrcpynW(This->desc.wszFileName, desc->wszFileName, DMUS_MAX_FILENAME); - if (desc->dwValidData & DMUS_OBJ_VERSION) - This->desc.vVersion = desc->vVersion; - if (desc->dwValidData & DMUS_OBJ_DATE) - This->desc.ftDate = desc->ftDate; - if (desc->dwValidData & DMUS_OBJ_MEMORY) { - This->desc.llMemLength = desc->llMemLength; - memcpy(This->desc.pbMemData, desc->pbMemData, desc->llMemLength); - } - if (desc->dwValidData & DMUS_OBJ_STREAM) - IStream_Clone(desc->pStream, &This->desc.pStream); - - This->desc.dwValidData |= desc->dwValidData; - - return ret; -} - -/* Helper for IDirectMusicObject::ParseDescriptor */ -static inline void info_get_name(IStream *stream, const struct chunk_entry *info, - DMUS_OBJECTDESC *desc) -{ - struct chunk_entry chunk = {.parent = info}; - char name[DMUS_MAX_NAME]; - ULONG len; - HRESULT hr = E_FAIL; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == mmioFOURCC('I','N','A','M')) - hr = IStream_Read(stream, name, min(chunk.size, sizeof(name)), &len); - - if (SUCCEEDED(hr)) { - len = MultiByteToWideChar(CP_ACP, 0, name, len, desc->wszName, sizeof(desc->wszName)); - desc->wszName[min(len, sizeof(desc->wszName) - 1)] = 0; - desc->dwValidData |= DMUS_OBJ_NAME; - } -} - -static inline void unfo_get_name(IStream *stream, const struct chunk_entry *unfo, - DMUS_OBJECTDESC *desc, BOOL inam) -{ - struct chunk_entry chunk = {.parent = unfo}; - - while (stream_next_chunk(stream, &chunk) == S_OK) - if (chunk.id == DMUS_FOURCC_UNAM_CHUNK || (inam && chunk.id == mmioFOURCC('I','N','A','M'))) - if (stream_chunk_get_wstr(stream, &chunk, desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; -} - -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported) -{ - struct chunk_entry chunk = {.parent = riff}; - HRESULT hr; - - TRACE("Looking for %#lx in %p: %s\n", supported, stream, debugstr_chunk(riff)); - - desc->dwValidData = 0; - desc->dwSize = sizeof(*desc); - - while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { - switch (chunk.id) { - case DMUS_FOURCC_CATEGORY_CHUNK: - if ((supported & DMUS_OBJ_CATEGORY) && stream_chunk_get_wstr(stream, &chunk, - desc->wszCategory, sizeof(desc->wszCategory)) == S_OK) - desc->dwValidData |= DMUS_OBJ_CATEGORY; - break; - case DMUS_FOURCC_DATE_CHUNK: - if ((supported & DMUS_OBJ_DATE) && stream_chunk_get_data(stream, &chunk, - &desc->ftDate, sizeof(desc->ftDate)) == S_OK) - desc->dwValidData |= DMUS_OBJ_DATE; - break; - case DMUS_FOURCC_FILE_CHUNK: - if ((supported & DMUS_OBJ_FILENAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_FILENAME; - break; - case DMUS_FOURCC_GUID_CHUNK: - if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, - &desc->guidObject, sizeof(desc->guidObject)) == S_OK) - desc->dwValidData |= DMUS_OBJ_OBJECT; - break; - case DMUS_FOURCC_NAME_CHUNK: - if ((supported & DMUS_OBJ_NAME) && stream_chunk_get_wstr(stream, &chunk, - desc->wszName, sizeof(desc->wszName)) == S_OK) - desc->dwValidData |= DMUS_OBJ_NAME; - break; - case DMUS_FOURCC_VERSION_CHUNK: - if ((supported & DMUS_OBJ_VERSION) && stream_chunk_get_data(stream, &chunk, - &desc->vVersion, sizeof(desc->vVersion)) == S_OK) - desc->dwValidData |= DMUS_OBJ_VERSION; - break; - case FOURCC_LIST: - if (chunk.type == DMUS_FOURCC_UNFO_LIST && (supported & DMUS_OBJ_NAME)) - unfo_get_name(stream, &chunk, desc, supported & DMUS_OBJ_NAME_INAM); - else if (chunk.type == DMUS_FOURCC_INFO_LIST && (supported & DMUS_OBJ_NAME_INFO)) - info_get_name(stream, &chunk, desc); - break; - } - } - TRACE("Found %#lx\n", desc->dwValidData); - - return hr; -} - -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj) -{ - struct chunk_entry chunk = {.parent = list}; - IDirectMusicGetLoader *getloader; - IDirectMusicLoader *loader; - DMUS_OBJECTDESC desc; - DMUS_IO_REFERENCE reference; - HRESULT hr; - - if (FAILED(hr = stream_next_chunk(stream, &chunk))) - return hr; - if (chunk.id != DMUS_FOURCC_REF_CHUNK) - return DMUS_E_UNSUPPORTED_STREAM; - - if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &reference, sizeof(reference)))) { - WARN("Failed to read data of %s\n", debugstr_chunk(&chunk)); - return hr; - } - TRACE("REFERENCE guidClassID %s, dwValidData %#lx\n", debugstr_dmguid(&reference.guidClassID), - reference.dwValidData); - - if (FAILED(hr = dmobj_parsedescriptor(stream, list, &desc, reference.dwValidData))) - return hr; - desc.guidClass = reference.guidClassID; - desc.dwValidData |= DMUS_OBJ_CLASS; - dump_DMUS_OBJECTDESC(&desc); - - if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) - return hr; - hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); - IDirectMusicGetLoader_Release(getloader); - if (FAILED(hr)) - return hr; - - hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); - IDirectMusicLoader_Release(loader); - - return hr; -} - -/* Generic IPersistStream methods */ -static inline struct dmobject *impl_from_IPersistStream(IPersistStream *iface) -{ - return CONTAINING_RECORD(iface, struct dmobject, IPersistStream_iface); -} - -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_QueryInterface(This->outer_unk, riid, ret_iface); -} - -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_AddRef(This->outer_unk); -} - -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - return IUnknown_Release(This->outer_unk); -} - -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - struct dmobject *This = impl_from_IPersistStream(iface); - - TRACE("(%p, %p)\n", This, class); - - if (!class) - return E_POINTER; - - *class = This->desc.guidClass; - - return S_OK; -} - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class) -{ - TRACE("(%p, %p): method not implemented\n", iface, class); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface) -{ - TRACE("(%p): method not implemented, always returning S_FALSE\n", iface); - return S_FALSE; -} - -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty) -{ - TRACE("(%p, %p, %d): method not implemented\n", iface, stream, clear_dirty); - return E_NOTIMPL; -} - -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *size) -{ - TRACE("(%p, %p): method not implemented\n", iface, size); - return E_NOTIMPL; -} - - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk) -{ - dmobj->outer_unk = outer_unk; - dmobj->desc.dwSize = sizeof(dmobj->desc); - dmobj->desc.dwValidData = DMUS_OBJ_CLASS; - dmobj->desc.guidClass = *class; -} diff --git a/dlls/dmime/dmobject.h b/dlls/dmime/dmobject.h deleted file mode 100644 index 772be015c80..00000000000 --- a/dlls/dmime/dmobject.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Base IDirectMusicObject Implementation - * Keep in sync with the master in dlls/dmusic/dmobject.h - * - * Copyright (C) 2014 Michael Stefaniuc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "wine/debug.h" - -/* RIFF stream parsing */ -struct chunk_entry; -struct chunk_entry { - FOURCC id; - DWORD size; - FOURCC type; /* valid only for LIST and RIFF chunks */ - ULARGE_INTEGER offset; /* chunk offset from start of stream */ - const struct chunk_entry *parent; /* enclosing RIFF or LIST chunk */ -}; - -HRESULT stream_get_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_next_chunk(IStream *stream, struct chunk_entry *chunk); -HRESULT stream_skip_chunk(IStream *stream, const struct chunk_entry *chunk); - -HRESULT stream_chunk_get_array(IStream *stream, const struct chunk_entry *chunk, void **array, - unsigned int *count, DWORD elem_size); -HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, void *data, - ULONG size); -HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, - ULONG size); - -static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart + sizeof(FOURCC) + sizeof(DWORD); - if (chunk->id == FOURCC_RIFF || chunk->id == FOURCC_LIST) - offset.QuadPart += sizeof(FOURCC); - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - -static inline HRESULT stream_reset_chunk_start(IStream *stream, const struct chunk_entry *chunk) -{ - LARGE_INTEGER offset; - - offset.QuadPart = chunk->offset.QuadPart; - - return IStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); -} - - -/* IDirectMusicObject base object */ -struct dmobject { - IDirectMusicObject IDirectMusicObject_iface; - IPersistStream IPersistStream_iface; - IUnknown *outer_unk; - DMUS_OBJECTDESC desc; -}; - -void dmobject_init(struct dmobject *dmobj, const GUID *class, IUnknown *outer_unk); - -/* Generic IDirectMusicObject methods */ -HRESULT WINAPI dmobj_IDirectMusicObject_QueryInterface(IDirectMusicObject *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IDirectMusicObject_AddRef(IDirectMusicObject *iface); -ULONG WINAPI dmobj_IDirectMusicObject_Release(IDirectMusicObject *iface); -HRESULT WINAPI dmobj_IDirectMusicObject_GetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); -HRESULT WINAPI dmobj_IDirectMusicObject_SetDescriptor(IDirectMusicObject *iface, - DMUS_OBJECTDESC *desc); - -/* Helper for IDirectMusicObject::ParseDescriptor */ -HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, - DMUS_OBJECTDESC *desc, DWORD supported); -/* Additional supported flags for dmobj_parsedescriptor. - DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ -#define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ - -/* 'DMRF' (reference list) helper */ -HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, - IDirectMusicObject **dmobj); - -/* Generic IPersistStream methods */ -HRESULT WINAPI dmobj_IPersistStream_QueryInterface(IPersistStream *iface, REFIID riid, - void **ret_iface); -ULONG WINAPI dmobj_IPersistStream_AddRef(IPersistStream *iface); -ULONG WINAPI dmobj_IPersistStream_Release(IPersistStream *iface); -HRESULT WINAPI dmobj_IPersistStream_GetClassID(IPersistStream *iface, CLSID *class); - -/* IPersistStream methods not implemented in native */ -HRESULT WINAPI unimpl_IPersistStream_GetClassID(IPersistStream *iface, - CLSID *class); -HRESULT WINAPI unimpl_IPersistStream_IsDirty(IPersistStream *iface); -HRESULT WINAPI unimpl_IPersistStream_Save(IPersistStream *iface, IStream *stream, - BOOL clear_dirty); -HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, - ULARGE_INTEGER *size); - -/* Debugging helpers */ -const char *debugstr_chunk(const struct chunk_entry *chunk); -const char *debugstr_dmguid(const GUID *id); -void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); - -static inline const char *debugstr_fourcc(DWORD fourcc) -{ - if (!fourcc) return "''"; - return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), - (char)(fourcc >> 16), (char)(fourcc >> 24)); -} From 33c9a7ed55f46e4b7e9b2bacb382ef0bc555ff58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 14 Sep 2023 12:24:35 +0200 Subject: [PATCH 0879/1148] dmime: Rewrite IDirectMusicPerformance8_GetTime. (cherry picked from commit d407b3ca52cc5afdb6beee13672a06ca2e57d042) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 46 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index d277c5de043..5ae8941cd76 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -60,11 +60,12 @@ struct performance /** Message Processing */ HANDLE procThread; DWORD procThreadId; - REFERENCE_TIME procThreadStartTime; BOOL procThreadTicStarted; CRITICAL_SECTION safe; struct DMUS_PMSGItem *head; struct DMUS_PMSGItem *imm_head; + + IReferenceClock *master_clock; }; typedef struct DMUS_PMSGItem DMUS_PMSGItem; @@ -476,25 +477,21 @@ static HRESULT WINAPI performance_IsPlaying(IDirectMusicPerformance8 *iface, return S_FALSE; } -static HRESULT WINAPI performance_GetTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtNow, MUSIC_TIME *pmtNow) +static HRESULT WINAPI performance_GetTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *time, MUSIC_TIME *music_time) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); - HRESULT hr = S_OK; - REFERENCE_TIME rtCur = 0; + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + REFERENCE_TIME now; + HRESULT hr; - /*TRACE("(%p, %p, %p)\n", This, prtNow, pmtNow); */ - if (This->procThreadTicStarted) { - rtCur = ((REFERENCE_TIME) GetTickCount() * 10000) - This->procThreadStartTime; - } else { - /*return DMUS_E_NO_MASTER_CLOCK;*/ - } - if (NULL != prtNow) { - *prtNow = rtCur; - } - if (NULL != pmtNow) { - hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, rtCur, pmtNow); - } - return hr; + TRACE("(%p, %p, %p)\n", iface, time, music_time); + + if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK; + if (FAILED(hr = IReferenceClock_GetTime(This->master_clock, &now))) return hr; + + if (time) *time = now; + if (music_time) hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, now, music_time); + + return hr; } static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULONG cb, DMUS_PMSG **ppPMSG) @@ -873,6 +870,11 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) This->procThreadTicStarted = FALSE; CloseHandle(This->procThread); } + if (This->master_clock) + { + IReferenceClock_Release(This->master_clock); + This->master_clock = NULL; + } if (This->dsound) { IDirectSound_Release(This->dsound); This->dsound = NULL; @@ -955,6 +957,9 @@ static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDi IDirectMusic8_AddRef(This->dmusic); } + if (FAILED(hr = IDirectMusic_GetMasterClock(This->dmusic, NULL, &This->master_clock))) + goto error; + if (!dsound || !*dsound) { hr = DirectSoundCreate8(NULL, (IDirectSound8 **)&This->dsound, NULL); if (FAILED(hr)) @@ -1006,6 +1011,11 @@ static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDi return S_OK; error: + if (This->master_clock) + { + IReferenceClock_Release(This->master_clock); + This->master_clock = NULL; + } if (This->dsound) { IDirectSound_Release(This->dsound); This->dsound = NULL; From f19139c953f7209fbb8be3ce9e5c1ffbae44a82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 14 Sep 2023 12:28:17 +0200 Subject: [PATCH 0880/1148] dmime: Implement MusicToReferenceTime and ReferenceToMusicTime. (cherry picked from commit 52ae3fada0baa400936997ce3dfb997ddbe03dab) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 40 ++++++++++++++++++++++++++++++++-------- dlls/dmime/tests/dmime.c | 28 ++++++++++++++-------------- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 5ae8941cd76..7ce8ccb0d84 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -66,6 +66,7 @@ struct performance struct DMUS_PMSGItem *imm_head; IReferenceClock *master_clock; + REFERENCE_TIME init_time; }; typedef struct DMUS_PMSGItem DMUS_PMSGItem; @@ -451,21 +452,41 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS } static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 *iface, - MUSIC_TIME mtTime, REFERENCE_TIME *prtTime) + MUSIC_TIME music_time, REFERENCE_TIME *time) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); - FIXME("(%p, %ld, %p): stub\n", This, mtTime, prtTime); - return S_OK; + FIXME("(%p, %ld, %p): semi-stub\n", This, music_time, time); + + if (!time) return E_POINTER; + *time = 0; + + if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK; + + /* FIXME: This should be (music_time * 60) / (DMUS_PPQ * tempo) + * but it gives innacurate results */ + *time = This->init_time + (music_time * 6510); + + return S_OK; } static HRESULT WINAPI performance_ReferenceToMusicTime(IDirectMusicPerformance8 *iface, - REFERENCE_TIME rtTime, MUSIC_TIME *pmtTime) + REFERENCE_TIME time, MUSIC_TIME *music_time) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); - FIXME("(%p, 0x%s, %p): stub\n", This, wine_dbgstr_longlong(rtTime), pmtTime); - return S_OK; + FIXME("(%p, %I64d, %p): semi-stub\n", This, time, music_time); + + if (!music_time) return E_POINTER; + *music_time = 0; + + if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK; + + /* FIXME: This should be (time * DMUS_PPQ * tempo) / 60 + * but it gives innacurate results */ + *music_time = (time - This->init_time) / 6510; + + return S_OK; } static HRESULT WINAPI performance_IsPlaying(IDirectMusicPerformance8 *iface, @@ -1006,6 +1027,9 @@ static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDi *dmusic = (IDirectMusic *)This->dmusic; IDirectMusic_AddRef(*dmusic); } + + if (FAILED(hr = IDirectMusicPerformance8_GetTime(iface, &This->init_time, NULL))) return hr; + PostMessageToProcessMsgThread(This, PROCESSMSG_START); return S_OK; diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index e178960b18f..8999d5564db 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1650,18 +1650,18 @@ static void test_performance_time(void) hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 0, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 0, &time); - todo_wine ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); - todo_wine ok(time == 0, "got %I64d\n", time); + ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); + ok(time == 0, "got %I64d\n", time); hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, 0, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); music_time = 0xdeadbeef; hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, 0, &music_time); - todo_wine ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); - todo_wine ok(music_time == 0, "got %ld\n", music_time); + ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); + ok(music_time == 0, "got %ld\n", music_time); dmusic = NULL; @@ -1684,38 +1684,38 @@ static void test_performance_time(void) time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 1, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time - init_time >= 6505, "got %I64d\n", time - init_time); + ok(time - init_time >= 6505, "got %I64d\n", time - init_time); ok(time - init_time <= 6515, "got %I64d\n", time - init_time); time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 1000, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time - init_time >= 1000 * 6505, "got %I64d\n", time - init_time); + ok(time - init_time >= 1000 * 6505, "got %I64d\n", time - init_time); ok(time - init_time <= 1000 * 6515, "got %I64d\n", time - init_time); time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 2000, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time - init_time >= 2000 * 6505, "got %I64d\n", time - init_time); + ok(time - init_time >= 2000 * 6505, "got %I64d\n", time - init_time); ok(time - init_time <= 2000 * 6515, "got %I64d\n", time - init_time); music_time = 0xdeadbeef; hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time, &music_time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(music_time == 0, "got %ld\n", music_time); + ok(music_time == 0, "got %ld\n", music_time); music_time = 0xdeadbeef; hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + 1000 * 6510, &music_time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(music_time == 1000, "got %ld\n", music_time); + ok(music_time == 1000, "got %ld\n", music_time); music_time = 0xdeadbeef; hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + 2000 * 6510, &music_time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(music_time == 2000, "got %ld\n", music_time); + ok(music_time == 2000, "got %ld\n", music_time); time = 0xdeadbeef; music_time = 0xdeadbeef; hr = IDirectMusicPerformance_GetTime(performance, &time, &music_time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time - init_time <= 200 * 10000, "got %I64d\n", time - init_time); - todo_wine ok(music_time == (time - init_time) / 6510, "got %ld\n", music_time); + ok(time - init_time <= 200 * 10000, "got %I64d\n", time - init_time); + ok(music_time == (time - init_time) / 6510, "got %ld\n", music_time); hr = IDirectMusicPerformance_CloseDown(performance); From f5fa4b6a150480a06588e6876a0cdc667bfee245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 10:44:16 +0200 Subject: [PATCH 0881/1148] dmime: Cleanup IDirectMusicPerformance_AllocPMsg. (cherry picked from commit 9d600ce191ffcf3af374142a7b3a8654b67aa8d5) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 27 ++++++++++++--------------- dlls/dmime/tests/dmime.c | 2 +- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 7ce8ccb0d84..7cf446d9816 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -81,7 +81,6 @@ struct DMUS_PMSGItem { }; #define DMUS_PMSGToItem(pMSG) ((DMUS_PMSGItem*) (((unsigned char*) pPMSG) - offsetof(DMUS_PMSGItem, pMsg))) -#define DMUS_ItemToPMSG(pItem) (&(pItem->pMsg)) #define DMUS_ItemRemoveFromQueue(This,pItem) \ {\ if (pItem->prev) pItem->prev->next = pItem->next;\ @@ -515,23 +514,21 @@ static HRESULT WINAPI performance_GetTime(IDirectMusicPerformance8 *iface, REFER return hr; } -static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULONG cb, DMUS_PMSG **ppPMSG) +static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULONG size, DMUS_PMSG **msg) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); - DMUS_PMSGItem* pItem = NULL; + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + DMUS_PMSGItem *message; - FIXME("(%p, %ld, %p): stub\n", This, cb, ppPMSG); + TRACE("(%p, %ld, %p)\n", This, size, msg); - if (sizeof(DMUS_PMSG) > cb) { - return E_INVALIDARG; - } - if (NULL == ppPMSG) { - return E_POINTER; - } - if (!(pItem = calloc(1, cb - sizeof(DMUS_PMSG) + sizeof(DMUS_PMSGItem)))) return E_OUTOFMEMORY; - pItem->pMsg.dwSize = cb; - *ppPMSG = DMUS_ItemToPMSG(pItem); - return S_OK; + if (!msg) return E_POINTER; + if (size < sizeof(DMUS_PMSG)) return E_INVALIDARG; + + if (!(message = calloc(1, size - sizeof(DMUS_PMSG) + sizeof(DMUS_PMSGItem)))) return E_OUTOFMEMORY; + message->pMsg.dwSize = size; + *msg = &message->pMsg; + + return S_OK; } static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *pPMSG) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 8999d5564db..5a1d5c9733e 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1747,7 +1747,7 @@ static void test_performance_pmsg(void) hr = IDirectMusicPerformance_AllocPMsg(performance, 0, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(DMUS_PMSG) - 1, &msg); ok(hr == E_INVALIDARG, "got %#lx\n", hr); From c699f0b07188d3bdcfad47d3237426b8532ae78c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 10:45:43 +0200 Subject: [PATCH 0882/1148] dmime: Cleanup IDirectMusicPerformance_FreePMsg. (cherry picked from commit 4adeeb72d3e023bcefe0adebff9565ca45a8a1a9) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 47 +++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 7cf446d9816..bcb7faa405e 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -80,7 +80,7 @@ struct DMUS_PMSGItem { DMUS_PMSG pMsg; }; -#define DMUS_PMSGToItem(pMSG) ((DMUS_PMSGItem*) (((unsigned char*) pPMSG) - offsetof(DMUS_PMSGItem, pMsg))) +#define DMUS_PMSGToItem(pMSG) ((DMUS_PMSGItem *)(((unsigned char *)pMSG) - offsetof(DMUS_PMSGItem, pMsg))) #define DMUS_ItemRemoveFromQueue(This,pItem) \ {\ if (pItem->prev) pItem->prev->next = pItem->next;\ @@ -531,37 +531,30 @@ static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULO return S_OK; } -static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *pPMSG) +static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); - DMUS_PMSGItem* pItem = NULL; - - FIXME("(%p, %p): stub\n", This, pPMSG); - - if (NULL == pPMSG) { - return E_POINTER; - } - pItem = DMUS_PMSGToItem(pPMSG); - if (pItem->bInUse) { - /** prevent for freeing PMsg in queue (ie to be processed) */ - return DMUS_E_CANNOT_FREE; - } - /** now we can remove it safely */ - EnterCriticalSection(&This->safe); - DMUS_ItemRemoveFromQueue( This, pItem ); - LeaveCriticalSection(&This->safe); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + DMUS_PMSGItem *message; + HRESULT hr; - if (pPMSG->pTool) - IDirectMusicTool_Release(pPMSG->pTool); + TRACE("(%p, %p)\n", This, msg); - if (pPMSG->pGraph) - IDirectMusicGraph_Release(pPMSG->pGraph); + if (!msg) return E_POINTER; + message = DMUS_PMSGToItem(msg); - if (pPMSG->punkUser) - IUnknown_Release(pPMSG->punkUser); + EnterCriticalSection(&This->safe); + hr = message->bInUse ? DMUS_E_CANNOT_FREE : S_OK; + LeaveCriticalSection(&This->safe); - free(pItem); - return S_OK; + if (SUCCEEDED(hr)) + { + if (msg->pTool) IDirectMusicTool_Release(msg->pTool); + if (msg->pGraph) IDirectMusicGraph_Release(msg->pGraph); + if (msg->punkUser) IUnknown_Release(msg->punkUser); + free(message); + } + + return hr; } static HRESULT WINAPI performance_GetGraph(IDirectMusicPerformance8 *iface, IDirectMusicGraph **graph) From e835ae78f4d1eecb09916d8374b49519a12b2897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 10:52:17 +0200 Subject: [PATCH 0883/1148] dmime: Cleanup IDirectMusicPerformance_SendPMsg. (cherry picked from commit 0c4e1e435243f1eff77ef8dc05008d7e3228cc0d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 99 +++++++++++++++++++++------------------- dlls/dmime/tests/dmime.c | 8 ++-- 2 files changed, 57 insertions(+), 50 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index bcb7faa405e..d08f72d8d1f 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -399,55 +399,62 @@ static HRESULT WINAPI performance_GetBumperLength(IDirectMusicPerformance8 *ifac return S_OK; } -static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *pPMSG) +static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); - DMUS_PMSGItem* pItem = NULL; - DMUS_PMSGItem* it = NULL; - DMUS_PMSGItem* prev_it = NULL; - DMUS_PMSGItem** queue = NULL; + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + DMUS_PMSGItem *message; + DMUS_PMSGItem *it = NULL; + DMUS_PMSGItem *prev_it = NULL; + DMUS_PMSGItem **queue; + HRESULT hr; - FIXME("(%p, %p): stub\n", This, pPMSG); - - if (NULL == pPMSG) { - return E_POINTER; - } - pItem = DMUS_PMSGToItem(pPMSG); - if (pItem->bInUse) { - return DMUS_E_ALREADY_SENT; - } - - /* TODO: Valid Flags */ - /* TODO: DMUS_PMSGF_MUSICTIME */ - pItem->rtItemTime = pPMSG->rtTime; - - if (pPMSG->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE) { - queue = &This->imm_head; - } else { - queue = &This->head; - } + FIXME("(%p, %p): semi-stub\n", This, msg); - EnterCriticalSection(&This->safe); - for (it = *queue; NULL != it && it->rtItemTime < pItem->rtItemTime; it = it->next) { - prev_it = it; - } - if (NULL == prev_it) { - pItem->prev = NULL; - if (NULL != *queue) pItem->next = (*queue)->next; - /*assert( NULL == pItem->next->prev );*/ - if (NULL != pItem->next) pItem->next->prev = pItem; - *queue = pItem; - } else { - pItem->prev = prev_it; - pItem->next = prev_it->next; - prev_it->next = pItem; - if (NULL != pItem->next) pItem->next->prev = pItem; - } - LeaveCriticalSection(&This->safe); - - /** now in use, prevent from stupid Frees */ - pItem->bInUse = TRUE; - return S_OK; + if (!msg) return E_POINTER; + if (!This->dmusic) return DMUS_E_NO_MASTER_CLOCK; + if (!(msg->dwFlags & (DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_REFTIME))) return E_INVALIDARG; + + if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE) queue = &This->imm_head; + else queue = &This->head; + + message = DMUS_PMSGToItem(msg); + + EnterCriticalSection(&This->safe); + + if (message->bInUse) + hr = DMUS_E_ALREADY_SENT; + else + { + /* TODO: Valid Flags */ + /* TODO: DMUS_PMSGF_MUSICTIME */ + message->rtItemTime = msg->rtTime; + + for (it = *queue; NULL != it && it->rtItemTime < message->rtItemTime; it = it->next) + prev_it = it; + + if (!prev_it) + { + message->prev = NULL; + if (*queue) message->next = (*queue)->next; + /*assert( NULL == message->next->prev );*/ + if (message->next) message->next->prev = message; + *queue = message; + } + else + { + message->prev = prev_it; + message->next = prev_it->next; + prev_it->next = message; + if (message->next) message->next->prev = message; + } + + message->bInUse = TRUE; + hr = S_OK; + } + + LeaveCriticalSection(&This->safe); + + return hr; } static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 *iface, diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 5a1d5c9733e..f5d64498abb 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1767,12 +1767,12 @@ static void test_performance_pmsg(void) hr = IDirectMusicPerformance_SendPMsg(performance, NULL); ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicPerformance_SendPMsg(performance, msg); - todo_wine ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); + ok(hr == DMUS_E_NO_MASTER_CLOCK, "got %#lx\n", hr); hr = IDirectMusicPerformance_FreePMsg(performance, NULL); ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicPerformance_FreePMsg(performance, msg); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_Init(performance, NULL, 0, 0); @@ -1802,7 +1802,7 @@ static void test_performance_pmsg(void) ok(!msg->dwGroupID, "got %ld\n", msg->dwGroupID); ok(!msg->punkUser, "got %p\n", msg->punkUser); hr = IDirectMusicPerformance_SendPMsg(performance, msg); - todo_wine ok(hr == E_INVALIDARG, "got %#lx\n", hr); + ok(hr == E_INVALIDARG, "got %#lx\n", hr); hr = IDirectMusicPerformance8_ClonePMsg((IDirectMusicPerformance8 *)performance, msg, NULL); todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); @@ -1816,7 +1816,7 @@ static void test_performance_pmsg(void) msg->mtTime = 500; msg->dwFlags = DMUS_PMSGF_MUSICTIME; hr = IDirectMusicPerformance_SendPMsg(performance, msg); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_SendPMsg(performance, msg); ok(hr == DMUS_E_ALREADY_SENT, "got %#lx\n", hr); hr = IDirectMusicPerformance_FreePMsg(performance, msg); From 99d5abb15df6577872522ba6b574e67739eea91a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 19:20:59 +0200 Subject: [PATCH 0884/1148] dmime: Implement IDirectMusicPerformance8_ClonePMsg. (cherry picked from commit 4b787aa9b3ff97fcf0a0527e0aeffb2aba6c3b5e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 19 ++++++++++++++----- dlls/dmime/tests/dmime.c | 9 ++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index d08f72d8d1f..b3fd4851d45 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1071,13 +1071,22 @@ static HRESULT WINAPI performance_StopEx(IDirectMusicPerformance8 *iface, IUnkno return S_OK; } -static HRESULT WINAPI performance_ClonePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *pSourcePMSG, - DMUS_PMSG **ppCopyPMSG) +static HRESULT WINAPI performance_ClonePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg, DMUS_PMSG **clone) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + HRESULT hr; - FIXME("(%p, %p, %p): stub\n", This, pSourcePMSG, ppCopyPMSG); - return S_OK; + TRACE("(%p, %p, %p)\n", This, msg, clone); + + if (!msg || !clone) return E_POINTER; + if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(iface, msg->dwSize, clone))) return hr; + + memcpy(*clone, msg, msg->dwSize); + if (msg->pTool) IDirectMusicTool_AddRef(msg->pTool); + if (msg->pGraph) IDirectMusicGraph_AddRef(msg->pGraph); + if (msg->punkUser) IUnknown_AddRef(msg->punkUser); + + return S_OK; } static HRESULT WINAPI performance_CreateAudioPath(IDirectMusicPerformance8 *iface, diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index f5d64498abb..26efdd4c72f 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1805,13 +1805,13 @@ static void test_performance_pmsg(void) ok(hr == E_INVALIDARG, "got %#lx\n", hr); hr = IDirectMusicPerformance8_ClonePMsg((IDirectMusicPerformance8 *)performance, msg, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicPerformance8_ClonePMsg((IDirectMusicPerformance8 *)performance, NULL, &clone); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); clone = NULL; hr = IDirectMusicPerformance8_ClonePMsg((IDirectMusicPerformance8 *)performance, msg, &clone); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(clone != NULL, "got %p\n", clone); + ok(clone != NULL, "got %p\n", clone); msg->mtTime = 500; msg->dwFlags = DMUS_PMSGF_MUSICTIME; @@ -1822,8 +1822,7 @@ static void test_performance_pmsg(void) hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == DMUS_E_CANNOT_FREE, "got %#lx\n", hr); - if (!clone) hr = S_OK; - else hr = IDirectMusicPerformance_FreePMsg(performance, clone); + hr = IDirectMusicPerformance_FreePMsg(performance, clone); ok(hr == S_OK, "got %#lx\n", hr); From 350ed2e02af2f92947d4f8ded1884f6b64b24a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 13:42:29 +0200 Subject: [PATCH 0885/1148] dmusic: Rewrite collection lins list parsing. (cherry picked from commit bb2a8312b129a0270f5fc74bc53217edff895772) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/buffer.c | 1 - dlls/dmusic/clock.c | 1 - dlls/dmusic/collection.c | 67 ++++++++++++++++++------------------ dlls/dmusic/dmusic.c | 1 - dlls/dmusic/dmusic_main.c | 1 - dlls/dmusic/dmusic_private.h | 5 ++- dlls/dmusic/download.c | 1 - dlls/dmusic/instrument.c | 28 +++------------ dlls/dmusic/port.c | 1 - 9 files changed, 43 insertions(+), 63 deletions(-) diff --git a/dlls/dmusic/buffer.c b/dlls/dmusic/buffer.c index a2ad137fe4b..7f6087fcaaf 100644 --- a/dlls/dmusic/buffer.c +++ b/dlls/dmusic/buffer.c @@ -20,7 +20,6 @@ */ #include "dmusic_private.h" -#include "dmobject.h" #include "initguid.h" #include "dmksctrl.h" diff --git a/dlls/dmusic/clock.c b/dlls/dmusic/clock.c index 7b574dd4432..35df27056fb 100644 --- a/dlls/dmusic/clock.c +++ b/dlls/dmusic/clock.c @@ -19,7 +19,6 @@ */ #include "dmusic_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 2d4a7f855ae..acd85768cec 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -19,7 +19,6 @@ */ #include "dmusic_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); WINE_DECLARE_DEBUG_CHANNEL(dmfile); @@ -177,6 +176,34 @@ static const IDirectMusicCollectionVtbl collection_vtbl = collection_EnumInstrument, }; +static HRESULT parse_lins_list(struct collection *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + struct instrument_entry *entry; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_LIST, FOURCC_INS): + if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY; + hr = instrument_create_from_chunk(stream, &chunk, &entry->instrument); + if (SUCCEEDED(hr)) list_add_tail(&This->instruments, &entry->entry); + else free(entry); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + return hr; +} + static HRESULT WINAPI collection_object_ParseDescriptor(IDirectMusicObject *iface, IStream *stream, DMUS_OBJECTDESC *desc) { @@ -373,39 +400,13 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, break; } case FOURCC_LINS: { + struct chunk_entry lins_chunk = {.id = FOURCC_LIST, .size = chunk.dwSize, .type = chunk.fccID}; TRACE_(dmfile)(": instruments list\n"); - do { - IStream_Read(stream, &chunk, sizeof(FOURCC) + sizeof(DWORD), NULL); - ListCount[0] += sizeof(FOURCC) + sizeof(DWORD) + chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %#04lx)", debugstr_fourcc(chunk.fccID), chunk.dwSize); - switch (chunk.fccID) { - case FOURCC_LIST: { - IStream_Read(stream, &chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": LIST chunk of type %s", debugstr_fourcc(chunk.fccID)); - ListSize[1] = chunk.dwSize - sizeof(FOURCC); - ListCount[1] = 0; - switch (chunk.fccID) { - case FOURCC_INS: { - struct instrument_entry *entry = calloc(1, sizeof(*entry)); - TRACE_(dmfile)(": instrument list\n"); - liMove.QuadPart = -12; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - if (FAILED(instrument_create_from_stream(stream, &entry->instrument))) free(entry); - else list_add_tail(&This->instruments, &entry->entry); - break; - } - } - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = chunk.dwSize; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - TRACE_(dmfile)(": ListCount[0] = %ld < ListSize[0] = %ld\n", ListCount[0], ListSize[0]); - } while (ListCount[0] < ListSize[0]); + liMove.QuadPart = 0; + IStream_Seek(stream, liMove, STREAM_SEEK_CUR, &lins_chunk.offset); + lins_chunk.offset.QuadPart -= 12; + parse_lins_list(This, stream, &lins_chunk); + stream_skip_chunk(stream, &lins_chunk); break; } default: { diff --git a/dlls/dmusic/dmusic.c b/dlls/dmusic/dmusic.c index 57a39d1465c..822c35ba616 100644 --- a/dlls/dmusic/dmusic.c +++ b/dlls/dmusic/dmusic.c @@ -22,7 +22,6 @@ #include #include "dmusic_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); diff --git a/dlls/dmusic/dmusic_main.c b/dlls/dmusic/dmusic_main.c index f0aaca8947a..52607e1d936 100644 --- a/dlls/dmusic/dmusic_main.c +++ b/dlls/dmusic/dmusic_main.c @@ -35,7 +35,6 @@ #include "dmusici.h" #include "dmusic_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index b668f2f2559..57aca4e8ea7 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -44,6 +44,8 @@ #include "dmusics.h" #include "dmksctrl.h" +#include "dmobject.h" + /***************************************************************************** * Interfaces */ @@ -98,7 +100,8 @@ extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface); -extern HRESULT instrument_create_from_stream(IStream *stream, IDirectMusicInstrument **ret_iface); +extern HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, + IDirectMusicInstrument **ret_iface); /***************************************************************************** * IDirectMusic8Impl implementation structure diff --git a/dlls/dmusic/download.c b/dlls/dmusic/download.c index 6dac3e1b725..008e52edbfd 100644 --- a/dlls/dmusic/download.c +++ b/dlls/dmusic/download.c @@ -19,7 +19,6 @@ */ #include "dmusic_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index b62b053c542..aacbe6e8c88 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -19,7 +19,6 @@ */ #include "dmusic_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); @@ -303,9 +302,9 @@ static HRESULT parse_ins_chunk(struct instrument *This, IStream *stream, struct return hr; } -HRESULT instrument_create_from_stream(IStream *stream, IDirectMusicInstrument **ret_iface) +HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, + IDirectMusicInstrument **ret_iface) { - struct chunk_entry chunk = {0}; IDirectMusicInstrument *iface; struct instrument *This; HRESULT hr; @@ -315,23 +314,12 @@ HRESULT instrument_create_from_stream(IStream *stream, IDirectMusicInstrument ** if (FAILED(hr = instrument_create(&iface))) return hr; This = impl_from_IDirectMusicInstrument(iface); - if ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + if (FAILED(hr = parse_ins_chunk(This, stream, parent))) { - switch (MAKE_IDTYPE(chunk.id, chunk.type)) - { - case MAKE_IDTYPE(FOURCC_LIST, FOURCC_INS): - hr = parse_ins_chunk(This, stream, &chunk); - break; - - default: - WARN("Invalid instrument chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); - hr = E_INVALIDARG; - break; - } + IDirectMusicInstrument_Release(iface); + return DMUS_E_UNSUPPORTED_STREAM; } - if (FAILED(hr)) goto error; - if (TRACE_ON(dmusic)) { TRACE("Created DirectMusicInstrument (%p) ***\n", This); @@ -347,10 +335,4 @@ HRESULT instrument_create_from_stream(IStream *stream, IDirectMusicInstrument ** *ret_iface = iface; return S_OK; - -error: - IDirectMusicInstrument_Release(iface); - - stream_skip_chunk(stream, &chunk); - return DMUS_E_UNSUPPORTED_STREAM; } diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index 27e20b1cf81..7e3fa5a67eb 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -21,7 +21,6 @@ #include #include "dmusic_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); From ceccff5b3bccba04587fed6115299fb9396c23d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 22:21:53 +0200 Subject: [PATCH 0886/1148] dmusic: Rewrite collection ptbl chunk parsing. (cherry picked from commit af79bf4f226456f12025ac25156275f24dbc27bd) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 46 +++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index acd85768cec..4d690f597db 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -29,6 +29,14 @@ struct instrument_entry IDirectMusicInstrument *instrument; }; +struct pool +{ + POOLTABLE table; + POOLCUE cues[]; +}; + +C_ASSERT(sizeof(struct pool) == offsetof(struct pool, cues[0])); + struct collection { IDirectMusicCollection IDirectMusicCollection_iface; @@ -38,10 +46,8 @@ struct collection IStream *pStm; /* stream from which we load collection and later instruments */ CHAR *szCopyright; /* FIXME: should probably be placed somewhere else */ DLSHEADER *pHeader; - /* pool table */ - POOLTABLE *pPoolTable; - POOLCUE *pPoolCues; + struct pool *pool; struct list instruments; }; @@ -204,6 +210,29 @@ static HRESULT parse_lins_list(struct collection *This, IStream *stream, struct return hr; } +static HRESULT parse_ptbl_chunk(struct collection *This, IStream *stream, struct chunk_entry *chunk) +{ + struct pool *pool; + POOLTABLE table; + HRESULT hr; + UINT size; + + if (chunk->size < sizeof(table)) return E_INVALIDARG; + if (FAILED(hr = stream_read(stream, &table, sizeof(table)))) return hr; + if (chunk->size != table.cbSize + sizeof(POOLCUE) * table.cCues) return E_INVALIDARG; + if (table.cbSize != sizeof(table)) return E_INVALIDARG; + + size = offsetof(struct pool, cues[table.cCues]); + if (!(pool = malloc(size))) return E_OUTOFMEMORY; + pool->table = table; + + size = sizeof(POOLCUE) * table.cCues; + if (FAILED(hr = stream_read(stream, pool->cues, size))) free(pool); + else This->pool = pool; + + return hr; +} + static HRESULT WINAPI collection_object_ParseDescriptor(IDirectMusicObject *iface, IStream *stream, DMUS_OBJECTDESC *desc) { @@ -303,12 +332,13 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, break; } case FOURCC_PTBL: { + struct chunk_entry ptbl_chunk = {.id = FOURCC_LIST, .size = chunk.dwSize, .type = chunk.fccID}; TRACE_(dmfile)(": pool table chunk\n"); - This->pPoolTable = calloc(1, sizeof(POOLTABLE)); - IStream_Read(stream, This->pPoolTable, sizeof(POOLTABLE), NULL); - chunk.dwSize -= sizeof(POOLTABLE); - This->pPoolCues = calloc(This->pPoolTable->cCues, sizeof(POOLCUE)); - IStream_Read(stream, This->pPoolCues, chunk.dwSize, NULL); + liMove.QuadPart = 0; + IStream_Seek(stream, liMove, STREAM_SEEK_CUR, &ptbl_chunk.offset); + ptbl_chunk.offset.QuadPart -= 12; + parse_ptbl_chunk(This, stream, &ptbl_chunk); + stream_skip_chunk(stream, &ptbl_chunk); break; } case FOURCC_LIST: { From 079524db870cc3b83dd301fa89cf9052c1792bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 22:19:45 +0200 Subject: [PATCH 0887/1148] dmusic: Rewrite collection INFO list parsing. (cherry picked from commit 866d4998ed0a917e6906167ae6985c793d1d88c4) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 109 ++++++----------------------------- dlls/dmusic/dmobject.c | 7 +++ dlls/dmusic/dmobject.h | 1 + dlls/dmusic/dmusic_main.c | 5 -- dlls/dmusic/dmusic_private.h | 2 - 5 files changed, 25 insertions(+), 99 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 4d690f597db..168a49fa8c0 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -44,7 +44,6 @@ struct collection LONG ref; IStream *pStm; /* stream from which we load collection and later instruments */ - CHAR *szCopyright; /* FIXME: should probably be placed somewhere else */ DLSHEADER *pHeader; struct pool *pool; @@ -277,9 +276,10 @@ static const IDirectMusicObjectVtbl collection_object_vtbl = static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *stream) { + struct chunk_entry dls_chunk = {0}; struct collection *This = impl_from_IPersistStream(iface); DMUS_PRIVATE_CHUNK chunk; - DWORD StreamSize, StreamCount, ListSize[2], ListCount[2]; + DWORD StreamSize, StreamCount; LARGE_INTEGER liMove; /* used when skipping chunks */ IStream_AddRef(stream); /* add count for later references */ @@ -307,6 +307,21 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, return E_FAIL; } + dls_chunk.id = FOURCC_RIFF; + dls_chunk.size = chunk.dwSize; + dls_chunk.type = chunk.fccID; + liMove.QuadPart = 0; + IStream_Seek(stream, liMove, STREAM_SEEK_CUR, &dls_chunk.offset); + dls_chunk.offset.QuadPart -= 12; + if (FAILED(dmobj_parsedescriptor(stream, &dls_chunk, &This->dmobj.desc, + DMUS_OBJ_NAME_INFO|DMUS_OBJ_VERSION|DMUS_OBJ_OBJECT|DMUS_OBJ_GUID_DLID))) + { + liMove.QuadPart = StreamSize; + IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); /* skip the rest of the chunk */ + return E_FAIL; + } + stream_reset_chunk_data(stream, &dls_chunk); + TRACE_(dmfile)(": collection form\n"); do { IStream_Read(stream, &chunk, sizeof(FOURCC) + sizeof(DWORD), NULL); @@ -319,18 +334,6 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream_Read(stream, This->pHeader, chunk.dwSize, NULL); break; } - case FOURCC_DLID: { - TRACE_(dmfile)(": DLID (GUID) chunk\n"); - This->dmobj.desc.dwValidData |= DMUS_OBJ_OBJECT; - IStream_Read(stream, &This->dmobj.desc.guidObject, chunk.dwSize, NULL); - break; - } - case FOURCC_VERS: { - TRACE_(dmfile)(": version chunk\n"); - This->dmobj.desc.dwValidData |= DMUS_OBJ_VERSION; - IStream_Read(stream, &This->dmobj.desc.vVersion, chunk.dwSize, NULL); - break; - } case FOURCC_PTBL: { struct chunk_entry ptbl_chunk = {.id = FOURCC_LIST, .size = chunk.dwSize, .type = chunk.fccID}; TRACE_(dmfile)(": pool table chunk\n"); @@ -344,85 +347,7 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, case FOURCC_LIST: { IStream_Read(stream, &chunk.fccID, sizeof(FOURCC), NULL); TRACE_(dmfile)(": LIST chunk of type %s", debugstr_fourcc(chunk.fccID)); - ListSize[0] = chunk.dwSize - sizeof(FOURCC); - ListCount[0] = 0; switch (chunk.fccID) { - case DMUS_FOURCC_INFO_LIST: { - TRACE_(dmfile)(": INFO list\n"); - do { - IStream_Read(stream, &chunk, sizeof(FOURCC) + sizeof(DWORD), NULL); - ListCount[0] += sizeof(FOURCC) + sizeof(DWORD) + chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %#04lx)", debugstr_fourcc(chunk.fccID), chunk.dwSize); - switch (chunk.fccID) { - case mmioFOURCC('I','N','A','M'): { - CHAR szName[DMUS_MAX_NAME]; - TRACE_(dmfile)(": name chunk\n"); - This->dmobj.desc.dwValidData |= DMUS_OBJ_NAME; - IStream_Read(stream, szName, chunk.dwSize, NULL); - MultiByteToWideChar(CP_ACP, 0, szName, -1, This->dmobj.desc.wszName, DMUS_MAX_NAME); - if (even_or_odd(chunk.dwSize)) { - ListCount[0]++; - liMove.QuadPart = 1; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - } - break; - } - case mmioFOURCC('I','A','R','T'): { - TRACE_(dmfile)(": artist chunk (ignored)\n"); - if (even_or_odd(chunk.dwSize)) { - ListCount[0]++; - chunk.dwSize++; - } - liMove.QuadPart = chunk.dwSize; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - break; - } - case mmioFOURCC('I','C','O','P'): { - TRACE_(dmfile)(": copyright chunk\n"); - This->szCopyright = calloc(1, chunk.dwSize); - IStream_Read(stream, This->szCopyright, chunk.dwSize, NULL); - if (even_or_odd(chunk.dwSize)) { - ListCount[0]++; - liMove.QuadPart = 1; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - } - break; - } - case mmioFOURCC('I','S','B','J'): { - TRACE_(dmfile)(": subject chunk (ignored)\n"); - if (even_or_odd(chunk.dwSize)) { - ListCount[0]++; - chunk.dwSize++; - } - liMove.QuadPart = chunk.dwSize; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - break; - } - case mmioFOURCC('I','C','M','T'): { - TRACE_(dmfile)(": comment chunk (ignored)\n"); - if (even_or_odd(chunk.dwSize)) { - ListCount[0]++; - chunk.dwSize++; - } - liMove.QuadPart = chunk.dwSize; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - if (even_or_odd(chunk.dwSize)) { - ListCount[0]++; - chunk.dwSize++; - } - liMove.QuadPart = chunk.dwSize; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - TRACE_(dmfile)(": ListCount[0] = %ld < ListSize[0] = %ld\n", ListCount[0], ListSize[0]); - } while (ListCount[0] < ListSize[0]); - break; - } case FOURCC_WVPL: { TRACE_(dmfile)(": wave pool list (mark & skip)\n"); liMove.QuadPart = chunk.dwSize - sizeof(FOURCC); diff --git a/dlls/dmusic/dmobject.c b/dlls/dmusic/dmobject.c index 2bef5511851..3007aceef3e 100644 --- a/dlls/dmusic/dmobject.c +++ b/dlls/dmusic/dmobject.c @@ -587,7 +587,14 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, desc->wszFileName, sizeof(desc->wszFileName)) == S_OK) desc->dwValidData |= DMUS_OBJ_FILENAME; break; + case FOURCC_DLID: + if (!(supported & DMUS_OBJ_GUID_DLID)) break; + if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, + &desc->guidObject, sizeof(desc->guidObject)) == S_OK) + desc->dwValidData |= DMUS_OBJ_OBJECT; + break; case DMUS_FOURCC_GUID_CHUNK: + if ((supported & DMUS_OBJ_GUID_DLID)) break; if ((supported & DMUS_OBJ_OBJECT) && stream_chunk_get_data(stream, &chunk, &desc->guidObject, sizeof(desc->guidObject)) == S_OK) desc->dwValidData |= DMUS_OBJ_OBJECT; diff --git a/dlls/dmusic/dmobject.h b/dlls/dmusic/dmobject.h index 98069de8cc4..ae06935dfea 100644 --- a/dlls/dmusic/dmobject.h +++ b/dlls/dmusic/dmobject.h @@ -92,6 +92,7 @@ HRESULT dmobj_parsedescriptor(IStream *stream, const struct chunk_entry *riff, DMUS_OBJ_NAME is 'UNAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INAM 0x1000 /* 'INAM' chunk in UNFO list */ #define DMUS_OBJ_NAME_INFO 0x2000 /* 'INAM' chunk in INFO list */ +#define DMUS_OBJ_GUID_DLID 0x4000 /* 'dlid' chunk instead of 'guid' */ /* 'DMRF' (reference list) helper */ HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, diff --git a/dlls/dmusic/dmusic_main.c b/dlls/dmusic/dmusic_main.c index 52607e1d936..eabf9f131b9 100644 --- a/dlls/dmusic/dmusic_main.c +++ b/dlls/dmusic/dmusic_main.c @@ -165,11 +165,6 @@ void Patch2MIDILOCALE (DWORD dwPatch, LPMIDILOCALE pLocale) { pLocale->ulBank |= (dwPatch & F_INSTRUMENT_DRUMS); /* get drum bit */ } -/* check whether the given DWORD is even (return 0) or odd (return 1) */ -int even_or_odd (DWORD number) { - return (number & 0x1); /* basically, check if bit 0 is set ;) */ -} - /* generic flag-dumping function */ static const char* debugstr_flags (DWORD flags, const flag_info* names, size_t num_names){ char buffer[128] = "", *ptr = &buffer[0]; diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 57aca4e8ea7..3e184fa86da 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -214,8 +214,6 @@ extern DWORD MIDILOCALE2Patch (const MIDILOCALE *pLocale); /* MIDILOCALE from dwPatch */ extern void Patch2MIDILOCALE (DWORD dwPatch, LPMIDILOCALE pLocale); -/* check whether the given DWORD is even (return 0) or odd (return 1) */ -extern int even_or_odd (DWORD number); /* Dump whole DMUS_PORTPARAMS struct */ extern void dump_DMUS_PORTPARAMS(LPDMUS_PORTPARAMS params); From f9203a224aea09023c0a5d4b123601b9ce9e2e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 14 Sep 2023 13:13:22 +0200 Subject: [PATCH 0888/1148] dmusic: Rewrite collection DLS chunk parsing. (cherry picked from commit d30f914de624f395e9adca7200c48d1fc0b62bac) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 176 +++++++++++++---------------------- dlls/dmusic/dmusic_private.h | 6 -- 2 files changed, 63 insertions(+), 119 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 168a49fa8c0..f26806a1afb 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -21,7 +21,6 @@ #include "dmusic_private.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); -WINE_DECLARE_DEBUG_CHANNEL(dmfile); struct instrument_entry { @@ -43,9 +42,7 @@ struct collection struct dmobject dmobj; LONG ref; - IStream *pStm; /* stream from which we load collection and later instruments */ - DLSHEADER *pHeader; - + DLSHEADER header; struct pool *pool; struct list instruments; }; @@ -232,6 +229,49 @@ static HRESULT parse_ptbl_chunk(struct collection *This, IStream *stream, struct return hr; } +static HRESULT parse_dls_chunk(struct collection *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; + + if (FAILED(hr = dmobj_parsedescriptor(stream, parent, &This->dmobj.desc, + DMUS_OBJ_NAME_INFO|DMUS_OBJ_VERSION|DMUS_OBJ_OBJECT|DMUS_OBJ_GUID_DLID)) + || FAILED(hr = stream_reset_chunk_data(stream, parent))) + return hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case FOURCC_DLID: + case FOURCC_VERS: + case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_INFO_LIST): + /* already parsed by dmobj_parsedescriptor */ + break; + + case FOURCC_COLH: + hr = stream_chunk_get_data(stream, &chunk, &This->header, sizeof(This->header)); + break; + + case FOURCC_PTBL: + hr = parse_ptbl_chunk(This, stream, &chunk); + break; + + case MAKE_IDTYPE(FOURCC_LIST, FOURCC_LINS): + hr = parse_lins_list(This, stream, &chunk); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + return hr; +} + static HRESULT WINAPI collection_object_ParseDescriptor(IDirectMusicObject *iface, IStream *stream, DMUS_OBJECTDESC *desc) { @@ -273,121 +313,30 @@ static const IDirectMusicObjectVtbl collection_object_vtbl = collection_object_ParseDescriptor, }; -static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, - IStream *stream) +static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *stream) { - struct chunk_entry dls_chunk = {0}; struct collection *This = impl_from_IPersistStream(iface); - DMUS_PRIVATE_CHUNK chunk; - DWORD StreamSize, StreamCount; - LARGE_INTEGER liMove; /* used when skipping chunks */ - - IStream_AddRef(stream); /* add count for later references */ - This->pStm = stream; - - IStream_Read(stream, &chunk, sizeof(FOURCC) + sizeof(DWORD), NULL); - TRACE_(dmfile)(": %s chunk (size = %#04lx)", debugstr_fourcc(chunk.fccID), chunk.dwSize); - - if (chunk.fccID != FOURCC_RIFF) { - TRACE_(dmfile)(": unexpected chunk; loading failed)\n"); - liMove.QuadPart = chunk.dwSize; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); /* skip the rest of the chunk */ - return E_FAIL; - } + struct chunk_entry chunk = {0}; + HRESULT hr; - IStream_Read(stream, &chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": RIFF chunk of type %s", debugstr_fourcc(chunk.fccID)); - StreamSize = chunk.dwSize - sizeof(FOURCC); - StreamCount = 0; + TRACE("(%p, %p)\n", This, stream); - if (chunk.fccID != FOURCC_DLS) { - TRACE_(dmfile)(": unexpected chunk; loading failed)\n"); - liMove.QuadPart = StreamSize; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); /* skip the rest of the chunk */ - return E_FAIL; - } - - dls_chunk.id = FOURCC_RIFF; - dls_chunk.size = chunk.dwSize; - dls_chunk.type = chunk.fccID; - liMove.QuadPart = 0; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, &dls_chunk.offset); - dls_chunk.offset.QuadPart -= 12; - if (FAILED(dmobj_parsedescriptor(stream, &dls_chunk, &This->dmobj.desc, - DMUS_OBJ_NAME_INFO|DMUS_OBJ_VERSION|DMUS_OBJ_OBJECT|DMUS_OBJ_GUID_DLID))) + if ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { - liMove.QuadPart = StreamSize; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); /* skip the rest of the chunk */ - return E_FAIL; - } - stream_reset_chunk_data(stream, &dls_chunk); - - TRACE_(dmfile)(": collection form\n"); - do { - IStream_Read(stream, &chunk, sizeof(FOURCC) + sizeof(DWORD), NULL); - StreamCount += sizeof(FOURCC) + sizeof(DWORD) + chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %#04lx)", debugstr_fourcc(chunk.fccID), chunk.dwSize); - switch (chunk.fccID) { - case FOURCC_COLH: { - TRACE_(dmfile)(": collection header chunk\n"); - This->pHeader = calloc(1, chunk.dwSize); - IStream_Read(stream, This->pHeader, chunk.dwSize, NULL); - break; - } - case FOURCC_PTBL: { - struct chunk_entry ptbl_chunk = {.id = FOURCC_LIST, .size = chunk.dwSize, .type = chunk.fccID}; - TRACE_(dmfile)(": pool table chunk\n"); - liMove.QuadPart = 0; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, &ptbl_chunk.offset); - ptbl_chunk.offset.QuadPart -= 12; - parse_ptbl_chunk(This, stream, &ptbl_chunk); - stream_skip_chunk(stream, &ptbl_chunk); - break; - } - case FOURCC_LIST: { - IStream_Read(stream, &chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": LIST chunk of type %s", debugstr_fourcc(chunk.fccID)); - switch (chunk.fccID) { - case FOURCC_WVPL: { - TRACE_(dmfile)(": wave pool list (mark & skip)\n"); - liMove.QuadPart = chunk.dwSize - sizeof(FOURCC); - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - break; - } - case FOURCC_LINS: { - struct chunk_entry lins_chunk = {.id = FOURCC_LIST, .size = chunk.dwSize, .type = chunk.fccID}; - TRACE_(dmfile)(": instruments list\n"); - liMove.QuadPart = 0; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, &lins_chunk.offset); - lins_chunk.offset.QuadPart -= 12; - parse_lins_list(This, stream, &lins_chunk); - stream_skip_chunk(stream, &lins_chunk); - break; - } - default: { - TRACE_(dmfile)(": unknown (skipping)\n"); - liMove.QuadPart = chunk.dwSize - sizeof(FOURCC); - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = chunk.dwSize; - IStream_Seek(stream, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - TRACE_(dmfile)(": StreamCount = %ld < StreamSize = %ld\n", StreamCount, StreamSize); - } while (StreamCount < StreamSize); - - TRACE_(dmfile)(": reading finished\n"); + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_RIFF, FOURCC_DLS): + hr = parse_dls_chunk(This, stream, &chunk); + break; + default: + WARN("Invalid collection chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + hr = DMUS_E_UNSUPPORTED_STREAM; + break; + } + } - /* DEBUG: dumps whole collection object tree: */ - if (TRACE_ON(dmusic)) + if (SUCCEEDED(hr) && TRACE_ON(dmusic)) { struct instrument_entry *entry; int i = 0; @@ -396,13 +345,14 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, dump_DMUS_OBJECTDESC(&This->dmobj.desc); TRACE(" - Collection header:\n"); - TRACE(" - cInstruments: %ld\n", This->pHeader->cInstruments); + TRACE(" - cInstruments: %ld\n", This->header.cInstruments); TRACE(" - Instruments:\n"); LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry) TRACE(" - Instrument[%i]: %p\n", i++, entry->instrument); } + stream_skip_chunk(stream, &chunk); return S_OK; } diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 3e184fa86da..b160b0031b6 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -195,12 +195,6 @@ static inline struct instrument *impl_from_IDirectMusicInstrument(IDirectMusicIn */ void dmusic_remove_port(IDirectMusic8Impl *dmusic, IDirectMusicPort *port); -/* for simpler reading */ -typedef struct _DMUS_PRIVATE_CHUNK { - FOURCC fccID; /* FOURCC ID of the chunk */ - DWORD dwSize; /* size of the chunk */ -} DMUS_PRIVATE_CHUNK, *LPDMUS_PRIVATE_CHUNK; - /* used for generic dumping (copied from ddraw) */ typedef struct { DWORD val; From 809ba6256c72ad3017f045e04b412ab7166f7bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 12 Sep 2023 09:50:27 +0200 Subject: [PATCH 0889/1148] dmusic: Parse instrument name from INFO list. (cherry picked from commit 3fa399b145408b1aecf1d00e001f12a6191d32ac) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 11 +++-------- dlls/dmusic/dmusic_private.h | 5 +---- dlls/dmusic/instrument.c | 17 +++++++++++------ 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index f26806a1afb..cbcdabb37c1 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -25,6 +25,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmusic); struct instrument_entry { struct list entry; + DMUS_OBJECTDESC desc; IDirectMusicInstrument *instrument; }; @@ -156,13 +157,7 @@ static HRESULT WINAPI collection_EnumInstrument(IDirectMusicCollection *iface, { if (index--) continue; if (FAILED(hr = IDirectMusicInstrument_GetPatch(entry->instrument, patch))) return hr; - if (name) - { - struct instrument *instrument = impl_from_IDirectMusicInstrument(entry->instrument); - DWORD length = min(lstrlenW(instrument->wszName), name_length - 1); - memcpy(name, instrument->wszName, length * sizeof(WCHAR)); - name[length] = '\0'; - } + if (name) lstrcpynW(name, entry->desc.wszName, name_length); return S_OK; } @@ -190,7 +185,7 @@ static HRESULT parse_lins_list(struct collection *This, IStream *stream, struct { case MAKE_IDTYPE(FOURCC_LIST, FOURCC_INS): if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY; - hr = instrument_create_from_chunk(stream, &chunk, &entry->instrument); + hr = instrument_create_from_chunk(stream, &chunk, &entry->desc, &entry->instrument); if (SUCCEEDED(hr)) list_add_tail(&This->instruments, &entry->entry); else free(entry); break; diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index b160b0031b6..73b872c4f9f 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -101,7 +101,7 @@ extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface); extern HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, - IDirectMusicInstrument **ret_iface); + DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface); /***************************************************************************** * IDirectMusic8Impl implementation structure @@ -176,10 +176,7 @@ struct instrument IDirectMusicInstrument IDirectMusicInstrument_iface; LONG ref; - GUID id; INSTHEADER header; - WCHAR wszName[DMUS_MAX_NAME]; - /* instrument data */ struct list articulations; struct list regions; diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index aacbe6e8c88..dbbfd6f0c78 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -266,11 +266,16 @@ static HRESULT parse_lrgn_list(struct instrument *This, IStream *stream, struct return hr; } -static HRESULT parse_ins_chunk(struct instrument *This, IStream *stream, struct chunk_entry *parent) +static HRESULT parse_ins_chunk(struct instrument *This, IStream *stream, struct chunk_entry *parent, + DMUS_OBJECTDESC *desc) { struct chunk_entry chunk = {.parent = parent}; HRESULT hr; + if (FAILED(hr = dmobj_parsedescriptor(stream, parent, desc, DMUS_OBJ_NAME_INFO|DMUS_OBJ_GUID_DLID)) + || FAILED(hr = stream_reset_chunk_data(stream, parent))) + return hr; + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { switch (MAKE_IDTYPE(chunk.id, chunk.type)) @@ -280,7 +285,8 @@ static HRESULT parse_ins_chunk(struct instrument *This, IStream *stream, struct break; case FOURCC_DLID: - hr = stream_chunk_get_data(stream, &chunk, &This->id, sizeof(This->id)); + case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_INFO_LIST): + /* already parsed by dmobj_parsedescriptor */ break; case MAKE_IDTYPE(FOURCC_LIST, FOURCC_LRGN): @@ -303,7 +309,7 @@ static HRESULT parse_ins_chunk(struct instrument *This, IStream *stream, struct } HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, - IDirectMusicInstrument **ret_iface) + DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface) { IDirectMusicInstrument *iface; struct instrument *This; @@ -314,7 +320,7 @@ HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent if (FAILED(hr = instrument_create(&iface))) return hr; This = impl_from_IDirectMusicInstrument(iface); - if (FAILED(hr = parse_ins_chunk(This, stream, parent))) + if (FAILED(hr = parse_ins_chunk(This, stream, parent, desc))) { IDirectMusicInstrument_Release(iface); return DMUS_E_UNSUPPORTED_STREAM; @@ -323,14 +329,13 @@ HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent if (TRACE_ON(dmusic)) { TRACE("Created DirectMusicInstrument (%p) ***\n", This); - if (!IsEqualGUID(&This->id, &GUID_NULL)) - TRACE(" - GUID = %s\n", debugstr_dmguid(&This->id)); TRACE(" - Instrument header:\n"); TRACE(" - cRegions: %ld\n", This->header.cRegions); TRACE(" - Locale:\n"); TRACE(" - ulBank: %ld\n", This->header.Locale.ulBank); TRACE(" - ulInstrument: %ld\n", This->header.Locale.ulInstrument); TRACE(" => dwPatch: %ld\n", MIDILOCALE2Patch(&This->header.Locale)); + if (desc->dwValidData & DMUS_OBJ_OBJECT) TRACE(" - guid: %s\n", debugstr_dmguid(&desc->guidObject)); } *ret_iface = iface; From 0d77823997c1df9e3c77a4007c149bf75b245d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 12 Sep 2023 09:53:37 +0200 Subject: [PATCH 0890/1148] dmusic: Add more parsed instruments traces. (cherry picked from commit f71315c8494575698634062cfad4f5d84fd230a1) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/instrument.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index dbbfd6f0c78..025bba940e5 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -328,14 +328,38 @@ HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent if (TRACE_ON(dmusic)) { - TRACE("Created DirectMusicInstrument (%p) ***\n", This); - TRACE(" - Instrument header:\n"); - TRACE(" - cRegions: %ld\n", This->header.cRegions); - TRACE(" - Locale:\n"); - TRACE(" - ulBank: %ld\n", This->header.Locale.ulBank); - TRACE(" - ulInstrument: %ld\n", This->header.Locale.ulInstrument); - TRACE(" => dwPatch: %ld\n", MIDILOCALE2Patch(&This->header.Locale)); + struct region *region; + UINT i; + + TRACE("Created DirectMusicInstrument %p\n", This); + TRACE(" - header:\n"); + TRACE(" - regions: %ld\n", This->header.cRegions); + TRACE(" - locale: {bank: %#lx, instrument: %#lx} (patch %#lx)\n", + This->header.Locale.ulBank, This->header.Locale.ulInstrument, + MIDILOCALE2Patch(&This->header.Locale)); if (desc->dwValidData & DMUS_OBJ_OBJECT) TRACE(" - guid: %s\n", debugstr_dmguid(&desc->guidObject)); + if (desc->dwValidData & DMUS_OBJ_NAME) TRACE(" - name: %s\n", debugstr_w(desc->wszName)); + + TRACE(" - regions:\n"); + LIST_FOR_EACH_ENTRY(region, &This->regions, struct region, entry) + { + TRACE(" - region:\n"); + TRACE(" - header: {key: %u - %u, vel: %u - %u, options: %#x, group: %#x}\n", + region->header.RangeKey.usLow, region->header.RangeKey.usHigh, + region->header.RangeVelocity.usLow, region->header.RangeVelocity.usHigh, + region->header.fusOptions, region->header.usKeyGroup); + TRACE(" - wave_link: {options: %#x, group: %u, channel: %lu, index: %lu}\n", + region->wave_link.fusOptions, region->wave_link.usPhaseGroup, + region->wave_link.ulChannel, region->wave_link.ulTableIndex); + TRACE(" - wave_sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n", + region->wave_sample.cbSize, region->wave_sample.usUnityNote, + region->wave_sample.sFineTune, region->wave_sample.lAttenuation, + region->wave_sample.fulOptions, region->wave_sample.cSampleLoops); + for (i = 0; i < region->wave_sample.cSampleLoops; i++) + TRACE(" - wave_loop[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i, + region->wave_loop.cbSize, region->wave_loop.ulType, + region->wave_loop.ulStart, region->wave_loop.ulLength); + } } *ret_iface = iface; From 129a2b97d54aca5c25ee61bf91599d6f1432ceb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 15 Sep 2023 11:27:46 +0200 Subject: [PATCH 0891/1148] dmusic: Avoid swallowing collection Load failures. (cherry picked from commit 0cb208cec2f03cef0e42d98f238fe126abc941d0) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index cbcdabb37c1..5d6b533d649 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -331,7 +331,9 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *str } } - if (SUCCEEDED(hr) && TRACE_ON(dmusic)) + if (FAILED(hr)) return hr; + + if (TRACE_ON(dmusic)) { struct instrument_entry *entry; int i = 0; From f2a3616e4485064b8622a1320b7378c829a9812f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 14:06:32 +0200 Subject: [PATCH 0892/1148] dmusic: Rewrite downloading instrument to port. (cherry picked from commit 120a743c96c9632eb37ff6b8f5ccfb199ffb764b) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/dmusic_private.h | 43 +-------- dlls/dmusic/instrument.c | 174 +++++++++++++++++++++++++++++++++++ dlls/dmusic/port.c | 172 +--------------------------------- 3 files changed, 180 insertions(+), 209 deletions(-) diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 73b872c4f9f..9eac6feaa2c 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -51,7 +51,6 @@ */ typedef struct IDirectMusic8Impl IDirectMusic8Impl; typedef struct IDirectMusicBufferImpl IDirectMusicBufferImpl; -typedef struct IDirectMusicDownloadedInstrumentImpl IDirectMusicDownloadedInstrumentImpl; typedef struct IReferenceClockImpl IReferenceClockImpl; /***************************************************************************** @@ -76,16 +75,6 @@ typedef struct port_info { ULONG device; } port_info; -struct region -{ - struct list entry; - RGNHEADER header; - WAVELINK wave_link; - WSMPL wave_sample; - WLOOP wave_loop; - BOOL loop_present; -}; - /***************************************************************************** * ClassFactory */ @@ -102,6 +91,9 @@ extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface); extern HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface); +extern HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicPortDownload *port, + IDirectMusicDownloadedInstrument **downloaded); +extern HRESULT instrument_unload_from_port(IDirectMusicDownloadedInstrument *iface, IDirectMusicPortDownload *port); /***************************************************************************** * IDirectMusic8Impl implementation structure @@ -133,19 +125,6 @@ struct IDirectMusicBufferImpl { REFERENCE_TIME start_time; }; -/***************************************************************************** - * IDirectMusicDownloadedInstrumentImpl implementation structure - */ -struct IDirectMusicDownloadedInstrumentImpl { - /* IUnknown fields */ - IDirectMusicDownloadedInstrument IDirectMusicDownloadedInstrument_iface; - LONG ref; - - /* IDirectMusicDownloadedInstrumentImpl fields */ - BOOL downloaded; - void *data; -}; - /** Internal factory */ extern HRESULT synth_port_create(IDirectMusic8Impl *parent, DMUS_PORTPARAMS *port_params, DMUS_PORTCAPS *port_caps, IDirectMusicPort **port); @@ -171,22 +150,6 @@ typedef struct _DMUS_PRIVATE_POOLCUE { struct list entry; /* for listing elements */ } DMUS_PRIVATE_POOLCUE, *LPDMUS_PRIVATE_POOLCUE; -struct instrument -{ - IDirectMusicInstrument IDirectMusicInstrument_iface; - LONG ref; - - INSTHEADER header; - - struct list articulations; - struct list regions; -}; - -static inline struct instrument *impl_from_IDirectMusicInstrument(IDirectMusicInstrument *iface) -{ - return CONTAINING_RECORD(iface, struct instrument, IDirectMusicInstrument_iface); -} - /***************************************************************************** * Misc. */ diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 025bba940e5..24b3c0305f8 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -33,6 +33,33 @@ struct articulation C_ASSERT(sizeof(struct articulation) == offsetof(struct articulation, connections[0])); +struct region +{ + struct list entry; + RGNHEADER header; + WAVELINK wave_link; + WSMPL wave_sample; + WLOOP wave_loop; + BOOL loop_present; +}; + +struct instrument +{ + IDirectMusicInstrument IDirectMusicInstrument_iface; + IDirectMusicDownloadedInstrument IDirectMusicDownloadedInstrument_iface; + LONG ref; + + INSTHEADER header; + IDirectMusicDownload *download; + struct list articulations; + struct list regions; +}; + +static inline struct instrument *impl_from_IDirectMusicInstrument(IDirectMusicInstrument *iface) +{ + return CONTAINING_RECORD(iface, struct instrument, IDirectMusicInstrument_iface); +} + static HRESULT WINAPI instrument_QueryInterface(LPDIRECTMUSICINSTRUMENT iface, REFIID riid, LPVOID *ret_iface) { TRACE("(%p)->(%s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); @@ -134,6 +161,46 @@ static const IDirectMusicInstrumentVtbl instrument_vtbl = instrument_SetPatch, }; +static inline struct instrument* impl_from_IDirectMusicDownloadedInstrument(IDirectMusicDownloadedInstrument *iface) +{ + return CONTAINING_RECORD(iface, struct instrument, IDirectMusicDownloadedInstrument_iface); +} + +static HRESULT WINAPI downloaded_instrument_QueryInterface(IDirectMusicDownloadedInstrument *iface, REFIID riid, VOID **ret_iface) +{ + TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); + + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDirectMusicDownloadedInstrument)) + { + IDirectMusicDownloadedInstrument_AddRef(iface); + *ret_iface = iface; + return S_OK; + } + + WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface); + + return E_NOINTERFACE; +} + +static ULONG WINAPI downloaded_instrument_AddRef(IDirectMusicDownloadedInstrument *iface) +{ + struct instrument *This = impl_from_IDirectMusicDownloadedInstrument(iface); + return IDirectMusicInstrument_AddRef(&This->IDirectMusicInstrument_iface); +} + +static ULONG WINAPI downloaded_instrument_Release(IDirectMusicDownloadedInstrument *iface) +{ + struct instrument *This = impl_from_IDirectMusicDownloadedInstrument(iface); + return IDirectMusicInstrument_Release(&This->IDirectMusicInstrument_iface); +} + +static const IDirectMusicDownloadedInstrumentVtbl downloaded_instrument_vtbl = +{ + downloaded_instrument_QueryInterface, + downloaded_instrument_AddRef, + downloaded_instrument_Release, +}; + static HRESULT instrument_create(IDirectMusicInstrument **ret_iface) { struct instrument *instrument; @@ -141,6 +208,7 @@ static HRESULT instrument_create(IDirectMusicInstrument **ret_iface) *ret_iface = NULL; if (!(instrument = calloc(1, sizeof(*instrument)))) return E_OUTOFMEMORY; instrument->IDirectMusicInstrument_iface.lpVtbl = &instrument_vtbl; + instrument->IDirectMusicDownloadedInstrument_iface.lpVtbl = &downloaded_instrument_vtbl; instrument->ref = 1; list_init(&instrument->articulations); list_init(&instrument->regions); @@ -365,3 +433,109 @@ HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent *ret_iface = iface; return S_OK; } + +struct download_buffer +{ + DMUS_DOWNLOADINFO info; + ULONG offsets[]; +}; + +C_ASSERT(sizeof(struct download_buffer) == offsetof(struct download_buffer, offsets[0])); + +HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicPortDownload *port, + IDirectMusicDownloadedInstrument **downloaded) +{ + struct instrument *This = impl_from_IDirectMusicInstrument(iface); + struct download_buffer *buffer; + IDirectMusicDownload *download; + DWORD size, offset_count; + struct region *region; + HRESULT hr; + + if (This->download) goto done; + + size = sizeof(DMUS_DOWNLOADINFO); + size += sizeof(ULONG) + sizeof(DMUS_INSTRUMENT); + offset_count = 1; + + LIST_FOR_EACH_ENTRY(region, &This->regions, struct region, entry) + { + size += sizeof(ULONG) + sizeof(DMUS_REGION); + offset_count++; + } + + if (FAILED(hr = IDirectMusicPortDownload_AllocateBuffer(port, size, &download))) return hr; + + if (SUCCEEDED(hr = IDirectMusicDownload_GetBuffer(download, (void **)&buffer, &size)) + && SUCCEEDED(hr = IDirectMusicPortDownload_GetDLId(port, &buffer->info.dwDLId, 1))) + { + BYTE *ptr = (BYTE *)&buffer->offsets[offset_count]; + DMUS_INSTRUMENT *dmus_instrument; + DMUS_REGION *dmus_region = NULL; + UINT index = 0; + + buffer->info.dwDLType = DMUS_DOWNLOADINFO_INSTRUMENT2; + buffer->info.dwNumOffsetTableEntries = offset_count; + buffer->info.cbSize = size; + + buffer->offsets[index++] = ptr - (BYTE *)buffer; + dmus_instrument = (DMUS_INSTRUMENT *)ptr; + ptr += sizeof(DMUS_INSTRUMENT); + + dmus_instrument->ulPatch = MIDILOCALE2Patch(&This->header.Locale); + dmus_instrument->ulFirstRegionIdx = 0; + dmus_instrument->ulCopyrightIdx = 0; + dmus_instrument->ulGlobalArtIdx = 0; + + LIST_FOR_EACH_ENTRY(region, &This->regions, struct region, entry) + { + if (dmus_region) dmus_region->ulNextRegionIdx = index; + else dmus_instrument->ulFirstRegionIdx = index; + + buffer->offsets[index++] = ptr - (BYTE *)buffer; + dmus_region = (DMUS_REGION *)ptr; + ptr += sizeof(DMUS_REGION); + + dmus_region->RangeKey = region->header.RangeKey; + dmus_region->RangeVelocity = region->header.RangeVelocity; + dmus_region->fusOptions = region->header.fusOptions; + dmus_region->usKeyGroup = region->header.usKeyGroup; + dmus_region->ulRegionArtIdx = 0; + dmus_region->ulNextRegionIdx = 0; + dmus_region->ulFirstExtCkIdx = 0; + dmus_region->WaveLink = region->wave_link; + dmus_region->WSMP = region->wave_sample; + dmus_region->WLOOP[0] = region->wave_loop; + } + + if (FAILED(hr = IDirectMusicPortDownload_Download(port, download))) goto failed; + } + + This->download = download; + +done: + *downloaded = &This->IDirectMusicDownloadedInstrument_iface; + IDirectMusicDownloadedInstrument_AddRef(*downloaded); + return S_OK; + +failed: + WARN("Failed to download instrument to port, hr %#lx\n", hr); + IDirectMusicDownload_Release(download); + return hr; +} + +HRESULT instrument_unload_from_port(IDirectMusicDownloadedInstrument *iface, IDirectMusicPortDownload *port) +{ + struct instrument *This = impl_from_IDirectMusicDownloadedInstrument(iface); + HRESULT hr; + + if (!This->download) return DMUS_E_NOT_DOWNLOADED_TO_PORT; + + if (FAILED(hr = IDirectMusicPortDownload_Unload(port, This->download))) + WARN("Failed to unload instrument download buffer, hr %#lx\n", hr); + + IDirectMusicDownload_Release(This->download); + This->download = NULL; + + return hr; +} diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index 7e3fa5a67eb..0eff1c5cb02 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -52,11 +52,6 @@ struct synth_port { DWORD next_dlid; }; -static inline IDirectMusicDownloadedInstrumentImpl* impl_from_IDirectMusicDownloadedInstrument(IDirectMusicDownloadedInstrument *iface) -{ - return CONTAINING_RECORD(iface, IDirectMusicDownloadedInstrumentImpl, IDirectMusicDownloadedInstrument_iface); -} - static inline struct synth_port *synth_from_IDirectMusicPort(IDirectMusicPort *iface) { return CONTAINING_RECORD(iface, struct synth_port, IDirectMusicPort_iface); @@ -77,83 +72,6 @@ static inline struct synth_port *synth_from_IKsControl(IKsControl *iface) return CONTAINING_RECORD(iface, struct synth_port, IKsControl_iface); } -/* IDirectMusicDownloadedInstrument IUnknown part follows: */ -static HRESULT WINAPI IDirectMusicDownloadedInstrumentImpl_QueryInterface(IDirectMusicDownloadedInstrument *iface, REFIID riid, VOID **ret_iface) -{ - TRACE("(%p, %s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); - - if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDirectMusicDownloadedInstrument)) - { - IDirectMusicDownloadedInstrument_AddRef(iface); - *ret_iface = iface; - return S_OK; - } - - WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface); - - return E_NOINTERFACE; -} - -static ULONG WINAPI IDirectMusicDownloadedInstrumentImpl_AddRef(LPDIRECTMUSICDOWNLOADEDINSTRUMENT iface) -{ - IDirectMusicDownloadedInstrumentImpl *This = impl_from_IDirectMusicDownloadedInstrument(iface); - ULONG ref = InterlockedIncrement(&This->ref); - - TRACE("(%p): new ref = %lu\n", iface, ref); - - return ref; -} - -static ULONG WINAPI IDirectMusicDownloadedInstrumentImpl_Release(LPDIRECTMUSICDOWNLOADEDINSTRUMENT iface) -{ - IDirectMusicDownloadedInstrumentImpl *This = impl_from_IDirectMusicDownloadedInstrument(iface); - ULONG ref = InterlockedDecrement(&This->ref); - - TRACE("(%p): new ref = %lu\n", iface, ref); - - if (!ref) - { - free(This->data); - free(This); - } - - return ref; -} - -static const IDirectMusicDownloadedInstrumentVtbl DirectMusicDownloadedInstrument_Vtbl = { - IDirectMusicDownloadedInstrumentImpl_QueryInterface, - IDirectMusicDownloadedInstrumentImpl_AddRef, - IDirectMusicDownloadedInstrumentImpl_Release -}; - -static inline IDirectMusicDownloadedInstrumentImpl* unsafe_impl_from_IDirectMusicDownloadedInstrument(IDirectMusicDownloadedInstrument *iface) -{ - if (!iface) - return NULL; - assert(iface->lpVtbl == &DirectMusicDownloadedInstrument_Vtbl); - - return impl_from_IDirectMusicDownloadedInstrument(iface); -} - -static HRESULT DMUSIC_CreateDirectMusicDownloadedInstrumentImpl(IDirectMusicDownloadedInstrument **instrument) -{ - IDirectMusicDownloadedInstrumentImpl *object; - - object = calloc(1, sizeof(*object)); - if (!object) - { - *instrument = NULL; - return E_OUTOFMEMORY; - } - - object->IDirectMusicDownloadedInstrument_iface.lpVtbl = &DirectMusicDownloadedInstrument_Vtbl; - object->ref = 1; - - *instrument = &object->IDirectMusicDownloadedInstrument_iface; - - return S_OK; -} - static HRESULT WINAPI synth_port_QueryInterface(IDirectMusicPort *iface, REFIID riid, void **ret_iface) { struct synth_port *This = synth_from_IDirectMusicPort(iface); @@ -265,110 +183,26 @@ static HRESULT WINAPI synth_port_DownloadInstrument(IDirectMusicPort *iface, IDi IDirectMusicDownloadedInstrument **downloaded_instrument, DMUS_NOTERANGE *note_ranges, DWORD num_note_ranges) { struct synth_port *This = synth_from_IDirectMusicPort(iface); - struct instrument *instrument_object; - struct region *instrument_region; - HRESULT ret; - BOOL on_heap; - HANDLE download; - DMUS_DOWNLOADINFO *info; - DMUS_OFFSETTABLE *offset_table; - DMUS_INSTRUMENT *instrument_info; - BYTE *data; - ULONG offset; - ULONG nb_regions; - ULONG size; - ULONG i; TRACE("(%p, %p, %p, %p, %ld)\n", iface, instrument, downloaded_instrument, note_ranges, num_note_ranges); if (!instrument || !downloaded_instrument || (num_note_ranges && !note_ranges)) return E_POINTER; - instrument_object = impl_from_IDirectMusicInstrument(instrument); - - nb_regions = instrument_object->header.cRegions; - size = sizeof(DMUS_DOWNLOADINFO) + sizeof(ULONG) * (1 + nb_regions) + sizeof(DMUS_INSTRUMENT) + sizeof(DMUS_REGION) * nb_regions; - - data = malloc(size); - if (!data) - return E_OUTOFMEMORY; - - info = (DMUS_DOWNLOADINFO*)data; - offset_table = (DMUS_OFFSETTABLE*)(data + sizeof(DMUS_DOWNLOADINFO)); - offset = sizeof(DMUS_DOWNLOADINFO) + sizeof(ULONG) * (1 + nb_regions); - - info->dwDLType = DMUS_DOWNLOADINFO_INSTRUMENT2; - info->dwDLId = 0; - info->dwNumOffsetTableEntries = 1 + instrument_object->header.cRegions; - info->cbSize = size; - - offset_table->ulOffsetTable[0] = offset; - instrument_info = (DMUS_INSTRUMENT*)(data + offset); - offset += sizeof(DMUS_INSTRUMENT); - instrument_info->ulPatch = MIDILOCALE2Patch(&instrument_object->header.Locale); - instrument_info->ulFirstRegionIdx = 1; - instrument_info->ulGlobalArtIdx = 0; /* FIXME */ - instrument_info->ulFirstExtCkIdx = 0; /* FIXME */ - instrument_info->ulCopyrightIdx = 0; /* FIXME */ - instrument_info->ulFlags = 0; /* FIXME */ - - i = 0; - LIST_FOR_EACH_ENTRY(instrument_region, &instrument_object->regions, struct region, entry) - { - DMUS_REGION *region = (DMUS_REGION*)(data + offset); - - offset_table->ulOffsetTable[1 + i] = offset; - offset += sizeof(DMUS_REGION); - region->RangeKey = instrument_region->header.RangeKey; - region->RangeVelocity = instrument_region->header.RangeVelocity; - region->fusOptions = instrument_region->header.fusOptions; - region->usKeyGroup = instrument_region->header.usKeyGroup; - region->ulRegionArtIdx = 0; /* FIXME */ - region->ulNextRegionIdx = i != (nb_regions - 1) ? (i + 2) : 0; - region->ulFirstExtCkIdx = 0; /* FIXME */ - region->WaveLink = instrument_region->wave_link; - region->WSMP = instrument_region->wave_sample; - region->WLOOP[0] = instrument_region->wave_loop; - i++; - } - - ret = IDirectMusicSynth8_Download(This->synth, &download, (void*)data, &on_heap); - - if (SUCCEEDED(ret)) - ret = DMUSIC_CreateDirectMusicDownloadedInstrumentImpl(downloaded_instrument); - - if (SUCCEEDED(ret)) - { - IDirectMusicDownloadedInstrumentImpl *downloaded_object = impl_from_IDirectMusicDownloadedInstrument(*downloaded_instrument); - - downloaded_object->data = data; - downloaded_object->downloaded = TRUE; - } - - *downloaded_instrument = NULL; - free(data); - - return E_FAIL; + return instrument_download_to_port(instrument, &This->IDirectMusicPortDownload_iface, downloaded_instrument); } static HRESULT WINAPI synth_port_UnloadInstrument(IDirectMusicPort *iface, IDirectMusicDownloadedInstrument *downloaded_instrument) { - IDirectMusicDownloadedInstrumentImpl *downloaded_object = unsafe_impl_from_IDirectMusicDownloadedInstrument(downloaded_instrument); + struct synth_port *This = synth_from_IDirectMusicPort(iface); TRACE("(%p, %p)\n", iface, downloaded_instrument); if (!downloaded_instrument) return E_POINTER; - if (!downloaded_object->downloaded) - return DMUS_E_NOT_DOWNLOADED_TO_PORT; - - free(downloaded_object->data); - downloaded_object->data = NULL; - downloaded_object->downloaded = FALSE; - - return S_OK; + return instrument_unload_from_port(downloaded_instrument, &This->IDirectMusicPortDownload_iface); } static HRESULT WINAPI synth_port_GetLatencyClock(IDirectMusicPort *iface, IReferenceClock **clock) From 1f52551b014c5c8539c3866346c018edf4a3f431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Aug 2023 13:20:17 +0200 Subject: [PATCH 0893/1148] dmusic: Parse collection wave table. (cherry picked from commit 2f5608efd1e4e8be0a67f69d5d7885ea4b2603b3) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 60 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 5d6b533d649..3bcca3c4b8a 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -37,6 +37,12 @@ struct pool C_ASSERT(sizeof(struct pool) == offsetof(struct pool, cues[0])); +struct wave_entry +{ + struct list entry; + DWORD offset; +}; + struct collection { IDirectMusicCollection IDirectMusicCollection_iface; @@ -46,6 +52,7 @@ struct collection DLSHEADER header; struct pool *pool; struct list instruments; + struct list waves; }; static inline struct collection *impl_from_IDirectMusicCollection(IDirectMusicCollection *iface) @@ -103,6 +110,7 @@ static ULONG WINAPI collection_Release(IDirectMusicCollection *iface) if (!ref) { struct instrument_entry *instrument_entry; + struct wave_entry *wave_entry; void *next; LIST_FOR_EACH_ENTRY_SAFE(instrument_entry, next, &This->instruments, struct instrument_entry, entry) @@ -112,6 +120,12 @@ static ULONG WINAPI collection_Release(IDirectMusicCollection *iface) free(instrument_entry); } + LIST_FOR_EACH_ENTRY_SAFE(wave_entry, next, &This->waves, struct wave_entry, entry) + { + list_remove(&wave_entry->entry); + free(wave_entry); + } + free(This); } @@ -201,6 +215,33 @@ static HRESULT parse_lins_list(struct collection *This, IStream *stream, struct return hr; } +static HRESULT parse_wvpl_list(struct collection *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + struct wave_entry *entry; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_LIST, FOURCC_wave): + if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY; + entry->offset = chunk.offset.QuadPart - parent->offset.QuadPart - 12; + list_add_tail(&This->waves, &entry->entry); + break; + + default: + FIXME("Skipping unknown chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + return hr; +} + static HRESULT parse_ptbl_chunk(struct collection *This, IStream *stream, struct chunk_entry *chunk) { struct pool *pool; @@ -256,6 +297,10 @@ static HRESULT parse_dls_chunk(struct collection *This, IStream *stream, struct hr = parse_lins_list(This, stream, &chunk); break; + case MAKE_IDTYPE(FOURCC_LIST, FOURCC_WVPL): + hr = parse_wvpl_list(This, stream, &chunk); + break; + default: FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); break; @@ -336,6 +381,7 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *str if (TRACE_ON(dmusic)) { struct instrument_entry *entry; + struct wave_entry *wave_entry; int i = 0; TRACE("*** IDirectMusicCollection (%p) ***\n", &This->IDirectMusicCollection_iface); @@ -346,7 +392,18 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *str TRACE(" - Instruments:\n"); LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry) - TRACE(" - Instrument[%i]: %p\n", i++, entry->instrument); + { + TRACE(" - Instrument[%i]: %p\n", i, entry->instrument); + i++; + } + + TRACE(" - cues: size %lu\n", This->pool->table.cbSize); + for (i = 0; i < This->pool->table.cCues; i++) + TRACE(" - index: %u, offset: %lu\n", i, This->pool->cues[i].ulOffset); + + TRACE(" - waves:\n"); + LIST_FOR_EACH_ENTRY(wave_entry, &This->waves, struct wave_entry, entry) + TRACE(" - offset: %lu\n", wave_entry->offset); } stream_skip_chunk(stream, &chunk); @@ -378,6 +435,7 @@ HRESULT collection_create(IUnknown **ret_iface) collection->dmobj.IDirectMusicObject_iface.lpVtbl = &collection_object_vtbl; collection->dmobj.IPersistStream_iface.lpVtbl = &collection_stream_vtbl; list_init(&collection->instruments); + list_init(&collection->waves); TRACE("Created DirectMusicCollection %p\n", collection); *ret_iface = (IUnknown *)&collection->IDirectMusicCollection_iface; From a7be1447c32c6e480c6130ed1f2928cc801012a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 15 Sep 2023 09:00:54 +0200 Subject: [PATCH 0894/1148] dmusic: Parse collection wave lists. (cherry picked from commit 82d1794cd49d1aedb652f7c6a07886d5178b5156) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/Makefile.in | 3 +- dlls/dmusic/collection.c | 14 ++- dlls/dmusic/dmusic_private.h | 2 + dlls/dmusic/wave.c | 214 +++++++++++++++++++++++++++++++++++ 4 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 dlls/dmusic/wave.c diff --git a/dlls/dmusic/Makefile.in b/dlls/dmusic/Makefile.in index a8955a2ab42..d9438af0d18 100644 --- a/dlls/dmusic/Makefile.in +++ b/dlls/dmusic/Makefile.in @@ -10,7 +10,8 @@ C_SRCS = \ dmusic_main.c \ download.c \ instrument.c \ - port.c + port.c \ + wave.c IDL_SRCS = dmusic.idl diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 3bcca3c4b8a..1c36f0b202d 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -40,6 +40,7 @@ C_ASSERT(sizeof(struct pool) == offsetof(struct pool, cues[0])); struct wave_entry { struct list entry; + IUnknown *wave; DWORD offset; }; @@ -123,6 +124,7 @@ static ULONG WINAPI collection_Release(IDirectMusicCollection *iface) LIST_FOR_EACH_ENTRY_SAFE(wave_entry, next, &This->waves, struct wave_entry, entry) { list_remove(&wave_entry->entry); + IDirectMusicInstrument_Release(wave_entry->wave); free(wave_entry); } @@ -227,8 +229,12 @@ static HRESULT parse_wvpl_list(struct collection *This, IStream *stream, struct { case MAKE_IDTYPE(FOURCC_LIST, FOURCC_wave): if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY; - entry->offset = chunk.offset.QuadPart - parent->offset.QuadPart - 12; - list_add_tail(&This->waves, &entry->entry); + if (FAILED(hr = wave_create_from_chunk(stream, &chunk, &entry->wave))) free(entry); + else + { + entry->offset = chunk.offset.QuadPart - parent->offset.QuadPart - 12; + list_add_tail(&This->waves, &entry->entry); + } break; default: @@ -397,13 +403,13 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *str i++; } - TRACE(" - cues: size %lu\n", This->pool->table.cbSize); + TRACE(" - cues:\n"); for (i = 0; i < This->pool->table.cCues; i++) TRACE(" - index: %u, offset: %lu\n", i, This->pool->cues[i].ulOffset); TRACE(" - waves:\n"); LIST_FOR_EACH_ENTRY(wave_entry, &This->waves, struct wave_entry, entry) - TRACE(" - offset: %lu\n", wave_entry->offset); + TRACE(" - offset: %lu, wave %p\n", wave_entry->offset, wave_entry->wave); } stream_skip_chunk(stream, &chunk); diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 9eac6feaa2c..84fdc5167a0 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -95,6 +95,8 @@ extern HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirec IDirectMusicDownloadedInstrument **downloaded); extern HRESULT instrument_unload_from_port(IDirectMusicDownloadedInstrument *iface, IDirectMusicPortDownload *port); +extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **out); + /***************************************************************************** * IDirectMusic8Impl implementation structure */ diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c new file mode 100644 index 00000000000..cda895484cb --- /dev/null +++ b/dlls/dmusic/wave.c @@ -0,0 +1,214 @@ +/* + * Copyright 2023 Rémi Bernon for CodeWeavers + * + * This program 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 program 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 program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "dmusic_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(dmusic); + +struct sample +{ + WSMPL head; + WLOOP loops[]; +}; + +C_ASSERT(sizeof(struct sample) == offsetof(struct sample, loops[0])); + +struct wave +{ + IUnknown IUnknown_iface; + LONG ref; + + struct sample *sample; + WAVEFORMATEX *format; + UINT data_size; + void *data; +}; + +static inline struct wave *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct wave, IUnknown_iface); +} + +static HRESULT WINAPI wave_QueryInterface(IUnknown *iface, REFIID riid, void **ret_iface) +{ + struct wave *This = impl_from_IUnknown(iface); + + TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ret_iface); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + *ret_iface = &This->IUnknown_iface; + IUnknown_AddRef(&This->IUnknown_iface); + return S_OK; + } + + WARN("(%p, %s, %p): not found\n", This, debugstr_dmguid(riid), ret_iface); + *ret_iface = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI wave_AddRef(IUnknown *iface) +{ + struct wave *This = impl_from_IUnknown(iface); + LONG ref = InterlockedIncrement(&This->ref); + TRACE("(%p) ref=%ld\n", This, ref); + return ref; +} + +static ULONG WINAPI wave_Release(IUnknown *iface) +{ + struct wave *This = impl_from_IUnknown(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + if (!ref) + { + free(This->format); + free(This->data); + free(This->sample); + free(This); + } + + return ref; +} + +static const IUnknownVtbl unknown_vtbl = +{ + wave_QueryInterface, + wave_AddRef, + wave_Release, +}; + +static HRESULT wave_create(IUnknown **ret_iface) +{ + struct wave *obj; + + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; + obj->IUnknown_iface.lpVtbl = &unknown_vtbl; + obj->ref = 1; + + *ret_iface = &obj->IUnknown_iface; + return S_OK; +} + +static HRESULT parse_wsmp_chunk(struct wave *This, IStream *stream, struct chunk_entry *chunk) +{ + struct sample *sample; + WSMPL wsmpl; + HRESULT hr; + UINT size; + + if (chunk->size < sizeof(wsmpl)) return E_INVALIDARG; + if (FAILED(hr = stream_read(stream, &wsmpl, sizeof(wsmpl)))) return hr; + if (chunk->size != wsmpl.cbSize + sizeof(WLOOP) * wsmpl.cSampleLoops) return E_INVALIDARG; + if (wsmpl.cbSize != sizeof(wsmpl)) return E_INVALIDARG; + if (wsmpl.cSampleLoops > 1) FIXME("Not implemented: found more than one wave loop\n"); + + size = offsetof(struct sample, loops[wsmpl.cSampleLoops]); + if (!(sample = malloc(size))) return E_OUTOFMEMORY; + sample->head = wsmpl; + + size = sizeof(WLOOP) * wsmpl.cSampleLoops; + if (FAILED(hr = stream_read(stream, sample->loops, size))) free(sample); + else This->sample = sample; + + return hr; +} + +static HRESULT parse_wave_chunk(struct wave *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case mmioFOURCC('f','m','t',' '): + if (!(This->format = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, This->format, chunk.size); + break; + + case mmioFOURCC('d','a','t','a'): + if (!(This->data = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, This->data, chunk.size); + if (SUCCEEDED(hr)) This->data_size = chunk.size; + break; + + case FOURCC_WSMP: + hr = parse_wsmp_chunk(This, stream, &chunk); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + return hr; +} + +HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **ret_iface) +{ + struct wave *This; + IUnknown *iface; + HRESULT hr; + + TRACE("(%p, %p, %p)\n", stream, parent, ret_iface); + + if (FAILED(hr = wave_create(&iface))) return hr; + This = impl_from_IUnknown(iface); + + if (FAILED(hr = parse_wave_chunk(This, stream, parent))) + { + IUnknown_Release(iface); + return DMUS_E_UNSUPPORTED_STREAM; + } + + if (TRACE_ON(dmusic)) + { + UINT i; + + TRACE("*** Created DirectMusicWave %p\n", This); + TRACE(" - format: %p\n", This->format); + if (This->format) + { + TRACE(" - wFormatTag: %u\n", This->format->wFormatTag); + TRACE(" - nChannels: %u\n", This->format->nChannels); + TRACE(" - nSamplesPerSec: %lu\n", This->format->nSamplesPerSec); + TRACE(" - nAvgBytesPerSec: %lu\n", This->format->nAvgBytesPerSec); + TRACE(" - nBlockAlign: %u\n", This->format->nBlockAlign); + TRACE(" - wBitsPerSample: %u\n", This->format->wBitsPerSample); + TRACE(" - cbSize: %u\n", This->format->cbSize); + } + TRACE(" - sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n", + This->sample->head.cbSize, This->sample->head.usUnityNote, + This->sample->head.sFineTune, This->sample->head.lAttenuation, + This->sample->head.fulOptions, This->sample->head.cSampleLoops); + for (i = 0; i < This->sample->head.cSampleLoops; i++) + TRACE(" - loops[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i, + This->sample->loops[i].cbSize, This->sample->loops[i].ulType, + This->sample->loops[i].ulStart, This->sample->loops[i].ulLength); + } + + *ret_iface = iface; + return S_OK; +} From 0091c0c03470ff707206d13b0c79bf1d4ce91ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 15 Sep 2023 09:21:05 +0200 Subject: [PATCH 0895/1148] dmusic: Keep an internal ref on the instrument's collection. (cherry picked from commit f54b0a5a9bf00de6643b9ea038532c1b2ff4952e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 21 +++++++++++++++++++-- dlls/dmusic/dmusic_private.h | 6 +++++- dlls/dmusic/instrument.c | 11 +++++++---- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 1c36f0b202d..cfe7589485d 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -48,6 +48,7 @@ struct collection { IDirectMusicCollection IDirectMusicCollection_iface; struct dmobject dmobj; + LONG internal_ref; LONG ref; DLSHEADER header; @@ -56,6 +57,21 @@ struct collection struct list waves; }; +extern void collection_internal_addref(struct collection *collection) +{ + ULONG ref = InterlockedIncrement( &collection->internal_ref ); + TRACE( "collection %p, internal ref %lu.\n", collection, ref ); +} + +extern void collection_internal_release(struct collection *collection) +{ + ULONG ref = InterlockedDecrement( &collection->internal_ref ); + TRACE( "collection %p, internal ref %lu.\n", collection, ref ); + + if (!ref) + free(collection); +} + static inline struct collection *impl_from_IDirectMusicCollection(IDirectMusicCollection *iface) { return CONTAINING_RECORD(iface, struct collection, IDirectMusicCollection_iface); @@ -128,7 +144,7 @@ static ULONG WINAPI collection_Release(IDirectMusicCollection *iface) free(wave_entry); } - free(This); + collection_internal_release(This); } return ref; @@ -201,7 +217,7 @@ static HRESULT parse_lins_list(struct collection *This, IStream *stream, struct { case MAKE_IDTYPE(FOURCC_LIST, FOURCC_INS): if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY; - hr = instrument_create_from_chunk(stream, &chunk, &entry->desc, &entry->instrument); + hr = instrument_create_from_chunk(stream, &chunk, This, &entry->desc, &entry->instrument); if (SUCCEEDED(hr)) list_add_tail(&This->instruments, &entry->entry); else free(entry); break; @@ -435,6 +451,7 @@ HRESULT collection_create(IUnknown **ret_iface) *ret_iface = NULL; if (!(collection = calloc(1, sizeof(*collection)))) return E_OUTOFMEMORY; collection->IDirectMusicCollection_iface.lpVtbl = &collection_vtbl; + collection->internal_ref = 1; collection->ref = 1; dmobject_init(&collection->dmobj, &CLSID_DirectMusicCollection, (IUnknown *)&collection->IDirectMusicCollection_iface); diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 84fdc5167a0..316e7743ecb 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -79,6 +79,10 @@ typedef struct port_info { * ClassFactory */ +struct collection; +extern void collection_internal_addref(struct collection *collection); +extern void collection_internal_release(struct collection *collection); + /* CLSID */ extern HRESULT music_create(IUnknown **ret_iface); extern HRESULT collection_create(IUnknown **ret_iface); @@ -90,7 +94,7 @@ extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface); extern HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, - DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface); + struct collection *collection, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface); extern HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicPortDownload *port, IDirectMusicDownloadedInstrument **downloaded); extern HRESULT instrument_unload_from_port(IDirectMusicDownloadedInstrument *iface, IDirectMusicPortDownload *port); diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 24b3c0305f8..b79e7671c5b 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -51,6 +51,7 @@ struct instrument INSTHEADER header; IDirectMusicDownload *download; + struct collection *collection; struct list articulations; struct list regions; }; @@ -124,6 +125,7 @@ static ULONG WINAPI instrument_Release(LPDIRECTMUSICINSTRUMENT iface) free(region); } + collection_internal_release(This->collection); free(This); } @@ -201,7 +203,7 @@ static const IDirectMusicDownloadedInstrumentVtbl downloaded_instrument_vtbl = downloaded_instrument_Release, }; -static HRESULT instrument_create(IDirectMusicInstrument **ret_iface) +static HRESULT instrument_create(struct collection *collection, IDirectMusicInstrument **ret_iface) { struct instrument *instrument; @@ -210,6 +212,7 @@ static HRESULT instrument_create(IDirectMusicInstrument **ret_iface) instrument->IDirectMusicInstrument_iface.lpVtbl = &instrument_vtbl; instrument->IDirectMusicDownloadedInstrument_iface.lpVtbl = &downloaded_instrument_vtbl; instrument->ref = 1; + collection_internal_addref((instrument->collection = collection)); list_init(&instrument->articulations); list_init(&instrument->regions); @@ -377,15 +380,15 @@ static HRESULT parse_ins_chunk(struct instrument *This, IStream *stream, struct } HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, - DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface) + struct collection *collection, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface) { IDirectMusicInstrument *iface; struct instrument *This; HRESULT hr; - TRACE("(%p, %p)\n", stream, ret_iface); + TRACE("(%p, %p, %p, %p, %p)\n", stream, parent, collection, desc, ret_iface); - if (FAILED(hr = instrument_create(&iface))) return hr; + if (FAILED(hr = instrument_create(collection, &iface))) return hr; This = impl_from_IDirectMusicInstrument(iface); if (FAILED(hr = parse_ins_chunk(This, stream, parent, desc))) From af66137343eed8fe7571a6720d4d3cf0c30929f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 14:06:32 +0200 Subject: [PATCH 0896/1148] dmusic: Implement downloading wave to port. (cherry picked from commit d32fb707ad30fad9e13d4609e4347dab55ec8a8b) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 21 ++++++++++++++++++ dlls/dmusic/dmusic_private.h | 2 ++ dlls/dmusic/instrument.c | 33 +++++++++++++++++++++++++++ dlls/dmusic/tests/dmusic.c | 4 +--- dlls/dmusic/wave.c | 43 ++++++++++++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 3 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index cfe7589485d..b7c11dacb77 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -72,6 +72,27 @@ extern void collection_internal_release(struct collection *collection) free(collection); } +extern HRESULT collection_get_wave(struct collection *collection, DWORD index, IUnknown **out) +{ + struct wave_entry *wave_entry; + DWORD offset; + + if (index >= collection->pool->table.cCues) return E_INVALIDARG; + offset = collection->pool->cues[index].ulOffset; + + LIST_FOR_EACH_ENTRY(wave_entry, &collection->waves, struct wave_entry, entry) + { + if (offset == wave_entry->offset) + { + *out = wave_entry->wave; + IUnknown_AddRef(wave_entry->wave); + return S_OK; + } + } + + return E_FAIL; +} + static inline struct collection *impl_from_IDirectMusicCollection(IDirectMusicCollection *iface) { return CONTAINING_RECORD(iface, struct collection, IDirectMusicCollection_iface); diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 316e7743ecb..08d84ff83e1 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -82,6 +82,7 @@ typedef struct port_info { struct collection; extern void collection_internal_addref(struct collection *collection); extern void collection_internal_release(struct collection *collection); +extern HRESULT collection_get_wave(struct collection *collection, DWORD index, IUnknown **out); /* CLSID */ extern HRESULT music_create(IUnknown **ret_iface); @@ -100,6 +101,7 @@ extern HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirec extern HRESULT instrument_unload_from_port(IDirectMusicDownloadedInstrument *iface, IDirectMusicPortDownload *port); extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **out); +extern HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id); /***************************************************************************** * IDirectMusic8Impl implementation structure diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index b79e7671c5b..663e2112a06 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -453,6 +453,7 @@ HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicP IDirectMusicDownload *download; DWORD size, offset_count; struct region *region; + IUnknown *wave; HRESULT hr; if (This->download) goto done; @@ -509,6 +510,13 @@ HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicP dmus_region->WaveLink = region->wave_link; dmus_region->WSMP = region->wave_sample; dmus_region->WLOOP[0] = region->wave_loop; + + if (SUCCEEDED(hr = collection_get_wave(This->collection, region->wave_link.ulTableIndex, &wave))) + { + hr = wave_download_to_port(wave, port, &dmus_region->WaveLink.ulTableIndex); + IUnknown_Release(wave); + } + if (FAILED(hr)) goto failed; } if (FAILED(hr = IDirectMusicPortDownload_Download(port, download))) goto failed; @@ -530,12 +538,37 @@ HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicP HRESULT instrument_unload_from_port(IDirectMusicDownloadedInstrument *iface, IDirectMusicPortDownload *port) { struct instrument *This = impl_from_IDirectMusicDownloadedInstrument(iface); + struct download_buffer *buffer; + DWORD size; HRESULT hr; if (!This->download) return DMUS_E_NOT_DOWNLOADED_TO_PORT; if (FAILED(hr = IDirectMusicPortDownload_Unload(port, This->download))) WARN("Failed to unload instrument download buffer, hr %#lx\n", hr); + else if (SUCCEEDED(hr = IDirectMusicDownload_GetBuffer(This->download, (void **)&buffer, &size))) + { + IDirectMusicDownload *wave_download; + DMUS_INSTRUMENT *instrument; + BYTE *ptr = (BYTE *)buffer; + DMUS_REGION *region; + UINT index; + + instrument = (DMUS_INSTRUMENT *)(ptr + buffer->offsets[0]); + for (index = instrument->ulFirstRegionIdx; index; index = region->ulNextRegionIdx) + { + region = (DMUS_REGION *)(ptr + buffer->offsets[index]); + + if (FAILED(hr = IDirectMusicPortDownload_GetBuffer(port, region->WaveLink.ulTableIndex, &wave_download))) + WARN("Failed to get wave download with id %#lx, hr %#lx\n", region->WaveLink.ulTableIndex, hr); + else + { + if (FAILED(hr = IDirectMusicPortDownload_Unload(port, wave_download))) + WARN("Failed to unload wave download buffer, hr %#lx\n", hr); + IDirectMusicDownload_Release(wave_download); + } + } + } IDirectMusicDownload_Release(This->download); This->download = NULL; diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 1561c51df76..879a6f800cd 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1300,8 +1300,7 @@ static void test_download_instrument(void) check_interface(instrument, &IID_IDirectMusicDownloadedInstrument, FALSE); hr = IDirectMusicPort_DownloadInstrument(port, instrument, &downloaded, NULL, 0); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (hr != S_OK) goto skip_tests; + ok(hr == S_OK, "got %#lx\n", hr); check_interface(downloaded, &IID_IDirectMusicObject, FALSE); check_interface(downloaded, &IID_IDirectMusicDownload, FALSE); @@ -1313,7 +1312,6 @@ static void test_download_instrument(void) IDirectMusicInstrument_Release(instrument); -skip_tests: IDirectMusicCollection_Release(collection); IDirectMusicPort_Release(port); IDirectMusic_Release(dmusic); diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index cda895484cb..08146db0d1c 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -212,3 +212,46 @@ HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnk *ret_iface = iface; return S_OK; } + +HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id) +{ + struct download_buffer + { + DMUS_DOWNLOADINFO info; + ULONG offsets[2]; + DMUS_WAVE wave; + DMUS_WAVEDATA data; + } *buffer; + + struct wave *This = impl_from_IUnknown(iface); + DWORD size = offsetof(struct download_buffer, data.byData[This->data_size]); + IDirectMusicDownload *download; + HRESULT hr; + + if (FAILED(hr = IDirectMusicPortDownload_AllocateBuffer(port, size, &download))) return hr; + + if (SUCCEEDED(hr = IDirectMusicDownload_GetBuffer(download, (void **)&buffer, &size)) + && SUCCEEDED(hr = IDirectMusicPortDownload_GetDLId(port, &buffer->info.dwDLId, 1))) + { + buffer->info.dwDLType = DMUS_DOWNLOADINFO_WAVE; + buffer->info.dwNumOffsetTableEntries = 2; + buffer->info.cbSize = size; + + buffer->offsets[0] = offsetof(struct download_buffer, wave); + buffer->offsets[1] = offsetof(struct download_buffer, data); + + buffer->wave.WaveformatEx = *This->format; + buffer->wave.ulWaveDataIdx = 1; + buffer->wave.ulCopyrightIdx = 0; + buffer->wave.ulFirstExtCkIdx = 0; + + buffer->data.cbSize = This->data_size; + memcpy(buffer->data.byData, This->data, This->data_size); + + if (SUCCEEDED(hr = IDirectMusicPortDownload_Download(port, download))) *id = buffer->info.dwDLId; + else WARN("Failed to download wave to port, hr %#lx\n", hr); + } + + IDirectMusicDownload_Release(download); + return hr; +} From 29c950ba9d42d579eae2895dae685955addea709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 15 Sep 2023 09:02:57 +0200 Subject: [PATCH 0897/1148] dmusic: Parse instrument regions articulation lists. (cherry picked from commit a788bf9641b6ed1ae0744e44aee8ae952ac1b212) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/instrument.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 663e2112a06..a15a8d4e84f 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -36,6 +36,7 @@ C_ASSERT(sizeof(struct articulation) == offsetof(struct articulation, connection struct region { struct list entry; + struct list articulations; RGNHEADER header; WAVELINK wave_link; WSMPL wave_sample; @@ -121,6 +122,13 @@ static ULONG WINAPI instrument_Release(LPDIRECTMUSICINSTRUMENT iface) LIST_FOR_EACH_ENTRY_SAFE(region, next_region, &This->regions, struct region, entry) { + LIST_FOR_EACH_ENTRY_SAFE(articulation, next_articulation, ®ion->articulations, + struct articulation, entry) + { + list_remove(&articulation->entry); + free(articulation); + } + list_remove(®ion->entry); free(region); } @@ -221,7 +229,8 @@ static HRESULT instrument_create(struct collection *collection, IDirectMusicInst return S_OK; } -static HRESULT parse_art1_chunk(struct instrument *This, IStream *stream, struct chunk_entry *chunk) +static HRESULT parse_art1_chunk(struct instrument *This, IStream *stream, struct chunk_entry *chunk, + struct list *articulations) { struct articulation *articulation; CONNECTIONLIST list; @@ -239,12 +248,13 @@ static HRESULT parse_art1_chunk(struct instrument *This, IStream *stream, struct size = sizeof(CONNECTION) * list.cConnections; if (FAILED(hr = stream_read(stream, articulation->connections, size))) free(articulation); - else list_add_tail(&This->articulations, &articulation->entry); + else list_add_tail(articulations, &articulation->entry); return hr; } -static HRESULT parse_lart_list(struct instrument *This, IStream *stream, struct chunk_entry *parent) +static HRESULT parse_lart_list(struct instrument *This, IStream *stream, struct chunk_entry *parent, + struct list *articulations) { struct chunk_entry chunk = {.parent = parent}; HRESULT hr; @@ -254,7 +264,7 @@ static HRESULT parse_lart_list(struct instrument *This, IStream *stream, struct switch (MAKE_IDTYPE(chunk.id, chunk.type)) { case FOURCC_ART1: - hr = parse_art1_chunk(This, stream, &chunk); + hr = parse_art1_chunk(This, stream, &chunk, articulations); break; default: @@ -275,6 +285,7 @@ static HRESULT parse_rgn_chunk(struct instrument *This, IStream *stream, struct HRESULT hr; if (!(region = malloc(sizeof(*region)))) return E_OUTOFMEMORY; + list_init(®ion->articulations); while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { @@ -299,6 +310,10 @@ static HRESULT parse_rgn_chunk(struct instrument *This, IStream *stream, struct hr = stream_chunk_get_data(stream, &chunk, ®ion->wave_link, sizeof(region->wave_link)); break; + case MAKE_IDTYPE(FOURCC_LIST, FOURCC_LART): + hr = parse_lart_list(This, stream, &chunk, ®ion->articulations); + break; + default: FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); break; @@ -365,7 +380,7 @@ static HRESULT parse_ins_chunk(struct instrument *This, IStream *stream, struct break; case MAKE_IDTYPE(FOURCC_LIST, FOURCC_LART): - hr = parse_lart_list(This, stream, &chunk); + hr = parse_lart_list(This, stream, &chunk, &This->articulations); break; default: From e468659046b859473dba23c6a68a2dc790bfcafd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 9 Sep 2023 14:06:32 +0200 Subject: [PATCH 0898/1148] dmusic: Implement instrument articulation downloads. (cherry picked from commit 5626be6bb8b22d6fd476ad8c5f7e236f5d1b4ff2) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/instrument.c | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index a15a8d4e84f..8ce3c0bc979 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -452,6 +452,39 @@ HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent return S_OK; } +static void write_articulation_download(struct list *articulations, ULONG *offsets, + BYTE **ptr, UINT index, DWORD *first, UINT *end) +{ + DMUS_ARTICULATION2 *dmus_articulation2 = NULL; + struct articulation *articulation; + CONNECTIONLIST *list; + UINT size; + + LIST_FOR_EACH_ENTRY(articulation, articulations, struct articulation, entry) + { + if (dmus_articulation2) dmus_articulation2->ulNextArtIdx = index; + else *first = index; + + offsets[index++] = sizeof(DMUS_DOWNLOADINFO) + *ptr - (BYTE *)offsets; + dmus_articulation2 = (DMUS_ARTICULATION2 *)*ptr; + (*ptr) += sizeof(DMUS_ARTICULATION2); + + dmus_articulation2->ulArtIdx = index; + dmus_articulation2->ulFirstExtCkIdx = 0; + dmus_articulation2->ulNextArtIdx = 0; + + size = articulation->list.cConnections * sizeof(CONNECTION); + offsets[index++] = sizeof(DMUS_DOWNLOADINFO) + *ptr - (BYTE *)offsets; + list = (CONNECTIONLIST *)*ptr; + (*ptr) += sizeof(CONNECTIONLIST) + size; + + *list = articulation->list; + memcpy(list + 1, articulation->connections, size); + } + + *end = index; +} + struct download_buffer { DMUS_DOWNLOADINFO info; @@ -464,6 +497,7 @@ HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicP IDirectMusicDownloadedInstrument **downloaded) { struct instrument *This = impl_from_IDirectMusicInstrument(iface); + struct articulation *articulation; struct download_buffer *buffer; IDirectMusicDownload *download; DWORD size, offset_count; @@ -477,10 +511,26 @@ HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicP size += sizeof(ULONG) + sizeof(DMUS_INSTRUMENT); offset_count = 1; + LIST_FOR_EACH_ENTRY(articulation, &This->articulations, struct articulation, entry) + { + size += sizeof(ULONG) + sizeof(DMUS_ARTICULATION2); + size += sizeof(ULONG) + sizeof(CONNECTIONLIST); + size += articulation->list.cConnections * sizeof(CONNECTION); + offset_count += 2; + } + LIST_FOR_EACH_ENTRY(region, &This->regions, struct region, entry) { size += sizeof(ULONG) + sizeof(DMUS_REGION); offset_count++; + + LIST_FOR_EACH_ENTRY(articulation, ®ion->articulations, struct articulation, entry) + { + size += sizeof(ULONG) + sizeof(DMUS_ARTICULATION2); + size += sizeof(ULONG) + sizeof(CONNECTIONLIST); + size += articulation->list.cConnections * sizeof(CONNECTION); + offset_count += 2; + } } if (FAILED(hr = IDirectMusicPortDownload_AllocateBuffer(port, size, &download))) return hr; @@ -506,6 +556,9 @@ HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicP dmus_instrument->ulCopyrightIdx = 0; dmus_instrument->ulGlobalArtIdx = 0; + write_articulation_download(&This->articulations, buffer->offsets, &ptr, index, + &dmus_instrument->ulGlobalArtIdx, &index); + LIST_FOR_EACH_ENTRY(region, &This->regions, struct region, entry) { if (dmus_region) dmus_region->ulNextRegionIdx = index; @@ -532,6 +585,9 @@ HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicP IUnknown_Release(wave); } if (FAILED(hr)) goto failed; + + write_articulation_download(®ion->articulations, buffer->offsets, &ptr, index, + &dmus_region->ulRegionArtIdx, &index); } if (FAILED(hr = IDirectMusicPortDownload_Download(port, download))) goto failed; From d598a729ce05ed1d4199fc4f00a8738c506894e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 13 Sep 2023 08:56:13 +0200 Subject: [PATCH 0899/1148] dmime/tests: Test that SendPMsg also converts reference time. And avoid checking a possibly freed message. (cherry picked from commit eedde528096aca8aa69b8c0c3b27084df79551f8) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 109 +++++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 32 deletions(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 26efdd4c72f..00cbd045bd8 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1730,9 +1730,10 @@ static void test_performance_pmsg(void) static const DWORD delivery_flags[] = {DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGF_TOOL_QUEUE, DMUS_PMSGF_TOOL_ATTIME}; static const DWORD message_types[] = {DMUS_PMSGT_MIDI, DMUS_PMSGT_USER}; IDirectMusicPerformance *performance; - IDirectMusicGraph *graph; + IDirectMusicGraph *graph, *performance_graph; IDirectMusicTool *tool; DMUS_PMSG *msg, *clone; + MUSIC_TIME music_time; REFERENCE_TIME time; HRESULT hr; DWORD ret; @@ -1744,6 +1745,8 @@ static void test_performance_pmsg(void) hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicPerformance, (void **)&performance); ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&performance_graph); + ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_AllocPMsg(performance, 0, NULL); @@ -1826,6 +1829,40 @@ static void test_performance_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); + /* SendPMsg skips all the tools unless messages are stamped beforehand */ + + hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(DMUS_PMSG), &msg); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); + msg->mtTime = 0; + msg->dwFlags = DMUS_PMSGF_MUSICTIME; + msg->dwType = DMUS_PMSGT_USER; + hr = IDirectMusicPerformance_SendPMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 10, &msg); + ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); + ok(!msg, "got %p\n", msg); + + + /* SendPMsg converts music time to reference time if it is missing */ + + hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(DMUS_PMSG), &msg); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); + msg->mtTime = 500; + msg->dwFlags = DMUS_PMSGF_MUSICTIME; + msg->dwType = DMUS_PMSGT_USER; + hr = IDirectMusicGraph_StampPMsg(performance_graph, msg); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SendPMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 50, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + todo_wine ok(msg != NULL, "got %p\n", msg); + if (!msg) goto skip_rtime; + time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, msg->mtTime, &time); ok(hr == S_OK, "got %#lx\n", hr); @@ -1833,32 +1870,26 @@ static void test_performance_pmsg(void) todo_wine ok(msg->rtTime == time, "got %I64d\n", msg->rtTime); ok(msg->mtTime == 500, "got %ld\n", msg->mtTime); todo_wine ok(msg->dwFlags & DMUS_PMSGF_REFTIME, "got %#lx\n", msg->dwFlags); + todo_wine ok(msg->dwFlags & DMUS_PMSGF_MUSICTIME, "got %#lx\n", msg->dwFlags); todo_wine ok(msg->dwFlags & (DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_IMMEDIATE), "got %#lx\n", msg->dwFlags); ok(!msg->dwPChannel, "got %ld\n", msg->dwPChannel); ok(!msg->dwVirtualTrackID, "got %ld\n", msg->dwVirtualTrackID); - ok(!msg->pTool, "got %p\n", msg->pTool); - ok(!msg->pGraph, "got %p\n", msg->pGraph); - ok(!msg->dwType, "got %#lx\n", msg->dwType); + ok(msg->pTool == tool, "got %p\n", msg->pTool); + ok(msg->pGraph == performance_graph, "got %p\n", msg->pGraph); + ok(msg->dwType == DMUS_PMSGT_USER, "got %#lx\n", msg->dwType); ok(!msg->dwVoiceID, "got %ld\n", msg->dwVoiceID); ok(!msg->dwGroupID, "got %ld\n", msg->dwGroupID); ok(!msg->punkUser, "got %p\n", msg->punkUser); - - /* SendPMsg skips all the tools unless messages are stamped beforehand */ - - hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(DMUS_PMSG), &msg); - ok(hr == S_OK, "got %#lx\n", hr); - ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); - msg->rtTime = time; - msg->dwFlags = DMUS_PMSGF_REFTIME; - msg->dwType = DMUS_PMSGT_USER; - hr = IDirectMusicPerformance_SendPMsg(performance, msg); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - ret = test_tool_wait_message(tool, 10, &msg); - ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); - ok(!msg, "got %p\n", msg); +skip_rtime: + /* SendPMsg converts reference time to music time if it is missing */ + + hr = IDirectMusicPerformance_GetTime(performance, &time, &music_time); + ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(DMUS_PMSG), &msg); ok(hr == S_OK, "got %#lx\n", hr); @@ -1866,24 +1897,39 @@ static void test_performance_pmsg(void) msg->rtTime = time; msg->dwFlags = DMUS_PMSGF_REFTIME; msg->dwType = DMUS_PMSGT_USER; - - hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); + hr = IDirectMusicGraph_StampPMsg(performance_graph, msg); ok(hr == S_OK, "got %#lx\n", hr); - hr = IDirectMusicGraph_StampPMsg(graph, msg); - ok(hr == S_OK, "got %#lx\n", hr); - IDirectMusicGraph_Release(graph); - hr = IDirectMusicPerformance_SendPMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 50, &msg); todo_wine ok(!ret, "got %#lx\n", ret); todo_wine ok(msg != NULL, "got %p\n", msg); - if (!msg) hr = S_OK; - else hr = IDirectMusicPerformance_FreePMsg(performance, msg); + if (!msg) goto skip_mtime; + + music_time = 0xdeadbeef; + hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, msg->rtTime, &music_time); + ok(hr == S_OK, "got %#lx\n", hr); + ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); + todo_wine ok(msg->rtTime == time, "got %I64d\n", msg->rtTime); + todo_wine ok(msg->mtTime == music_time, "got %ld\n", msg->mtTime); + todo_wine ok(msg->dwFlags & DMUS_PMSGF_REFTIME, "got %#lx\n", msg->dwFlags); + todo_wine ok(msg->dwFlags & DMUS_PMSGF_MUSICTIME, "got %#lx\n", msg->dwFlags); + todo_wine ok(msg->dwFlags & (DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_IMMEDIATE), "got %#lx\n", msg->dwFlags); + ok(!msg->dwPChannel, "got %ld\n", msg->dwPChannel); + ok(!msg->dwVirtualTrackID, "got %ld\n", msg->dwVirtualTrackID); + ok(msg->pTool == tool, "got %p\n", msg->pTool); + ok(msg->pGraph == performance_graph, "got %p\n", msg->pGraph); + ok(msg->dwType == DMUS_PMSGT_USER, "got %#lx\n", msg->dwType); + ok(!msg->dwVoiceID, "got %ld\n", msg->dwVoiceID); + ok(!msg->dwGroupID, "got %ld\n", msg->dwGroupID); + ok(!msg->punkUser, "got %p\n", msg->punkUser); + + hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); +skip_mtime: for (i = 0; i < ARRAY_SIZE(delivery_flags); i++) { DWORD duration = 0; @@ -1896,12 +1942,8 @@ static void test_performance_pmsg(void) msg->rtTime = time + 150 * 10000; msg->dwFlags = DMUS_PMSGF_REFTIME; msg->dwType = DMUS_PMSGT_USER; - - hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph); - ok(hr == S_OK, "got %#lx\n", hr); - hr = IDirectMusicGraph_StampPMsg(graph, msg); + hr = IDirectMusicGraph_StampPMsg(performance_graph, msg); ok(hr == S_OK, "got %#lx\n", hr); - IDirectMusicGraph_Release(graph); msg->dwFlags &= ~(DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME); msg->dwFlags |= delivery_flags[i]; @@ -1921,8 +1963,8 @@ static void test_performance_pmsg(void) switch (delivery_flags[i]) { case DMUS_PMSGF_TOOL_IMMEDIATE: todo_wine ok(duration <= 50, "got %lu\n", duration); break; - case DMUS_PMSGF_TOOL_QUEUE: todo_wine ok(duration >= 50 && duration <= 100, "got %lu\n", duration); break; - case DMUS_PMSGF_TOOL_ATTIME: todo_wine ok(duration >= 150 && duration <= 500, "got %lu\n", duration); break; + case DMUS_PMSGF_TOOL_QUEUE: todo_wine ok(duration >= 50 && duration <= 125, "got %lu\n", duration); break; + case DMUS_PMSGF_TOOL_ATTIME: todo_wine ok(duration >= 125 && duration <= 500, "got %lu\n", duration); break; } } @@ -1931,7 +1973,10 @@ static void test_performance_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicGraph_Release(performance_graph); + IDirectMusicPerformance_Release(performance); + IDirectMusicTool_Release(tool); } START_TEST(dmime) From 433c5fc5a10e4256843c2681c1dad69a304547cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 11:03:39 +0200 Subject: [PATCH 0900/1148] dmime: Convert DMUS_PMSG music and reference times in SendPMsg. (cherry picked from commit 6cd1c4e64aa0c9bd82b1b307f40edfc1ea4258f6) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index b3fd4851d45..c2681c4b150 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -74,7 +74,6 @@ struct DMUS_PMSGItem { DMUS_PMSGItem* next; DMUS_PMSGItem* prev; - REFERENCE_TIME rtItemTime; BOOL bInUse; DWORD cb; DMUS_PMSG pMsg; @@ -141,14 +140,14 @@ static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) { it = it_next; } - for (it = This->head; NULL != it && it->rtItemTime < rtCurTime + dwDec; ) { + for (it = This->head; NULL != it && it->pMsg.rtTime < rtCurTime + dwDec; ) { it_next = it->next; cur = ProceedMsg(This, it); free(cur); it = it_next; } if (NULL != it) { - timeOut = ( it->rtItemTime - rtCurTime ) + This->rtLatencyTime; + timeOut = ( it->pMsg.rtTime - rtCurTime ) + This->rtLatencyTime; } outrefresh: @@ -425,11 +424,22 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS hr = DMUS_E_ALREADY_SENT; else { - /* TODO: Valid Flags */ - /* TODO: DMUS_PMSGF_MUSICTIME */ - message->rtItemTime = msg->rtTime; + if (!(msg->dwFlags & DMUS_PMSGF_MUSICTIME)) + { + if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, + msg->rtTime, &msg->mtTime))) + goto done; + msg->dwFlags |= DMUS_PMSGF_MUSICTIME; + } + if (!(msg->dwFlags & DMUS_PMSGF_REFTIME)) + { + if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(iface, + msg->mtTime, &msg->rtTime))) + goto done; + msg->dwFlags |= DMUS_PMSGF_REFTIME; + } - for (it = *queue; NULL != it && it->rtItemTime < message->rtItemTime; it = it->next) + for (it = *queue; NULL != it && it->pMsg.rtTime < message->pMsg.rtTime; it = it->next) prev_it = it; if (!prev_it) @@ -452,6 +462,7 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS hr = S_OK; } +done: LeaveCriticalSection(&This->safe); return hr; From 5eccbcc905cb7f5541bfc192fa4630153890a9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 10:55:52 +0200 Subject: [PATCH 0901/1148] dmime: Get rid of the DMUS_PMSGItem typedef. (cherry picked from commit 419ab9284ab10627a0d068a0846b7c79e2fb1b3c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 43 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index c2681c4b150..786ad817cd4 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -62,24 +62,24 @@ struct performance DWORD procThreadId; BOOL procThreadTicStarted; CRITICAL_SECTION safe; - struct DMUS_PMSGItem *head; - struct DMUS_PMSGItem *imm_head; + struct message *head; + struct message *imm_head; IReferenceClock *master_clock; REFERENCE_TIME init_time; }; -typedef struct DMUS_PMSGItem DMUS_PMSGItem; -struct DMUS_PMSGItem { - DMUS_PMSGItem* next; - DMUS_PMSGItem* prev; +struct message +{ + struct message *next; + struct message *prev; - BOOL bInUse; - DWORD cb; - DMUS_PMSG pMsg; + BOOL bInUse; + DWORD cb; + DMUS_PMSG pMsg; }; -#define DMUS_PMSGToItem(pMSG) ((DMUS_PMSGItem *)(((unsigned char *)pMSG) - offsetof(DMUS_PMSGItem, pMsg))) +#define DMUS_PMSGToItem(pMSG) ((struct message *)(((unsigned char *)pMSG) - offsetof(struct message, pMsg))) #define DMUS_ItemRemoveFromQueue(This,pItem) \ {\ if (pItem->prev) pItem->prev->next = pItem->next;\ @@ -95,7 +95,8 @@ struct DMUS_PMSGItem { #define PROCESSMSG_ADD (WM_APP + 4) -static DMUS_PMSGItem* ProceedMsg(struct performance *This, DMUS_PMSGItem* cur) { +static struct message *ProceedMsg(struct performance *This, struct message *cur) +{ if (cur->pMsg.dwType == DMUS_PMSGT_NOTIFICATION) { SetEvent(This->hNotification); } @@ -117,9 +118,9 @@ static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) { MSG msg; HRESULT hr; REFERENCE_TIME rtCurTime; - DMUS_PMSGItem* it = NULL; - DMUS_PMSGItem* cur = NULL; - DMUS_PMSGItem* it_next = NULL; + struct message *it = NULL; + struct message *cur = NULL; + struct message *it_next = NULL; while (TRUE) { DWORD dwDec = This->rtLatencyTime + This->dwBumperLength; @@ -401,10 +402,10 @@ static HRESULT WINAPI performance_GetBumperLength(IDirectMusicPerformance8 *ifac static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); - DMUS_PMSGItem *message; - DMUS_PMSGItem *it = NULL; - DMUS_PMSGItem *prev_it = NULL; - DMUS_PMSGItem **queue; + struct message *message; + struct message *it = NULL; + struct message *prev_it = NULL; + struct message **queue; HRESULT hr; FIXME("(%p, %p): semi-stub\n", This, msg); @@ -535,14 +536,14 @@ static HRESULT WINAPI performance_GetTime(IDirectMusicPerformance8 *iface, REFER static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULONG size, DMUS_PMSG **msg) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); - DMUS_PMSGItem *message; + struct message *message; TRACE("(%p, %ld, %p)\n", This, size, msg); if (!msg) return E_POINTER; if (size < sizeof(DMUS_PMSG)) return E_INVALIDARG; - if (!(message = calloc(1, size - sizeof(DMUS_PMSG) + sizeof(DMUS_PMSGItem)))) return E_OUTOFMEMORY; + if (!(message = calloc(1, size - sizeof(DMUS_PMSG) + sizeof(struct message)))) return E_OUTOFMEMORY; message->pMsg.dwSize = size; *msg = &message->pMsg; @@ -552,7 +553,7 @@ static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULO static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); - DMUS_PMSGItem *message; + struct message *message; HRESULT hr; TRACE("(%p, %p)\n", This, msg); From 7f1192f64d5631874fc3681b566037c6b759c106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 18:24:32 +0200 Subject: [PATCH 0902/1148] dmime: Use a struct list to keep performance messages. (cherry picked from commit db9758fb4e064b46fcb17f6d13f4a6cfa7c525b6) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 79 +++++++++++++--------------------------- 1 file changed, 26 insertions(+), 53 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 786ad817cd4..b3d821d90bc 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -62,8 +62,8 @@ struct performance DWORD procThreadId; BOOL procThreadTicStarted; CRITICAL_SECTION safe; - struct message *head; - struct message *imm_head; + struct list immediate_messages; + struct list queued_messages; IReferenceClock *master_clock; REFERENCE_TIME init_time; @@ -71,23 +71,13 @@ struct performance struct message { - struct message *next; - struct message *prev; - + struct list entry; BOOL bInUse; DWORD cb; DMUS_PMSG pMsg; }; #define DMUS_PMSGToItem(pMSG) ((struct message *)(((unsigned char *)pMSG) - offsetof(struct message, pMsg))) -#define DMUS_ItemRemoveFromQueue(This,pItem) \ -{\ - if (pItem->prev) pItem->prev->next = pItem->next;\ - if (pItem->next) pItem->next->prev = pItem->prev;\ - if (This->head == pItem) This->head = pItem->next;\ - if (This->imm_head == pItem) This->imm_head = pItem->next;\ - pItem->bInUse = FALSE;\ -} #define PROCESSMSG_START (WM_APP + 0) #define PROCESSMSG_EXIT (WM_APP + 1) @@ -100,7 +90,8 @@ static struct message *ProceedMsg(struct performance *This, struct message *cur) if (cur->pMsg.dwType == DMUS_PMSGT_NOTIFICATION) { SetEvent(This->hNotification); } - DMUS_ItemRemoveFromQueue(This, cur); + list_remove(&cur->entry); + cur->bInUse = FALSE; switch (cur->pMsg.dwType) { case DMUS_PMSGT_WAVE: case DMUS_PMSGT_TEMPO: @@ -118,9 +109,8 @@ static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) { MSG msg; HRESULT hr; REFERENCE_TIME rtCurTime; - struct message *it = NULL; + struct message *message, *next; struct message *cur = NULL; - struct message *it_next = NULL; while (TRUE) { DWORD dwDec = This->rtLatencyTime + This->dwBumperLength; @@ -134,21 +124,18 @@ static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) { goto outrefresh; } - for (it = This->imm_head; NULL != it; ) { - it_next = it->next; - cur = ProceedMsg(This, it); - free(cur); - it = it_next; + LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->immediate_messages, struct message, entry) + { + cur = ProceedMsg(This, message); + free(cur); } - for (it = This->head; NULL != it && it->pMsg.rtTime < rtCurTime + dwDec; ) { - it_next = it->next; - cur = ProceedMsg(This, it); - free(cur); - it = it_next; - } - if (NULL != it) { - timeOut = ( it->pMsg.rtTime - rtCurTime ) + This->rtLatencyTime; + LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->queued_messages, struct message, entry) + { + timeOut = (message->pMsg.rtTime - rtCurTime) + This->rtLatencyTime; + if (message->pMsg.rtTime >= rtCurTime + dwDec) break; + cur = ProceedMsg(This, message); + free(cur); } outrefresh: @@ -402,10 +389,8 @@ static HRESULT WINAPI performance_GetBumperLength(IDirectMusicPerformance8 *ifac static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); - struct message *message; - struct message *it = NULL; - struct message *prev_it = NULL; - struct message **queue; + struct message *message, *next; + struct list *queue; HRESULT hr; FIXME("(%p, %p): semi-stub\n", This, msg); @@ -414,8 +399,8 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS if (!This->dmusic) return DMUS_E_NO_MASTER_CLOCK; if (!(msg->dwFlags & (DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_REFTIME))) return E_INVALIDARG; - if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE) queue = &This->imm_head; - else queue = &This->head; + if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE) queue = &This->immediate_messages; + else queue = &This->queued_messages; message = DMUS_PMSGToItem(msg); @@ -440,24 +425,9 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS msg->dwFlags |= DMUS_PMSGF_REFTIME; } - for (it = *queue; NULL != it && it->pMsg.rtTime < message->pMsg.rtTime; it = it->next) - prev_it = it; - - if (!prev_it) - { - message->prev = NULL; - if (*queue) message->next = (*queue)->next; - /*assert( NULL == message->next->prev );*/ - if (message->next) message->next->prev = message; - *queue = message; - } - else - { - message->prev = prev_it; - message->next = prev_it->next; - prev_it->next = message; - if (message->next) message->next->prev = message; - } + LIST_FOR_EACH_ENTRY(next, queue, struct message, entry) + if (next->pMsg.rtTime >= message->pMsg.rtTime) break; + list_add_before(&next->entry, &message->entry); message->bInUse = TRUE; hr = S_OK; @@ -1517,6 +1487,9 @@ HRESULT create_dmperformance(REFIID iid, void **ret_iface) obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": performance->safe"); wine_rb_init(&obj->pchannels, pchannel_block_compare); + list_init(&obj->immediate_messages); + list_init(&obj->queued_messages); + obj->rtLatencyTime = 100; /* 100 ms TO FIX */ obj->dwBumperLength = 50; /* 50 ms default */ obj->dwPrepareTime = 1000; /* 1000 ms default */ From cd74a8d3c7337553f6b7d3f04bd65c628754a629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 18:27:10 +0200 Subject: [PATCH 0903/1148] dmime: Remove unnecessary struct message members. (cherry picked from commit 61ebdbcbbdf997b0971527f68571500245d687c3) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index b3d821d90bc..77089d51709 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -72,8 +72,6 @@ struct performance struct message { struct list entry; - BOOL bInUse; - DWORD cb; DMUS_PMSG pMsg; }; @@ -91,7 +89,7 @@ static struct message *ProceedMsg(struct performance *This, struct message *cur) SetEvent(This->hNotification); } list_remove(&cur->entry); - cur->bInUse = FALSE; + list_init(&cur->entry); switch (cur->pMsg.dwType) { case DMUS_PMSGT_WAVE: case DMUS_PMSGT_TEMPO: @@ -406,7 +404,7 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS EnterCriticalSection(&This->safe); - if (message->bInUse) + if (!list_empty(&message->entry)) hr = DMUS_E_ALREADY_SENT; else { @@ -429,7 +427,6 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS if (next->pMsg.rtTime >= message->pMsg.rtTime) break; list_add_before(&next->entry, &message->entry); - message->bInUse = TRUE; hr = S_OK; } @@ -515,6 +512,7 @@ static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULO if (!(message = calloc(1, size - sizeof(DMUS_PMSG) + sizeof(struct message)))) return E_OUTOFMEMORY; message->pMsg.dwSize = size; + list_init(&message->entry); *msg = &message->pMsg; return S_OK; @@ -532,7 +530,7 @@ static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS message = DMUS_PMSGToItem(msg); EnterCriticalSection(&This->safe); - hr = message->bInUse ? DMUS_E_CANNOT_FREE : S_OK; + hr = !list_empty(&message->entry) ? DMUS_E_CANNOT_FREE : S_OK; LeaveCriticalSection(&This->safe); if (SUCCEEDED(hr)) From fe890c79c27a59ee5669cd4fa042ba0a7ec95e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 18:27:38 +0200 Subject: [PATCH 0904/1148] dmime: Rename DMUS_PMSGToItem to message_from_DMUS_PMSG. (cherry picked from commit 0a93c69b13f4c6a16f5a5bac40b5844d40504070) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 77089d51709..4c79a22091c 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -72,10 +72,13 @@ struct performance struct message { struct list entry; - DMUS_PMSG pMsg; + DMUS_PMSG msg; }; -#define DMUS_PMSGToItem(pMSG) ((struct message *)(((unsigned char *)pMSG) - offsetof(struct message, pMsg))) +static inline struct message *message_from_DMUS_PMSG(DMUS_PMSG *msg) +{ + return msg ? CONTAINING_RECORD(msg, struct message, msg) : NULL; +} #define PROCESSMSG_START (WM_APP + 0) #define PROCESSMSG_EXIT (WM_APP + 1) @@ -85,17 +88,17 @@ struct message static struct message *ProceedMsg(struct performance *This, struct message *cur) { - if (cur->pMsg.dwType == DMUS_PMSGT_NOTIFICATION) { + if (cur->msg.dwType == DMUS_PMSGT_NOTIFICATION) { SetEvent(This->hNotification); } list_remove(&cur->entry); list_init(&cur->entry); - switch (cur->pMsg.dwType) { + switch (cur->msg.dwType) { case DMUS_PMSGT_WAVE: case DMUS_PMSGT_TEMPO: case DMUS_PMSGT_STOP: default: - FIXME("Unhandled PMsg Type: %#lx\n", cur->pMsg.dwType); + FIXME("Unhandled PMsg Type: %#lx\n", cur->msg.dwType); break; } return cur; @@ -130,8 +133,8 @@ static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) { LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->queued_messages, struct message, entry) { - timeOut = (message->pMsg.rtTime - rtCurTime) + This->rtLatencyTime; - if (message->pMsg.rtTime >= rtCurTime + dwDec) break; + timeOut = (message->msg.rtTime - rtCurTime) + This->rtLatencyTime; + if (message->msg.rtTime >= rtCurTime + dwDec) break; cur = ProceedMsg(This, message); free(cur); } @@ -393,15 +396,13 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS FIXME("(%p, %p): semi-stub\n", This, msg); - if (!msg) return E_POINTER; + if (!(message = message_from_DMUS_PMSG(msg))) return E_POINTER; if (!This->dmusic) return DMUS_E_NO_MASTER_CLOCK; if (!(msg->dwFlags & (DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_REFTIME))) return E_INVALIDARG; if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE) queue = &This->immediate_messages; else queue = &This->queued_messages; - message = DMUS_PMSGToItem(msg); - EnterCriticalSection(&This->safe); if (!list_empty(&message->entry)) @@ -424,7 +425,7 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS } LIST_FOR_EACH_ENTRY(next, queue, struct message, entry) - if (next->pMsg.rtTime >= message->pMsg.rtTime) break; + if (next->msg.rtTime >= message->msg.rtTime) break; list_add_before(&next->entry, &message->entry); hr = S_OK; @@ -511,9 +512,9 @@ static HRESULT WINAPI performance_AllocPMsg(IDirectMusicPerformance8 *iface, ULO if (size < sizeof(DMUS_PMSG)) return E_INVALIDARG; if (!(message = calloc(1, size - sizeof(DMUS_PMSG) + sizeof(struct message)))) return E_OUTOFMEMORY; - message->pMsg.dwSize = size; + message->msg.dwSize = size; list_init(&message->entry); - *msg = &message->pMsg; + *msg = &message->msg; return S_OK; } @@ -526,8 +527,7 @@ static HRESULT WINAPI performance_FreePMsg(IDirectMusicPerformance8 *iface, DMUS TRACE("(%p, %p)\n", This, msg); - if (!msg) return E_POINTER; - message = DMUS_PMSGToItem(msg); + if (!(message = message_from_DMUS_PMSG(msg))) return E_POINTER; EnterCriticalSection(&This->safe); hr = !list_empty(&message->entry) ? DMUS_E_CANNOT_FREE : S_OK; From 16f7c3c1f1abaeb2624bb34c94a9bf4815f3cd68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 13 Sep 2023 09:05:37 +0200 Subject: [PATCH 0905/1148] dmime: Pass the DMUS_PMSG through the performance graph. (cherry picked from commit 5f0474009ea7dd57b289685471228d6b53d07ec2) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 203 ++++++++++++++++++++++----------------- dlls/dmime/tests/dmime.c | 42 ++++---- 2 files changed, 133 insertions(+), 112 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 4c79a22091c..d8624427423 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -62,11 +62,10 @@ struct performance DWORD procThreadId; BOOL procThreadTicStarted; CRITICAL_SECTION safe; - struct list immediate_messages; - struct list queued_messages; IReferenceClock *master_clock; REFERENCE_TIME init_time; + struct list messages; }; struct message @@ -80,99 +79,109 @@ static inline struct message *message_from_DMUS_PMSG(DMUS_PMSG *msg) return msg ? CONTAINING_RECORD(msg, struct message, msg) : NULL; } +static HRESULT performance_process_message(struct performance *This, DMUS_PMSG *msg, DWORD *timeout) +{ + static const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME; + IDirectMusicPerformance *performance = (IDirectMusicPerformance *)&This->IDirectMusicPerformance8_iface; + HRESULT hr; + + do + { + REFERENCE_TIME current, offset = 0; + IDirectMusicTool *tool; + + if (FAILED(hr = IDirectMusicPerformance_GetTime(performance, ¤t, NULL))) return hr; + if (!(tool = msg->pTool)) tool = &This->IDirectMusicTool_iface; + + switch (msg->dwFlags & delivery_flags) + { + default: + WARN("No delivery flag found for message %p\n", msg); + /* fallthrough */ + case DMUS_PMSGF_TOOL_IMMEDIATE: + hr = IDirectMusicTool_ProcessPMsg(tool, performance, msg); + break; + case DMUS_PMSGF_TOOL_QUEUE: + offset = This->dwBumperLength * 10000; + /* fallthrough */ + case DMUS_PMSGF_TOOL_ATTIME: + if (msg->rtTime >= offset && msg->rtTime - offset >= current) + { + if (timeout) *timeout = (msg->rtTime - offset - current) / 10000; + return DMUS_S_REQUEUE; + } + + hr = IDirectMusicTool_ProcessPMsg(tool, performance, msg); + break; + } + } while (hr == DMUS_S_REQUEUE); + + if (hr == DMUS_S_FREE) hr = IDirectMusicPerformance_FreePMsg(performance, msg); + if (FAILED(hr)) WARN("Failed to process message, hr %#lx\n", hr); + return hr; +} + #define PROCESSMSG_START (WM_APP + 0) #define PROCESSMSG_EXIT (WM_APP + 1) #define PROCESSMSG_REMOVE (WM_APP + 2) #define PROCESSMSG_ADD (WM_APP + 4) +static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) +{ + struct performance *This = lpParam; + DWORD timeout = INFINITE; + MSG msg; + HRESULT hr; + struct message *message, *next; -static struct message *ProceedMsg(struct performance *This, struct message *cur) -{ - if (cur->msg.dwType == DMUS_PMSGT_NOTIFICATION) { - SetEvent(This->hNotification); - } - list_remove(&cur->entry); - list_init(&cur->entry); - switch (cur->msg.dwType) { - case DMUS_PMSGT_WAVE: - case DMUS_PMSGT_TEMPO: - case DMUS_PMSGT_STOP: - default: - FIXME("Unhandled PMsg Type: %#lx\n", cur->msg.dwType); - break; - } - return cur; -} + while (TRUE) + { + if (timeout > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeout, QS_POSTMESSAGE | QS_SENDMESSAGE | QS_TIMER); + timeout = INFINITE; -static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) { - struct performance *This = lpParam; - DWORD timeOut = INFINITE; - MSG msg; - HRESULT hr; - REFERENCE_TIME rtCurTime; - struct message *message, *next; - struct message *cur = NULL; + EnterCriticalSection(&This->safe); - while (TRUE) { - DWORD dwDec = This->rtLatencyTime + This->dwBumperLength; + LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry) + { + list_remove(&message->entry); + list_init(&message->entry); - if (timeOut > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeOut, QS_POSTMESSAGE|QS_SENDMESSAGE|QS_TIMER); - timeOut = INFINITE; + hr = performance_process_message(This, &message->msg, &timeout); + if (hr == DMUS_S_REQUEUE) list_add_before(&next->entry, &message->entry); + if (hr != S_OK) break; + } - EnterCriticalSection(&This->safe); - hr = IDirectMusicPerformance8_GetTime(&This->IDirectMusicPerformance8_iface, &rtCurTime, NULL); - if (FAILED(hr)) { - goto outrefresh; - } - - LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->immediate_messages, struct message, entry) - { - cur = ProceedMsg(This, message); - free(cur); - } + LeaveCriticalSection(&This->safe); - LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->queued_messages, struct message, entry) - { - timeOut = (message->msg.rtTime - rtCurTime) + This->rtLatencyTime; - if (message->msg.rtTime >= rtCurTime + dwDec) break; - cur = ProceedMsg(This, message); - free(cur); - } + while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) + { + /** if hwnd we suppose that is a windows event ... */ + if (NULL != msg.hwnd) + { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + else + { + switch (msg.message) + { + case WM_QUIT: + case PROCESSMSG_EXIT: goto outofthread; + case PROCESSMSG_START: break; + case PROCESSMSG_ADD: break; + case PROCESSMSG_REMOVE: break; + default: ERR("Unhandled message %u. Critical Path\n", msg.message); break; + } + } + } -outrefresh: - LeaveCriticalSection(&This->safe); - - while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) { - /** if hwnd we suppose that is a windows event ... */ - if (NULL != msg.hwnd) { - TranslateMessage(&msg); - DispatchMessageA(&msg); - } else { - switch (msg.message) { - case WM_QUIT: - case PROCESSMSG_EXIT: - goto outofthread; - case PROCESSMSG_START: - break; - case PROCESSMSG_ADD: - break; - case PROCESSMSG_REMOVE: - break; - default: - ERR("Unhandled message %u. Critical Path\n", msg.message); - break; - } - } + /** here we should run a little of current AudioPath */ } - /** here we should run a little of current AudioPath */ - - } - outofthread: - TRACE("(%p): Exiting\n", This); - - return 0; + TRACE("(%p): Exiting\n", This); + + return 0; } static BOOL PostMessageToProcessMsgThread(struct performance *This, UINT iMsg) { @@ -389,9 +398,9 @@ static HRESULT WINAPI performance_GetBumperLength(IDirectMusicPerformance8 *ifac static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS_PMSG *msg) { + const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME; struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct message *message, *next; - struct list *queue; HRESULT hr; FIXME("(%p, %p): semi-stub\n", This, msg); @@ -400,15 +409,13 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS if (!This->dmusic) return DMUS_E_NO_MASTER_CLOCK; if (!(msg->dwFlags & (DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_REFTIME))) return E_INVALIDARG; - if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE) queue = &This->immediate_messages; - else queue = &This->queued_messages; - EnterCriticalSection(&This->safe); if (!list_empty(&message->entry)) hr = DMUS_E_ALREADY_SENT; else { + if (!(msg->dwFlags & delivery_flags)) msg->dwFlags |= DMUS_PMSGF_TOOL_IMMEDIATE; if (!(msg->dwFlags & DMUS_PMSGF_MUSICTIME)) { if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, @@ -424,9 +431,16 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS msg->dwFlags |= DMUS_PMSGF_REFTIME; } - LIST_FOR_EACH_ENTRY(next, queue, struct message, entry) + if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE) + { + hr = performance_process_message(This, &message->msg, NULL); + if (hr != DMUS_S_REQUEUE) goto done; + } + + LIST_FOR_EACH_ENTRY(next, &This->messages, struct message, entry) if (next->msg.rtTime >= message->msg.rtTime) break; list_add_before(&next->entry, &message->entry); + PostThreadMessageW(This->procThreadId, PROCESSMSG_ADD, 0, 0); hr = S_OK; } @@ -1440,8 +1454,20 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, IDirectMusicPerformance *performance, DMUS_PMSG *msg) { struct performance *This = impl_from_IDirectMusicTool(iface); - FIXME("(%p, %p, %p): stub\n", This, performance, msg); - return E_NOTIMPL; + + FIXME("(%p, %p, %p): semi-stub\n", This, performance, msg); + + switch (msg->dwType) + { + case DMUS_PMSGT_NOTIFICATION: + SetEvent(This->hNotification); + /* fallthrough */ + default: + FIXME("Unhandled message type %#lx\n", msg->dwType); + break; + } + + return DMUS_S_FREE; } static HRESULT WINAPI performance_tool_Flush(IDirectMusicTool *iface, @@ -1485,8 +1511,7 @@ HRESULT create_dmperformance(REFIID iid, void **ret_iface) obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": performance->safe"); wine_rb_init(&obj->pchannels, pchannel_block_compare); - list_init(&obj->immediate_messages); - list_init(&obj->queued_messages); + list_init(&obj->messages); obj->rtLatencyTime = 100; /* 100 ms TO FIX */ obj->dwBumperLength = 50; /* 50 ms default */ diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 00cbd045bd8..89be2c32019 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1506,7 +1506,7 @@ static void test_performance_tool(void) hr = IDirectMusicTool_GetMediaTypes(tool, (DWORD **)&types, 64); ok(hr == E_NOTIMPL, "got %#lx\n", hr); hr = IDirectMusicTool_ProcessPMsg(tool, performance, &msg); - todo_wine ok(hr == DMUS_S_FREE, "got %#lx\n", hr); + ok(hr == DMUS_S_FREE, "got %#lx\n", hr); hr = IDirectMusicTool_Flush(tool, performance, &msg, 0); todo_wine ok(hr == S_OK, "got %#lx\n", hr); @@ -1859,19 +1859,18 @@ static void test_performance_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 50, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - todo_wine ok(msg != NULL, "got %p\n", msg); - if (!msg) goto skip_rtime; + ok(!ret, "got %#lx\n", ret); + ok(msg != NULL, "got %p\n", msg); time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, msg->mtTime, &time); ok(hr == S_OK, "got %#lx\n", hr); ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); - todo_wine ok(msg->rtTime == time, "got %I64d\n", msg->rtTime); + ok(msg->rtTime == time, "got %I64d\n", msg->rtTime); ok(msg->mtTime == 500, "got %ld\n", msg->mtTime); - todo_wine ok(msg->dwFlags & DMUS_PMSGF_REFTIME, "got %#lx\n", msg->dwFlags); - todo_wine ok(msg->dwFlags & DMUS_PMSGF_MUSICTIME, "got %#lx\n", msg->dwFlags); - todo_wine ok(msg->dwFlags & (DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_IMMEDIATE), "got %#lx\n", msg->dwFlags); + ok(msg->dwFlags & DMUS_PMSGF_REFTIME, "got %#lx\n", msg->dwFlags); + ok(msg->dwFlags & DMUS_PMSGF_MUSICTIME, "got %#lx\n", msg->dwFlags); + ok(msg->dwFlags & (DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_IMMEDIATE), "got %#lx\n", msg->dwFlags); ok(!msg->dwPChannel, "got %ld\n", msg->dwPChannel); ok(!msg->dwVirtualTrackID, "got %ld\n", msg->dwVirtualTrackID); ok(msg->pTool == tool, "got %p\n", msg->pTool); @@ -1885,7 +1884,6 @@ static void test_performance_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); -skip_rtime: /* SendPMsg converts reference time to music time if it is missing */ hr = IDirectMusicPerformance_GetTime(performance, &time, &music_time); @@ -1903,19 +1901,18 @@ static void test_performance_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 50, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - todo_wine ok(msg != NULL, "got %p\n", msg); - if (!msg) goto skip_mtime; + ok(!ret, "got %#lx\n", ret); + ok(msg != NULL, "got %p\n", msg); music_time = 0xdeadbeef; hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, msg->rtTime, &music_time); ok(hr == S_OK, "got %#lx\n", hr); ok(msg->dwSize == sizeof(DMUS_PMSG), "got %ld\n", msg->dwSize); - todo_wine ok(msg->rtTime == time, "got %I64d\n", msg->rtTime); - todo_wine ok(msg->mtTime == music_time, "got %ld\n", msg->mtTime); - todo_wine ok(msg->dwFlags & DMUS_PMSGF_REFTIME, "got %#lx\n", msg->dwFlags); - todo_wine ok(msg->dwFlags & DMUS_PMSGF_MUSICTIME, "got %#lx\n", msg->dwFlags); - todo_wine ok(msg->dwFlags & (DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_IMMEDIATE), "got %#lx\n", msg->dwFlags); + ok(msg->rtTime == time, "got %I64d\n", msg->rtTime); + ok(msg->mtTime == music_time, "got %ld\n", msg->mtTime); + ok(msg->dwFlags & DMUS_PMSGF_REFTIME, "got %#lx\n", msg->dwFlags); + ok(msg->dwFlags & DMUS_PMSGF_MUSICTIME, "got %#lx\n", msg->dwFlags); + ok(msg->dwFlags & (DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_IMMEDIATE), "got %#lx\n", msg->dwFlags); ok(!msg->dwPChannel, "got %ld\n", msg->dwPChannel); ok(!msg->dwVirtualTrackID, "got %ld\n", msg->dwVirtualTrackID); ok(msg->pTool == tool, "got %p\n", msg->pTool); @@ -1929,7 +1926,6 @@ static void test_performance_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); -skip_mtime: for (i = 0; i < ARRAY_SIZE(delivery_flags); i++) { DWORD duration = 0; @@ -1953,8 +1949,8 @@ static void test_performance_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); msg = NULL; ret = test_tool_wait_message(tool, 1000, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - todo_wine ok(msg != NULL, "got %p\n", msg); + ok(!ret, "got %#lx\n", ret); + ok(msg != NULL, "got %p\n", msg); duration += GetTickCount(); if (msg) hr = IDirectMusicPerformance_FreePMsg(performance, msg); @@ -1962,9 +1958,9 @@ static void test_performance_pmsg(void) switch (delivery_flags[i]) { - case DMUS_PMSGF_TOOL_IMMEDIATE: todo_wine ok(duration <= 50, "got %lu\n", duration); break; - case DMUS_PMSGF_TOOL_QUEUE: todo_wine ok(duration >= 50 && duration <= 125, "got %lu\n", duration); break; - case DMUS_PMSGF_TOOL_ATTIME: todo_wine ok(duration >= 125 && duration <= 500, "got %lu\n", duration); break; + case DMUS_PMSGF_TOOL_IMMEDIATE: ok(duration <= 50, "got %lu\n", duration); break; + case DMUS_PMSGF_TOOL_QUEUE: ok(duration >= 50 && duration <= 125, "got %lu\n", duration); break; + case DMUS_PMSGF_TOOL_ATTIME: ok(duration >= 125 && duration <= 500, "got %lu\n", duration); break; } } From eb336421c63daa262e57be492acba4fbd1205c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 13:41:13 +0200 Subject: [PATCH 0906/1148] dmband: Rename IDirectMusicBandImpl prefix to band. (cherry picked from commit be0085238e510d4c8de8a874b218c533ab55638c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 53 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index 02e3573bab9..eb662f732ca 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -39,8 +39,7 @@ static inline IDirectMusicBandImpl *impl_from_IDirectMusicBand(IDirectMusicBand return CONTAINING_RECORD(iface, IDirectMusicBandImpl, IDirectMusicBand_iface); } -/* DirectMusicBandImpl IDirectMusicBand part: */ -static HRESULT WINAPI IDirectMusicBandImpl_QueryInterface(IDirectMusicBand *iface, REFIID riid, +static HRESULT WINAPI band_QueryInterface(IDirectMusicBand *iface, REFIID riid, void **ret_iface) { IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); @@ -64,7 +63,7 @@ static HRESULT WINAPI IDirectMusicBandImpl_QueryInterface(IDirectMusicBand *ifac return S_OK; } -static ULONG WINAPI IDirectMusicBandImpl_AddRef(IDirectMusicBand *iface) +static ULONG WINAPI band_AddRef(IDirectMusicBand *iface) { IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); LONG ref = InterlockedIncrement(&This->ref); @@ -74,7 +73,7 @@ static ULONG WINAPI IDirectMusicBandImpl_AddRef(IDirectMusicBand *iface) return ref; } -static ULONG WINAPI IDirectMusicBandImpl_Release(IDirectMusicBand *iface) +static ULONG WINAPI band_Release(IDirectMusicBand *iface) { IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); LONG ref = InterlockedDecrement(&This->ref); @@ -86,7 +85,7 @@ static ULONG WINAPI IDirectMusicBandImpl_Release(IDirectMusicBand *iface) return ref; } -static HRESULT WINAPI IDirectMusicBandImpl_CreateSegment(IDirectMusicBand *iface, +static HRESULT WINAPI band_CreateSegment(IDirectMusicBand *iface, IDirectMusicSegment **segment) { IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); @@ -110,7 +109,7 @@ static HRESULT WINAPI IDirectMusicBandImpl_CreateSegment(IDirectMusicBand *iface return hr; } -static HRESULT WINAPI IDirectMusicBandImpl_Download(IDirectMusicBand *iface, +static HRESULT WINAPI band_Download(IDirectMusicBand *iface, IDirectMusicPerformance *pPerformance) { IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); @@ -118,7 +117,7 @@ static HRESULT WINAPI IDirectMusicBandImpl_Download(IDirectMusicBand *iface, return S_OK; } -static HRESULT WINAPI IDirectMusicBandImpl_Unload(IDirectMusicBand *iface, +static HRESULT WINAPI band_Unload(IDirectMusicBand *iface, IDirectMusicPerformance *pPerformance) { IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); @@ -126,17 +125,17 @@ static HRESULT WINAPI IDirectMusicBandImpl_Unload(IDirectMusicBand *iface, return S_OK; } -static const IDirectMusicBandVtbl dmband_vtbl = { - IDirectMusicBandImpl_QueryInterface, - IDirectMusicBandImpl_AddRef, - IDirectMusicBandImpl_Release, - IDirectMusicBandImpl_CreateSegment, - IDirectMusicBandImpl_Download, - IDirectMusicBandImpl_Unload +static const IDirectMusicBandVtbl band_vtbl = +{ + band_QueryInterface, + band_AddRef, + band_Release, + band_CreateSegment, + band_Download, + band_Unload, }; -/* IDirectMusicBandImpl IDirectMusicObject part: */ -static HRESULT WINAPI band_IDirectMusicObject_ParseDescriptor(IDirectMusicObject *iface, +static HRESULT WINAPI band_object_ParseDescriptor(IDirectMusicObject *iface, IStream *stream, DMUS_OBJECTDESC *desc) { struct chunk_entry riff = {0}; @@ -175,18 +174,18 @@ static HRESULT WINAPI band_IDirectMusicObject_ParseDescriptor(IDirectMusicObject return S_OK; } -static const IDirectMusicObjectVtbl dmobject_vtbl = { +static const IDirectMusicObjectVtbl band_object_vtbl = +{ dmobj_IDirectMusicObject_QueryInterface, dmobj_IDirectMusicObject_AddRef, dmobj_IDirectMusicObject_Release, dmobj_IDirectMusicObject_GetDescriptor, dmobj_IDirectMusicObject_SetDescriptor, - band_IDirectMusicObject_ParseDescriptor + band_object_ParseDescriptor, }; #define DMUS_IO_INSTRUMENT_DX7_SIZE offsetof(DMUS_IO_INSTRUMENT, nPitchBendRange) -/* IDirectMusicBandImpl IPersistStream part: */ static HRESULT parse_instrument(IDirectMusicBandImpl *This, DMUS_PRIVATE_CHUNK *pChunk, IStream *pStm) { @@ -447,7 +446,7 @@ static inline IDirectMusicBandImpl *impl_from_IPersistStream(IPersistStream *ifa return CONTAINING_RECORD(iface, IDirectMusicBandImpl, dmobj.IPersistStream_iface); } -static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, IStream *pStm) +static HRESULT WINAPI band_persist_stream_Load(IPersistStream *iface, IStream *pStm) { IDirectMusicBandImpl *This = impl_from_IPersistStream(iface); DMUS_PRIVATE_CHUNK Chunk; @@ -490,18 +489,18 @@ static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, IStream *pS return S_OK; } -static const IPersistStreamVtbl persiststream_vtbl = { +static const IPersistStreamVtbl band_persist_stream_vtbl = +{ dmobj_IPersistStream_QueryInterface, dmobj_IPersistStream_AddRef, dmobj_IPersistStream_Release, unimpl_IPersistStream_GetClassID, unimpl_IPersistStream_IsDirty, - IPersistStreamImpl_Load, + band_persist_stream_Load, unimpl_IPersistStream_Save, - unimpl_IPersistStream_GetSizeMax + unimpl_IPersistStream_GetSizeMax, }; -/* for ClassFactory */ HRESULT create_dmband(REFIID lpcGUID, void **ppobj) { IDirectMusicBandImpl* obj; @@ -509,11 +508,11 @@ HRESULT create_dmband(REFIID lpcGUID, void **ppobj) *ppobj = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->IDirectMusicBand_iface.lpVtbl = &dmband_vtbl; + obj->IDirectMusicBand_iface.lpVtbl = &band_vtbl; obj->ref = 1; dmobject_init(&obj->dmobj, &CLSID_DirectMusicBand, (IUnknown *)&obj->IDirectMusicBand_iface); - obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; - obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; + obj->dmobj.IDirectMusicObject_iface.lpVtbl = &band_object_vtbl; + obj->dmobj.IPersistStream_iface.lpVtbl = &band_persist_stream_vtbl; list_init (&obj->Instruments); hr = IDirectMusicBand_QueryInterface(&obj->IDirectMusicBand_iface, lpcGUID, ppobj); From 63e960b0e286ca4406cda8e3772960b01703fb12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 13:42:05 +0200 Subject: [PATCH 0907/1148] dmband: Get rid of the IDirectMusicBandImpl typedef. (cherry picked from commit d3c5fe89f9cac1e059b803700899c7bbefcac40f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 39 ++++++++++++++++++--------------------- dlls/dmband/bandtrack.c | 3 +-- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index eb662f732ca..bbf8c93a067 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -23,26 +23,23 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmband); WINE_DECLARE_DEBUG_CHANNEL(dmfile); - -/***************************************************************************** - * IDirectMusicBandImpl implementation - */ -typedef struct IDirectMusicBandImpl { +struct band +{ IDirectMusicBand IDirectMusicBand_iface; struct dmobject dmobj; LONG ref; struct list Instruments; -} IDirectMusicBandImpl; +}; -static inline IDirectMusicBandImpl *impl_from_IDirectMusicBand(IDirectMusicBand *iface) +static inline struct band *impl_from_IDirectMusicBand(IDirectMusicBand *iface) { - return CONTAINING_RECORD(iface, IDirectMusicBandImpl, IDirectMusicBand_iface); + return CONTAINING_RECORD(iface, struct band, IDirectMusicBand_iface); } static HRESULT WINAPI band_QueryInterface(IDirectMusicBand *iface, REFIID riid, void **ret_iface) { - IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); + struct band *This = impl_from_IDirectMusicBand(iface); TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ret_iface); @@ -65,7 +62,7 @@ static HRESULT WINAPI band_QueryInterface(IDirectMusicBand *iface, REFIID riid, static ULONG WINAPI band_AddRef(IDirectMusicBand *iface) { - IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); + struct band *This = impl_from_IDirectMusicBand(iface); LONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) ref=%ld\n", This, ref); @@ -75,7 +72,7 @@ static ULONG WINAPI band_AddRef(IDirectMusicBand *iface) static ULONG WINAPI band_Release(IDirectMusicBand *iface) { - IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); + struct band *This = impl_from_IDirectMusicBand(iface); LONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) ref=%ld\n", This, ref); @@ -88,7 +85,7 @@ static ULONG WINAPI band_Release(IDirectMusicBand *iface) static HRESULT WINAPI band_CreateSegment(IDirectMusicBand *iface, IDirectMusicSegment **segment) { - IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); + struct band *This = impl_from_IDirectMusicBand(iface); HRESULT hr; DMUS_BAND_PARAM bandparam; @@ -112,7 +109,7 @@ static HRESULT WINAPI band_CreateSegment(IDirectMusicBand *iface, static HRESULT WINAPI band_Download(IDirectMusicBand *iface, IDirectMusicPerformance *pPerformance) { - IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); + struct band *This = impl_from_IDirectMusicBand(iface); FIXME("(%p, %p): stub\n", This, pPerformance); return S_OK; } @@ -120,7 +117,7 @@ static HRESULT WINAPI band_Download(IDirectMusicBand *iface, static HRESULT WINAPI band_Unload(IDirectMusicBand *iface, IDirectMusicPerformance *pPerformance) { - IDirectMusicBandImpl *This = impl_from_IDirectMusicBand(iface); + struct band *This = impl_from_IDirectMusicBand(iface); FIXME("(%p, %p): stub\n", This, pPerformance); return S_OK; } @@ -186,7 +183,7 @@ static const IDirectMusicObjectVtbl band_object_vtbl = #define DMUS_IO_INSTRUMENT_DX7_SIZE offsetof(DMUS_IO_INSTRUMENT, nPitchBendRange) -static HRESULT parse_instrument(IDirectMusicBandImpl *This, DMUS_PRIVATE_CHUNK *pChunk, +static HRESULT parse_instrument(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, IStream *pStm) { DMUS_PRIVATE_CHUNK Chunk; @@ -293,7 +290,7 @@ static HRESULT parse_instrument(IDirectMusicBandImpl *This, DMUS_PRIVATE_CHUNK * return S_OK; } -static HRESULT parse_instruments_list(IDirectMusicBandImpl *This, DMUS_PRIVATE_CHUNK *pChunk, +static HRESULT parse_instruments_list(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, IStream *pStm) { HRESULT hr; @@ -348,7 +345,7 @@ static HRESULT parse_instruments_list(IDirectMusicBandImpl *This, DMUS_PRIVATE_C return S_OK; } -static HRESULT parse_band_form(IDirectMusicBandImpl *This, DMUS_PRIVATE_CHUNK *pChunk, +static HRESULT parse_band_form(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, IStream *pStm) { HRESULT hr = E_FAIL; @@ -441,14 +438,14 @@ static HRESULT parse_band_form(IDirectMusicBandImpl *This, DMUS_PRIVATE_CHUNK *p return S_OK; } -static inline IDirectMusicBandImpl *impl_from_IPersistStream(IPersistStream *iface) +static inline struct band *impl_from_IPersistStream(IPersistStream *iface) { - return CONTAINING_RECORD(iface, IDirectMusicBandImpl, dmobj.IPersistStream_iface); + return CONTAINING_RECORD(iface, struct band, dmobj.IPersistStream_iface); } static HRESULT WINAPI band_persist_stream_Load(IPersistStream *iface, IStream *pStm) { - IDirectMusicBandImpl *This = impl_from_IPersistStream(iface); + struct band *This = impl_from_IPersistStream(iface); DMUS_PRIVATE_CHUNK Chunk; LARGE_INTEGER liMove; HRESULT hr; @@ -503,7 +500,7 @@ static const IPersistStreamVtbl band_persist_stream_vtbl = HRESULT create_dmband(REFIID lpcGUID, void **ppobj) { - IDirectMusicBandImpl* obj; + struct band* obj; HRESULT hr; *ppobj = NULL; diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index bdb9344b064..bf494d39df2 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -1,5 +1,4 @@ -/* IDirectMusicBandTrack Implementation - * +/* * Copyright (C) 2003-2004 Rok Mandeljc * * This program is free software; you can redistribute it and/or From 4d95067a5447c0f480472bb96abdfadf2373bba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 13:44:12 +0200 Subject: [PATCH 0908/1148] dmband: Get rid of the IDirectMusicBandTrack typedef. (cherry picked from commit 696e8c12087989be82c0c23f7c4c70d8f59ff65e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/bandtrack.c | 101 +++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 52 deletions(-) diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index bf494d39df2..53515bc8690 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -22,27 +22,24 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmband); WINE_DECLARE_DEBUG_CHANNEL(dmfile); -/***************************************************************************** - * IDirectMusicBandTrack implementation - */ -typedef struct IDirectMusicBandTrack { +struct band_track +{ IDirectMusicTrack8 IDirectMusicTrack8_iface; struct dmobject dmobj; /* IPersistStream only */ LONG ref; DMUS_IO_BAND_TRACK_HEADER header; struct list Bands; -} IDirectMusicBandTrack; +}; -/* IDirectMusicBandTrack IDirectMusicTrack8 part: */ -static inline IDirectMusicBandTrack *impl_from_IDirectMusicTrack8(IDirectMusicTrack8 *iface) +static inline struct band_track *impl_from_IDirectMusicTrack8(IDirectMusicTrack8 *iface) { - return CONTAINING_RECORD(iface, IDirectMusicBandTrack, IDirectMusicTrack8_iface); + return CONTAINING_RECORD(iface, struct band_track, IDirectMusicTrack8_iface); } static HRESULT WINAPI band_track_QueryInterface(IDirectMusicTrack8 *iface, REFIID riid, void **ret_iface) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ret_iface); @@ -64,7 +61,7 @@ static HRESULT WINAPI band_track_QueryInterface(IDirectMusicTrack8 *iface, REFII static ULONG WINAPI band_track_AddRef(IDirectMusicTrack8 *iface) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); LONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) ref=%ld\n", This, ref); @@ -74,7 +71,7 @@ static ULONG WINAPI band_track_AddRef(IDirectMusicTrack8 *iface) static ULONG WINAPI band_track_Release(IDirectMusicTrack8 *iface) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); LONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) ref=%ld\n", This, ref); @@ -86,16 +83,16 @@ static ULONG WINAPI band_track_Release(IDirectMusicTrack8 *iface) static HRESULT WINAPI band_track_Init(IDirectMusicTrack8 *iface, IDirectMusicSegment *pSegment) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p): stub\n", This, pSegment); - return S_OK; + struct band_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %p): stub\n", This, pSegment); + return S_OK; } static HRESULT WINAPI band_track_InitPlay(IDirectMusicTrack8 *iface, IDirectMusicSegmentState *segment_state, IDirectMusicPerformance *performance, void **state_data, DWORD virtual_track8id, DWORD flags) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); FIXME("(%p, %p, %p, %p, %ld, %lx): stub\n", This, segment_state, performance, state_data, virtual_track8id, flags); @@ -104,9 +101,9 @@ static HRESULT WINAPI band_track_InitPlay(IDirectMusicTrack8 *iface, static HRESULT WINAPI band_track_EndPlay(IDirectMusicTrack8 *iface, void *pStateData) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p): stub\n", This, pStateData); - return S_OK; + struct band_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %p): stub\n", This, pStateData); + return S_OK; } static HRESULT WINAPI band_track_Play(IDirectMusicTrack8 *iface, void *state_data, @@ -114,7 +111,7 @@ static HRESULT WINAPI band_track_Play(IDirectMusicTrack8 *iface, void *state_dat IDirectMusicPerformance *performance, IDirectMusicSegmentState *segment_state, DWORD virtual_id) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); FIXME("(%p, %p, %ld, %ld, %ld, %lx, %p, %p, %ld): semi-stub\n", This, state_data, mtStart, mtEnd, mtOffset, flags, performance, segment_state, virtual_id); @@ -131,7 +128,7 @@ static HRESULT WINAPI band_track_Play(IDirectMusicTrack8 *iface, void *state_dat static HRESULT WINAPI band_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time, MUSIC_TIME *next, void *param) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s, %ld, %p, %p)\n", This, debugstr_dmguid(type), time, next, param); @@ -148,7 +145,7 @@ static HRESULT WINAPI band_track_GetParam(IDirectMusicTrack8 *iface, REFGUID typ static HRESULT WINAPI band_track_SetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time, void *param) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s, %ld, %p)\n", This, debugstr_dmguid(type), time, param); @@ -183,7 +180,7 @@ static HRESULT WINAPI band_track_SetParam(IDirectMusicTrack8 *iface, REFGUID typ static HRESULT WINAPI band_track_IsParamSupported(IDirectMusicTrack8 *iface, REFGUID rguidType) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s)\n", This, debugstr_dmguid(rguidType)); @@ -211,7 +208,7 @@ static HRESULT WINAPI band_track_IsParamSupported(IDirectMusicTrack8 *iface, REF static HRESULT WINAPI band_track_AddNotificationType(IDirectMusicTrack8 *iface, REFGUID notiftype) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s): method not implemented\n", This, debugstr_dmguid(notiftype)); return E_NOTIMPL; @@ -220,7 +217,7 @@ static HRESULT WINAPI band_track_AddNotificationType(IDirectMusicTrack8 *iface, static HRESULT WINAPI band_track_RemoveNotificationType(IDirectMusicTrack8 *iface, REFGUID notiftype) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s): method not implemented\n", This, debugstr_dmguid(notiftype)); return E_NOTIMPL; @@ -229,9 +226,9 @@ static HRESULT WINAPI band_track_RemoveNotificationType(IDirectMusicTrack8 *ifac static HRESULT WINAPI band_track_Clone(IDirectMusicTrack8 *iface, MUSIC_TIME mtStart, MUSIC_TIME mtEnd, IDirectMusicTrack **ppTrack) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %ld, %ld, %p): stub\n", This, mtStart, mtEnd, ppTrack); - return S_OK; + struct band_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %ld, %ld, %p): stub\n", This, mtStart, mtEnd, ppTrack); + return S_OK; } static HRESULT WINAPI band_track_PlayEx(IDirectMusicTrack8 *iface, void *state_data, @@ -239,7 +236,7 @@ static HRESULT WINAPI band_track_PlayEx(IDirectMusicTrack8 *iface, void *state_d IDirectMusicPerformance *performance, IDirectMusicSegmentState *segment_state, DWORD virtual_id) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); FIXME("(%p, %p, 0x%s, 0x%s, 0x%s, %lx, %p, %p, %ld): stub\n", This, state_data, wine_dbgstr_longlong(rtStart), wine_dbgstr_longlong(rtEnd), wine_dbgstr_longlong(rtOffset), flags, performance, segment_state, virtual_id); @@ -251,7 +248,7 @@ static HRESULT WINAPI band_track_GetParamEx(IDirectMusicTrack8 *iface, REFGUID rguidType, REFERENCE_TIME rtTime, REFERENCE_TIME *rtNext, void *param, void *state_data, DWORD flags) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); FIXME("(%p, %s, 0x%s, %p, %p, %p, %lx): stub\n", This, debugstr_dmguid(rguidType), wine_dbgstr_longlong(rtTime), rtNext, param, state_data, flags); @@ -262,7 +259,7 @@ static HRESULT WINAPI band_track_GetParamEx(IDirectMusicTrack8 *iface, static HRESULT WINAPI band_track_SetParamEx(IDirectMusicTrack8 *iface, REFGUID rguidType, REFERENCE_TIME rtTime, void *param, void *state_data, DWORD flags) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); FIXME("(%p, %s, 0x%s, %p, %p, %lx): stub\n", This, debugstr_dmguid(rguidType), wine_dbgstr_longlong(rtTime), param, state_data, flags); @@ -273,7 +270,7 @@ static HRESULT WINAPI band_track_SetParamEx(IDirectMusicTrack8 *iface, REFGUID r static HRESULT WINAPI band_track_Compose(IDirectMusicTrack8 *iface, IUnknown *context, DWORD trackgroup, IDirectMusicTrack **track) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); + struct band_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %p, %ld, %p): method not implemented\n", This, context, trackgroup, track); return E_NOTIMPL; @@ -283,12 +280,13 @@ static HRESULT WINAPI band_track_Join(IDirectMusicTrack8 *iface, IDirectMusicTra MUSIC_TIME mtJoin, IUnknown *pContext, DWORD dwTrackGroup, IDirectMusicTrack **ppResultTrack) { - IDirectMusicBandTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p, %ld, %p, %ld, %p): stub\n", This, pNewTrack, mtJoin, pContext, dwTrackGroup, ppResultTrack); - return S_OK; + struct band_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %p, %ld, %p, %ld, %p): stub\n", This, pNewTrack, mtJoin, pContext, dwTrackGroup, ppResultTrack); + return S_OK; } -static const IDirectMusicTrack8Vtbl dmtrack8_vtbl = { +static const IDirectMusicTrack8Vtbl band_track_vtbl = +{ band_track_QueryInterface, band_track_AddRef, band_track_Release, @@ -306,11 +304,10 @@ static const IDirectMusicTrack8Vtbl dmtrack8_vtbl = { band_track_GetParamEx, band_track_SetParamEx, band_track_Compose, - band_track_Join + band_track_Join, }; -/* IDirectMusicBandTrack IPersistStream part: */ -static HRESULT load_band(IDirectMusicBandTrack *This, IStream *pClonedStream, +static HRESULT load_band(struct band_track *This, IStream *pClonedStream, IDirectMusicBand **ppBand, DMUS_PRIVATE_BAND_ITEM_HEADER *pHeader) { HRESULT hr = E_FAIL; @@ -352,7 +349,7 @@ static HRESULT load_band(IDirectMusicBandTrack *This, IStream *pClonedStream, return S_OK; } -static HRESULT parse_bands_list(IDirectMusicBandTrack *This, DMUS_PRIVATE_CHUNK *pChunk, +static HRESULT parse_bands_list(struct band_track *This, DMUS_PRIVATE_CHUNK *pChunk, IStream *pStm) { HRESULT hr = E_FAIL; @@ -475,7 +472,7 @@ static HRESULT parse_bands_list(IDirectMusicBandTrack *This, DMUS_PRIVATE_CHUNK return S_OK; } -static HRESULT parse_bandtrack_form(IDirectMusicBandTrack *This, DMUS_PRIVATE_CHUNK *pChunk, +static HRESULT parse_bandtrack_form(struct band_track *This, DMUS_PRIVATE_CHUNK *pChunk, IStream *pStm) { HRESULT hr = E_FAIL; @@ -566,14 +563,14 @@ static HRESULT parse_bandtrack_form(IDirectMusicBandTrack *This, DMUS_PRIVATE_CH return S_OK; } -static inline IDirectMusicBandTrack *impl_from_IPersistStream(IPersistStream *iface) +static inline struct band_track *impl_from_IPersistStream(IPersistStream *iface) { - return CONTAINING_RECORD(iface, IDirectMusicBandTrack, dmobj.IPersistStream_iface); + return CONTAINING_RECORD(iface, struct band_track, dmobj.IPersistStream_iface); } -static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, IStream *pStm) +static HRESULT WINAPI band_track_persist_stream_Load(IPersistStream *iface, IStream *pStm) { - IDirectMusicBandTrack *This = impl_from_IPersistStream(iface); + struct band_track *This = impl_from_IPersistStream(iface); DMUS_PRIVATE_CHUNK Chunk; LARGE_INTEGER liMove; HRESULT hr; @@ -614,30 +611,30 @@ static HRESULT WINAPI IPersistStreamImpl_Load(IPersistStream *iface, IStream *pS return S_OK; } -static const IPersistStreamVtbl persiststream_vtbl = { +static const IPersistStreamVtbl band_track_persist_stream_vtbl = +{ dmobj_IPersistStream_QueryInterface, dmobj_IPersistStream_AddRef, dmobj_IPersistStream_Release, dmobj_IPersistStream_GetClassID, unimpl_IPersistStream_IsDirty, - IPersistStreamImpl_Load, + band_track_persist_stream_Load, unimpl_IPersistStream_Save, - unimpl_IPersistStream_GetSizeMax + unimpl_IPersistStream_GetSizeMax, }; /* for ClassFactory */ HRESULT create_dmbandtrack(REFIID lpcGUID, void **ppobj) { - IDirectMusicBandTrack *track; + struct band_track *track; HRESULT hr; *ppobj = NULL; if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; - track->IDirectMusicTrack8_iface.lpVtbl = &dmtrack8_vtbl; + track->IDirectMusicTrack8_iface.lpVtbl = &band_track_vtbl; track->ref = 1; - dmobject_init(&track->dmobj, &CLSID_DirectMusicBandTrack, - (IUnknown *)&track->IDirectMusicTrack8_iface); - track->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; + dmobject_init(&track->dmobj, &CLSID_DirectMusicBandTrack, (IUnknown *)&track->IDirectMusicTrack8_iface); + track->dmobj.IPersistStream_iface.lpVtbl = &band_track_persist_stream_vtbl; list_init (&track->Bands); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); From 737073a9f41c1c13caa8b07c3dd47f1f582d470e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 14:40:22 +0200 Subject: [PATCH 0909/1148] dmband: Avoid leaking bands on band track Release. (cherry picked from commit 7fb9afea1a15735ded07bbb65056218a4d0e40da) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/bandtrack.c | 45 ++++++++++++++++++++++++++++-------- dlls/dmband/dmband_private.h | 6 ----- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index 53515bc8690..1a3cde5f1db 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -22,13 +22,26 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmband); WINE_DECLARE_DEBUG_CHANNEL(dmfile); +struct band_entry +{ + struct list entry; + DMUS_PRIVATE_BAND_ITEM_HEADER head; + IDirectMusicBand *band; +}; + +static void band_entry_destroy(struct band_entry *entry) +{ + IDirectMusicTrack_Release(entry->band); + free(entry); +} + struct band_track { IDirectMusicTrack8 IDirectMusicTrack8_iface; struct dmobject dmobj; /* IPersistStream only */ LONG ref; DMUS_IO_BAND_TRACK_HEADER header; - struct list Bands; + struct list bands; }; static inline struct band_track *impl_from_IDirectMusicTrack8(IDirectMusicTrack8 *iface) @@ -76,7 +89,18 @@ static ULONG WINAPI band_track_Release(IDirectMusicTrack8 *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) free(This); + if (!ref) + { + struct band_entry *entry, *next; + + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &This->bands, struct band_entry, entry) + { + list_remove(&entry->entry); + band_entry_destroy(entry); + } + + free(This); + } return ref; } @@ -337,13 +361,14 @@ static HRESULT load_band(struct band_track *This, IStream *pClonedStream, /* * @TODO insert pBand into This */ - if (SUCCEEDED(hr)) { - LPDMUS_PRIVATE_BAND pNewBand; - if (!(pNewBand = calloc(1, sizeof(*pNewBand)))) return E_OUTOFMEMORY; - pNewBand->BandHeader = *pHeader; - pNewBand->band = *ppBand; - IDirectMusicBand_AddRef(*ppBand); - list_add_tail (&This->Bands, &pNewBand->entry); + if (SUCCEEDED(hr)) + { + struct band_entry *entry; + if (!(entry = calloc(1, sizeof(*entry)))) return E_OUTOFMEMORY; + entry->head = *pHeader; + entry->band = *ppBand; + IDirectMusicBand_AddRef(*ppBand); + list_add_tail(&This->bands, &entry->entry); } return S_OK; @@ -635,7 +660,7 @@ HRESULT create_dmbandtrack(REFIID lpcGUID, void **ppobj) track->ref = 1; dmobject_init(&track->dmobj, &CLSID_DirectMusicBandTrack, (IUnknown *)&track->IDirectMusicTrack8_iface); track->dmobj.IPersistStream_iface.lpVtbl = &band_track_persist_stream_vtbl; - list_init (&track->Bands); + list_init(&track->bands); hr = IDirectMusicTrack8_QueryInterface(&track->IDirectMusicTrack8_iface, lpcGUID, ppobj); IDirectMusicTrack8_Release(&track->IDirectMusicTrack8_iface); diff --git a/dlls/dmband/dmband_private.h b/dlls/dmband/dmband_private.h index 0b1807eaf93..f4d2bbbbc8e 100644 --- a/dlls/dmband/dmband_private.h +++ b/dlls/dmband/dmband_private.h @@ -67,12 +67,6 @@ typedef struct _DMUS_PRIVATE_INSTRUMENT { IDirectMusicCollection* ppReferenceCollection; } DMUS_PRIVATE_INSTRUMENT, *LPDMUS_PRIVATE_INSTRUMENT; -typedef struct _DMUS_PRIVATE_BAND { - struct list entry; /* for listing elements */ - DMUS_PRIVATE_BAND_ITEM_HEADER BandHeader; - IDirectMusicBand *band; -} DMUS_PRIVATE_BAND, *LPDMUS_PRIVATE_BAND; - /***************************************************************************** * Misc. */ From b8db784bd2e0efcf86742d0da0f07f851fa28af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 17:23:57 +0200 Subject: [PATCH 0910/1148] dmband: Avoid leaking collection on band release. (cherry picked from commit 9d390da9647db915744bacfda162f0ce7dbea9ae) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 40 ++++++++++++++++++++++++++++-------- dlls/dmband/dmband_private.h | 6 ------ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index bbf8c93a067..102e58fe39b 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -23,12 +23,25 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmband); WINE_DECLARE_DEBUG_CHANNEL(dmfile); +struct instrument_entry +{ + struct list entry; + DMUS_IO_INSTRUMENT instrument; + IDirectMusicCollection *collection; +}; + +static void instrument_entry_destroy(struct instrument_entry *entry) +{ + if (entry->collection) IDirectMusicCollection_Release(entry->collection); + free(entry); +} + struct band { IDirectMusicBand IDirectMusicBand_iface; struct dmobject dmobj; LONG ref; - struct list Instruments; + struct list instruments; }; static inline struct band *impl_from_IDirectMusicBand(IDirectMusicBand *iface) @@ -77,7 +90,18 @@ static ULONG WINAPI band_Release(IDirectMusicBand *iface) TRACE("(%p) ref=%ld\n", This, ref); - if (!ref) free(This); + if (!ref) + { + struct instrument_entry *entry, *next; + + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &This->instruments, struct instrument_entry, entry) + { + list_remove(&entry->entry); + instrument_entry_destroy(entry); + } + + free(This); + } return ref; } @@ -192,7 +216,7 @@ static HRESULT parse_instrument(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, HRESULT hr; DMUS_IO_INSTRUMENT inst; - LPDMUS_PRIVATE_INSTRUMENT pNewInstrument; + struct instrument_entry *pNewInstrument; IDirectMusicObject* pObject = NULL; if (pChunk->fccID != DMUS_FOURCC_INSTRUMENT_LIST) { @@ -272,8 +296,8 @@ static HRESULT parse_instrument(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, * @TODO insert pNewInstrument into This */ if (!(pNewInstrument = calloc(1, sizeof(*pNewInstrument)))) return E_OUTOFMEMORY; - memcpy(&pNewInstrument->pInstrument, &inst, sizeof(DMUS_IO_INSTRUMENT)); - pNewInstrument->ppReferenceCollection = NULL; + memcpy(&pNewInstrument->instrument, &inst, sizeof(DMUS_IO_INSTRUMENT)); + pNewInstrument->collection = NULL; if (NULL != pObject) { IDirectMusicCollection* pCol = NULL; hr = IDirectMusicObject_QueryInterface (pObject, &IID_IDirectMusicCollection, (void**) &pCol); @@ -282,10 +306,10 @@ static HRESULT parse_instrument(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, free(pNewInstrument); return hr; } - pNewInstrument->ppReferenceCollection = pCol; + pNewInstrument->collection = pCol; IDirectMusicObject_Release(pObject); } - list_add_tail (&This->Instruments, &pNewInstrument->entry); + list_add_tail(&This->instruments, &pNewInstrument->entry); return S_OK; } @@ -510,7 +534,7 @@ HRESULT create_dmband(REFIID lpcGUID, void **ppobj) dmobject_init(&obj->dmobj, &CLSID_DirectMusicBand, (IUnknown *)&obj->IDirectMusicBand_iface); obj->dmobj.IDirectMusicObject_iface.lpVtbl = &band_object_vtbl; obj->dmobj.IPersistStream_iface.lpVtbl = &band_persist_stream_vtbl; - list_init (&obj->Instruments); + list_init(&obj->instruments); hr = IDirectMusicBand_QueryInterface(&obj->IDirectMusicBand_iface, lpcGUID, ppobj); IDirectMusicBand_Release(&obj->IDirectMusicBand_iface); diff --git a/dlls/dmband/dmband_private.h b/dlls/dmband/dmband_private.h index f4d2bbbbc8e..da6f0fa302f 100644 --- a/dlls/dmband/dmband_private.h +++ b/dlls/dmband/dmband_private.h @@ -61,12 +61,6 @@ typedef struct _DMUS_PRIVATE_BAND_ITEM_HEADER { MUSIC_TIME lBandTimePhysical; } DMUS_PRIVATE_BAND_ITEM_HEADER; -typedef struct _DMUS_PRIVATE_INSTRUMENT { - struct list entry; /* for listing elements */ - DMUS_IO_INSTRUMENT pInstrument; - IDirectMusicCollection* ppReferenceCollection; -} DMUS_PRIVATE_INSTRUMENT, *LPDMUS_PRIVATE_INSTRUMENT; - /***************************************************************************** * Misc. */ From cd6d538dea54d417302583541297bc00578f5377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 17:24:30 +0200 Subject: [PATCH 0911/1148] dmband: Rewrite band lbin list parsing. (cherry picked from commit dfcb827318b17b2b166b523c8d655b981ab930d3) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 180 +++++++++++++++++---------------------------- 1 file changed, 69 insertions(+), 111 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index 102e58fe39b..fa6fd35580e 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -156,6 +156,70 @@ static const IDirectMusicBandVtbl band_vtbl = band_Unload, }; +static HRESULT parse_lbin_list(struct band *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + IDirectMusicCollection *collection = NULL; + struct instrument_entry *entry; + DMUS_IO_INSTRUMENT inst = {0}; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case DMUS_FOURCC_INSTRUMENT_CHUNK: + { + UINT size = sizeof(inst); + + if (chunk.size == offsetof(DMUS_IO_INSTRUMENT, nPitchBendRange)) size = chunk.size; + if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &inst, size))) break; + TRACE_(dmfile)(" - dwPatch: %lu\n", inst.dwPatch); + TRACE_(dmfile)(" - dwAssignPatch: %lu\n", inst.dwAssignPatch); + TRACE_(dmfile)(" - dwNoteRanges[0]: %lu\n", inst.dwNoteRanges[0]); + TRACE_(dmfile)(" - dwNoteRanges[1]: %lu\n", inst.dwNoteRanges[1]); + TRACE_(dmfile)(" - dwNoteRanges[2]: %lu\n", inst.dwNoteRanges[2]); + TRACE_(dmfile)(" - dwNoteRanges[3]: %lu\n", inst.dwNoteRanges[3]); + TRACE_(dmfile)(" - dwPChannel: %lu\n", inst.dwPChannel); + TRACE_(dmfile)(" - dwFlags: %lx\n", inst.dwFlags); + TRACE_(dmfile)(" - bPan: %u\n", inst.bPan); + TRACE_(dmfile)(" - bVolume: %u\n", inst.bVolume); + TRACE_(dmfile)(" - nTranspose: %d\n", inst.nTranspose); + TRACE_(dmfile)(" - dwChannelPriority: %lu\n", inst.dwChannelPriority); + TRACE_(dmfile)(" - nPitchBendRange: %d\n", inst.nPitchBendRange); + break; + } + + case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_REF_LIST): + { + IDirectMusicObject *object; + + if (FAILED(hr = dmobj_parsereference(stream, &chunk, &object))) break; + TRACE_(dmfile)(" - object: %p\n", object); + + hr = IDirectMusicObject_QueryInterface(object, &IID_IDirectMusicCollection, (void **)&collection); + IDirectMusicObject_Release(object); + break; + } + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + if (FAILED(hr)) return hr; + + if (!(entry = calloc(1, sizeof(*entry)))) return E_OUTOFMEMORY; + memcpy(&entry->instrument, &inst, sizeof(DMUS_IO_INSTRUMENT)); + entry->collection = collection; + list_add_tail(&This->instruments, &entry->entry); + + return hr; +} + static HRESULT WINAPI band_object_ParseDescriptor(IDirectMusicObject *iface, IStream *stream, DMUS_OBJECTDESC *desc) { @@ -205,115 +269,6 @@ static const IDirectMusicObjectVtbl band_object_vtbl = band_object_ParseDescriptor, }; -#define DMUS_IO_INSTRUMENT_DX7_SIZE offsetof(DMUS_IO_INSTRUMENT, nPitchBendRange) - -static HRESULT parse_instrument(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, - IStream *pStm) -{ - DMUS_PRIVATE_CHUNK Chunk; - DWORD ListSize[3], ListCount[3]; - LARGE_INTEGER liMove; /* used when skipping chunks */ - HRESULT hr; - - DMUS_IO_INSTRUMENT inst; - struct instrument_entry *pNewInstrument; - IDirectMusicObject* pObject = NULL; - - if (pChunk->fccID != DMUS_FOURCC_INSTRUMENT_LIST) { - ERR_(dmfile)(": %s chunk should be an INSTRUMENT list\n", debugstr_fourcc (pChunk->fccID)); - return E_FAIL; - } - - ListSize[0] = pChunk->dwSize - sizeof(FOURCC); - ListCount[0] = 0; - - do { - IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); - ListCount[0] += sizeof(FOURCC) + sizeof(DWORD) + Chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - switch (Chunk.fccID) { - case DMUS_FOURCC_INSTRUMENT_CHUNK: { - TRACE_(dmfile)(": Instrument chunk\n"); - if (Chunk.dwSize != sizeof(DMUS_IO_INSTRUMENT) && Chunk.dwSize != DMUS_IO_INSTRUMENT_DX7_SIZE) { - ERR_(dmfile)("unexpected size %ld\n", Chunk.dwSize); - return E_FAIL; - } - IStream_Read (pStm, &inst, Chunk.dwSize, NULL); - if (Chunk.dwSize != sizeof(DMUS_IO_INSTRUMENT)) - inst.nPitchBendRange = 0; - - TRACE_(dmfile)(" - dwPatch: %lu\n", inst.dwPatch); - TRACE_(dmfile)(" - dwAssignPatch: %lu\n", inst.dwAssignPatch); - TRACE_(dmfile)(" - dwNoteRanges[0]: %lu\n", inst.dwNoteRanges[0]); - TRACE_(dmfile)(" - dwNoteRanges[1]: %lu\n", inst.dwNoteRanges[1]); - TRACE_(dmfile)(" - dwNoteRanges[2]: %lu\n", inst.dwNoteRanges[2]); - TRACE_(dmfile)(" - dwNoteRanges[3]: %lu\n", inst.dwNoteRanges[3]); - TRACE_(dmfile)(" - dwPChannel: %lu\n", inst.dwPChannel); - TRACE_(dmfile)(" - dwFlags: %lx\n", inst.dwFlags); - TRACE_(dmfile)(" - bPan: %u\n", inst.bPan); - TRACE_(dmfile)(" - bVolume: %u\n", inst.bVolume); - TRACE_(dmfile)(" - nTranspose: %d\n", inst.nTranspose); - TRACE_(dmfile)(" - dwChannelPriority: %lu\n", inst.dwChannelPriority); - TRACE_(dmfile)(" - nPitchBendRange: %d\n", inst.nPitchBendRange); - break; - } - case FOURCC_LIST: { - IStream_Read (pStm, &Chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": LIST chunk of type %s", debugstr_fourcc(Chunk.fccID)); - ListSize[1] = Chunk.dwSize - sizeof(FOURCC); - ListCount[1] = 0; - switch (Chunk.fccID) { - case DMUS_FOURCC_REF_LIST: { - FIXME_(dmfile)(": DMRF (DM References) list\n"); - hr = IDirectMusicUtils_IPersistStream_ParseReference(&This->dmobj.IPersistStream_iface, - &Chunk, pStm, &pObject); - if (FAILED(hr)) { - ERR(": could not load Reference\n"); - return hr; - } - break; - } - default: { - TRACE_(dmfile)(": unknown (skipping)\n"); - liMove.QuadPart = Chunk.dwSize - sizeof(FOURCC); - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - TRACE_(dmfile)(": ListCount[0] = %ld < ListSize[0] = %ld\n", ListCount[0], ListSize[0]); - } while (ListCount[0] < ListSize[0]); - - /* - * @TODO insert pNewInstrument into This - */ - if (!(pNewInstrument = calloc(1, sizeof(*pNewInstrument)))) return E_OUTOFMEMORY; - memcpy(&pNewInstrument->instrument, &inst, sizeof(DMUS_IO_INSTRUMENT)); - pNewInstrument->collection = NULL; - if (NULL != pObject) { - IDirectMusicCollection* pCol = NULL; - hr = IDirectMusicObject_QueryInterface (pObject, &IID_IDirectMusicCollection, (void**) &pCol); - if (FAILED(hr)) { - ERR(": failed to get IDirectMusicCollection Interface from DMObject\n"); - free(pNewInstrument); - return hr; - } - pNewInstrument->collection = pCol; - IDirectMusicObject_Release(pObject); - } - list_add_tail(&This->instruments, &pNewInstrument->entry); - - return S_OK; -} - static HRESULT parse_instruments_list(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, IStream *pStm) { @@ -342,9 +297,12 @@ static HRESULT parse_instruments_list(struct band *This, DMUS_PRIVATE_CHUNK *pCh ListCount[1] = 0; switch (Chunk.fccID) { case DMUS_FOURCC_INSTRUMENT_LIST: { + static const LARGE_INTEGER zero = {0}; + struct chunk_entry chunk = {FOURCC_LIST, .size = Chunk.dwSize, .type = Chunk.fccID}; TRACE_(dmfile)(": Instrument list\n"); - hr = parse_instrument(This, &Chunk, pStm); - if (FAILED(hr)) return hr; + IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &chunk.offset); + chunk.offset.QuadPart -= 12; + if (FAILED(hr = parse_lbin_list(This, pStm, &chunk))) return hr; break; } default: { From eb2e46eb41bacfccbe10524fa676c4836c48b28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 18:14:13 +0200 Subject: [PATCH 0912/1148] dmband: Rewrite band lbil list parsing. (cherry picked from commit ba69ffeff4b63a91e9b4116523945a811257507f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 88 +++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 59 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index fa6fd35580e..6ed31075f42 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -220,6 +220,30 @@ static HRESULT parse_lbin_list(struct band *This, IStream *stream, struct chunk_ return hr; } +static HRESULT parse_lbil_list(struct band *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_INSTRUMENT_LIST): + hr = parse_lbin_list(This, stream, &chunk); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + return hr; +} + static HRESULT WINAPI band_object_ParseDescriptor(IDirectMusicObject *iface, IStream *stream, DMUS_OBJECTDESC *desc) { @@ -269,64 +293,6 @@ static const IDirectMusicObjectVtbl band_object_vtbl = band_object_ParseDescriptor, }; -static HRESULT parse_instruments_list(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, - IStream *pStm) -{ - HRESULT hr; - DMUS_PRIVATE_CHUNK Chunk; - DWORD ListSize[3], ListCount[3]; - LARGE_INTEGER liMove; /* used when skipping chunks */ - - if (pChunk->fccID != DMUS_FOURCC_INSTRUMENTS_LIST) { - ERR_(dmfile)(": %s chunk should be an INSTRUMENTS list\n", debugstr_fourcc (pChunk->fccID)); - return E_FAIL; - } - - ListSize[0] = pChunk->dwSize - sizeof(FOURCC); - ListCount[0] = 0; - - do { - IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); - ListCount[0] += sizeof(FOURCC) + sizeof(DWORD) + Chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - switch (Chunk.fccID) { - case FOURCC_LIST: { - IStream_Read (pStm, &Chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": LIST chunk of type %s", debugstr_fourcc(Chunk.fccID)); - ListSize[1] = Chunk.dwSize - sizeof(FOURCC); - ListCount[1] = 0; - switch (Chunk.fccID) { - case DMUS_FOURCC_INSTRUMENT_LIST: { - static const LARGE_INTEGER zero = {0}; - struct chunk_entry chunk = {FOURCC_LIST, .size = Chunk.dwSize, .type = Chunk.fccID}; - TRACE_(dmfile)(": Instrument list\n"); - IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &chunk.offset); - chunk.offset.QuadPart -= 12; - if (FAILED(hr = parse_lbin_list(This, pStm, &chunk))) return hr; - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = ListSize[1]; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - TRACE_(dmfile)(": ListCount[0] = %ld < ListSize[0] = %ld\n", ListCount[0], ListSize[0]); - } while (ListCount[0] < ListSize[0]); - - return S_OK; -} - static HRESULT parse_band_form(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, IStream *pStm) { @@ -392,8 +358,12 @@ static HRESULT parse_band_form(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, break; } case DMUS_FOURCC_INSTRUMENTS_LIST: { + static const LARGE_INTEGER zero = {0}; + struct chunk_entry chunk = {FOURCC_LIST, .size = Chunk.dwSize, .type = Chunk.fccID}; TRACE_(dmfile)(": INSTRUMENTS list\n"); - hr = parse_instruments_list(This, &Chunk, pStm); + IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &chunk.offset); + chunk.offset.QuadPart -= 12; + hr = parse_lbil_list(This, pStm, &chunk); if (FAILED(hr)) return hr; break; } From 5890feb58907716e3b8cc11bf02f86e814c51455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 18:14:41 +0200 Subject: [PATCH 0913/1148] dmband: Rewrite band dmbd chunk parsing. (cherry picked from commit b2f1e97813a509221a3a4caa931393f899d1f0c4) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 138 +++++++++++++-------------------------------- 1 file changed, 40 insertions(+), 98 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index 6ed31075f42..b732595f19d 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -244,6 +244,41 @@ static HRESULT parse_lbil_list(struct band *This, IStream *stream, struct chunk_ return hr; } +static HRESULT parse_dmbd_chunk(struct band *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; + + if (FAILED(hr = dmobj_parsedescriptor(stream, parent, &This->dmobj.desc, + DMUS_OBJ_OBJECT|DMUS_OBJ_NAME|DMUS_OBJ_NAME_INAM|DMUS_OBJ_CATEGORY|DMUS_OBJ_VERSION)) + || FAILED(hr = stream_reset_chunk_data(stream, parent))) + return hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case DMUS_FOURCC_GUID_CHUNK: + case DMUS_FOURCC_VERSION_CHUNK: + case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_UNFO_LIST): + /* already parsed by dmobj_parsedescriptor */ + break; + + case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_INSTRUMENTS_LIST): + hr = parse_lbil_list(This, stream, &chunk); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + return hr; +} + static HRESULT WINAPI band_object_ParseDescriptor(IDirectMusicObject *iface, IStream *stream, DMUS_OBJECTDESC *desc) { @@ -293,103 +328,6 @@ static const IDirectMusicObjectVtbl band_object_vtbl = band_object_ParseDescriptor, }; -static HRESULT parse_band_form(struct band *This, DMUS_PRIVATE_CHUNK *pChunk, - IStream *pStm) -{ - HRESULT hr = E_FAIL; - DMUS_PRIVATE_CHUNK Chunk; - DWORD StreamSize, StreamCount, ListSize[3], ListCount[3]; - LARGE_INTEGER liMove; /* used when skipping chunks */ - - GUID tmp_guid; - - if (pChunk->fccID != DMUS_FOURCC_BAND_FORM) { - ERR_(dmfile)(": %s chunk should be a BAND form\n", debugstr_fourcc (pChunk->fccID)); - return E_FAIL; - } - - StreamSize = pChunk->dwSize - sizeof(FOURCC); - StreamCount = 0; - - do { - IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); - StreamCount += sizeof(FOURCC) + sizeof(DWORD) + Chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - - hr = IDirectMusicUtils_IPersistStream_ParseDescGeneric(&Chunk, pStm, &This->dmobj.desc); - if (FAILED(hr)) return hr; - - if (hr == S_FALSE) { - switch (Chunk.fccID) { - case DMUS_FOURCC_GUID_CHUNK: { - TRACE_(dmfile)(": GUID\n"); - IStream_Read (pStm, &tmp_guid, sizeof(GUID), NULL); - TRACE_(dmfile)(" - guid: %s\n", debugstr_dmguid(&tmp_guid)); - break; - } - case FOURCC_LIST: { - IStream_Read (pStm, &Chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": LIST chunk of type %s", debugstr_fourcc(Chunk.fccID)); - ListSize[0] = Chunk.dwSize - sizeof(FOURCC); - ListCount[0] = 0; - switch (Chunk.fccID) { - case DMUS_FOURCC_UNFO_LIST: { - TRACE_(dmfile)(": UNFO list\n"); - do { - IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); - ListCount[0] += sizeof(FOURCC) + sizeof(DWORD) + Chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - - hr = IDirectMusicUtils_IPersistStream_ParseUNFOGeneric(&Chunk, pStm, &This->dmobj.desc); - if (FAILED(hr)) return hr; - - if (hr == S_FALSE) { - switch (Chunk.fccID) { - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - } - TRACE_(dmfile)(": ListCount[0] = %ld < ListSize[0] = %ld\n", ListCount[0], ListSize[0]); - } while (ListCount[0] < ListSize[0]); - break; - } - case DMUS_FOURCC_INSTRUMENTS_LIST: { - static const LARGE_INTEGER zero = {0}; - struct chunk_entry chunk = {FOURCC_LIST, .size = Chunk.dwSize, .type = Chunk.fccID}; - TRACE_(dmfile)(": INSTRUMENTS list\n"); - IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &chunk.offset); - chunk.offset.QuadPart -= 12; - hr = parse_lbil_list(This, pStm, &chunk); - if (FAILED(hr)) return hr; - break; - } - default: { - TRACE_(dmfile)(": unknown (skipping)\n"); - liMove.QuadPart = Chunk.dwSize - sizeof(FOURCC); - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - } - TRACE_(dmfile)(": StreamCount[0] = %ld < StreamSize[0] = %ld\n", StreamCount, StreamSize); - } while (StreamCount < StreamSize); - - return S_OK; -} - static inline struct band *impl_from_IPersistStream(IPersistStream *iface) { return CONTAINING_RECORD(iface, struct band, dmobj.IPersistStream_iface); @@ -412,8 +350,12 @@ static HRESULT WINAPI band_persist_stream_Load(IPersistStream *iface, IStream *p TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); switch (Chunk.fccID) { case DMUS_FOURCC_BAND_FORM: { + static const LARGE_INTEGER zero = {0}; + struct chunk_entry chunk = {FOURCC_LIST, .size = Chunk.dwSize, .type = Chunk.fccID}; TRACE_(dmfile)(": Band form\n"); - hr = parse_band_form(This, &Chunk, pStm); + IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &chunk.offset); + chunk.offset.QuadPart -= 12; + hr = parse_dmbd_chunk(This, pStm, &chunk); if (FAILED(hr)) return hr; break; } From b6da1f9fc62770ab557143ab275f957a49cc6529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 17:37:39 +0200 Subject: [PATCH 0914/1148] dmband: Rewrite band IPersistStream_Load. (cherry picked from commit 94c1dd8a6e69425319bb3145c592e8e5846cd9e9) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 117 ++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 60 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index b732595f19d..d91775681bb 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -21,7 +21,24 @@ #include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmband); -WINE_DECLARE_DEBUG_CHANNEL(dmfile); + +void dump_DMUS_IO_INSTRUMENT(DMUS_IO_INSTRUMENT *inst) +{ + TRACE("DMUS_IO_INSTRUMENT: %p\n", inst); + TRACE(" - dwPatch: %lu\n", inst->dwPatch); + TRACE(" - dwAssignPatch: %lu\n", inst->dwAssignPatch); + TRACE(" - dwNoteRanges[0]: %lu\n", inst->dwNoteRanges[0]); + TRACE(" - dwNoteRanges[1]: %lu\n", inst->dwNoteRanges[1]); + TRACE(" - dwNoteRanges[2]: %lu\n", inst->dwNoteRanges[2]); + TRACE(" - dwNoteRanges[3]: %lu\n", inst->dwNoteRanges[3]); + TRACE(" - dwPChannel: %lu\n", inst->dwPChannel); + TRACE(" - dwFlags: %lx\n", inst->dwFlags); + TRACE(" - bPan: %u\n", inst->bPan); + TRACE(" - bVolume: %u\n", inst->bVolume); + TRACE(" - nTranspose: %d\n", inst->nTranspose); + TRACE(" - dwChannelPriority: %lu\n", inst->dwChannelPriority); + TRACE(" - nPitchBendRange: %d\n", inst->nPitchBendRange); +} struct instrument_entry { @@ -171,32 +188,15 @@ static HRESULT parse_lbin_list(struct band *This, IStream *stream, struct chunk_ case DMUS_FOURCC_INSTRUMENT_CHUNK: { UINT size = sizeof(inst); - if (chunk.size == offsetof(DMUS_IO_INSTRUMENT, nPitchBendRange)) size = chunk.size; if (FAILED(hr = stream_chunk_get_data(stream, &chunk, &inst, size))) break; - TRACE_(dmfile)(" - dwPatch: %lu\n", inst.dwPatch); - TRACE_(dmfile)(" - dwAssignPatch: %lu\n", inst.dwAssignPatch); - TRACE_(dmfile)(" - dwNoteRanges[0]: %lu\n", inst.dwNoteRanges[0]); - TRACE_(dmfile)(" - dwNoteRanges[1]: %lu\n", inst.dwNoteRanges[1]); - TRACE_(dmfile)(" - dwNoteRanges[2]: %lu\n", inst.dwNoteRanges[2]); - TRACE_(dmfile)(" - dwNoteRanges[3]: %lu\n", inst.dwNoteRanges[3]); - TRACE_(dmfile)(" - dwPChannel: %lu\n", inst.dwPChannel); - TRACE_(dmfile)(" - dwFlags: %lx\n", inst.dwFlags); - TRACE_(dmfile)(" - bPan: %u\n", inst.bPan); - TRACE_(dmfile)(" - bVolume: %u\n", inst.bVolume); - TRACE_(dmfile)(" - nTranspose: %d\n", inst.nTranspose); - TRACE_(dmfile)(" - dwChannelPriority: %lu\n", inst.dwChannelPriority); - TRACE_(dmfile)(" - nPitchBendRange: %d\n", inst.nPitchBendRange); break; } case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_REF_LIST): { IDirectMusicObject *object; - if (FAILED(hr = dmobj_parsereference(stream, &chunk, &object))) break; - TRACE_(dmfile)(" - object: %p\n", object); - hr = IDirectMusicObject_QueryInterface(object, &IID_IDirectMusicCollection, (void **)&collection); IDirectMusicObject_Release(object); break; @@ -333,51 +333,48 @@ static inline struct band *impl_from_IPersistStream(IPersistStream *iface) return CONTAINING_RECORD(iface, struct band, dmobj.IPersistStream_iface); } -static HRESULT WINAPI band_persist_stream_Load(IPersistStream *iface, IStream *pStm) +static HRESULT WINAPI band_persist_stream_Load(IPersistStream *iface, IStream *stream) { - struct band *This = impl_from_IPersistStream(iface); - DMUS_PRIVATE_CHUNK Chunk; - LARGE_INTEGER liMove; - HRESULT hr; - - TRACE("(%p,%p): loading\n", This, pStm); - - IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - switch (Chunk.fccID) { - case FOURCC_RIFF: { - IStream_Read (pStm, &Chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - switch (Chunk.fccID) { - case DMUS_FOURCC_BAND_FORM: { - static const LARGE_INTEGER zero = {0}; - struct chunk_entry chunk = {FOURCC_LIST, .size = Chunk.dwSize, .type = Chunk.fccID}; - TRACE_(dmfile)(": Band form\n"); - IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &chunk.offset); - chunk.offset.QuadPart -= 12; - hr = parse_dmbd_chunk(This, pStm, &chunk); - if (FAILED(hr)) return hr; - break; - } - default: { - TRACE_(dmfile)(": unexpected chunk; loading failed)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - return E_FAIL; + struct band *This = impl_from_IPersistStream(iface); + struct chunk_entry chunk = {0}; + HRESULT hr; + + TRACE("%p, %p\n", iface, stream); + + if ((hr = stream_get_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_RIFF, DMUS_FOURCC_BAND_FORM): + hr = parse_dmbd_chunk(This, stream, &chunk); + break; + + default: + WARN("Invalid band chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + hr = DMUS_E_UNSUPPORTED_STREAM; + break; + } } + + if (FAILED(hr)) return hr; + + if (TRACE_ON(dmband)) + { + struct instrument_entry *entry; + + TRACE("Loaded IDirectMusicBand %p\n", This); + dump_DMUS_OBJECTDESC(&This->dmobj.desc); + + TRACE(" - Instruments:\n"); + LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry) + { + dump_DMUS_IO_INSTRUMENT(&entry->instrument); + TRACE(" - collection: %p\n", entry->collection); + } } - TRACE_(dmfile)(": reading finished\n"); - break; - } - default: { - TRACE_(dmfile)(": unexpected chunk; loading failed)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); /* skip the rest of the chunk */ - return E_FAIL; - } - } - - return S_OK; + + stream_skip_chunk(stream, &chunk); + return S_OK; } static const IPersistStreamVtbl band_persist_stream_vtbl = From 8f6652107f86aca51be8010ee2c002c50bdf3947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 13:47:50 +0200 Subject: [PATCH 0915/1148] dmime: Rename IDirectMusicSegment8Impl method prefix to segment. (cherry picked from commit 3ff263deba21e043360546ce7c0c0feb95558e0c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 199 +++++++++++++++++++------------------------ 1 file changed, 88 insertions(+), 111 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 39a5333fbbb..f86e4a2bb5f 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -39,15 +39,14 @@ typedef struct IDirectMusicSegment8Impl { int data_size; } IDirectMusicSegment8Impl; -IDirectMusicSegment8Impl *create_segment(void); +static IDirectMusicSegment8Impl *segment_create(void); static inline IDirectMusicSegment8Impl *impl_from_IDirectMusicSegment8(IDirectMusicSegment8 *iface) { return CONTAINING_RECORD(iface, IDirectMusicSegment8Impl, IDirectMusicSegment8_iface); } -static HRESULT WINAPI IDirectMusicSegment8Impl_QueryInterface(IDirectMusicSegment8 *iface, - REFIID riid, void **ret_iface) +static HRESULT WINAPI segment_QueryInterface(IDirectMusicSegment8 *iface, REFIID riid, void **ret_iface) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -72,7 +71,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_QueryInterface(IDirectMusicSegmen return S_OK; } -static ULONG WINAPI IDirectMusicSegment8Impl_AddRef(IDirectMusicSegment8 *iface) +static ULONG WINAPI segment_AddRef(IDirectMusicSegment8 *iface) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); LONG ref = InterlockedIncrement(&This->ref); @@ -82,7 +81,7 @@ static ULONG WINAPI IDirectMusicSegment8Impl_AddRef(IDirectMusicSegment8 *iface) return ref; } -static ULONG WINAPI IDirectMusicSegment8Impl_Release(IDirectMusicSegment8 *iface) +static ULONG WINAPI segment_Release(IDirectMusicSegment8 *iface) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); LONG ref = InterlockedDecrement(&This->ref); @@ -99,8 +98,7 @@ static ULONG WINAPI IDirectMusicSegment8Impl_Release(IDirectMusicSegment8 *iface return ref; } -static HRESULT WINAPI IDirectMusicSegment8Impl_GetLength(IDirectMusicSegment8 *iface, - MUSIC_TIME *pmtLength) +static HRESULT WINAPI segment_GetLength(IDirectMusicSegment8 *iface, MUSIC_TIME *pmtLength) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -112,8 +110,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_GetLength(IDirectMusicSegment8 *i return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_SetLength(IDirectMusicSegment8 *iface, - MUSIC_TIME mtLength) +static HRESULT WINAPI segment_SetLength(IDirectMusicSegment8 *iface, MUSIC_TIME mtLength) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -122,8 +119,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_SetLength(IDirectMusicSegment8 *i return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_GetRepeats(IDirectMusicSegment8 *iface, - DWORD *pdwRepeats) +static HRESULT WINAPI segment_GetRepeats(IDirectMusicSegment8 *iface, DWORD *pdwRepeats) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -135,8 +131,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_GetRepeats(IDirectMusicSegment8 * return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_SetRepeats(IDirectMusicSegment8 *iface, - DWORD dwRepeats) +static HRESULT WINAPI segment_SetRepeats(IDirectMusicSegment8 *iface, DWORD dwRepeats) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -145,8 +140,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_SetRepeats(IDirectMusicSegment8 * return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_GetDefaultResolution(IDirectMusicSegment8 *iface, - DWORD *pdwResolution) +static HRESULT WINAPI segment_GetDefaultResolution(IDirectMusicSegment8 *iface, DWORD *pdwResolution) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -158,8 +152,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_GetDefaultResolution(IDirectMusic return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_SetDefaultResolution(IDirectMusicSegment8 *iface, - DWORD dwResolution) +static HRESULT WINAPI segment_SetDefaultResolution(IDirectMusicSegment8 *iface, DWORD dwResolution) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -168,8 +161,8 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_SetDefaultResolution(IDirectMusic return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_GetTrack(IDirectMusicSegment8 *iface, - REFGUID rguidType, DWORD dwGroupBits, DWORD dwIndex, IDirectMusicTrack **ppTrack) +static HRESULT WINAPI segment_GetTrack(IDirectMusicSegment8 *iface, REFGUID rguidType, + DWORD dwGroupBits, DWORD dwIndex, IDirectMusicTrack **ppTrack) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); CLSID pIt_clsid; @@ -217,8 +210,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_GetTrack(IDirectMusicSegment8 *if return DMUS_E_NOT_FOUND; } -static HRESULT WINAPI IDirectMusicSegment8Impl_GetTrackGroup(IDirectMusicSegment8 *iface, - IDirectMusicTrack *pTrack, DWORD *pdwGroupBits) +static HRESULT WINAPI segment_GetTrackGroup(IDirectMusicSegment8 *iface, IDirectMusicTrack *pTrack, DWORD *pdwGroupBits) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); struct list* pEntry = NULL; @@ -242,8 +234,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_GetTrackGroup(IDirectMusicSegment return DMUS_E_NOT_FOUND; } -static HRESULT WINAPI IDirectMusicSegment8Impl_InsertTrack(IDirectMusicSegment8 *iface, - IDirectMusicTrack *pTrack, DWORD group) +static HRESULT WINAPI segment_InsertTrack(IDirectMusicSegment8 *iface, IDirectMusicTrack *pTrack, DWORD group) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); DWORD i = 0; @@ -277,8 +268,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_InsertTrack(IDirectMusicSegment8 return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_RemoveTrack(IDirectMusicSegment8 *iface, - IDirectMusicTrack *pTrack) +static HRESULT WINAPI segment_RemoveTrack(IDirectMusicSegment8 *iface, IDirectMusicTrack *pTrack) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); struct list* pEntry = NULL; @@ -303,7 +293,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_RemoveTrack(IDirectMusicSegment8 return S_FALSE; } -static HRESULT WINAPI IDirectMusicSegment8Impl_InitPlay(IDirectMusicSegment8 *iface, +static HRESULT WINAPI segment_InitPlay(IDirectMusicSegment8 *iface, IDirectMusicSegmentState **ppSegState, IDirectMusicPerformance *pPerformance, DWORD dwFlags) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -321,8 +311,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_InitPlay(IDirectMusicSegment8 *if return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_GetGraph(IDirectMusicSegment8 *iface, - IDirectMusicGraph **ppGraph) +static HRESULT WINAPI segment_GetGraph(IDirectMusicSegment8 *iface, IDirectMusicGraph **ppGraph) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -342,8 +331,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_GetGraph(IDirectMusicSegment8 *if return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_SetGraph(IDirectMusicSegment8 *iface, - IDirectMusicGraph *pGraph) +static HRESULT WINAPI segment_SetGraph(IDirectMusicSegment8 *iface, IDirectMusicGraph *pGraph) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -358,24 +346,22 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_SetGraph(IDirectMusicSegment8 *if return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_AddNotificationType(IDirectMusicSegment8 *iface, - REFGUID rguidNotificationType) +static HRESULT WINAPI segment_AddNotificationType(IDirectMusicSegment8 *iface, REFGUID rguidNotificationType) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); FIXME("(%p, %s): stub\n", This, debugstr_dmguid(rguidNotificationType)); return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_RemoveNotificationType(IDirectMusicSegment8 *iface, - REFGUID rguidNotificationType) +static HRESULT WINAPI segment_RemoveNotificationType(IDirectMusicSegment8 *iface, REFGUID rguidNotificationType) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); FIXME("(%p, %s): stub\n", This, debugstr_dmguid(rguidNotificationType)); return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_GetParam(IDirectMusicSegment8 *iface, REFGUID type, - DWORD group, DWORD index, MUSIC_TIME time, MUSIC_TIME *next, void *param) +static HRESULT WINAPI segment_GetParam(IDirectMusicSegment8 *iface, REFGUID type, DWORD group, + DWORD index, MUSIC_TIME time, MUSIC_TIME *next, void *param) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); IDirectMusicTrack *track; @@ -390,8 +376,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_GetParam(IDirectMusicSegment8 *if /* Index is relative to the search pattern: group bits and supported param type */ for (i = 0, count = 0; i < DMUS_SEG_ANYTRACK && count <= index; i++) { - if (FAILED(IDirectMusicSegment8Impl_GetTrack(iface, &GUID_NULL, group, i, &track))) - break; + if (FAILED(segment_GetTrack(iface, &GUID_NULL, group, i, &track))) break; if (FAILED(IDirectMusicTrack_IsParamSupported(track, type))) continue; if (index == count || index == DMUS_SEG_ANYTRACK) @@ -408,15 +393,15 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_GetParam(IDirectMusicSegment8 *if return hr; } -static HRESULT WINAPI IDirectMusicSegment8Impl_SetParam(IDirectMusicSegment8 *iface, - REFGUID rguidType, DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, void *pParam) +static HRESULT WINAPI segment_SetParam(IDirectMusicSegment8 *iface, REFGUID rguidType, + DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, void *pParam) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); FIXME("(%p, %s, %#lx, %ld, %ld, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, mtTime, pParam); return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_Clone(IDirectMusicSegment8 *iface, MUSIC_TIME start, MUSIC_TIME end, +static HRESULT WINAPI segment_Clone(IDirectMusicSegment8 *iface, MUSIC_TIME start, MUSIC_TIME end, IDirectMusicSegment **segment) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -431,7 +416,8 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_Clone(IDirectMusicSegment8 *iface if (!segment) return E_POINTER; - if (!(clone = create_segment())) { + if (!(clone = segment_create())) + { *segment = NULL; return E_OUTOFMEMORY; } @@ -465,8 +451,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_Clone(IDirectMusicSegment8 *iface return track_clone_fail ? S_FALSE : S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_SetStartPoint(IDirectMusicSegment8 *iface, - MUSIC_TIME mtStart) +static HRESULT WINAPI segment_SetStartPoint(IDirectMusicSegment8 *iface, MUSIC_TIME mtStart) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -478,8 +463,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_SetStartPoint(IDirectMusicSegment return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_GetStartPoint(IDirectMusicSegment8 *iface, - MUSIC_TIME *pmtStart) +static HRESULT WINAPI segment_GetStartPoint(IDirectMusicSegment8 *iface, MUSIC_TIME *pmtStart) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -491,8 +475,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_GetStartPoint(IDirectMusicSegment return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_SetLoopPoints(IDirectMusicSegment8 *iface, - MUSIC_TIME start, MUSIC_TIME end) +static HRESULT WINAPI segment_SetLoopPoints(IDirectMusicSegment8 *iface, MUSIC_TIME start, MUSIC_TIME end) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -508,8 +491,7 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_SetLoopPoints(IDirectMusicSegment return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_GetLoopPoints(IDirectMusicSegment8 *iface, - MUSIC_TIME *pmtStart, MUSIC_TIME *pmtEnd) +static HRESULT WINAPI segment_GetLoopPoints(IDirectMusicSegment8 *iface, MUSIC_TIME *pmtStart, MUSIC_TIME *pmtEnd) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); @@ -522,93 +504,86 @@ static HRESULT WINAPI IDirectMusicSegment8Impl_GetLoopPoints(IDirectMusicSegment return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_SetPChannelsUsed(IDirectMusicSegment8 *iface, - DWORD dwNumPChannels, DWORD *paPChannels) +static HRESULT WINAPI segment_SetPChannelsUsed(IDirectMusicSegment8 *iface, DWORD dwNumPChannels, DWORD *paPChannels) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); FIXME("(%p, %ld, %p): stub\n", This, dwNumPChannels, paPChannels); return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_SetTrackConfig(IDirectMusicSegment8 *iface, - REFGUID rguidTrackClassID, DWORD dwGroupBits, DWORD dwIndex, DWORD dwFlagsOn, - DWORD dwFlagsOff) +static HRESULT WINAPI segment_SetTrackConfig(IDirectMusicSegment8 *iface, REFGUID rguidTrackClassID, + DWORD dwGroupBits, DWORD dwIndex, DWORD dwFlagsOn, DWORD dwFlagsOff) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); FIXME("(%p, %s, %#lx, %ld, %ld, %ld): stub\n", This, debugstr_dmguid(rguidTrackClassID), dwGroupBits, dwIndex, dwFlagsOn, dwFlagsOff); return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_GetAudioPathConfig(IDirectMusicSegment8 *iface, - IUnknown **ppAudioPathConfig) +static HRESULT WINAPI segment_GetAudioPathConfig(IDirectMusicSegment8 *iface, IUnknown **ppAudioPathConfig) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); FIXME("(%p, %p): stub\n", This, ppAudioPathConfig); return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_Compose(IDirectMusicSegment8 *iface, - MUSIC_TIME mtTime, IDirectMusicSegment *pFromSegment, IDirectMusicSegment *pToSegment, - IDirectMusicSegment **ppComposedSegment) +static HRESULT WINAPI segment_Compose(IDirectMusicSegment8 *iface, MUSIC_TIME mtTime, + IDirectMusicSegment *pFromSegment, IDirectMusicSegment *pToSegment, IDirectMusicSegment **ppComposedSegment) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); FIXME("(%p, %ld, %p, %p, %p): stub\n", This, mtTime, pFromSegment, pToSegment, ppComposedSegment); return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_Download(IDirectMusicSegment8 *iface, - IUnknown *pAudioPath) +static HRESULT WINAPI segment_Download(IDirectMusicSegment8 *iface, IUnknown *pAudioPath) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); FIXME("(%p, %p): stub\n", This, pAudioPath); return S_OK; } -static HRESULT WINAPI IDirectMusicSegment8Impl_Unload(IDirectMusicSegment8 *iface, - IUnknown *pAudioPath) +static HRESULT WINAPI segment_Unload(IDirectMusicSegment8 *iface, IUnknown *pAudioPath) { IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); FIXME("(%p, %p): stub\n", This, pAudioPath); return S_OK; } -static const IDirectMusicSegment8Vtbl dmsegment8_vtbl = { - IDirectMusicSegment8Impl_QueryInterface, - IDirectMusicSegment8Impl_AddRef, - IDirectMusicSegment8Impl_Release, - IDirectMusicSegment8Impl_GetLength, - IDirectMusicSegment8Impl_SetLength, - IDirectMusicSegment8Impl_GetRepeats, - IDirectMusicSegment8Impl_SetRepeats, - IDirectMusicSegment8Impl_GetDefaultResolution, - IDirectMusicSegment8Impl_SetDefaultResolution, - IDirectMusicSegment8Impl_GetTrack, - IDirectMusicSegment8Impl_GetTrackGroup, - IDirectMusicSegment8Impl_InsertTrack, - IDirectMusicSegment8Impl_RemoveTrack, - IDirectMusicSegment8Impl_InitPlay, - IDirectMusicSegment8Impl_GetGraph, - IDirectMusicSegment8Impl_SetGraph, - IDirectMusicSegment8Impl_AddNotificationType, - IDirectMusicSegment8Impl_RemoveNotificationType, - IDirectMusicSegment8Impl_GetParam, - IDirectMusicSegment8Impl_SetParam, - IDirectMusicSegment8Impl_Clone, - IDirectMusicSegment8Impl_SetStartPoint, - IDirectMusicSegment8Impl_GetStartPoint, - IDirectMusicSegment8Impl_SetLoopPoints, - IDirectMusicSegment8Impl_GetLoopPoints, - IDirectMusicSegment8Impl_SetPChannelsUsed, - IDirectMusicSegment8Impl_SetTrackConfig, - IDirectMusicSegment8Impl_GetAudioPathConfig, - IDirectMusicSegment8Impl_Compose, - IDirectMusicSegment8Impl_Download, - IDirectMusicSegment8Impl_Unload +static const IDirectMusicSegment8Vtbl segment_vtbl = +{ + segment_QueryInterface, + segment_AddRef, + segment_Release, + segment_GetLength, + segment_SetLength, + segment_GetRepeats, + segment_SetRepeats, + segment_GetDefaultResolution, + segment_SetDefaultResolution, + segment_GetTrack, + segment_GetTrackGroup, + segment_InsertTrack, + segment_RemoveTrack, + segment_InitPlay, + segment_GetGraph, + segment_SetGraph, + segment_AddNotificationType, + segment_RemoveNotificationType, + segment_GetParam, + segment_SetParam, + segment_Clone, + segment_SetStartPoint, + segment_GetStartPoint, + segment_SetLoopPoints, + segment_GetLoopPoints, + segment_SetPChannelsUsed, + segment_SetTrackConfig, + segment_GetAudioPathConfig, + segment_Compose, + segment_Download, + segment_Unload, }; -/* IDirectMusicSegment8Impl IDirectMusicObject part: */ -static HRESULT WINAPI seg_IDirectMusicObject_ParseDescriptor(IDirectMusicObject *iface, - IStream *stream, DMUS_OBJECTDESC *desc) +static HRESULT WINAPI segment_object_ParseDescriptor(IDirectMusicObject *iface, IStream *stream, DMUS_OBJECTDESC *desc) { struct chunk_entry riff = {0}; DWORD supported = DMUS_OBJ_OBJECT | DMUS_OBJ_VERSION; @@ -643,16 +618,16 @@ static HRESULT WINAPI seg_IDirectMusicObject_ParseDescriptor(IDirectMusicObject return S_OK; } -static const IDirectMusicObjectVtbl dmobject_vtbl = { +static const IDirectMusicObjectVtbl segment_object_vtbl = +{ dmobj_IDirectMusicObject_QueryInterface, dmobj_IDirectMusicObject_AddRef, dmobj_IDirectMusicObject_Release, dmobj_IDirectMusicObject_GetDescriptor, dmobj_IDirectMusicObject_SetDescriptor, - seg_IDirectMusicObject_ParseDescriptor + segment_object_ParseDescriptor, }; -/* IDirectMusicSegment8Impl IPersistStream part: */ static HRESULT parse_track_form(IDirectMusicSegment8Impl *This, IStream *stream, const struct chunk_entry *riff) { @@ -871,7 +846,7 @@ static HRESULT parse_wave_form(IDirectMusicSegment8Impl *This, IStream *stream, return SUCCEEDED(hr) ? S_OK : hr; } -static HRESULT WINAPI seg_IPersistStream_Load(IPersistStream *iface, IStream *stream) +static HRESULT WINAPI segment_persist_stream_Load(IPersistStream *iface, IStream *stream) { IDirectMusicSegment8Impl *This = impl_from_IPersistStream(iface); struct chunk_entry riff = {0}; @@ -910,28 +885,29 @@ static HRESULT WINAPI seg_IPersistStream_Load(IPersistStream *iface, IStream *st return hr; } -static const IPersistStreamVtbl persiststream_vtbl = { +static const IPersistStreamVtbl segment_persist_stream_vtbl = +{ dmobj_IPersistStream_QueryInterface, dmobj_IPersistStream_AddRef, dmobj_IPersistStream_Release, dmobj_IPersistStream_GetClassID, unimpl_IPersistStream_IsDirty, - seg_IPersistStream_Load, + segment_persist_stream_Load, unimpl_IPersistStream_Save, - unimpl_IPersistStream_GetSizeMax + unimpl_IPersistStream_GetSizeMax, }; -IDirectMusicSegment8Impl *create_segment(void) +IDirectMusicSegment8Impl *segment_create(void) { IDirectMusicSegment8Impl *obj; if (!(obj = calloc(1, sizeof(*obj)))) return NULL; - obj->IDirectMusicSegment8_iface.lpVtbl = &dmsegment8_vtbl; + obj->IDirectMusicSegment8_iface.lpVtbl = &segment_vtbl; obj->ref = 1; dmobject_init(&obj->dmobj, &CLSID_DirectMusicSegment, (IUnknown *)&obj->IDirectMusicSegment8_iface); - obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; - obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; + obj->dmobj.IDirectMusicObject_iface.lpVtbl = &segment_object_vtbl; + obj->dmobj.IPersistStream_iface.lpVtbl = &segment_persist_stream_vtbl; list_init (&obj->Tracks); return obj; @@ -943,7 +919,8 @@ HRESULT create_dmsegment(REFIID guid, void **ret_iface) IDirectMusicSegment8Impl *obj; HRESULT hr; - if (!(obj = create_segment())) { + if (!(obj = segment_create())) + { *ret_iface = NULL; return E_OUTOFMEMORY; } From ff4b8d1fd6005fa59bd08529fa0cfd9a06e051a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 13:59:56 +0200 Subject: [PATCH 0916/1148] dmime: Use one-liners for segment parameter checks. (cherry picked from commit d2cdb9cfa2b770e7348c75dee62008c34884aa3a) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 202 ++++++++++++++++++++----------------------- 1 file changed, 95 insertions(+), 107 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index f86e4a2bb5f..7dd21731f6f 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -98,67 +98,71 @@ static ULONG WINAPI segment_Release(IDirectMusicSegment8 *iface) return ref; } -static HRESULT WINAPI segment_GetLength(IDirectMusicSegment8 *iface, MUSIC_TIME *pmtLength) +static HRESULT WINAPI segment_GetLength(IDirectMusicSegment8 *iface, MUSIC_TIME *length) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - TRACE("(%p, %p)\n", This, pmtLength); - if (NULL == pmtLength) { - return E_POINTER; - } - *pmtLength = This->header.mtLength; - return S_OK; + TRACE("(%p, %p)\n", This, length); + + if (!length) return E_POINTER; + *length = This->header.mtLength; + + return S_OK; } -static HRESULT WINAPI segment_SetLength(IDirectMusicSegment8 *iface, MUSIC_TIME mtLength) +static HRESULT WINAPI segment_SetLength(IDirectMusicSegment8 *iface, MUSIC_TIME length) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - TRACE("(%p, %ld)\n", This, mtLength); - This->header.mtLength = mtLength; - return S_OK; + TRACE("(%p, %ld)\n", This, length); + + This->header.mtLength = length; + + return S_OK; } -static HRESULT WINAPI segment_GetRepeats(IDirectMusicSegment8 *iface, DWORD *pdwRepeats) +static HRESULT WINAPI segment_GetRepeats(IDirectMusicSegment8 *iface, DWORD *repeats) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - TRACE("(%p, %p)\n", This, pdwRepeats); - if (NULL == pdwRepeats) { - return E_POINTER; - } - *pdwRepeats = This->header.dwRepeats; - return S_OK; + TRACE("(%p, %p)\n", This, repeats); + + if (!repeats) return E_POINTER; + *repeats = This->header.dwRepeats; + + return S_OK; } -static HRESULT WINAPI segment_SetRepeats(IDirectMusicSegment8 *iface, DWORD dwRepeats) +static HRESULT WINAPI segment_SetRepeats(IDirectMusicSegment8 *iface, DWORD repeats) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - TRACE("(%p, %ld)\n", This, dwRepeats); - This->header.dwRepeats = dwRepeats; - return S_OK; + TRACE("(%p, %ld)\n", This, repeats); + This->header.dwRepeats = repeats; + + return S_OK; } -static HRESULT WINAPI segment_GetDefaultResolution(IDirectMusicSegment8 *iface, DWORD *pdwResolution) +static HRESULT WINAPI segment_GetDefaultResolution(IDirectMusicSegment8 *iface, DWORD *resolution) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - TRACE("(%p, %p)\n", This, pdwResolution); - if (NULL == pdwResolution) { - return E_POINTER; - } - *pdwResolution = This->header.dwResolution; - return S_OK; + TRACE("(%p, %p)\n", This, resolution); + + if (!resolution) return E_POINTER; + *resolution = This->header.dwResolution; + + return S_OK; } -static HRESULT WINAPI segment_SetDefaultResolution(IDirectMusicSegment8 *iface, DWORD dwResolution) +static HRESULT WINAPI segment_SetDefaultResolution(IDirectMusicSegment8 *iface, DWORD resolution) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - TRACE("(%p, %ld)\n", This, dwResolution); - This->header.dwResolution = dwResolution; - return S_OK; + TRACE("(%p, %ld)\n", This, resolution); + This->header.dwResolution = resolution; + + return S_OK; } static HRESULT WINAPI segment_GetTrack(IDirectMusicSegment8 *iface, REFGUID rguidType, @@ -294,56 +298,43 @@ static HRESULT WINAPI segment_RemoveTrack(IDirectMusicSegment8 *iface, IDirectMu } static HRESULT WINAPI segment_InitPlay(IDirectMusicSegment8 *iface, - IDirectMusicSegmentState **ppSegState, IDirectMusicPerformance *pPerformance, DWORD dwFlags) + IDirectMusicSegmentState **state, IDirectMusicPerformance *performance, DWORD flags) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - HRESULT hr; + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + HRESULT hr; - FIXME("(%p, %p, %p, %ld): semi-stub\n", This, ppSegState, pPerformance, dwFlags); - if (NULL == ppSegState) { - return E_POINTER; - } - hr = create_dmsegmentstate(&IID_IDirectMusicSegmentState,(void**) ppSegState); - if (FAILED(hr)) { - return hr; - } - /* TODO: DMUS_SEGF_FLAGS */ - return S_OK; + FIXME("(%p, %p, %p, %ld): semi-stub\n", This, state, performance, flags); + + if (!state) return E_POINTER; + if (FAILED(hr = create_dmsegmentstate(&IID_IDirectMusicSegmentState, (void **)state))) return hr; + + /* TODO: DMUS_SEGF_FLAGS */ + return S_OK; } -static HRESULT WINAPI segment_GetGraph(IDirectMusicSegment8 *iface, IDirectMusicGraph **ppGraph) +static HRESULT WINAPI segment_GetGraph(IDirectMusicSegment8 *iface, IDirectMusicGraph **graph) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %p): semi-stub\n", This, ppGraph); - if (NULL == ppGraph) { - return E_POINTER; - } - if (NULL == This->pGraph) { - return DMUS_E_NOT_FOUND; - } - /** - * should return This, as seen in msdn - * "...The segment object implements IDirectMusicGraph directly..." - */ - *ppGraph = This->pGraph; - IDirectMusicGraph_AddRef(This->pGraph); - return S_OK; + FIXME("(%p, %p): semi-stub\n", This, graph); + + if (!graph) return E_POINTER; + if (!(*graph = This->pGraph)) return DMUS_E_NOT_FOUND; + IDirectMusicGraph_AddRef(This->pGraph); + + return S_OK; } -static HRESULT WINAPI segment_SetGraph(IDirectMusicSegment8 *iface, IDirectMusicGraph *pGraph) +static HRESULT WINAPI segment_SetGraph(IDirectMusicSegment8 *iface, IDirectMusicGraph *graph) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %p): to complete\n", This, pGraph); - if (NULL != This->pGraph) { - IDirectMusicGraph_Release(This->pGraph); - } - This->pGraph = pGraph; - if (NULL != This->pGraph) { - IDirectMusicGraph_AddRef(This->pGraph); - } - return S_OK; + FIXME("(%p, %p): to complete\n", This, graph); + + if (This->pGraph) IDirectMusicGraph_Release(This->pGraph); + if ((This->pGraph = graph)) IDirectMusicGraph_AddRef(This->pGraph); + + return S_OK; } static HRESULT WINAPI segment_AddNotificationType(IDirectMusicSegment8 *iface, REFGUID rguidNotificationType) @@ -451,28 +442,27 @@ static HRESULT WINAPI segment_Clone(IDirectMusicSegment8 *iface, MUSIC_TIME star return track_clone_fail ? S_FALSE : S_OK; } -static HRESULT WINAPI segment_SetStartPoint(IDirectMusicSegment8 *iface, MUSIC_TIME mtStart) +static HRESULT WINAPI segment_SetStartPoint(IDirectMusicSegment8 *iface, MUSIC_TIME start) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - TRACE("(%p, %ld)\n", This, mtStart); - if (mtStart >= This->header.mtLength) { - return DMUS_E_OUT_OF_RANGE; - } - This->header.mtPlayStart = mtStart; - return S_OK; + TRACE("(%p, %ld)\n", This, start); + + if (start >= This->header.mtLength) return DMUS_E_OUT_OF_RANGE; + This->header.mtPlayStart = start; + + return S_OK; } -static HRESULT WINAPI segment_GetStartPoint(IDirectMusicSegment8 *iface, MUSIC_TIME *pmtStart) +static HRESULT WINAPI segment_GetStartPoint(IDirectMusicSegment8 *iface, MUSIC_TIME *start) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - TRACE("(%p, %p)\n", This, pmtStart); - if (NULL == pmtStart) { - return E_POINTER; - } - *pmtStart = This->header.mtPlayStart; - return S_OK; + TRACE("(%p, %p)\n", This, start); + if (!start) return E_POINTER; + *start = This->header.mtPlayStart; + + return S_OK; } static HRESULT WINAPI segment_SetLoopPoints(IDirectMusicSegment8 *iface, MUSIC_TIME start, MUSIC_TIME end) @@ -481,27 +471,25 @@ static HRESULT WINAPI segment_SetLoopPoints(IDirectMusicSegment8 *iface, MUSIC_T TRACE("(%p, %ld, %ld)\n", This, start, end); - if ((end || start) && - (start >= This->header.mtLength || end > This->header.mtLength || start > end)) + if ((end || start) && (start >= This->header.mtLength || end > This->header.mtLength || start > end)) return DMUS_E_OUT_OF_RANGE; - This->header.mtLoopStart = start; This->header.mtLoopEnd = end; return S_OK; } -static HRESULT WINAPI segment_GetLoopPoints(IDirectMusicSegment8 *iface, MUSIC_TIME *pmtStart, MUSIC_TIME *pmtEnd) +static HRESULT WINAPI segment_GetLoopPoints(IDirectMusicSegment8 *iface, MUSIC_TIME *start, MUSIC_TIME *end) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - TRACE("(%p, %p, %p)\n", This, pmtStart, pmtEnd); - if (NULL == pmtStart || NULL == pmtEnd) { - return E_POINTER; - } - *pmtStart = This->header.mtLoopStart; - *pmtEnd = This->header.mtLoopEnd; - return S_OK; + TRACE("(%p, %p, %p)\n", This, start, end); + + if (!start || !end) return E_POINTER; + *start = This->header.mtLoopStart; + *end = This->header.mtLoopEnd; + + return S_OK; } static HRESULT WINAPI segment_SetPChannelsUsed(IDirectMusicSegment8 *iface, DWORD dwNumPChannels, DWORD *paPChannels) From 2f5ab3ebfee677a85cfd0c46f484cdd1488e4bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 14:03:02 +0200 Subject: [PATCH 0917/1148] dmime: Get rid of the IDirectMusicSegmentImpl typedef. (cherry picked from commit 6699becf522f2aa2bf7e508d290996e02620e7fd) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 140 +++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 72 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 7dd21731f6f..fd042842272 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -23,10 +23,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmime); -/***************************************************************************** - * IDirectMusicSegmentImpl implementation - */ -typedef struct IDirectMusicSegment8Impl { +struct segment +{ IDirectMusicSegment8 IDirectMusicSegment8_iface; struct dmobject dmobj; LONG ref; @@ -37,18 +35,18 @@ typedef struct IDirectMusicSegment8Impl { PCMWAVEFORMAT wave_format; void *wave_data; int data_size; -} IDirectMusicSegment8Impl; +}; -static IDirectMusicSegment8Impl *segment_create(void); +static struct segment *segment_create(void); -static inline IDirectMusicSegment8Impl *impl_from_IDirectMusicSegment8(IDirectMusicSegment8 *iface) +static inline struct segment *impl_from_IDirectMusicSegment8(IDirectMusicSegment8 *iface) { - return CONTAINING_RECORD(iface, IDirectMusicSegment8Impl, IDirectMusicSegment8_iface); + return CONTAINING_RECORD(iface, struct segment, IDirectMusicSegment8_iface); } static HRESULT WINAPI segment_QueryInterface(IDirectMusicSegment8 *iface, REFIID riid, void **ret_iface) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ret_iface); @@ -73,7 +71,7 @@ static HRESULT WINAPI segment_QueryInterface(IDirectMusicSegment8 *iface, REFIID static ULONG WINAPI segment_AddRef(IDirectMusicSegment8 *iface) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); LONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) ref=%ld\n", This, ref); @@ -83,7 +81,7 @@ static ULONG WINAPI segment_AddRef(IDirectMusicSegment8 *iface) static ULONG WINAPI segment_Release(IDirectMusicSegment8 *iface) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); LONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) ref=%ld\n", This, ref); @@ -100,7 +98,7 @@ static ULONG WINAPI segment_Release(IDirectMusicSegment8 *iface) static HRESULT WINAPI segment_GetLength(IDirectMusicSegment8 *iface, MUSIC_TIME *length) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); TRACE("(%p, %p)\n", This, length); @@ -112,7 +110,7 @@ static HRESULT WINAPI segment_GetLength(IDirectMusicSegment8 *iface, MUSIC_TIME static HRESULT WINAPI segment_SetLength(IDirectMusicSegment8 *iface, MUSIC_TIME length) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); TRACE("(%p, %ld)\n", This, length); @@ -123,7 +121,7 @@ static HRESULT WINAPI segment_SetLength(IDirectMusicSegment8 *iface, MUSIC_TIME static HRESULT WINAPI segment_GetRepeats(IDirectMusicSegment8 *iface, DWORD *repeats) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); TRACE("(%p, %p)\n", This, repeats); @@ -135,7 +133,7 @@ static HRESULT WINAPI segment_GetRepeats(IDirectMusicSegment8 *iface, DWORD *rep static HRESULT WINAPI segment_SetRepeats(IDirectMusicSegment8 *iface, DWORD repeats) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); TRACE("(%p, %ld)\n", This, repeats); This->header.dwRepeats = repeats; @@ -145,7 +143,7 @@ static HRESULT WINAPI segment_SetRepeats(IDirectMusicSegment8 *iface, DWORD repe static HRESULT WINAPI segment_GetDefaultResolution(IDirectMusicSegment8 *iface, DWORD *resolution) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); TRACE("(%p, %p)\n", This, resolution); @@ -157,7 +155,7 @@ static HRESULT WINAPI segment_GetDefaultResolution(IDirectMusicSegment8 *iface, static HRESULT WINAPI segment_SetDefaultResolution(IDirectMusicSegment8 *iface, DWORD resolution) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); TRACE("(%p, %ld)\n", This, resolution); This->header.dwResolution = resolution; @@ -168,7 +166,7 @@ static HRESULT WINAPI segment_SetDefaultResolution(IDirectMusicSegment8 *iface, static HRESULT WINAPI segment_GetTrack(IDirectMusicSegment8 *iface, REFGUID rguidType, DWORD dwGroupBits, DWORD dwIndex, IDirectMusicTrack **ppTrack) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); CLSID pIt_clsid; struct list* pEntry = NULL; LPDMUS_PRIVATE_SEGMENT_TRACK pIt = NULL; @@ -216,7 +214,7 @@ static HRESULT WINAPI segment_GetTrack(IDirectMusicSegment8 *iface, REFGUID rgui static HRESULT WINAPI segment_GetTrackGroup(IDirectMusicSegment8 *iface, IDirectMusicTrack *pTrack, DWORD *pdwGroupBits) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); struct list* pEntry = NULL; LPDMUS_PRIVATE_SEGMENT_TRACK pIt = NULL; @@ -240,7 +238,7 @@ static HRESULT WINAPI segment_GetTrackGroup(IDirectMusicSegment8 *iface, IDirect static HRESULT WINAPI segment_InsertTrack(IDirectMusicSegment8 *iface, IDirectMusicTrack *pTrack, DWORD group) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); DWORD i = 0; struct list* pEntry = NULL; LPDMUS_PRIVATE_SEGMENT_TRACK pIt = NULL; @@ -274,7 +272,7 @@ static HRESULT WINAPI segment_InsertTrack(IDirectMusicSegment8 *iface, IDirectMu static HRESULT WINAPI segment_RemoveTrack(IDirectMusicSegment8 *iface, IDirectMusicTrack *pTrack) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); struct list* pEntry = NULL; LPDMUS_PRIVATE_SEGMENT_TRACK pIt = NULL; @@ -300,7 +298,7 @@ static HRESULT WINAPI segment_RemoveTrack(IDirectMusicSegment8 *iface, IDirectMu static HRESULT WINAPI segment_InitPlay(IDirectMusicSegment8 *iface, IDirectMusicSegmentState **state, IDirectMusicPerformance *performance, DWORD flags) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); HRESULT hr; FIXME("(%p, %p, %p, %ld): semi-stub\n", This, state, performance, flags); @@ -314,7 +312,7 @@ static HRESULT WINAPI segment_InitPlay(IDirectMusicSegment8 *iface, static HRESULT WINAPI segment_GetGraph(IDirectMusicSegment8 *iface, IDirectMusicGraph **graph) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); FIXME("(%p, %p): semi-stub\n", This, graph); @@ -327,7 +325,7 @@ static HRESULT WINAPI segment_GetGraph(IDirectMusicSegment8 *iface, IDirectMusic static HRESULT WINAPI segment_SetGraph(IDirectMusicSegment8 *iface, IDirectMusicGraph *graph) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); FIXME("(%p, %p): to complete\n", This, graph); @@ -339,22 +337,22 @@ static HRESULT WINAPI segment_SetGraph(IDirectMusicSegment8 *iface, IDirectMusic static HRESULT WINAPI segment_AddNotificationType(IDirectMusicSegment8 *iface, REFGUID rguidNotificationType) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %s): stub\n", This, debugstr_dmguid(rguidNotificationType)); - return S_OK; + struct segment *This = impl_from_IDirectMusicSegment8(iface); + FIXME("(%p, %s): stub\n", This, debugstr_dmguid(rguidNotificationType)); + return S_OK; } static HRESULT WINAPI segment_RemoveNotificationType(IDirectMusicSegment8 *iface, REFGUID rguidNotificationType) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %s): stub\n", This, debugstr_dmguid(rguidNotificationType)); - return S_OK; + struct segment *This = impl_from_IDirectMusicSegment8(iface); + FIXME("(%p, %s): stub\n", This, debugstr_dmguid(rguidNotificationType)); + return S_OK; } static HRESULT WINAPI segment_GetParam(IDirectMusicSegment8 *iface, REFGUID type, DWORD group, DWORD index, MUSIC_TIME time, MUSIC_TIME *next, void *param) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); IDirectMusicTrack *track; unsigned int i, count; HRESULT hr = DMUS_E_TRACK_NOT_FOUND; @@ -387,16 +385,17 @@ static HRESULT WINAPI segment_GetParam(IDirectMusicSegment8 *iface, REFGUID type static HRESULT WINAPI segment_SetParam(IDirectMusicSegment8 *iface, REFGUID rguidType, DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, void *pParam) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %s, %#lx, %ld, %ld, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, mtTime, pParam); - return S_OK; + struct segment *This = impl_from_IDirectMusicSegment8(iface); + FIXME("(%p, %s, %#lx, %ld, %ld, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, + dwIndex, mtTime, pParam); + return S_OK; } static HRESULT WINAPI segment_Clone(IDirectMusicSegment8 *iface, MUSIC_TIME start, MUSIC_TIME end, IDirectMusicSegment **segment) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - IDirectMusicSegment8Impl *clone; + struct segment *This = impl_from_IDirectMusicSegment8(iface); + struct segment *clone; IDirectMusicTrack *track; DMUS_PRIVATE_SEGMENT_TRACK *track_item, *cloned_item; HRESULT hr; @@ -444,7 +443,7 @@ static HRESULT WINAPI segment_Clone(IDirectMusicSegment8 *iface, MUSIC_TIME star static HRESULT WINAPI segment_SetStartPoint(IDirectMusicSegment8 *iface, MUSIC_TIME start) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); TRACE("(%p, %ld)\n", This, start); @@ -456,7 +455,7 @@ static HRESULT WINAPI segment_SetStartPoint(IDirectMusicSegment8 *iface, MUSIC_T static HRESULT WINAPI segment_GetStartPoint(IDirectMusicSegment8 *iface, MUSIC_TIME *start) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); TRACE("(%p, %p)\n", This, start); if (!start) return E_POINTER; @@ -467,7 +466,7 @@ static HRESULT WINAPI segment_GetStartPoint(IDirectMusicSegment8 *iface, MUSIC_T static HRESULT WINAPI segment_SetLoopPoints(IDirectMusicSegment8 *iface, MUSIC_TIME start, MUSIC_TIME end) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); TRACE("(%p, %ld, %ld)\n", This, start, end); @@ -481,7 +480,7 @@ static HRESULT WINAPI segment_SetLoopPoints(IDirectMusicSegment8 *iface, MUSIC_T static HRESULT WINAPI segment_GetLoopPoints(IDirectMusicSegment8 *iface, MUSIC_TIME *start, MUSIC_TIME *end) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); + struct segment *This = impl_from_IDirectMusicSegment8(iface); TRACE("(%p, %p, %p)\n", This, start, end); @@ -494,46 +493,47 @@ static HRESULT WINAPI segment_GetLoopPoints(IDirectMusicSegment8 *iface, MUSIC_T static HRESULT WINAPI segment_SetPChannelsUsed(IDirectMusicSegment8 *iface, DWORD dwNumPChannels, DWORD *paPChannels) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %ld, %p): stub\n", This, dwNumPChannels, paPChannels); - return S_OK; + struct segment *This = impl_from_IDirectMusicSegment8(iface); + FIXME("(%p, %ld, %p): stub\n", This, dwNumPChannels, paPChannels); + return S_OK; } static HRESULT WINAPI segment_SetTrackConfig(IDirectMusicSegment8 *iface, REFGUID rguidTrackClassID, DWORD dwGroupBits, DWORD dwIndex, DWORD dwFlagsOn, DWORD dwFlagsOff) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %s, %#lx, %ld, %ld, %ld): stub\n", This, debugstr_dmguid(rguidTrackClassID), dwGroupBits, dwIndex, dwFlagsOn, dwFlagsOff); - return S_OK; + struct segment *This = impl_from_IDirectMusicSegment8(iface); + FIXME("(%p, %s, %#lx, %ld, %ld, %ld): stub\n", This, debugstr_dmguid(rguidTrackClassID), + dwGroupBits, dwIndex, dwFlagsOn, dwFlagsOff); + return S_OK; } static HRESULT WINAPI segment_GetAudioPathConfig(IDirectMusicSegment8 *iface, IUnknown **ppAudioPathConfig) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %p): stub\n", This, ppAudioPathConfig); - return S_OK; + struct segment *This = impl_from_IDirectMusicSegment8(iface); + FIXME("(%p, %p): stub\n", This, ppAudioPathConfig); + return S_OK; } static HRESULT WINAPI segment_Compose(IDirectMusicSegment8 *iface, MUSIC_TIME mtTime, IDirectMusicSegment *pFromSegment, IDirectMusicSegment *pToSegment, IDirectMusicSegment **ppComposedSegment) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %ld, %p, %p, %p): stub\n", This, mtTime, pFromSegment, pToSegment, ppComposedSegment); - return S_OK; + struct segment *This = impl_from_IDirectMusicSegment8(iface); + FIXME("(%p, %ld, %p, %p, %p): stub\n", This, mtTime, pFromSegment, pToSegment, ppComposedSegment); + return S_OK; } static HRESULT WINAPI segment_Download(IDirectMusicSegment8 *iface, IUnknown *pAudioPath) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %p): stub\n", This, pAudioPath); - return S_OK; + struct segment *This = impl_from_IDirectMusicSegment8(iface); + FIXME("(%p, %p): stub\n", This, pAudioPath); + return S_OK; } static HRESULT WINAPI segment_Unload(IDirectMusicSegment8 *iface, IUnknown *pAudioPath) { - IDirectMusicSegment8Impl *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %p): stub\n", This, pAudioPath); - return S_OK; + struct segment *This = impl_from_IDirectMusicSegment8(iface); + FIXME("(%p, %p): stub\n", This, pAudioPath); + return S_OK; } static const IDirectMusicSegment8Vtbl segment_vtbl = @@ -616,8 +616,7 @@ static const IDirectMusicObjectVtbl segment_object_vtbl = segment_object_ParseDescriptor, }; -static HRESULT parse_track_form(IDirectMusicSegment8Impl *This, IStream *stream, - const struct chunk_entry *riff) +static HRESULT parse_track_form(struct segment *This, IStream *stream, const struct chunk_entry *riff) { struct chunk_entry chunk = {.parent = riff}; IDirectMusicTrack *track = NULL; @@ -699,8 +698,7 @@ static HRESULT parse_track_form(IDirectMusicSegment8Impl *This, IStream *stream, return hr; } -static HRESULT parse_track_list(IDirectMusicSegment8Impl *This, IStream *stream, - const struct chunk_entry *trkl) +static HRESULT parse_track_list(struct segment *This, IStream *stream, const struct chunk_entry *trkl) { struct chunk_entry chunk = {.parent = trkl}; HRESULT hr; @@ -742,8 +740,7 @@ static inline void dump_segment_header(DMUS_IO_SEGMENT_HEADER *h, DWORD size) } } -static HRESULT parse_segment_form(IDirectMusicSegment8Impl *This, IStream *stream, - const struct chunk_entry *riff) +static HRESULT parse_segment_form(struct segment *This, IStream *stream, const struct chunk_entry *riff) { struct chunk_entry chunk = {.parent = riff}; HRESULT hr; @@ -783,12 +780,12 @@ static HRESULT parse_segment_form(IDirectMusicSegment8Impl *This, IStream *strea return SUCCEEDED(hr) ? S_OK : hr; } -static inline IDirectMusicSegment8Impl *impl_from_IPersistStream(IPersistStream *iface) +static inline struct segment *impl_from_IPersistStream(IPersistStream *iface) { - return CONTAINING_RECORD(iface, IDirectMusicSegment8Impl, dmobj.IPersistStream_iface); + return CONTAINING_RECORD(iface, struct segment, dmobj.IPersistStream_iface); } -static HRESULT parse_wave_form(IDirectMusicSegment8Impl *This, IStream *stream, const struct chunk_entry *riff) +static HRESULT parse_wave_form(struct segment *This, IStream *stream, const struct chunk_entry *riff) { HRESULT hr; struct chunk_entry chunk = {.parent = riff}; @@ -836,7 +833,7 @@ static HRESULT parse_wave_form(IDirectMusicSegment8Impl *This, IStream *stream, static HRESULT WINAPI segment_persist_stream_Load(IPersistStream *iface, IStream *stream) { - IDirectMusicSegment8Impl *This = impl_from_IPersistStream(iface); + struct segment *This = impl_from_IPersistStream(iface); struct chunk_entry riff = {0}; HRESULT hr; @@ -885,12 +882,11 @@ static const IPersistStreamVtbl segment_persist_stream_vtbl = unimpl_IPersistStream_GetSizeMax, }; -IDirectMusicSegment8Impl *segment_create(void) +static struct segment *segment_create(void) { - IDirectMusicSegment8Impl *obj; + struct segment *obj; if (!(obj = calloc(1, sizeof(*obj)))) return NULL; - obj->IDirectMusicSegment8_iface.lpVtbl = &segment_vtbl; obj->ref = 1; dmobject_init(&obj->dmobj, &CLSID_DirectMusicSegment, (IUnknown *)&obj->IDirectMusicSegment8_iface); @@ -904,7 +900,7 @@ IDirectMusicSegment8Impl *segment_create(void) /* for ClassFactory */ HRESULT create_dmsegment(REFIID guid, void **ret_iface) { - IDirectMusicSegment8Impl *obj; + struct segment *obj; HRESULT hr; if (!(obj = segment_create())) From 29e885aa327440342194f5fe2a0306ff68638841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 14:25:56 +0200 Subject: [PATCH 0918/1148] dmime: Avoid leaking tracks in IDirectMusicSegment_Release. (cherry picked from commit c0b52aa3a7c64fa42c87d595dd2d5d88c28aa9a7) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 7 ---- dlls/dmime/segment.c | 69 ++++++++++++++++++++++++++------------ 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index be0c463339f..eacde4b46be 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -73,13 +73,6 @@ extern void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath*,IDirectSo /***************************************************************************** * Auxiliary definitions */ -typedef struct _DMUS_PRIVATE_SEGMENT_TRACK { - struct list entry; /* for listing elements */ - DWORD dwGroupBits; - DWORD flags; - IDirectMusicTrack* pTrack; -} DMUS_PRIVATE_SEGMENT_TRACK, *LPDMUS_PRIVATE_SEGMENT_TRACK; - typedef struct _DMUS_PRIVATE_TEMPO_ITEM { struct list entry; /* for listing elements */ DMUS_IO_TEMPO_ITEM item; diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index fd042842272..1ecc7c9566c 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -23,6 +23,25 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmime); +struct track_entry +{ + struct list entry; + DWORD dwGroupBits; + DWORD flags; + IDirectMusicTrack *pTrack; +}; + +static void track_entry_destroy(struct track_entry *entry) +{ + HRESULT hr; + + if (FAILED(hr = IDirectMusicTrack_Init(entry->pTrack, NULL))) + WARN("Failed to de-init track %p, hr %#lx\n", entry->pTrack, hr); + IDirectMusicTrack_Release(entry->pTrack); + + free(entry); +} + struct segment { IDirectMusicSegment8 IDirectMusicSegment8_iface; @@ -30,7 +49,7 @@ struct segment LONG ref; DMUS_IO_SEGMENT_HEADER header; IDirectMusicGraph *pGraph; - struct list Tracks; + struct list tracks; PCMWAVEFORMAT wave_format; void *wave_data; @@ -87,6 +106,14 @@ static ULONG WINAPI segment_Release(IDirectMusicSegment8 *iface) TRACE("(%p) ref=%ld\n", This, ref); if (!ref) { + struct track_entry *entry, *next; + + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &This->tracks, struct track_entry, entry) + { + list_remove(&entry->entry); + track_entry_destroy(entry); + } + if (This->wave_data) free(This->wave_data); @@ -169,7 +196,7 @@ static HRESULT WINAPI segment_GetTrack(IDirectMusicSegment8 *iface, REFGUID rgui struct segment *This = impl_from_IDirectMusicSegment8(iface); CLSID pIt_clsid; struct list* pEntry = NULL; - LPDMUS_PRIVATE_SEGMENT_TRACK pIt = NULL; + struct track_entry *pIt = NULL; IPersistStream* pCLSIDStream = NULL; HRESULT hr = S_OK; @@ -179,8 +206,8 @@ static HRESULT WINAPI segment_GetTrack(IDirectMusicSegment8 *iface, REFGUID rgui return E_POINTER; } - LIST_FOR_EACH (pEntry, &This->Tracks) { - pIt = LIST_ENTRY(pEntry, DMUS_PRIVATE_SEGMENT_TRACK, entry); + LIST_FOR_EACH (pEntry, &This->tracks) { + pIt = LIST_ENTRY(pEntry, struct track_entry, entry); TRACE(" - %p -> %#lx,%p\n", pIt, pIt->dwGroupBits, pIt->pTrack); if (0xFFFFFFFF != dwGroupBits && 0 == (pIt->dwGroupBits & dwGroupBits)) continue ; if (FALSE == IsEqualGUID(&GUID_NULL, rguidType)) { @@ -216,7 +243,7 @@ static HRESULT WINAPI segment_GetTrackGroup(IDirectMusicSegment8 *iface, IDirect { struct segment *This = impl_from_IDirectMusicSegment8(iface); struct list* pEntry = NULL; - LPDMUS_PRIVATE_SEGMENT_TRACK pIt = NULL; + struct track_entry *pIt = NULL; TRACE("(%p, %p, %p)\n", This, pTrack, pdwGroupBits); @@ -224,8 +251,8 @@ static HRESULT WINAPI segment_GetTrackGroup(IDirectMusicSegment8 *iface, IDirect return E_POINTER; } - LIST_FOR_EACH (pEntry, &This->Tracks) { - pIt = LIST_ENTRY(pEntry, DMUS_PRIVATE_SEGMENT_TRACK, entry); + LIST_FOR_EACH (pEntry, &This->tracks) { + pIt = LIST_ENTRY(pEntry, struct track_entry, entry); TRACE(" - %p -> %#lx, %p\n", pIt, pIt->dwGroupBits, pIt->pTrack); if (NULL != pIt && pIt->pTrack == pTrack) { *pdwGroupBits = pIt->dwGroupBits; @@ -241,17 +268,17 @@ static HRESULT WINAPI segment_InsertTrack(IDirectMusicSegment8 *iface, IDirectMu struct segment *This = impl_from_IDirectMusicSegment8(iface); DWORD i = 0; struct list* pEntry = NULL; - LPDMUS_PRIVATE_SEGMENT_TRACK pIt = NULL; - LPDMUS_PRIVATE_SEGMENT_TRACK pNewSegTrack = NULL; + struct track_entry *pIt = NULL; + struct track_entry *pNewSegTrack = NULL; TRACE("(%p, %p, %#lx)\n", This, pTrack, group); if (!group) return E_INVALIDARG; - LIST_FOR_EACH (pEntry, &This->Tracks) { + LIST_FOR_EACH (pEntry, &This->tracks) { i++; - pIt = LIST_ENTRY(pEntry, DMUS_PRIVATE_SEGMENT_TRACK, entry); + pIt = LIST_ENTRY(pEntry, struct track_entry, entry); TRACE(" - #%lu: %p -> %#lx, %p\n", i, pIt, pIt->dwGroupBits, pIt->pTrack); if (NULL != pIt && pIt->pTrack == pTrack) { ERR("(%p, %p): track is already in list\n", This, pTrack); @@ -265,7 +292,7 @@ static HRESULT WINAPI segment_InsertTrack(IDirectMusicSegment8 *iface, IDirectMu pNewSegTrack->pTrack = pTrack; IDirectMusicTrack_Init(pTrack, (IDirectMusicSegment *)iface); IDirectMusicTrack_AddRef(pTrack); - list_add_tail (&This->Tracks, &pNewSegTrack->entry); + list_add_tail (&This->tracks, &pNewSegTrack->entry); return S_OK; } @@ -274,12 +301,12 @@ static HRESULT WINAPI segment_RemoveTrack(IDirectMusicSegment8 *iface, IDirectMu { struct segment *This = impl_from_IDirectMusicSegment8(iface); struct list* pEntry = NULL; - LPDMUS_PRIVATE_SEGMENT_TRACK pIt = NULL; + struct track_entry *pIt = NULL; TRACE("(%p, %p)\n", This, pTrack); - LIST_FOR_EACH (pEntry, &This->Tracks) { - pIt = LIST_ENTRY(pEntry, DMUS_PRIVATE_SEGMENT_TRACK, entry); + LIST_FOR_EACH (pEntry, &This->tracks) { + pIt = LIST_ENTRY(pEntry, struct track_entry, entry); if (pIt->pTrack == pTrack) { TRACE("(%p, %p): track in list\n", This, pTrack); @@ -397,7 +424,7 @@ static HRESULT WINAPI segment_Clone(IDirectMusicSegment8 *iface, MUSIC_TIME star struct segment *This = impl_from_IDirectMusicSegment8(iface); struct segment *clone; IDirectMusicTrack *track; - DMUS_PRIVATE_SEGMENT_TRACK *track_item, *cloned_item; + struct track_entry *track_item, *cloned_item; HRESULT hr; BOOL track_clone_fail = FALSE; @@ -417,14 +444,14 @@ static HRESULT WINAPI segment_Clone(IDirectMusicSegment8 *iface, MUSIC_TIME star if (clone->pGraph) IDirectMusicGraph_AddRef(clone->pGraph); - LIST_FOR_EACH_ENTRY(track_item, &This->Tracks, DMUS_PRIVATE_SEGMENT_TRACK, entry) { + LIST_FOR_EACH_ENTRY(track_item, &This->tracks, struct track_entry, entry) { if (SUCCEEDED(hr = IDirectMusicTrack_Clone(track_item->pTrack, start, end, &track))) { if ((cloned_item = malloc(sizeof(*cloned_item)))) { cloned_item->dwGroupBits = track_item->dwGroupBits; cloned_item->flags = track_item->flags; cloned_item->pTrack = track; - list_add_tail(&clone->Tracks, &cloned_item->entry); + list_add_tail(&clone->tracks, &cloned_item->entry); continue; } else @@ -625,7 +652,7 @@ static HRESULT parse_track_form(struct segment *This, IStream *stream, const str DMUS_IO_TRACK_HEADER thdr; DMUS_IO_TRACK_EXTRAS_HEADER txhdr = {0}; HRESULT hr; - DMUS_PRIVATE_SEGMENT_TRACK *item; + struct track_entry *item; TRACE("Parsing track form in %p: %s\n", stream, debugstr_chunk(riff)); @@ -685,7 +712,7 @@ static HRESULT parse_track_form(struct segment *This, IStream *stream, const str if (FAILED(hr)) goto done; - item = LIST_ENTRY(list_tail(&This->Tracks), DMUS_PRIVATE_SEGMENT_TRACK, entry); + item = LIST_ENTRY(list_tail(&This->tracks), struct track_entry, entry); item->flags = txhdr.dwFlags; done: @@ -892,7 +919,7 @@ static struct segment *segment_create(void) dmobject_init(&obj->dmobj, &CLSID_DirectMusicSegment, (IUnknown *)&obj->IDirectMusicSegment8_iface); obj->dmobj.IDirectMusicObject_iface.lpVtbl = &segment_object_vtbl; obj->dmobj.IPersistStream_iface.lpVtbl = &segment_persist_stream_vtbl; - list_init (&obj->Tracks); + list_init(&obj->tracks); return obj; } From 62c8ff66fb0d0d78e7f39d6378c087f2f1a14f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 15 Sep 2023 11:09:31 +0200 Subject: [PATCH 0919/1148] dmime: Rewrite segment IDirectMusicSegment_GetTrack. (cherry picked from commit a713797597b90b0da425f92f7451f06fd497a975) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 68 +++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 1ecc7c9566c..fc219554588 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -190,52 +190,44 @@ static HRESULT WINAPI segment_SetDefaultResolution(IDirectMusicSegment8 *iface, return S_OK; } -static HRESULT WINAPI segment_GetTrack(IDirectMusicSegment8 *iface, REFGUID rguidType, - DWORD dwGroupBits, DWORD dwIndex, IDirectMusicTrack **ppTrack) +static HRESULT WINAPI segment_GetTrack(IDirectMusicSegment8 *iface, REFGUID type, DWORD group, + DWORD index, IDirectMusicTrack **ret_track) { struct segment *This = impl_from_IDirectMusicSegment8(iface); - CLSID pIt_clsid; - struct list* pEntry = NULL; - struct track_entry *pIt = NULL; - IPersistStream* pCLSIDStream = NULL; + struct track_entry *entry; HRESULT hr = S_OK; - TRACE("(%p, %s, %#lx, %#lx, %p)\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, ppTrack); + TRACE("(%p, %s, %#lx, %#lx, %p)\n", This, debugstr_dmguid(type), group, index, ret_track); - if (NULL == ppTrack) { - return E_POINTER; - } + if (!ret_track) return E_POINTER; - LIST_FOR_EACH (pEntry, &This->tracks) { - pIt = LIST_ENTRY(pEntry, struct track_entry, entry); - TRACE(" - %p -> %#lx,%p\n", pIt, pIt->dwGroupBits, pIt->pTrack); - if (0xFFFFFFFF != dwGroupBits && 0 == (pIt->dwGroupBits & dwGroupBits)) continue ; - if (FALSE == IsEqualGUID(&GUID_NULL, rguidType)) { - /** - * it rguidType is not null we must check if CLSIDs are equal - * and the unique way to get it is using IPersistStream Interface - */ - hr = IDirectMusicTrack_QueryInterface(pIt->pTrack, &IID_IPersistStream, (void**) &pCLSIDStream); - if (FAILED(hr)) { - ERR("(%p): object %p don't implement IPersistStream Interface. Expect a crash (critical problem)\n", This, pIt->pTrack); - continue ; + LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) + { + if (group != -1 && !(entry->dwGroupBits & group)) continue; + + if (!IsEqualGUID(&GUID_NULL, type)) + { + CLSID entry_type = GUID_NULL; + IPersistStream *persist; + + if (SUCCEEDED(hr = IDirectMusicTrack_QueryInterface(entry->pTrack, &IID_IPersistStream, (void **)&persist))) + { + hr = IPersistStream_GetClassID(persist, &entry_type); + if (SUCCEEDED(hr)) TRACE(" - %p -> %s\n", entry, debugstr_dmguid(&entry_type)); + IPersistStream_Release(persist); + } + + if (!IsEqualGUID(&entry_type, type)) continue; } - hr = IPersistStream_GetClassID(pCLSIDStream, &pIt_clsid); - IPersistStream_Release(pCLSIDStream); pCLSIDStream = NULL; - if (FAILED(hr)) { - ERR("(%p): non-implemented GetClassID for object %p\n", This, pIt->pTrack); - continue ; + + if (!index--) + { + *ret_track = entry->pTrack; + IDirectMusicTrack_AddRef(entry->pTrack); + return S_OK; } - TRACE(" - %p -> %s\n", pIt, debugstr_dmguid(&pIt_clsid)); - if (FALSE == IsEqualGUID(&pIt_clsid, rguidType)) continue ; - } - if (0 == dwIndex) { - *ppTrack = pIt->pTrack; - IDirectMusicTrack_AddRef(*ppTrack); - return S_OK; - } - --dwIndex; - } + } + return DMUS_E_NOT_FOUND; } From b8bf4d7233ca74be192c7ad1427f1b2571c0efdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 15 Sep 2023 11:11:52 +0200 Subject: [PATCH 0920/1148] dmime: Rewrite segment IDirectMusicSegment_GetTrackGroup. (cherry picked from commit d045eae8b7383fdccfd870bad4c70892b2518e50) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index fc219554588..367e526d659 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -231,25 +231,22 @@ static HRESULT WINAPI segment_GetTrack(IDirectMusicSegment8 *iface, REFGUID type return DMUS_E_NOT_FOUND; } -static HRESULT WINAPI segment_GetTrackGroup(IDirectMusicSegment8 *iface, IDirectMusicTrack *pTrack, DWORD *pdwGroupBits) +static HRESULT WINAPI segment_GetTrackGroup(IDirectMusicSegment8 *iface, IDirectMusicTrack *track, DWORD *ret_group) { struct segment *This = impl_from_IDirectMusicSegment8(iface); - struct list* pEntry = NULL; - struct track_entry *pIt = NULL; + struct track_entry *entry; - TRACE("(%p, %p, %p)\n", This, pTrack, pdwGroupBits); + TRACE("(%p, %p, %p)\n", This, track, ret_group); - if (NULL == pdwGroupBits) { - return E_POINTER; - } + if (!ret_group) return E_POINTER; - LIST_FOR_EACH (pEntry, &This->tracks) { - pIt = LIST_ENTRY(pEntry, struct track_entry, entry); - TRACE(" - %p -> %#lx, %p\n", pIt, pIt->dwGroupBits, pIt->pTrack); - if (NULL != pIt && pIt->pTrack == pTrack) { - *pdwGroupBits = pIt->dwGroupBits; - return S_OK; - } + LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) + { + if (entry->pTrack == track) + { + *ret_group = entry->dwGroupBits; + return S_OK; + } } return DMUS_E_NOT_FOUND; From 2c3e98486b4a69c68fd571551bc04378afb4deb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 15 Sep 2023 11:11:52 +0200 Subject: [PATCH 0921/1148] dmime: Rewrite segment IDirectMusicSegment_InsertTrack. (cherry picked from commit f04976e01da864249884350fec55657cbb39c23e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 49 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 367e526d659..a1126fe3d5f 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -252,38 +252,37 @@ static HRESULT WINAPI segment_GetTrackGroup(IDirectMusicSegment8 *iface, IDirect return DMUS_E_NOT_FOUND; } -static HRESULT WINAPI segment_InsertTrack(IDirectMusicSegment8 *iface, IDirectMusicTrack *pTrack, DWORD group) +static HRESULT segment_append_track(struct segment *This, IDirectMusicTrack *track, DWORD group, DWORD flags) { - struct segment *This = impl_from_IDirectMusicSegment8(iface); - DWORD i = 0; - struct list* pEntry = NULL; - struct track_entry *pIt = NULL; - struct track_entry *pNewSegTrack = NULL; + struct track_entry *entry; + HRESULT hr; - TRACE("(%p, %p, %#lx)\n", This, pTrack, group); + if (!(entry = calloc(1, sizeof(*entry)))) return E_OUTOFMEMORY; + entry->dwGroupBits = group; + entry->flags = flags; + entry->pTrack = track; + IDirectMusicTrack_AddRef(track); - if (!group) - return E_INVALIDARG; + hr = IDirectMusicTrack_Init(track, (IDirectMusicSegment *)&This->IDirectMusicSegment8_iface); + if (FAILED(hr)) track_entry_destroy(entry); + else list_add_tail(&This->tracks, &entry->entry); - LIST_FOR_EACH (pEntry, &This->tracks) { - i++; - pIt = LIST_ENTRY(pEntry, struct track_entry, entry); - TRACE(" - #%lu: %p -> %#lx, %p\n", i, pIt, pIt->dwGroupBits, pIt->pTrack); - if (NULL != pIt && pIt->pTrack == pTrack) { - ERR("(%p, %p): track is already in list\n", This, pTrack); - return E_FAIL; - } - } + return hr; +} + +static HRESULT WINAPI segment_InsertTrack(IDirectMusicSegment8 *iface, IDirectMusicTrack *track, DWORD group) +{ + struct segment *This = impl_from_IDirectMusicSegment8(iface); + struct track_entry *entry; + + TRACE("(%p, %p, %#lx)\n", This, track, group); - if (!(pNewSegTrack = calloc(1, sizeof(*pNewSegTrack)))) return E_OUTOFMEMORY; + if (!group) return E_INVALIDARG; - pNewSegTrack->dwGroupBits = group; - pNewSegTrack->pTrack = pTrack; - IDirectMusicTrack_Init(pTrack, (IDirectMusicSegment *)iface); - IDirectMusicTrack_AddRef(pTrack); - list_add_tail (&This->tracks, &pNewSegTrack->entry); + LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) + if (entry->pTrack == track) return E_FAIL; - return S_OK; + return segment_append_track(This, track, group, 0); } static HRESULT WINAPI segment_RemoveTrack(IDirectMusicSegment8 *iface, IDirectMusicTrack *pTrack) From b7ec719c7e687c797751eaf38e4acdc1feb59f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 15 Sep 2023 11:14:08 +0200 Subject: [PATCH 0922/1148] dmime: Rewrite segment IDirectMusicSegment_RemoveTrack. (cherry picked from commit 0bdc248cfe292590de87817d40efe4f5931e1499) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index a1126fe3d5f..8b5f12243d9 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -285,29 +285,24 @@ static HRESULT WINAPI segment_InsertTrack(IDirectMusicSegment8 *iface, IDirectMu return segment_append_track(This, track, group, 0); } -static HRESULT WINAPI segment_RemoveTrack(IDirectMusicSegment8 *iface, IDirectMusicTrack *pTrack) +static HRESULT WINAPI segment_RemoveTrack(IDirectMusicSegment8 *iface, IDirectMusicTrack *track) { - struct segment *This = impl_from_IDirectMusicSegment8(iface); - struct list* pEntry = NULL; - struct track_entry *pIt = NULL; - - TRACE("(%p, %p)\n", This, pTrack); - - LIST_FOR_EACH (pEntry, &This->tracks) { - pIt = LIST_ENTRY(pEntry, struct track_entry, entry); - if (pIt->pTrack == pTrack) { - TRACE("(%p, %p): track in list\n", This, pTrack); - - list_remove(&pIt->entry); - IDirectMusicTrack_Init(pIt->pTrack, NULL); - IDirectMusicTrack_Release(pIt->pTrack); - free(pIt); - - return S_OK; + struct segment *This = impl_from_IDirectMusicSegment8(iface); + struct track_entry *entry; + + TRACE("(%p, %p)\n", This, track); + + LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) + { + if (entry->pTrack == track) + { + list_remove(&entry->entry); + track_entry_destroy(entry); + return S_OK; + } } - } - - return S_FALSE; + + return S_FALSE; } static HRESULT WINAPI segment_InitPlay(IDirectMusicSegment8 *iface, From 2e0aa241907c303e433903757f6343a9714bba44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 15 Sep 2023 11:14:08 +0200 Subject: [PATCH 0923/1148] dmime: Use segment_append_track in Clone and parse_track_form. (cherry picked from commit a4c1dec89d84ff95f97da8e0dee5f9622d99a42e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 46 +++++++++++--------------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 8b5f12243d9..a72ec786623 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -407,14 +407,12 @@ static HRESULT WINAPI segment_Clone(IDirectMusicSegment8 *iface, MUSIC_TIME star struct segment *This = impl_from_IDirectMusicSegment8(iface); struct segment *clone; IDirectMusicTrack *track; - struct track_entry *track_item, *cloned_item; - HRESULT hr; - BOOL track_clone_fail = FALSE; + struct track_entry *entry; + HRESULT hr = S_OK; TRACE("(%p, %ld, %ld, %p)\n", This, start, end, segment); - if (!segment) - return E_POINTER; + if (!segment) return E_POINTER; if (!(clone = segment_create())) { @@ -423,32 +421,16 @@ static HRESULT WINAPI segment_Clone(IDirectMusicSegment8 *iface, MUSIC_TIME star } clone->header = This->header; - clone->pGraph = This->pGraph; - if (clone->pGraph) - IDirectMusicGraph_AddRef(clone->pGraph); - - LIST_FOR_EACH_ENTRY(track_item, &This->tracks, struct track_entry, entry) { - if (SUCCEEDED(hr = IDirectMusicTrack_Clone(track_item->pTrack, start, end, &track))) { - if ((cloned_item = malloc(sizeof(*cloned_item)))) - { - cloned_item->dwGroupBits = track_item->dwGroupBits; - cloned_item->flags = track_item->flags; - cloned_item->pTrack = track; - list_add_tail(&clone->tracks, &cloned_item->entry); - continue; - } - else - { - IDirectMusicTrack_Release(track); - } - } - WARN("Failed to clone track %p: %#lx\n", track_item->pTrack, hr); - track_clone_fail = TRUE; + if ((clone->pGraph = This->pGraph)) IDirectMusicGraph_AddRef(clone->pGraph); + + LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) + { + if (FAILED(hr = IDirectMusicTrack_Clone(entry->pTrack, start, end, &track))) break; + if (FAILED(hr = segment_append_track(clone, track, entry->dwGroupBits, entry->flags))) break; } *segment = (IDirectMusicSegment *)&clone->IDirectMusicSegment8_iface; - - return track_clone_fail ? S_FALSE : S_OK; + return FAILED(hr) ? S_FALSE : S_OK; } static HRESULT WINAPI segment_SetStartPoint(IDirectMusicSegment8 *iface, MUSIC_TIME start) @@ -635,7 +617,6 @@ static HRESULT parse_track_form(struct segment *This, IStream *stream, const str DMUS_IO_TRACK_HEADER thdr; DMUS_IO_TRACK_EXTRAS_HEADER txhdr = {0}; HRESULT hr; - struct track_entry *item; TRACE("Parsing track form in %p: %s\n", stream, debugstr_chunk(riff)); @@ -691,12 +672,7 @@ static HRESULT parse_track_form(struct segment *This, IStream *stream, const str if (FAILED(hr)) goto done; - hr = IDirectMusicSegment8_InsertTrack(&This->IDirectMusicSegment8_iface, track, thdr.dwGroup); - if (FAILED(hr)) - goto done; - - item = LIST_ENTRY(list_tail(&This->tracks), struct track_entry, entry); - item->flags = txhdr.dwFlags; + hr = segment_append_track(This, track, thdr.dwGroup, txhdr.dwFlags); done: if (ps) From d07c26e53a1e60e8f24dc1b0cc16df2e6d386473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 19 Sep 2023 15:43:03 +0200 Subject: [PATCH 0924/1148] dmime/tests: Add some DMUS_NOTIFICATION_PMSG tests. (cherry picked from commit b1bf0f0296da6b1df5b5f58c9b232db730d93464) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 284 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 277 insertions(+), 7 deletions(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 89be2c32019..e011488d126 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -64,10 +64,30 @@ struct test_tool const DWORD *types; DWORD types_count; + SRWLOCK lock; HANDLE message_event; - DMUS_PMSG *message; + DMUS_PMSG *messages[32]; + UINT message_count; }; +static DMUS_PMSG *test_tool_push_msg(struct test_tool *tool, DMUS_PMSG *msg) +{ + AcquireSRWLockExclusive(&tool->lock); + ok(tool->message_count < ARRAY_SIZE(tool->messages), + "got %u messages\n", tool->message_count + 1); + if (tool->message_count < ARRAY_SIZE(tool->messages)) + { + memmove(tool->messages + 1, tool->messages, + tool->message_count * sizeof(*tool->messages)); + tool->messages[0] = msg; + tool->message_count++; + msg = NULL; + } + ReleaseSRWLockExclusive(&tool->lock); + + return msg; +} + static struct test_tool *impl_from_IDirectMusicTool(IDirectMusicTool *iface) { return CONTAINING_RECORD(iface, struct test_tool, IDirectMusicTool_iface); @@ -103,7 +123,7 @@ static ULONG WINAPI test_tool_Release(IDirectMusicTool *iface) if (!ref) { if (tool->graph) IDirectMusicGraph_Release(tool->graph); - ok(!tool->message, "got %p\n", tool->message); + ok(!tool->message_count, "got %p\n", &tool->message_count); CloseHandle(tool->message_event); free(tool); } @@ -147,8 +167,7 @@ static HRESULT WINAPI test_tool_ProcessPMsg(IDirectMusicTool *iface, IDirectMusi hr = IDirectMusicPerformance8_ClonePMsg((IDirectMusicPerformance8 *)performance, msg, &clone); ok(hr == S_OK, "got %#lx\n", hr); - - clone = InterlockedExchangePointer((void **)&tool->message, clone); + clone = test_tool_push_msg(tool, clone); ok(!clone, "got %p\n", clone); SetEvent(tool->message_event); @@ -207,10 +226,23 @@ static HRESULT test_tool_get_graph(IDirectMusicTool *iface, IDirectMusicGraph ** static DWORD test_tool_wait_message(IDirectMusicTool *iface, DWORD timeout, DMUS_PMSG **msg) { struct test_tool *tool = impl_from_IDirectMusicTool(iface); - DWORD ret; + DWORD ret = WAIT_FAILED; + + do + { + AcquireSRWLockExclusive(&tool->lock); + if (!tool->message_count) + *msg = NULL; + else + { + UINT index = --tool->message_count; + *msg = tool->messages[index]; + tool->messages[index] = NULL; + } + ReleaseSRWLockExclusive(&tool->lock); - ret = WaitForSingleObject(tool->message_event, timeout); - *msg = InterlockedExchangePointer((void **)&tool->message, NULL); + if (*msg) return 0; + } while (!(ret = WaitForSingleObject(tool->message_event, timeout))); return ret; } @@ -1975,6 +2007,243 @@ static void test_performance_pmsg(void) IDirectMusicTool_Release(tool); } +#define check_dmus_notification_pmsg(a, b, c) check_dmus_notification_pmsg_(__LINE__, a, b, c) +static void check_dmus_notification_pmsg_(int line, DMUS_NOTIFICATION_PMSG *msg, + const GUID *type, DWORD option) +{ + ok_(__FILE__, line)(msg->dwType == DMUS_PMSGT_NOTIFICATION, + "got dwType %#lx\n", msg->dwType); + ok_(__FILE__, line)(IsEqualGUID(&msg->guidNotificationType, type), + "got guidNotificationType %s\n", debugstr_guid(&msg->guidNotificationType)); + ok_(__FILE__, line)(msg->dwNotificationOption == option, + "got dwNotificationOption %#lx\n", msg->dwNotificationOption); + ok_(__FILE__, line)(!msg->dwField1, "got dwField1 %lu\n", msg->dwField1); + ok_(__FILE__, line)(!msg->dwField2, "got dwField2 %lu\n", msg->dwField2); + + if (!IsEqualGUID(&msg->guidNotificationType, &GUID_NOTIFICATION_SEGMENT)) + ok_(__FILE__, line)(!msg->punkUser, "got punkUser %p\n", msg->punkUser); + else + { + check_interface_(line, msg->punkUser, &IID_IDirectMusicSegmentState, TRUE); + check_interface_(line, msg->punkUser, &IID_IDirectMusicSegmentState8, TRUE); + } +} + +static void test_notification_pmsg(void) +{ + static const DWORD message_types[] = + { + DMUS_PMSGT_DIRTY, + DMUS_PMSGT_NOTIFICATION, + DMUS_PMSGT_WAVE, + }; + IDirectMusicPerformance *performance; + DMUS_NOTIFICATION_PMSG *notif; + IDirectMusicSegment *segment; + IDirectMusicGraph *graph; + IDirectMusicTool *tool; + DMUS_PMSG *msg; + HRESULT hr; + DWORD ret; + + hr = test_tool_create(message_types, ARRAY_SIZE(message_types), &tool); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void **)&performance); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicGraph, (void **)&graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SetGraph(performance, graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_InsertTool(graph, (IDirectMusicTool *)tool, NULL, 0, -1); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicGraph_Release(graph); + + hr = IDirectMusicPerformance_Init(performance, NULL, 0, 0); + ok(hr == S_OK, "got %#lx\n", hr); + + + hr = CoCreateInstance(&CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicSegment, (void **)&segment); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicSegment8_Download((IDirectMusicSegment8 *)segment, (IUnknown *)performance); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSegment8_Unload((IDirectMusicSegment8 *)segment, (IUnknown *)performance); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 100, &msg); + ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); + ok(!msg, "got %p\n", msg); + + + /* AddNotificationType is necessary to receive notification messages */ + + hr = IDirectMusicPerformance_AddNotificationType(performance, &GUID_NOTIFICATION_PERFORMANCE); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_AddNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 100, &msg); + ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); + ok(!msg, "got %p\n", msg); + + IDirectMusicSegment_Release(segment); + + hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_PERFORMANCE); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); + ok(hr == S_OK, "got %#lx\n", hr); + + + /* notification messages are also queued for direct access */ + + hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (hr == S_OK) + { + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + + hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (hr == S_OK) + { + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + + hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (hr == S_OK) + { + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + + hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (hr == S_OK) + { + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + + hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + if (hr == S_OK) + { + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + + hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); + ok(hr == S_FALSE, "got %#lx\n", hr); + + + hr = IDirectMusicPerformance_CloseDown(performance); + ok(hr == S_OK, "got %#lx\n", hr); + + IDirectMusicPerformance_Release(performance); + IDirectMusicTool_Release(tool); +} + START_TEST(dmime) { CoInitialize(NULL); @@ -2002,6 +2271,7 @@ START_TEST(dmime) test_performance_graph(); test_performance_time(); test_performance_pmsg(); + test_notification_pmsg(); CoUninitialize(); } From 16cda9a3d27d092802bf849eb51ce04867463cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 19 Sep 2023 17:28:30 +0200 Subject: [PATCH 0925/1148] dmime/tests: Test wave segments and DMUS_WAVE_PMSG. (cherry picked from commit c04e686ad5801b581b43ca58926692075eb39e89) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/Makefile.in | 2 + dlls/dmime/tests/dmime.c | 168 +++++++++++++++++++++++++++++++++++ dlls/dmime/tests/resource.rc | 23 +++++ dlls/dmime/tests/test.wav | Bin 0 -> 4488 bytes 4 files changed, 193 insertions(+) create mode 100644 dlls/dmime/tests/resource.rc create mode 100644 dlls/dmime/tests/test.wav diff --git a/dlls/dmime/tests/Makefile.in b/dlls/dmime/tests/Makefile.in index f4c47038e54..2491f9ff1b8 100644 --- a/dlls/dmime/tests/Makefile.in +++ b/dlls/dmime/tests/Makefile.in @@ -4,3 +4,5 @@ IMPORTS = user32 ole32 dsound C_SRCS = \ dmime.c \ performance.c + +RC_SRCS = resource.rc diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index e011488d126..e76949e71c7 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -55,6 +55,28 @@ static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOO } } +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, (const WCHAR *)RT_RCDATA); + 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); +} + struct test_tool { IDirectMusicTool IDirectMusicTool_iface; @@ -2244,6 +2266,151 @@ static void test_notification_pmsg(void) IDirectMusicTool_Release(tool); } +static void test_wave_pmsg(void) +{ + static const DWORD message_types[] = + { + DMUS_PMSGT_DIRTY, + DMUS_PMSGT_WAVE, + }; + IDirectMusicPerformance *performance; + IDirectMusicSegment *segment; + IDirectMusicLoader8 *loader; + IDirectMusicGraph *graph; + WCHAR test_wav[MAX_PATH]; + IDirectMusicTool *tool; + DMUS_WAVE_PMSG *wave; + MUSIC_TIME length; + DMUS_PMSG *msg; + HRESULT hr; + DWORD ret; + + hr = test_tool_create(message_types, ARRAY_SIZE(message_types), &tool); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void **)&performance); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicGraph, (void **)&graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SetGraph(performance, graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_InsertTool(graph, (IDirectMusicTool *)tool, NULL, 0, -1); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicGraph_Release(graph); + + hr = IDirectMusicPerformance8_InitAudio((IDirectMusicPerformance8 *)performance, NULL, NULL, NULL, + DMUS_APATH_SHARED_STEREOPLUSREVERB, 64, DMUS_AUDIOF_ALL, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + + load_resource(L"test.wav", test_wav); + + hr = CoCreateInstance(&CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicLoader8, (void **)&loader); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicLoader8_LoadObjectFromFile(loader, &CLSID_DirectMusicSegment, + &IID_IDirectMusicSegment, test_wav, (void **)&segment); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicLoader8_Release(loader); + + + length = 0xdeadbeef; + hr = IDirectMusicSegment_GetLength(segment, &length); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(length == 1, "got %lu\n", length); + + + /* without Download, no DMUS_PMSGT_WAVE is sent */ + + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %p\n", msg); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %p\n", msg); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 100, &msg); + ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); + ok(!msg, "got %p\n", msg); + + + /* a single DMUS_PMSGT_WAVE message is sent with punkUser set */ + + hr = IDirectMusicSegment8_Download((IDirectMusicSegment8 *)segment, (IUnknown *)performance); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %p\n", msg); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&wave); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(wave->dwType == DMUS_PMSGT_WAVE, "got %p\n", wave); + ok(!!wave->punkUser, "got %p\n", wave->punkUser); + ok(wave->rtStartOffset == 0, "got %I64d\n", wave->rtStartOffset); + ok(wave->rtDuration == 1000000, "got %I64d\n", wave->rtDuration); + ok(wave->lOffset == 0, "got %lu\n", wave->lOffset); + ok(wave->lVolume == 0, "got %lu\n", wave->lVolume); + ok(wave->lPitch == 0, "got %lu\n", wave->lPitch); + ok(wave->bFlags == 0, "got %#x\n", wave->bFlags); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)wave); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %p\n", msg); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + hr = IDirectMusicSegment8_Unload((IDirectMusicSegment8 *)segment, (IUnknown *)performance); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 100, &msg); + ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); + ok(!msg, "got %p\n", msg); + + + IDirectMusicSegment_Release(segment); + + + hr = IDirectMusicPerformance_CloseDown(performance); + ok(hr == S_OK, "got %#lx\n", hr); + + IDirectMusicPerformance_Release(performance); + IDirectMusicTool_Release(tool); +} + START_TEST(dmime) { CoInitialize(NULL); @@ -2272,6 +2439,7 @@ START_TEST(dmime) test_performance_time(); test_performance_pmsg(); test_notification_pmsg(); + test_wave_pmsg(); CoUninitialize(); } diff --git a/dlls/dmime/tests/resource.rc b/dlls/dmime/tests/resource.rc new file mode 100644 index 00000000000..d49b647b934 --- /dev/null +++ b/dlls/dmime/tests/resource.rc @@ -0,0 +1,23 @@ +/* + * 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 "windef.h" + +/* ffmpeg -f lavfi -i "sine=frequency=600" -t 0.1 -ar 44100 -f wav -acodec pcm_u8 test.wav */ +/* @makedep: test.wav */ +test.wav RCDATA test.wav diff --git a/dlls/dmime/tests/test.wav b/dlls/dmime/tests/test.wav new file mode 100644 index 0000000000000000000000000000000000000000..51d23936196da1a0452c78713c8af819259d225e GIT binary patch literal 4488 zcmWIYbaQJEWMBw)40BD(Em06)U|?VbLYFlRV9dzC!QkT=93ll2_w;k~_Y8Im;RCXL z63fy|&GbwR^b8FQ8B!8U60LxyG&DA~w6?W(c6Imk^!D}jLqT6(Z%=nuXGeQmYjaa$ zeO*m;Rb_cuX-RQWQDFfL6c!d0mz0*3S5#Kl)YdmNHMg|2cYsVnGN`|=7h+OdYfE!u zLtSl6Rb>Ulq(Zn!AcH_ARa910*VfgKI%zbNM$^)0jvFnPMvKbP3T3p~9c`R|TVA8> dC3y36v{gCU_8#q_jCPtvyOyIJ@PQhp007ieqznK6 literal 0 HcmV?d00001 From ab87518c536226a36c2e04f31cfa666852e48669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 19 Sep 2023 15:43:03 +0200 Subject: [PATCH 0926/1148] dmime/tests: Test sequence track and DMUS_NOTE_PMSG. (cherry picked from commit 47c299ce17ff0c4f37df128302292658cb7499da) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 261 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index e76949e71c7..9173297abb7 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -77,6 +77,81 @@ static void load_resource(const WCHAR *name, WCHAR *filename) CloseHandle(file); } +static void stream_begin_chunk(IStream *stream, const char type[5], ULARGE_INTEGER *offset) +{ + static const LARGE_INTEGER zero = {0}; + HRESULT hr; + hr = IStream_Write(stream, type, 4, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, offset); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IStream_Write(stream, "\0\0\0\0", 4, NULL); + ok(hr == S_OK, "got %#lx\n", hr); +} + +static void stream_end_chunk(IStream *stream, ULARGE_INTEGER *offset) +{ + static const LARGE_INTEGER zero = {0}; + ULARGE_INTEGER position; + HRESULT hr; + UINT size; + hr = IStream_Seek(stream, zero, STREAM_SEEK_CUR, &position); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IStream_Seek(stream, *(LARGE_INTEGER *)offset, STREAM_SEEK_SET, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + size = position.QuadPart - offset->QuadPart - 4; + hr = IStream_Write(stream, &size, 4, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IStream_Seek(stream, *(LARGE_INTEGER *)&position, STREAM_SEEK_SET, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IStream_Write(stream, &zero, (position.QuadPart & 1), NULL); + ok(hr == S_OK, "got %#lx\n", hr); +} + +#define CHUNK_BEGIN(stream, type) \ + do { \ + ULARGE_INTEGER __off; \ + IStream *__stream = (stream); \ + stream_begin_chunk(stream, type, &__off); \ + do + +#define CHUNK_RIFF(stream, form) \ + do { \ + ULARGE_INTEGER __off; \ + IStream *__stream = (stream); \ + stream_begin_chunk(stream, "RIFF", &__off); \ + IStream_Write(stream, form, 4, NULL); \ + do + +#define CHUNK_LIST(stream, form) \ + do { \ + ULARGE_INTEGER __off; \ + IStream *__stream = (stream); \ + stream_begin_chunk(stream, "LIST", &__off); \ + IStream_Write(stream, form, 4, NULL); \ + do + +#define CHUNK_END \ + while (0); \ + stream_end_chunk(__stream, &__off); \ + } while (0) + +#define CHUNK_DATA(stream, type, data) \ + CHUNK_BEGIN(stream, type) \ + { \ + IStream_Write((stream), &(data), sizeof(data), NULL); \ + } \ + CHUNK_END + +#define CHUNK_ARRAY(stream, type, items) \ + CHUNK_BEGIN(stream, type) \ + { \ + UINT __size = sizeof(*(items)); \ + IStream_Write((stream), &__size, 4, NULL); \ + IStream_Write((stream), &(items), sizeof(items), NULL); \ + } \ + CHUNK_END + struct test_tool { IDirectMusicTool IDirectMusicTool_iface; @@ -2411,6 +2486,191 @@ static void test_wave_pmsg(void) IDirectMusicTool_Release(tool); } +#define check_dmus_note_pmsg(a, b, c, d, e, f) check_dmus_note_pmsg_(__LINE__, a, b, c, d, e, f) +static void check_dmus_note_pmsg_(int line, DMUS_NOTE_PMSG *msg, MUSIC_TIME time, UINT chan, + UINT duration, UINT key, UINT vel) +{ + ok_(__FILE__, line)(msg->dwSize == sizeof(*msg), "got dwSize %lu\n", msg->dwSize); + ok_(__FILE__, line)(!!msg->rtTime, "got rtTime %I64u\n", msg->rtTime); + ok_(__FILE__, line)(abs(msg->mtTime - time) < 10, "got mtTime %lu\n", msg->mtTime); + ok_(__FILE__, line)(msg->dwPChannel == chan, "got dwPChannel %lu\n", msg->dwPChannel); + ok_(__FILE__, line)(!!msg->dwVirtualTrackID, "got dwVirtualTrackID %lu\n", msg->dwVirtualTrackID); + ok_(__FILE__, line)(msg->dwType == DMUS_PMSGT_NOTE, "got %#lx\n", msg->dwType); + ok_(__FILE__, line)(!msg->dwVoiceID, "got dwVoiceID %lu\n", msg->dwVoiceID); + ok_(__FILE__, line)(msg->dwGroupID == 1, "got dwGroupID %lu\n", msg->dwGroupID); + ok_(__FILE__, line)(!msg->punkUser, "got punkUser %p\n", msg->punkUser); + ok_(__FILE__, line)(msg->mtDuration == duration, "got mtDuration %lu\n", msg->mtDuration); + ok_(__FILE__, line)(msg->wMusicValue == key, "got wMusicValue %u\n", msg->wMusicValue); + ok_(__FILE__, line)(!msg->wMeasure, "got wMeasure %u\n", msg->wMeasure); + /* FIXME: ok_(__FILE__, line)(!msg->nOffset, "got nOffset %u\n", msg->nOffset); */ + /* FIXME: ok_(__FILE__, line)(!msg->bBeat, "got bBeat %u\n", msg->bBeat); */ + /* FIXME: ok_(__FILE__, line)(!msg->bGrid, "got bGrid %u\n", msg->bGrid); */ + ok_(__FILE__, line)(msg->bVelocity == vel, "got bVelocity %u\n", msg->bVelocity); + ok_(__FILE__, line)(msg->bFlags == 1, "got bFlags %#x\n", msg->bFlags); + ok_(__FILE__, line)(!msg->bTimeRange, "got bTimeRange %u\n", msg->bTimeRange); + ok_(__FILE__, line)(!msg->bDurRange, "got bDurRange %u\n", msg->bDurRange); + ok_(__FILE__, line)(!msg->bVelRange, "got bVelRange %u\n", msg->bVelRange); + ok_(__FILE__, line)(!msg->bPlayModeFlags, "got bPlayModeFlags %#x\n", msg->bPlayModeFlags); + ok_(__FILE__, line)(!msg->bSubChordLevel, "got bSubChordLevel %u\n", msg->bSubChordLevel); + ok_(__FILE__, line)(msg->bMidiValue == key, "got bMidiValue %u\n", msg->bMidiValue); + ok_(__FILE__, line)(!msg->cTranspose, "got cTranspose %u\n", msg->cTranspose); +} + +static void test_sequence_track(void) +{ + static const DWORD message_types[] = + { + DMUS_PMSGT_MIDI, + DMUS_PMSGT_NOTE, + DMUS_PMSGT_CURVE, + DMUS_PMSGT_DIRTY, + }; + static const LARGE_INTEGER zero = {0}; + IDirectMusicPerformance *performance; + IDirectMusicSegment *segment; + IDirectMusicGraph *graph; + IDirectMusicTrack *track; + IPersistStream *persist; + IDirectMusicTool *tool; + DMUS_NOTE_PMSG *note; + IStream *stream; + DMUS_PMSG *msg; + HRESULT hr; + DWORD ret; + + hr = test_tool_create(message_types, ARRAY_SIZE(message_types), &tool); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void **)&performance); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicGraph, (void **)&graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_InsertTool(graph, (IDirectMusicTool *)tool, NULL, 0, -1); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SetGraph(performance, graph); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicGraph_Release(graph); + + + /* create a segment and load a simple RIFF stream */ + + hr = CoCreateInstance(&CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicSegment, (void **)&segment); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicSegment_QueryInterface(segment, &IID_IPersistStream, (void **)&persist); + ok(hr == S_OK, "got %#lx\n", hr); + hr = CreateStreamOnHGlobal(0, TRUE, &stream); + ok(hr == S_OK, "got %#lx\n", hr); + + CHUNK_RIFF(stream, "DMSG") + { + /* set a non-zero segment length, or nothing will be played */ + DMUS_IO_SEGMENT_HEADER head = {.mtLength = 2000}; + CHUNK_DATA(stream, "segh", head); + CHUNK_DATA(stream, "guid", CLSID_DirectMusicSegment); + } + CHUNK_END; + + hr = IStream_Seek(stream, zero, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IPersistStream_Load(persist, stream); + ok(hr == S_OK, "got %#lx\n", hr); + IPersistStream_Release(persist); + IStream_Release(stream); + + + /* add a sequence track to our segment */ + + hr = CoCreateInstance(&CLSID_DirectMusicSeqTrack, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicTrack, (void **)&track); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicSegment_QueryInterface(track, &IID_IPersistStream, (void **)&persist); + ok(hr == S_OK, "got %#lx\n", hr); + hr = CreateStreamOnHGlobal(0, TRUE, &stream); + ok(hr == S_OK, "got %#lx\n", hr); + + CHUNK_BEGIN(stream, "seqt") + { + DMUS_IO_SEQ_ITEM items[] = + { + {.mtTime = 0, .mtDuration = 500, .dwPChannel = 0, .bStatus = 0x90, .bByte1 = 60, .bByte2 = 120}, + {.mtTime = 1000, .mtDuration = 200, .dwPChannel = 1, .bStatus = 0x90, .bByte1 = 50, .bByte2 = 100}, + }; + CHUNK_ARRAY(stream, "evtl", items); + } + CHUNK_END; + + hr = IStream_Seek(stream, zero, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IPersistStream_Load(persist, stream); + ok(hr == S_OK, "got %#lx\n", hr); + IPersistStream_Release(persist); + IStream_Release(stream); + + hr = IDirectMusicSegment_InsertTrack(segment, (IDirectMusicTrack *)track, 1); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicTrack_Release(track); + + + /* now play the segment, and check produced messages */ + + hr = IDirectMusicPerformance_Init(performance, NULL, 0, 0); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0x800, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬e); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_note_pmsg(note, 0, 0, 500, 60, 120); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)note); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬e); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_note_pmsg(note, 1000, 1, 200, 50, 100); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)note); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + IDirectMusicSegment_Release(segment); + + + hr = IDirectMusicPerformance_CloseDown(performance); + ok(hr == S_OK, "got %#lx\n", hr); + + IDirectMusicPerformance_Release(performance); + IDirectMusicTool_Release(tool); +} + START_TEST(dmime) { CoInitialize(NULL); @@ -2440,6 +2700,7 @@ START_TEST(dmime) test_performance_pmsg(); test_notification_pmsg(); test_wave_pmsg(); + test_sequence_track(); CoUninitialize(); } From 605eec2435b0611cbe52565e5bb48d75575e283c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 19 Sep 2023 19:16:35 +0200 Subject: [PATCH 0927/1148] dmime/tests: Test band track and DMUS_PATCH_PMSG. (cherry picked from commit 4ea18f87cc2e4184a30d0ee01885811b3d9deb5f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 483 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 483 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 9173297abb7..1266c5d053b 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -344,6 +344,213 @@ static DWORD test_tool_wait_message(IDirectMusicTool *iface, DWORD timeout, DMUS return ret; } +struct test_loader_stream +{ + IStream IStream_iface; + IDirectMusicGetLoader IDirectMusicGetLoader_iface; + LONG ref; + + IStream *stream; + IDirectMusicLoader *loader; +}; + +static struct test_loader_stream *impl_from_IStream(IStream *iface) +{ + return CONTAINING_RECORD(iface, struct test_loader_stream, IStream_iface); +} + +static HRESULT WINAPI test_loader_stream_QueryInterface(IStream *iface, REFIID iid, void **out) +{ + struct test_loader_stream *impl = impl_from_IStream(iface); + + if (IsEqualGUID(iid, &IID_IUnknown) + || IsEqualGUID(iid, &IID_IStream)) + { + IStream_AddRef(&impl->IStream_iface); + *out = &impl->IStream_iface; + return S_OK; + } + + if (IsEqualGUID(iid, &IID_IDirectMusicGetLoader)) + { + IDirectMusicGetLoader_AddRef(&impl->IDirectMusicGetLoader_iface); + *out = &impl->IDirectMusicGetLoader_iface; + return S_OK; + } + + ok(IsEqualGUID(iid, &IID_IStream), + "got iid %s\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_loader_stream_AddRef(IStream *iface) +{ + struct test_loader_stream *impl = impl_from_IStream(iface); + return InterlockedIncrement(&impl->ref); +} + +static ULONG WINAPI test_loader_stream_Release(IStream *iface) +{ + struct test_loader_stream *impl = impl_from_IStream(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + + if (!ref) + { + IDirectMusicLoader_Release(impl->loader); + IStream_Release(impl->stream); + free(impl); + } + + return ref; +} + +static HRESULT WINAPI test_loader_stream_Read(IStream *iface, void *data, ULONG size, ULONG *ret_size) +{ + struct test_loader_stream *impl = impl_from_IStream(iface); + return IStream_Read(impl->stream, data, size, ret_size); +} + +static HRESULT WINAPI test_loader_stream_Write(IStream *iface, const void *data, ULONG size, ULONG *ret_size) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_loader_stream_Seek(IStream *iface, LARGE_INTEGER offset, DWORD method, ULARGE_INTEGER *ret_offset) +{ + struct test_loader_stream *impl = impl_from_IStream(iface); + return IStream_Seek(impl->stream, offset, method, ret_offset); +} + +static HRESULT WINAPI test_loader_stream_SetSize(IStream *iface, ULARGE_INTEGER size) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_loader_stream_CopyTo(IStream *iface, IStream *dest, ULARGE_INTEGER size, + ULARGE_INTEGER *read_size, ULARGE_INTEGER *write_size) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_loader_stream_Commit(IStream *iface, DWORD flags) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_loader_stream_Revert(IStream *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_loader_stream_LockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_loader_stream_UnlockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_loader_stream_Stat(IStream *iface, STATSTG *stat, DWORD flags) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_loader_stream_Clone(IStream *iface, IStream **out) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IStreamVtbl test_loader_stream_vtbl = +{ + test_loader_stream_QueryInterface, + test_loader_stream_AddRef, + test_loader_stream_Release, + test_loader_stream_Read, + test_loader_stream_Write, + test_loader_stream_Seek, + test_loader_stream_SetSize, + test_loader_stream_CopyTo, + test_loader_stream_Commit, + test_loader_stream_Revert, + test_loader_stream_LockRegion, + test_loader_stream_UnlockRegion, + test_loader_stream_Stat, + test_loader_stream_Clone, +}; + +static struct test_loader_stream *impl_from_IDirectMusicGetLoader(IDirectMusicGetLoader *iface) +{ + return CONTAINING_RECORD(iface, struct test_loader_stream, IDirectMusicGetLoader_iface); +} + +static HRESULT WINAPI test_loader_stream_getter_QueryInterface(IDirectMusicGetLoader *iface, REFIID iid, void **out) +{ + struct test_loader_stream *impl = impl_from_IDirectMusicGetLoader(iface); + return IStream_QueryInterface(&impl->IStream_iface, iid, out); +} + +static ULONG WINAPI test_loader_stream_getter_AddRef(IDirectMusicGetLoader *iface) +{ + struct test_loader_stream *impl = impl_from_IDirectMusicGetLoader(iface); + return IStream_AddRef(&impl->IStream_iface); +} + +static ULONG WINAPI test_loader_stream_getter_Release(IDirectMusicGetLoader *iface) +{ + struct test_loader_stream *impl = impl_from_IDirectMusicGetLoader(iface); + return IStream_Release(&impl->IStream_iface); +} + +static HRESULT WINAPI test_loader_stream_getter_GetLoader(IDirectMusicGetLoader *iface, IDirectMusicLoader **ret_loader) +{ + struct test_loader_stream *impl = impl_from_IDirectMusicGetLoader(iface); + + *ret_loader = impl->loader; + IDirectMusicLoader_AddRef(impl->loader); + + return S_OK; +} + +static const IDirectMusicGetLoaderVtbl test_loader_stream_getter_vtbl = +{ + test_loader_stream_getter_QueryInterface, + test_loader_stream_getter_AddRef, + test_loader_stream_getter_Release, + test_loader_stream_getter_GetLoader, +}; + +static HRESULT test_loader_stream_create(IStream *stream, IDirectMusicLoader *loader, + IStream **ret_iface) +{ + struct test_loader_stream *obj; + + *ret_iface = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; + obj->IStream_iface.lpVtbl = &test_loader_stream_vtbl; + obj->IDirectMusicGetLoader_iface.lpVtbl = &test_loader_stream_getter_vtbl; + obj->ref = 1; + + obj->stream = stream; + IStream_AddRef(stream); + obj->loader = loader; + IDirectMusicLoader_AddRef(loader); + + *ret_iface = &obj->IStream_iface; + return S_OK; +} + static BOOL missing_dmime(void) { IDirectMusicSegment8 *dms; @@ -2671,6 +2878,281 @@ static void test_sequence_track(void) IDirectMusicTool_Release(tool); } +#define check_dmus_patch_pmsg(a, b, c, d, e) check_dmus_patch_pmsg_(__LINE__, a, b, c, d, e) +static void check_dmus_patch_pmsg_(int line, DMUS_PATCH_PMSG *msg, MUSIC_TIME time, UINT chan, + UINT bank, UINT patch) +{ + ok_(__FILE__, line)(msg->dwSize == sizeof(*msg), "got dwSize %lu\n", msg->dwSize); + ok_(__FILE__, line)(msg->rtTime != 0, "got rtTime %I64u\n", msg->rtTime); + ok_(__FILE__, line)(abs(msg->mtTime - time) < 10, "got mtTime %lu\n", msg->mtTime); + ok_(__FILE__, line)(msg->dwPChannel == chan, "got dwPChannel %lu\n", msg->dwPChannel); + ok_(__FILE__, line)(!!msg->dwVirtualTrackID, "got dwVirtualTrackID %lu\n", msg->dwVirtualTrackID); + ok_(__FILE__, line)(msg->dwType == DMUS_PMSGT_PATCH, "got %#lx\n", msg->dwType); + ok_(__FILE__, line)(!msg->dwVoiceID, "got dwVoiceID %lu\n", msg->dwVoiceID); + ok_(__FILE__, line)(msg->dwGroupID == 1, "got dwGroupID %lu\n", msg->dwGroupID); + ok_(__FILE__, line)(!msg->punkUser, "got punkUser %p\n", msg->punkUser); + ok_(__FILE__, line)(msg->byInstrument == patch, "got byInstrument %u\n", msg->byInstrument); + ok_(__FILE__, line)(msg->byMSB == bank >> 8, "got byMSB %u\n", msg->byMSB); + ok_(__FILE__, line)(msg->byLSB == (bank & 0xff), "got byLSB %u\n", msg->byLSB); +} + +static void test_band_track_play(void) +{ + static const DWORD message_types[] = + { + DMUS_PMSGT_MIDI, + DMUS_PMSGT_NOTE, + DMUS_PMSGT_SYSEX, + DMUS_PMSGT_NOTIFICATION, + DMUS_PMSGT_TEMPO, + DMUS_PMSGT_CURVE, + DMUS_PMSGT_TIMESIG, + DMUS_PMSGT_PATCH, + DMUS_PMSGT_TRANSPOSE, + DMUS_PMSGT_CHANNEL_PRIORITY, + DMUS_PMSGT_STOP, + DMUS_PMSGT_DIRTY, + DMUS_PMSGT_WAVE, + DMUS_PMSGT_LYRIC, + DMUS_PMSGT_SCRIPTLYRIC, + DMUS_PMSGT_USER, + }; + DMUS_OBJECTDESC desc = + { + .dwSize = sizeof(DMUS_OBJECTDESC), + .dwValidData = DMUS_OBJ_OBJECT | DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH, + .guidClass = CLSID_DirectMusicCollection, + .guidObject = GUID_DefaultGMCollection, + .wszFileName = L"C:\\windows\\system32\\drivers\\gm.dls", + }; + static const LARGE_INTEGER zero = {0}; + IDirectMusicPerformance *performance; + IStream *stream, *loader_stream; + IDirectMusicSegment *segment; + IDirectMusicLoader *loader; + IDirectMusicGraph *graph; + IDirectMusicTrack *track; + IPersistStream *persist; + IDirectMusicTool *tool; + DMUS_PATCH_PMSG *patch; + DMUS_PMSG *msg; + HRESULT hr; + DWORD ret; + + hr = test_tool_create(message_types, ARRAY_SIZE(message_types), &tool); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void **)&performance); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicGraph, (void **)&graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_InsertTool(graph, (IDirectMusicTool *)tool, NULL, 0, -1); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SetGraph(performance, graph); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicGraph_Release(graph); + + + /* create a segment and load a simple RIFF stream */ + + hr = CoCreateInstance(&CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicSegment, (void **)&segment); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicSegment_QueryInterface(segment, &IID_IPersistStream, (void **)&persist); + ok(hr == S_OK, "got %#lx\n", hr); + hr = CreateStreamOnHGlobal(0, TRUE, &stream); + ok(hr == S_OK, "got %#lx\n", hr); + + CHUNK_RIFF(stream, "DMSG") + { + /* set a non-zero segment length, or nothing will be played */ + DMUS_IO_SEGMENT_HEADER head = {.mtLength = 2000}; + CHUNK_DATA(stream, "segh", head); + CHUNK_DATA(stream, "guid", CLSID_DirectMusicSegment); + } + CHUNK_END; + + hr = IStream_Seek(stream, zero, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IPersistStream_Load(persist, stream); + ok(hr == S_OK, "got %#lx\n", hr); + IPersistStream_Release(persist); + IStream_Release(stream); + + + /* add a sequence track to our segment */ + + hr = CoCreateInstance(&CLSID_DirectMusicBandTrack, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicTrack, (void **)&track); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicSegment_QueryInterface(track, &IID_IPersistStream, (void **)&persist); + ok(hr == S_OK, "got %#lx\n", hr); + hr = CreateStreamOnHGlobal(0, TRUE, &stream); + ok(hr == S_OK, "got %#lx\n", hr); + + CHUNK_RIFF(stream, "DMBT") + { + DMUS_IO_BAND_TRACK_HEADER head = {.bAutoDownload = TRUE}; + + CHUNK_DATA(stream, "bdth", head); + CHUNK_LIST(stream, "lbdl") + { + CHUNK_LIST(stream, "lbnd") + { + DMUS_IO_BAND_ITEM_HEADER head = {.lBandTime = 0}; + CHUNK_DATA(stream, "bdih", head); + + CHUNK_RIFF(stream, "DMBD") + { + GUID guid = CLSID_DirectMusicBand; + CHUNK_DATA(stream, "guid", guid); + + CHUNK_LIST(stream, "lbil") + { + CHUNK_LIST(stream, "lbin") + { + DMUS_IO_INSTRUMENT head = {.dwPatch = 1, .dwPChannel = 1, .dwFlags = DMUS_IO_INST_PATCH}; + CHUNK_DATA(stream, "bins", head); + } + CHUNK_END; + } + CHUNK_END; + } + CHUNK_END; + } + CHUNK_END; + + CHUNK_LIST(stream, "lbnd") + { + DMUS_IO_BAND_ITEM_HEADER head = {.lBandTime = 1000}; + CHUNK_DATA(stream, "bdih", head); + + CHUNK_RIFF(stream, "DMBD") + { + GUID guid = CLSID_DirectMusicBand; + CHUNK_DATA(stream, "guid", guid); + + CHUNK_LIST(stream, "lbil") + { + CHUNK_LIST(stream, "lbin") + { + DMUS_IO_INSTRUMENT head = {.dwPatch = 2, .dwPChannel = 1, .dwFlags = DMUS_IO_INST_PATCH}; + CHUNK_DATA(stream, "bins", head); + } + CHUNK_END; + + CHUNK_LIST(stream, "lbin") + { + DMUS_IO_INSTRUMENT head = {.dwPatch = 3, .dwPChannel = 2, .dwFlags = DMUS_IO_INST_PATCH}; + CHUNK_DATA(stream, "bins", head); + } + CHUNK_END; + } + CHUNK_END; + } + CHUNK_END; + } + CHUNK_END; + } + CHUNK_END; + } + CHUNK_END; + + hr = IStream_Seek(stream, zero, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + /* band track requires the stream to implement IDirectMusicGetLoader */ + + hr = CoCreateInstance(&CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicLoader8, (void **)&loader); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicLoader_SetObject(loader, &desc); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + + hr = test_loader_stream_create(stream, loader, &loader_stream); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicLoader8_Release(loader); + + hr = IPersistStream_Load(persist, loader_stream); + ok(hr == S_OK, "got %#lx\n", hr); + IStream_Release(loader_stream); + + IPersistStream_Release(persist); + IStream_Release(stream); + + hr = IDirectMusicSegment_InsertTrack(segment, (IDirectMusicTrack *)track, 1); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicTrack_Release(track); + + + /* now play the segment, and check produced messages */ + + hr = IDirectMusicPerformance_Init(performance, NULL, 0, 0); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0x800, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&patch); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_patch_pmsg(patch, 0, 1, 0, 1); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)patch); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&patch); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_patch_pmsg(patch, 1000, 2, 0, 3); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)patch); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&patch); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_patch_pmsg(patch, 1000, 1, 0, 2); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)patch); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + } + + IDirectMusicSegment_Release(segment); + + + hr = IDirectMusicPerformance_CloseDown(performance); + ok(hr == S_OK, "got %#lx\n", hr); + + IDirectMusicPerformance_Release(performance); + IDirectMusicTool_Release(tool); +} + START_TEST(dmime) { CoInitialize(NULL); @@ -2701,6 +3183,7 @@ START_TEST(dmime) test_notification_pmsg(); test_wave_pmsg(); test_sequence_track(); + test_band_track_play(); CoUninitialize(); } From 07b1ffc70e1cd9b2bf8155c02076ea9f234c0231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 19 Sep 2023 17:28:30 +0200 Subject: [PATCH 0928/1148] dmusic/tests: Fixup chunk alignment in steam_end_chunk. (cherry picked from commit b6f1a1a186c270b5643bfcc910be37663b30c98d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/tests/dmusic.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 879a6f800cd..6210cb05e6c 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -85,17 +85,21 @@ static void stream_end_chunk(IStream *stream, ULARGE_INTEGER *offset) ok(hr == S_OK, "got %#lx\n", hr); hr = IStream_Seek(stream, *(LARGE_INTEGER *)&position, STREAM_SEEK_SET, NULL); ok(hr == S_OK, "got %#lx\n", hr); + hr = IStream_Write(stream, &zero, (position.QuadPart & 1), NULL); + ok(hr == S_OK, "got %#lx\n", hr); } #define CHUNK_BEGIN(stream, type) \ do { \ ULARGE_INTEGER __off; \ + IStream *__stream = (stream); \ stream_begin_chunk(stream, type, &__off); \ do #define CHUNK_RIFF(stream, form) \ do { \ ULARGE_INTEGER __off; \ + IStream *__stream = (stream); \ stream_begin_chunk(stream, "RIFF", &__off); \ IStream_Write(stream, form, 4, NULL); \ do @@ -103,13 +107,14 @@ static void stream_end_chunk(IStream *stream, ULARGE_INTEGER *offset) #define CHUNK_LIST(stream, form) \ do { \ ULARGE_INTEGER __off; \ + IStream *__stream = (stream); \ stream_begin_chunk(stream, "LIST", &__off); \ IStream_Write(stream, form, 4, NULL); \ do #define CHUNK_END \ while (0); \ - stream_end_chunk(stream, &__off); \ + stream_end_chunk(__stream, &__off); \ } while (0) #define CHUNK_DATA(stream, type, data) \ From 59a3e12010f98c16ef796f366283307262983893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 23:00:56 +0200 Subject: [PATCH 0929/1148] dmusic: Keep the original instrument patch in the entry. (cherry picked from commit ecb38bf1c9202f8621583dd41db7945c3523081b) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 10 ++++------ dlls/dmusic/tests/dmusic.c | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index b7c11dacb77..1074c4e2868 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -25,6 +25,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmusic); struct instrument_entry { struct list entry; + DWORD patch; DMUS_OBJECTDESC desc; IDirectMusicInstrument *instrument; }; @@ -176,15 +177,12 @@ static HRESULT WINAPI collection_GetInstrument(IDirectMusicCollection *iface, { struct collection *This = impl_from_IDirectMusicCollection(iface); struct instrument_entry *entry; - DWORD inst_patch; - HRESULT hr; TRACE("(%p, %lu, %p)\n", iface, patch, instrument); LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry) { - if (FAILED(hr = IDirectMusicInstrument_GetPatch(entry->instrument, &inst_patch))) return hr; - if (patch == inst_patch) + if (patch == entry->patch) { *instrument = entry->instrument; IDirectMusicInstrument_AddRef(entry->instrument); @@ -202,14 +200,13 @@ static HRESULT WINAPI collection_EnumInstrument(IDirectMusicCollection *iface, { struct collection *This = impl_from_IDirectMusicCollection(iface); struct instrument_entry *entry; - HRESULT hr; TRACE("(%p, %ld, %p, %p, %ld)\n", iface, index, patch, name, name_length); LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry) { if (index--) continue; - if (FAILED(hr = IDirectMusicInstrument_GetPatch(entry->instrument, patch))) return hr; + *patch = entry->patch; if (name) lstrcpynW(name, entry->desc.wszName, name_length); return S_OK; } @@ -239,6 +236,7 @@ static HRESULT parse_lins_list(struct collection *This, IStream *stream, struct case MAKE_IDTYPE(FOURCC_LIST, FOURCC_INS): if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY; hr = instrument_create_from_chunk(stream, &chunk, This, &entry->desc, &entry->instrument); + if (SUCCEEDED(hr)) hr = IDirectMusicInstrument_GetPatch(entry->instrument, &entry->patch); if (SUCCEEDED(hr)) list_add_tail(&This->instruments, &entry->entry); else free(entry); break; diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 6210cb05e6c..703251bb9da 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1184,7 +1184,7 @@ static void test_download_instrument(void) static const LARGE_INTEGER zero = {0}; IDirectMusicDownloadedInstrument *downloaded; IDirectMusicCollection *collection; - IDirectMusicInstrument *instrument; + IDirectMusicInstrument *instrument, *tmp_instrument; IPersistStream *persist; IDirectMusicPort *port; IDirectMusic *dmusic; @@ -1299,6 +1299,22 @@ static void test_download_instrument(void) hr = IDirectMusicCollection_GetInstrument(collection, 0x1234, &instrument); ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicInstrument_GetPatch(instrument, &patch); + ok(hr == S_OK, "got %#lx\n", hr); + ok(patch == 0x1234, "got %#lx\n", patch); + hr = IDirectMusicInstrument_SetPatch(instrument, 0x4321); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicInstrument_GetPatch(instrument, &patch); + ok(hr == S_OK, "got %#lx\n", hr); + ok(patch == 0x4321, "got %#lx\n", patch); + + hr = IDirectMusicCollection_GetInstrument(collection, 0x1234, &tmp_instrument); + ok(hr == S_OK, "got %#lx\n", hr); + ok(instrument == tmp_instrument, "got %p\n", tmp_instrument); + hr = IDirectMusicInstrument_GetPatch(tmp_instrument, &patch); + ok(hr == S_OK, "got %#lx\n", hr); + ok(patch == 0x4321, "got %#lx\n", patch); + IDirectMusicInstrument_Release(tmp_instrument); check_interface(instrument, &IID_IDirectMusicObject, FALSE); check_interface(instrument, &IID_IDirectMusicDownload, FALSE); From a09555418450a3e74aa363868a1f8aa5a0d9e9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 19:23:22 +0200 Subject: [PATCH 0930/1148] dmband: Rewrite band track lbdl/lbnd lists parsing. (cherry picked from commit 9276aec695b59ace9ea0ed2491e138593158e255) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/bandtrack.c | 238 ++++++++++++----------------------- dlls/dmband/dmband_private.h | 14 --- 2 files changed, 83 insertions(+), 169 deletions(-) diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index 1a3cde5f1db..4bcf6cd8bfe 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -25,7 +25,7 @@ WINE_DECLARE_DEBUG_CHANNEL(dmfile); struct band_entry { struct list entry; - DMUS_PRIVATE_BAND_ITEM_HEADER head; + DMUS_IO_BAND_ITEM_HEADER2 head; IDirectMusicBand *band; }; @@ -331,170 +331,94 @@ static const IDirectMusicTrack8Vtbl band_track_vtbl = band_track_Join, }; -static HRESULT load_band(struct band_track *This, IStream *pClonedStream, - IDirectMusicBand **ppBand, DMUS_PRIVATE_BAND_ITEM_HEADER *pHeader) +static HRESULT parse_lbnd_list(struct band_track *This, IStream *stream, struct chunk_entry *parent) { - HRESULT hr = E_FAIL; - IPersistStream* pPersistStream = NULL; - - hr = CoCreateInstance (&CLSID_DirectMusicBand, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicBand, (LPVOID*) ppBand); - if (FAILED(hr)) { - ERR(": could not create object\n"); - return hr; - } - /* acquire PersistStream interface */ - hr = IDirectMusicBand_QueryInterface (*ppBand, &IID_IPersistStream, (LPVOID*) &pPersistStream); - if (FAILED(hr)) { - ERR(": could not acquire IPersistStream\n"); - return hr; - } - /* load */ - hr = IPersistStream_Load (pPersistStream, pClonedStream); - if (FAILED(hr)) { - ERR(": failed to load object\n"); - return hr; - } - - /* release all loading-related stuff */ - IPersistStream_Release (pPersistStream); - - /* - * @TODO insert pBand into This - */ - if (SUCCEEDED(hr)) - { - struct band_entry *entry; - if (!(entry = calloc(1, sizeof(*entry)))) return E_OUTOFMEMORY; - entry->head = *pHeader; - entry->band = *ppBand; - IDirectMusicBand_AddRef(*ppBand); - list_add_tail(&This->bands, &entry->entry); - } + struct chunk_entry chunk = {.parent = parent}; + DMUS_IO_BAND_ITEM_HEADER2 header2; + struct band_entry *entry; + IDirectMusicBand *band; + HRESULT hr; - return S_OK; -} + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case DMUS_FOURCC_BANDITEM_CHUNK: + { + DMUS_IO_BAND_ITEM_HEADER header; -static HRESULT parse_bands_list(struct band_track *This, DMUS_PRIVATE_CHUNK *pChunk, - IStream *pStm) -{ - HRESULT hr = E_FAIL; - DMUS_PRIVATE_CHUNK Chunk; - DWORD StreamSize, ListSize[3], ListCount[3]; - LARGE_INTEGER liMove; /* used when skipping chunks */ + if (SUCCEEDED(hr = stream_chunk_get_data(stream, &chunk, &header, sizeof(header)))) + { + header2.lBandTimeLogical = header.lBandTime; + header2.lBandTimePhysical = header.lBandTime; + } - IDirectMusicBand* pBand = NULL; - DMUS_PRIVATE_BAND_ITEM_HEADER header; + break; + } - memset(&header, 0, sizeof header); + case DMUS_FOURCC_BANDITEM_CHUNK2: + hr = stream_chunk_get_data(stream, &chunk, &header2, sizeof(header2)); + break; - if (pChunk->fccID != DMUS_FOURCC_BANDS_LIST) { - ERR_(dmfile)(": %s chunk should be a BANDS list\n", debugstr_fourcc (pChunk->fccID)); - return E_FAIL; - } + case MAKE_IDTYPE(FOURCC_RIFF, DMUS_FOURCC_BAND_FORM): + { + IPersistStream *persist; - ListSize[0] = pChunk->dwSize - sizeof(FOURCC); - ListCount[0] = 0; + if (FAILED(hr = CoCreateInstance(&CLSID_DirectMusicBand, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicBand, (void **)&band))) + break; - do { - IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); - ListCount[0] += sizeof(FOURCC) + sizeof(DWORD) + Chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - switch (Chunk.fccID) { - case FOURCC_LIST: { - IStream_Read (pStm, &Chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": LIST chunk of type %s", debugstr_fourcc(Chunk.fccID)); - ListSize[1] = Chunk.dwSize - sizeof(FOURCC); - ListCount[1] = 0; - do { - IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); - ListCount[1] += sizeof(FOURCC) + sizeof(DWORD) + Chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - switch (Chunk.fccID) { - case DMUS_FOURCC_BANDITEM_CHUNK: { - DMUS_IO_BAND_ITEM_HEADER tmp_header; - TRACE_(dmfile)(": Band Item chunk v1\n"); - - IStream_Read (pStm, &tmp_header, sizeof(DMUS_IO_BAND_ITEM_HEADER), NULL); - TRACE_(dmfile)(" - lBandTime: %lu\n", tmp_header.lBandTime); - - header.dwVersion = 1; - header.lBandTime = tmp_header.lBandTime; - break; - } - case DMUS_FOURCC_BANDITEM_CHUNK2: { - DMUS_IO_BAND_ITEM_HEADER2 tmp_header2; - TRACE_(dmfile)(": Band Item chunk v2\n"); - - IStream_Read (pStm, &tmp_header2, sizeof(DMUS_IO_BAND_ITEM_HEADER2), NULL); - TRACE_(dmfile)(" - lBandTimeLogical: %lu\n", tmp_header2.lBandTimeLogical); - TRACE_(dmfile)(" - lBandTimePhysical: %lu\n", tmp_header2.lBandTimePhysical); - - header.dwVersion = 2; - header.lBandTimeLogical = tmp_header2.lBandTimeLogical; - header.lBandTimePhysical = tmp_header2.lBandTimePhysical; - break; - } - case FOURCC_RIFF: { - IStream_Read (pStm, &Chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": RIFF chunk of type %s\n", debugstr_fourcc(Chunk.fccID)); - StreamSize = Chunk.dwSize - sizeof(FOURCC); - switch (Chunk.fccID) { - case DMUS_FOURCC_BAND_FORM: { - ULARGE_INTEGER liOrigPos; - TRACE_(dmfile)(": BAND RIFF\n"); - - liMove.QuadPart = 0; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, &liOrigPos); - - liMove.QuadPart -= sizeof(FOURCC) + (sizeof(FOURCC)+sizeof(DWORD)); - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - - hr = load_band(This, pStm, &pBand, &header); - if (FAILED(hr)) { - ERR(": could not load track\n"); - return hr; - } - liMove.QuadPart = (LONGLONG)liOrigPos.QuadPart; - IStream_Seek (pStm, liMove, STREAM_SEEK_SET, NULL); - - IDirectMusicTrack_Release(pBand); pBand = NULL; /* now we can release at as it inserted */ - - /** now safe move the cursor */ - liMove.QuadPart = StreamSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = StreamSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - TRACE_(dmfile)(": ListCount[1] = %ld < ListSize[1] = %ld\n", ListCount[1], ListSize[1]); - } while (ListCount[1] < ListSize[1]); - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; + if (SUCCEEDED(hr = IDirectMusicBand_QueryInterface(band, &IID_IPersistStream, (void **)&persist))) + { + if (SUCCEEDED(hr = stream_reset_chunk_start(stream, &chunk))) + hr = IPersistStream_Load(persist, stream); + IPersistStream_Release(persist); + } + + break; + } + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; } + + if (FAILED(hr)) return hr; + + if (!(entry = calloc(1, sizeof(*entry)))) return E_OUTOFMEMORY; + entry->head = header2; + entry->band = band; + IDirectMusicBand_AddRef(band); + list_add_tail(&This->bands, &entry->entry); + + return S_OK; +} + +static HRESULT parse_lbdl_list(struct band_track *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_BAND_LIST): + hr = parse_lbnd_list(This, stream, &chunk); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; } - TRACE_(dmfile)(": ListCount[0] = %ld < ListSize[0] = %ld\n", ListCount[0], ListSize[0]); - } while (ListCount[0] < ListSize[0]); - return S_OK; + return S_OK; } static HRESULT parse_bandtrack_form(struct band_track *This, DMUS_PRIVATE_CHUNK *pChunk, @@ -560,8 +484,12 @@ static HRESULT parse_bandtrack_form(struct band_track *This, DMUS_PRIVATE_CHUNK break; } case DMUS_FOURCC_BANDS_LIST: { + static const LARGE_INTEGER zero = {0}; + struct chunk_entry chunk = {FOURCC_LIST, .size = Chunk.dwSize, .type = Chunk.fccID}; TRACE_(dmfile)(": TRACK list\n"); - hr = parse_bands_list(This, &Chunk, pStm); + IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &chunk.offset); + chunk.offset.QuadPart -= 12; + hr = parse_lbdl_list(This, pStm, &chunk); if (FAILED(hr)) return hr; break; } diff --git a/dlls/dmband/dmband_private.h b/dlls/dmband/dmband_private.h index da6f0fa302f..40516fac49b 100644 --- a/dlls/dmband/dmband_private.h +++ b/dlls/dmband/dmband_private.h @@ -47,20 +47,6 @@ extern HRESULT create_dmband(REFIID riid, void **ret_iface); extern HRESULT create_dmbandtrack(REFIID riid, void **ret_iface); - -/***************************************************************************** - * Auxiliary definitions - */ -/* i don't like M$'s idea about two different band item headers, so behold: universal one */ -typedef struct _DMUS_PRIVATE_BAND_ITEM_HEADER { - DWORD dwVersion; /* 1 or 2 */ - /* v.1 */ - MUSIC_TIME lBandTime; - /* v.2 */ - MUSIC_TIME lBandTimeLogical; - MUSIC_TIME lBandTimePhysical; -} DMUS_PRIVATE_BAND_ITEM_HEADER; - /***************************************************************************** * Misc. */ From 206c0fcb3756098b9e0c3f37864204f52134d049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 19:28:15 +0200 Subject: [PATCH 0931/1148] dmband: Rewrite band track DBMT chunk parsing. (cherry picked from commit 48f276f8360afaf4fd80a8ca432b500d1feaac94) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/bandtrack.c | 122 ++++++++++++---------------------------- 1 file changed, 35 insertions(+), 87 deletions(-) diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index 4bcf6cd8bfe..ae240ee448f 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -421,99 +421,43 @@ static HRESULT parse_lbdl_list(struct band_track *This, IStream *stream, struct return S_OK; } -static HRESULT parse_bandtrack_form(struct band_track *This, DMUS_PRIVATE_CHUNK *pChunk, - IStream *pStm) +static HRESULT parse_dmbt_chunk(struct band_track *This, IStream *stream, struct chunk_entry *parent) { - HRESULT hr = E_FAIL; - DMUS_PRIVATE_CHUNK Chunk; - DWORD StreamSize, StreamCount, ListSize[3], ListCount[3]; - LARGE_INTEGER liMove; /* used when skipping chunks */ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; - if (pChunk->fccID != DMUS_FOURCC_BANDTRACK_FORM) { - ERR_(dmfile)(": %s chunk should be a BANDTRACK form\n", debugstr_fourcc (pChunk->fccID)); - return E_FAIL; - } + if (FAILED(hr = dmobj_parsedescriptor(stream, parent, &This->dmobj.desc, + DMUS_OBJ_OBJECT|DMUS_OBJ_NAME|DMUS_OBJ_NAME_INAM|DMUS_OBJ_VERSION)) + || FAILED(hr = stream_reset_chunk_data(stream, parent))) + return hr; - StreamSize = pChunk->dwSize - sizeof(FOURCC); - StreamCount = 0; + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case DMUS_FOURCC_GUID_CHUNK: + case DMUS_FOURCC_VERSION_CHUNK: + case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_UNFO_LIST): + /* already parsed by dmobj_parsedescriptor */ + break; - do { - IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); - StreamCount += sizeof(FOURCC) + sizeof(DWORD) + Chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - - hr = IDirectMusicUtils_IPersistStream_ParseDescGeneric(&Chunk, pStm, &This->dmobj.desc); - if (FAILED(hr)) return hr; + case DMUS_FOURCC_BANDTRACK_CHUNK: + hr = stream_chunk_get_data(stream, &chunk, &This->header, sizeof(This->header)); + break; - if (hr == S_FALSE) { - switch (Chunk.fccID) { - case DMUS_FOURCC_BANDTRACK_CHUNK: { - TRACE_(dmfile)(": BandTrack chunk\n"); - IStream_Read (pStm, &This->header, sizeof(DMUS_IO_BAND_TRACK_HEADER), NULL); - TRACE_(dmfile)(" - bAutoDownload: %u\n", This->header.bAutoDownload); - break; - } - case FOURCC_LIST: { - IStream_Read (pStm, &Chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": LIST chunk of type %s", debugstr_fourcc(Chunk.fccID)); - ListSize[0] = Chunk.dwSize - sizeof(FOURCC); - ListCount[0] = 0; - switch (Chunk.fccID) { - case DMUS_FOURCC_UNFO_LIST: { - TRACE_(dmfile)(": UNFO list\n"); - do { - IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); - ListCount[0] += sizeof(FOURCC) + sizeof(DWORD) + Chunk.dwSize; - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - - hr = IDirectMusicUtils_IPersistStream_ParseUNFOGeneric(&Chunk, pStm, &This->dmobj.desc); - if (FAILED(hr)) return hr; - - if (hr == S_FALSE) { - switch (Chunk.fccID) { - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - } - TRACE_(dmfile)(": ListCount[0] = %ld < ListSize[0] = %ld\n", ListCount[0], ListSize[0]); - } while (ListCount[0] < ListSize[0]); - break; - } - case DMUS_FOURCC_BANDS_LIST: { - static const LARGE_INTEGER zero = {0}; - struct chunk_entry chunk = {FOURCC_LIST, .size = Chunk.dwSize, .type = Chunk.fccID}; - TRACE_(dmfile)(": TRACK list\n"); - IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &chunk.offset); - chunk.offset.QuadPart -= 12; - hr = parse_lbdl_list(This, pStm, &chunk); - if (FAILED(hr)) return hr; - break; - } - default: { - TRACE_(dmfile)(": unknown (skipping)\n"); - liMove.QuadPart = Chunk.dwSize - sizeof(FOURCC); - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - break; - } - default: { - TRACE_(dmfile)(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } + case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_BANDS_LIST): + hr = parse_lbdl_list(This, stream, &chunk); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; } - TRACE_(dmfile)(": StreamCount[0] = %ld < StreamSize[0] = %ld\n", StreamCount, StreamSize); - } while (StreamCount < StreamSize); - return S_OK; + return hr; } static inline struct band_track *impl_from_IPersistStream(IPersistStream *iface) @@ -538,8 +482,12 @@ static HRESULT WINAPI band_track_persist_stream_Load(IPersistStream *iface, IStr TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); switch (Chunk.fccID) { case DMUS_FOURCC_BANDTRACK_FORM: { + static const LARGE_INTEGER zero = {0}; + struct chunk_entry chunk = {FOURCC_LIST, .size = Chunk.dwSize, .type = Chunk.fccID}; TRACE_(dmfile)(": Band track form\n"); - hr = parse_bandtrack_form(This, &Chunk, pStm); + IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &chunk.offset); + chunk.offset.QuadPart -= 12; + hr = parse_dmbt_chunk(This, pStm, &chunk); if (FAILED(hr)) return hr; break; } From c98f60b8c881c2814d02cbc62a135ff242154591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 19:29:56 +0200 Subject: [PATCH 0932/1148] dmband: Rewrite band track IPersistStream_Load. (cherry picked from commit a955339b3c00012f799ad53bdcaed6ee0491c2da) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/Makefile.in | 3 +- dlls/dmband/bandtrack.c | 85 +++++++------- dlls/dmband/dmband_main.c | 1 + dlls/dmband/dmband_private.h | 6 - dlls/dmband/dmutils.c | 219 ----------------------------------- dlls/dmband/dmutils.h | 37 ------ 6 files changed, 45 insertions(+), 306 deletions(-) delete mode 100644 dlls/dmband/dmutils.c delete mode 100644 dlls/dmband/dmutils.h diff --git a/dlls/dmband/Makefile.in b/dlls/dmband/Makefile.in index ef6b6a44c60..2c6a59dad4c 100644 --- a/dlls/dmband/Makefile.in +++ b/dlls/dmband/Makefile.in @@ -6,8 +6,7 @@ C_SRCS = \ band.c \ bandtrack.c \ dmband_main.c \ - dmobject.c \ - dmutils.c + dmobject.c IDL_SRCS = dmband.idl diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index ae240ee448f..ab12e1f1355 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -20,7 +20,6 @@ #include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmband); -WINE_DECLARE_DEBUG_CHANNEL(dmfile); struct band_entry { @@ -465,51 +464,53 @@ static inline struct band_track *impl_from_IPersistStream(IPersistStream *iface) return CONTAINING_RECORD(iface, struct band_track, dmobj.IPersistStream_iface); } -static HRESULT WINAPI band_track_persist_stream_Load(IPersistStream *iface, IStream *pStm) +static HRESULT WINAPI band_track_persist_stream_Load(IPersistStream *iface, IStream *stream) { - struct band_track *This = impl_from_IPersistStream(iface); - DMUS_PRIVATE_CHUNK Chunk; - LARGE_INTEGER liMove; - HRESULT hr; - - TRACE("(%p, %p): Loading\n", This, pStm); - - IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - switch (Chunk.fccID) { - case FOURCC_RIFF: { - IStream_Read (pStm, &Chunk.fccID, sizeof(FOURCC), NULL); - TRACE_(dmfile)(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - switch (Chunk.fccID) { - case DMUS_FOURCC_BANDTRACK_FORM: { - static const LARGE_INTEGER zero = {0}; - struct chunk_entry chunk = {FOURCC_LIST, .size = Chunk.dwSize, .type = Chunk.fccID}; - TRACE_(dmfile)(": Band track form\n"); - IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &chunk.offset); - chunk.offset.QuadPart -= 12; - hr = parse_dmbt_chunk(This, pStm, &chunk); - if (FAILED(hr)) return hr; - break; - } - default: { - TRACE_(dmfile)(": unexpected chunk; loading failed)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - return E_FAIL; + struct band_track *This = impl_from_IPersistStream(iface); + struct chunk_entry chunk = {0}; + HRESULT hr; + + TRACE("(%p, %p)\n", This, stream); + + if ((hr = stream_get_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_RIFF, DMUS_FOURCC_BANDTRACK_FORM): + hr = parse_dmbt_chunk(This, stream, &chunk); + break; + + default: + WARN("Invalid band track chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + hr = DMUS_E_UNSUPPORTED_STREAM; + break; + } } + + if (FAILED(hr)) return hr; + + if (TRACE_ON(dmband)) + { + struct band_entry *entry; + int i = 0; + + TRACE("Loaded DirectMusicBandTrack %p\n", This); + dump_DMUS_OBJECTDESC(&This->dmobj.desc); + + TRACE(" - header:\n"); + TRACE(" - bAutoDownload: %u\n", This->header.bAutoDownload); + + TRACE(" - bands:\n"); + LIST_FOR_EACH_ENTRY(entry, &This->bands, struct band_entry, entry) + { + TRACE(" - band[%u]: %p\n", i++, entry->band); + TRACE(" - lBandTimeLogical: %ld\n", entry->head.lBandTimeLogical); + TRACE(" - lBandTimePhysical: %ld\n", entry->head.lBandTimePhysical); + } } - TRACE_(dmfile)(": reading finished\n"); - break; - } - default: { - TRACE_(dmfile)(": unexpected chunk; loading failed)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); /* skip the rest of the chunk */ - return E_FAIL; - } - } - return S_OK; + stream_skip_chunk(stream, &chunk); + return S_OK; } static const IPersistStreamVtbl band_track_persist_stream_vtbl = diff --git a/dlls/dmband/dmband_main.c b/dlls/dmband/dmband_main.c index c032a931f31..55f979e3fbc 100644 --- a/dlls/dmband/dmband_main.c +++ b/dlls/dmband/dmband_main.c @@ -17,6 +17,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "initguid.h" #include "dmband_private.h" #include "rpcproxy.h" #include "dmobject.h" diff --git a/dlls/dmband/dmband_private.h b/dlls/dmband/dmband_private.h index 40516fac49b..444fe5ccf55 100644 --- a/dlls/dmband/dmband_private.h +++ b/dlls/dmband/dmband_private.h @@ -47,10 +47,4 @@ extern HRESULT create_dmband(REFIID riid, void **ret_iface); extern HRESULT create_dmbandtrack(REFIID riid, void **ret_iface); -/***************************************************************************** - * Misc. - */ - -#include "dmutils.h" - #endif /* __WINE_DMBAND_PRIVATE_H */ diff --git a/dlls/dmband/dmutils.c b/dlls/dmband/dmutils.c deleted file mode 100644 index 01cec0f4c64..00000000000 --- a/dlls/dmband/dmutils.c +++ /dev/null @@ -1,219 +0,0 @@ -/* Debug and Helper Functions - * - * Copyright (C) 2004 Rok Mandeljc - * Copyright (C) 2004 Raphael Junqueira - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#define COBJMACROS - - -#include -#include -#include - -#include "windef.h" -#include "winbase.h" -#include "winnt.h" -#include "wingdi.h" -#include "winuser.h" - -#include "wine/debug.h" -#include "objbase.h" - -#include "initguid.h" -#include "dmusici.h" -#include "dmusicf.h" -#include "dmusics.h" - -#include "dmutils.h" -#include "dmobject.h" - -WINE_DEFAULT_DEBUG_CHANNEL(dmfile); - -HRESULT IDirectMusicUtils_IPersistStream_ParseDescGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc) { - - switch (pChunk->fccID) { - case DMUS_FOURCC_GUID_CHUNK: { - TRACE(": GUID chunk\n"); - pDesc->dwValidData |= DMUS_OBJ_OBJECT; - IStream_Read (pStm, &pDesc->guidObject, pChunk->dwSize, NULL); - break; - } - case DMUS_FOURCC_DATE_CHUNK: { - TRACE(": file date chunk\n"); - pDesc->dwValidData |= DMUS_OBJ_DATE; - IStream_Read (pStm, &pDesc->ftDate, pChunk->dwSize, NULL); - break; - } - case DMUS_FOURCC_NAME_CHUNK: { - TRACE(": name chunk\n"); - pDesc->dwValidData |= DMUS_OBJ_NAME; - IStream_Read (pStm, pDesc->wszName, pChunk->dwSize, NULL); - break; - } - case DMUS_FOURCC_FILE_CHUNK: { - TRACE(": file name chunk\n"); - pDesc->dwValidData |= DMUS_OBJ_FILENAME; - IStream_Read (pStm, pDesc->wszFileName, pChunk->dwSize, NULL); - break; - } - case DMUS_FOURCC_VERSION_CHUNK: { - TRACE(": version chunk\n"); - pDesc->dwValidData |= DMUS_OBJ_VERSION; - IStream_Read (pStm, &pDesc->vVersion, pChunk->dwSize, NULL); - break; - } - case DMUS_FOURCC_CATEGORY_CHUNK: { - TRACE(": category chunk\n"); - pDesc->dwValidData |= DMUS_OBJ_CATEGORY; - IStream_Read (pStm, pDesc->wszCategory, pChunk->dwSize, NULL); - break; - } - default: - /* not handled */ - return S_FALSE; - } - - return S_OK; -} - -HRESULT IDirectMusicUtils_IPersistStream_ParseUNFOGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc) { - - LARGE_INTEGER liMove; /* used when skipping chunks */ - - /** - * don't ask me why, but M$ puts INFO elements in UNFO list sometimes - * (though strings seem to be valid unicode) - */ - switch (pChunk->fccID) { - - case mmioFOURCC('I','N','A','M'): - case DMUS_FOURCC_UNAM_CHUNK: { - TRACE(": name chunk\n"); - pDesc->dwValidData |= DMUS_OBJ_NAME; - IStream_Read (pStm, pDesc->wszName, pChunk->dwSize, NULL); - TRACE(" - wszName: %s\n", debugstr_w(pDesc->wszName)); - break; - } - - case mmioFOURCC('I','A','R','T'): - case DMUS_FOURCC_UART_CHUNK: { - TRACE(": artist chunk (ignored)\n"); - liMove.QuadPart = pChunk->dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - case mmioFOURCC('I','C','O','P'): - case DMUS_FOURCC_UCOP_CHUNK: { - TRACE(": copyright chunk (ignored)\n"); - liMove.QuadPart = pChunk->dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - case mmioFOURCC('I','S','B','J'): - case DMUS_FOURCC_USBJ_CHUNK: { - TRACE(": subject chunk (ignored)\n"); - liMove.QuadPart = pChunk->dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - case mmioFOURCC('I','C','M','T'): - case DMUS_FOURCC_UCMT_CHUNK: { - TRACE(": comment chunk (ignored)\n"); - liMove.QuadPart = pChunk->dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - default: - /* not handled */ - return S_FALSE; - } - - return S_OK; -} - -HRESULT IDirectMusicUtils_IPersistStream_ParseReference (LPPERSISTSTREAM iface, DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, IDirectMusicObject** ppObject) { - DMUS_PRIVATE_CHUNK Chunk; - DWORD ListSize[3], ListCount[3]; - LARGE_INTEGER liMove; /* used when skipping chunks */ - HRESULT hr; - - DMUS_IO_REFERENCE ref; - DMUS_OBJECTDESC ref_desc; - - memset(&ref, 0, sizeof(ref)); - memset(&ref_desc, 0, sizeof(ref_desc)); - - if (pChunk->fccID != DMUS_FOURCC_REF_LIST) { - ERR_(dmfile)(": %s chunk should be a REF list\n", debugstr_fourcc (pChunk->fccID)); - return E_FAIL; - } - - ListSize[0] = pChunk->dwSize - sizeof(FOURCC); - ListCount[0] = 0; - - do { - IStream_Read (pStm, &Chunk, sizeof(FOURCC)+sizeof(DWORD), NULL); - ListCount[0] += sizeof(FOURCC) + sizeof(DWORD) + Chunk.dwSize; - TRACE(": %s chunk (size = %ld)", debugstr_fourcc (Chunk.fccID), Chunk.dwSize); - - hr = IDirectMusicUtils_IPersistStream_ParseDescGeneric(&Chunk, pStm, &ref_desc); - if (FAILED(hr)) return hr; - - if (hr == S_FALSE) { - switch (Chunk.fccID) { - case DMUS_FOURCC_REF_CHUNK: { - TRACE(": Reference chunk\n"); - if (Chunk.dwSize != sizeof(DMUS_IO_REFERENCE)) return E_FAIL; - IStream_Read (pStm, &ref, sizeof(DMUS_IO_REFERENCE), NULL); - TRACE(" - guidClassID: %s\n", debugstr_dmguid(&ref.guidClassID)); - TRACE(" - dwValidData: %lu\n", ref.dwValidData); - break; - } - default: { - TRACE(": unknown chunk (irrelevant & skipping)\n"); - liMove.QuadPart = Chunk.dwSize; - IStream_Seek (pStm, liMove, STREAM_SEEK_CUR, NULL); - break; - } - } - } - TRACE(": ListCount[0] = %ld < ListSize[0] = %ld\n", ListCount[0], ListSize[0]); - } while (ListCount[0] < ListSize[0]); - - ref_desc.dwValidData |= DMUS_OBJ_CLASS; - ref_desc.guidClass = ref.guidClassID; - - TRACE("** DM Reference Begin of Load ***\n"); - TRACE("With Desc:\n"); - dump_DMUS_OBJECTDESC(&ref_desc); - - { - LPDIRECTMUSICGETLOADER pGetLoader = NULL; - LPDIRECTMUSICLOADER pLoader = NULL; - - IStream_QueryInterface (pStm, &IID_IDirectMusicGetLoader, (LPVOID*)&pGetLoader); - IDirectMusicGetLoader_GetLoader (pGetLoader, &pLoader); - IDirectMusicGetLoader_Release (pGetLoader); - - hr = IDirectMusicLoader_GetObject (pLoader, &ref_desc, &IID_IDirectMusicObject, (LPVOID*)ppObject); - IDirectMusicLoader_Release (pLoader); /* release loader */ - } - TRACE("** DM Reference End of Load ***\n"); - - return hr; -} diff --git a/dlls/dmband/dmutils.h b/dlls/dmband/dmutils.h deleted file mode 100644 index 7f9b98cf310..00000000000 --- a/dlls/dmband/dmutils.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Debug and Helper Functions - * - * Copyright (C) 2003-2004 Rok Mandeljc - * Copyright (C) 2003-2004 Raphael Junqueira - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#ifndef __WINE_DMUTILS_H -#define __WINE_DMUTILS_H - -/* for simpler reading */ -typedef struct _DMUS_PRIVATE_CHUNK { - FOURCC fccID; /* FOURCC ID of the chunk */ - DWORD dwSize; /* size of the chunk */ -} DMUS_PRIVATE_CHUNK, *LPDMUS_PRIVATE_CHUNK; - -/** - * Parsing utilities - */ -extern HRESULT IDirectMusicUtils_IPersistStream_ParseDescGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc); -extern HRESULT IDirectMusicUtils_IPersistStream_ParseUNFOGeneric (DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, LPDMUS_OBJECTDESC pDesc); -extern HRESULT IDirectMusicUtils_IPersistStream_ParseReference (LPPERSISTSTREAM iface, DMUS_PRIVATE_CHUNK* pChunk, IStream* pStm, IDirectMusicObject** ppObject); - -#endif /* __WINE_DMUTILS_H */ From e306168e3feb46e7ef98d6f81c758917e0026913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 15 Sep 2023 11:45:59 +0200 Subject: [PATCH 0933/1148] dmime: Get rid of the IDirectMusicSeqTrack typedef. (cherry picked from commit d3742ab843ba26ac4c41f526d54af8b1ce8176f1) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/seqtrack.c | 54 ++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/dlls/dmime/seqtrack.c b/dlls/dmime/seqtrack.c index 0e496a5131c..16483c85457 100644 --- a/dlls/dmime/seqtrack.c +++ b/dlls/dmime/seqtrack.c @@ -1,5 +1,4 @@ -/* IDirectMusicSeqTrack Implementation - * +/* * Copyright (C) 2003-2004 Rok Mandeljc * * This program is free software; you can redistribute it and/or @@ -22,25 +21,22 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmime); -/***************************************************************************** - * IDirectMusicSeqTrack implementation - */ -typedef struct IDirectMusicSeqTrack { +struct sequence_track +{ IDirectMusicTrack8 IDirectMusicTrack8_iface; struct dmobject dmobj; /* IPersistStream only */ LONG ref; -} IDirectMusicSeqTrack; +}; -/* IDirectMusicSeqTrack IDirectMusicTrack8 part: */ -static inline IDirectMusicSeqTrack *impl_from_IDirectMusicTrack8(IDirectMusicTrack8 *iface) +static inline struct sequence_track *impl_from_IDirectMusicTrack8(IDirectMusicTrack8 *iface) { - return CONTAINING_RECORD(iface, IDirectMusicSeqTrack, IDirectMusicTrack8_iface); + return CONTAINING_RECORD(iface, struct sequence_track, IDirectMusicTrack8_iface); } static HRESULT WINAPI sequence_track_QueryInterface(IDirectMusicTrack8 *iface, REFIID riid, void **ret_iface) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ret_iface); @@ -62,7 +58,7 @@ static HRESULT WINAPI sequence_track_QueryInterface(IDirectMusicTrack8 *iface, R static ULONG WINAPI sequence_track_AddRef(IDirectMusicTrack8 *iface) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); LONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) ref=%ld\n", This, ref); @@ -72,7 +68,7 @@ static ULONG WINAPI sequence_track_AddRef(IDirectMusicTrack8 *iface) static ULONG WINAPI sequence_track_Release(IDirectMusicTrack8 *iface) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); LONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) ref=%ld\n", This, ref); @@ -86,7 +82,7 @@ static ULONG WINAPI sequence_track_Release(IDirectMusicTrack8 *iface) static HRESULT WINAPI sequence_track_Init(IDirectMusicTrack8 *iface, IDirectMusicSegment *pSegment) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); FIXME("(%p, %p): stub\n", This, pSegment); return S_OK; } @@ -95,14 +91,14 @@ static HRESULT WINAPI sequence_track_InitPlay(IDirectMusicTrack8 *iface, IDirectMusicSegmentState *pSegmentState, IDirectMusicPerformance *pPerformance, void **ppStateData, DWORD dwVirtualTrack8ID, DWORD dwFlags) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); FIXME("(%p, %p, %p, %p, %ld, %ld): stub\n", This, pSegmentState, pPerformance, ppStateData, dwVirtualTrack8ID, dwFlags); return S_OK; } static HRESULT WINAPI sequence_track_EndPlay(IDirectMusicTrack8 *iface, void *pStateData) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); FIXME("(%p, %p): stub\n", This, pStateData); return S_OK; } @@ -111,7 +107,7 @@ static HRESULT WINAPI sequence_track_Play(IDirectMusicTrack8 *iface, void *pStat MUSIC_TIME mtStart, MUSIC_TIME mtEnd, MUSIC_TIME mtOffset, DWORD dwFlags, IDirectMusicPerformance *pPerf, IDirectMusicSegmentState *pSegSt, DWORD dwVirtualID) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); FIXME("(%p, %p, %ld, %ld, %ld, %ld, %p, %p, %ld): stub\n", This, pStateData, mtStart, mtEnd, mtOffset, dwFlags, pPerf, pSegSt, dwVirtualID); return S_OK; } @@ -119,7 +115,7 @@ static HRESULT WINAPI sequence_track_Play(IDirectMusicTrack8 *iface, void *pStat static HRESULT WINAPI sequence_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time, MUSIC_TIME *next, void *param) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s, %ld, %p, %p): method not implemented\n", This, debugstr_dmguid(type), time, next, param); @@ -129,7 +125,7 @@ static HRESULT WINAPI sequence_track_GetParam(IDirectMusicTrack8 *iface, REFGUID static HRESULT WINAPI sequence_track_SetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time, void *param) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s, %ld, %p): method not implemented\n", This, debugstr_dmguid(type), time, param); return E_NOTIMPL; @@ -137,7 +133,7 @@ static HRESULT WINAPI sequence_track_SetParam(IDirectMusicTrack8 *iface, REFGUID static HRESULT WINAPI sequence_track_IsParamSupported(IDirectMusicTrack8 *iface, REFGUID type) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s): method not implemented\n", This, debugstr_dmguid(type)); return E_NOTIMPL; @@ -146,7 +142,7 @@ static HRESULT WINAPI sequence_track_IsParamSupported(IDirectMusicTrack8 *iface, static HRESULT WINAPI sequence_track_AddNotificationType(IDirectMusicTrack8 *iface, REFGUID notiftype) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s): method not implemented\n", This, debugstr_dmguid(notiftype)); return E_NOTIMPL; @@ -155,7 +151,7 @@ static HRESULT WINAPI sequence_track_AddNotificationType(IDirectMusicTrack8 *ifa static HRESULT WINAPI sequence_track_RemoveNotificationType(IDirectMusicTrack8 *iface, REFGUID notiftype) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s): method not implemented\n", This, debugstr_dmguid(notiftype)); return E_NOTIMPL; @@ -164,7 +160,7 @@ static HRESULT WINAPI sequence_track_RemoveNotificationType(IDirectMusicTrack8 * static HRESULT WINAPI sequence_track_Clone(IDirectMusicTrack8 *iface, MUSIC_TIME mtStart, MUSIC_TIME mtEnd, IDirectMusicTrack **ppTrack) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); FIXME("(%p, %ld, %ld, %p): stub\n", This, mtStart, mtEnd, ppTrack); return S_OK; } @@ -173,7 +169,7 @@ static HRESULT WINAPI sequence_track_PlayEx(IDirectMusicTrack8 *iface, void *pSt REFERENCE_TIME rtStart, REFERENCE_TIME rtEnd, REFERENCE_TIME rtOffset, DWORD dwFlags, IDirectMusicPerformance *pPerf, IDirectMusicSegmentState *pSegSt, DWORD dwVirtualID) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); FIXME("(%p, %p, 0x%s, 0x%s, 0x%s, %ld, %p, %p, %ld): stub\n", This, pStateData, wine_dbgstr_longlong(rtStart), wine_dbgstr_longlong(rtEnd), wine_dbgstr_longlong(rtOffset), dwFlags, pPerf, pSegSt, dwVirtualID); return S_OK; @@ -182,7 +178,7 @@ static HRESULT WINAPI sequence_track_PlayEx(IDirectMusicTrack8 *iface, void *pSt static HRESULT WINAPI sequence_track_GetParamEx(IDirectMusicTrack8 *iface, REFGUID type, REFERENCE_TIME time, REFERENCE_TIME *next, void *param, void *state, DWORD flags) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s, %s, %p, %p, %p, %lx): method not implemented\n", This, debugstr_dmguid(type), wine_dbgstr_longlong(time), next, param, state, flags); @@ -192,7 +188,7 @@ static HRESULT WINAPI sequence_track_GetParamEx(IDirectMusicTrack8 *iface, REFGU static HRESULT WINAPI sequence_track_SetParamEx(IDirectMusicTrack8 *iface, REFGUID type, REFERENCE_TIME time, void *param, void *state, DWORD flags) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s, %s, %p, %p, %lx): method not implemented\n", This, debugstr_dmguid(type), wine_dbgstr_longlong(time), param, state, flags); @@ -202,7 +198,7 @@ static HRESULT WINAPI sequence_track_SetParamEx(IDirectMusicTrack8 *iface, REFGU static HRESULT WINAPI sequence_track_Compose(IDirectMusicTrack8 *iface, IUnknown *context, DWORD trackgroup, IDirectMusicTrack **track) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %p, %ld, %p): method not implemented\n", This, context, trackgroup, track); return E_NOTIMPL; @@ -211,7 +207,7 @@ static HRESULT WINAPI sequence_track_Compose(IDirectMusicTrack8 *iface, IUnknown static HRESULT WINAPI sequence_track_Join(IDirectMusicTrack8 *iface, IDirectMusicTrack *newtrack, MUSIC_TIME join, IUnknown *context, DWORD trackgroup, IDirectMusicTrack **resulttrack) { - IDirectMusicSeqTrack *This = impl_from_IDirectMusicTrack8(iface); + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %p, %ld, %p, %ld, %p): method not implemented\n", This, newtrack, join, context, trackgroup, resulttrack); return E_NOTIMPL; @@ -258,7 +254,7 @@ static const IPersistStreamVtbl persiststream_vtbl = { /* for ClassFactory */ HRESULT create_dmseqtrack(REFIID lpcGUID, void **ppobj) { - IDirectMusicSeqTrack *track; + struct sequence_track *track; HRESULT hr; *ppobj = NULL; From 3931ff22a67f8ed3ba32606854ade670522dfc61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 15 Sep 2023 11:46:23 +0200 Subject: [PATCH 0934/1148] dmime: Implement DirectMusicSeqTrack IPersistStream_Load. (cherry picked from commit 27ab696752294f6b847515fe0bd905d448d26e64) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/seqtrack.c | 132 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) diff --git a/dlls/dmime/seqtrack.c b/dlls/dmime/seqtrack.c index 16483c85457..ae2f002da92 100644 --- a/dlls/dmime/seqtrack.c +++ b/dlls/dmime/seqtrack.c @@ -26,6 +26,12 @@ struct sequence_track IDirectMusicTrack8 IDirectMusicTrack8_iface; struct dmobject dmobj; /* IPersistStream only */ LONG ref; + + DMUS_IO_SEQ_ITEM *items; + unsigned int count; + + DMUS_IO_CURVE_ITEM *curve_items; + unsigned int curve_count; }; static inline struct sequence_track *impl_from_IDirectMusicTrack8(IDirectMusicTrack8 *iface) @@ -234,10 +240,132 @@ static const IDirectMusicTrack8Vtbl dmtrack8_vtbl = { sequence_track_Join }; +static HRESULT parse_curl_list(struct sequence_track *This, IStream *stream, struct chunk_entry *chunk) +{ + HRESULT hr; + UINT i; + + if (FAILED(hr = stream_chunk_get_array(stream, chunk, (void **)&This->curve_items, + &This->curve_count, sizeof(*This->curve_items)))) + { + /* try again with the older DMUS_IO_CURVE_ITEM size */ + UINT size = offsetof(DMUS_IO_CURVE_ITEM, wParamType); + BYTE *buffer; + + if (FAILED(hr = stream_reset_chunk_data(stream, chunk))) return hr; + if (FAILED(hr = stream_chunk_get_array(stream, chunk, (void **)&buffer, + &This->curve_count, size))) + return hr; + + if (!(This->curve_items = calloc(This->curve_count, sizeof(*This->curve_items)))) return E_OUTOFMEMORY; + for (i = 0; i < This->curve_count; i++) memcpy(This->curve_items + i, buffer + size * i, size); + free(buffer); + } + + return S_OK; +} + +static HRESULT parse_seqt_chunk(struct sequence_track *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case DMUS_FOURCC_SEQ_LIST: + hr = stream_chunk_get_array(stream, &chunk, (void **)&This->items, + &This->count, sizeof(*This->items)); + break; + + case DMUS_FOURCC_CURVE_LIST: + hr = parse_curl_list(This, stream, &chunk); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + return hr; +} + +static inline struct sequence_track *impl_from_IPersistStream(IPersistStream *iface) +{ + return CONTAINING_RECORD(iface, struct sequence_track, dmobj.IPersistStream_iface); +} + static HRESULT WINAPI track_IPersistStream_Load(IPersistStream *iface, IStream *stream) { - FIXME(": Loading not implemented yet\n"); - return S_OK; + struct sequence_track *This = impl_from_IPersistStream(iface); + struct chunk_entry chunk = {0}; + HRESULT hr; + + TRACE("(%p, %p)\n", This, stream); + + if ((hr = stream_get_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case DMUS_FOURCC_SEQ_TRACK: + hr = parse_seqt_chunk(This, stream, &chunk); + break; + + default: + WARN("Invalid seq track chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + hr = DMUS_E_UNSUPPORTED_STREAM; + break; + } + } + + if (FAILED(hr)) return hr; + + if (TRACE_ON(dmime)) + { + UINT i; + + TRACE("Loaded DirectMusicSeqTrack %p\n", This); + + TRACE("- %u items:\n", This->count); + for (i = 0; i < This->count; i++) + { + TRACE(" - DMUS_IO_SEQ_ITEM[%u]\n", i); + TRACE(" - mtTime: %ld\n", This->items[i].mtTime); + TRACE(" - mtDuration: %ld\n", This->items[i].mtDuration); + TRACE(" - dwPChannel: %ld\n", This->items[i].dwPChannel); + TRACE(" - nOffset: %d\n", This->items[i].nOffset); + TRACE(" - bStatus: %d\n", This->items[i].bStatus); + TRACE(" - bByte1: %#x\n", This->items[i].bByte1); + TRACE(" - bByte2: %#x\n", This->items[i].bByte2); + } + + TRACE("- %u curves:\n", This->curve_count); + for (i = 0; i < This->curve_count; i++) + { + TRACE(" - DMUS_IO_CURVE_ITEM[%u]\n", i); + TRACE(" - mtStart: %ld\n", This->curve_items[i].mtStart); + TRACE(" - mtDuration: %ld\n", This->curve_items[i].mtDuration); + TRACE(" - mtResetDuration: %ld\n", This->curve_items[i].mtResetDuration); + TRACE(" - dwPChannel: %ld\n", This->curve_items[i].dwPChannel); + TRACE(" - nOffset: %d\n", This->curve_items[i].nOffset); + TRACE(" - nStartValue: %d\n", This->curve_items[i].nStartValue); + TRACE(" - nEndValue: %d\n", This->curve_items[i].nEndValue); + TRACE(" - nResetValue: %d\n", This->curve_items[i].nResetValue); + TRACE(" - bType: %d\n", This->curve_items[i].bType); + TRACE(" - bCurveShape: %d\n", This->curve_items[i].bCurveShape); + TRACE(" - bCCData: %d\n", This->curve_items[i].bCCData); + TRACE(" - bFlags: %d\n", This->curve_items[i].bFlags); + TRACE(" - wParamType: %d\n", This->curve_items[i].wParamType); + TRACE(" - wMergeIndex: %d\n", This->curve_items[i].wMergeIndex); + } + } + + stream_skip_chunk(stream, &chunk); + return S_OK; } static const IPersistStreamVtbl persiststream_vtbl = { From 585b9f93463924d3417915c01851cba62a908f3f Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 12 Jul 2023 15:47:09 +0200 Subject: [PATCH 0935/1148] dmloader: Use nameless unions/structs. (cherry picked from commit e0dd29fffe8df167de6cec219722ee3e8d776471) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/loaderstream.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dlls/dmloader/loaderstream.c b/dlls/dmloader/loaderstream.c index c2c5105a5e9..5d6c5ad3e25 100644 --- a/dlls/dmloader/loaderstream.c +++ b/dlls/dmloader/loaderstream.c @@ -44,8 +44,6 @@ * - Rok Mandeljc; 24. April, 2004 */ -#define NONAMELESSUNION - #include "dmloader_private.h" WINE_DEFAULT_DEBUG_CHANNEL(dmloader); @@ -149,10 +147,10 @@ static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Seek (LPSTREAM iface, if (This->hFile == INVALID_HANDLE_VALUE) return E_FAIL; - liNewPos.u.HighPart = dlibMove.u.HighPart; - liNewPos.u.LowPart = SetFilePointer (This->hFile, dlibMove.u.LowPart, &liNewPos.u.HighPart, dwOrigin); + liNewPos.HighPart = dlibMove.HighPart; + liNewPos.LowPart = SetFilePointer (This->hFile, dlibMove.LowPart, &liNewPos.HighPart, dwOrigin); - if (liNewPos.u.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) return E_FAIL; + if (liNewPos.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) return E_FAIL; if (plibNewPosition) plibNewPosition->QuadPart = liNewPos.QuadPart; return S_OK; From a3ab3363a3a03d104fe81ef71a897e139738cabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 08:05:11 +0200 Subject: [PATCH 0936/1148] dmloader: Rename IDirectMusicLoaderImpl method prefix to loader. (cherry picked from commit 464c1a8f72524af8a4947e6f80e8032a77bf7477) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/loader.c | 72 ++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index 7b1fb8d04a5..c5801ed32fd 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -111,12 +111,7 @@ static HRESULT DMUSIC_CopyDescriptor(DMUS_OBJECTDESC *pDst, DMUS_OBJECTDESC *pSr return S_OK; } -/***************************************************************************** - * IDirectMusicLoaderImpl implementation - */ -/* IUnknown/IDirectMusicLoader(8) part: */ - -static HRESULT WINAPI IDirectMusicLoaderImpl_QueryInterface(IDirectMusicLoader8 *iface, REFIID riid, void **ppobj) +static HRESULT WINAPI loader_QueryInterface(IDirectMusicLoader8 *iface, REFIID riid, void **ppobj) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); @@ -133,7 +128,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_QueryInterface(IDirectMusicLoader8 return E_NOINTERFACE; } -static ULONG WINAPI IDirectMusicLoaderImpl_AddRef(IDirectMusicLoader8 *iface) +static ULONG WINAPI loader_AddRef(IDirectMusicLoader8 *iface) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); ULONG ref = InterlockedIncrement(&This->ref); @@ -143,7 +138,7 @@ static ULONG WINAPI IDirectMusicLoaderImpl_AddRef(IDirectMusicLoader8 *iface) return ref; } -static ULONG WINAPI IDirectMusicLoaderImpl_Release(IDirectMusicLoader8 *iface) +static ULONG WINAPI loader_Release(IDirectMusicLoader8 *iface) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); ULONG ref = InterlockedDecrement(&This->ref); @@ -251,7 +246,7 @@ static struct cache_entry *find_cache_object(IDirectMusicLoaderImpl *This, DMUS_ return NULL; } -static HRESULT WINAPI IDirectMusicLoaderImpl_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc, REFIID riid, void **ppv) +static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc, REFIID riid, void **ppv) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); HRESULT result = S_OK; @@ -451,7 +446,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_GetObject(IDirectMusicLoader8 *ifac return result; } -static HRESULT WINAPI IDirectMusicLoaderImpl_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc) +static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); LPSTREAM pStream; @@ -575,7 +570,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_SetObject(IDirectMusicLoader8 *ifac return S_OK; } -static HRESULT WINAPI IDirectMusicLoaderImpl_SetSearchDirectory(IDirectMusicLoader8 *iface, +static HRESULT WINAPI loader_SetSearchDirectory(IDirectMusicLoader8 *iface, REFGUID class, WCHAR *path, BOOL clear) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); @@ -610,7 +605,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_SetSearchDirectory(IDirectMusicLoad return S_OK; } -static HRESULT WINAPI IDirectMusicLoaderImpl_ScanDirectory(IDirectMusicLoader8 *iface, REFGUID rguidClass, WCHAR *pwzFileExtension, WCHAR *pwzScanFileName) +static HRESULT WINAPI loader_ScanDirectory(IDirectMusicLoader8 *iface, REFGUID rguidClass, WCHAR *pwzFileExtension, WCHAR *pwzScanFileName) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); WIN32_FIND_DATAW FileData; @@ -669,7 +664,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_ScanDirectory(IDirectMusicLoader8 * } while (1); } -static HRESULT WINAPI IDirectMusicLoaderImpl_CacheObject(IDirectMusicLoader8 *iface, +static HRESULT WINAPI loader_CacheObject(IDirectMusicLoader8 *iface, IDirectMusicObject *object) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); @@ -699,7 +694,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_CacheObject(IDirectMusicLoader8 *if return DMUS_E_LOADER_OBJECTNOTFOUND; } -static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObject(IDirectMusicLoader8 *iface, +static HRESULT WINAPI loader_ReleaseObject(IDirectMusicLoader8 *iface, IDirectMusicObject *object) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); @@ -730,7 +725,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObject(IDirectMusicLoader8 * return S_FALSE; } -static HRESULT WINAPI IDirectMusicLoaderImpl_ClearCache(IDirectMusicLoader8 *iface, REFGUID class) +static HRESULT WINAPI loader_ClearCache(IDirectMusicLoader8 *iface, REFGUID class) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); struct cache_entry *obj, *obj2; @@ -750,7 +745,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_ClearCache(IDirectMusicLoader8 *ifa return S_OK; } -static HRESULT WINAPI IDirectMusicLoaderImpl_EnableCache(IDirectMusicLoader8 *iface, REFGUID class, +static HRESULT WINAPI loader_EnableCache(IDirectMusicLoader8 *iface, REFGUID class, BOOL enable) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); @@ -780,7 +775,7 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_EnableCache(IDirectMusicLoader8 *if return S_OK; } -static HRESULT WINAPI IDirectMusicLoaderImpl_EnumObject(IDirectMusicLoader8 *iface, REFGUID rguidClass, DWORD dwIndex, DMUS_OBJECTDESC *pDesc) +static HRESULT WINAPI loader_EnumObject(IDirectMusicLoader8 *iface, REFGUID rguidClass, DWORD dwIndex, DMUS_OBJECTDESC *pDesc) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); DWORD dwCount = 0; @@ -808,12 +803,12 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_EnumObject(IDirectMusicLoader8 *ifa return S_FALSE; } -static void WINAPI IDirectMusicLoaderImpl_CollectGarbage(IDirectMusicLoader8 *iface) +static void WINAPI loader_CollectGarbage(IDirectMusicLoader8 *iface) { FIXME("(%p)->(): stub\n", iface); } -static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObjectByUnknown(IDirectMusicLoader8 *iface, IUnknown *pObject) +static HRESULT WINAPI loader_ReleaseObjectByUnknown(IDirectMusicLoader8 *iface, IUnknown *pObject) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); HRESULT result; @@ -835,13 +830,13 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObjectByUnknown(IDirectMusic return result; } -static HRESULT WINAPI IDirectMusicLoaderImpl_LoadObjectFromFile(IDirectMusicLoader8 *iface, REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR *pwzFilePath, void **ppObject) +static HRESULT WINAPI loader_LoadObjectFromFile(IDirectMusicLoader8 *iface, REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR *pwzFilePath, void **ppObject) { IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); DMUS_OBJECTDESC ObjDesc; WCHAR wszLoaderSearchPath[MAX_PATH]; - TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoaderImpl_GetObject\n", This, debugstr_dmguid(rguidClassID), debugstr_dmguid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject); + TRACE("(%p, %s, %s, %s, %p): wrapping to loader_GetObject\n", This, debugstr_dmguid(rguidClassID), debugstr_dmguid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject); DM_STRUCT_INIT(&ObjDesc); ObjDesc.dwValidData = DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_CLASS; /* I believe I've read somewhere in MSDN that this function requires either full path or relative path */ @@ -866,22 +861,23 @@ static HRESULT WINAPI IDirectMusicLoaderImpl_LoadObjectFromFile(IDirectMusicLoad return IDirectMusicLoader_GetObject(iface, &ObjDesc, iidInterfaceID, ppObject); } -static const IDirectMusicLoader8Vtbl DirectMusicLoader_Loader_Vtbl = { - IDirectMusicLoaderImpl_QueryInterface, - IDirectMusicLoaderImpl_AddRef, - IDirectMusicLoaderImpl_Release, - IDirectMusicLoaderImpl_GetObject, - IDirectMusicLoaderImpl_SetObject, - IDirectMusicLoaderImpl_SetSearchDirectory, - IDirectMusicLoaderImpl_ScanDirectory, - IDirectMusicLoaderImpl_CacheObject, - IDirectMusicLoaderImpl_ReleaseObject, - IDirectMusicLoaderImpl_ClearCache, - IDirectMusicLoaderImpl_EnableCache, - IDirectMusicLoaderImpl_EnumObject, - IDirectMusicLoaderImpl_CollectGarbage, - IDirectMusicLoaderImpl_ReleaseObjectByUnknown, - IDirectMusicLoaderImpl_LoadObjectFromFile +static const IDirectMusicLoader8Vtbl loader_vtbl = +{ + loader_QueryInterface, + loader_AddRef, + loader_Release, + loader_GetObject, + loader_SetObject, + loader_SetSearchDirectory, + loader_ScanDirectory, + loader_CacheObject, + loader_ReleaseObject, + loader_ClearCache, + loader_EnableCache, + loader_EnumObject, + loader_CollectGarbage, + loader_ReleaseObjectByUnknown, + loader_LoadObjectFromFile, }; /* help function for DMUSIC_SetDefaultDLS */ @@ -912,7 +908,7 @@ HRESULT create_dmloader(REFIID lpcGUID, void **ppobj) TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID), ppobj); *ppobj = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->IDirectMusicLoader8_iface.lpVtbl = &DirectMusicLoader_Loader_Vtbl; + obj->IDirectMusicLoader8_iface.lpVtbl = &loader_vtbl; obj->ref = 0; /* Will be inited with QueryInterface */ list_init(&obj->cache); /* Caching is enabled by default for all classes */ From dd7310dbecfc825461313ca054bcc5e3c205400e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 08:05:59 +0200 Subject: [PATCH 0937/1148] dmloader: Get rid of the IDirectMusicLoaderImpl typedef. (cherry picked from commit 177158210d9d3d654c06121f742a68fb4b8f0b44) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/loader.c | 48 ++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index c5801ed32fd..9c3a73e9612 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -1,6 +1,4 @@ /* - * IDirectMusicLoaderImpl - * * Copyright (C) 2003-2004 Rok Mandeljc * * This program is free software; you can redistribute it and/or @@ -45,18 +43,18 @@ struct cache_entry { BOOL bInvalidDefaultDLS; /* workaround for enabling caching of "faulty" default dls collection */ }; -typedef struct IDirectMusicLoaderImpl { +struct loader +{ IDirectMusicLoader8 IDirectMusicLoader8_iface; LONG ref; WCHAR *search_paths[ARRAY_SIZE(classes)]; unsigned int cache_class; struct list cache; -} IDirectMusicLoaderImpl; - +}; -static inline IDirectMusicLoaderImpl* impl_from_IDirectMusicLoader8(IDirectMusicLoader8 *iface) +static inline struct loader *impl_from_IDirectMusicLoader8(IDirectMusicLoader8 *iface) { - return CONTAINING_RECORD(iface, IDirectMusicLoaderImpl, IDirectMusicLoader8_iface); + return CONTAINING_RECORD(iface, struct loader, IDirectMusicLoader8_iface); } static int index_from_class(REFCLSID class) @@ -70,12 +68,12 @@ static int index_from_class(REFCLSID class) return -1; } -static inline BOOL is_cache_enabled(IDirectMusicLoaderImpl *This, REFCLSID class) +static inline BOOL is_cache_enabled(struct loader *This, REFCLSID class) { return !!(This->cache_class & 1 << index_from_class(class)); } -static void get_search_path(IDirectMusicLoaderImpl *This, REFGUID class, WCHAR *path) +static void get_search_path(struct loader *This, REFGUID class, WCHAR *path) { int index = index_from_class(class); @@ -113,7 +111,7 @@ static HRESULT DMUSIC_CopyDescriptor(DMUS_OBJECTDESC *pDst, DMUS_OBJECTDESC *pSr static HRESULT WINAPI loader_QueryInterface(IDirectMusicLoader8 *iface, REFIID riid, void **ppobj) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); TRACE("(%p, %s, %p)\n",This, debugstr_dmguid(riid), ppobj); if (IsEqualIID (riid, &IID_IUnknown) || @@ -130,7 +128,7 @@ static HRESULT WINAPI loader_QueryInterface(IDirectMusicLoader8 *iface, REFIID r static ULONG WINAPI loader_AddRef(IDirectMusicLoader8 *iface) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p)->(): new ref = %lu\n", iface, ref); @@ -140,7 +138,7 @@ static ULONG WINAPI loader_AddRef(IDirectMusicLoader8 *iface) static ULONG WINAPI loader_Release(IDirectMusicLoader8 *iface) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p)->(): new ref = %lu\n", iface, ref); @@ -157,7 +155,7 @@ static ULONG WINAPI loader_Release(IDirectMusicLoader8 *iface) return ref; } -static struct cache_entry *find_cache_object(IDirectMusicLoaderImpl *This, DMUS_OBJECTDESC *desc) +static struct cache_entry *find_cache_object(struct loader *This, DMUS_OBJECTDESC *desc) { struct cache_entry *existing; @@ -248,7 +246,7 @@ static struct cache_entry *find_cache_object(IDirectMusicLoaderImpl *This, DMUS_ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc, REFIID riid, void **ppv) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); HRESULT result = S_OK; HRESULT ret = S_OK; /* used at the end of function, to determine whether everything went OK */ struct cache_entry *pExistingEntry, *pObjectEntry = NULL; @@ -448,7 +446,7 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); LPSTREAM pStream; LPDIRECTMUSICOBJECT pObject; DMUS_OBJECTDESC Desc; @@ -573,7 +571,7 @@ static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE static HRESULT WINAPI loader_SetSearchDirectory(IDirectMusicLoader8 *iface, REFGUID class, WCHAR *path, BOOL clear) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); int index = index_from_class(class); DWORD attr; @@ -607,7 +605,7 @@ static HRESULT WINAPI loader_SetSearchDirectory(IDirectMusicLoader8 *iface, static HRESULT WINAPI loader_ScanDirectory(IDirectMusicLoader8 *iface, REFGUID rguidClass, WCHAR *pwzFileExtension, WCHAR *pwzScanFileName) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); WIN32_FIND_DATAW FileData; HANDLE hSearch; WCHAR wszSearchString[MAX_PATH]; @@ -667,7 +665,7 @@ static HRESULT WINAPI loader_ScanDirectory(IDirectMusicLoader8 *iface, REFGUID r static HRESULT WINAPI loader_CacheObject(IDirectMusicLoader8 *iface, IDirectMusicObject *object) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); DMUS_OBJECTDESC desc; struct cache_entry *entry; @@ -697,7 +695,7 @@ static HRESULT WINAPI loader_CacheObject(IDirectMusicLoader8 *iface, static HRESULT WINAPI loader_ReleaseObject(IDirectMusicLoader8 *iface, IDirectMusicObject *object) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); DMUS_OBJECTDESC desc; struct cache_entry *entry; @@ -727,7 +725,7 @@ static HRESULT WINAPI loader_ReleaseObject(IDirectMusicLoader8 *iface, static HRESULT WINAPI loader_ClearCache(IDirectMusicLoader8 *iface, REFGUID class) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); struct cache_entry *obj, *obj2; TRACE("(%p, %s)\n", This, debugstr_dmguid(class)); @@ -748,7 +746,7 @@ static HRESULT WINAPI loader_ClearCache(IDirectMusicLoader8 *iface, REFGUID clas static HRESULT WINAPI loader_EnableCache(IDirectMusicLoader8 *iface, REFGUID class, BOOL enable) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); BOOL current; TRACE("(%p, %s, %d)\n", This, debugstr_dmguid(class), enable); @@ -777,7 +775,7 @@ static HRESULT WINAPI loader_EnableCache(IDirectMusicLoader8 *iface, REFGUID cla static HRESULT WINAPI loader_EnumObject(IDirectMusicLoader8 *iface, REFGUID rguidClass, DWORD dwIndex, DMUS_OBJECTDESC *pDesc) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); DWORD dwCount = 0; struct cache_entry *pObjectEntry; TRACE("(%p, %s, %ld, %p)\n", This, debugstr_dmguid(rguidClass), dwIndex, pDesc); @@ -810,7 +808,7 @@ static void WINAPI loader_CollectGarbage(IDirectMusicLoader8 *iface) static HRESULT WINAPI loader_ReleaseObjectByUnknown(IDirectMusicLoader8 *iface, IUnknown *pObject) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); HRESULT result; LPDIRECTMUSICOBJECT pObjectInterface; @@ -832,7 +830,7 @@ static HRESULT WINAPI loader_ReleaseObjectByUnknown(IDirectMusicLoader8 *iface, static HRESULT WINAPI loader_LoadObjectFromFile(IDirectMusicLoader8 *iface, REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR *pwzFilePath, void **ppObject) { - IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); + struct loader *This = impl_from_IDirectMusicLoader8(iface); DMUS_OBJECTDESC ObjDesc; WCHAR wszLoaderSearchPath[MAX_PATH]; @@ -900,7 +898,7 @@ static HRESULT DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) { /* for ClassFactory */ HRESULT create_dmloader(REFIID lpcGUID, void **ppobj) { - IDirectMusicLoaderImpl *obj; + struct loader *obj; DMUS_OBJECTDESC Desc; struct cache_entry *dls; struct list *pEntry; From 590808c9c5585e6130c141cdfeb81baf7171701c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 08:07:56 +0200 Subject: [PATCH 0938/1148] dmloader: Initialize ref to 1, and release after QueryInterface. (cherry picked from commit 7d33a77b567614ed53b4f028b79a0ea5a656e5a3) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/loader.c | 70 ++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index 9c3a73e9612..3d51b65c335 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -898,37 +898,41 @@ static HRESULT DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) { /* for ClassFactory */ HRESULT create_dmloader(REFIID lpcGUID, void **ppobj) { - struct loader *obj; - DMUS_OBJECTDESC Desc; - struct cache_entry *dls; - struct list *pEntry; - - TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID), ppobj); - *ppobj = NULL; - if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->IDirectMusicLoader8_iface.lpVtbl = &loader_vtbl; - obj->ref = 0; /* Will be inited with QueryInterface */ - list_init(&obj->cache); - /* Caching is enabled by default for all classes */ - obj->cache_class = ~0; - - /* set default DLS collection (via SetObject... so that loading via DMUS_OBJ_OBJECT is possible) */ - DM_STRUCT_INIT(&Desc); - Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_OBJECT; - Desc.guidClass = CLSID_DirectMusicCollection; - Desc.guidObject = GUID_DefaultGMCollection; - DMUSIC_GetDefaultGMPath (Desc.wszFileName); - IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc); - /* and now the workaroundTM for "invalid" default DLS; basically, - my tests showed that if GUID chunk is present in default DLS - collection, loader treats it as "invalid" and returns - DMUS_E_LOADER_NOFILENAME for all requests for it; basically, we check - if out input guidObject was overwritten */ - pEntry = list_head(&obj->cache); - dls = LIST_ENTRY(pEntry, struct cache_entry, entry); - if (!IsEqualGUID(&Desc.guidObject, &GUID_DefaultGMCollection)) { - dls->bInvalidDefaultDLS = TRUE; - } - - return IDirectMusicLoader_QueryInterface(&obj->IDirectMusicLoader8_iface, lpcGUID, ppobj); + struct loader *obj; + DMUS_OBJECTDESC Desc; + struct cache_entry *dls; + struct list *pEntry; + HRESULT hr; + + TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID), ppobj); + + *ppobj = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; + obj->IDirectMusicLoader8_iface.lpVtbl = &loader_vtbl; + obj->ref = 1; + list_init(&obj->cache); + /* Caching is enabled by default for all classes */ + obj->cache_class = ~0; + + /* set default DLS collection (via SetObject... so that loading via DMUS_OBJ_OBJECT is possible) */ + DM_STRUCT_INIT(&Desc); + Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_OBJECT; + Desc.guidClass = CLSID_DirectMusicCollection; + Desc.guidObject = GUID_DefaultGMCollection; + DMUSIC_GetDefaultGMPath(Desc.wszFileName); + IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc); + + /* and now the workaroundTM for "invalid" default DLS; basically, + my tests showed that if GUID chunk is present in default DLS + collection, loader treats it as "invalid" and returns + DMUS_E_LOADER_NOFILENAME for all requests for it; basically, we check + if out input guidObject was overwritten */ + pEntry = list_head(&obj->cache); + dls = LIST_ENTRY(pEntry, struct cache_entry, entry); + if (!IsEqualGUID(&Desc.guidObject, &GUID_DefaultGMCollection)) + dls->bInvalidDefaultDLS = TRUE; + + hr = IDirectMusicLoader_QueryInterface(&obj->IDirectMusicLoader8_iface, lpcGUID, ppobj); + IDirectMusicLoader_Release(&obj->IDirectMusicLoader8_iface); + return hr; } From 5a6d6d3055512aa9c622a18b04dcf795dce0bebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 20 Sep 2023 11:02:15 +0200 Subject: [PATCH 0939/1148] dmloader: Introduce a new loader_stream_create helper. (cherry picked from commit 975f262986fd41c1c83c8da1c3cd3ca64105f2d3) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/dmloader_private.h | 18 +- dlls/dmloader/loader.c | 51 ++-- dlls/dmloader/loaderstream.c | 383 ++++++++++++++++++++----------- 3 files changed, 289 insertions(+), 163 deletions(-) diff --git a/dlls/dmloader/dmloader_private.h b/dlls/dmloader/dmloader_private.h index 0aea1ba8499..bd5777f54f1 100644 --- a/dlls/dmloader/dmloader_private.h +++ b/dlls/dmloader/dmloader_private.h @@ -69,18 +69,15 @@ extern HRESULT DMUSIC_CreateDirectMusicLoaderGenericStream(void **ppobj); struct IDirectMusicLoaderFileStream { /* VTABLEs */ const IStreamVtbl *StreamVtbl; - const IDirectMusicGetLoaderVtbl *GetLoaderVtbl; /* reference counter */ LONG dwRef; /* file */ WCHAR wzFileName[MAX_PATH]; /* for clone */ HANDLE hFile; - /* loader */ - LPDIRECTMUSICLOADER8 pLoader; }; /* Custom: */ -extern HRESULT WINAPI IDirectMusicLoaderFileStream_Attach (LPSTREAM iface, LPCWSTR wzFile, LPDIRECTMUSICLOADER8 pLoader); +extern HRESULT WINAPI IDirectMusicLoaderFileStream_Attach(LPSTREAM iface, LPCWSTR wzFile); /***************************************************************************** * IDirectMusicLoaderResourceStream implementation structure @@ -88,7 +85,6 @@ extern HRESULT WINAPI IDirectMusicLoaderFileStream_Attach (LPSTREAM iface, LPCWS struct IDirectMusicLoaderResourceStream { /* IUnknown fields */ const IStreamVtbl *StreamVtbl; - const IDirectMusicGetLoaderVtbl *GetLoaderVtbl; /* reference counter */ LONG dwRef; /* data */ @@ -96,12 +92,11 @@ struct IDirectMusicLoaderResourceStream { LONGLONG llMemLength; /* current position */ LONGLONG llPos; - /* loader */ - LPDIRECTMUSICLOADER8 pLoader; }; /* Custom: */ -extern HRESULT WINAPI IDirectMusicLoaderResourceStream_Attach (LPSTREAM iface, LPBYTE pbMemData, LONGLONG llMemLength, LONGLONG llPos, LPDIRECTMUSICLOADER8 pLoader); +extern HRESULT WINAPI IDirectMusicLoaderResourceStream_Attach(LPSTREAM iface, LPBYTE pbMemData, + LONGLONG llMemLength, LONGLONG llPos); /***************************************************************************** * IDirectMusicLoaderGenericStream implementation structure @@ -109,17 +104,16 @@ extern HRESULT WINAPI IDirectMusicLoaderResourceStream_Attach (LPSTREAM iface, L struct IDirectMusicLoaderGenericStream { /* IUnknown fields */ const IStreamVtbl *StreamVtbl; - const IDirectMusicGetLoaderVtbl *GetLoaderVtbl; /* reference counter */ LONG dwRef; /* stream */ LPSTREAM pStream; - /* loader */ - LPDIRECTMUSICLOADER8 pLoader; }; /* Custom: */ -extern HRESULT WINAPI IDirectMusicLoaderGenericStream_Attach (LPSTREAM iface, LPSTREAM pStream, LPDIRECTMUSICLOADER8 pLoader); +extern HRESULT WINAPI IDirectMusicLoaderGenericStream_Attach(LPSTREAM iface, LPSTREAM pStream); + +extern HRESULT loader_stream_create(IDirectMusicLoader *loader, IStream *stream, IStream **ret_iface); #include "debug.h" diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index 3d51b65c335..a0683307f8f 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -256,8 +256,10 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE LPDIRECTMUSICOBJECT pObject; DMUS_OBJECTDESC GotDesc; BOOL bCache; + IStream *loader_stream; + HRESULT hr; - TRACE("(%p)->(%p, %s, %p)\n", This, pDesc, debugstr_dmguid(riid), ppv); + TRACE("(%p)->(%p, %s, %p)\n", This, pDesc, debugstr_dmguid(riid), ppv); if (TRACE_ON(dmloader)) dump_DMUS_OBJECTDESC(pDesc); @@ -329,8 +331,9 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE ERR(": could not create file stream\n"); return result; } - result = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface); - if (FAILED(result)) { + result = IDirectMusicLoaderFileStream_Attach(pStream, wszFileName); + if (FAILED(result)) + { ERR(": could not attach stream to file\n"); IStream_Release (pStream); return result; @@ -345,8 +348,9 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE ERR(": could not create resource stream\n"); return result; } - result = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface); - if (FAILED(result)) { + result = IDirectMusicLoaderResourceStream_Attach(pStream, pDesc->pbMemData, pDesc->llMemLength, 0); + if (FAILED(result)) + { ERR(": could not attach stream to resource\n"); IStream_Release (pStream); return result; @@ -361,8 +365,9 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE ERR(": could not create generic stream\n"); return result; } - result = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface); - if (FAILED(result)) { + result = IDirectMusicLoaderGenericStream_Attach(pStream, pDesc->pStream); + if (FAILED(result)) + { ERR(": failed to attach stream\n"); IStream_Release (pStream); return result; @@ -373,7 +378,12 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE return DMUS_E_LOADER_NOFILENAME; /* test shows this is returned */ } - /* create object */ + if (FAILED(hr = loader_stream_create((IDirectMusicLoader *)iface, pStream, &loader_stream))) + return hr; + IStream_Release(pStream); + pStream = loader_stream; + + /* create object */ result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject); if (FAILED(result)) { IStream_Release(pStream); @@ -451,7 +461,8 @@ static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE LPDIRECTMUSICOBJECT pObject; DMUS_OBJECTDESC Desc; struct cache_entry *pObjectEntry, *pNewEntry; - HRESULT hr; + IStream *loader_stream; + HRESULT hr; TRACE("(%p)->(%p)\n", This, pDesc); @@ -480,8 +491,9 @@ static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE return DMUS_E_LOADER_FAILEDOPEN; } /* attach stream */ - hr = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface); - if (FAILED(hr)) { + hr = IDirectMusicLoaderFileStream_Attach(pStream, wszFileName); + if (FAILED(hr)) + { ERR(": could not attach stream to file %s, make sure it exists\n", debugstr_w(wszFileName)); IStream_Release (pStream); return DMUS_E_LOADER_FAILEDOPEN; @@ -495,8 +507,9 @@ static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE return DMUS_E_LOADER_FAILEDOPEN; } /* attach stream */ - hr = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface); - if (FAILED(hr)) { + hr = IDirectMusicLoaderGenericStream_Attach(pStream, pDesc->pStream); + if (FAILED(hr)) + { ERR(": could not attach stream\n"); IStream_Release (pStream); return DMUS_E_LOADER_FAILEDOPEN; @@ -510,8 +523,9 @@ static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE return DMUS_E_LOADER_FAILEDOPEN; } /* attach stream */ - hr = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface); - if (FAILED(hr)) { + hr = IDirectMusicLoaderResourceStream_Attach(pStream, pDesc->pbMemData, pDesc->llMemLength, 0); + if (FAILED(hr)) + { ERR(": could not attach stream to resource\n"); IStream_Release (pStream); return DMUS_E_LOADER_FAILEDOPEN; @@ -522,7 +536,12 @@ static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE return DMUS_E_LOADER_FAILEDOPEN; } - /* create object */ + if (FAILED(hr = loader_stream_create((IDirectMusicLoader *)iface, pStream, &loader_stream))) + return hr; + IStream_Release(pStream); + pStream = loader_stream; + + /* create object */ hr = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject); if (FAILED(hr)) { IStream_Release(pStream); diff --git a/dlls/dmloader/loaderstream.c b/dlls/dmloader/loaderstream.c index 5d6c5ad3e25..3e01d423483 100644 --- a/dlls/dmloader/loaderstream.c +++ b/dlls/dmloader/loaderstream.c @@ -49,12 +49,244 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmloader); WINE_DECLARE_DEBUG_CHANNEL(dmfileraw); +struct loader_stream +{ + IStream IStream_iface; + IDirectMusicGetLoader IDirectMusicGetLoader_iface; + LONG ref; + + IStream *stream; + IDirectMusicLoader *loader; +}; + +static struct loader_stream *impl_from_IStream(IStream *iface) +{ + return CONTAINING_RECORD(iface, struct loader_stream, IStream_iface); +} + +static HRESULT WINAPI loader_stream_QueryInterface(IStream *iface, REFIID riid, void **ret_iface) +{ + struct loader_stream *This = impl_from_IStream(iface); + + TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ret_iface); + + if (IsEqualGUID(riid, &IID_IUnknown) + || IsEqualGUID(riid, &IID_IStream)) + { + IStream_AddRef(&This->IStream_iface); + *ret_iface = &This->IStream_iface; + return S_OK; + } + + if (IsEqualGUID(riid, &IID_IDirectMusicGetLoader)) + { + IDirectMusicGetLoader_AddRef(&This->IDirectMusicGetLoader_iface); + *ret_iface = &This->IDirectMusicGetLoader_iface; + return S_OK; + } + + WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface); + *ret_iface = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI loader_stream_AddRef(IStream *iface) +{ + struct loader_stream *This = impl_from_IStream(iface); + ULONG ref = InterlockedIncrement(&This->ref); + TRACE("(%p): new ref = %lu\n", This, ref); + return ref; +} + +static ULONG WINAPI loader_stream_Release(IStream *iface) +{ + struct loader_stream *This = impl_from_IStream(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p): new ref = %lu\n", This, ref); + + if (!ref) + { + IDirectMusicLoader_Release(This->loader); + IStream_Release(This->stream); + free(This); + } + + return ref; +} + +static HRESULT WINAPI loader_stream_Read(IStream *iface, void *data, ULONG size, ULONG *ret_size) +{ + struct loader_stream *This = impl_from_IStream(iface); + TRACE("(%p, %p, %#lx, %p)\n", This, data, size, ret_size); + return IStream_Read(This->stream, data, size, ret_size); +} + +static HRESULT WINAPI loader_stream_Write(IStream *iface, const void *data, ULONG size, ULONG *ret_size) +{ + struct loader_stream *This = impl_from_IStream(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI loader_stream_Seek(IStream *iface, LARGE_INTEGER offset, DWORD method, ULARGE_INTEGER *ret_offset) +{ + struct loader_stream *This = impl_from_IStream(iface); + TRACE("(%p, %I64d, %#lx, %p)\n", This, offset.QuadPart, method, ret_offset); + return IStream_Seek(This->stream, offset, method, ret_offset); +} + +static HRESULT WINAPI loader_stream_SetSize(IStream *iface, ULARGE_INTEGER size) +{ + struct loader_stream *This = impl_from_IStream(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI loader_stream_CopyTo(IStream *iface, IStream *dest, ULARGE_INTEGER size, + ULARGE_INTEGER *read_size, ULARGE_INTEGER *write_size) +{ + struct loader_stream *This = impl_from_IStream(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI loader_stream_Commit(IStream *iface, DWORD flags) +{ + struct loader_stream *This = impl_from_IStream(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI loader_stream_Revert(IStream *iface) +{ + struct loader_stream *This = impl_from_IStream(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI loader_stream_LockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) +{ + struct loader_stream *This = impl_from_IStream(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI loader_stream_UnlockRegion(IStream *iface, ULARGE_INTEGER offset, + ULARGE_INTEGER size, DWORD type) +{ + struct loader_stream *This = impl_from_IStream(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI loader_stream_Stat(IStream *iface, STATSTG *stat, DWORD flags) +{ + struct loader_stream *This = impl_from_IStream(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI loader_stream_Clone(IStream *iface, IStream **ret_iface) +{ + struct loader_stream *This = impl_from_IStream(iface); + IStream *stream; + HRESULT hr; + + TRACE("(%p, %p)\n", This, ret_iface); + + if (SUCCEEDED(hr = IStream_Clone(This->stream, &stream))) + { + hr = loader_stream_create(This->loader, stream, ret_iface); + IStream_Release(stream); + } + + return hr; +} + +static const IStreamVtbl loader_stream_vtbl = +{ + loader_stream_QueryInterface, + loader_stream_AddRef, + loader_stream_Release, + loader_stream_Read, + loader_stream_Write, + loader_stream_Seek, + loader_stream_SetSize, + loader_stream_CopyTo, + loader_stream_Commit, + loader_stream_Revert, + loader_stream_LockRegion, + loader_stream_UnlockRegion, + loader_stream_Stat, + loader_stream_Clone, +}; + +static struct loader_stream *impl_from_IDirectMusicGetLoader(IDirectMusicGetLoader *iface) +{ + return CONTAINING_RECORD(iface, struct loader_stream, IDirectMusicGetLoader_iface); +} + +static HRESULT WINAPI loader_stream_getter_QueryInterface(IDirectMusicGetLoader *iface, REFIID iid, void **out) +{ + struct loader_stream *This = impl_from_IDirectMusicGetLoader(iface); + return IStream_QueryInterface(&This->IStream_iface, iid, out); +} + +static ULONG WINAPI loader_stream_getter_AddRef(IDirectMusicGetLoader *iface) +{ + struct loader_stream *This = impl_from_IDirectMusicGetLoader(iface); + return IStream_AddRef(&This->IStream_iface); +} + +static ULONG WINAPI loader_stream_getter_Release(IDirectMusicGetLoader *iface) +{ + struct loader_stream *This = impl_from_IDirectMusicGetLoader(iface); + return IStream_Release(&This->IStream_iface); +} + +static HRESULT WINAPI loader_stream_getter_GetLoader(IDirectMusicGetLoader *iface, IDirectMusicLoader **ret_loader) +{ + struct loader_stream *This = impl_from_IDirectMusicGetLoader(iface); + + TRACE("(%p, %p)\n", This, ret_loader); + + *ret_loader = This->loader; + IDirectMusicLoader_AddRef(This->loader); + return S_OK; +} + +static const IDirectMusicGetLoaderVtbl loader_stream_getter_vtbl = +{ + loader_stream_getter_QueryInterface, + loader_stream_getter_AddRef, + loader_stream_getter_Release, + loader_stream_getter_GetLoader, +}; + +HRESULT loader_stream_create(IDirectMusicLoader *loader, IStream *stream, + IStream **ret_iface) +{ + struct loader_stream *obj; + + *ret_iface = NULL; + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; + obj->IStream_iface.lpVtbl = &loader_stream_vtbl; + obj->IDirectMusicGetLoader_iface.lpVtbl = &loader_stream_getter_vtbl; + obj->ref = 1; + + obj->stream = stream; + IStream_AddRef(stream); + obj->loader = loader; + IDirectMusicLoader_AddRef(loader); + + *ret_iface = &obj->IStream_iface; + return S_OK; +} + static ULONG WINAPI IDirectMusicLoaderFileStream_IStream_AddRef (LPSTREAM iface); -static ULONG WINAPI IDirectMusicLoaderFileStream_IDirectMusicGetLoader_AddRef (LPDIRECTMUSICGETLOADER iface); -static ULONG WINAPI IDirectMusicLoaderResourceStream_IDirectMusicGetLoader_AddRef (LPDIRECTMUSICGETLOADER iface); static ULONG WINAPI IDirectMusicLoaderResourceStream_IStream_AddRef (LPSTREAM iface); static ULONG WINAPI IDirectMusicLoaderGenericStream_IStream_AddRef (LPSTREAM iface); -static ULONG WINAPI IDirectMusicLoaderGenericStream_IDirectMusicGetLoader_AddRef (LPDIRECTMUSICGETLOADER iface); /***************************************************************************** @@ -69,9 +301,10 @@ static void IDirectMusicLoaderFileStream_Detach (LPSTREAM iface) { This->wzFileName[0] = '\0'; } -HRESULT WINAPI IDirectMusicLoaderFileStream_Attach (LPSTREAM iface, LPCWSTR wzFile, LPDIRECTMUSICLOADER8 pLoader) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - TRACE("(%p, %s, %p)\n", This, debugstr_w(wzFile), pLoader); +HRESULT WINAPI IDirectMusicLoaderFileStream_Attach(LPSTREAM iface, LPCWSTR wzFile) +{ + ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); + TRACE("(%p, %s)\n", This, debugstr_w(wzFile)); IDirectMusicLoaderFileStream_Detach (iface); This->hFile = CreateFileW (wzFile, (GENERIC_READ | GENERIC_WRITE), (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (This->hFile == INVALID_HANDLE_VALUE) { @@ -79,7 +312,6 @@ HRESULT WINAPI IDirectMusicLoaderFileStream_Attach (LPSTREAM iface, LPCWSTR wzFi return DMUS_E_LOADER_FAILEDOPEN; } /* create IDirectMusicGetLoader */ - This->pLoader = pLoader; lstrcpynW (This->wzFileName, wzFile, MAX_PATH); TRACE(": succeeded\n"); return S_OK; @@ -96,10 +328,6 @@ static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_QueryInterface (LPSTR *ppobj = &This->StreamVtbl; IDirectMusicLoaderFileStream_IStream_AddRef ((LPSTREAM)&This->StreamVtbl); return S_OK; - } else if (IsEqualIID (riid, &IID_IDirectMusicGetLoader)) { - *ppobj = &This->GetLoaderVtbl; - IDirectMusicLoaderFileStream_IDirectMusicGetLoader_AddRef ((LPDIRECTMUSICGETLOADER)&This->GetLoaderVtbl); - return S_OK; } WARN(": not found\n"); @@ -166,8 +394,9 @@ static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Clone (LPSTREAM iface if (FAILED(result)) return result; if (This->hFile != INVALID_HANDLE_VALUE) { ULARGE_INTEGER ullCurrentPosition; - result = IDirectMusicLoaderFileStream_Attach (pOther, This->wzFileName, This->pLoader); - if (SUCCEEDED(result)) { + result = IDirectMusicLoaderFileStream_Attach(pOther, This->wzFileName); + if (SUCCEEDED(result)) + { LARGE_INTEGER liZero; liZero.QuadPart = 0; result = IDirectMusicLoaderFileStream_IStream_Seek (iface, liZero, STREAM_SEEK_CUR, &ullCurrentPosition); /* get current position in current stream */ @@ -253,39 +482,6 @@ static const IStreamVtbl DirectMusicLoaderFileStream_Stream_Vtbl = { IDirectMusicLoaderFileStream_IStream_Clone }; -/* IDirectMusicGetLoader part: */ -static HRESULT WINAPI IDirectMusicLoaderFileStream_IDirectMusicGetLoader_QueryInterface (LPDIRECTMUSICGETLOADER iface, REFIID riid, void** ppobj) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, GetLoaderVtbl, iface); - return IDirectMusicLoaderFileStream_IStream_QueryInterface ((LPSTREAM)&This->StreamVtbl, riid, ppobj); -} - -static ULONG WINAPI IDirectMusicLoaderFileStream_IDirectMusicGetLoader_AddRef (LPDIRECTMUSICGETLOADER iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, GetLoaderVtbl, iface); - return IDirectMusicLoaderFileStream_IStream_AddRef ((LPSTREAM)&This->StreamVtbl); -} - -static ULONG WINAPI IDirectMusicLoaderFileStream_IDirectMusicGetLoader_Release (LPDIRECTMUSICGETLOADER iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, GetLoaderVtbl, iface); - return IDirectMusicLoaderFileStream_IStream_Release ((LPSTREAM)&This->StreamVtbl); -} - -static HRESULT WINAPI IDirectMusicLoaderFileStream_IDirectMusicGetLoader_GetLoader (LPDIRECTMUSICGETLOADER iface, IDirectMusicLoader **ppLoader) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, GetLoaderVtbl, iface); - - TRACE("(%p, %p)\n", This, ppLoader); - *ppLoader = (LPDIRECTMUSICLOADER)This->pLoader; - IDirectMusicLoader8_AddRef ((LPDIRECTMUSICLOADER8)*ppLoader); - - return S_OK; -} - -static const IDirectMusicGetLoaderVtbl DirectMusicLoaderFileStream_GetLoader_Vtbl = { - IDirectMusicLoaderFileStream_IDirectMusicGetLoader_QueryInterface, - IDirectMusicLoaderFileStream_IDirectMusicGetLoader_AddRef, - IDirectMusicLoaderFileStream_IDirectMusicGetLoader_Release, - IDirectMusicLoaderFileStream_IDirectMusicGetLoader_GetLoader -}; - HRESULT DMUSIC_CreateDirectMusicLoaderFileStream (void** ppobj) { IDirectMusicLoaderFileStream *obj; @@ -294,7 +490,6 @@ HRESULT DMUSIC_CreateDirectMusicLoaderFileStream (void** ppobj) { *ppobj = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->StreamVtbl = &DirectMusicLoaderFileStream_Stream_Vtbl; - obj->GetLoaderVtbl = &DirectMusicLoaderFileStream_GetLoader_Vtbl; obj->dwRef = 0; /* will be inited with QueryInterface */ return IDirectMusicLoaderFileStream_IStream_QueryInterface ((LPSTREAM)&obj->StreamVtbl, &IID_IStream, ppobj); @@ -314,10 +509,10 @@ static void IDirectMusicLoaderResourceStream_Detach (LPSTREAM iface) { This->llMemLength = 0; } -HRESULT WINAPI IDirectMusicLoaderResourceStream_Attach (LPSTREAM iface, LPBYTE pbMemData, LONGLONG llMemLength, LONGLONG llPos, LPDIRECTMUSICLOADER8 pLoader) { +HRESULT WINAPI IDirectMusicLoaderResourceStream_Attach (LPSTREAM iface, LPBYTE pbMemData, LONGLONG llMemLength, LONGLONG llPos) { ICOM_THIS_MULTI(IDirectMusicLoaderResourceStream, StreamVtbl, iface); - TRACE("(%p, %p, %s, %s, %p)\n", This, pbMemData, wine_dbgstr_longlong(llMemLength), wine_dbgstr_longlong(llPos), pLoader); + TRACE("(%p, %p, %s, %s)\n", This, pbMemData, wine_dbgstr_longlong(llMemLength), wine_dbgstr_longlong(llPos)); if (!pbMemData || !llMemLength) { WARN(": invalid pbMemData or llMemLength\n"); return E_FAIL; @@ -326,7 +521,6 @@ HRESULT WINAPI IDirectMusicLoaderResourceStream_Attach (LPSTREAM iface, LPBYTE p This->pbMemData = pbMemData; This->llMemLength = llMemLength; This->llPos = llPos; - This->pLoader = pLoader; return S_OK; } @@ -342,10 +536,6 @@ static HRESULT WINAPI IDirectMusicLoaderResourceStream_IStream_QueryInterface (L *ppobj = &This->StreamVtbl; IDirectMusicLoaderResourceStream_IStream_AddRef ((LPSTREAM)&This->StreamVtbl); return S_OK; - } else if (IsEqualIID (riid, &IID_IDirectMusicGetLoader)) { - *ppobj = &This->GetLoaderVtbl; - IDirectMusicLoaderResourceStream_IDirectMusicGetLoader_AddRef ((LPDIRECTMUSICGETLOADER)&This->GetLoaderVtbl); - return S_OK; } WARN(": not found\n"); @@ -444,7 +634,7 @@ static HRESULT WINAPI IDirectMusicLoaderResourceStream_IStream_Clone (LPSTREAM i result = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pOther); if (FAILED(result)) return result; - IDirectMusicLoaderResourceStream_Attach (pOther, This->pbMemData, This->llMemLength, This->llPos, This->pLoader); + IDirectMusicLoaderResourceStream_Attach (pOther, This->pbMemData, This->llMemLength, This->llPos); TRACE(": succeeded\n"); *ppstm = pOther; @@ -508,39 +698,6 @@ static const IStreamVtbl DirectMusicLoaderResourceStream_Stream_Vtbl = { IDirectMusicLoaderResourceStream_IStream_Clone }; -/* IDirectMusicGetLoader part: */ -static HRESULT WINAPI IDirectMusicLoaderResourceStream_IDirectMusicGetLoader_QueryInterface (LPDIRECTMUSICGETLOADER iface, REFIID riid, void** ppobj) { - ICOM_THIS_MULTI(IDirectMusicLoaderResourceStream, GetLoaderVtbl, iface); - return IDirectMusicLoaderResourceStream_IStream_QueryInterface ((LPSTREAM)&This->StreamVtbl, riid, ppobj); -} - -static ULONG WINAPI IDirectMusicLoaderResourceStream_IDirectMusicGetLoader_AddRef (LPDIRECTMUSICGETLOADER iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderResourceStream, GetLoaderVtbl, iface); - return IDirectMusicLoaderResourceStream_IStream_AddRef ((LPSTREAM)&This->StreamVtbl); -} - -static ULONG WINAPI IDirectMusicLoaderResourceStream_IDirectMusicGetLoader_Release (LPDIRECTMUSICGETLOADER iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderResourceStream, GetLoaderVtbl, iface); - return IDirectMusicLoaderResourceStream_IStream_Release ((LPSTREAM)&This->StreamVtbl); -} - -static HRESULT WINAPI IDirectMusicLoaderResourceStream_IDirectMusicGetLoader_GetLoader (LPDIRECTMUSICGETLOADER iface, IDirectMusicLoader **ppLoader) { - ICOM_THIS_MULTI(IDirectMusicLoaderResourceStream, GetLoaderVtbl, iface); - - TRACE("(%p, %p)\n", This, ppLoader); - *ppLoader = (LPDIRECTMUSICLOADER)This->pLoader; - IDirectMusicLoader8_AddRef ((LPDIRECTMUSICLOADER8)*ppLoader); - - return S_OK; -} - -static const IDirectMusicGetLoaderVtbl DirectMusicLoaderResourceStream_GetLoader_Vtbl = { - IDirectMusicLoaderResourceStream_IDirectMusicGetLoader_QueryInterface, - IDirectMusicLoaderResourceStream_IDirectMusicGetLoader_AddRef, - IDirectMusicLoaderResourceStream_IDirectMusicGetLoader_Release, - IDirectMusicLoaderResourceStream_IDirectMusicGetLoader_GetLoader -}; - HRESULT DMUSIC_CreateDirectMusicLoaderResourceStream (void** ppobj) { IDirectMusicLoaderResourceStream *obj; @@ -549,7 +706,6 @@ HRESULT DMUSIC_CreateDirectMusicLoaderResourceStream (void** ppobj) { *ppobj = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->StreamVtbl = &DirectMusicLoaderResourceStream_Stream_Vtbl; - obj->GetLoaderVtbl = &DirectMusicLoaderResourceStream_GetLoader_Vtbl; obj->dwRef = 0; /* will be inited with QueryInterface */ return IDirectMusicLoaderResourceStream_IStream_QueryInterface ((LPSTREAM)&obj->StreamVtbl, &IID_IStream, ppobj); @@ -569,22 +725,17 @@ static void IDirectMusicLoaderGenericStream_Detach (LPSTREAM iface) { This->pStream = NULL; } -HRESULT WINAPI IDirectMusicLoaderGenericStream_Attach (LPSTREAM iface, LPSTREAM pStream, LPDIRECTMUSICLOADER8 pLoader) { +HRESULT WINAPI IDirectMusicLoaderGenericStream_Attach (LPSTREAM iface, LPSTREAM pStream) { ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - TRACE("(%p, %p, %p)\n", This, pStream, pLoader); + TRACE("(%p, %p)\n", This, pStream); if (!pStream) { WARN(": invalid pStream\n"); return E_FAIL; } - if (!pLoader) { - WARN(": invalid pLoader\n"); - return E_FAIL; - } IDirectMusicLoaderGenericStream_Detach (iface); IStream_Clone (pStream, &This->pStream); - This->pLoader = pLoader; return S_OK; } @@ -600,10 +751,6 @@ static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_QueryInterface (LP *ppobj = &This->StreamVtbl; IDirectMusicLoaderGenericStream_IStream_AddRef ((LPSTREAM)&This->StreamVtbl); return S_OK; - } else if (IsEqualIID (riid, &IID_IDirectMusicGetLoader)) { - *ppobj = &This->GetLoaderVtbl; - IDirectMusicLoaderGenericStream_IDirectMusicGetLoader_AddRef ((LPDIRECTMUSICGETLOADER)&This->GetLoaderVtbl); - return S_OK; } WARN(": not found\n"); @@ -662,7 +809,7 @@ static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_Clone (LPSTREAM if IStream_Release(pOther); return E_FAIL; } - IDirectMusicLoaderGenericStream_Attach (pOther, pLowLevel, This->pLoader); + IDirectMusicLoaderGenericStream_Attach (pOther, pLowLevel); TRACE(": succeeded\n"); *ppstm = pOther; @@ -758,39 +905,6 @@ static const IStreamVtbl DirectMusicLoaderGenericStream_Stream_Vtbl = { IDirectMusicLoaderGenericStream_IStream_Clone }; -/* IDirectMusicGetLoader part: */ -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IDirectMusicGetLoader_QueryInterface (LPDIRECTMUSICGETLOADER iface, REFIID riid, void** ppobj) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, GetLoaderVtbl, iface); - return IDirectMusicLoaderGenericStream_IStream_QueryInterface ((LPSTREAM)&This->StreamVtbl, riid, ppobj); -} - -static ULONG WINAPI IDirectMusicLoaderGenericStream_IDirectMusicGetLoader_AddRef (LPDIRECTMUSICGETLOADER iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, GetLoaderVtbl, iface); - return IDirectMusicLoaderGenericStream_IStream_AddRef ((LPSTREAM)&This->StreamVtbl); -} - -static ULONG WINAPI IDirectMusicLoaderGenericStream_IDirectMusicGetLoader_Release (LPDIRECTMUSICGETLOADER iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, GetLoaderVtbl, iface); - return IDirectMusicLoaderGenericStream_IStream_Release ((LPSTREAM)&This->StreamVtbl); -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IDirectMusicGetLoader_GetLoader (LPDIRECTMUSICGETLOADER iface, IDirectMusicLoader **ppLoader) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, GetLoaderVtbl, iface); - - TRACE("(%p, %p)\n", This, ppLoader); - *ppLoader = (LPDIRECTMUSICLOADER)This->pLoader; - IDirectMusicLoader8_AddRef ((LPDIRECTMUSICLOADER8)*ppLoader); - - return S_OK; -} - -static const IDirectMusicGetLoaderVtbl DirectMusicLoaderGenericStream_GetLoader_Vtbl = { - IDirectMusicLoaderGenericStream_IDirectMusicGetLoader_QueryInterface, - IDirectMusicLoaderGenericStream_IDirectMusicGetLoader_AddRef, - IDirectMusicLoaderGenericStream_IDirectMusicGetLoader_Release, - IDirectMusicLoaderGenericStream_IDirectMusicGetLoader_GetLoader -}; - HRESULT DMUSIC_CreateDirectMusicLoaderGenericStream (void** ppobj) { IDirectMusicLoaderGenericStream *obj; @@ -799,7 +913,6 @@ HRESULT DMUSIC_CreateDirectMusicLoaderGenericStream (void** ppobj) { *ppobj = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->StreamVtbl = &DirectMusicLoaderGenericStream_Stream_Vtbl; - obj->GetLoaderVtbl = &DirectMusicLoaderGenericStream_GetLoader_Vtbl; obj->dwRef = 0; /* will be inited with QueryInterface */ return IDirectMusicLoaderGenericStream_IStream_QueryInterface ((LPSTREAM)&obj->StreamVtbl, &IID_IStream, ppobj); From d9af43cae42a44c704ed59a71e16fd741f98a758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 20 Sep 2023 11:22:14 +0200 Subject: [PATCH 0940/1148] dmloader: Get rid of the custom generic stream wrapper. (cherry picked from commit 196aa81738b0cde2b06f2c3f474c9a940fe98f6c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/dmloader_private.h | 16 --- dlls/dmloader/loader.c | 53 +++----- dlls/dmloader/loaderstream.c | 208 ------------------------------- 3 files changed, 16 insertions(+), 261 deletions(-) diff --git a/dlls/dmloader/dmloader_private.h b/dlls/dmloader/dmloader_private.h index bd5777f54f1..87fcf087bc0 100644 --- a/dlls/dmloader/dmloader_private.h +++ b/dlls/dmloader/dmloader_private.h @@ -61,7 +61,6 @@ extern HRESULT create_dmloader(REFIID riid, void **ret_iface); extern HRESULT create_dmcontainer(REFIID riid, void **ret_iface); extern HRESULT DMUSIC_CreateDirectMusicLoaderFileStream(void **ppobj); extern HRESULT DMUSIC_CreateDirectMusicLoaderResourceStream(void **ppobj); -extern HRESULT DMUSIC_CreateDirectMusicLoaderGenericStream(void **ppobj); /***************************************************************************** * IDirectMusicLoaderFileStream implementation structure @@ -98,21 +97,6 @@ struct IDirectMusicLoaderResourceStream { extern HRESULT WINAPI IDirectMusicLoaderResourceStream_Attach(LPSTREAM iface, LPBYTE pbMemData, LONGLONG llMemLength, LONGLONG llPos); -/***************************************************************************** - * IDirectMusicLoaderGenericStream implementation structure - */ -struct IDirectMusicLoaderGenericStream { - /* IUnknown fields */ - const IStreamVtbl *StreamVtbl; - /* reference counter */ - LONG dwRef; - /* stream */ - LPSTREAM pStream; -}; - -/* Custom: */ -extern HRESULT WINAPI IDirectMusicLoaderGenericStream_Attach(LPSTREAM iface, LPSTREAM pStream); - extern HRESULT loader_stream_create(IDirectMusicLoader *loader, IStream *stream, IStream **ret_iface); #include "debug.h" diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index a0683307f8f..ef1a8c3dfe0 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -356,27 +356,17 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE return result; } } - else if (pDesc->dwValidData & DMUS_OBJ_STREAM) { - /* load object from stream */ - TRACE(": loading from stream\n"); - /* create universal stream and associate it with given one */ - result = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream); - if (FAILED(result)) { - ERR(": could not create generic stream\n"); - return result; - } - result = IDirectMusicLoaderGenericStream_Attach(pStream, pDesc->pStream); - if (FAILED(result)) - { - ERR(": failed to attach stream\n"); - IStream_Release (pStream); - return result; - } - } else { - /* nowhere to load from */ - FIXME(": unknown/unsupported way of loading\n"); - return DMUS_E_LOADER_NOFILENAME; /* test shows this is returned */ - } + else if (pDesc->dwValidData & DMUS_OBJ_STREAM) + { + pStream = pDesc->pStream; + IStream_AddRef(pStream); + } + else + { + /* nowhere to load from */ + FIXME(": unknown/unsupported way of loading\n"); + return DMUS_E_LOADER_NOFILENAME; /* test shows this is returned */ + } if (FAILED(hr = loader_stream_create((IDirectMusicLoader *)iface, pStream, &loader_stream))) return hr; @@ -499,22 +489,11 @@ static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE return DMUS_E_LOADER_FAILEDOPEN; } } - else if (pDesc->dwValidData & DMUS_OBJ_STREAM) { - /* create stream */ - hr = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream); - if (FAILED(hr)) { - ERR(": could not create generic stream\n"); - return DMUS_E_LOADER_FAILEDOPEN; - } - /* attach stream */ - hr = IDirectMusicLoaderGenericStream_Attach(pStream, pDesc->pStream); - if (FAILED(hr)) - { - ERR(": could not attach stream\n"); - IStream_Release (pStream); - return DMUS_E_LOADER_FAILEDOPEN; - } - } + else if (pDesc->dwValidData & DMUS_OBJ_STREAM) + { + pStream = pDesc->pStream; + IStream_AddRef(pStream); + } else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) { /* create stream */ hr = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream); diff --git a/dlls/dmloader/loaderstream.c b/dlls/dmloader/loaderstream.c index 3e01d423483..5b7862c16f8 100644 --- a/dlls/dmloader/loaderstream.c +++ b/dlls/dmloader/loaderstream.c @@ -286,7 +286,6 @@ HRESULT loader_stream_create(IDirectMusicLoader *loader, IStream *stream, static ULONG WINAPI IDirectMusicLoaderFileStream_IStream_AddRef (LPSTREAM iface); static ULONG WINAPI IDirectMusicLoaderResourceStream_IStream_AddRef (LPSTREAM iface); -static ULONG WINAPI IDirectMusicLoaderGenericStream_IStream_AddRef (LPSTREAM iface); /***************************************************************************** @@ -710,210 +709,3 @@ HRESULT DMUSIC_CreateDirectMusicLoaderResourceStream (void** ppobj) { return IDirectMusicLoaderResourceStream_IStream_QueryInterface ((LPSTREAM)&obj->StreamVtbl, &IID_IStream, ppobj); } - - -/***************************************************************************** - * IDirectMusicLoaderGenericStream implementation - */ -/* Custom : */ - -static void IDirectMusicLoaderGenericStream_Detach (LPSTREAM iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - - if (This->pStream) - IStream_Release (This->pStream); - This->pStream = NULL; -} - -HRESULT WINAPI IDirectMusicLoaderGenericStream_Attach (LPSTREAM iface, LPSTREAM pStream) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - - TRACE("(%p, %p)\n", This, pStream); - if (!pStream) { - WARN(": invalid pStream\n"); - return E_FAIL; - } - - IDirectMusicLoaderGenericStream_Detach (iface); - IStream_Clone (pStream, &This->pStream); - - return S_OK; -} - - -/* IUnknown/IStream part: */ -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_QueryInterface (LPSTREAM iface, REFIID riid, void** ppobj) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - - TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ppobj); - if (IsEqualIID (riid, &IID_IUnknown) || - IsEqualIID (riid, &IID_IStream)) { - *ppobj = &This->StreamVtbl; - IDirectMusicLoaderGenericStream_IStream_AddRef ((LPSTREAM)&This->StreamVtbl); - return S_OK; - } - - WARN(": not found\n"); - return E_NOINTERFACE; -} - -static ULONG WINAPI IDirectMusicLoaderGenericStream_IStream_AddRef (LPSTREAM iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - TRACE("(%p): AddRef from %ld\n", This, This->dwRef); - return InterlockedIncrement (&This->dwRef); -} - -static ULONG WINAPI IDirectMusicLoaderGenericStream_IStream_Release (LPSTREAM iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - - DWORD dwRef = InterlockedDecrement (&This->dwRef); - TRACE("(%p): ReleaseRef to %ld\n", This, dwRef); - if (dwRef == 0) { - IDirectMusicLoaderGenericStream_Detach (iface); - HeapFree (GetProcessHeap(), 0, This); - } - - return dwRef; -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_Read (LPSTREAM iface, void* pv, ULONG cb, ULONG* pcbRead) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - - TRACE_(dmfileraw)("(%p, %p, %#lx, %p): redirecting to low-level stream\n", This, pv, cb, pcbRead); - if (!This->pStream) - return E_FAIL; - - return IStream_Read (This->pStream, pv, cb, pcbRead); -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_Seek (LPSTREAM iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - TRACE_(dmfileraw)("(%p, %s, %s, %p): redirecting to low-level stream\n", This, wine_dbgstr_longlong(dlibMove.QuadPart), resolve_STREAM_SEEK(dwOrigin), plibNewPosition); - if (!This->pStream) - return E_FAIL; - - return IStream_Seek (This->pStream, dlibMove, dwOrigin, plibNewPosition); -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_Clone (LPSTREAM iface, IStream** ppstm) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - LPSTREAM pOther = NULL; - LPSTREAM pLowLevel = NULL; - HRESULT result; - - TRACE("(%p, %p)\n", iface, ppstm); - result = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pOther); - if (FAILED(result)) return result; - - if (FAILED(IStream_Clone (This->pStream, &pLowLevel))) { - IStream_Release(pOther); - return E_FAIL; - } - IDirectMusicLoaderGenericStream_Attach (pOther, pLowLevel); - - TRACE(": succeeded\n"); - *ppstm = pOther; - return S_OK; -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_Write (LPSTREAM iface, const void* pv, ULONG cb, ULONG* pcbWritten) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - TRACE_(dmfileraw)("(%p, %p, %#lx, %p): redirecting to low-level stream\n", This, pv, cb, pcbWritten); - if (!This->pStream) - return E_FAIL; - - return IStream_Write (This->pStream, pv, cb, pcbWritten); -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_SetSize (LPSTREAM iface, ULARGE_INTEGER libNewSize) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - TRACE("(%p, %s): redirecting to low-level stream\n", This, wine_dbgstr_longlong(libNewSize.QuadPart)); - if (!This->pStream) - return E_FAIL; - - return IStream_SetSize (This->pStream, libNewSize); -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_CopyTo (LPSTREAM iface, IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - TRACE("(%p, %p, %s, %p, %p): redirecting to low-level stream\n", This, pstm, wine_dbgstr_longlong(cb.QuadPart), pcbRead, pcbWritten); - if (!This->pStream) - return E_FAIL; - - return IStream_CopyTo (This->pStream, pstm, cb, pcbRead, pcbWritten); -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_Commit (LPSTREAM iface, DWORD grfCommitFlags) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - TRACE("(%p, %#lx): redirecting to low-level stream\n", This, grfCommitFlags); - if (!This->pStream) - return E_FAIL; - - return IStream_Commit (This->pStream, grfCommitFlags); -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_Revert (LPSTREAM iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - TRACE("(%p): redirecting to low-level stream\n", This); - if (!This->pStream) - return E_FAIL; - - return IStream_Revert (This->pStream); -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_LockRegion (LPSTREAM iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - TRACE("(%p, %s, %s, %#lx): redirecting to low-level stream\n", This, wine_dbgstr_longlong(libOffset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), dwLockType); - if (!This->pStream) - return E_FAIL; - - return IStream_LockRegion (This->pStream, libOffset, cb, dwLockType); -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_UnlockRegion (LPSTREAM iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - TRACE("(%p, %s, %s, %#lx): redirecting to low-level stream\n", This, wine_dbgstr_longlong(libOffset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), dwLockType); - if (!This->pStream) - return E_FAIL; - - return IStream_UnlockRegion (This->pStream, libOffset, cb, dwLockType); -} - -static HRESULT WINAPI IDirectMusicLoaderGenericStream_IStream_Stat (LPSTREAM iface, STATSTG* pstatstg, DWORD grfStatFlag) { - ICOM_THIS_MULTI(IDirectMusicLoaderGenericStream, StreamVtbl, iface); - TRACE("(%p, %p, %#lx): redirecting to low-level stream\n", This, pstatstg, grfStatFlag); - if (!This->pStream) - return E_FAIL; - - return IStream_Stat (This->pStream, pstatstg, grfStatFlag); -} - -static const IStreamVtbl DirectMusicLoaderGenericStream_Stream_Vtbl = { - IDirectMusicLoaderGenericStream_IStream_QueryInterface, - IDirectMusicLoaderGenericStream_IStream_AddRef, - IDirectMusicLoaderGenericStream_IStream_Release, - IDirectMusicLoaderGenericStream_IStream_Read, - IDirectMusicLoaderGenericStream_IStream_Write, - IDirectMusicLoaderGenericStream_IStream_Seek, - IDirectMusicLoaderGenericStream_IStream_SetSize, - IDirectMusicLoaderGenericStream_IStream_CopyTo, - IDirectMusicLoaderGenericStream_IStream_Commit, - IDirectMusicLoaderGenericStream_IStream_Revert, - IDirectMusicLoaderGenericStream_IStream_LockRegion, - IDirectMusicLoaderGenericStream_IStream_UnlockRegion, - IDirectMusicLoaderGenericStream_IStream_Stat, - IDirectMusicLoaderGenericStream_IStream_Clone -}; - -HRESULT DMUSIC_CreateDirectMusicLoaderGenericStream (void** ppobj) { - IDirectMusicLoaderGenericStream *obj; - - TRACE("(%p)\n", ppobj); - - *ppobj = NULL; - if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->StreamVtbl = &DirectMusicLoaderGenericStream_Stream_Vtbl; - obj->dwRef = 0; /* will be inited with QueryInterface */ - - return IDirectMusicLoaderGenericStream_IStream_QueryInterface ((LPSTREAM)&obj->StreamVtbl, &IID_IStream, ppobj); -} From 5b9859b99f2218f6b6da3dbe9eca2dc7fe7dd89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 28 Sep 2023 08:20:46 +0200 Subject: [PATCH 0941/1148] dmime/tests: Remove some duplicated tests. These are now more extensively tested in dmime/tests/dmime.c. (cherry picked from commit ed2eebf2f5b9390f5edad605860a61851ba15623) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/performance.c | 147 --------------------------------- 1 file changed, 147 deletions(-) diff --git a/dlls/dmime/tests/performance.c b/dlls/dmime/tests/performance.c index 5cb91a78387..6a8efcdf28a 100644 --- a/dlls/dmime/tests/performance.c +++ b/dlls/dmime/tests/performance.c @@ -489,151 +489,6 @@ static void test_COM(void) ok (refcount == 0, "refcount == %lu, expected 0\n", refcount); } -static void test_notification_type(void) -{ - static unsigned char rifffile[8+4+8+16+8+256] = "RIFF\x24\x01\x00\x00WAVE" /* header: 4 ("WAVE") + (8 + 16) (format segment) + (8 + 256) (data segment) = 0x124 */ - "fmt \x10\x00\x00\x00\x01\x00\x20\x00\xAC\x44\x00\x00\x10\xB1\x02\x00\x04\x00\x10\x00" /* format segment: PCM, 2 chan, 44100 Hz, 16 bits */ - "data\x00\x01\x00\x00"; /* 256 byte data segment (silence) */ - - IDirectMusicPerformance8 *perf; - IDirectMusic *music = NULL; - IDirectMusicSegment8 *prime_segment8; - IDirectMusicSegment8 *segment8 = NULL; - IDirectMusicLoader8 *loader; - IDirectMusicAudioPath8 *path; - IDirectMusicSegmentState *state; - IDirectSound *dsound = NULL; - HRESULT hr; - DWORD result; - HANDLE messages; - DMUS_NOTIFICATION_PMSG *msg; - BOOL found_end = FALSE; - DMUS_OBJECTDESC desc = {0}; - - hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, - CLSCTX_INPROC_SERVER, &IID_IDirectMusicPerformance8, (void**)&perf); - ok(hr == S_OK, "CoCreateInstance failed: %#lx\n", hr); - - hr = IDirectMusicPerformance8_InitAudio(perf, &music, &dsound, NULL, DMUS_APATH_DYNAMIC_STEREO, 64, DMUS_AUDIOF_ALL, NULL); - ok(music != NULL, "Didn't get IDirectMusic pointer\n"); - ok(dsound != NULL, "Didn't get IDirectSound pointer\n"); - - hr = CoCreateInstance(&CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicLoader8, (void**)&loader); - ok(hr == S_OK, "CoCreateInstance failed: %#lx\n", hr); - - messages = CreateEventA( NULL, FALSE, FALSE, NULL ); - - hr = IDirectMusicPerformance8_AddNotificationType(perf, &GUID_NOTIFICATION_SEGMENT); - ok(hr == S_OK, "Failed: %#lx\n", hr); - - hr = IDirectMusicPerformance8_SetNotificationHandle(perf, messages, 0); - ok(hr == S_OK, "Failed: %#lx\n", hr); - - hr = IDirectMusicPerformance8_GetDefaultAudioPath(perf, &path); - ok(hr == S_OK, "Failed: %#lx\n", hr); - ok(path != NULL, "Didn't get IDirectMusicAudioPath pointer\n"); - - desc.dwSize = sizeof(DMUS_OBJECTDESC); - desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_MEMORY; - desc.guidClass = CLSID_DirectMusicSegment; - desc.pbMemData = rifffile; - desc.llMemLength = sizeof(rifffile); - hr = IDirectMusicLoader8_GetObject(loader, &desc, &IID_IDirectMusicSegment8, (void**)&prime_segment8); - ok(hr == S_OK, "Failed: %#lx\n", hr); - ok(prime_segment8 != NULL, "Didn't get IDirectMusicSegment pointer\n"); - - hr = IDirectMusicSegment8_Download(prime_segment8, (IUnknown*)path); - ok(hr == S_OK, "Download failed: %#lx\n", hr); - - hr = IDirectMusicPerformance8_PlaySegmentEx(perf, (IUnknown*)prime_segment8, - NULL, NULL, DMUS_SEGF_SECONDARY, 0, &state, NULL, (IUnknown*)path); - ok(hr == S_OK, "PlaySegmentEx failed: %#lx\n", hr); - ok(state != NULL, "Didn't get IDirectMusicSegmentState pointer\n"); - - while (!found_end) { - result = WaitForSingleObject(messages, 500); - todo_wine ok(result == WAIT_OBJECT_0, "Failed: %ld\n", result); - if (result != WAIT_OBJECT_0) - break; - - msg = NULL; - hr = IDirectMusicPerformance8_GetNotificationPMsg(perf, &msg); - ok(hr == S_OK, "Failed: %#lx\n", hr); - ok(msg != NULL, "Unexpected NULL pointer\n"); - if (FAILED(hr) || !msg) - break; - - trace("Notification: %ld\n", msg->dwNotificationOption); - - if (msg->dwNotificationOption == DMUS_NOTIFICATION_SEGEND || - msg->dwNotificationOption == DMUS_NOTIFICATION_SEGALMOSTEND) { - ok(msg->punkUser != NULL, "Unexpected NULL pointer\n"); - if (msg->punkUser) { - IDirectMusicSegmentState8 *segmentstate; - IDirectMusicSegment *segment; - - hr = IUnknown_QueryInterface(msg->punkUser, &IID_IDirectMusicSegmentState8, (void**)&segmentstate); - ok(hr == S_OK, "Failed: %#lx\n", hr); - - hr = IDirectMusicSegmentState8_GetSegment(segmentstate, &segment); - ok(hr == S_OK, "Failed: %#lx\n", hr); - if (FAILED(hr)) { - IDirectMusicSegmentState8_Release(segmentstate); - break; - } - - hr = IDirectMusicSegment_QueryInterface(segment, &IID_IDirectMusicSegment8, (void**)&segment8); - ok(hr == S_OK, "Failed: %#lx\n", hr); - - found_end = TRUE; - - IDirectMusicSegment_Release(segment); - IDirectMusicSegmentState8_Release(segmentstate); - } - } - - IDirectMusicPerformance8_FreePMsg(perf, (DMUS_PMSG*)msg); - } - todo_wine ok(prime_segment8 == segment8, "Wrong end segment\n"); - todo_wine ok(found_end, "Didn't receive DMUS_NOTIFICATION_SEGEND message\n"); - - CloseHandle(messages); - - if(segment8) - IDirectMusicSegment8_Release(segment8); - IDirectSound_Release(dsound); - IDirectMusicSegmentState_Release(state); - IDirectMusicAudioPath_Release(path); - IDirectMusicLoader8_Release(loader); - IDirectMusic_Release(music); - IDirectMusicPerformance8_Release(perf); -} - -static void test_performance_graph(void) -{ - HRESULT hr; - IDirectMusicPerformance8 *perf; - IDirectMusicGraph *graph = NULL, *graph2; - - create_performance(&perf, NULL, NULL, FALSE); - hr = IDirectMusicPerformance8_Init(perf, NULL, NULL, NULL); - ok(hr == S_OK, "Init failed: %#lx\n", hr); - - hr = IDirectMusicPerformance8_GetGraph(perf, NULL); - ok(hr == E_POINTER, "Failed: %#lx\n", hr); - - hr = IDirectMusicPerformance8_GetGraph(perf, &graph2); - ok(hr == DMUS_E_NOT_FOUND, "Failed: %#lx\n", hr); - ok(graph2 == NULL, "unexpected pointer.\n"); - - hr = IDirectMusicPerformance8_QueryInterface(perf, &IID_IDirectMusicGraph, (void**)&graph); - ok(hr == S_OK, "Failed: %#lx\n", hr); - - if (graph) - IDirectMusicGraph_Release(graph); - destroy_performance(perf, NULL, NULL); -} - START_TEST( performance ) { HRESULT hr; @@ -653,8 +508,6 @@ START_TEST( performance ) test_COM(); test_createport(); test_pchannel(); - test_notification_type(); - test_performance_graph(); CoUninitialize(); } From 8c01b5f067867b985f694cd1f93b4f1666f69a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 28 Sep 2023 08:25:39 +0200 Subject: [PATCH 0942/1148] dmime/tests: Move performance tests into dmime.c. (cherry picked from commit de27d59a8a71919f22f6e69b673588fdb43daac8) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/Makefile.in | 3 +- dlls/dmime/tests/dmime.c | 458 +++++++++++++++++++++++++++++ dlls/dmime/tests/performance.c | 513 --------------------------------- 3 files changed, 459 insertions(+), 515 deletions(-) delete mode 100644 dlls/dmime/tests/performance.c diff --git a/dlls/dmime/tests/Makefile.in b/dlls/dmime/tests/Makefile.in index 2491f9ff1b8..f07cdf835e9 100644 --- a/dlls/dmime/tests/Makefile.in +++ b/dlls/dmime/tests/Makefile.in @@ -2,7 +2,6 @@ TESTDLL = dmime.dll IMPORTS = user32 ole32 dsound C_SRCS = \ - dmime.c \ - performance.c + dmime.c RC_SRCS = resource.rc diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 1266c5d053b..12056f15376 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -21,12 +21,16 @@ #include #include #include +#include #include #include #include #include #include +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); +DEFINE_GUID(GUID_Bunk,0xFFFFFFFF,0xFFFF,0xFFFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF); + static ULONG get_refcount(void *iface) { IUnknown *unknown = iface; @@ -551,6 +555,43 @@ static HRESULT test_loader_stream_create(IStream *stream, IDirectMusicLoader *lo return S_OK; } +static void create_performance(IDirectMusicPerformance8 **performance, IDirectMusic **dmusic, + IDirectSound **dsound, BOOL set_cooplevel) +{ + HRESULT hr; + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance8, (void **)performance); + ok(hr == S_OK, "DirectMusicPerformance create failed: %#lx\n", hr); + if (dmusic) { + hr = CoCreateInstance(&CLSID_DirectMusic, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusic8, + (void **)dmusic); + ok(hr == S_OK, "DirectMusic create failed: %#lx\n", hr); + } + if (dsound) { + hr = DirectSoundCreate8(NULL, (IDirectSound8 **)dsound, NULL); + ok(hr == S_OK, "DirectSoundCreate failed: %#lx\n", hr); + if (set_cooplevel) { + hr = IDirectSound_SetCooperativeLevel(*dsound, GetForegroundWindow(), DSSCL_PRIORITY); + ok(hr == S_OK, "SetCooperativeLevel failed: %#lx\n", hr); + } + } +} + +static void destroy_performance(IDirectMusicPerformance8 *performance, IDirectMusic *dmusic, + IDirectSound *dsound) +{ + HRESULT hr; + + hr = IDirectMusicPerformance8_CloseDown(performance); + ok(hr == S_OK, "CloseDown failed: %#lx\n", hr); + IDirectMusicPerformance8_Release(performance); + if (dmusic) + IDirectMusic_Release(dmusic); + if (dsound) + IDirectSound_Release(dsound); +} + static BOOL missing_dmime(void) { IDirectMusicSegment8 *dms; @@ -945,6 +986,49 @@ static void test_COM_track(void) } } +static void test_COM_performance(void) +{ + IDirectMusicPerformance *dmp = (IDirectMusicPerformance*)0xdeadbeef; + IDirectMusicPerformance *dmp2; + IDirectMusicPerformance8 *dmp8; + ULONG refcount; + HRESULT hr; + + /* COM aggregation */ + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, (IUnknown *)0xdeadbeef, CLSCTX_INPROC_SERVER, + &IID_IUnknown, (void**)&dmp); + ok(hr == CLASS_E_NOAGGREGATION, + "DirectMusicPerformance create failed: %#lx, expected CLASS_E_NOAGGREGATION\n", hr); + ok(!dmp, "dmp = %p\n", dmp); + + /* Invalid RIID */ + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicObject, (void**)&dmp); + ok(hr == E_NOINTERFACE, "DirectMusicPerformance create failed: %#lx, expected E_NOINTERFACE\n", hr); + + /* Same refcount */ + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void**)&dmp); + ok(hr == S_OK, "DirectMusicPerformance create failed: %#lx, expected S_OK\n", hr); + refcount = IDirectMusicPerformance_AddRef(dmp); + ok (refcount == 2, "refcount == %lu, expected 2\n", refcount); + hr = IDirectMusicPerformance_QueryInterface(dmp, &IID_IDirectMusicPerformance2, (void**)&dmp2); + ok(hr == S_OK, "QueryInterface for IID_IDirectMusicPerformance2 failed: %#lx\n", hr); + IDirectMusicPerformance_AddRef(dmp); + refcount = IDirectMusicPerformance_Release(dmp); + ok (refcount == 3, "refcount == %lu, expected 3\n", refcount); + hr = IDirectMusicPerformance_QueryInterface(dmp, &IID_IDirectMusicPerformance8, (void**)&dmp8); + ok(hr == S_OK, "QueryInterface for IID_IDirectMusicPerformance8 failed: %#lx\n", hr); + refcount = IDirectMusicPerformance_Release(dmp); + ok (refcount == 3, "refcount == %lu, expected 3\n", refcount); + refcount = IDirectMusicPerformance8_Release(dmp8); + ok (refcount == 2, "refcount == %lu, expected 2\n", refcount); + refcount = IDirectMusicPerformance_Release(dmp2); + ok (refcount == 1, "refcount == %lu, expected 1\n", refcount); + refcount = IDirectMusicPerformance_Release(dmp); + ok (refcount == 0, "refcount == %lu, expected 0\n", refcount); +} + static void test_audiopathconfig(void) { IDirectMusicObject *dmo; @@ -1809,6 +1893,376 @@ static void test_parsedescriptor(void) } } +static void test_performance_InitAudio(void) +{ + IDirectMusicPerformance8 *performance; + IDirectMusic *dmusic; + IDirectSound *dsound; + IDirectMusicPort *port; + IDirectMusicAudioPath *path; + DWORD channel, group; + HRESULT hr; + ULONG ref; + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance8, (void **)&performance); + if (hr != S_OK) { + win_skip("Cannot create DirectMusicPerformance object (%lx)\n", hr); + CoUninitialize(); + return; + } + + dsound = NULL; + hr = IDirectMusicPerformance8_InitAudio(performance, NULL, &dsound, NULL, + DMUS_APATH_SHARED_STEREOPLUSREVERB, 128, DMUS_AUDIOF_ALL, NULL); + if (hr != S_OK) { + IDirectMusicPerformance8_Release(performance); + win_skip("InitAudio failed (%lx)\n", hr); + return; + } + + hr = IDirectMusicPerformance8_PChannelInfo(performance, 128, &port, NULL, NULL); + ok(hr == E_INVALIDARG, "PChannelInfo failed, got %#lx\n", hr); + hr = IDirectMusicPerformance8_PChannelInfo(performance, 127, &port, NULL, NULL); + ok(hr == S_OK, "PChannelInfo failed, got %#lx\n", hr); + IDirectMusicPort_Release(port); + port = NULL; + hr = IDirectMusicPerformance8_PChannelInfo(performance, 0, &port, NULL, NULL); + ok(hr == S_OK, "PChannelInfo failed, got %#lx\n", hr); + ok(port != NULL, "IDirectMusicPort not set\n"); + hr = IDirectMusicPerformance8_AssignPChannel(performance, 0, port, 0, 0); + todo_wine ok(hr == DMUS_E_AUDIOPATHS_IN_USE, "AssignPChannel failed (%#lx)\n", hr); + hr = IDirectMusicPerformance8_AssignPChannelBlock(performance, 0, port, 0); + todo_wine ok(hr == DMUS_E_AUDIOPATHS_IN_USE, "AssignPChannelBlock failed (%#lx)\n", hr); + IDirectMusicPort_Release(port); + + hr = IDirectMusicPerformance8_GetDefaultAudioPath(performance, &path); + ok(hr == S_OK, "Failed to call GetDefaultAudioPath (%lx)\n", hr); + if (hr == S_OK) + IDirectMusicAudioPath_Release(path); + + hr = IDirectMusicPerformance8_CloseDown(performance); + ok(hr == S_OK, "Failed to call CloseDown (%lx)\n", hr); + + IDirectMusicPerformance8_Release(performance); + + /* Auto generated dmusic and dsound */ + create_performance(&performance, NULL, NULL, FALSE); + hr = IDirectMusicPerformance8_InitAudio(performance, NULL, NULL, NULL, 0, 64, 0, NULL); + ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); + hr = IDirectMusicPerformance8_PChannelInfo(performance, 0, &port, NULL, NULL); + ok(hr == E_INVALIDARG, "PChannelInfo failed, got %#lx\n", hr); + destroy_performance(performance, NULL, NULL); + + /* Refcounts for auto generated dmusic and dsound */ + create_performance(&performance, NULL, NULL, FALSE); + dmusic = NULL; + dsound = NULL; + hr = IDirectMusicPerformance8_InitAudio(performance, &dmusic, &dsound, NULL, 0, 64, 0, NULL); + ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); + ref = get_refcount(dsound); + ok(ref == 3, "dsound ref count got %ld expected 3\n", ref); + ref = get_refcount(dmusic); + ok(ref == 2, "dmusic ref count got %ld expected 2\n", ref); + destroy_performance(performance, NULL, NULL); + + /* dsound without SetCooperativeLevel() */ + create_performance(&performance, NULL, &dsound, FALSE); + hr = IDirectMusicPerformance8_InitAudio(performance, NULL, &dsound, NULL, 0, 0, 0, NULL); + todo_wine ok(hr == DSERR_PRIOLEVELNEEDED, "InitAudio failed: %#lx\n", hr); + destroy_performance(performance, NULL, dsound); + + /* Using the wrong CLSID_DirectSound */ + create_performance(&performance, NULL, NULL, FALSE); + hr = DirectSoundCreate(NULL, &dsound, NULL); + ok(hr == S_OK, "DirectSoundCreate failed: %#lx\n", hr); + hr = IDirectMusicPerformance8_InitAudio(performance, NULL, &dsound, NULL, 0, 0, 0, NULL); + todo_wine ok(hr == E_NOINTERFACE, "InitAudio failed: %#lx\n", hr); + destroy_performance(performance, NULL, dsound); + + /* Init() works with just a CLSID_DirectSound */ + create_performance(&performance, NULL, NULL, FALSE); + hr = DirectSoundCreate(NULL, &dsound, NULL); + ok(hr == S_OK, "DirectSoundCreate failed: %#lx\n", hr); + hr = IDirectSound_SetCooperativeLevel(dsound, GetForegroundWindow(), DSSCL_PRIORITY); + ok(hr == S_OK, "SetCooperativeLevel failed: %#lx\n", hr); + hr = IDirectMusicPerformance8_Init(performance, NULL, dsound, NULL); + ok(hr == S_OK, "Init failed: %#lx\n", hr); + destroy_performance(performance, NULL, dsound); + + /* Init() followed by InitAudio() */ + create_performance(&performance, NULL, &dsound, TRUE); + hr = IDirectMusicPerformance8_Init(performance, NULL, dsound, NULL); + ok(hr == S_OK, "Init failed: %#lx\n", hr); + hr = IDirectMusicPerformance8_InitAudio(performance, NULL, &dsound, NULL, 0, 0, 0, NULL); + ok(hr == DMUS_E_ALREADY_INITED, "InitAudio failed: %#lx\n", hr); + destroy_performance(performance, NULL, dsound); + + /* Provided dmusic and dsound */ + create_performance(&performance, &dmusic, &dsound, TRUE); + hr = IDirectMusicPerformance8_InitAudio(performance, &dmusic, &dsound, NULL, 0, 64, 0, NULL); + ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); + ref = get_refcount(dsound); + todo_wine ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); + ref = get_refcount(dmusic); + ok(ref == 2, "dmusic ref count got %ld expected 2\n", ref); + destroy_performance(performance, dmusic, dsound); + + /* Provided dmusic initialized with SetDirectSound */ + create_performance(&performance, &dmusic, &dsound, TRUE); + hr = IDirectMusic_SetDirectSound(dmusic, dsound, NULL); + ok(hr == S_OK, "SetDirectSound failed: %#lx\n", hr); + ref = get_refcount(dsound); + ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); + hr = IDirectMusicPerformance8_InitAudio(performance, &dmusic, NULL, NULL, 0, 64, 0, NULL); + ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); + ref = get_refcount(dsound); + todo_wine ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); + ref = get_refcount(dmusic); + ok(ref == 2, "dmusic ref count got %ld expected 2\n", ref); + destroy_performance(performance, dmusic, dsound); + + /* Provided dmusic and dsound, dmusic initialized with SetDirectSound */ + create_performance(&performance, &dmusic, &dsound, TRUE); + hr = IDirectMusic_SetDirectSound(dmusic, dsound, NULL); + ok(hr == S_OK, "SetDirectSound failed: %#lx\n", hr); + ref = get_refcount(dsound); + ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); + hr = IDirectMusicPerformance8_InitAudio(performance, &dmusic, &dsound, NULL, 0, 64, 0, NULL); + ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); + ref = get_refcount(dsound); + ok(ref == 3, "dsound ref count got %ld expected 3\n", ref); + ref = get_refcount(dmusic); + ok(ref == 2, "dmusic ref count got %ld expected 2\n", ref); + destroy_performance(performance, dmusic, dsound); + + /* InitAudio with perf channel count not a multiple of 16 rounds up */ + create_performance(&performance, NULL, NULL, TRUE); + hr = IDirectMusicPerformance8_InitAudio(performance, NULL, NULL, NULL, + DMUS_APATH_SHARED_STEREOPLUSREVERB, 29, DMUS_AUDIOF_ALL, NULL); + ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); + hr = IDirectMusicPerformance8_PChannelInfo(performance, 31, &port, &group, &channel); + ok(hr == S_OK && group == 2 && channel == 15, + "PChannelInfo failed, got %#lx, %lu, %lu\n", hr, group, channel); + hr = IDirectMusicPerformance8_PChannelInfo(performance, 32, &port, NULL, NULL); + ok(hr == E_INVALIDARG, "PChannelInfo failed, got %#lx\n", hr); + destroy_performance(performance, NULL, NULL); +} + +static void test_performance_createport(void) +{ + IDirectMusicPerformance8 *perf; + IDirectMusic *music = NULL; + IDirectMusicPort *port = NULL; + DMUS_PORTCAPS portcaps; + DMUS_PORTPARAMS portparams; + DWORD i; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, + CLSCTX_INPROC_SERVER, &IID_IDirectMusicPerformance8, (void**)&perf); + ok(hr == S_OK, "CoCreateInstance failed: %#lx\n", hr); + + hr = IDirectMusicPerformance8_Init(perf, &music, NULL, NULL); + ok(hr == S_OK, "Init failed: %#lx\n", hr); + ok(music != NULL, "Didn't get IDirectMusic pointer\n"); + + i = 0; + while(1){ + portcaps.dwSize = sizeof(portcaps); + + hr = IDirectMusic_EnumPort(music, i, &portcaps); + ok(hr == S_OK || hr == S_FALSE || (i == 0 && hr == E_INVALIDARG), "EnumPort failed: %#lx\n", hr); + if(hr != S_OK) + break; + + ok(portcaps.dwSize == sizeof(portcaps), "Got unexpected portcaps struct size: %lu\n", portcaps.dwSize); + trace("portcaps(%lu).dwFlags: %#lx\n", i, portcaps.dwFlags); + trace("portcaps(%lu).guidPort: %s\n", i, wine_dbgstr_guid(&portcaps.guidPort)); + trace("portcaps(%lu).dwClass: %#lx\n", i, portcaps.dwClass); + trace("portcaps(%lu).dwType: %#lx\n", i, portcaps.dwType); + trace("portcaps(%lu).dwMemorySize: %#lx\n", i, portcaps.dwMemorySize); + trace("portcaps(%lu).dwMaxChannelGroups: %lu\n", i, portcaps.dwMaxChannelGroups); + trace("portcaps(%lu).dwMaxVoices: %lu\n", i, portcaps.dwMaxVoices); + trace("portcaps(%lu).dwMaxAudioChannels: %lu\n", i, portcaps.dwMaxAudioChannels); + trace("portcaps(%lu).dwEffectFlags: %#lx\n", i, portcaps.dwEffectFlags); + trace("portcaps(%lu).wszDescription: %s\n", i, wine_dbgstr_w(portcaps.wszDescription)); + + ++i; + } + + if(i == 0){ + win_skip("No ports available, skipping tests\n"); + return; + } + + portparams.dwSize = sizeof(portparams); + + /* dwValidParams == 0 -> S_OK, filled struct */ + portparams.dwValidParams = 0; + hr = IDirectMusic_CreatePort(music, &CLSID_DirectMusicSynth, &portparams, &port, NULL); + ok(hr == S_OK, "CreatePort failed: %#lx\n", hr); + ok(port != NULL, "Didn't get IDirectMusicPort pointer\n"); + ok(portparams.dwValidParams, "portparams struct was not filled in\n"); + IDirectMusicPort_Release(port); + port = NULL; + + /* dwValidParams != 0, invalid param -> S_FALSE, filled struct */ + portparams.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS; + portparams.dwChannelGroups = 0; + hr = IDirectMusic_CreatePort(music, &CLSID_DirectMusicSynth, &portparams, &port, NULL); + todo_wine ok(hr == S_FALSE, "CreatePort failed: %#lx\n", hr); + ok(port != NULL, "Didn't get IDirectMusicPort pointer\n"); + ok(portparams.dwValidParams, "portparams struct was not filled in\n"); + IDirectMusicPort_Release(port); + port = NULL; + + /* dwValidParams != 0, valid params -> S_OK */ + hr = IDirectMusic_CreatePort(music, &CLSID_DirectMusicSynth, &portparams, &port, NULL); + ok(hr == S_OK, "CreatePort failed: %#lx\n", hr); + ok(port != NULL, "Didn't get IDirectMusicPort pointer\n"); + IDirectMusicPort_Release(port); + port = NULL; + + /* GUID_NULL succeeds */ + portparams.dwValidParams = 0; + hr = IDirectMusic_CreatePort(music, &GUID_NULL, &portparams, &port, NULL); + ok(hr == S_OK, "CreatePort failed: %#lx\n", hr); + ok(port != NULL, "Didn't get IDirectMusicPort pointer\n"); + ok(portparams.dwValidParams, "portparams struct was not filled in\n"); + IDirectMusicPort_Release(port); + port = NULL; + + /* null GUID fails */ + portparams.dwValidParams = 0; + hr = IDirectMusic_CreatePort(music, NULL, &portparams, &port, NULL); + ok(hr == E_POINTER, "CreatePort failed: %#lx\n", hr); + ok(port == NULL, "Get IDirectMusicPort pointer? %p\n", port); + ok(portparams.dwValidParams == 0, "portparams struct was filled in?\n"); + + /* garbage GUID fails */ + portparams.dwValidParams = 0; + hr = IDirectMusic_CreatePort(music, &GUID_Bunk, &portparams, &port, NULL); + ok(hr == E_NOINTERFACE, "CreatePort failed: %#lx\n", hr); + ok(port == NULL, "Get IDirectMusicPort pointer? %p\n", port); + ok(portparams.dwValidParams == 0, "portparams struct was filled in?\n"); + + hr = IDirectMusicPerformance8_CloseDown(perf); + ok(hr == S_OK, "CloseDown failed: %#lx\n", hr); + + IDirectMusic_Release(music); + IDirectMusicPerformance8_Release(perf); +} + +static void test_performance_pchannel(void) +{ + IDirectMusicPerformance8 *perf; + IDirectMusicPort *port = NULL, *port2; + DWORD channel, group; + unsigned int i; + HRESULT hr; + + create_performance(&perf, NULL, NULL, TRUE); + hr = IDirectMusicPerformance8_Init(perf, NULL, NULL, NULL); + ok(hr == S_OK, "Init failed: %#lx\n", hr); + hr = IDirectMusicPerformance8_PChannelInfo(perf, 0, &port, NULL, NULL); + ok(hr == E_INVALIDARG && !port, "PChannelInfo failed, got %#lx, %p\n", hr, port); + + /* Add default port. Sets PChannels 0-15 to the corresponding channels in group 1 */ + hr = IDirectMusicPerformance8_AddPort(perf, NULL); + ok(hr == S_OK, "AddPort of default port failed: %#lx\n", hr); + hr = IDirectMusicPerformance8_PChannelInfo(perf, 0, NULL, NULL, NULL); + ok(hr == S_OK, "PChannelInfo failed, got %#lx\n", hr); + hr = IDirectMusicPerformance8_PChannelInfo(perf, 0, &port, NULL, NULL); + ok(hr == S_OK && port, "PChannelInfo failed, got %#lx, %p\n", hr, port); + for (i = 1; i < 16; i++) { + hr = IDirectMusicPerformance8_PChannelInfo(perf, i, &port2, &group, &channel); + ok(hr == S_OK && port == port2 && group == 1 && channel == i, + "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); + IDirectMusicPort_Release(port2); + } + + /* Unset PChannels fail to retrieve */ + hr = IDirectMusicPerformance8_PChannelInfo(perf, 16, &port2, NULL, NULL); + ok(hr == E_INVALIDARG, "PChannelInfo failed, got %#lx, %p\n", hr, port); + hr = IDirectMusicPerformance8_PChannelInfo(perf, MAXDWORD - 16, &port2, NULL, NULL); + ok(hr == E_INVALIDARG, "PChannelInfo failed, got %#lx, %p\n", hr, port); + + /* Channel group 0 can be set just fine */ + hr = IDirectMusicPerformance8_AssignPChannel(perf, 0, port, 0, 0); + ok(hr == S_OK, "AssignPChannel failed, got %#lx\n", hr); + hr = IDirectMusicPerformance8_AssignPChannelBlock(perf, 0, port, 0); + ok(hr == S_OK, "AssignPChannelBlock failed, got %#lx\n", hr); + for (i = 1; i < 16; i++) { + hr = IDirectMusicPerformance8_PChannelInfo(perf, i, &port2, &group, &channel); + ok(hr == S_OK && port == port2 && group == 0 && channel == i, + "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); + IDirectMusicPort_Release(port2); + } + + /* Last PChannel Block can be set only individually but not read */ + hr = IDirectMusicPerformance8_AssignPChannel(perf, MAXDWORD, port, 0, 3); + ok(hr == S_OK, "AssignPChannel failed, got %#lx\n", hr); + port2 = (IDirectMusicPort *)0xdeadbeef; + hr = IDirectMusicPerformance8_PChannelInfo(perf, MAXDWORD, &port2, NULL, NULL); + todo_wine ok(hr == E_INVALIDARG && port2 == (IDirectMusicPort *)0xdeadbeef, + "PChannelInfo failed, got %#lx, %p\n", hr, port2); + hr = IDirectMusicPerformance8_AssignPChannelBlock(perf, MAXDWORD, port, 0); + ok(hr == E_INVALIDARG, "AssignPChannelBlock failed, got %#lx\n", hr); + hr = IDirectMusicPerformance8_AssignPChannelBlock(perf, MAXDWORD / 16, port, 1); + todo_wine ok(hr == E_INVALIDARG, "AssignPChannelBlock failed, got %#lx\n", hr); + for (i = MAXDWORD - 15; i < MAXDWORD; i++) { + hr = IDirectMusicPerformance8_AssignPChannel(perf, i, port, 0, 0); + ok(hr == S_OK, "AssignPChannel failed, got %#lx\n", hr); + hr = IDirectMusicPerformance8_PChannelInfo(perf, i, &port2, NULL, NULL); + todo_wine ok(hr == E_INVALIDARG && port2 == (IDirectMusicPort *)0xdeadbeef, + "PChannelInfo failed, got %#lx, %p\n", hr, port2); + } + + /* Second to last PChannel Block can be set only individually and read */ + hr = IDirectMusicPerformance8_AssignPChannelBlock(perf, MAXDWORD / 16 - 1, port, 1); + todo_wine ok(hr == E_INVALIDARG, "AssignPChannelBlock failed, got %#lx\n", hr); + for (i = MAXDWORD - 31; i < MAXDWORD - 15; i++) { + hr = IDirectMusicPerformance8_AssignPChannel(perf, i, port, 1, 7); + ok(hr == S_OK, "AssignPChannel failed, got %#lx\n", hr); + hr = IDirectMusicPerformance8_PChannelInfo(perf, i, &port2, &group, &channel); + ok(hr == S_OK && port2 == port && group == 1 && channel == 7, + "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); + IDirectMusicPort_Release(port2); + } + + /* Third to last PChannel Block behaves normal */ + hr = IDirectMusicPerformance8_AssignPChannelBlock(perf, MAXDWORD / 16 - 2, port, 0); + ok(hr == S_OK, "AssignPChannelBlock failed, got %#lx\n", hr); + for (i = MAXDWORD - 47; i < MAXDWORD - 31; i++) { + hr = IDirectMusicPerformance8_PChannelInfo(perf, i, &port2, &group, &channel); + ok(hr == S_OK && port2 == port && group == 0 && channel == i % 16, + "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); + IDirectMusicPort_Release(port2); + } + + /* One PChannel set in a Block, rest is initialized too */ + hr = IDirectMusicPerformance8_AssignPChannel(perf, 4711, port, 1, 13); + ok(hr == S_OK, "AssignPChannel failed, got %#lx\n", hr); + hr = IDirectMusicPerformance8_PChannelInfo(perf, 4711, &port2, &group, &channel); + ok(hr == S_OK && port2 == port && group == 1 && channel == 13, + "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); + IDirectMusicPort_Release(port2); + group = channel = 0xdeadbeef; + hr = IDirectMusicPerformance8_PChannelInfo(perf, 4712, &port2, &group, &channel); + ok(hr == S_OK && port2 == port && group == 0 && channel == 8, + "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); + IDirectMusicPort_Release(port2); + group = channel = 0xdeadbeef; + hr = IDirectMusicPerformance8_PChannelInfo(perf, 4719, &port2, &group, &channel); + ok(hr == S_OK && port2 == port && group == 0 && channel == 15, + "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); + IDirectMusicPort_Release(port2); + + IDirectMusicPort_Release(port); + destroy_performance(perf, NULL, NULL); +} + static void test_performance_tool(void) { IDirectMusicPerformance *performance; @@ -3169,6 +3623,7 @@ START_TEST(dmime) test_COM_segment(); test_COM_segmentstate(); test_COM_track(); + test_COM_performance(); test_audiopathconfig(); test_graph(); test_segment(); @@ -3176,6 +3631,9 @@ START_TEST(dmime) test_segment_param(); test_track(); test_parsedescriptor(); + test_performance_InitAudio(); + test_performance_createport(); + test_performance_pchannel(); test_performance_tool(); test_performance_graph(); test_performance_time(); diff --git a/dlls/dmime/tests/performance.c b/dlls/dmime/tests/performance.c deleted file mode 100644 index 6a8efcdf28a..00000000000 --- a/dlls/dmime/tests/performance.c +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Unit test suite for IDirectMusicPerformance - * - * Copyright 2010 Austin Lund - * - * 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 -#include -#include -#include -#include - -#include - -DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); -DEFINE_GUID(GUID_Bunk,0xFFFFFFFF,0xFFFF,0xFFFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF); - -static void create_performance(IDirectMusicPerformance8 **performance, IDirectMusic **dmusic, - IDirectSound **dsound, BOOL set_cooplevel) -{ - HRESULT hr; - - hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectMusicPerformance8, (void **)performance); - ok(hr == S_OK, "DirectMusicPerformance create failed: %#lx\n", hr); - if (dmusic) { - hr = CoCreateInstance(&CLSID_DirectMusic, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusic8, - (void **)dmusic); - ok(hr == S_OK, "DirectMusic create failed: %#lx\n", hr); - } - if (dsound) { - hr = DirectSoundCreate8(NULL, (IDirectSound8 **)dsound, NULL); - ok(hr == S_OK, "DirectSoundCreate failed: %#lx\n", hr); - if (set_cooplevel) { - hr = IDirectSound_SetCooperativeLevel(*dsound, GetForegroundWindow(), DSSCL_PRIORITY); - ok(hr == S_OK, "SetCooperativeLevel failed: %#lx\n", hr); - } - } -} - -static void destroy_performance(IDirectMusicPerformance8 *performance, IDirectMusic *dmusic, - IDirectSound *dsound) -{ - HRESULT hr; - - hr = IDirectMusicPerformance8_CloseDown(performance); - ok(hr == S_OK, "CloseDown failed: %#lx\n", hr); - IDirectMusicPerformance8_Release(performance); - if (dmusic) - IDirectMusic_Release(dmusic); - if (dsound) - IDirectSound_Release(dsound); -} - -static ULONG get_refcount(void *iface) -{ - IUnknown *unknown = iface; - IUnknown_AddRef(unknown); - return IUnknown_Release(unknown); -} - -static HRESULT test_InitAudio(void) -{ - IDirectMusicPerformance8 *performance; - IDirectMusic *dmusic; - IDirectSound *dsound; - IDirectMusicPort *port; - IDirectMusicAudioPath *path; - DWORD channel, group; - HRESULT hr; - ULONG ref; - - hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectMusicPerformance8, (void **)&performance); - if (hr != S_OK) { - skip("Cannot create DirectMusicPerformance object (%lx)\n", hr); - CoUninitialize(); - return hr; - } - - dsound = NULL; - hr = IDirectMusicPerformance8_InitAudio(performance, NULL, &dsound, NULL, - DMUS_APATH_SHARED_STEREOPLUSREVERB, 128, DMUS_AUDIOF_ALL, NULL); - if (hr != S_OK) { - IDirectMusicPerformance8_Release(performance); - return hr; - } - - hr = IDirectMusicPerformance8_PChannelInfo(performance, 128, &port, NULL, NULL); - ok(hr == E_INVALIDARG, "PChannelInfo failed, got %#lx\n", hr); - hr = IDirectMusicPerformance8_PChannelInfo(performance, 127, &port, NULL, NULL); - ok(hr == S_OK, "PChannelInfo failed, got %#lx\n", hr); - IDirectMusicPort_Release(port); - port = NULL; - hr = IDirectMusicPerformance8_PChannelInfo(performance, 0, &port, NULL, NULL); - ok(hr == S_OK, "PChannelInfo failed, got %#lx\n", hr); - ok(port != NULL, "IDirectMusicPort not set\n"); - hr = IDirectMusicPerformance8_AssignPChannel(performance, 0, port, 0, 0); - todo_wine ok(hr == DMUS_E_AUDIOPATHS_IN_USE, "AssignPChannel failed (%#lx)\n", hr); - hr = IDirectMusicPerformance8_AssignPChannelBlock(performance, 0, port, 0); - todo_wine ok(hr == DMUS_E_AUDIOPATHS_IN_USE, "AssignPChannelBlock failed (%#lx)\n", hr); - IDirectMusicPort_Release(port); - - hr = IDirectMusicPerformance8_GetDefaultAudioPath(performance, &path); - ok(hr == S_OK, "Failed to call GetDefaultAudioPath (%lx)\n", hr); - if (hr == S_OK) - IDirectMusicAudioPath_Release(path); - - hr = IDirectMusicPerformance8_CloseDown(performance); - ok(hr == S_OK, "Failed to call CloseDown (%lx)\n", hr); - - IDirectMusicPerformance8_Release(performance); - - /* Auto generated dmusic and dsound */ - create_performance(&performance, NULL, NULL, FALSE); - hr = IDirectMusicPerformance8_InitAudio(performance, NULL, NULL, NULL, 0, 64, 0, NULL); - ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); - hr = IDirectMusicPerformance8_PChannelInfo(performance, 0, &port, NULL, NULL); - ok(hr == E_INVALIDARG, "PChannelInfo failed, got %#lx\n", hr); - destroy_performance(performance, NULL, NULL); - - /* Refcounts for auto generated dmusic and dsound */ - create_performance(&performance, NULL, NULL, FALSE); - dmusic = NULL; - dsound = NULL; - hr = IDirectMusicPerformance8_InitAudio(performance, &dmusic, &dsound, NULL, 0, 64, 0, NULL); - ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); - ref = get_refcount(dsound); - ok(ref == 3, "dsound ref count got %ld expected 3\n", ref); - ref = get_refcount(dmusic); - ok(ref == 2, "dmusic ref count got %ld expected 2\n", ref); - destroy_performance(performance, NULL, NULL); - - /* dsound without SetCooperativeLevel() */ - create_performance(&performance, NULL, &dsound, FALSE); - hr = IDirectMusicPerformance8_InitAudio(performance, NULL, &dsound, NULL, 0, 0, 0, NULL); - todo_wine ok(hr == DSERR_PRIOLEVELNEEDED, "InitAudio failed: %#lx\n", hr); - destroy_performance(performance, NULL, dsound); - - /* Using the wrong CLSID_DirectSound */ - create_performance(&performance, NULL, NULL, FALSE); - hr = DirectSoundCreate(NULL, &dsound, NULL); - ok(hr == S_OK, "DirectSoundCreate failed: %#lx\n", hr); - hr = IDirectMusicPerformance8_InitAudio(performance, NULL, &dsound, NULL, 0, 0, 0, NULL); - todo_wine ok(hr == E_NOINTERFACE, "InitAudio failed: %#lx\n", hr); - destroy_performance(performance, NULL, dsound); - - /* Init() works with just a CLSID_DirectSound */ - create_performance(&performance, NULL, NULL, FALSE); - hr = DirectSoundCreate(NULL, &dsound, NULL); - ok(hr == S_OK, "DirectSoundCreate failed: %#lx\n", hr); - hr = IDirectSound_SetCooperativeLevel(dsound, GetForegroundWindow(), DSSCL_PRIORITY); - ok(hr == S_OK, "SetCooperativeLevel failed: %#lx\n", hr); - hr = IDirectMusicPerformance8_Init(performance, NULL, dsound, NULL); - ok(hr == S_OK, "Init failed: %#lx\n", hr); - destroy_performance(performance, NULL, dsound); - - /* Init() followed by InitAudio() */ - create_performance(&performance, NULL, &dsound, TRUE); - hr = IDirectMusicPerformance8_Init(performance, NULL, dsound, NULL); - ok(hr == S_OK, "Init failed: %#lx\n", hr); - hr = IDirectMusicPerformance8_InitAudio(performance, NULL, &dsound, NULL, 0, 0, 0, NULL); - ok(hr == DMUS_E_ALREADY_INITED, "InitAudio failed: %#lx\n", hr); - destroy_performance(performance, NULL, dsound); - - /* Provided dmusic and dsound */ - create_performance(&performance, &dmusic, &dsound, TRUE); - hr = IDirectMusicPerformance8_InitAudio(performance, &dmusic, &dsound, NULL, 0, 64, 0, NULL); - ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); - ref = get_refcount(dsound); - todo_wine ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); - ref = get_refcount(dmusic); - ok(ref == 2, "dmusic ref count got %ld expected 2\n", ref); - destroy_performance(performance, dmusic, dsound); - - /* Provided dmusic initialized with SetDirectSound */ - create_performance(&performance, &dmusic, &dsound, TRUE); - hr = IDirectMusic_SetDirectSound(dmusic, dsound, NULL); - ok(hr == S_OK, "SetDirectSound failed: %#lx\n", hr); - ref = get_refcount(dsound); - ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); - hr = IDirectMusicPerformance8_InitAudio(performance, &dmusic, NULL, NULL, 0, 64, 0, NULL); - ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); - ref = get_refcount(dsound); - todo_wine ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); - ref = get_refcount(dmusic); - ok(ref == 2, "dmusic ref count got %ld expected 2\n", ref); - destroy_performance(performance, dmusic, dsound); - - /* Provided dmusic and dsound, dmusic initialized with SetDirectSound */ - create_performance(&performance, &dmusic, &dsound, TRUE); - hr = IDirectMusic_SetDirectSound(dmusic, dsound, NULL); - ok(hr == S_OK, "SetDirectSound failed: %#lx\n", hr); - ref = get_refcount(dsound); - ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); - hr = IDirectMusicPerformance8_InitAudio(performance, &dmusic, &dsound, NULL, 0, 64, 0, NULL); - ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); - ref = get_refcount(dsound); - ok(ref == 3, "dsound ref count got %ld expected 3\n", ref); - ref = get_refcount(dmusic); - ok(ref == 2, "dmusic ref count got %ld expected 2\n", ref); - destroy_performance(performance, dmusic, dsound); - - /* InitAudio with perf channel count not a multiple of 16 rounds up */ - create_performance(&performance, NULL, NULL, TRUE); - hr = IDirectMusicPerformance8_InitAudio(performance, NULL, NULL, NULL, - DMUS_APATH_SHARED_STEREOPLUSREVERB, 29, DMUS_AUDIOF_ALL, NULL); - ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); - hr = IDirectMusicPerformance8_PChannelInfo(performance, 31, &port, &group, &channel); - ok(hr == S_OK && group == 2 && channel == 15, - "PChannelInfo failed, got %#lx, %lu, %lu\n", hr, group, channel); - hr = IDirectMusicPerformance8_PChannelInfo(performance, 32, &port, NULL, NULL); - ok(hr == E_INVALIDARG, "PChannelInfo failed, got %#lx\n", hr); - destroy_performance(performance, NULL, NULL); - - return S_OK; -} - -static void test_createport(void) -{ - IDirectMusicPerformance8 *perf; - IDirectMusic *music = NULL; - IDirectMusicPort *port = NULL; - DMUS_PORTCAPS portcaps; - DMUS_PORTPARAMS portparams; - DWORD i; - HRESULT hr; - - hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, - CLSCTX_INPROC_SERVER, &IID_IDirectMusicPerformance8, (void**)&perf); - ok(hr == S_OK, "CoCreateInstance failed: %#lx\n", hr); - - hr = IDirectMusicPerformance8_Init(perf, &music, NULL, NULL); - ok(hr == S_OK, "Init failed: %#lx\n", hr); - ok(music != NULL, "Didn't get IDirectMusic pointer\n"); - - i = 0; - while(1){ - portcaps.dwSize = sizeof(portcaps); - - hr = IDirectMusic_EnumPort(music, i, &portcaps); - ok(hr == S_OK || hr == S_FALSE || (i == 0 && hr == E_INVALIDARG), "EnumPort failed: %#lx\n", hr); - if(hr != S_OK) - break; - - ok(portcaps.dwSize == sizeof(portcaps), "Got unexpected portcaps struct size: %lu\n", portcaps.dwSize); - trace("portcaps(%lu).dwFlags: %#lx\n", i, portcaps.dwFlags); - trace("portcaps(%lu).guidPort: %s\n", i, wine_dbgstr_guid(&portcaps.guidPort)); - trace("portcaps(%lu).dwClass: %#lx\n", i, portcaps.dwClass); - trace("portcaps(%lu).dwType: %#lx\n", i, portcaps.dwType); - trace("portcaps(%lu).dwMemorySize: %#lx\n", i, portcaps.dwMemorySize); - trace("portcaps(%lu).dwMaxChannelGroups: %lu\n", i, portcaps.dwMaxChannelGroups); - trace("portcaps(%lu).dwMaxVoices: %lu\n", i, portcaps.dwMaxVoices); - trace("portcaps(%lu).dwMaxAudioChannels: %lu\n", i, portcaps.dwMaxAudioChannels); - trace("portcaps(%lu).dwEffectFlags: %#lx\n", i, portcaps.dwEffectFlags); - trace("portcaps(%lu).wszDescription: %s\n", i, wine_dbgstr_w(portcaps.wszDescription)); - - ++i; - } - - if(i == 0){ - win_skip("No ports available, skipping tests\n"); - return; - } - - portparams.dwSize = sizeof(portparams); - - /* dwValidParams == 0 -> S_OK, filled struct */ - portparams.dwValidParams = 0; - hr = IDirectMusic_CreatePort(music, &CLSID_DirectMusicSynth, &portparams, &port, NULL); - ok(hr == S_OK, "CreatePort failed: %#lx\n", hr); - ok(port != NULL, "Didn't get IDirectMusicPort pointer\n"); - ok(portparams.dwValidParams, "portparams struct was not filled in\n"); - IDirectMusicPort_Release(port); - port = NULL; - - /* dwValidParams != 0, invalid param -> S_FALSE, filled struct */ - portparams.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS; - portparams.dwChannelGroups = 0; - hr = IDirectMusic_CreatePort(music, &CLSID_DirectMusicSynth, &portparams, &port, NULL); - todo_wine ok(hr == S_FALSE, "CreatePort failed: %#lx\n", hr); - ok(port != NULL, "Didn't get IDirectMusicPort pointer\n"); - ok(portparams.dwValidParams, "portparams struct was not filled in\n"); - IDirectMusicPort_Release(port); - port = NULL; - - /* dwValidParams != 0, valid params -> S_OK */ - hr = IDirectMusic_CreatePort(music, &CLSID_DirectMusicSynth, &portparams, &port, NULL); - ok(hr == S_OK, "CreatePort failed: %#lx\n", hr); - ok(port != NULL, "Didn't get IDirectMusicPort pointer\n"); - IDirectMusicPort_Release(port); - port = NULL; - - /* GUID_NULL succeeds */ - portparams.dwValidParams = 0; - hr = IDirectMusic_CreatePort(music, &GUID_NULL, &portparams, &port, NULL); - ok(hr == S_OK, "CreatePort failed: %#lx\n", hr); - ok(port != NULL, "Didn't get IDirectMusicPort pointer\n"); - ok(portparams.dwValidParams, "portparams struct was not filled in\n"); - IDirectMusicPort_Release(port); - port = NULL; - - /* null GUID fails */ - portparams.dwValidParams = 0; - hr = IDirectMusic_CreatePort(music, NULL, &portparams, &port, NULL); - ok(hr == E_POINTER, "CreatePort failed: %#lx\n", hr); - ok(port == NULL, "Get IDirectMusicPort pointer? %p\n", port); - ok(portparams.dwValidParams == 0, "portparams struct was filled in?\n"); - - /* garbage GUID fails */ - portparams.dwValidParams = 0; - hr = IDirectMusic_CreatePort(music, &GUID_Bunk, &portparams, &port, NULL); - ok(hr == E_NOINTERFACE, "CreatePort failed: %#lx\n", hr); - ok(port == NULL, "Get IDirectMusicPort pointer? %p\n", port); - ok(portparams.dwValidParams == 0, "portparams struct was filled in?\n"); - - hr = IDirectMusicPerformance8_CloseDown(perf); - ok(hr == S_OK, "CloseDown failed: %#lx\n", hr); - - IDirectMusic_Release(music); - IDirectMusicPerformance8_Release(perf); -} - -static void test_pchannel(void) -{ - IDirectMusicPerformance8 *perf; - IDirectMusicPort *port = NULL, *port2; - DWORD channel, group; - unsigned int i; - HRESULT hr; - - create_performance(&perf, NULL, NULL, TRUE); - hr = IDirectMusicPerformance8_Init(perf, NULL, NULL, NULL); - ok(hr == S_OK, "Init failed: %#lx\n", hr); - hr = IDirectMusicPerformance8_PChannelInfo(perf, 0, &port, NULL, NULL); - ok(hr == E_INVALIDARG && !port, "PChannelInfo failed, got %#lx, %p\n", hr, port); - - /* Add default port. Sets PChannels 0-15 to the corresponding channels in group 1 */ - hr = IDirectMusicPerformance8_AddPort(perf, NULL); - ok(hr == S_OK, "AddPort of default port failed: %#lx\n", hr); - hr = IDirectMusicPerformance8_PChannelInfo(perf, 0, NULL, NULL, NULL); - ok(hr == S_OK, "PChannelInfo failed, got %#lx\n", hr); - hr = IDirectMusicPerformance8_PChannelInfo(perf, 0, &port, NULL, NULL); - ok(hr == S_OK && port, "PChannelInfo failed, got %#lx, %p\n", hr, port); - for (i = 1; i < 16; i++) { - hr = IDirectMusicPerformance8_PChannelInfo(perf, i, &port2, &group, &channel); - ok(hr == S_OK && port == port2 && group == 1 && channel == i, - "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); - IDirectMusicPort_Release(port2); - } - - /* Unset PChannels fail to retrieve */ - hr = IDirectMusicPerformance8_PChannelInfo(perf, 16, &port2, NULL, NULL); - ok(hr == E_INVALIDARG, "PChannelInfo failed, got %#lx, %p\n", hr, port); - hr = IDirectMusicPerformance8_PChannelInfo(perf, MAXDWORD - 16, &port2, NULL, NULL); - ok(hr == E_INVALIDARG, "PChannelInfo failed, got %#lx, %p\n", hr, port); - - /* Channel group 0 can be set just fine */ - hr = IDirectMusicPerformance8_AssignPChannel(perf, 0, port, 0, 0); - ok(hr == S_OK, "AssignPChannel failed, got %#lx\n", hr); - hr = IDirectMusicPerformance8_AssignPChannelBlock(perf, 0, port, 0); - ok(hr == S_OK, "AssignPChannelBlock failed, got %#lx\n", hr); - for (i = 1; i < 16; i++) { - hr = IDirectMusicPerformance8_PChannelInfo(perf, i, &port2, &group, &channel); - ok(hr == S_OK && port == port2 && group == 0 && channel == i, - "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); - IDirectMusicPort_Release(port2); - } - - /* Last PChannel Block can be set only individually but not read */ - hr = IDirectMusicPerformance8_AssignPChannel(perf, MAXDWORD, port, 0, 3); - ok(hr == S_OK, "AssignPChannel failed, got %#lx\n", hr); - port2 = (IDirectMusicPort *)0xdeadbeef; - hr = IDirectMusicPerformance8_PChannelInfo(perf, MAXDWORD, &port2, NULL, NULL); - todo_wine ok(hr == E_INVALIDARG && port2 == (IDirectMusicPort *)0xdeadbeef, - "PChannelInfo failed, got %#lx, %p\n", hr, port2); - hr = IDirectMusicPerformance8_AssignPChannelBlock(perf, MAXDWORD, port, 0); - ok(hr == E_INVALIDARG, "AssignPChannelBlock failed, got %#lx\n", hr); - hr = IDirectMusicPerformance8_AssignPChannelBlock(perf, MAXDWORD / 16, port, 1); - todo_wine ok(hr == E_INVALIDARG, "AssignPChannelBlock failed, got %#lx\n", hr); - for (i = MAXDWORD - 15; i < MAXDWORD; i++) { - hr = IDirectMusicPerformance8_AssignPChannel(perf, i, port, 0, 0); - ok(hr == S_OK, "AssignPChannel failed, got %#lx\n", hr); - hr = IDirectMusicPerformance8_PChannelInfo(perf, i, &port2, NULL, NULL); - todo_wine ok(hr == E_INVALIDARG && port2 == (IDirectMusicPort *)0xdeadbeef, - "PChannelInfo failed, got %#lx, %p\n", hr, port2); - } - - /* Second to last PChannel Block can be set only individually and read */ - hr = IDirectMusicPerformance8_AssignPChannelBlock(perf, MAXDWORD / 16 - 1, port, 1); - todo_wine ok(hr == E_INVALIDARG, "AssignPChannelBlock failed, got %#lx\n", hr); - for (i = MAXDWORD - 31; i < MAXDWORD - 15; i++) { - hr = IDirectMusicPerformance8_AssignPChannel(perf, i, port, 1, 7); - ok(hr == S_OK, "AssignPChannel failed, got %#lx\n", hr); - hr = IDirectMusicPerformance8_PChannelInfo(perf, i, &port2, &group, &channel); - ok(hr == S_OK && port2 == port && group == 1 && channel == 7, - "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); - IDirectMusicPort_Release(port2); - } - - /* Third to last PChannel Block behaves normal */ - hr = IDirectMusicPerformance8_AssignPChannelBlock(perf, MAXDWORD / 16 - 2, port, 0); - ok(hr == S_OK, "AssignPChannelBlock failed, got %#lx\n", hr); - for (i = MAXDWORD - 47; i < MAXDWORD - 31; i++) { - hr = IDirectMusicPerformance8_PChannelInfo(perf, i, &port2, &group, &channel); - ok(hr == S_OK && port2 == port && group == 0 && channel == i % 16, - "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); - IDirectMusicPort_Release(port2); - } - - /* One PChannel set in a Block, rest is initialized too */ - hr = IDirectMusicPerformance8_AssignPChannel(perf, 4711, port, 1, 13); - ok(hr == S_OK, "AssignPChannel failed, got %#lx\n", hr); - hr = IDirectMusicPerformance8_PChannelInfo(perf, 4711, &port2, &group, &channel); - ok(hr == S_OK && port2 == port && group == 1 && channel == 13, - "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); - IDirectMusicPort_Release(port2); - group = channel = 0xdeadbeef; - hr = IDirectMusicPerformance8_PChannelInfo(perf, 4712, &port2, &group, &channel); - ok(hr == S_OK && port2 == port && group == 0 && channel == 8, - "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); - IDirectMusicPort_Release(port2); - group = channel = 0xdeadbeef; - hr = IDirectMusicPerformance8_PChannelInfo(perf, 4719, &port2, &group, &channel); - ok(hr == S_OK && port2 == port && group == 0 && channel == 15, - "PChannelInfo failed, got %#lx, %p, %lu, %lu\n", hr, port2, group, channel); - IDirectMusicPort_Release(port2); - - IDirectMusicPort_Release(port); - destroy_performance(perf, NULL, NULL); -} - -static void test_COM(void) -{ - IDirectMusicPerformance *dmp = (IDirectMusicPerformance*)0xdeadbeef; - IDirectMusicPerformance *dmp2; - IDirectMusicPerformance8 *dmp8; - ULONG refcount; - HRESULT hr; - - /* COM aggregation */ - hr = CoCreateInstance(&CLSID_DirectMusicPerformance, (IUnknown *)0xdeadbeef, CLSCTX_INPROC_SERVER, - &IID_IUnknown, (void**)&dmp); - ok(hr == CLASS_E_NOAGGREGATION, - "DirectMusicPerformance create failed: %#lx, expected CLASS_E_NOAGGREGATION\n", hr); - ok(!dmp, "dmp = %p\n", dmp); - - /* Invalid RIID */ - hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectMusicObject, (void**)&dmp); - ok(hr == E_NOINTERFACE, "DirectMusicPerformance create failed: %#lx, expected E_NOINTERFACE\n", hr); - - /* Same refcount */ - hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectMusicPerformance, (void**)&dmp); - ok(hr == S_OK, "DirectMusicPerformance create failed: %#lx, expected S_OK\n", hr); - refcount = IDirectMusicPerformance_AddRef(dmp); - ok (refcount == 2, "refcount == %lu, expected 2\n", refcount); - hr = IDirectMusicPerformance_QueryInterface(dmp, &IID_IDirectMusicPerformance2, (void**)&dmp2); - ok(hr == S_OK, "QueryInterface for IID_IDirectMusicPerformance2 failed: %#lx\n", hr); - IDirectMusicPerformance_AddRef(dmp); - refcount = IDirectMusicPerformance_Release(dmp); - ok (refcount == 3, "refcount == %lu, expected 3\n", refcount); - hr = IDirectMusicPerformance_QueryInterface(dmp, &IID_IDirectMusicPerformance8, (void**)&dmp8); - ok(hr == S_OK, "QueryInterface for IID_IDirectMusicPerformance8 failed: %#lx\n", hr); - refcount = IDirectMusicPerformance_Release(dmp); - ok (refcount == 3, "refcount == %lu, expected 3\n", refcount); - refcount = IDirectMusicPerformance8_Release(dmp8); - ok (refcount == 2, "refcount == %lu, expected 2\n", refcount); - refcount = IDirectMusicPerformance_Release(dmp2); - ok (refcount == 1, "refcount == %lu, expected 1\n", refcount); - refcount = IDirectMusicPerformance_Release(dmp); - ok (refcount == 0, "refcount == %lu, expected 0\n", refcount); -} - -START_TEST( performance ) -{ - HRESULT hr; - - hr = CoInitialize(NULL); - if (FAILED(hr)) { - skip("Cannot initialize COM (%lx)\n", hr); - return; - } - - hr = test_InitAudio(); - if (hr != S_OK) { - skip("InitAudio failed (%lx)\n", hr); - return; - } - - test_COM(); - test_createport(); - test_pchannel(); - - CoUninitialize(); -} From 716f939bb466c2c817801c449c0011cc6bf5447f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 28 Sep 2023 08:26:45 +0200 Subject: [PATCH 0943/1148] dmime/tests: Test performance Init with a created port. Final Fantasy VIII does this, more or less, and needs Init to succeed. (cherry picked from commit 3400e41d5cca6a2eb869fad7cf2fc68680a1b11a) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 12056f15376..f016af2f9e6 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1895,6 +1895,12 @@ static void test_parsedescriptor(void) static void test_performance_InitAudio(void) { + DMUS_PORTPARAMS params = + { + .dwSize = sizeof(params), + .dwValidParams = DMUS_PORTPARAMS_EFFECTS, + .dwEffectFlags = 1, + }; IDirectMusicPerformance8 *performance; IDirectMusic *dmusic; IDirectSound *dsound; @@ -2036,6 +2042,20 @@ static void test_performance_InitAudio(void) ok(ref == 2, "dmusic ref count got %ld expected 2\n", ref); destroy_performance(performance, dmusic, dsound); + /* Provided dmusic and dsound, dmusic initialized with SetDirectSound, port created and activated */ + create_performance(&performance, &dmusic, &dsound, TRUE); + hr = IDirectMusic_SetDirectSound(dmusic, dsound, NULL); + ok(hr == S_OK, "SetDirectSound failed: %#lx\n", hr); + hr = IDirectMusic_CreatePort(dmusic, &CLSID_DirectMusicSynth, ¶ms, &port, NULL); + ok(hr == S_OK, "CreatePort failed: %#lx\n", hr); + hr = IDirectMusicPort_Activate(port, TRUE); + ok(hr == S_OK, "Activate failed: %#lx\n", hr); + hr = IDirectMusicPort_SetNumChannelGroups(port, 1); + ok(hr == S_OK, "SetNumChannelGroups failed: %#lx\n", hr); + hr = IDirectMusicPerformance8_Init(performance, &dmusic, dsound, 0); + todo_wine ok(hr == S_OK, "Init failed: %#lx\n", hr); + destroy_performance(performance, dmusic, dsound); + /* InitAudio with perf channel count not a multiple of 16 rounds up */ create_performance(&performance, NULL, NULL, TRUE); hr = IDirectMusicPerformance8_InitAudio(performance, NULL, NULL, NULL, From 82f2221efc23f3e76a297028683d96a362a4c8ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Sep 2023 19:10:17 +0200 Subject: [PATCH 0944/1148] dmime: Set the port direct sound before activating it. (cherry picked from commit 50b09dcf11b2939a5702104b6726d10ad44df43b) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index d8624427423..e2d070b0fbe 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -651,10 +651,14 @@ static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *par if (FAILED(hr = IDirectMusic8_CreatePort(perf->dmusic, &guid, params, &port, NULL))) return hr; - if (FAILED(hr = IDirectMusicPort_Activate(port, TRUE))) { + + if (FAILED(hr = IDirectMusicPort_SetDirectSound(port, perf->dsound, NULL)) + || FAILED(hr = IDirectMusicPort_Activate(port, TRUE))) + { IDirectMusicPort_Release(port); return hr; } + for (i = 0; i < params->dwChannelGroups; i++) pchannel_block_set(&perf->pchannels, i, port, i + 1, FALSE); From 46ed61299ed21c00894d524e1e4b3a791ee7248a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Sep 2023 18:49:40 +0200 Subject: [PATCH 0945/1148] dmime: Initialize performance in Init rather than InitAudio. (cherry picked from commit 9788fb6911ab56186c95edd6fe487f48ab98e118) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 148 ++++++++++++++++++++------------------- dlls/dmime/tests/dmime.c | 6 +- 2 files changed, 79 insertions(+), 75 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index e2d070b0fbe..a8c2016802c 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -40,10 +40,9 @@ struct performance IDirectMusicGraph IDirectMusicGraph_iface; IDirectMusicTool IDirectMusicTool_iface; LONG ref; - IDirectMusic8 *dmusic; + IDirectMusic *dmusic; IDirectSound *dsound; IDirectMusicGraph *pToolGraph; - DMUS_AUDIOPARAMS params; BOOL fAutoDownload; char cMasterGrooveLevel; float fMasterTempo; @@ -314,14 +313,74 @@ static ULONG WINAPI performance_Release(IDirectMusicPerformance8 *iface) return ref; } -/* IDirectMusicPerformanceImpl IDirectMusicPerformance Interface part: */ +static HRESULT performance_init_dsound(struct performance *This, HWND hwnd) +{ + IDirectSound *dsound; + HRESULT hr; + + if (FAILED(hr = DirectSoundCreate(NULL, &dsound, NULL))) return hr; + + if (!hwnd) hwnd = GetForegroundWindow(); + hr = IDirectSound_SetCooperativeLevel(dsound, hwnd, DSSCL_PRIORITY); + + if (SUCCEEDED(hr)) This->dsound = dsound; + else IDirectSound_Release(dsound); + + return hr; +} + +static HRESULT performance_init_dmusic(struct performance *This, IDirectSound *dsound) +{ + IDirectMusic *dmusic; + HRESULT hr; + + if (FAILED(hr = CoCreateInstance(&CLSID_DirectMusic, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusic8, (void **)&dmusic))) + return hr; + + hr = IDirectMusic_SetDirectSound(dmusic, dsound, NULL); + + if (SUCCEEDED(hr)) This->dmusic = dmusic; + else IDirectSound_Release(dmusic); + + return hr; +} + static HRESULT WINAPI performance_Init(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic, IDirectSound *dsound, HWND hwnd) { + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + HRESULT hr; + TRACE("(%p, %p, %p, %p)\n", iface, dmusic, dsound, hwnd); - return IDirectMusicPerformance8_InitAudio(iface, dmusic, dsound ? &dsound : NULL, hwnd, 0, 0, - 0, NULL); + if (This->dmusic) return DMUS_E_ALREADY_INITED; + + if ((This->dsound = dsound)) IDirectMusic8_AddRef(This->dsound); + else if (FAILED(hr = performance_init_dsound(This, hwnd))) return hr; + + if (dmusic && (This->dmusic = *dmusic)) IDirectMusic_AddRef(This->dmusic); + else if (FAILED(hr = performance_init_dmusic(This, This->dsound))) + { + IDirectMusicPerformance_CloseDown(iface); + return hr; + } + + if (FAILED(hr = IDirectMusic_GetMasterClock(This->dmusic, NULL, &This->master_clock)) + || FAILED(hr = IDirectMusicPerformance8_GetTime(iface, &This->init_time, NULL))) + { + IDirectMusicPerformance_CloseDown(iface); + return hr; + } + + PostMessageToProcessMsgThread(This, PROCESSMSG_START); + + if (dmusic && !*dmusic) + { + *dmusic = This->dmusic; + IDirectMusic_AddRef(*dmusic); + } + return S_OK; } static HRESULT WINAPI performance_PlaySegment(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, @@ -900,6 +959,7 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) IDirectMusic8_Release(This->dmusic); This->dmusic = NULL; } + return S_OK; } @@ -960,57 +1020,21 @@ static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDi TRACE("(%p, %p, %p, %p, %lx, %lu, %lx, %p)\n", This, dmusic, dsound, hwnd, default_path_type, num_channels, flags, params); - if (This->dmusic) - return DMUS_E_ALREADY_INITED; - - if (!dmusic || !*dmusic) { - hr = CoCreateInstance(&CLSID_DirectMusic, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusic8, - (void **)&This->dmusic); - if (FAILED(hr)) - return hr; - } else { - This->dmusic = (IDirectMusic8 *)*dmusic; - IDirectMusic8_AddRef(This->dmusic); - } - - if (FAILED(hr = IDirectMusic_GetMasterClock(This->dmusic, NULL, &This->master_clock))) - goto error; + if (flags) FIXME("flags parameter not used\n"); + if (params) FIXME("params parameter not used\n"); - if (!dsound || !*dsound) { - hr = DirectSoundCreate8(NULL, (IDirectSound8 **)&This->dsound, NULL); - if (FAILED(hr)) - goto error; - hr = IDirectSound_SetCooperativeLevel(This->dsound, hwnd ? hwnd : GetForegroundWindow(), - DSSCL_PRIORITY); - if (FAILED(hr)) - goto error; - } else { - This->dsound = *dsound; - IDirectSound_AddRef(This->dsound); - } + if (FAILED(hr = IDirectMusicPerformance8_Init(iface, dmusic && *dmusic ? dmusic : NULL, + dsound ? *dsound : NULL, hwnd))) + return hr; - hr = IDirectMusic8_SetDirectSound(This->dmusic, This->dsound, NULL); - if (FAILED(hr)) - goto error; - - if (!params) { - This->params.dwSize = sizeof(DMUS_AUDIOPARAMS); - This->params.fInitNow = FALSE; - This->params.dwValidData = DMUS_AUDIOPARAMS_FEATURES | DMUS_AUDIOPARAMS_VOICES | - DMUS_AUDIOPARAMS_SAMPLERATE | DMUS_AUDIOPARAMS_DEFAULTSYNTH; - This->params.dwVoices = 64; - This->params.dwSampleRate = 22050; - This->params.dwFeatures = flags; - This->params.clsidDefaultSynth = CLSID_DirectMusicSynthSink; - } else - This->params = *params; - - if (default_path_type) { + if (default_path_type) + { hr = IDirectMusicPerformance8_CreateStandardAudioPath(iface, default_path_type, num_channels, FALSE, &This->pDefaultPath); - if (FAILED(hr)) { - IDirectMusic8_SetDirectSound(This->dmusic, NULL, NULL); - goto error; + if (FAILED(hr)) + { + IDirectMusicPerformance_CloseDown(iface); + return hr; } } @@ -1023,27 +1047,7 @@ static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDi IDirectMusic_AddRef(*dmusic); } - if (FAILED(hr = IDirectMusicPerformance8_GetTime(iface, &This->init_time, NULL))) return hr; - - PostMessageToProcessMsgThread(This, PROCESSMSG_START); - return S_OK; - -error: - if (This->master_clock) - { - IReferenceClock_Release(This->master_clock); - This->master_clock = NULL; - } - if (This->dsound) { - IDirectSound_Release(This->dsound); - This->dsound = NULL; - } - if (This->dmusic) { - IDirectMusic8_Release(This->dmusic); - This->dmusic = NULL; - } - return hr; } static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, IUnknown *pSource, diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index f016af2f9e6..c1043fe90f3 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -2009,7 +2009,7 @@ static void test_performance_InitAudio(void) hr = IDirectMusicPerformance8_InitAudio(performance, &dmusic, &dsound, NULL, 0, 64, 0, NULL); ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); ref = get_refcount(dsound); - todo_wine ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); + ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); ref = get_refcount(dmusic); ok(ref == 2, "dmusic ref count got %ld expected 2\n", ref); destroy_performance(performance, dmusic, dsound); @@ -2023,7 +2023,7 @@ static void test_performance_InitAudio(void) hr = IDirectMusicPerformance8_InitAudio(performance, &dmusic, NULL, NULL, 0, 64, 0, NULL); ok(hr == S_OK, "InitAudio failed: %#lx\n", hr); ref = get_refcount(dsound); - todo_wine ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); + ok(ref == 2, "dsound ref count got %ld expected 2\n", ref); ref = get_refcount(dmusic); ok(ref == 2, "dmusic ref count got %ld expected 2\n", ref); destroy_performance(performance, dmusic, dsound); @@ -2053,7 +2053,7 @@ static void test_performance_InitAudio(void) hr = IDirectMusicPort_SetNumChannelGroups(port, 1); ok(hr == S_OK, "SetNumChannelGroups failed: %#lx\n", hr); hr = IDirectMusicPerformance8_Init(performance, &dmusic, dsound, 0); - todo_wine ok(hr == S_OK, "Init failed: %#lx\n", hr); + ok(hr == S_OK, "Init failed: %#lx\n", hr); destroy_performance(performance, dmusic, dsound); /* InitAudio with perf channel count not a multiple of 16 rounds up */ From 0ac7c6c0ba62bf7f9a6b9c75179df88c3ffe12ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Sep 2023 18:49:40 +0200 Subject: [PATCH 0946/1148] dmime: Return DMUS_E_AUDIOPATHS_IN_USE when audio paths are in use. (cherry picked from commit 0fb4e5ec479bc7424c3818d51884387b15e90e52) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 28 ++++++++++++++++------------ dlls/dmime/tests/dmime.c | 4 ++-- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index a8c2016802c..7b606998616 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -50,6 +50,7 @@ struct performance /* performance channels */ struct wine_rb_tree pchannels; + BOOL audio_paths_enabled; IDirectMusicAudioPath *pDefaultPath; HANDLE hNotification; REFERENCE_TIME rtMinimum; @@ -730,8 +731,8 @@ static HRESULT WINAPI performance_AddPort(IDirectMusicPerformance8 *iface, IDire FIXME("(%p, %p): semi-stub\n", This, port); - if (!This->dmusic) - return DMUS_E_NOT_INIT; + if (!This->dmusic) return DMUS_E_NOT_INIT; + if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; if (!port) { DMUS_PORTPARAMS params = { @@ -753,11 +754,13 @@ static HRESULT WINAPI performance_AddPort(IDirectMusicPerformance8 *iface, IDire static HRESULT WINAPI performance_RemovePort(IDirectMusicPerformance8 *iface, IDirectMusicPort *pPort) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); - FIXME("(%p, %p): stub\n", This, pPort); - IDirectMusicPort_Release (pPort); - return S_OK; + if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; + + FIXME("(%p, %p): stub\n", This, pPort); + IDirectMusicPort_Release(pPort); + return S_OK; } static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 *iface, @@ -767,10 +770,9 @@ static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 * FIXME("(%p, %ld, %p, %ld): semi-stub\n", This, block_num, port, group); - if (!port) - return E_POINTER; - if (block_num > MAXDWORD / 16) - return E_INVALIDARG; + if (!port) return E_POINTER; + if (block_num > MAXDWORD / 16) return E_INVALIDARG; + if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; pchannel_block_set(&This->pchannels, block_num, port, group, FALSE); @@ -785,8 +787,8 @@ static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface FIXME("(%p)->(%ld, %p, %ld, %ld) semi-stub\n", This, pchannel, port, group, channel); - if (!port) - return E_POINTER; + if (!port) return E_POINTER; + if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; block = pchannel_block_set(&This->pchannels, pchannel / 16, port, 0, TRUE); if (block) { @@ -959,6 +961,7 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) IDirectMusic8_Release(This->dmusic); This->dmusic = NULL; } + This->audio_paths_enabled = FALSE; return S_OK; } @@ -1027,6 +1030,7 @@ static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDi dsound ? *dsound : NULL, hwnd))) return hr; + This->audio_paths_enabled = TRUE; if (default_path_type) { hr = IDirectMusicPerformance8_CreateStandardAudioPath(iface, default_path_type, diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index c1043fe90f3..119bfc74fc5 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -1937,9 +1937,9 @@ static void test_performance_InitAudio(void) ok(hr == S_OK, "PChannelInfo failed, got %#lx\n", hr); ok(port != NULL, "IDirectMusicPort not set\n"); hr = IDirectMusicPerformance8_AssignPChannel(performance, 0, port, 0, 0); - todo_wine ok(hr == DMUS_E_AUDIOPATHS_IN_USE, "AssignPChannel failed (%#lx)\n", hr); + ok(hr == DMUS_E_AUDIOPATHS_IN_USE, "AssignPChannel failed (%#lx)\n", hr); hr = IDirectMusicPerformance8_AssignPChannelBlock(performance, 0, port, 0); - todo_wine ok(hr == DMUS_E_AUDIOPATHS_IN_USE, "AssignPChannelBlock failed (%#lx)\n", hr); + ok(hr == DMUS_E_AUDIOPATHS_IN_USE, "AssignPChannelBlock failed (%#lx)\n", hr); IDirectMusicPort_Release(port); hr = IDirectMusicPerformance8_GetDefaultAudioPath(performance, &path); From 27ea1ff2a01ad1ece1857ef3f193b5beb07cc297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Sep 2023 19:08:34 +0200 Subject: [PATCH 0947/1148] dmime: Return DMUS_E_AUDIOPATH_INACTIVE when audio paths are not enabled. (cherry picked from commit 9e0487c4cc36cdbf915d35222c875d23f54958ae) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 102 ++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 55 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 7b606998616..bdb7475370a 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1096,45 +1096,41 @@ static HRESULT WINAPI performance_ClonePMsg(IDirectMusicPerformance8 *iface, DMU } static HRESULT WINAPI performance_CreateAudioPath(IDirectMusicPerformance8 *iface, - IUnknown *pSourceConfig, BOOL fActivate, IDirectMusicAudioPath **ppNewPath) + IUnknown *pSourceConfig, BOOL fActivate, IDirectMusicAudioPath **ret_iface) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); - IDirectMusicAudioPath *pPath; + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + IDirectMusicAudioPath *pPath; - FIXME("(%p, %p, %d, %p): stub\n", This, pSourceConfig, fActivate, ppNewPath); + FIXME("(%p, %p, %d, %p): stub\n", This, pSourceConfig, fActivate, ret_iface); - if (NULL == ppNewPath) { - return E_POINTER; - } + if (!ret_iface) return E_POINTER; + if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE; - create_dmaudiopath(&IID_IDirectMusicAudioPath, (void**)&pPath); - set_audiopath_perf_pointer(pPath, iface); + create_dmaudiopath(&IID_IDirectMusicAudioPath, (void **)&pPath); + set_audiopath_perf_pointer(pPath, iface); - /** TODO */ - - *ppNewPath = pPath; - - return IDirectMusicAudioPath_Activate(*ppNewPath, fActivate); + /** TODO */ + *ret_iface = pPath; + return IDirectMusicAudioPath_Activate(*ret_iface, fActivate); } static HRESULT WINAPI performance_CreateStandardAudioPath(IDirectMusicPerformance8 *iface, - DWORD dwType, DWORD pchannel_count, BOOL fActivate, IDirectMusicAudioPath **ppNewPath) + DWORD dwType, DWORD pchannel_count, BOOL fActivate, IDirectMusicAudioPath **ret_iface) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); - IDirectMusicAudioPath *pPath; - DSBUFFERDESC desc; - WAVEFORMATEX format; - DMUS_PORTPARAMS params = {0}; - IDirectSoundBuffer *buffer, *primary_buffer; - HRESULT hr = S_OK; + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + IDirectMusicAudioPath *pPath; + DSBUFFERDESC desc; + WAVEFORMATEX format; + DMUS_PORTPARAMS params = {0}; + IDirectSoundBuffer *buffer, *primary_buffer; + HRESULT hr = S_OK; - FIXME("(%p)->(%ld, %ld, %d, %p): semi-stub\n", This, dwType, pchannel_count, fActivate, ppNewPath); + FIXME("(%p)->(%ld, %ld, %d, %p): semi-stub\n", This, dwType, pchannel_count, fActivate, ret_iface); - if (NULL == ppNewPath) { - return E_POINTER; - } + if (!ret_iface) return E_POINTER; + if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE; - *ppNewPath = NULL; + *ret_iface = NULL; /* Secondary buffer description */ memset(&format, 0, sizeof(format)); @@ -1204,46 +1200,42 @@ static HRESULT WINAPI performance_CreateStandardAudioPath(IDirectMusicPerformanc set_audiopath_dsound_buffer(pPath, buffer); set_audiopath_primary_dsound_buffer(pPath, primary_buffer); - *ppNewPath = pPath; - - TRACE(" returning IDirectMusicAudioPath interface at %p.\n", *ppNewPath); - - return IDirectMusicAudioPath_Activate(*ppNewPath, fActivate); + *ret_iface = pPath; + TRACE(" returning IDirectMusicAudioPath interface at %p.\n", *ret_iface); + return IDirectMusicAudioPath_Activate(*ret_iface, fActivate); } -static HRESULT WINAPI performance_SetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath *pAudioPath) +static HRESULT WINAPI performance_SetDefaultAudioPath(IDirectMusicPerformance8 *iface, IDirectMusicAudioPath *audio_path) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); - FIXME("(%p, %p): semi-stub\n", This, pAudioPath); + FIXME("(%p, %p): semi-stub\n", This, audio_path); - if (This->pDefaultPath) { - IDirectMusicAudioPath_Release(This->pDefaultPath); - This->pDefaultPath = NULL; - } - This->pDefaultPath = pAudioPath; - if (This->pDefaultPath) { - IDirectMusicAudioPath_AddRef(This->pDefaultPath); - set_audiopath_perf_pointer(This->pDefaultPath, iface); - } + if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE; - return S_OK; + if (This->pDefaultPath) IDirectMusicAudioPath_Release(This->pDefaultPath); + if ((This->pDefaultPath = audio_path)) + { + IDirectMusicAudioPath_AddRef(This->pDefaultPath); + set_audiopath_perf_pointer(This->pDefaultPath, iface); + } + + return S_OK; } static HRESULT WINAPI performance_GetDefaultAudioPath(IDirectMusicPerformance8 *iface, - IDirectMusicAudioPath **ppAudioPath) + IDirectMusicAudioPath **ret_iface) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); - FIXME("(%p, %p): semi-stub (%p)\n", This, ppAudioPath, This->pDefaultPath); + FIXME("(%p, %p): semi-stub (%p)\n", This, ret_iface, This->pDefaultPath); - if (NULL != This->pDefaultPath) { - *ppAudioPath = This->pDefaultPath; - IDirectMusicAudioPath_AddRef(*ppAudioPath); - } else { - *ppAudioPath = NULL; - } - return S_OK; + if (!ret_iface) return E_POINTER; + if (!This->audio_paths_enabled) return DMUS_E_AUDIOPATH_INACTIVE; + + if ((*ret_iface = This->pDefaultPath)) IDirectMusicAudioPath_AddRef(*ret_iface); + + return S_OK; } static HRESULT WINAPI performance_GetParamEx(IDirectMusicPerformance8 *iface, REFGUID rguidType, DWORD dwTrackID, From 198f5b7c6a64354917412b283f0b569bb5c84568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 08:03:31 +0200 Subject: [PATCH 0948/1148] dmloader: Use a simpler file stream implementation. (cherry picked from commit 1ffc47b5e69b6377244c7df51e3de9a6d02082e9) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/dmloader_private.h | 18 +- dlls/dmloader/loader.c | 105 ++++------ dlls/dmloader/loaderstream.c | 335 ++++++++++++++----------------- 3 files changed, 189 insertions(+), 269 deletions(-) diff --git a/dlls/dmloader/dmloader_private.h b/dlls/dmloader/dmloader_private.h index 87fcf087bc0..c590040389b 100644 --- a/dlls/dmloader/dmloader_private.h +++ b/dlls/dmloader/dmloader_private.h @@ -59,25 +59,8 @@ typedef struct IDirectMusicLoaderGenericStream IDirectMusicLoaderGenericStream; */ extern HRESULT create_dmloader(REFIID riid, void **ret_iface); extern HRESULT create_dmcontainer(REFIID riid, void **ret_iface); -extern HRESULT DMUSIC_CreateDirectMusicLoaderFileStream(void **ppobj); extern HRESULT DMUSIC_CreateDirectMusicLoaderResourceStream(void **ppobj); -/***************************************************************************** - * IDirectMusicLoaderFileStream implementation structure - */ -struct IDirectMusicLoaderFileStream { - /* VTABLEs */ - const IStreamVtbl *StreamVtbl; - /* reference counter */ - LONG dwRef; - /* file */ - WCHAR wzFileName[MAX_PATH]; /* for clone */ - HANDLE hFile; -}; - -/* Custom: */ -extern HRESULT WINAPI IDirectMusicLoaderFileStream_Attach(LPSTREAM iface, LPCWSTR wzFile); - /***************************************************************************** * IDirectMusicLoaderResourceStream implementation structure */ @@ -98,6 +81,7 @@ extern HRESULT WINAPI IDirectMusicLoaderResourceStream_Attach(LPSTREAM iface, LP LONGLONG llMemLength, LONGLONG llPos); extern HRESULT loader_stream_create(IDirectMusicLoader *loader, IStream *stream, IStream **ret_iface); +extern HRESULT file_stream_create(const WCHAR *path, IStream **ret_iface); #include "debug.h" diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index ef1a8c3dfe0..9c97f47e5f7 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -305,40 +305,29 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE TRACE(": no cache/alias entry found for requested object\n"); } - if (pDesc->dwValidData & DMUS_OBJ_URL) { - TRACE(": loading from URLs not supported yet\n"); - return DMUS_E_LOADER_FORMATNOTSUPPORTED; - } - else if (pDesc->dwValidData & DMUS_OBJ_FILENAME) { - /* load object from file */ - /* generate filename; if it's full path, don't add search - directory path, otherwise do */ - WCHAR wszFileName[MAX_PATH]; - - if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) { - lstrcpyW(wszFileName, pDesc->wszFileName); - } else { - WCHAR *p; - get_search_path(This, &pDesc->guidClass, wszFileName); - p = wszFileName + lstrlenW(wszFileName); - if (p > wszFileName && p[-1] != '\\') *p++ = '\\'; - lstrcpyW(p, pDesc->wszFileName); - } - TRACE(": loading from file (%s)\n", debugstr_w(wszFileName)); - /* create stream and associate it with file */ - result = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream); - if (FAILED(result)) { - ERR(": could not create file stream\n"); - return result; - } - result = IDirectMusicLoaderFileStream_Attach(pStream, wszFileName); - if (FAILED(result)) - { - ERR(": could not attach stream to file\n"); - IStream_Release (pStream); - return result; - } - } + if (pDesc->dwValidData & DMUS_OBJ_URL) + { + TRACE(": loading from URLs not supported yet\n"); + return DMUS_E_LOADER_FORMATNOTSUPPORTED; + } + else if (pDesc->dwValidData & DMUS_OBJ_FILENAME) + { + WCHAR file_name[MAX_PATH]; + + if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) + lstrcpyW(file_name, pDesc->wszFileName); + else + { + WCHAR *p; + get_search_path(This, &pDesc->guidClass, file_name); + p = file_name + lstrlenW(file_name); + if (p > file_name && p[-1] != '\\') *p++ = '\\'; + lstrcpyW(p, pDesc->wszFileName); + } + + TRACE(": loading from file (%s)\n", debugstr_w(file_name)); + if (FAILED(hr = file_stream_create(file_name, &pStream))) return hr; + } else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) { /* load object from resource */ TRACE(": loading from resource\n"); @@ -363,9 +352,8 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE } else { - /* nowhere to load from */ FIXME(": unknown/unsupported way of loading\n"); - return DMUS_E_LOADER_NOFILENAME; /* test shows this is returned */ + return DMUS_E_LOADER_NOFILENAME; } if (FAILED(hr = loader_stream_create((IDirectMusicLoader *)iface, pStream, &loader_stream))) @@ -459,36 +447,23 @@ static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE if (TRACE_ON(dmloader)) dump_DMUS_OBJECTDESC(pDesc); - /* create stream and load additional info from it */ - if (pDesc->dwValidData & DMUS_OBJ_FILENAME) { - /* generate filename; if it's full path, don't add search - directory path, otherwise do */ - WCHAR wszFileName[MAX_PATH]; + if (pDesc->dwValidData & DMUS_OBJ_FILENAME) + { + WCHAR file_name[MAX_PATH]; - if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) { - lstrcpyW(wszFileName, pDesc->wszFileName); - } else { - WCHAR *p; - get_search_path(This, &pDesc->guidClass, wszFileName); - p = wszFileName + lstrlenW(wszFileName); - if (p > wszFileName && p[-1] != '\\') *p++ = '\\'; - lstrcpyW(p, pDesc->wszFileName); - } - /* create stream */ - hr = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream); - if (FAILED(hr)) { - ERR(": could not create file stream\n"); - return DMUS_E_LOADER_FAILEDOPEN; - } - /* attach stream */ - hr = IDirectMusicLoaderFileStream_Attach(pStream, wszFileName); - if (FAILED(hr)) - { - ERR(": could not attach stream to file %s, make sure it exists\n", debugstr_w(wszFileName)); - IStream_Release (pStream); - return DMUS_E_LOADER_FAILEDOPEN; - } - } + if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) + lstrcpyW(file_name, pDesc->wszFileName); + else + { + WCHAR *p; + get_search_path(This, &pDesc->guidClass, file_name); + p = file_name + lstrlenW(file_name); + if (p > file_name && p[-1] != '\\') *p++ = '\\'; + lstrcpyW(p, pDesc->wszFileName); + } + + if (FAILED(hr = file_stream_create(file_name, &pStream))) return hr; + } else if (pDesc->dwValidData & DMUS_OBJ_STREAM) { pStream = pDesc->pStream; diff --git a/dlls/dmloader/loaderstream.c b/dlls/dmloader/loaderstream.c index 5b7862c16f8..80e9f91e77a 100644 --- a/dlls/dmloader/loaderstream.c +++ b/dlls/dmloader/loaderstream.c @@ -1,8 +1,6 @@ -/* IDirectMusicLoaderFileStream - * IDirectMusicLoaderResourceStream - * IDirectMusicLoaderGenericStream - * +/* * Copyright (C) 2003-2004 Rok Mandeljc + * Copyright 2023 Rémi Bernon for CodeWeavers * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,31 +17,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ - -/* SIDE NOTES: - * After extensive testing and structure dumping I came to a conclusion that - * DirectMusic as in present state implements three types of streams: - * 1. IDirectMusicLoaderFileStream: stream that was most obvious, since - * it's used for loading from files; it is sort of wrapper around - * CreateFile, ReadFile, WriteFile and SetFilePointer and it supports - * both read and write - * 2. IDirectMusicLoaderResourceStream: a stream that had to exist, since - * according to MSDN, IDirectMusicLoader supports loading from resource - * as well; in this case, data is represented as a big chunk of bytes, - * from which we "read" (copy) data and keep the trace of our position; - * it supports read only - * 3. IDirectMusicLoaderGenericStream: this one was the most problematic, - * since I thought it was URL-related; besides, there's no obvious need - * for it, since input streams can simply be cloned, lest loading from - * stream is requested; but if one really thinks about it, input stream - * could be none of 1. or 2.; in this case, a wrapper that offers - * IDirectMusicGetLoader interface would be nice, and this is what this - * stream is; as such, all functions are supported, as long as underlying - * ("low-level") stream supports them - * - * - Rok Mandeljc; 24. April, 2004 -*/ - #include "dmloader_private.h" WINE_DEFAULT_DEBUG_CHANNEL(dmloader); @@ -284,216 +257,204 @@ HRESULT loader_stream_create(IDirectMusicLoader *loader, IStream *stream, return S_OK; } -static ULONG WINAPI IDirectMusicLoaderFileStream_IStream_AddRef (LPSTREAM iface); -static ULONG WINAPI IDirectMusicLoaderResourceStream_IStream_AddRef (LPSTREAM iface); - +struct file_stream +{ + IStream IStream_iface; + LONG ref; -/***************************************************************************** - * IDirectMusicLoaderFileStream implementation - */ -/* Custom : */ + WCHAR path[MAX_PATH]; + HANDLE file; +}; -static void IDirectMusicLoaderFileStream_Detach (LPSTREAM iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - TRACE("(%p)\n", This); - if (This->hFile != INVALID_HANDLE_VALUE) CloseHandle(This->hFile); - This->wzFileName[0] = '\0'; +static struct file_stream *file_stream_from_IStream(IStream *iface) +{ + return CONTAINING_RECORD(iface, struct file_stream, IStream_iface); } -HRESULT WINAPI IDirectMusicLoaderFileStream_Attach(LPSTREAM iface, LPCWSTR wzFile) +static HRESULT WINAPI file_stream_QueryInterface(IStream *iface, REFIID riid, void **ret_iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - TRACE("(%p, %s)\n", This, debugstr_w(wzFile)); - IDirectMusicLoaderFileStream_Detach (iface); - This->hFile = CreateFileW (wzFile, (GENERIC_READ | GENERIC_WRITE), (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (This->hFile == INVALID_HANDLE_VALUE) { - WARN(": failed\n"); - return DMUS_E_LOADER_FAILEDOPEN; - } - /* create IDirectMusicGetLoader */ - lstrcpynW (This->wzFileName, wzFile, MAX_PATH); - TRACE(": succeeded\n"); - return S_OK; -} + struct file_stream *This = file_stream_from_IStream(iface); + TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ret_iface); -/* IUnknown/IStream part: */ -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_QueryInterface (LPSTREAM iface, REFIID riid, void** ppobj) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - - TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ppobj); - if (IsEqualIID (riid, &IID_IUnknown) || - IsEqualIID (riid, &IID_IStream)) { - *ppobj = &This->StreamVtbl; - IDirectMusicLoaderFileStream_IStream_AddRef ((LPSTREAM)&This->StreamVtbl); - return S_OK; - } + if (IsEqualGUID(riid, &IID_IUnknown) + || IsEqualGUID(riid, &IID_IStream)) + { + IStream_AddRef(&This->IStream_iface); + *ret_iface = &This->IStream_iface; + return S_OK; + } - WARN(": not found\n"); - return E_NOINTERFACE; + WARN("(%p, %s, %p): not found\n", iface, debugstr_dmguid(riid), ret_iface); + *ret_iface = NULL; + return E_NOINTERFACE; } -static ULONG WINAPI IDirectMusicLoaderFileStream_IStream_AddRef (LPSTREAM iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - TRACE("(%p): AddRef from %ld\n", This, This->dwRef); - return InterlockedIncrement (&This->dwRef); +static ULONG WINAPI file_stream_AddRef(IStream *iface) +{ + struct file_stream *This = file_stream_from_IStream(iface); + ULONG ref = InterlockedIncrement(&This->ref); + TRACE("(%p): new ref = %lu\n", This, ref); + return ref; } -static ULONG WINAPI IDirectMusicLoaderFileStream_IStream_Release (LPSTREAM iface) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - - DWORD dwRef = InterlockedDecrement (&This->dwRef); - TRACE("(%p): ReleaseRef to %ld\n", This, dwRef); - if (dwRef == 0) { - if (This->hFile) - IDirectMusicLoaderFileStream_Detach (iface); - free(This); - } - - return dwRef; -} +static ULONG WINAPI file_stream_Release(IStream *iface) +{ + struct file_stream *This = file_stream_from_IStream(iface); + ULONG ref = InterlockedDecrement(&This->ref); -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Read (LPSTREAM iface, void* pv, ULONG cb, ULONG* pcbRead) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - ULONG cbRead; - - TRACE_(dmfileraw)("(%p, %p, %#lx, %p)\n", This, pv, cb, pcbRead); - if (This->hFile == INVALID_HANDLE_VALUE) return E_FAIL; - if (pcbRead == NULL) pcbRead = &cbRead; - if (!ReadFile (This->hFile, pv, cb, pcbRead, NULL) || *pcbRead != cb) return E_FAIL; - - TRACE_(dmfileraw)(": data (size = %#lx): %s\n", *pcbRead, debugstr_an(pv, *pcbRead)); - return S_OK; + TRACE("(%p): new ref = %lu\n", This, ref); + + if (!ref) + { + CloseHandle(This->file); + free(This); + } + + return ref; } -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Seek (LPSTREAM iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - LARGE_INTEGER liNewPos; - - TRACE_(dmfileraw)("(%p, %s, %s, %p)\n", This, wine_dbgstr_longlong(dlibMove.QuadPart), resolve_STREAM_SEEK(dwOrigin), plibNewPosition); +static HRESULT WINAPI file_stream_Read(IStream *iface, void *data, ULONG size, ULONG *ret_size) +{ + struct file_stream *This = file_stream_from_IStream(iface); + DWORD dummy; - if (This->hFile == INVALID_HANDLE_VALUE) return E_FAIL; + TRACE("(%p, %p, %#lx, %p)\n", This, data, size, ret_size); - liNewPos.HighPart = dlibMove.HighPart; - liNewPos.LowPart = SetFilePointer (This->hFile, dlibMove.LowPart, &liNewPos.HighPart, dwOrigin); + if (!ret_size) ret_size = &dummy; + if (!ReadFile(This->file, data, size, ret_size, NULL)) return HRESULT_FROM_WIN32(GetLastError()); + return *ret_size == size ? S_OK : S_FALSE; +} - if (liNewPos.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) return E_FAIL; - if (plibNewPosition) plibNewPosition->QuadPart = liNewPos.QuadPart; - - return S_OK; +static HRESULT WINAPI file_stream_Write(IStream *iface, const void *data, ULONG size, ULONG *ret_size) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); + return E_NOTIMPL; } -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Clone (LPSTREAM iface, IStream** ppstm) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - LPSTREAM pOther = NULL; - HRESULT result; +static HRESULT WINAPI file_stream_Seek(IStream *iface, LARGE_INTEGER offset, DWORD method, ULARGE_INTEGER *ret_offset) +{ + struct file_stream *This = file_stream_from_IStream(iface); + DWORD position; - TRACE("(%p, %p)\n", iface, ppstm); - result = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pOther); - if (FAILED(result)) return result; - if (This->hFile != INVALID_HANDLE_VALUE) { - ULARGE_INTEGER ullCurrentPosition; - result = IDirectMusicLoaderFileStream_Attach(pOther, This->wzFileName); - if (SUCCEEDED(result)) - { - LARGE_INTEGER liZero; - liZero.QuadPart = 0; - result = IDirectMusicLoaderFileStream_IStream_Seek (iface, liZero, STREAM_SEEK_CUR, &ullCurrentPosition); /* get current position in current stream */ - } - if (SUCCEEDED(result)) { - LARGE_INTEGER liNewPosition; - liNewPosition.QuadPart = ullCurrentPosition.QuadPart; - result = IDirectMusicLoaderFileStream_IStream_Seek (pOther, liNewPosition, STREAM_SEEK_SET, &ullCurrentPosition); - } - if (FAILED(result)) { - TRACE(": failed\n"); - IDirectMusicLoaderFileStream_IStream_Release (pOther); - return result; - } - } - TRACE(": succeeded\n"); - *ppstm = pOther; - return S_OK; -} + TRACE("(%p, %I64d, %#lx, %p)\n", This, offset.QuadPart, method, ret_offset); -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Write (LPSTREAM iface, const void* pv, ULONG cb, ULONG* pcbWritten) { - ICOM_THIS_MULTI(IDirectMusicLoaderFileStream, StreamVtbl, iface); - ULONG cbWrite; - - TRACE_(dmfileraw)("(%p, %p, %#lx, %p)\n", This, pv, cb, pcbWritten); - if (This->hFile == INVALID_HANDLE_VALUE) return E_FAIL; - if (pcbWritten == NULL) pcbWritten = &cbWrite; - if (!WriteFile (This->hFile, pv, cb, pcbWritten, NULL) || *pcbWritten != cb) return E_FAIL; - - TRACE_(dmfileraw)(": data (size = %#lx): %s\n", *pcbWritten, debugstr_an(pv, *pcbWritten)); + position = SetFilePointer(This->file, offset.u.LowPart, NULL, method); + if (position == INVALID_SET_FILE_POINTER) return HRESULT_FROM_WIN32(GetLastError()); + if (ret_offset) ret_offset->QuadPart = position; return S_OK; } -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_SetSize (LPSTREAM iface, ULARGE_INTEGER libNewSize) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_SetSize(IStream *iface, ULARGE_INTEGER size) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; } -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_CopyTo (LPSTREAM iface, IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_CopyTo(IStream *iface, IStream *dest, ULARGE_INTEGER size, + ULARGE_INTEGER *read_size, ULARGE_INTEGER *write_size) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; } -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Commit (LPSTREAM iface, DWORD grfCommitFlags) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_Commit(IStream *iface, DWORD flags) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; } -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Revert (LPSTREAM iface) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_Revert(IStream *iface) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; } -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_LockRegion (LPSTREAM iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_LockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; } -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_UnlockRegion (LPSTREAM iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_UnlockRegion(IStream *iface, ULARGE_INTEGER offset, + ULARGE_INTEGER size, DWORD type) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; } -static HRESULT WINAPI IDirectMusicLoaderFileStream_IStream_Stat (LPSTREAM iface, STATSTG* pstatstg, DWORD grfStatFlag) { - ERR(": should not be needed\n"); +static HRESULT WINAPI file_stream_Stat(IStream *iface, STATSTG *stat, DWORD flags) +{ + struct file_stream *This = file_stream_from_IStream(iface); + FIXME("(%p): stub\n", This); return E_NOTIMPL; } -static const IStreamVtbl DirectMusicLoaderFileStream_Stream_Vtbl = { - IDirectMusicLoaderFileStream_IStream_QueryInterface, - IDirectMusicLoaderFileStream_IStream_AddRef, - IDirectMusicLoaderFileStream_IStream_Release, - IDirectMusicLoaderFileStream_IStream_Read, - IDirectMusicLoaderFileStream_IStream_Write, - IDirectMusicLoaderFileStream_IStream_Seek, - IDirectMusicLoaderFileStream_IStream_SetSize, - IDirectMusicLoaderFileStream_IStream_CopyTo, - IDirectMusicLoaderFileStream_IStream_Commit, - IDirectMusicLoaderFileStream_IStream_Revert, - IDirectMusicLoaderFileStream_IStream_LockRegion, - IDirectMusicLoaderFileStream_IStream_UnlockRegion, - IDirectMusicLoaderFileStream_IStream_Stat, - IDirectMusicLoaderFileStream_IStream_Clone -}; +static HRESULT WINAPI file_stream_Clone(IStream *iface, IStream **ret_iface) +{ + struct file_stream *This = file_stream_from_IStream(iface); + HRESULT hr; -HRESULT DMUSIC_CreateDirectMusicLoaderFileStream (void** ppobj) { - IDirectMusicLoaderFileStream *obj; + TRACE("(%p, %p)\n", This, ret_iface); - TRACE("(%p)\n", ppobj); + if (SUCCEEDED(hr = file_stream_create(This->path, ret_iface))) + { + LARGE_INTEGER position = {0}; + position.LowPart = SetFilePointer(This->file, 0, NULL, SEEK_CUR); + hr = IStream_Seek(*ret_iface, position, SEEK_SET, NULL); + } - *ppobj = NULL; - if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->StreamVtbl = &DirectMusicLoaderFileStream_Stream_Vtbl; - obj->dwRef = 0; /* will be inited with QueryInterface */ + return hr; +} - return IDirectMusicLoaderFileStream_IStream_QueryInterface ((LPSTREAM)&obj->StreamVtbl, &IID_IStream, ppobj); +static const IStreamVtbl file_stream_vtbl = +{ + file_stream_QueryInterface, + file_stream_AddRef, + file_stream_Release, + file_stream_Read, + file_stream_Write, + file_stream_Seek, + file_stream_SetSize, + file_stream_CopyTo, + file_stream_Commit, + file_stream_Revert, + file_stream_LockRegion, + file_stream_UnlockRegion, + file_stream_Stat, + file_stream_Clone, +}; + +HRESULT file_stream_create(const WCHAR *path, IStream **ret_iface) +{ + struct file_stream *stream; + + *ret_iface = NULL; + if (!(stream = calloc(1, sizeof(*stream)))) return E_OUTOFMEMORY; + stream->IStream_iface.lpVtbl = &file_stream_vtbl; + stream->ref = 1; + + wcscpy(stream->path, path); + stream->file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (stream->file == INVALID_HANDLE_VALUE) + { + free(stream); + return DMUS_E_LOADER_FAILEDOPEN; + } + + *ret_iface = &stream->IStream_iface; + return S_OK; } +static ULONG WINAPI IDirectMusicLoaderResourceStream_IStream_AddRef (LPSTREAM iface); /***************************************************************************** * IDirectMusicLoaderResourceStream implementation From 58804a7454e3406f843d6ecbc48703a25b92f42a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 20 Sep 2023 10:32:33 +0200 Subject: [PATCH 0949/1148] dmusic/tests: Test default gm.dls sound font instruments. (cherry picked from commit 767c5ddbf9bd2dc50442c5ba9e7eceffcd28c2fd) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/tests/dmusic.c | 300 +++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 703251bb9da..0e0ff7e609a 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1338,6 +1338,305 @@ static void test_download_instrument(void) IDirectMusic_Release(dmusic); } +struct result +{ + DWORD patch; + WCHAR name[DMUS_MAX_NAME]; +}; + +static int __cdecl result_cmp(const void *a, const void *b) +{ + const struct result *ra = a, *rb = b; + if (ra->patch != rb->patch) return ra->patch < rb->patch ? -1 : 1; + return wcscmp(ra->name, rb->name); +} + +static void test_default_gm_collection(void) +{ + DMUS_OBJECTDESC desc = + { + .dwSize = sizeof(DMUS_OBJECTDESC), + .dwValidData = DMUS_OBJ_OBJECT | DMUS_OBJ_CLASS, + .guidClass = CLSID_DirectMusicCollection, + .guidObject = GUID_DefaultGMCollection, + }; + struct result expected[] = + { + { 0, L"Piano 1 "}, + { 0x1, L"Piano 2 "}, + { 0x2, L"Piano 3 "}, + { 0x3, L"Honky-tonk "}, + { 0x4, L"E.Piano 1 "}, + { 0x5, L"E.Piano 2 "}, + { 0x6, L"Harpsichord "}, + { 0x7, L"Clav. "}, + { 0x8, L"Celesta "}, + { 0x9, L"Glockenspiel"}, + { 0xa, L"Music Box "}, + { 0xb, L"Vibraphone "}, + { 0xc, L"Marimba "}, + { 0xd, L"Xylophone "}, + { 0xe, L"Tubular-bell"}, + { 0xf, L"Santur "}, + { 0x10, L"Organ 1 "}, + { 0x11, L"Organ 2 "}, + { 0x12, L"Organ 3 "}, + { 0x13, L"Church Org.1"}, + { 0x14, L"Reed Organ "}, + { 0x15, L"Accordion Fr"}, + { 0x16, L"Harmonica "}, + { 0x17, L"Bandoneon "}, + { 0x18, L"Nylon-str.Gt"}, + { 0x19, L"Steel-str.Gt"}, + { 0x1a, L"Jazz Gt. "}, + { 0x1b, L"Clean Gt. "}, + { 0x1c, L"Muted Gt. "}, + { 0x1d, L"Overdrive Gt"}, + { 0x1e, L"DistortionGt"}, + { 0x1f, L"Gt.Harmonics"}, + { 0x20, L"Acoustic Bs."}, + { 0x21, L"Fingered Bs."}, + { 0x22, L"Picked Bs. "}, + { 0x23, L"Fretless Bs."}, + { 0x24, L"Slap Bass 1 "}, + { 0x25, L"Slap Bass 2 "}, + { 0x26, L"Synth Bass 1"}, + { 0x27, L"Synth Bass 2"}, + { 0x28, L"Violin "}, + { 0x29, L"Viola "}, + { 0x2a, L"Cello "}, + { 0x2b, L"Contrabass "}, + { 0x2c, L"Tremolo Str "}, + { 0x2d, L"PizzicatoStr"}, + { 0x2e, L"Harp "}, + { 0x2f, L"Timpani "}, + { 0x30, L"Strings "}, + { 0x31, L"Slow Strings"}, + { 0x32, L"Syn.Strings1"}, + { 0x33, L"Syn.Strings2"}, + { 0x34, L"Choir Aahs "}, + { 0x35, L"Voice Oohs "}, + { 0x36, L"SynVox "}, + { 0x37, L"OrchestraHit"}, + { 0x38, L"Trumpet "}, + { 0x39, L"Trombone "}, + { 0x3a, L"Tuba "}, + { 0x3b, L"MutedTrumpet"}, + { 0x3c, L"French Horns"}, + { 0x3d, L"Brass 1 "}, + { 0x3e, L"Synth Brass1"}, + { 0x3f, L"Synth Brass2"}, + { 0x40, L"Soprano Sax "}, + { 0x41, L"Alto Sax "}, + { 0x42, L"Tenor Sax "}, + { 0x43, L"Baritone Sax"}, + { 0x44, L"Oboe "}, + { 0x45, L"English Horn"}, + { 0x46, L"Bassoon "}, + { 0x47, L"Clarinet "}, + { 0x48, L"Piccolo "}, + { 0x49, L"Flute "}, + { 0x4a, L"Recorder "}, + { 0x4b, L"Pan Flute "}, + { 0x4c, L"Bottle Blow "}, + { 0x4d, L"Shakuhachi "}, + { 0x4e, L"Whistle "}, + { 0x4f, L"Ocarina "}, + { 0x50, L"Square Wave "}, + { 0x51, L"Saw Wave "}, + { 0x52, L"Syn.Calliope"}, + { 0x53, L"Chiffer Lead"}, + { 0x54, L"Charang "}, + { 0x55, L"Solo Vox "}, + { 0x56, L"5th Saw Wave"}, + { 0x57, L"Bass & Lead "}, + { 0x58, L"Fantasia "}, + { 0x59, L"Warm Pad "}, + { 0x5a, L"Polysynth "}, + { 0x5b, L"Space Voice "}, + { 0x5c, L"Bowed Glass "}, + { 0x5d, L"Metal Pad "}, + { 0x5e, L"Halo Pad "}, + { 0x5f, L"Sweep Pad "}, + { 0x60, L"Ice Rain "}, + { 0x61, L"Soundtrack "}, + { 0x62, L"Crystal "}, + { 0x63, L"Atmosphere "}, + { 0x64, L"Brightness "}, + { 0x65, L"Goblin "}, + { 0x66, L"Echo Drops "}, + { 0x67, L"Star Theme "}, + { 0x68, L"Sitar "}, + { 0x69, L"Banjo "}, + { 0x6a, L"Shamisen "}, + { 0x6b, L"Koto "}, + { 0x6c, L"Kalimba "}, + { 0x6d, L"Bagpipe "}, + { 0x6e, L"Fiddle "}, + { 0x6f, L"Shanai "}, + { 0x70, L"Tinkle Bell "}, + { 0x71, L"Agogo "}, + { 0x72, L"Steel Drums "}, + { 0x73, L"Woodblock "}, + { 0x74, L"Taiko "}, + { 0x75, L"Melo. Tom 1 "}, + { 0x76, L"Synth Drum "}, + { 0x77, L"Reverse Cym."}, + { 0x78, L"Gt.FretNoise"}, + { 0x79, L"Breath Noise"}, + { 0x7a, L"Seashore "}, + { 0x7b, L"Bird "}, + { 0x7c, L"Telephone 1 "}, + { 0x7d, L"Helicopter "}, + { 0x7e, L"Applause "}, + { 0x7f, L"Gun Shot "}, + { 0x10026, L"SynthBass101"}, + { 0x10039, L"Trombone 2 "}, + { 0x1003c, L"Fr.Horn 2 "}, + { 0x10050, L"Square "}, + { 0x10051, L"Saw "}, + { 0x10062, L"Syn Mallet "}, + { 0x10066, L"Echo Bell "}, + { 0x10068, L"Sitar 2 "}, + { 0x10078, L"Gt.Cut Noise"}, + { 0x10079, L"Fl.Key Click"}, + { 0x1007a, L"Rain "}, + { 0x1007b, L"Dog "}, + { 0x1007c, L"Telephone 2 "}, + { 0x1007d, L"Car-Engine "}, + { 0x1007e, L"Laughing "}, + { 0x1007f, L"Machine Gun "}, + { 0x20066, L"Echo Pan "}, + { 0x20078, L"String Slap "}, + { 0x2007a, L"Thunder "}, + { 0x2007b, L"Horse-Gallop"}, + { 0x2007c, L"DoorCreaking"}, + { 0x2007d, L"Car-Stop "}, + { 0x2007e, L"Screaming "}, + { 0x2007f, L"Lasergun "}, + { 0x3007a, L"Wind "}, + { 0x3007b, L"Bird 2 "}, + { 0x3007c, L"Door "}, + { 0x3007d, L"Car-Pass "}, + { 0x3007e, L"Punch "}, + { 0x3007f, L"Explosion "}, + { 0x4007a, L"Stream "}, + { 0x4007c, L"Scratch "}, + { 0x4007d, L"Car-Crash "}, + { 0x4007e, L"Heart Beat "}, + { 0x5007a, L"Bubble "}, + { 0x5007c, L"Wind Chimes "}, + { 0x5007d, L"Siren "}, + { 0x5007e, L"Footsteps "}, + { 0x6007d, L"Train "}, + { 0x7007d, L"Jetplane "}, + { 0x80000, L"Piano 1 "}, + { 0x80001, L"Piano 2 "}, + { 0x80002, L"Piano 3 "}, + { 0x80003, L"Honky-tonk "}, + { 0x80004, L"Detuned EP 1"}, + { 0x80005, L"Detuned EP 2"}, + { 0x80006, L"Coupled Hps."}, + { 0x8000b, L"Vibraphone "}, + { 0x8000c, L"Marimba "}, + { 0x8000e, L"Church Bell "}, + { 0x80010, L"Detuned Or.1"}, + { 0x80011, L"Detuned Or.2"}, + { 0x80013, L"Church Org.2"}, + { 0x80015, L"Accordion It"}, + { 0x80018, L"Ukulele "}, + { 0x80019, L"12-str.Gt "}, + { 0x8001a, L"Hawaiian Gt."}, + { 0x8001b, L"Chorus Gt. "}, + { 0x8001c, L"Funk Gt. "}, + { 0x8001e, L"Feedback Gt."}, + { 0x8001f, L"Gt. Feedback"}, + { 0x80026, L"Synth Bass 3"}, + { 0x80027, L"Synth Bass 4"}, + { 0x80028, L"Slow Violin "}, + { 0x80030, L"Orchestra "}, + { 0x80032, L"Syn.Strings3"}, + { 0x8003d, L"Brass 2 "}, + { 0x8003e, L"Synth Brass3"}, + { 0x8003f, L"Synth Brass4"}, + { 0x80050, L"Sine Wave "}, + { 0x80051, L"Doctor Solo "}, + { 0x8006b, L"Taisho Koto "}, + { 0x80073, L"Castanets "}, + { 0x80074, L"Concert BD "}, + { 0x80075, L"Melo. Tom 2 "}, + { 0x80076, L"808 Tom "}, + { 0x8007d, L"Starship "}, + { 0x9000e, L"Carillon "}, + { 0x90076, L"Elec Perc. "}, + { 0x9007d, L"Burst Noise "}, + { 0x100000, L"Piano 1d "}, + { 0x100004, L"E.Piano 1v "}, + { 0x100005, L"E.Piano 2v "}, + { 0x100006, L"Harpsichord "}, + { 0x100010, L"60's Organ 1"}, + { 0x100013, L"Church Org.3"}, + { 0x100018, L"Nylon Gt.o "}, + { 0x100019, L"Mandolin "}, + { 0x10001c, L"Funk Gt.2 "}, + { 0x100027, L"Rubber Bass "}, + { 0x10003e, L"AnalogBrass1"}, + { 0x10003f, L"AnalogBrass2"}, + { 0x180004, L"60's E.Piano"}, + { 0x180006, L"Harpsi.o "}, + { 0x200010, L"Organ 4 "}, + { 0x200011, L"Organ 5 "}, + { 0x200018, L"Nylon Gt.2 "}, + { 0x200034, L"Choir Aahs 2"}, + {0x80000000, L"Standard "}, + {0x80000008, L"Room "}, + {0x80000010, L"Power "}, + {0x80000018, L"Electronic "}, + {0x80000019, L"TR-808 "}, + {0x80000020, L"Jazz "}, + {0x80000028, L"Brush "}, + {0x80000030, L"Orchestra "}, + {0x80000038, L"SFX "}, + }, results[ARRAY_SIZE(expected) + 1]; + IDirectMusicCollection *collection; + IDirectMusicLoader *loader; + HRESULT hr; + DWORD i; + + hr = CoCreateInstance(&CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicLoader, (void**)&loader); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicCollection, (void **)&collection); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + + for (i = 0; hr == S_OK && i < ARRAY_SIZE(results); i++) + { + results[i].patch = 0xdeadbeef; + wcscpy(results[i].name, L"DeadBeef"); + hr = IDirectMusicCollection_EnumInstrument(collection, i, &results[i].patch, + results[i].name, ARRAY_SIZE(results[i].name)); + } + if (hr == S_FALSE) i--; + todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + todo_wine ok(i > 0, "got %lu\n", i); + todo_wine ok(i == ARRAY_SIZE(expected), "got %lu\n", i); + + qsort(results, i, sizeof(*results), result_cmp); + + while (i--) + { + winetest_push_context("%lu", i); + trace("got %#lx %s\n", results[i].patch, debugstr_w(results[i].name)); + ok(results[i].patch == expected[i].patch, "got %#lx\n", results[i].patch); + /* system soundfont names are not very predictable, let's not check them */ + winetest_pop_context(); + } + + if (hr == S_FALSE) IDirectMusicCollection_Release(collection); + IDirectMusicLoader_Release(loader); +} + START_TEST(dmusic) { CoInitializeEx(NULL, COINIT_MULTITHREADED); @@ -1360,6 +1659,7 @@ START_TEST(dmusic) test_synthport(); test_port_download(); test_download_instrument(); + test_default_gm_collection(); CoUninitialize(); } From e93e53a064b99efb7483665c609537c9a67cfffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 26 Sep 2023 08:22:38 +0200 Subject: [PATCH 0950/1148] dmloader: Remove invalid default DLS collection check. (cherry picked from commit 49c6e57d95d6f7d7647b132ea658ef0705b27d1d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/loader.c | 54 +++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index 9c97f47e5f7..d15c99f6fb6 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -40,7 +40,6 @@ struct cache_entry { struct list entry; DMUS_OBJECTDESC Desc; IDirectMusicObject *pObject; - BOOL bInvalidDefaultDLS; /* workaround for enabling caching of "faulty" default dls collection */ }; struct loader @@ -276,12 +275,6 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE TRACE(": looking if we have object in the cache or if it can be found via alias\n"); pExistingEntry = find_cache_object(This, pDesc); if (pExistingEntry) { - - if (pExistingEntry->bInvalidDefaultDLS) { - TRACE(": found faulty default DLS collection... enabling M$ compliant behaviour\n"); - return DMUS_E_LOADER_NOFILENAME; - } - if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) { TRACE(": already loaded\n"); return IDirectMusicObject_QueryInterface(pExistingEntry->pObject, riid, ppv); @@ -404,23 +397,26 @@ static HRESULT WINAPI loader_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE IStream_Release (pStream); IPersistStream_Release (pPersistStream); - /* add object to cache/overwrite existing info (if cache is enabled) */ - bCache = is_cache_enabled(This, &pDesc->guidClass); - if (bCache) { - if (!pObjectEntry) { + /* add object to cache/overwrite existing info (if cache is enabled) */ + bCache = is_cache_enabled(This, &pDesc->guidClass); + if (!bCache) TRACE(": caching disabled\n"); + else + { + if (!pObjectEntry) + { pObjectEntry = calloc(1, sizeof(*pObjectEntry)); - DM_STRUCT_INIT(&pObjectEntry->Desc); - DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); - pObjectEntry->pObject = pObject; - pObjectEntry->bInvalidDefaultDLS = FALSE; - list_add_head(&This->cache, &pObjectEntry->entry); - } else { - DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); - pObjectEntry->pObject = pObject; - pObjectEntry->bInvalidDefaultDLS = FALSE; - } - TRACE(": filled in cache entry\n"); - } else TRACE(": caching disabled\n"); + DM_STRUCT_INIT(&pObjectEntry->Desc); + DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); + pObjectEntry->pObject = pObject; + list_add_head(&This->cache, &pObjectEntry->entry); + } + else + { + DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); + pObjectEntry->pObject = pObject; + } + TRACE(": filled in cache entry\n"); + } result = IDirectMusicObject_QueryInterface (pObject, riid, ppv); if (!bCache) IDirectMusicObject_Release (pObject); /* since loader's reference is not needed */ @@ -873,8 +869,6 @@ HRESULT create_dmloader(REFIID lpcGUID, void **ppobj) { struct loader *obj; DMUS_OBJECTDESC Desc; - struct cache_entry *dls; - struct list *pEntry; HRESULT hr; TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID), ppobj); @@ -895,16 +889,6 @@ HRESULT create_dmloader(REFIID lpcGUID, void **ppobj) DMUSIC_GetDefaultGMPath(Desc.wszFileName); IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc); - /* and now the workaroundTM for "invalid" default DLS; basically, - my tests showed that if GUID chunk is present in default DLS - collection, loader treats it as "invalid" and returns - DMUS_E_LOADER_NOFILENAME for all requests for it; basically, we check - if out input guidObject was overwritten */ - pEntry = list_head(&obj->cache); - dls = LIST_ENTRY(pEntry, struct cache_entry, entry); - if (!IsEqualGUID(&Desc.guidObject, &GUID_DefaultGMCollection)) - dls->bInvalidDefaultDLS = TRUE; - hr = IDirectMusicLoader_QueryInterface(&obj->IDirectMusicLoader8_iface, lpcGUID, ppobj); IDirectMusicLoader_Release(&obj->IDirectMusicLoader8_iface); return hr; From f8513fab0cf225744b0970a4d247802e270f448f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 26 Sep 2023 15:12:45 +0200 Subject: [PATCH 0951/1148] dmloader: Add fallbacks if the configured GMFilePath doesn't exist. (cherry picked from commit 8e881787fc542355e8a9422590fdbbb334f63450) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmloader/loader.c | 67 ++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index d15c99f6fb6..78b9790dff3 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -20,6 +20,32 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmloader); +static const WCHAR *system_default_gm_paths[] = +{ + L"/usr/share/sounds/sf2/default-GM.sf2", + L"/usr/share/soundfonts/default.sf2", +}; + +static HRESULT get_system_default_gm_path(WCHAR *path, UINT max_len) +{ + UINT i; + + for (i = 0; i < ARRAY_SIZE(system_default_gm_paths); i++) + { + swprintf(path, max_len, L"\\??\\unix%s", system_default_gm_paths[i]); + if (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) break; + } + + if (i < ARRAY_SIZE(system_default_gm_paths)) + { + WARN("Using system %s for the default collection\n", debugstr_w(path)); + return S_OK; + } + + ERR("Unable to find system path, default collection will not be available\n"); + return DMUS_E_LOADER_FAILEDOPEN; +} + static const GUID *classes[] = { &GUID_DirectMusicAllTypes, /* Keep as first */ &CLSID_DirectMusicAudioPathConfig, @@ -458,6 +484,13 @@ static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDE lstrcpyW(p, pDesc->wszFileName); } + if (!wcsicmp(file_name, L"C:\\windows\\system32\\drivers\\gm.dls") + && GetFileAttributesW(file_name) == INVALID_FILE_ATTRIBUTES) + { + hr = get_system_default_gm_path(file_name, ARRAY_SIZE(file_name)); + if (FAILED(hr)) return hr; + } + if (FAILED(hr = file_stream_create(file_name, &pStream))) return hr; } else if (pDesc->dwValidData & DMUS_OBJ_STREAM) @@ -847,21 +880,24 @@ static const IDirectMusicLoader8Vtbl loader_vtbl = loader_LoadObjectFromFile, }; -/* help function for DMUSIC_SetDefaultDLS */ -static HRESULT DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) { - HKEY hkDM; - DWORD returnType, sizeOfReturnBuffer = MAX_PATH; - char szPath[MAX_PATH]; +static HRESULT get_default_gm_path(WCHAR *path, DWORD max_len) +{ + DWORD ret; + HKEY hkey; + + if (!(ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkey))) + { + DWORD type, size = max_len * sizeof(WCHAR); + ret = RegQueryValueExW(hkey, L"GMFilePath", NULL, &type, (BYTE *)path, &size); + RegCloseKey(hkey); - if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) || - (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, (LPBYTE) szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) { - WARN(": registry entry missing\n" ); - return E_FAIL; - } - /* FIXME: Check return types to ensure we're interpreting data right */ - MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH); + if (!ret && GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) return S_OK; + } - return S_OK; + if (!ret) WARN("Failed to find %s, using system fallbacks\n", debugstr_w(path)); + else WARN("Failed to open GMFilePath registry key, using system fallbacks\n"); + + return get_system_default_gm_path(path, max_len); } /* for ClassFactory */ @@ -886,8 +922,9 @@ HRESULT create_dmloader(REFIID lpcGUID, void **ppobj) Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_OBJECT; Desc.guidClass = CLSID_DirectMusicCollection; Desc.guidObject = GUID_DefaultGMCollection; - DMUSIC_GetDefaultGMPath(Desc.wszFileName); - IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc); + if (SUCCEEDED(hr = get_default_gm_path(Desc.wszFileName, ARRAY_SIZE(Desc.wszFileName)))) + hr = IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc); + if (FAILED(hr)) WARN("Failed to load the default collection, hr %#lx\n", hr); hr = IDirectMusicLoader_QueryInterface(&obj->IDirectMusicLoader8_iface, lpcGUID, ppobj); IDirectMusicLoader_Release(&obj->IDirectMusicLoader8_iface); From 8c978eeaa9774edfd8127d6330ba2e6e79c3ff85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 22 Sep 2023 20:56:17 +0200 Subject: [PATCH 0952/1148] dmusic: Avoid leaking articulations when freeing regions. (cherry picked from commit 2a1fd9851c33fae7d8cc3b6ef63e9bf8a0effe4f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/instrument.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 8ce3c0bc979..54ce7508ee4 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -44,6 +44,19 @@ struct region BOOL loop_present; }; +static void region_destroy(struct region *region) +{ + struct articulation *articulation, *next; + + LIST_FOR_EACH_ENTRY_SAFE(articulation, next, ®ion->articulations, struct articulation, entry) + { + list_remove(&articulation->entry); + free(articulation); + } + + free(region); +} + struct instrument { IDirectMusicInstrument IDirectMusicInstrument_iface; @@ -122,15 +135,8 @@ static ULONG WINAPI instrument_Release(LPDIRECTMUSICINSTRUMENT iface) LIST_FOR_EACH_ENTRY_SAFE(region, next_region, &This->regions, struct region, entry) { - LIST_FOR_EACH_ENTRY_SAFE(articulation, next_articulation, ®ion->articulations, - struct articulation, entry) - { - list_remove(&articulation->entry); - free(articulation); - } - list_remove(®ion->entry); - free(region); + region_destroy(region); } collection_internal_release(This->collection); @@ -322,7 +328,7 @@ static HRESULT parse_rgn_chunk(struct instrument *This, IStream *stream, struct if (FAILED(hr)) break; } - if (FAILED(hr)) free(region); + if (FAILED(hr)) region_destroy(region); else list_add_tail(&This->regions, ®ion->entry); return hr; From 6bdd3ff8c3c776b64be655a92207b6b8a5d3ee32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 26 Sep 2023 08:28:19 +0200 Subject: [PATCH 0953/1148] dmusic: Avoid crashing in traces if wave doesn't have a WSMPL. (cherry picked from commit 182338bab26cdfd904336c0a0508537ad8e62d47) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/wave.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index 08146db0d1c..6637612596a 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -199,14 +199,17 @@ HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnk TRACE(" - wBitsPerSample: %u\n", This->format->wBitsPerSample); TRACE(" - cbSize: %u\n", This->format->cbSize); } - TRACE(" - sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n", - This->sample->head.cbSize, This->sample->head.usUnityNote, - This->sample->head.sFineTune, This->sample->head.lAttenuation, - This->sample->head.fulOptions, This->sample->head.cSampleLoops); - for (i = 0; i < This->sample->head.cSampleLoops; i++) - TRACE(" - loops[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i, - This->sample->loops[i].cbSize, This->sample->loops[i].ulType, - This->sample->loops[i].ulStart, This->sample->loops[i].ulLength); + if (This->sample) + { + TRACE(" - sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n", + This->sample->head.cbSize, This->sample->head.usUnityNote, + This->sample->head.sFineTune, This->sample->head.lAttenuation, + This->sample->head.fulOptions, This->sample->head.cSampleLoops); + for (i = 0; i < This->sample->head.cSampleLoops; i++) + TRACE(" - loops[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i, + This->sample->loops[i].cbSize, This->sample->loops[i].ulType, + This->sample->loops[i].ulStart, This->sample->loops[i].ulLength); + } } *ret_iface = iface; From 4b46c1f4c8627d0aed9b824aa31924f5cdb22661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 09:11:55 +0200 Subject: [PATCH 0954/1148] dmusic: Implement SoundFont2 collection parsing. (cherry picked from commit b733a46adae2af03d364bac9a0ecfd83390d25d3) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 2 +- dlls/dmusic/collection.c | 249 +++++++++++++++++++++++++++- dlls/dmusic/soundfont.h | 323 +++++++++++++++++++++++++++++++++++++ dlls/dmusic/tests/dmusic.c | 6 +- 4 files changed, 574 insertions(+), 6 deletions(-) create mode 100644 dlls/dmusic/soundfont.h diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 119bfc74fc5..57879f52069 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3546,7 +3546,7 @@ static void test_band_track_play(void) &IID_IDirectMusicLoader8, (void **)&loader); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicLoader_SetObject(loader, &desc); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); hr = test_loader_stream_create(stream, loader, &loader_stream); ok(hr == S_OK, "got %#lx\n", hr); diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index 1074c4e2868..feda1cbf8a9 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -19,6 +19,7 @@ */ #include "dmusic_private.h" +#include "soundfont.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); @@ -353,6 +354,246 @@ static HRESULT parse_dls_chunk(struct collection *This, IStream *stream, struct return hr; } +static HRESULT parse_sdta_list(struct collection *This, IStream *stream, struct chunk_entry *parent, + struct soundfont *soundfont) +{ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case mmioFOURCC('s','m','p','l'): + if (soundfont->sdta) return E_INVALIDARG; + if (!(soundfont->sdta = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, soundfont->sdta, chunk.size); + break; + + default: + FIXME("Skipping unknown chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + return hr; +} + +static HRESULT parse_pdta_list(struct collection *This, IStream *stream, struct chunk_entry *parent, + struct soundfont *soundfont) +{ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case mmioFOURCC('p','h','d','r'): + if (soundfont->phdr) return E_INVALIDARG; + if (!(soundfont->phdr = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, soundfont->phdr, chunk.size); + soundfont->preset_count = chunk.size / sizeof(*soundfont->phdr) - 1; + break; + + case mmioFOURCC('p','b','a','g'): + if (soundfont->pbag) return E_INVALIDARG; + if (!(soundfont->pbag = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, soundfont->pbag, chunk.size); + break; + + case mmioFOURCC('p','m','o','d'): + if (soundfont->pmod) return E_INVALIDARG; + if (!(soundfont->pmod = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, soundfont->pmod, chunk.size); + break; + + case mmioFOURCC('p','g','e','n'): + if (soundfont->pgen) return E_INVALIDARG; + if (!(soundfont->pgen = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, soundfont->pgen, chunk.size); + break; + + case mmioFOURCC('i','n','s','t'): + if (soundfont->inst) return E_INVALIDARG; + if (!(soundfont->inst = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, soundfont->inst, chunk.size); + soundfont->instrument_count = chunk.size / sizeof(*soundfont->inst) - 1; + break; + + case mmioFOURCC('i','b','a','g'): + if (soundfont->ibag) return E_INVALIDARG; + if (!(soundfont->ibag = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, soundfont->ibag, chunk.size); + break; + + case mmioFOURCC('i','m','o','d'): + if (soundfont->imod) return E_INVALIDARG; + if (!(soundfont->imod = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, soundfont->imod, chunk.size); + break; + + case mmioFOURCC('i','g','e','n'): + if (soundfont->igen) return E_INVALIDARG; + if (!(soundfont->igen = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, soundfont->igen, chunk.size); + break; + + case mmioFOURCC('s','h','d','r'): + if (soundfont->shdr) return E_INVALIDARG; + if (!(soundfont->shdr = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, soundfont->shdr, chunk.size); + soundfont->sample_count = chunk.size / sizeof(*soundfont->shdr) - 1; + break; + + default: + FIXME("Skipping unknown chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + return hr; +} + +static HRESULT parse_sfbk_chunk(struct collection *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + struct soundfont soundfont = {0}; + UINT i, j, k; + HRESULT hr; + + if (FAILED(hr = dmobj_parsedescriptor(stream, parent, &This->dmobj.desc, + DMUS_OBJ_NAME_INFO|DMUS_OBJ_VERSION|DMUS_OBJ_OBJECT|DMUS_OBJ_GUID_DLID)) + || FAILED(hr = stream_reset_chunk_data(stream, parent))) + return hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_INFO_LIST): + /* already parsed by dmobj_parsedescriptor */ + break; + + case MAKE_IDTYPE(FOURCC_LIST, mmioFOURCC('s','d','t','a')): + hr = parse_sdta_list(This, stream, &chunk, &soundfont); + break; + + case MAKE_IDTYPE(FOURCC_LIST, mmioFOURCC('p','d','t','a')): + hr = parse_pdta_list(This, stream, &chunk, &soundfont); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + if (SUCCEEDED(hr)) + { + TRACE("presets:\n"); + for (i = 0; i < soundfont.preset_count; i++) + { + struct sf_preset *preset = soundfont.phdr + i; + + TRACE("preset[%u]:\n", i); + TRACE(" - name: %s\n", debugstr_a(preset->name)); + TRACE(" - preset: %u\n", preset->preset); + TRACE(" - bank: %u\n", preset->bank); + TRACE(" - preset_bag_ndx: %u\n", preset->bag_ndx); + TRACE(" - library: %lu\n", preset->library); + TRACE(" - genre: %lu\n", preset->genre); + TRACE(" - morphology: %#lx\n", preset->morphology); + + for (j = preset->bag_ndx; j < (preset + 1)->bag_ndx; j++) + { + struct sf_bag *bag = soundfont.pbag + j; + TRACE(" - bag[%u]:\n", j); + TRACE(" - gen_ndx: %u\n", bag->gen_ndx); + TRACE(" - mod_ndx: %u\n", bag->mod_ndx); + + for (k = bag->gen_ndx; k < (bag + 1)->gen_ndx; k++) + { + struct sf_gen *gen = soundfont.pgen + k; + TRACE(" - gen[%u]: %s\n", k, debugstr_sf_gen(gen)); + } + + for (k = bag->mod_ndx; k < (bag + 1)->mod_ndx; k++) + { + struct sf_mod *mod = soundfont.pmod + k; + TRACE(" - mod[%u]: %s\n", k, debugstr_sf_mod(mod)); + } + } + } + + TRACE("instruments:\n"); + for (i = 0; i < soundfont.instrument_count; i++) + { + struct sf_instrument *instrument = soundfont.inst + i; + TRACE("instrument[%u]:\n", i); + TRACE(" - name: %s\n", debugstr_a(instrument->name)); + TRACE(" - bag_ndx: %u\n", instrument->bag_ndx); + + for (j = instrument->bag_ndx; j < (instrument + 1)->bag_ndx; j++) + { + struct sf_bag *bag = soundfont.ibag + j; + TRACE(" - bag[%u]:\n", j); + TRACE(" - wGenNdx: %u\n", bag->gen_ndx); + TRACE(" - wModNdx: %u\n", bag->mod_ndx); + + for (k = bag->gen_ndx; k < (bag + 1)->gen_ndx; k++) + { + struct sf_gen *gen = soundfont.igen + k; + TRACE(" - gen[%u]: %s\n", k, debugstr_sf_gen(gen)); + } + + for (k = bag->mod_ndx; k < (bag + 1)->mod_ndx; k++) + { + struct sf_mod *mod = soundfont.imod + k; + TRACE(" - mod[%u]: %s\n", k, debugstr_sf_mod(mod)); + } + } + } + + TRACE("samples:\n"); + for (i = 0; i < soundfont.sample_count; i++) + { + struct sf_sample *sample = soundfont.shdr + i; + + TRACE("sample[%u]:\n", i); + TRACE(" - name: %s\n", debugstr_a(sample->name)); + TRACE(" - start: %lu\n", sample->start); + TRACE(" - end: %lu\n", sample->end); + TRACE(" - start_loop: %lu\n", sample->start_loop); + TRACE(" - end_loop: %lu\n", sample->end_loop); + TRACE(" - sample_rate: %lu\n", sample->sample_rate); + TRACE(" - original_key: %u\n", sample->original_key); + TRACE(" - correction: %d\n", sample->correction); + TRACE(" - sample_link: %#x\n", sample->sample_link); + TRACE(" - sample_type: %#x\n", sample->sample_type); + } + } + + free(soundfont.phdr); + free(soundfont.pbag); + free(soundfont.pmod); + free(soundfont.pgen); + free(soundfont.inst); + free(soundfont.ibag); + free(soundfont.imod); + free(soundfont.igen); + free(soundfont.shdr); + free(soundfont.sdta); + + return hr; +} + static HRESULT WINAPI collection_object_ParseDescriptor(IDirectMusicObject *iface, IStream *stream, DMUS_OBJECTDESC *desc) { @@ -366,7 +607,7 @@ static HRESULT WINAPI collection_object_ParseDescriptor(IDirectMusicObject *ifac if ((hr = stream_get_chunk(stream, &riff)) != S_OK) return hr; - if (riff.id != FOURCC_RIFF || riff.type != FOURCC_DLS) { + if (riff.id != FOURCC_RIFF || (riff.type != FOURCC_DLS && riff.type != mmioFOURCC('s','f','b','k'))) { TRACE("loading failed: unexpected %s\n", debugstr_chunk(&riff)); stream_skip_chunk(stream, &riff); return DMUS_E_NOTADLSCOL; @@ -410,6 +651,10 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *str hr = parse_dls_chunk(This, stream, &chunk); break; + case MAKE_IDTYPE(FOURCC_RIFF, mmioFOURCC('s','f','b','k')): + hr = parse_sfbk_chunk(This, stream, &chunk); + break; + default: WARN("Invalid collection chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); hr = DMUS_E_UNSUPPORTED_STREAM; @@ -439,7 +684,7 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *str } TRACE(" - cues:\n"); - for (i = 0; i < This->pool->table.cCues; i++) + for (i = 0; This->pool && i < This->pool->table.cCues; i++) TRACE(" - index: %u, offset: %lu\n", i, This->pool->cues[i].ulOffset); TRACE(" - waves:\n"); diff --git a/dlls/dmusic/soundfont.h b/dlls/dmusic/soundfont.h new file mode 100644 index 00000000000..ac71ba7909a --- /dev/null +++ b/dlls/dmusic/soundfont.h @@ -0,0 +1,323 @@ +/* + * Copyright 2023 Rémi Bernon for CodeWeavers + * + * This program 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 program 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 program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "stdarg.h" +#include "stddef.h" + +#include "windef.h" +#include "winbase.h" + +#include "wine/debug.h" + +#include + +/* SoundFont 2.04 data structures, from http://www.synthfont.com/sfspec24.pdf */ + +struct sf_range +{ + BYTE low; + BYTE high; +}; + +union sf_amount +{ + struct sf_range range; + WORD value; +}; + +C_ASSERT(sizeof(union sf_amount) == 2); + +enum +{ + SF_SAMPLE_MONO = 1, + SF_SAMPLE_RIGHT = 2, + SF_SAMPLE_LEFT = 4, + SF_SAMPLE_LINKED = 8, + SF_SAMPLE_ROM_MONO = 0x8001, + SF_SAMPLE_ROM_RIGHT = 0x8002, + SF_SAMPLE_ROM_LEFT = 0x8004, + SF_SAMPLE_ROM_LINKED = 0x8008, +}; +typedef WORD sf_sample_type; + +enum +{ + SF_GEN_START_ADDRS_OFFSET = 0, + SF_GEN_END_ADDRS_OFFSET = 1, + SF_GEN_STARTLOOP_ADDRS_OFFSET = 2, + SF_GEN_ENDLOOP_ADDRS_OFFSET = 3, + SF_GEN_START_ADDRS_COARSE_OFFSET = 4, + SF_GEN_MOD_LFO_TO_PITCH = 5, + SF_GEN_VIB_LFO_TO_PITCH = 6, + SF_GEN_MOD_ENV_TO_PITCH = 7, + SF_GEN_INITIAL_FILTER_FC = 8, + SF_GEN_INITIAL_FILTER_Q = 9, + SF_GEN_MOD_LFO_TO_FILTER_FC = 10, + SF_GEN_MOD_ENV_TO_FILTER_FC = 11, + SF_GEN_END_ADDRS_COARSE_OFFSET = 12, + SF_GEN_MOD_LFO_TO_VOLUME = 13, + + SF_GEN_CHORUS_EFFECTS_SEND = 15, + SF_GEN_REVERB_EFFECTS_SEND = 16, + SF_GEN_PAN = 17, + + SF_GEN_DELAY_MOD_LFO = 21, + SF_GEN_FREQ_MOD_LFO = 22, + SF_GEN_DELAY_VIB_LFO = 23, + SF_GEN_FREQ_VIB_LFO = 24, + SF_GEN_DELAY_MOD_ENV = 25, + SF_GEN_ATTACK_MOD_ENV = 26, + SF_GEN_HOLD_MOD_ENV = 27, + SF_GEN_DECAY_MOD_ENV = 28, + SF_GEN_SUSTAIN_MOD_ENV = 29, + SF_GEN_RELEASE_MOD_ENV = 30, + SF_GEN_KEYNUM_TO_MOD_ENV_HOLD = 31, + SF_GEN_KEYNUM_TO_MOD_ENV_DECAY = 32, + SF_GEN_DELAY_VOL_ENV = 33, + SF_GEN_ATTACK_VOL_ENV = 34, + SF_GEN_HOLD_VOL_ENV = 35, + SF_GEN_DECAY_VOL_ENV = 36, + SF_GEN_SUSTAIN_VOL_ENV = 37, + SF_GEN_RELEASE_VOL_ENV = 38, + SF_GEN_KEYNUM_TO_VOL_ENV_HOLD = 39, + SF_GEN_KEYNUM_TO_VOL_ENV_DECAY = 40, + SF_GEN_INSTRUMENT = 41, + + SF_GEN_KEY_RANGE = 43, + SF_GEN_VEL_RANGE = 44, + SF_GEN_STARTLOOP_ADDRS_COARSE_OFFSET = 45, + SF_GEN_KEYNUM = 46, + SF_GEN_VELOCITY = 47, + SF_GEN_INITIAL_ATTENUATION = 48, + + SF_GEN_ENDLOOP_ADDRS_COARSE_OFFSET = 50, + SF_GEN_COARSE_TUNE = 51, + SF_GEN_FINE_TUNE = 52, + SF_GEN_SAMPLE_ID = 53, + SF_GEN_SAMPLE_MODES = 54, + + SF_GEN_SCALE_TUNING = 56, + SF_GEN_EXCLUSIVE_CLASS = 57, + SF_GEN_OVERRIDING_ROOT_KEY = 58, + + SF_GEN_END_OPER = 60, +}; +typedef WORD sf_generator; + +static inline const char *debugstr_sf_generator(sf_generator oper) +{ + switch (oper) + { + case SF_GEN_START_ADDRS_OFFSET: return "start_addrs_offset"; + case SF_GEN_END_ADDRS_OFFSET: return "end_addrs_offset"; + case SF_GEN_STARTLOOP_ADDRS_OFFSET: return "startloop_addrs_offset"; + case SF_GEN_ENDLOOP_ADDRS_OFFSET: return "endloop_addrs_offset"; + case SF_GEN_START_ADDRS_COARSE_OFFSET: return "start_addrs_coarse_offset"; + case SF_GEN_MOD_LFO_TO_PITCH: return "mod_lfo_to_pitch"; + case SF_GEN_VIB_LFO_TO_PITCH: return "vib_lfo_to_pitch"; + case SF_GEN_MOD_ENV_TO_PITCH: return "mod_env_to_pitch"; + case SF_GEN_INITIAL_FILTER_FC: return "initial_filter_fc"; + case SF_GEN_INITIAL_FILTER_Q: return "initial_filter_q"; + case SF_GEN_MOD_LFO_TO_FILTER_FC: return "mod_lfo_to_filter_fc"; + case SF_GEN_MOD_ENV_TO_FILTER_FC: return "mod_env_to_filter_fc"; + case SF_GEN_END_ADDRS_COARSE_OFFSET: return "end_addrs_coarse_offset"; + case SF_GEN_MOD_LFO_TO_VOLUME: return "mod_lfo_to_volume"; + case SF_GEN_CHORUS_EFFECTS_SEND: return "chorus_effects_send"; + case SF_GEN_REVERB_EFFECTS_SEND: return "reverb_effects_send"; + case SF_GEN_PAN: return "pan"; + case SF_GEN_DELAY_MOD_LFO: return "delay_mod_lfo"; + case SF_GEN_FREQ_MOD_LFO: return "freq_mod_lfo"; + case SF_GEN_DELAY_VIB_LFO: return "delay_vib_lfo"; + case SF_GEN_FREQ_VIB_LFO: return "freq_vib_lfo"; + case SF_GEN_DELAY_MOD_ENV: return "delay_mod_env"; + case SF_GEN_ATTACK_MOD_ENV: return "attack_mod_env"; + case SF_GEN_HOLD_MOD_ENV: return "hold_mod_env"; + case SF_GEN_DECAY_MOD_ENV: return "decay_mod_env"; + case SF_GEN_SUSTAIN_MOD_ENV: return "sustain_mod_env"; + case SF_GEN_RELEASE_MOD_ENV: return "release_mod_env"; + case SF_GEN_KEYNUM_TO_MOD_ENV_HOLD: return "keynum_to_mod_env_hold"; + case SF_GEN_KEYNUM_TO_MOD_ENV_DECAY: return "keynum_to_mod_env_decay"; + case SF_GEN_DELAY_VOL_ENV: return "delay_vol_env"; + case SF_GEN_ATTACK_VOL_ENV: return "attack_vol_env"; + case SF_GEN_HOLD_VOL_ENV: return "hold_vol_env"; + case SF_GEN_DECAY_VOL_ENV: return "decay_vol_env"; + case SF_GEN_SUSTAIN_VOL_ENV: return "sustain_vol_env"; + case SF_GEN_RELEASE_VOL_ENV: return "release_vol_env"; + case SF_GEN_KEYNUM_TO_VOL_ENV_HOLD: return "keynum_to_vol_env_hold"; + case SF_GEN_KEYNUM_TO_VOL_ENV_DECAY: return "keynum_to_vol_env_decay"; + case SF_GEN_INSTRUMENT: return "instrument"; + case SF_GEN_KEY_RANGE: return "key_range"; + case SF_GEN_VEL_RANGE: return "vel_range"; + case SF_GEN_STARTLOOP_ADDRS_COARSE_OFFSET: return "startloop_addrs_coarse_offset"; + case SF_GEN_KEYNUM: return "keynum"; + case SF_GEN_VELOCITY: return "velocity"; + case SF_GEN_INITIAL_ATTENUATION: return "initial_attenuation"; + case SF_GEN_ENDLOOP_ADDRS_COARSE_OFFSET: return "endloop_addrs_coarse_offset"; + case SF_GEN_COARSE_TUNE: return "coarse_tune"; + case SF_GEN_FINE_TUNE: return "fine_tune"; + case SF_GEN_SAMPLE_ID: return "sample_id"; + case SF_GEN_SAMPLE_MODES: return "sample_modes"; + case SF_GEN_SCALE_TUNING: return "scale_tuning"; + case SF_GEN_EXCLUSIVE_CLASS: return "exclusive_class"; + case SF_GEN_OVERRIDING_ROOT_KEY: return "overriding_root_key"; + case SF_GEN_END_OPER: return "end_oper"; + } + + return wine_dbg_sprintf("%u", oper); +} + +enum +{ + /* sf_modulator is a set of flags ored together */ + + SF_MOD_CTRL_GEN_NONE = 0, + SF_MOD_CTRL_GEN_VELOCITY = 0x2, + SF_MOD_CTRL_GEN_KEY = 0x3, + SF_MOD_CTRL_GEN_POLY_PRESSURE = 0xa, + SF_MOD_CTRL_GEN_CHAN_PRESSURE = 0xd, + SF_MOD_CTRL_GEN_PITCH_WHEEL = 0xe, + SF_MOD_CTRL_GEN_PITCH_WHEEL_SENSITIVITY = 0x10, + SF_MOD_CTRL_GEN_LINK = 0x7f, + + SF_MOD_CTRL_GEN = 0 << 7, + SF_MOD_CTRL_MIDI = 1 << 7, /* with LSB: MIDI CC */ + + SF_MOD_DIR_INCREASING = 0 << 8, + SF_MOD_DIR_DECREASING = 1 << 8, + + SF_MOD_POL_UNIPOLAR = 0 << 9, + SF_MOD_POL_BIPOLAR = 1 << 9, + + SF_MOD_SRC_LINEAR = 0 << 10, + SF_MOD_SRC_CONCAVE = 1 << 10, + SF_MOD_SRC_CONVEX = 2 << 10, + SF_MOD_SRC_SWITCH = 3 << 10, +}; +typedef WORD sf_modulator; + +enum +{ + SF_TRAN_LINEAR = 0, + SF_TRAN_ABSOLUTE = 2, +}; +typedef WORD sf_transform; + +struct sf_preset /* */ +{ + char name[20]; + WORD preset; + WORD bank; + WORD bag_ndx; + DWORD library; + DWORD genre; + DWORD morphology; +}; + +C_ASSERT(sizeof(struct sf_preset) == 38); + +struct sf_bag /* / */ +{ + WORD gen_ndx; + WORD mod_ndx; +}; + +C_ASSERT(sizeof(struct sf_bag) == 4); + +struct sf_mod /* / */ +{ + sf_modulator src_mod; + sf_generator dest_gen; + SHORT amount; + sf_modulator amount_src_mod; + sf_transform transform; +}; + +C_ASSERT(sizeof(struct sf_mod) == 10); + +static inline const char *debugstr_sf_mod(struct sf_mod *mod) +{ + const char *dest_name = debugstr_sf_generator(mod->dest_gen); + return wine_dbg_sprintf("%#x x %#x -> %s: %d (%#x)", mod->src_mod, mod->amount_src_mod, dest_name, mod->amount, mod->transform); +} + +struct sf_gen /* / */ +{ + sf_generator oper; + union sf_amount amount; +}; + +C_ASSERT(sizeof(struct sf_gen) == 4); + +static inline const char *debugstr_sf_gen(struct sf_gen *gen) +{ + const char *name = debugstr_sf_generator(gen->oper); + + switch (gen->oper) + { + case SF_GEN_KEY_RANGE: + case SF_GEN_VEL_RANGE: + return wine_dbg_sprintf("%s: %u-%u", name, gen->amount.range.low, gen->amount.range.high); + default: + return wine_dbg_sprintf("%s: %u", name, gen->amount.value); + } +} + +struct sf_instrument /* */ +{ + char name[20]; + WORD bag_ndx; +}; + +C_ASSERT(sizeof(struct sf_instrument) == 22); + +struct sf_sample /* */ +{ + char name[20]; + DWORD start; + DWORD end; + DWORD start_loop; + DWORD end_loop; + DWORD sample_rate; + BYTE original_key; + char correction; + WORD sample_link; + sf_sample_type sample_type; +}; + +C_ASSERT(sizeof(struct sf_sample) == 46); + +#include + +struct soundfont +{ + UINT preset_count; + struct sf_preset *phdr; + struct sf_bag *pbag; + struct sf_mod *pmod; + struct sf_gen *pgen; + + UINT instrument_count; + struct sf_instrument *inst; + struct sf_bag *ibag; + struct sf_mod *imod; + struct sf_gen *igen; + + UINT sample_count; + struct sf_sample *shdr; + BYTE *sdta; +}; diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 0e0ff7e609a..cf0faaa5b87 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1608,7 +1608,7 @@ static void test_default_gm_collection(void) ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicCollection, (void **)&collection); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); for (i = 0; hr == S_OK && i < ARRAY_SIZE(results); i++) { @@ -1618,7 +1618,7 @@ static void test_default_gm_collection(void) results[i].name, ARRAY_SIZE(results[i].name)); } if (hr == S_FALSE) i--; - todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + ok(hr == S_FALSE, "got %#lx\n", hr); todo_wine ok(i > 0, "got %lu\n", i); todo_wine ok(i == ARRAY_SIZE(expected), "got %lu\n", i); @@ -1633,7 +1633,7 @@ static void test_default_gm_collection(void) winetest_pop_context(); } - if (hr == S_FALSE) IDirectMusicCollection_Release(collection); + IDirectMusicCollection_Release(collection); IDirectMusicLoader_Release(loader); } From 6b0b3f71054d8f5d6bffec0c4df2918d71865db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 09:05:55 +0200 Subject: [PATCH 0955/1148] dmusic: Implement SoundFont2 wave sample parsing. (cherry picked from commit 215a55d603bdaf501aae1df1db328131a314b443) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 23 +++++++++++ dlls/dmusic/dmusic_private.h | 2 + dlls/dmusic/wave.c | 80 ++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index feda1cbf8a9..a7d9d666e6c 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -580,6 +580,29 @@ static HRESULT parse_sfbk_chunk(struct collection *This, IStream *stream, struct } } + if (SUCCEEDED(hr)) + { + UINT size = offsetof(struct pool, cues[soundfont.sample_count]); + if (!(This->pool = calloc(1, size))) return E_OUTOFMEMORY; + This->pool->table.cbSize = sizeof(This->pool->table); + } + + for (i = 0; SUCCEEDED(hr) && i < soundfont.sample_count; i++) + { + struct wave_entry *entry; + + if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY; + hr = wave_create_from_soundfont(&soundfont, i, &entry->wave); + if (FAILED(hr)) free(entry); + else + { + entry->offset = i; + This->pool->table.cCues++; + This->pool->cues[i].ulOffset = i; + list_add_tail(&This->waves, &entry->entry); + } + } + free(soundfont.phdr); free(soundfont.pbag); free(soundfont.pmod); diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 08d84ff83e1..d40de3da909 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -94,12 +94,14 @@ extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface); +struct soundfont; extern HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, struct collection *collection, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface); extern HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicPortDownload *port, IDirectMusicDownloadedInstrument **downloaded); extern HRESULT instrument_unload_from_port(IDirectMusicDownloadedInstrument *iface, IDirectMusicPortDownload *port); +extern HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnknown **out); extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **out); extern HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id); diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index 6637612596a..40a8c9e129f 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -17,6 +17,7 @@ */ #include "dmusic_private.h" +#include "soundfont.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); @@ -216,6 +217,85 @@ HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnk return S_OK; } +HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnknown **ret_iface) +{ + struct sf_sample *sf_sample = soundfont->shdr + index; + struct sample *sample = NULL; + WAVEFORMATEX *format = NULL; + HRESULT hr = E_OUTOFMEMORY; + UINT data_size, offset; + struct wave *This; + void *data = NULL; + IUnknown *iface; + + TRACE("(%p, %u, %p)\n", soundfont, index, ret_iface); + + if (sf_sample->sample_link) FIXME("Stereo sample not supported\n"); + + if (!(format = calloc(1, sizeof(*format)))) goto failed; + format->wFormatTag = WAVE_FORMAT_PCM; + format->nChannels = 1; + format->wBitsPerSample = 16; + format->nSamplesPerSec = sf_sample->sample_rate; + format->nBlockAlign = format->wBitsPerSample * format->nChannels / 8; + format->nAvgBytesPerSec = format->nBlockAlign * format->nSamplesPerSec; + + if (!(sample = calloc(1, offsetof(struct sample, loops[1])))) goto failed; + sample->head.cbSize = sizeof(sample->head); + sample->head.cSampleLoops = 1; + sample->loops[0].ulStart = sf_sample->start_loop - sf_sample->start; + sample->loops[0].ulLength = sf_sample->end_loop - sf_sample->start_loop; + + data_size = sf_sample->end - sf_sample->start; + if (!(data = malloc(data_size * format->nBlockAlign))) goto failed; + offset = sf_sample->start * format->nBlockAlign / format->nChannels; + memcpy(data, soundfont->sdta + offset, data_size); + + if (FAILED(hr = wave_create(&iface))) goto failed; + + This = impl_from_IUnknown(iface); + This->format = format; + This->sample = sample; + This->data_size = data_size; + This->data = data; + + if (TRACE_ON(dmusic)) + { + UINT i; + + TRACE("*** Created DirectMusicWave %p\n", This); + TRACE(" - format: %p\n", This->format); + if (This->format) + { + TRACE(" - wFormatTag: %u\n", This->format->wFormatTag); + TRACE(" - nChannels: %u\n", This->format->nChannels); + TRACE(" - nSamplesPerSec: %lu\n", This->format->nSamplesPerSec); + TRACE(" - nAvgBytesPerSec: %lu\n", This->format->nAvgBytesPerSec); + TRACE(" - nBlockAlign: %u\n", This->format->nBlockAlign); + TRACE(" - wBitsPerSample: %u\n", This->format->wBitsPerSample); + TRACE(" - cbSize: %u\n", This->format->cbSize); + } + + TRACE(" - sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n", + This->sample->head.cbSize, This->sample->head.usUnityNote, + This->sample->head.sFineTune, This->sample->head.lAttenuation, + This->sample->head.fulOptions, This->sample->head.cSampleLoops); + for (i = 0; i < This->sample->head.cSampleLoops; i++) + TRACE(" - loops[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i, + This->sample->loops[i].cbSize, This->sample->loops[i].ulType, + This->sample->loops[i].ulStart, This->sample->loops[i].ulLength); + } + + *ret_iface = iface; + return S_OK; + +failed: + free(data); + free(sample); + free(format); + return hr; +} + HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id) { struct download_buffer From 5fcafc29fc7a775379763c9f9024b2f2b0985da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 09:13:43 +0200 Subject: [PATCH 0956/1148] dmusic: Implement SoundFont2 instrument parsing. (cherry picked from commit 086e114f05e89a9b670b9ef738b017f3d65004d8) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/collection.c | 11 ++ dlls/dmusic/dmusic_private.h | 2 + dlls/dmusic/instrument.c | 239 +++++++++++++++++++++++++++++++++++ dlls/dmusic/tests/dmusic.c | 3 +- 4 files changed, 254 insertions(+), 1 deletion(-) diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index a7d9d666e6c..bf21d3148e3 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -580,6 +580,17 @@ static HRESULT parse_sfbk_chunk(struct collection *This, IStream *stream, struct } } + for (i = 0; SUCCEEDED(hr) && i < soundfont.preset_count; i++) + { + struct instrument_entry *entry; + + if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY; + hr = instrument_create_from_soundfont(&soundfont, i, This, &entry->desc, &entry->instrument); + if (SUCCEEDED(hr)) hr = IDirectMusicInstrument_GetPatch(entry->instrument, &entry->patch); + if (SUCCEEDED(hr)) list_add_tail(&This->instruments, &entry->entry); + else free(entry); + } + if (SUCCEEDED(hr)) { UINT size = offsetof(struct pool, cues[soundfont.sample_count]); diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index d40de3da909..f67bc5c4b2f 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -95,6 +95,8 @@ extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface); struct soundfont; +extern HRESULT instrument_create_from_soundfont(struct soundfont *soundfont, UINT index, + struct collection *collection, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface); extern HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, struct collection *collection, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface); extern HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicPortDownload *port, diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 54ce7508ee4..7a0f8a2f1eb 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -19,11 +19,21 @@ */ #include "dmusic_private.h" +#include "soundfont.h" +#include "dls2.h" WINE_DEFAULT_DEBUG_CHANNEL(dmusic); static const GUID IID_IDirectMusicInstrumentPRIVATE = { 0xbcb20080, 0xa40c, 0x11d1, { 0x86, 0xbc, 0x00, 0xc0, 0x4f, 0xbf, 0x8f, 0xef } }; +#define CONN_SRC_CC2 0x0082 +#define CONN_SRC_RPN0 0x0100 + +#define CONN_TRN_BIPOLAR (1<<4) +#define CONN_TRN_INVERT (1<<5) + +#define CONN_TRANSFORM(src, ctrl, dst) (((src) & 0x3f) << 10) | (((ctrl) & 0x3f) << 4) | ((dst) & 0xf) + struct articulation { struct list entry; @@ -458,6 +468,235 @@ HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent return S_OK; } +struct sf_generators +{ + union sf_amount amount[SF_GEN_END_OPER]; +}; + +static const struct sf_generators SF_DEFAULT_GENERATORS = +{ + .amount = + { + [SF_GEN_INITIAL_FILTER_FC] = {.value = 13500}, + [SF_GEN_DELAY_MOD_LFO] = {.value = -12000}, + [SF_GEN_DELAY_VIB_LFO] = {.value = -12000}, + [SF_GEN_DELAY_MOD_ENV] = {.value = -12000}, + [SF_GEN_ATTACK_MOD_ENV] = {.value = -12000}, + [SF_GEN_HOLD_MOD_ENV] = {.value = -12000}, + [SF_GEN_DECAY_MOD_ENV] = {.value = -12000}, + [SF_GEN_RELEASE_MOD_ENV] = {.value = -12000}, + [SF_GEN_DELAY_VOL_ENV] = {.value = -12000}, + [SF_GEN_ATTACK_VOL_ENV] = {.value = -12000}, + [SF_GEN_HOLD_VOL_ENV] = {.value = -12000}, + [SF_GEN_DECAY_VOL_ENV] = {.value = -12000}, + [SF_GEN_RELEASE_VOL_ENV] = {.value = -12000}, + [SF_GEN_KEY_RANGE] = {.range = {.low = 0, .high = 127}}, + [SF_GEN_VEL_RANGE] = {.range = {.low = 0, .high = 127}}, + [SF_GEN_KEYNUM] = {.value = -1}, + [SF_GEN_VELOCITY] = {.value = -1}, + [SF_GEN_SCALE_TUNING] = {.value = 100}, + [SF_GEN_OVERRIDING_ROOT_KEY] = {.value = -1}, + } +}; + +static BOOL parse_soundfont_generators(struct soundfont *soundfont, UINT index, + struct sf_generators *preset_generators, struct sf_generators *generators) +{ + struct sf_bag *bag = (preset_generators ? soundfont->ibag : soundfont->pbag) + index; + struct sf_gen *gen, *gens = preset_generators ? soundfont->igen : soundfont->pgen; + + for (gen = gens + bag->gen_ndx; gen < gens + (bag + 1)->gen_ndx; gen++) + { + switch (gen->oper) + { + case SF_GEN_START_ADDRS_OFFSET: + case SF_GEN_END_ADDRS_OFFSET: + case SF_GEN_STARTLOOP_ADDRS_OFFSET: + case SF_GEN_ENDLOOP_ADDRS_OFFSET: + case SF_GEN_START_ADDRS_COARSE_OFFSET: + case SF_GEN_END_ADDRS_COARSE_OFFSET: + case SF_GEN_STARTLOOP_ADDRS_COARSE_OFFSET: + case SF_GEN_KEYNUM: + case SF_GEN_VELOCITY: + case SF_GEN_ENDLOOP_ADDRS_COARSE_OFFSET: + case SF_GEN_SAMPLE_MODES: + case SF_GEN_EXCLUSIVE_CLASS: + case SF_GEN_OVERRIDING_ROOT_KEY: + if (preset_generators) generators->amount[gen->oper] = gen->amount; + else WARN("Ignoring invalid preset generator %s\n", debugstr_sf_gen(gen)); + break; + + case SF_GEN_INSTRUMENT: + if (!preset_generators) generators->amount[gen->oper] = gen->amount; + else WARN("Ignoring invalid instrument generator %s\n", debugstr_sf_gen(gen)); + /* should always be the last generator */ + return FALSE; + + case SF_GEN_SAMPLE_ID: + if (preset_generators) generators->amount[gen->oper] = gen->amount; + else WARN("Ignoring invalid preset generator %s\n", debugstr_sf_gen(gen)); + /* should always be the last generator */ + return FALSE; + + default: + generators->amount[gen->oper] = gen->amount; + if (preset_generators) generators->amount[gen->oper].value += preset_generators->amount[gen->oper].value; + break; + } + } + + return TRUE; +} + +static HRESULT instrument_add_soundfont_region(struct instrument *This, struct soundfont *soundfont, + struct sf_generators *generators) +{ + UINT start_loop, end_loop, unity_note, sample_index = generators->amount[SF_GEN_SAMPLE_ID].value; + struct sf_sample *sample = soundfont->shdr + sample_index; + struct region *region; + + if (!(region = calloc(1, sizeof(*region)))) return E_OUTOFMEMORY; + list_init(®ion->articulations); + + region->header.RangeKey.usLow = generators->amount[SF_GEN_KEY_RANGE].range.low; + region->header.RangeKey.usHigh = generators->amount[SF_GEN_KEY_RANGE].range.high; + region->header.RangeVelocity.usLow = generators->amount[SF_GEN_VEL_RANGE].range.low; + region->header.RangeVelocity.usHigh = generators->amount[SF_GEN_VEL_RANGE].range.high; + + region->wave_link.ulTableIndex = sample_index; + + unity_note = generators->amount[SF_GEN_OVERRIDING_ROOT_KEY].value; + if (unity_note == -1) unity_note = sample->original_key; + region->wave_sample.usUnityNote = unity_note; + region->wave_sample.sFineTune = generators->amount[SF_GEN_FINE_TUNE].value; + region->wave_sample.lAttenuation = sample->correction; + + start_loop = generators->amount[SF_GEN_STARTLOOP_ADDRS_OFFSET].value; + end_loop = generators->amount[SF_GEN_ENDLOOP_ADDRS_OFFSET].value; + if (start_loop || end_loop) + { + region->loop_present = TRUE; + region->wave_sample.cSampleLoops = 1; + region->wave_loop.ulStart = start_loop; + region->wave_loop.ulLength = end_loop - start_loop; + } + + list_add_tail(&This->regions, ®ion->entry); + This->header.cRegions++; + return S_OK; +} + +static HRESULT instrument_add_soundfont_instrument(struct instrument *This, struct soundfont *soundfont, + UINT index, struct sf_generators *preset_generators) +{ + struct sf_generators global_generators = SF_DEFAULT_GENERATORS; + struct sf_instrument *instrument = soundfont->inst + index; + UINT i = instrument->bag_ndx; + HRESULT hr = S_OK; + + for (i = instrument->bag_ndx; SUCCEEDED(hr) && i < (instrument + 1)->bag_ndx; i++) + { + struct sf_generators generators = global_generators; + + if (parse_soundfont_generators(soundfont, i, preset_generators, &generators)) + { + if (i > instrument->bag_ndx) + WARN("Ignoring instrument zone without a sample id\n"); + else + global_generators = generators; + continue; + } + + hr = instrument_add_soundfont_region(This, soundfont, &generators); + } + + return hr; +} + +HRESULT instrument_create_from_soundfont(struct soundfont *soundfont, UINT index, + struct collection *collection, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface) +{ + struct sf_preset *preset = soundfont->phdr + index; + struct sf_generators global_generators = {0}; + IDirectMusicInstrument *iface; + struct instrument *This; + HRESULT hr; + UINT i; + + TRACE("(%p, %u, %p, %p, %p)\n", soundfont, index, collection, desc, ret_iface); + + if (FAILED(hr = instrument_create(collection, &iface))) return hr; + This = impl_from_IDirectMusicInstrument(iface); + + This->header.Locale.ulBank = (preset->bank & 0x7f) | ((preset->bank << 1) & 0x7f00); + This->header.Locale.ulInstrument = preset->preset; + MultiByteToWideChar(CP_ACP, 0, preset->name, strlen(preset->name) + 1, + desc->wszName, sizeof(desc->wszName)); + + for (i = preset->bag_ndx; SUCCEEDED(hr) && i < (preset + 1)->bag_ndx; i++) + { + struct sf_generators generators = global_generators; + UINT instrument; + + if (parse_soundfont_generators(soundfont, i, NULL, &generators)) + { + if (i > preset->bag_ndx) + WARN("Ignoring preset zone without an instrument\n"); + else + global_generators = generators; + continue; + } + + instrument = generators.amount[SF_GEN_INSTRUMENT].value; + hr = instrument_add_soundfont_instrument(This, soundfont, instrument, &generators); + } + + if (FAILED(hr)) + { + IDirectMusicInstrument_Release(iface); + return hr; + } + + if (TRACE_ON(dmusic)) + { + struct region *region; + UINT i; + + TRACE("Created DirectMusicInstrument %p\n", This); + TRACE(" - header:\n"); + TRACE(" - regions: %ld\n", This->header.cRegions); + TRACE(" - locale: {bank: %#lx, instrument: %#lx} (patch %#lx)\n", + This->header.Locale.ulBank, This->header.Locale.ulInstrument, + MIDILOCALE2Patch(&This->header.Locale)); + if (desc->dwValidData & DMUS_OBJ_OBJECT) TRACE(" - guid: %s\n", debugstr_dmguid(&desc->guidObject)); + if (desc->dwValidData & DMUS_OBJ_NAME) TRACE(" - name: %s\n", debugstr_w(desc->wszName)); + + TRACE(" - regions:\n"); + LIST_FOR_EACH_ENTRY(region, &This->regions, struct region, entry) + { + TRACE(" - region:\n"); + TRACE(" - header: {key: %u - %u, vel: %u - %u, options: %#x, group: %#x}\n", + region->header.RangeKey.usLow, region->header.RangeKey.usHigh, + region->header.RangeVelocity.usLow, region->header.RangeVelocity.usHigh, + region->header.fusOptions, region->header.usKeyGroup); + TRACE(" - wave_link: {options: %#x, group: %u, channel: %lu, index: %lu}\n", + region->wave_link.fusOptions, region->wave_link.usPhaseGroup, + region->wave_link.ulChannel, region->wave_link.ulTableIndex); + TRACE(" - wave_sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n", + region->wave_sample.cbSize, region->wave_sample.usUnityNote, + region->wave_sample.sFineTune, region->wave_sample.lAttenuation, + region->wave_sample.fulOptions, region->wave_sample.cSampleLoops); + for (i = 0; i < region->wave_sample.cSampleLoops; i++) + TRACE(" - wave_loop[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i, + region->wave_loop.cbSize, region->wave_loop.ulType, + region->wave_loop.ulStart, region->wave_loop.ulLength); + } + } + + *ret_iface = iface; + return S_OK; +} + static void write_articulation_download(struct list *articulations, ULONG *offsets, BYTE **ptr, UINT index, DWORD *first, UINT *end) { diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index cf0faaa5b87..51a6b0d8774 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1619,7 +1619,7 @@ static void test_default_gm_collection(void) } if (hr == S_FALSE) i--; ok(hr == S_FALSE, "got %#lx\n", hr); - todo_wine ok(i > 0, "got %lu\n", i); + ok(i > 0, "got %lu\n", i); todo_wine ok(i == ARRAY_SIZE(expected), "got %lu\n", i); qsort(results, i, sizeof(*results), result_cmp); @@ -1628,6 +1628,7 @@ static void test_default_gm_collection(void) { winetest_push_context("%lu", i); trace("got %#lx %s\n", results[i].patch, debugstr_w(results[i].name)); + todo_wine_if(expected[i].patch >= 128) ok(results[i].patch == expected[i].patch, "got %#lx\n", results[i].patch); /* system soundfont names are not very predictable, let's not check them */ winetest_pop_context(); From 50b95d0b792565e20634f39a083c6446d55a2210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 12:25:35 +0200 Subject: [PATCH 0957/1148] dmime/tests: Add some tests for GUID_ConnectToDLSCollection. (cherry picked from commit 0d3b83c021455654aa02ccd05ef56a8d3e63ef5f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 57879f52069..f69e42d2fec 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3627,6 +3627,51 @@ static void test_band_track_play(void) IDirectMusicTool_Release(tool); } +static void test_connect_to_collection(void) +{ + IDirectMusicCollection *collection; + IDirectMusicSegment *segment; + IDirectMusicTrack *track; + void *param; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_DirectMusicCollection, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicCollection, (void **)&collection); + ok(hr == S_OK, "got %#lx\n", hr); + hr = CoCreateInstance(&CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicSegment, (void **)&segment); + ok(hr == S_OK, "got %#lx\n", hr); + hr = CoCreateInstance(&CLSID_DirectMusicBandTrack, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicTrack, (void **)&track); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicSegment_InsertTrack(segment, track, 1); + ok(hr == S_OK, "got %#lx\n", hr); + + /* it is possible to connect the band track to another collection, but not to disconnect it */ + hr = IDirectMusicTrack_IsParamSupported(track, &GUID_ConnectToDLSCollection); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicTrack_SetParam(track, &GUID_ConnectToDLSCollection, 0, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicTrack_SetParam(track, &GUID_ConnectToDLSCollection, 0, collection); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicTrack_GetParam(track, &GUID_ConnectToDLSCollection, 0, NULL, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicTrack_GetParam(track, &GUID_ConnectToDLSCollection, 0, NULL, ¶m); + ok(hr == DMUS_E_GET_UNSUPPORTED, "got %#lx\n", hr); + + hr = IDirectMusicSegment_SetParam(segment, &GUID_ConnectToDLSCollection, -1, DMUS_SEG_ALLTRACKS, 0, NULL); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicSegment_SetParam(segment, &GUID_ConnectToDLSCollection, -1, DMUS_SEG_ALLTRACKS, 0, collection); + ok(hr == S_OK, "got %#lx\n", hr); + + IDirectMusicTrack_Release(track); + IDirectMusicSegment_Release(segment); + IDirectMusicCollection_Release(collection); +} + START_TEST(dmime) { CoInitialize(NULL); @@ -3662,6 +3707,7 @@ START_TEST(dmime) test_wave_pmsg(); test_sequence_track(); test_band_track_play(); + test_connect_to_collection(); CoUninitialize(); } From 6e1d37eb9764f1bf4907924eec2ce045ffd4292c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 29 Sep 2023 09:02:56 +0200 Subject: [PATCH 0958/1148] dmime/tests: Test segment state and playing a custom track. (cherry picked from commit 406f1783a926b0ff404e897650739a9fcaecb2df) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 443 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 443 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index f69e42d2fec..cfd58b8b229 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -555,6 +555,228 @@ static HRESULT test_loader_stream_create(IStream *stream, IDirectMusicLoader *lo return S_OK; } +struct test_track +{ + /* Implementing IDirectMusicTrack8 will cause native to call PlayEx */ + IDirectMusicTrack IDirectMusicTrack_iface; + LONG ref; + + DWORD data; + BOOL inserted; + BOOL initialized; + BOOL downloaded; + BOOL playing; + HANDLE playing_event; +}; + +#define check_track_state(track, state, value) \ + do \ + { \ + DWORD ret = impl_from_IDirectMusicTrack(track)->state; \ + ok(ret == (value), "got %#lx\n", ret); \ + } while (0); + +static inline struct test_track *impl_from_IDirectMusicTrack(IDirectMusicTrack *iface) +{ + return CONTAINING_RECORD(iface, struct test_track, IDirectMusicTrack_iface); +} + +static HRESULT WINAPI test_track_QueryInterface(IDirectMusicTrack *iface, REFIID riid, + void **ret_iface) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + + if (IsEqualIID(riid, &IID_IUnknown) + || IsEqualIID(riid, &IID_IDirectMusicTrack)) + { + *ret_iface = &This->IDirectMusicTrack_iface; + IDirectMusicTrack_AddRef(&This->IDirectMusicTrack_iface); + return S_OK; + } + + ok(IsEqualGUID(riid, &IID_IDirectMusicTrack8) || IsEqualGUID(riid, &IID_IPersistStream), + "unexpected %s %p %s\n", __func__, This, debugstr_guid(riid)); + *ret_iface = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_track_AddRef(IDirectMusicTrack *iface) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI test_track_Release(IDirectMusicTrack *iface) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + if (!ref) + { + CloseHandle(This->playing_event); + free(This); + } + + return ref; +} + +static HRESULT WINAPI test_track_Init(IDirectMusicTrack *iface, IDirectMusicSegment *segment) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + This->inserted = TRUE; + return S_OK; +} + +static HRESULT WINAPI test_track_InitPlay(IDirectMusicTrack *iface, IDirectMusicSegmentState *segment_state, + IDirectMusicPerformance *performance, void **state_data, DWORD track_id, DWORD segment_flags) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + + ok(!!segment_state, "got %p\n", segment_state); + ok(!!performance, "got %p\n", performance); + ok(!!state_data, "got %p\n", state_data); + ok(!!track_id, "got %lu\n", track_id); + ok(!segment_flags, "got %#lx\n", segment_flags); + This->initialized = TRUE; + + *state_data = &This->data; + return S_OK; +} + +static HRESULT WINAPI test_track_EndPlay(IDirectMusicTrack *iface, void *state_data) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + + ok(state_data == &This->data, "got %p\n", state_data); + This->playing = FALSE; + + return S_OK; +} + +static HRESULT WINAPI test_track_Play(IDirectMusicTrack *iface, void *state_data, + MUSIC_TIME start_time, MUSIC_TIME end_time, MUSIC_TIME time_offset, DWORD segment_flags, + IDirectMusicPerformance *performance, IDirectMusicSegmentState *segment_state, DWORD track_id) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + + ok(state_data == &This->data, "got %p\n", state_data); + ok(start_time == 50, "got %lu\n", start_time); + ok(end_time == 100, "got %lu\n", end_time); + ok(time_offset < 0, "got %lu\n", time_offset); + ok(segment_flags == (DMUS_TRACKF_DIRTY|DMUS_TRACKF_START|DMUS_TRACKF_SEEK), + "got %#lx\n", segment_flags); + ok(!!performance, "got %p\n", performance); + ok(!!segment_state, "got %p\n", segment_state); + ok(!!track_id, "got %lu\n", track_id); + This->playing = TRUE; + SetEvent(This->playing_event); + + return S_OK; +} + +static HRESULT WINAPI test_track_GetParam(IDirectMusicTrack *iface, REFGUID type, MUSIC_TIME time, + MUSIC_TIME *next, void *param) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + ok(0, "unexpected %s %p\n", __func__, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_track_SetParam(IDirectMusicTrack *iface, REFGUID type, MUSIC_TIME time, void *param) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + + if (IsEqualGUID(type, &GUID_DownloadToAudioPath)) + { + This->downloaded = TRUE; + return S_OK; + } + + if (IsEqualGUID(type, &GUID_UnloadFromAudioPath)) + { + This->downloaded = FALSE; + return S_OK; + } + + ok(0, "unexpected %s %p %s %lu %p\n", __func__, This, debugstr_guid(type), time, param); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_track_IsParamSupported(IDirectMusicTrack *iface, REFGUID type) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + + if (IsEqualGUID(type, &GUID_DownloadToAudioPath)) return S_OK; + if (IsEqualGUID(type, &GUID_UnloadFromAudioPath)) return S_OK; + if (IsEqualGUID(type, &GUID_TimeSignature)) return DMUS_E_TYPE_UNSUPPORTED; + if (IsEqualGUID(type, &GUID_TempoParam)) return DMUS_E_TYPE_UNSUPPORTED; + + ok(broken(type->Data1 == 0xe8dbd832), /* native also checks some unknown parameter */ + "unexpected %s %p %s\n", __func__, This, debugstr_guid(type)); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_track_AddNotificationType(IDirectMusicTrack *iface, REFGUID type) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + ok(0, "unexpected %s %p\n", __func__, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_track_RemoveNotificationType(IDirectMusicTrack *iface, REFGUID type) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + ok(0, "unexpected %s %p\n", __func__, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_track_Clone(IDirectMusicTrack *iface, MUSIC_TIME start_time, + MUSIC_TIME end_time, IDirectMusicTrack **ret_track) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + ok(0, "unexpected %s %p\n", __func__, This); + return E_NOTIMPL; +} + +static const IDirectMusicTrackVtbl test_track_vtbl = +{ + test_track_QueryInterface, + test_track_AddRef, + test_track_Release, + test_track_Init, + test_track_InitPlay, + test_track_EndPlay, + test_track_Play, + test_track_GetParam, + test_track_SetParam, + test_track_IsParamSupported, + test_track_AddNotificationType, + test_track_RemoveNotificationType, + test_track_Clone, +}; + +static HRESULT test_track_create(IDirectMusicTrack **ret_iface) +{ + struct test_track *track; + + *ret_iface = NULL; + if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; + track->IDirectMusicTrack_iface.lpVtbl = &test_track_vtbl; + track->ref = 1; + + track->playing_event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(!!track->playing_event, "CreateEventW failed, error %lu\n", GetLastError()); + + *ret_iface = &track->IDirectMusicTrack_iface; + return S_OK; +} + +static DWORD test_track_wait_playing(IDirectMusicTrack *iface, DWORD timeout) +{ + struct test_track *This = impl_from_IDirectMusicTrack(iface); + return WaitForSingleObject(This->playing_event, timeout); +} + static void create_performance(IDirectMusicPerformance8 **performance, IDirectMusic **dmusic, IDirectSound **dsound, BOOL set_cooplevel) { @@ -3672,6 +3894,226 @@ static void test_connect_to_collection(void) IDirectMusicCollection_Release(collection); } +static void test_segment_state(void) +{ + static const DWORD message_types[] = + { + DMUS_PMSGT_DIRTY, + DMUS_PMSGT_NOTIFICATION, + DMUS_PMSGT_WAVE, + }; + IDirectMusicSegmentState *state, *tmp_state; + IDirectMusicSegment *segment, *tmp_segment; + IDirectMusicPerformance *performance; + IDirectMusicTrack *track; + IDirectMusicGraph *graph; + IDirectMusicTool *tool; + DWORD value, ret; + MUSIC_TIME time; + HRESULT hr; + + hr = test_tool_create(message_types, ARRAY_SIZE(message_types), &tool); + ok(hr == S_OK, "got %#lx\n", hr); + hr = test_track_create(&track); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void **)&performance); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicGraph, (void **)&graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SetGraph(performance, graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_InsertTool(graph, (IDirectMusicTool *)tool, NULL, 0, -1); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicGraph_Release(graph); + + + hr = CoCreateInstance(&CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicSegment, (void **)&segment); + ok(hr == S_OK, "got %#lx\n", hr); + + check_track_state(track, inserted, FALSE); + hr = IDirectMusicSegment_InsertTrack(segment, track, 1); + ok(hr == S_OK, "got %#lx\n", hr); + check_track_state(track, inserted, TRUE); + + check_track_state(track, downloaded, FALSE); + hr = IDirectMusicSegment8_Download((IDirectMusicSegment8 *)segment, (IUnknown *)performance); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine check_track_state(track, downloaded, TRUE); + hr = IDirectMusicSegment8_Unload((IDirectMusicSegment8 *)segment, (IUnknown *)performance); + ok(hr == S_OK, "got %#lx\n", hr); + check_track_state(track, downloaded, FALSE); + + + /* by default the segment length is 1 */ + + time = 0xdeadbeef; + hr = IDirectMusicSegment_GetLength(segment, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time == 1, "got %lu\n", time); + hr = IDirectMusicSegment_SetStartPoint(segment, 50); + ok(hr == DMUS_E_OUT_OF_RANGE, "got %#lx\n", hr); + hr = IDirectMusicSegment_SetRepeats(segment, 10); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSegment_SetLoopPoints(segment, 10, 70); + ok(hr == DMUS_E_OUT_OF_RANGE, "got %#lx\n", hr); + + /* Setting a larger length will cause PlayEx to be called multiple times, + * as native splits the segment into chunks and play each chunk separately */ + hr = IDirectMusicSegment_SetLength(segment, 100); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSegment_SetStartPoint(segment, 50); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSegment_SetRepeats(segment, 0); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSegment_SetLoopPoints(segment, 0, 0); + ok(hr == S_OK, "got %#lx\n", hr); + + + /* InitPlay returns a dummy segment state */ + + state = (void *)0xdeadbeef; + hr = IDirectMusicSegment_InitPlay(segment, &state, performance, 0); + ok(hr == S_OK, "got %#lx\n", hr); + ok(state != NULL, "got %p\n", state); + ok(state != (void *)0xdeadbeef, "got %p\n", state); + check_track_state(track, initialized, FALSE); + + tmp_segment = (void *)0xdeadbeef; + hr = IDirectMusicSegmentState_GetSegment(state, &tmp_segment); + todo_wine ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + todo_wine ok(tmp_segment == NULL, "got %p\n", tmp_segment); + time = 0xdeadbeef; + hr = IDirectMusicSegmentState_GetStartPoint(state, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time == 0, "got %#lx\n", time); + time = 0xdeadbeef; + hr = IDirectMusicSegmentState_GetRepeats(state, &value); + ok(hr == S_OK, "got %#lx\n", hr); + ok(time == 0xdeadbeef, "got %#lx\n", time); + time = 0xdeadbeef; + hr = IDirectMusicSegmentState_GetStartTime(state, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time == -1, "got %#lx\n", time); + time = 0xdeadbeef; + hr = IDirectMusicSegmentState_GetSeek(state, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time == 0, "got %#lx\n", time); + + + /* PlaySegment returns a different, genuine segment state */ + + hr = IDirectMusicPerformance_Init(performance, NULL, 0, 0); + ok(hr == S_OK, "got %#lx\n", hr); + + check_track_state(track, downloaded, FALSE); + check_track_state(track, initialized, FALSE); + check_track_state(track, playing, FALSE); + + tmp_state = state; + state = (void *)0xdeadbeef; + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 20, &state); + ok(hr == S_OK, "got %#lx\n", hr); + ok(state != NULL, "got %p\n", state); + ok(state != (void *)0xdeadbeef, "got %p\n", state); + ok(state != tmp_state, "got %p\n", state); + IDirectMusicSegmentState_Release(tmp_state); + + check_track_state(track, downloaded, FALSE); + todo_wine check_track_state(track, initialized, TRUE); + + + /* The track can be removed from the segment */ + hr = IDirectMusicSegment_RemoveTrack(segment, track); + ok(hr == S_OK, "got %#lx\n", hr); + + + /* This might be timing dependent and if PlaySegment is already + * late, the tracks are played synchronously and right away. + */ + check_track_state(track, playing, FALSE); + + ret = test_track_wait_playing(track, 50); + todo_wine ok(ret == 0, "got %#lx\n", ret); + + + tmp_segment = (void *)0xdeadbeef; + hr = IDirectMusicSegmentState_GetSegment(state, &tmp_segment); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(tmp_segment == segment, "got %p\n", tmp_segment); + if (tmp_segment != (void *)0xdeadbeef) IDirectMusicSegment_Release(tmp_segment); + + time = 0xdeadbeef; + hr = IDirectMusicSegmentState_GetStartPoint(state, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time == 50, "got %lu\n", time); + time = 0xdeadbeef; + hr = IDirectMusicSegmentState_GetRepeats(state, &value); + ok(hr == S_OK, "got %#lx\n", hr); + ok(time == 0xdeadbeef, "got %#lx\n", time); + time = 0xdeadbeef; + hr = IDirectMusicSegmentState_GetStartTime(state, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time == 20, "got %#lx\n", time); + time = 0xdeadbeef; + + /* The seek value is also dependent on whether the tracks are playing. + * It is initially 0, then start_point right before playing, then length. + */ + hr = IDirectMusicSegmentState_GetSeek(state, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time == 100, "got %#lx\n", time); + + /* changing the segment values doesn't change the segment state */ + + hr = IDirectMusicSegment_SetStartPoint(segment, 0); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSegment_SetRepeats(segment, 10); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSegment_SetLoopPoints(segment, 50, 70); + ok(hr == S_OK, "got %#lx\n", hr); + + time = 0xdeadbeef; + hr = IDirectMusicSegmentState_GetStartPoint(state, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time == 50, "got %#lx\n", time); + time = 0xdeadbeef; + hr = IDirectMusicSegmentState_GetRepeats(state, &value); + ok(hr == S_OK, "got %#lx\n", hr); + ok(time == 0xdeadbeef, "got %#lx\n", time); + time = 0xdeadbeef; + hr = IDirectMusicSegmentState_GetStartTime(state, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time == 20, "got %#lx\n", time); + time = 0xdeadbeef; + hr = IDirectMusicSegmentState_GetSeek(state, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(time == 100, "got %#lx\n", time); + + IDirectMusicSegment_Release(segment); + + + check_track_state(track, downloaded, FALSE); + todo_wine check_track_state(track, initialized, TRUE); + todo_wine check_track_state(track, playing, TRUE); + + hr = IDirectMusicPerformance_CloseDown(performance); + ok(hr == S_OK, "got %#lx\n", hr); + + check_track_state(track, downloaded, FALSE); + todo_wine check_track_state(track, initialized, TRUE); + check_track_state(track, playing, FALSE); + + + IDirectMusicPerformance_Release(performance); + IDirectMusicTrack_Release(track); + IDirectMusicTool_Release(tool); +} + START_TEST(dmime) { CoInitialize(NULL); @@ -3708,6 +4150,7 @@ START_TEST(dmime) test_sequence_track(); test_band_track_play(); test_connect_to_collection(); + test_segment_state(); CoUninitialize(); } From 4c7e2a4e1edabf6de548cb298c3a4c36b8a221fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 6 Sep 2023 09:54:12 +0200 Subject: [PATCH 0959/1148] dmime: Implement IDirectMusicSegment_SetParam. (cherry picked from commit 8645d9eb4e97c7347405a51518b6a31b0ac7deef) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index a72ec786623..bc52931f667 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -392,12 +392,28 @@ static HRESULT WINAPI segment_GetParam(IDirectMusicSegment8 *iface, REFGUID type return hr; } -static HRESULT WINAPI segment_SetParam(IDirectMusicSegment8 *iface, REFGUID rguidType, - DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, void *pParam) +static HRESULT WINAPI segment_SetParam(IDirectMusicSegment8 *iface, REFGUID type, + DWORD group, DWORD index, MUSIC_TIME music_time, void *param) { struct segment *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %s, %#lx, %ld, %ld, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, - dwIndex, mtTime, pParam); + struct track_entry *entry; + HRESULT hr; + + TRACE("(%p, %s, %#lx, %ld, %ld, %p)\n", This, debugstr_dmguid(type), group, + index, music_time, param); + + LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) + { + if (group != -1 && !(group & entry->dwGroupBits)) continue; + if (index != DMUS_SEG_ALLTRACKS && index--) continue; + + hr = IDirectMusicTrack_IsParamSupported(entry->pTrack, type); + if (hr == DMUS_E_TYPE_UNSUPPORTED) continue; + + hr = IDirectMusicTrack_SetParam(entry->pTrack, type, music_time, param); + if (FAILED(hr)) break; + } + return S_OK; } From b8a957c0670d9a1bd44ae3bcab4e865c69247852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 12:08:42 +0200 Subject: [PATCH 0960/1148] dmband: Implement band track GUID_ConnectToDLSCollection parameter. (cherry picked from commit 92985253e757202cc24b2ed933e099f44e936c93) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 26 ++++++++++++++++++++++ dlls/dmband/bandtrack.c | 7 +++++- dlls/dmband/dmband_private.h | 2 ++ dlls/dmusic/dmobject.c | 43 +++++++++++++++++++++++++----------- dlls/dmusic/dmobject.h | 3 +++ 5 files changed, 67 insertions(+), 14 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index d91775681bb..361df5f8986 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -59,6 +59,7 @@ struct band struct dmobject dmobj; LONG ref; struct list instruments; + IDirectMusicCollection *collection; }; static inline struct band *impl_from_IDirectMusicBand(IDirectMusicBand *iface) @@ -117,6 +118,7 @@ static ULONG WINAPI band_Release(IDirectMusicBand *iface) instrument_entry_destroy(entry); } + if (This->collection) IDirectMusicCollection_Release(This->collection); free(This); } @@ -336,11 +338,23 @@ static inline struct band *impl_from_IPersistStream(IPersistStream *iface) static HRESULT WINAPI band_persist_stream_Load(IPersistStream *iface, IStream *stream) { struct band *This = impl_from_IPersistStream(iface); + DMUS_OBJECTDESC default_desc = + { + .dwSize = sizeof(DMUS_OBJECTDESC), + .dwValidData = DMUS_OBJ_OBJECT | DMUS_OBJ_CLASS, + .guidClass = CLSID_DirectMusicCollection, + .guidObject = GUID_DefaultGMCollection, + }; struct chunk_entry chunk = {0}; HRESULT hr; TRACE("%p, %p\n", iface, stream); + if (This->collection) IDirectMusicCollection_Release(This->collection); + if (FAILED(hr = stream_get_object(stream, &default_desc, &IID_IDirectMusicCollection, + (void **)&This->collection))) + WARN("Failed to load default collection from loader, hr %#lx\n", hr); + if ((hr = stream_get_chunk(stream, &chunk)) == S_OK) { switch (MAKE_IDTYPE(chunk.id, chunk.type)) @@ -408,3 +422,15 @@ HRESULT create_dmband(REFIID lpcGUID, void **ppobj) return hr; } + +HRESULT band_connect_to_collection(IDirectMusicBand *iface, IDirectMusicCollection *collection) +{ + struct band *This = impl_from_IDirectMusicBand(iface); + + TRACE("%p, %p\n", iface, collection); + + if (This->collection) IDirectMusicCollection_Release(This->collection); + if ((This->collection = collection)) IDirectMusicCollection_AddRef(This->collection); + + return S_OK; +} diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index ab12e1f1355..45dc7ace86a 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -182,7 +182,12 @@ static HRESULT WINAPI band_track_SetParam(IDirectMusicTrack8 *iface, REFGUID typ else if (IsEqualGUID(type, &GUID_Clear_All_Bands)) FIXME("GUID_Clear_All_Bands not handled yet\n"); else if (IsEqualGUID(type, &GUID_ConnectToDLSCollection)) - FIXME("GUID_ConnectToDLSCollection not handled yet\n"); + { + struct band_entry *entry; + + LIST_FOR_EACH_ENTRY(entry, &This->bands, struct band_entry, entry) + band_connect_to_collection(entry->band, param); + } else if (IsEqualGUID(type, &GUID_Disable_Auto_Download)) FIXME("GUID_Disable_Auto_Download not handled yet\n"); else if (IsEqualGUID(type, &GUID_Download)) diff --git a/dlls/dmband/dmband_private.h b/dlls/dmband/dmband_private.h index 444fe5ccf55..a12b9f8cc82 100644 --- a/dlls/dmband/dmband_private.h +++ b/dlls/dmband/dmband_private.h @@ -47,4 +47,6 @@ extern HRESULT create_dmband(REFIID riid, void **ret_iface); extern HRESULT create_dmbandtrack(REFIID riid, void **ret_iface); +extern HRESULT band_connect_to_collection(IDirectMusicBand *iface, IDirectMusicCollection *collection); + #endif /* __WINE_DMBAND_PRIVATE_H */ diff --git a/dlls/dmusic/dmobject.c b/dlls/dmusic/dmobject.c index 3007aceef3e..8cb4719c4e6 100644 --- a/dlls/dmusic/dmobject.c +++ b/dlls/dmusic/dmobject.c @@ -444,6 +444,35 @@ HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, return S_OK; } +HRESULT stream_get_loader(IStream *stream, IDirectMusicLoader **ret_loader) +{ + IDirectMusicGetLoader *getter; + HRESULT hr; + + if (SUCCEEDED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getter))) + { + hr = IDirectMusicGetLoader_GetLoader(getter, ret_loader); + IDirectMusicGetLoader_Release(getter); + } + + if (FAILED(hr)) *ret_loader = NULL; + return hr; +} + +HRESULT stream_get_object(IStream *stream, DMUS_OBJECTDESC *desc, REFIID iid, void **ret_iface) +{ + IDirectMusicLoader *loader; + HRESULT hr; + + if (SUCCEEDED(hr = stream_get_loader(stream, &loader))) + { + hr = IDirectMusicLoader_GetObject(loader, desc, iid, (void **)ret_iface); + IDirectMusicLoader_Release(loader); + } + + if (FAILED(hr)) *ret_iface = NULL; + return hr; +} /* Generic IDirectMusicObject methods */ @@ -626,8 +655,6 @@ HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, IDirectMusicObject **dmobj) { struct chunk_entry chunk = {.parent = list}; - IDirectMusicGetLoader *getloader; - IDirectMusicLoader *loader; DMUS_OBJECTDESC desc; DMUS_IO_REFERENCE reference; HRESULT hr; @@ -650,17 +677,7 @@ HRESULT dmobj_parsereference(IStream *stream, const struct chunk_entry *list, desc.dwValidData |= DMUS_OBJ_CLASS; dump_DMUS_OBJECTDESC(&desc); - if (FAILED(hr = IStream_QueryInterface(stream, &IID_IDirectMusicGetLoader, (void**)&getloader))) - return hr; - hr = IDirectMusicGetLoader_GetLoader(getloader, &loader); - IDirectMusicGetLoader_Release(getloader); - if (FAILED(hr)) - return hr; - - hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicObject, (void**)dmobj); - IDirectMusicLoader_Release(loader); - - return hr; + return stream_get_object(stream, &desc, &IID_IDirectMusicObject, (void **)dmobj); } /* Generic IPersistStream methods */ diff --git a/dlls/dmusic/dmobject.h b/dlls/dmusic/dmobject.h index ae06935dfea..82ac05f31b9 100644 --- a/dlls/dmusic/dmobject.h +++ b/dlls/dmusic/dmobject.h @@ -44,6 +44,9 @@ HRESULT stream_chunk_get_data(IStream *stream, const struct chunk_entry *chunk, HRESULT stream_chunk_get_wstr(IStream *stream, const struct chunk_entry *chunk, WCHAR *str, ULONG size); +HRESULT stream_get_loader(IStream *stream, IDirectMusicLoader **ret_loader); +HRESULT stream_get_object(IStream *stream, DMUS_OBJECTDESC *desc, REFIID riid, void **ret_iface); + static inline HRESULT stream_reset_chunk_data(IStream *stream, const struct chunk_entry *chunk) { LARGE_INTEGER offset; From b96e71d9657d6f73f343edf03f807bf62c9aa043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 26 Sep 2023 09:35:50 +0200 Subject: [PATCH 0961/1148] dmime: Implement IDirectMusicSegment_(Download|Unload). (cherry picked from commit 17f68bfabf5ca35e8483d69bd4b5a88f5e636723) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 12 ++++++------ dlls/dmime/tests/dmime.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index bc52931f667..af7729f34b9 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -530,18 +530,18 @@ static HRESULT WINAPI segment_Compose(IDirectMusicSegment8 *iface, MUSIC_TIME mt return S_OK; } -static HRESULT WINAPI segment_Download(IDirectMusicSegment8 *iface, IUnknown *pAudioPath) +static HRESULT WINAPI segment_Download(IDirectMusicSegment8 *iface, IUnknown *audio_path) { struct segment *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %p): stub\n", This, pAudioPath); - return S_OK; + TRACE("(%p, %p)\n", This, audio_path); + return IDirectMusicSegment8_SetParam(iface, &GUID_DownloadToAudioPath, -1, DMUS_SEG_ALLTRACKS, 0, audio_path); } -static HRESULT WINAPI segment_Unload(IDirectMusicSegment8 *iface, IUnknown *pAudioPath) +static HRESULT WINAPI segment_Unload(IDirectMusicSegment8 *iface, IUnknown *audio_path) { struct segment *This = impl_from_IDirectMusicSegment8(iface); - FIXME("(%p, %p): stub\n", This, pAudioPath); - return S_OK; + TRACE("(%p, %p)\n", This, audio_path); + return IDirectMusicSegment8_SetParam(iface, &GUID_UnloadFromAudioPath, -1, DMUS_SEG_ALLTRACKS, 0, audio_path); } static const IDirectMusicSegment8Vtbl segment_vtbl = diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index cfd58b8b229..ea5a972dc80 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3943,7 +3943,7 @@ static void test_segment_state(void) check_track_state(track, downloaded, FALSE); hr = IDirectMusicSegment8_Download((IDirectMusicSegment8 *)segment, (IUnknown *)performance); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine check_track_state(track, downloaded, TRUE); + check_track_state(track, downloaded, TRUE); hr = IDirectMusicSegment8_Unload((IDirectMusicSegment8 *)segment, (IUnknown *)performance); ok(hr == S_OK, "got %#lx\n", hr); check_track_state(track, downloaded, FALSE); From 0adc2e8a74cdae49b011d8cdddc49a8f5f1943ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 11:41:36 +0200 Subject: [PATCH 0962/1148] dmband: Implement IDirectMusicBand_(Download|Unload). (cherry picked from commit 0ad7b553490a5e04a9494ddc07335c857cd0d4ef) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 70 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index 361df5f8986..4b3c053c586 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -45,10 +45,30 @@ struct instrument_entry struct list entry; DMUS_IO_INSTRUMENT instrument; IDirectMusicCollection *collection; + + IDirectMusicDownloadedInstrument *download; + IDirectMusicPort *download_port; }; +static HRESULT instrument_entry_unload(struct instrument_entry *entry) +{ + HRESULT hr; + + if (!entry->download) return S_OK; + + if (FAILED(hr = IDirectMusicPort_UnloadInstrument(entry->download_port, entry->download))) + WARN("Failed to unload entry instrument, hr %#lx\n", hr); + IDirectMusicDownloadedInstrument_Release(entry->download); + entry->download = NULL; + IDirectMusicPort_Release(entry->download_port); + entry->download_port = NULL; + + return hr; +} + static void instrument_entry_destroy(struct instrument_entry *entry) { + instrument_entry_unload(entry); if (entry->collection) IDirectMusicCollection_Release(entry->collection); free(entry); } @@ -150,19 +170,51 @@ static HRESULT WINAPI band_CreateSegment(IDirectMusicBand *iface, } static HRESULT WINAPI band_Download(IDirectMusicBand *iface, - IDirectMusicPerformance *pPerformance) + IDirectMusicPerformance *performance) { - struct band *This = impl_from_IDirectMusicBand(iface); - FIXME("(%p, %p): stub\n", This, pPerformance); - return S_OK; + struct band *This = impl_from_IDirectMusicBand(iface); + struct instrument_entry *entry; + HRESULT hr = S_OK; + + TRACE("(%p, %p)\n", This, performance); + + LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry) + { + IDirectMusicCollection *collection; + IDirectMusicInstrument *instrument; + + if (FAILED(hr = instrument_entry_unload(entry))) break; + if (!(collection = entry->collection) && !(collection = This->collection)) continue; + + if (SUCCEEDED(hr = IDirectMusicCollection_GetInstrument(collection, entry->instrument.dwPatch, &instrument))) + { + hr = IDirectMusicPerformance_DownloadInstrument(performance, instrument, entry->instrument.dwPChannel, + &entry->download, NULL, 0, &entry->download_port, NULL, NULL); + IDirectMusicInstrument_Release(instrument); + } + + if (FAILED(hr)) break; + } + + if (FAILED(hr)) WARN("Failed to download instruments, hr %#lx\n", hr); + return hr; } -static HRESULT WINAPI band_Unload(IDirectMusicBand *iface, - IDirectMusicPerformance *pPerformance) +static HRESULT WINAPI band_Unload(IDirectMusicBand *iface, IDirectMusicPerformance *performance) { - struct band *This = impl_from_IDirectMusicBand(iface); - FIXME("(%p, %p): stub\n", This, pPerformance); - return S_OK; + struct band *This = impl_from_IDirectMusicBand(iface); + struct instrument_entry *entry; + HRESULT hr = S_OK; + + TRACE("(%p, %p)\n", This, performance); + + if (performance) FIXME("performance parameter not implemented\n"); + + LIST_FOR_EACH_ENTRY(entry, &This->instruments, struct instrument_entry, entry) + if (FAILED(hr = instrument_entry_unload(entry))) break; + + if (FAILED(hr)) WARN("Failed to unload instruments, hr %#lx\n", hr); + return hr; } static const IDirectMusicBandVtbl band_vtbl = From d963859faf01f6921421514331d66b35e6227eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 12:08:42 +0200 Subject: [PATCH 0963/1148] dmband: Implement band track GUID_UnloadFromAudioPath parameter. (cherry picked from commit 52a38dc41fde3d3636c10c49f1896807b244313f) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/bandtrack.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index 45dc7ace86a..b808faaf567 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -201,7 +201,13 @@ static HRESULT WINAPI band_track_SetParam(IDirectMusicTrack8 *iface, REFGUID typ else if (IsEqualGUID(type, &GUID_StandardMIDIFile)) FIXME("GUID_StandardMIDIFile not handled yet\n"); else if (IsEqualGUID(type, &GUID_UnloadFromAudioPath)) - FIXME("GUID_UnloadFromAudioPath not handled yet\n"); + { + struct band_entry *entry; + HRESULT hr; + + LIST_FOR_EACH_ENTRY(entry, &This->bands, struct band_entry, entry) + if (FAILED(hr = IDirectMusicBand_Unload(entry->band, NULL))) break; + } return S_OK; } From 0e5088cf61703083a035966c9bceec45cedc3252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 12:08:42 +0200 Subject: [PATCH 0964/1148] dmband: Implement band track GUID_DownloadToAudioPath parameter. (cherry picked from commit 96b0bdd7b59b2c5c52b2b9a05d629cc2596cef5a) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/bandtrack.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index b808faaf567..845345a9fa2 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -193,7 +193,32 @@ static HRESULT WINAPI band_track_SetParam(IDirectMusicTrack8 *iface, REFGUID typ else if (IsEqualGUID(type, &GUID_Download)) FIXME("GUID_Download not handled yet\n"); else if (IsEqualGUID(type, &GUID_DownloadToAudioPath)) - FIXME("GUID_DownloadToAudioPath not handled yet\n"); + { + IDirectMusicPerformance *performance; + IDirectMusicAudioPath *audio_path; + IUnknown *object = param; + struct band_entry *entry; + HRESULT hr; + + if (FAILED(hr = IDirectMusicAudioPath_QueryInterface(object, &IID_IDirectMusicPerformance8, (void **)&performance)) + && SUCCEEDED(hr = IDirectMusicAudioPath_QueryInterface(object, &IID_IDirectMusicAudioPath, (void **)&audio_path))) + { + hr = IDirectMusicAudioPath_GetObjectInPath(audio_path, DMUS_PCHANNEL_ALL, DMUS_PATH_PERFORMANCE, 0, + &GUID_All_Objects, 0, &IID_IDirectMusicPerformance8, (void **)&performance); + IDirectMusicAudioPath_Release(audio_path); + } + + if (FAILED(hr)) + { + WARN("Failed to get IDirectMusicPerformance from param %p\n", param); + return hr; + } + + LIST_FOR_EACH_ENTRY(entry, &This->bands, struct band_entry, entry) + if (FAILED(hr = IDirectMusicBand_Download(entry->band, performance))) break; + + IDirectMusicPerformance_Release(performance); + } else if (IsEqualGUID(type, &GUID_Enable_Auto_Download)) FIXME("GUID_Enable_Auto_Download not handled yet\n"); else if (IsEqualGUID(type, &GUID_IDirectMusicBand)) From de6cdb5ec2bb6027a0ea103168c3938475f19a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 6 Sep 2023 09:54:22 +0200 Subject: [PATCH 0965/1148] dmime: Implement IDirectMusicPerformance_DownloadInstrument. (cherry picked from commit 3c4d83609e16ad326685f01e7245684d7f526225) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index bdb7475370a..111d7c60568 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -828,14 +828,24 @@ static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, } static HRESULT WINAPI performance_DownloadInstrument(IDirectMusicPerformance8 *iface, - IDirectMusicInstrument *pInst, DWORD dwPChannel, - IDirectMusicDownloadedInstrument **ppDownInst, DMUS_NOTERANGE *pNoteRanges, - DWORD dwNumNoteRanges, IDirectMusicPort **ppPort, DWORD *pdwGroup, DWORD *pdwMChannel) + IDirectMusicInstrument *instrument, DWORD port_channel, + IDirectMusicDownloadedInstrument **downloaded, DMUS_NOTERANGE *note_ranges, + DWORD note_range_count, IDirectMusicPort **port, DWORD *group, DWORD *music_channel) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + IDirectMusicPort *tmp_port = NULL; + HRESULT hr; - FIXME("(%p, %p, %ld, %p, %p, %ld, %p, %p, %p): stub\n", This, pInst, dwPChannel, ppDownInst, pNoteRanges, dwNumNoteRanges, ppPort, pdwGroup, pdwMChannel); - return S_OK; + TRACE("(%p, %p, %ld, %p, %p, %ld, %p, %p, %p)\n", This, instrument, port_channel, downloaded, + note_ranges, note_range_count, port, group, music_channel); + + if (!port) port = &tmp_port; + if (FAILED(hr = IDirectMusicPerformance_PChannelInfo(iface, port_channel, port, group, music_channel))) + return hr; + + hr = IDirectMusicPort_DownloadInstrument(*port, instrument, downloaded, note_ranges, note_range_count); + if (tmp_port) IDirectMusicPort_Release(tmp_port); + return hr; } static HRESULT WINAPI performance_Invalidate(IDirectMusicPerformance8 *iface, MUSIC_TIME mtTime, DWORD dwFlags) From b050c53078293709ab099d5e638c386c4f6ccd96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 22 Sep 2023 09:30:08 +0200 Subject: [PATCH 0966/1148] dmime: Rename DirectMusicSegmentState8 method prefix to segment_state. (cherry picked from commit 939162b674049a3e5a2266384e35aa52244deb1e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segmentstate.c | 49 +++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index 1a0b63419f0..3a5eee72a57 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -32,7 +32,7 @@ static inline IDirectMusicSegmentState8Impl *impl_from_IDirectMusicSegmentState8 return CONTAINING_RECORD(iface, IDirectMusicSegmentState8Impl, IDirectMusicSegmentState8_iface); } -static HRESULT WINAPI DirectMusicSegmentState8_QueryInterface(IDirectMusicSegmentState8 *iface, REFIID riid, void **ppobj) +static HRESULT WINAPI segment_state_QueryInterface(IDirectMusicSegmentState8 *iface, REFIID riid, void **ppobj) { IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); @@ -56,7 +56,7 @@ static HRESULT WINAPI DirectMusicSegmentState8_QueryInterface(IDirectMusicSegmen return E_NOINTERFACE; } -static ULONG WINAPI DirectMusicSegmentState8_AddRef(IDirectMusicSegmentState8 *iface) +static ULONG WINAPI segment_state_AddRef(IDirectMusicSegmentState8 *iface) { IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); ULONG ref = InterlockedIncrement(&This->ref); @@ -66,7 +66,7 @@ static ULONG WINAPI DirectMusicSegmentState8_AddRef(IDirectMusicSegmentState8 *i return ref; } -static ULONG WINAPI DirectMusicSegmentState8_Release(IDirectMusicSegmentState8 *iface) +static ULONG WINAPI segment_state_Release(IDirectMusicSegmentState8 *iface) { IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); ULONG ref = InterlockedDecrement(&This->ref); @@ -78,64 +78,69 @@ static ULONG WINAPI DirectMusicSegmentState8_Release(IDirectMusicSegmentState8 * return ref; } -static HRESULT WINAPI DirectMusicSegmentState8_GetRepeats(IDirectMusicSegmentState8 *iface, DWORD* pdwRepeats) +static HRESULT WINAPI segment_state_GetRepeats(IDirectMusicSegmentState8 *iface, DWORD *pdwRepeats) { IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %p): stub\n", This, pdwRepeats); return S_OK; } -static HRESULT WINAPI DirectMusicSegmentState8_GetSegment(IDirectMusicSegmentState8 *iface, IDirectMusicSegment** ppSegment) +static HRESULT WINAPI segment_state_GetSegment(IDirectMusicSegmentState8 *iface, IDirectMusicSegment **ppSegment) { IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %p): stub\n", This, ppSegment); return S_OK; } -static HRESULT WINAPI DirectMusicSegmentState8_GetStartTime(IDirectMusicSegmentState8 *iface, MUSIC_TIME* pmtStart) +static HRESULT WINAPI segment_state_GetStartTime(IDirectMusicSegmentState8 *iface, MUSIC_TIME *pmtStart) { IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %p): stub\n", This, pmtStart); return S_OK; } -static HRESULT WINAPI DirectMusicSegmentState8_GetSeek(IDirectMusicSegmentState8 *iface, MUSIC_TIME* pmtSeek) +static HRESULT WINAPI segment_state_GetSeek(IDirectMusicSegmentState8 *iface, MUSIC_TIME *pmtSeek) { IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %p): stub\n", This, pmtSeek); return S_OK; } -static HRESULT WINAPI DirectMusicSegmentState8_GetStartPoint(IDirectMusicSegmentState8 *iface, MUSIC_TIME* pmtStart) +static HRESULT WINAPI segment_state_GetStartPoint(IDirectMusicSegmentState8 *iface, MUSIC_TIME *pmtStart) { IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %p): stub\n", This, pmtStart); return S_OK; } -static HRESULT WINAPI DirectMusicSegmentState8_SetTrackConfig(IDirectMusicSegmentState8 *iface, REFGUID rguidTrackClassID, DWORD dwGroupBits, DWORD dwIndex, DWORD dwFlagsOn, DWORD dwFlagsOff) { +static HRESULT WINAPI segment_state_SetTrackConfig(IDirectMusicSegmentState8 *iface, + REFGUID rguidTrackClassID, DWORD dwGroupBits, DWORD dwIndex, DWORD dwFlagsOn, DWORD dwFlagsOff) +{ IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %s, %ld, %ld, %ld, %ld): stub\n", This, debugstr_dmguid(rguidTrackClassID), dwGroupBits, dwIndex, dwFlagsOn, dwFlagsOff); return S_OK; } -static HRESULT WINAPI DirectMusicSegmentState8_GetObjectInPath(IDirectMusicSegmentState8 *iface, DWORD dwPChannel, DWORD dwStage, DWORD dwBuffer, REFGUID guidObject, DWORD dwIndex, REFGUID iidInterface, void** ppObject) { +static HRESULT WINAPI segment_state_GetObjectInPath(IDirectMusicSegmentState8 *iface, DWORD dwPChannel, + DWORD dwStage, DWORD dwBuffer, REFGUID guidObject, DWORD dwIndex, REFGUID iidInterface, void **ppObject) +{ IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %ld, %ld, %ld, %s, %ld, %s, %p): stub\n", This, dwPChannel, dwStage, dwBuffer, debugstr_dmguid(guidObject), dwIndex, debugstr_dmguid(iidInterface), ppObject); return S_OK; } -static const IDirectMusicSegmentState8Vtbl DirectMusicSegmentState8Vtbl = { - DirectMusicSegmentState8_QueryInterface, - DirectMusicSegmentState8_AddRef, - DirectMusicSegmentState8_Release, - DirectMusicSegmentState8_GetRepeats, - DirectMusicSegmentState8_GetSegment, - DirectMusicSegmentState8_GetStartTime, - DirectMusicSegmentState8_GetSeek, - DirectMusicSegmentState8_GetStartPoint, - DirectMusicSegmentState8_SetTrackConfig, - DirectMusicSegmentState8_GetObjectInPath +static const IDirectMusicSegmentState8Vtbl segment_state_vtbl = +{ + segment_state_QueryInterface, + segment_state_AddRef, + segment_state_Release, + segment_state_GetRepeats, + segment_state_GetSegment, + segment_state_GetStartTime, + segment_state_GetSeek, + segment_state_GetStartPoint, + segment_state_SetTrackConfig, + segment_state_GetObjectInPath, }; /* for ClassFactory */ @@ -146,7 +151,7 @@ HRESULT create_dmsegmentstate(REFIID riid, void **ret_iface) *ret_iface = NULL; if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->IDirectMusicSegmentState8_iface.lpVtbl = &DirectMusicSegmentState8Vtbl; + obj->IDirectMusicSegmentState8_iface.lpVtbl = &segment_state_vtbl; obj->ref = 1; hr = IDirectMusicSegmentState8_QueryInterface(&obj->IDirectMusicSegmentState8_iface, riid, ret_iface); From 3d5361d80cc01d452d90e0180d75c5ef1f1f555d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 22 Sep 2023 11:28:43 +0200 Subject: [PATCH 0967/1148] dmime: Get rid of the IDirectMusicSegmentState8Impl typedef. (cherry picked from commit 94386b4fbafcaf7b0dd1871a20c447d17c53d5c0) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segmentstate.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index 3a5eee72a57..b49308369ac 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -1,5 +1,4 @@ -/* IDirectMusicSegmentState8 Implementation - * +/* * Copyright (C) 2003-2004 Rok Mandeljc * * This program is free software; you can redistribute it and/or @@ -22,19 +21,20 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmime); -typedef struct IDirectMusicSegmentState8Impl { +struct segment_state +{ IDirectMusicSegmentState8 IDirectMusicSegmentState8_iface; LONG ref; -} IDirectMusicSegmentState8Impl; +}; -static inline IDirectMusicSegmentState8Impl *impl_from_IDirectMusicSegmentState8(IDirectMusicSegmentState8 *iface) +static inline struct segment_state *impl_from_IDirectMusicSegmentState8(IDirectMusicSegmentState8 *iface) { - return CONTAINING_RECORD(iface, IDirectMusicSegmentState8Impl, IDirectMusicSegmentState8_iface); + return CONTAINING_RECORD(iface, struct segment_state, IDirectMusicSegmentState8_iface); } static HRESULT WINAPI segment_state_QueryInterface(IDirectMusicSegmentState8 *iface, REFIID riid, void **ppobj) { - IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); + struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ppobj); @@ -58,7 +58,7 @@ static HRESULT WINAPI segment_state_QueryInterface(IDirectMusicSegmentState8 *if static ULONG WINAPI segment_state_AddRef(IDirectMusicSegmentState8 *iface) { - IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); + struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p): %ld\n", This, ref); @@ -68,7 +68,7 @@ static ULONG WINAPI segment_state_AddRef(IDirectMusicSegmentState8 *iface) static ULONG WINAPI segment_state_Release(IDirectMusicSegmentState8 *iface) { - IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); + struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p): %ld\n", This, ref); @@ -80,35 +80,35 @@ static ULONG WINAPI segment_state_Release(IDirectMusicSegmentState8 *iface) static HRESULT WINAPI segment_state_GetRepeats(IDirectMusicSegmentState8 *iface, DWORD *pdwRepeats) { - IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); + struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %p): stub\n", This, pdwRepeats); return S_OK; } static HRESULT WINAPI segment_state_GetSegment(IDirectMusicSegmentState8 *iface, IDirectMusicSegment **ppSegment) { - IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); + struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %p): stub\n", This, ppSegment); return S_OK; } static HRESULT WINAPI segment_state_GetStartTime(IDirectMusicSegmentState8 *iface, MUSIC_TIME *pmtStart) { - IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); + struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %p): stub\n", This, pmtStart); return S_OK; } static HRESULT WINAPI segment_state_GetSeek(IDirectMusicSegmentState8 *iface, MUSIC_TIME *pmtSeek) { - IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); + struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %p): stub\n", This, pmtSeek); return S_OK; } static HRESULT WINAPI segment_state_GetStartPoint(IDirectMusicSegmentState8 *iface, MUSIC_TIME *pmtStart) { - IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); + struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %p): stub\n", This, pmtStart); return S_OK; } @@ -116,7 +116,7 @@ static HRESULT WINAPI segment_state_GetStartPoint(IDirectMusicSegmentState8 *ifa static HRESULT WINAPI segment_state_SetTrackConfig(IDirectMusicSegmentState8 *iface, REFGUID rguidTrackClassID, DWORD dwGroupBits, DWORD dwIndex, DWORD dwFlagsOn, DWORD dwFlagsOff) { - IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); + struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %s, %ld, %ld, %ld, %ld): stub\n", This, debugstr_dmguid(rguidTrackClassID), dwGroupBits, dwIndex, dwFlagsOn, dwFlagsOff); return S_OK; } @@ -124,7 +124,7 @@ static HRESULT WINAPI segment_state_SetTrackConfig(IDirectMusicSegmentState8 *if static HRESULT WINAPI segment_state_GetObjectInPath(IDirectMusicSegmentState8 *iface, DWORD dwPChannel, DWORD dwStage, DWORD dwBuffer, REFGUID guidObject, DWORD dwIndex, REFGUID iidInterface, void **ppObject) { - IDirectMusicSegmentState8Impl *This = impl_from_IDirectMusicSegmentState8(iface); + struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); FIXME("(%p, %ld, %ld, %ld, %s, %ld, %s, %p): stub\n", This, dwPChannel, dwStage, dwBuffer, debugstr_dmguid(guidObject), dwIndex, debugstr_dmguid(iidInterface), ppObject); return S_OK; } @@ -146,7 +146,7 @@ static const IDirectMusicSegmentState8Vtbl segment_state_vtbl = /* for ClassFactory */ HRESULT create_dmsegmentstate(REFIID riid, void **ret_iface) { - IDirectMusicSegmentState8Impl* obj; + struct segment_state *obj; HRESULT hr; *ret_iface = NULL; From a7852664809a6a9e489655c507af5f5589e3e7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 22 Sep 2023 11:29:00 +0200 Subject: [PATCH 0968/1148] dmime: Implement some segment state default values. (cherry picked from commit c6e6f87a898817ac13e49685f0bacdd9b17518a6) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segmentstate.c | 30 +++++++++++++++++++----------- dlls/dmime/tests/dmime.c | 14 +++++++------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index b49308369ac..655940311b3 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -78,38 +78,46 @@ static ULONG WINAPI segment_state_Release(IDirectMusicSegmentState8 *iface) return ref; } -static HRESULT WINAPI segment_state_GetRepeats(IDirectMusicSegmentState8 *iface, DWORD *pdwRepeats) +static HRESULT WINAPI segment_state_GetRepeats(IDirectMusicSegmentState8 *iface, DWORD *repeats) { struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); - FIXME("(%p, %p): stub\n", This, pdwRepeats); + FIXME("(%p, %p): semi-stub\n", This, repeats); return S_OK; } -static HRESULT WINAPI segment_state_GetSegment(IDirectMusicSegmentState8 *iface, IDirectMusicSegment **ppSegment) +static HRESULT WINAPI segment_state_GetSegment(IDirectMusicSegmentState8 *iface, IDirectMusicSegment **segment) { struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); - FIXME("(%p, %p): stub\n", This, ppSegment); - return S_OK; + + FIXME("(%p, %p): semi-stub\n", This, segment); + + *segment = NULL; + return DMUS_E_NOT_FOUND; } -static HRESULT WINAPI segment_state_GetStartTime(IDirectMusicSegmentState8 *iface, MUSIC_TIME *pmtStart) +static HRESULT WINAPI segment_state_GetStartTime(IDirectMusicSegmentState8 *iface, MUSIC_TIME *start_time) { struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); - FIXME("(%p, %p): stub\n", This, pmtStart); + FIXME("(%p, %p): semi-stub\n", This, start_time); + *start_time = -1; return S_OK; } -static HRESULT WINAPI segment_state_GetSeek(IDirectMusicSegmentState8 *iface, MUSIC_TIME *pmtSeek) +static HRESULT WINAPI segment_state_GetSeek(IDirectMusicSegmentState8 *iface, MUSIC_TIME *seek_time) { struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); - FIXME("(%p, %p): stub\n", This, pmtSeek); + FIXME("(%p, %p): semi-stub\n", This, seek_time); + *seek_time = 0; return S_OK; } -static HRESULT WINAPI segment_state_GetStartPoint(IDirectMusicSegmentState8 *iface, MUSIC_TIME *pmtStart) +static HRESULT WINAPI segment_state_GetStartPoint(IDirectMusicSegmentState8 *iface, MUSIC_TIME *start_time) { struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); - FIXME("(%p, %p): stub\n", This, pmtStart); + + FIXME("(%p, %p): semi-stub\n", This, start_time); + + *start_time = 0; return S_OK; } diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index ea5a972dc80..e7113d2bb5c 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3985,12 +3985,12 @@ static void test_segment_state(void) tmp_segment = (void *)0xdeadbeef; hr = IDirectMusicSegmentState_GetSegment(state, &tmp_segment); - todo_wine ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); - todo_wine ok(tmp_segment == NULL, "got %p\n", tmp_segment); + ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + ok(tmp_segment == NULL, "got %p\n", tmp_segment); time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetStartPoint(state, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time == 0, "got %#lx\n", time); + ok(time == 0, "got %#lx\n", time); time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetRepeats(state, &value); ok(hr == S_OK, "got %#lx\n", hr); @@ -3998,11 +3998,11 @@ static void test_segment_state(void) time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetStartTime(state, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time == -1, "got %#lx\n", time); + ok(time == -1, "got %#lx\n", time); time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetSeek(state, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time == 0, "got %#lx\n", time); + ok(time == 0, "got %#lx\n", time); /* PlaySegment returns a different, genuine segment state */ @@ -4043,9 +4043,9 @@ static void test_segment_state(void) tmp_segment = (void *)0xdeadbeef; hr = IDirectMusicSegmentState_GetSegment(state, &tmp_segment); - ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); todo_wine ok(tmp_segment == segment, "got %p\n", tmp_segment); - if (tmp_segment != (void *)0xdeadbeef) IDirectMusicSegment_Release(tmp_segment); + if (tmp_segment) IDirectMusicSegment_Release(tmp_segment); time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetStartPoint(state, &time); From 270f6c5dafec31df8013936e99c41e2764f95a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 26 Sep 2023 08:42:49 +0200 Subject: [PATCH 0969/1148] dmime: Redirect IDirectMusicPerformance_PlaySegment to PlaySegmentEx. (cherry picked from commit 9cb0142632da119b0d808d21fec0f29b9baaef9b) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 111d7c60568..e590db88997 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -384,16 +384,15 @@ static HRESULT WINAPI performance_Init(IDirectMusicPerformance8 *iface, IDirectM return S_OK; } -static HRESULT WINAPI performance_PlaySegment(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, - DWORD dwFlags, __int64 i64StartTime, IDirectMusicSegmentState **ppSegmentState) +static HRESULT WINAPI performance_PlaySegment(IDirectMusicPerformance8 *iface, IDirectMusicSegment *segment, + DWORD segment_flags, INT64 start_time, IDirectMusicSegmentState **ret_state) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); - FIXME("(%p, %p, %ld, 0x%s, %p): stub\n", This, pSegment, dwFlags, - wine_dbgstr_longlong(i64StartTime), ppSegmentState); - if (ppSegmentState) - return create_dmsegmentstate(&IID_IDirectMusicSegmentState,(void**)ppSegmentState); - return S_OK; + TRACE("(%p, %p, %ld, %I64d, %p)\n", This, segment, segment_flags, start_time, ret_state); + + return IDirectMusicPerformance8_PlaySegmentEx(iface, (IUnknown *)segment, NULL, NULL, + segment_flags, start_time, ret_state, NULL, NULL); } static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, From 81080d8c95f2a0e759a5f5a14e934b31d49ab235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 3 Oct 2023 11:37:11 +0200 Subject: [PATCH 0970/1148] dmime: Introduce a new segment_state_create constructor. (cherry picked from commit 45d1c00eeaf833e64767a43241612d0411f86493) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 3 +++ dlls/dmime/performance.c | 36 ++++++++++++++++++------- dlls/dmime/segmentstate.c | 55 +++++++++++++++++++++++++++++++------- dlls/dmime/tests/dmime.c | 14 +++++----- 4 files changed, 83 insertions(+), 25 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index eacde4b46be..c35c52eb066 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -70,6 +70,9 @@ extern void set_audiopath_perf_pointer(IDirectMusicAudioPath*,IDirectMusicPerfor extern void set_audiopath_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*); extern void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*); +extern HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, + IDirectMusicSegmentState **ret_iface); + /***************************************************************************** * Auxiliary definitions */ diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index e590db88997..9f293358ca6 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1063,17 +1063,35 @@ static HRESULT WINAPI performance_InitAudio(IDirectMusicPerformance8 *iface, IDi return S_OK; } -static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, IUnknown *pSource, - WCHAR *pwzSegmentName, IUnknown *pTransition, DWORD dwFlags, __int64 i64StartTime, - IDirectMusicSegmentState **ppSegmentState, IUnknown *pFrom, IUnknown *pAudioPath) +static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, IUnknown *source, + WCHAR *segment_name, IUnknown *transition, DWORD segment_flags, INT64 start_time, + IDirectMusicSegmentState **segment_state, IUnknown *from, IUnknown *audio_path) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + IDirectMusicSegmentState *state; + IDirectMusicSegment *segment; + 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, %s, %p, %#lx, %I64d, %p, %p, %p): stub\n", This, source, debugstr_w(segment_name), + transition, segment_flags, start_time, segment_state, from, audio_path); + + if (FAILED(hr = IUnknown_QueryInterface(source, &IID_IDirectMusicSegment, (void **)&segment))) + return hr; + if (FAILED(hr = segment_state_create((IDirectMusicSegment *)segment, start_time, &state))) + { + IDirectMusicSegment_Release(segment); + return hr; + } + + if (segment_state) + { + *segment_state = state; + IDirectMusicSegmentState_AddRef(state); + } + + IDirectMusicSegmentState_Release(state); + IDirectMusicSegment_Release(segment); + return hr; } static HRESULT WINAPI performance_StopEx(IDirectMusicPerformance8 *iface, IUnknown *pObjectToStop, diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index 655940311b3..1a09bed5dd3 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -25,6 +25,10 @@ struct segment_state { IDirectMusicSegmentState8 IDirectMusicSegmentState8_iface; LONG ref; + + IDirectMusicSegment *segment; + MUSIC_TIME start_point; + MUSIC_TIME start_time; }; static inline struct segment_state *impl_from_IDirectMusicSegmentState8(IDirectMusicSegmentState8 *iface) @@ -73,7 +77,11 @@ static ULONG WINAPI segment_state_Release(IDirectMusicSegmentState8 *iface) TRACE("(%p): %ld\n", This, ref); - if (!ref) free(This); + if (!ref) + { + if (This->segment) IDirectMusicSegment_Release(This->segment); + free(This); + } return ref; } @@ -89,17 +97,21 @@ static HRESULT WINAPI segment_state_GetSegment(IDirectMusicSegmentState8 *iface, { struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); - FIXME("(%p, %p): semi-stub\n", This, segment); + TRACE("(%p, %p)\n", This, segment); - *segment = NULL; - return DMUS_E_NOT_FOUND; + if (!(*segment = This->segment)) return DMUS_E_NOT_FOUND; + + IDirectMusicSegment_AddRef(This->segment); + return S_OK; } static HRESULT WINAPI segment_state_GetStartTime(IDirectMusicSegmentState8 *iface, MUSIC_TIME *start_time) { struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); - FIXME("(%p, %p): semi-stub\n", This, start_time); - *start_time = -1; + + TRACE("(%p, %p)\n", This, start_time); + + *start_time = This->start_time; return S_OK; } @@ -111,13 +123,13 @@ static HRESULT WINAPI segment_state_GetSeek(IDirectMusicSegmentState8 *iface, MU return S_OK; } -static HRESULT WINAPI segment_state_GetStartPoint(IDirectMusicSegmentState8 *iface, MUSIC_TIME *start_time) +static HRESULT WINAPI segment_state_GetStartPoint(IDirectMusicSegmentState8 *iface, MUSIC_TIME *start_point) { struct segment_state *This = impl_from_IDirectMusicSegmentState8(iface); - FIXME("(%p, %p): semi-stub\n", This, start_time); + TRACE("(%p, %p)\n", This, start_point); - *start_time = 0; + *start_point = This->start_point; return S_OK; } @@ -161,8 +173,33 @@ HRESULT create_dmsegmentstate(REFIID riid, void **ret_iface) if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; obj->IDirectMusicSegmentState8_iface.lpVtbl = &segment_state_vtbl; obj->ref = 1; + obj->start_time = -1; + TRACE("Created IDirectMusicSegmentState %p\n", obj); hr = IDirectMusicSegmentState8_QueryInterface(&obj->IDirectMusicSegmentState8_iface, riid, ret_iface); IDirectMusicSegmentState8_Release(&obj->IDirectMusicSegmentState8_iface); return hr; } + +HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, + IDirectMusicSegmentState **ret_iface) +{ + IDirectMusicSegmentState *iface; + struct segment_state *This; + HRESULT hr; + + TRACE("(%p, %lu, %p)\n", segment, start_time, ret_iface); + + if (FAILED(hr = create_dmsegmentstate(&IID_IDirectMusicSegmentState, (void **)&iface))) return hr; + This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); + + This->segment = segment; + IDirectMusicSegment_AddRef(This->segment); + + This->start_time = start_time; + hr = IDirectMusicSegment_GetStartPoint(segment, &This->start_point); + + if (SUCCEEDED(hr)) *ret_iface = iface; + else IDirectMusicSegmentState_Release(iface); + return hr; +} diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index e7113d2bb5c..87d938a9015 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -4043,14 +4043,14 @@ static void test_segment_state(void) tmp_segment = (void *)0xdeadbeef; hr = IDirectMusicSegmentState_GetSegment(state, &tmp_segment); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(tmp_segment == segment, "got %p\n", tmp_segment); - if (tmp_segment) IDirectMusicSegment_Release(tmp_segment); + ok(hr == S_OK, "got %#lx\n", hr); + ok(tmp_segment == segment, "got %p\n", tmp_segment); + IDirectMusicSegment_Release(tmp_segment); time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetStartPoint(state, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time == 50, "got %lu\n", time); + ok(time == 50, "got %lu\n", time); time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetRepeats(state, &value); ok(hr == S_OK, "got %#lx\n", hr); @@ -4058,7 +4058,7 @@ static void test_segment_state(void) time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetStartTime(state, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time == 20, "got %#lx\n", time); + ok(time == 20, "got %#lx\n", time); time = 0xdeadbeef; /* The seek value is also dependent on whether the tracks are playing. @@ -4080,7 +4080,7 @@ static void test_segment_state(void) time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetStartPoint(state, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time == 50, "got %#lx\n", time); + ok(time == 50, "got %#lx\n", time); time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetRepeats(state, &value); ok(hr == S_OK, "got %#lx\n", hr); @@ -4088,7 +4088,7 @@ static void test_segment_state(void) time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetStartTime(state, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(time == 20, "got %#lx\n", time); + ok(time == 20, "got %#lx\n", time); time = 0xdeadbeef; hr = IDirectMusicSegmentState_GetSeek(state, &time); ok(hr == S_OK, "got %#lx\n", hr); From aaae04e676225123c6a3be00cc126aaba3614919 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Wed, 4 Oct 2023 23:31:22 -0600 Subject: [PATCH 0971/1148] mfplat: Rename debugstr_fourcc to mf_debugstr_fourcc. (cherry picked from commit ae77ddafb02dbffe32631c925826c9c70de9b7f1) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/mfplat/buffer.c | 2 +- dlls/mfplat/mediatype.c | 4 ++-- dlls/mfplat/mfplat_private.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 3850efa594c..b7f32f12cdc 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -1643,7 +1643,7 @@ HRESULT WINAPI MFCreateAlignedMemoryBuffer(DWORD max_length, DWORD alignment, IM */ HRESULT WINAPI MFCreate2DMediaBuffer(DWORD width, DWORD height, DWORD fourcc, BOOL bottom_up, IMFMediaBuffer **buffer) { - TRACE("%lu, %lu, %s, %d, %p.\n", width, height, debugstr_fourcc(fourcc), bottom_up, buffer); + TRACE("%lu, %lu, %s, %d, %p.\n", width, height, mf_debugstr_fourcc(fourcc), bottom_up, buffer); return create_2d_buffer(width, height, fourcc, bottom_up, buffer); } diff --git a/dlls/mfplat/mediatype.c b/dlls/mfplat/mediatype.c index 374024d8190..99fe4d1c733 100644 --- a/dlls/mfplat/mediatype.c +++ b/dlls/mfplat/mediatype.c @@ -2761,7 +2761,7 @@ HRESULT WINAPI MFGetStrideForBitmapInfoHeader(DWORD fourcc, DWORD width, LONG *s struct uncompressed_video_format *format; GUID subtype; - TRACE("%s, %lu, %p.\n", debugstr_fourcc(fourcc), width, stride); + TRACE("%s, %lu, %p.\n", mf_debugstr_fourcc(fourcc), width, stride); memcpy(&subtype, &MFVideoFormat_Base, sizeof(subtype)); subtype.Data1 = fourcc; @@ -2831,7 +2831,7 @@ HRESULT WINAPI MFGetPlaneSize(DWORD fourcc, DWORD width, DWORD height, DWORD *si unsigned int stride; GUID subtype; - TRACE("%s, %lu, %lu, %p.\n", debugstr_fourcc(fourcc), width, height, size); + TRACE("%s, %lu, %lu, %p.\n", mf_debugstr_fourcc(fourcc), width, height, size); memcpy(&subtype, &MFVideoFormat_Base, sizeof(subtype)); subtype.Data1 = fourcc; diff --git a/dlls/mfplat/mfplat_private.h b/dlls/mfplat/mfplat_private.h index 8418c8eb2ef..8c7dc73dab1 100644 --- a/dlls/mfplat/mfplat_private.h +++ b/dlls/mfplat/mfplat_private.h @@ -150,7 +150,7 @@ static inline const char *debugstr_propvar(const PROPVARIANT *v) } } -static inline const char *debugstr_fourcc(DWORD format) +static inline const char *mf_debugstr_fourcc(DWORD format) { static const struct format_name { From 47008b074390a709848ab868ffb412b1324f57fe Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Wed, 4 Oct 2023 23:32:04 -0600 Subject: [PATCH 0972/1148] winecoreaudio: Rename wine_dbgstr_fourcc to coreaudio_dbgstr_fourcc. (cherry picked from commit b490890d2de2042de5ff883f2ab0e2ad987f4bf5) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/winecoreaudio.drv/coreaudio.h | 2 +- dlls/winecoreaudio.drv/coremidi.c | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dlls/winecoreaudio.drv/coreaudio.h b/dlls/winecoreaudio.drv/coreaudio.h index e04043c4010..687bad54b55 100644 --- a/dlls/winecoreaudio.drv/coreaudio.h +++ b/dlls/winecoreaudio.drv/coreaudio.h @@ -23,7 +23,7 @@ #include "wine/debug.h" /* fourcc is in native order, where MSB is the first character. */ -static inline const char* wine_dbgstr_fourcc(INT32 fourcc) +static inline const char* coreaudio_dbgstr_fourcc(INT32 fourcc) { char buf[4] = { (char) (fourcc >> 24), (char) (fourcc >> 16), (char) (fourcc >> 8), (char) fourcc }; diff --git a/dlls/winecoreaudio.drv/coremidi.c b/dlls/winecoreaudio.drv/coremidi.c index c9b377f68b0..37cf859a913 100644 --- a/dlls/winecoreaudio.drv/coremidi.c +++ b/dlls/winecoreaudio.drv/coremidi.c @@ -448,7 +448,7 @@ static BOOL synth_unit_create_default(AUGraph *graph, AudioUnit *synth) sc = NewAUGraph(graph); if (sc != noErr) { - ERR("NewAUGraph return %s\n", wine_dbgstr_fourcc(sc)); + ERR("NewAUGraph return %s\n", coreaudio_dbgstr_fourcc(sc)); return FALSE; } @@ -463,7 +463,7 @@ static BOOL synth_unit_create_default(AUGraph *graph, AudioUnit *synth) sc = AUGraphAddNode(*graph, &desc, &synth_node); if (sc != noErr) { - ERR("AUGraphAddNode cannot create synthNode : %s\n", wine_dbgstr_fourcc(sc)); + ERR("AUGraphAddNode cannot create synthNode : %s\n", coreaudio_dbgstr_fourcc(sc)); return FALSE; } @@ -474,14 +474,14 @@ static BOOL synth_unit_create_default(AUGraph *graph, AudioUnit *synth) sc = AUGraphAddNode(*graph, &desc, &out_node); if (sc != noErr) { - ERR("AUGraphAddNode cannot create outNode %s\n", wine_dbgstr_fourcc(sc)); + ERR("AUGraphAddNode cannot create outNode %s\n", coreaudio_dbgstr_fourcc(sc)); return FALSE; } sc = AUGraphOpen(*graph); if (sc != noErr) { - ERR("AUGraphOpen returns %s\n", wine_dbgstr_fourcc(sc)); + ERR("AUGraphOpen returns %s\n", coreaudio_dbgstr_fourcc(sc)); return FALSE; } @@ -490,7 +490,7 @@ static BOOL synth_unit_create_default(AUGraph *graph, AudioUnit *synth) if (sc != noErr) { ERR("AUGraphConnectNodeInput cannot connect synthNode to outNode : %s\n", - wine_dbgstr_fourcc(sc)); + coreaudio_dbgstr_fourcc(sc)); return FALSE; } @@ -498,7 +498,7 @@ static BOOL synth_unit_create_default(AUGraph *graph, AudioUnit *synth) sc = AUGraphNodeInfo(*graph, synth_node, 0, synth); if (sc != noErr) { - ERR("AUGraphNodeInfo return %s\n", wine_dbgstr_fourcc(sc)); + ERR("AUGraphNodeInfo return %s\n", coreaudio_dbgstr_fourcc(sc)); return FALSE; } @@ -512,14 +512,14 @@ static BOOL synth_unit_init(AudioUnit synth, AUGraph graph) sc = AUGraphInitialize(graph); if (sc != noErr) { - ERR("AUGraphInitialize(%p) returns %s\n", graph, wine_dbgstr_fourcc(sc)); + ERR("AUGraphInitialize(%p) returns %s\n", graph, coreaudio_dbgstr_fourcc(sc)); return FALSE; } sc = AUGraphStart(graph); if (sc != noErr) { - ERR("AUGraphStart(%p) returns %s\n", graph, wine_dbgstr_fourcc(sc)); + ERR("AUGraphStart(%p) returns %s\n", graph, coreaudio_dbgstr_fourcc(sc)); return FALSE; } @@ -533,14 +533,14 @@ static BOOL synth_unit_close(AUGraph graph) sc = AUGraphStop(graph); if (sc != noErr) { - ERR("AUGraphStop(%p) returns %s\n", graph, wine_dbgstr_fourcc(sc)); + ERR("AUGraphStop(%p) returns %s\n", graph, coreaudio_dbgstr_fourcc(sc)); return FALSE; } sc = DisposeAUGraph(graph); if (sc != noErr) { - ERR("DisposeAUGraph(%p) returns %s\n", graph, wine_dbgstr_fourcc(sc)); + ERR("DisposeAUGraph(%p) returns %s\n", graph, coreaudio_dbgstr_fourcc(sc)); return FALSE; } @@ -686,7 +686,7 @@ static UINT midi_out_data(WORD dev_id, UINT data) sc = MusicDeviceMIDIEvent(dest->synth, bytes[0], bytes[1], bytes[2], 0); if (sc != noErr) { - ERR("MusicDeviceMIDIEvent returns %s\n", wine_dbgstr_fourcc(sc)); + ERR("MusicDeviceMIDIEvent returns %s\n", coreaudio_dbgstr_fourcc(sc)); return MMSYSERR_ERROR; } } @@ -739,7 +739,7 @@ static UINT midi_out_long_data(WORD dev_id, MIDIHDR *hdr, UINT hdr_size, struct sc = MusicDeviceSysEx(dest->synth, (const UInt8 *)hdr->lpData, hdr->dwBufferLength); if (sc != noErr) { - ERR("MusicDeviceSysEx returns %s\n", wine_dbgstr_fourcc(sc)); + ERR("MusicDeviceSysEx returns %s\n", coreaudio_dbgstr_fourcc(sc)); return MMSYSERR_ERROR; } } From 143f9d145d186eeecb63fdbfd96b4601f6d0d88f Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Mon, 2 Oct 2023 21:38:30 -0600 Subject: [PATCH 0973/1148] include: Introduce wine_dbgstr_fourcc and debugstr_fourcc. (cherry picked from commit 8b20c588012421ba8750bbeb886135266d14205d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/d3dxof/parsing.c | 9 --------- dlls/dmusic/dmobject.h | 7 ------- include/wine/debug.h | 11 +++++++++++ libs/strmbase/mediatype.c | 8 -------- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/dlls/d3dxof/parsing.c b/dlls/d3dxof/parsing.c index c853060dea1..c020df46bf7 100644 --- a/dlls/d3dxof/parsing.c +++ b/dlls/d3dxof/parsing.c @@ -81,15 +81,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3dxof_parsing); #define CLSIDFMT "<%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X>" -/* FOURCC to string conversion for debug messages */ -static const char *debugstr_fourcc(DWORD fourcc) -{ - if (!fourcc) return "'null'"; - return wine_dbg_sprintf ("\'%c%c%c%c\'", - (char)(fourcc), (char)(fourcc >> 8), - (char)(fourcc >> 16), (char)(fourcc >> 24)); -} - static const char* get_primitive_string(DWORD token) { switch(token) diff --git a/dlls/dmusic/dmobject.h b/dlls/dmusic/dmobject.h index 82ac05f31b9..bab96c77724 100644 --- a/dlls/dmusic/dmobject.h +++ b/dlls/dmusic/dmobject.h @@ -121,10 +121,3 @@ HRESULT WINAPI unimpl_IPersistStream_GetSizeMax(IPersistStream *iface, const char *debugstr_chunk(const struct chunk_entry *chunk); const char *debugstr_dmguid(const GUID *id); void dump_DMUS_OBJECTDESC(DMUS_OBJECTDESC *desc); - -static inline const char *debugstr_fourcc(DWORD fourcc) -{ - if (!fourcc) return "''"; - return wine_dbg_sprintf("'%c%c%c%c'", (char)(fourcc), (char)(fourcc >> 8), - (char)(fourcc >> 16), (char)(fourcc >> 24)); -} diff --git a/include/wine/debug.h b/include/wine/debug.h index c20924818dd..c282f52a3b6 100644 --- a/include/wine/debug.h +++ b/include/wine/debug.h @@ -315,6 +315,16 @@ static inline const char *wine_dbgstr_guid( const GUID *id ) id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] ); } +static inline const char *wine_dbgstr_fourcc( unsigned int fourcc ) +{ + char str[4] = { fourcc, fourcc >> 8, fourcc >> 16, fourcc >> 24 }; + if (!fourcc) + return "''"; + if (isprint( str[0] ) && isprint( str[1] ) && isprint( str[2] ) && isprint( str[3] )) + return wine_dbg_sprintf( "'%4s'", str ); + return wine_dbg_sprintf( "0x%08x", fourcc ); +} + static inline const char *wine_dbgstr_point( const POINT *pt ) { if (!pt) return "(null)"; @@ -495,6 +505,7 @@ static inline const char *wine_dbgstr_variant( const VARIANT *v ) static inline const char *debugstr_an( const char * s, int n ) { return wine_dbgstr_an( s, n ); } static inline const char *debugstr_wn( const WCHAR *s, int n ) { return wine_dbgstr_wn( s, n ); } static inline const char *debugstr_guid( const struct _GUID *id ) { return wine_dbgstr_guid(id); } +static inline const char *debugstr_fourcc( unsigned int cc ) { return wine_dbgstr_fourcc( cc ); } static inline const char *debugstr_a( const char *s ) { return wine_dbgstr_an( s, -1 ); } static inline const char *debugstr_w( const WCHAR *s ) { return wine_dbgstr_wn( s, -1 ); } diff --git a/libs/strmbase/mediatype.c b/libs/strmbase/mediatype.c index 33af6f2e636..5c157be521d 100644 --- a/libs/strmbase/mediatype.c +++ b/libs/strmbase/mediatype.c @@ -55,14 +55,6 @@ static const char *strmbase_debugstr_guid(const GUID *guid) return debugstr_guid(guid); } -static const char *debugstr_fourcc(DWORD fourcc) -{ - char str[4] = {fourcc, fourcc >> 8, fourcc >> 16, fourcc >> 24}; - if (isprint(str[0]) && isprint(str[1]) && isprint(str[2]) && isprint(str[3])) - return wine_dbgstr_an(str, 4); - return wine_dbg_sprintf("%#lx", fourcc); -} - void strmbase_dump_media_type(const AM_MEDIA_TYPE *mt) { if (!TRACE_ON(quartz) || !mt) return; From 1e74d47887228dac5d3772260eb08366882a5f88 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Mon, 2 Oct 2023 21:38:30 -0600 Subject: [PATCH 0974/1148] comctl32: Use the debugstr_fourcc function instead of reimplementing it. (cherry picked from commit 6159a50127d928b1831fb0233271ce583ef3f1b0) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/comctl32/animate.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/dlls/comctl32/animate.c b/dlls/comctl32/animate.c index f4d848fef91..721379f329c 100644 --- a/dlls/comctl32/animate.c +++ b/dlls/comctl32/animate.c @@ -534,14 +534,8 @@ static BOOL ANIMATE_GetAviInfo(ANIMATE_INFO *infoPtr) mmioRead(infoPtr->hMMio, (LPSTR)&infoPtr->ash, sizeof(infoPtr->ash)); - TRACE("ash.fccType='%c%c%c%c'\n", LOBYTE(LOWORD(infoPtr->ash.fccType)), - HIBYTE(LOWORD(infoPtr->ash.fccType)), - LOBYTE(HIWORD(infoPtr->ash.fccType)), - HIBYTE(HIWORD(infoPtr->ash.fccType))); - TRACE("ash.fccHandler='%c%c%c%c'\n", LOBYTE(LOWORD(infoPtr->ash.fccHandler)), - HIBYTE(LOWORD(infoPtr->ash.fccHandler)), - LOBYTE(HIWORD(infoPtr->ash.fccHandler)), - HIBYTE(HIWORD(infoPtr->ash.fccHandler))); + TRACE("ash.fccType=%s\n", debugstr_fourcc(infoPtr->ash.fccType)); + TRACE("ash.fccHandler=%s\n", debugstr_fourcc(infoPtr->ash.fccHandler)); TRACE("ash.dwFlags=%ld\n", infoPtr->ash.dwFlags); TRACE("ash.wPriority=%d\n", infoPtr->ash.wPriority); TRACE("ash.wLanguage=%d\n", infoPtr->ash.wLanguage); From b6fb91bbe0be5fbdb398a28022ee965b430b1c48 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Mon, 2 Oct 2023 21:38:30 -0600 Subject: [PATCH 0975/1148] mciavi32: Use the debugstr_fourcc function instead of reimplementing it. (cherry picked from commit 6da1c970a455843073f58b57f30dc0267e4f9f58) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/mciavi32/mmoutput.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/dlls/mciavi32/mmoutput.c b/dlls/mciavi32/mmoutput.c index ec2751e6d9a..dffdbc9de37 100644 --- a/dlls/mciavi32/mmoutput.c +++ b/dlls/mciavi32/mmoutput.c @@ -28,15 +28,9 @@ static BOOL MCIAVI_GetInfoAudio(WINE_MCIAVI* wma, const MMCKINFO* mmckList, MMCK { MMCKINFO mmckInfo; - TRACE("ash.fccType='%c%c%c%c'\n", LOBYTE(LOWORD(wma->ash_audio.fccType)), - HIBYTE(LOWORD(wma->ash_audio.fccType)), - LOBYTE(HIWORD(wma->ash_audio.fccType)), - HIBYTE(HIWORD(wma->ash_audio.fccType))); + TRACE("ash.fccType=%s\n", debugstr_fourcc(wma->ash_audio.fccType)); if (wma->ash_audio.fccHandler) /* not all streams specify a handler */ - TRACE("ash.fccHandler='%c%c%c%c'\n", LOBYTE(LOWORD(wma->ash_audio.fccHandler)), - HIBYTE(LOWORD(wma->ash_audio.fccHandler)), - LOBYTE(HIWORD(wma->ash_audio.fccHandler)), - HIBYTE(HIWORD(wma->ash_audio.fccHandler))); + TRACE("ash.fccHandler=%s\n", debugstr_fourcc(wma->ash_audio.fccHandler)); else TRACE("ash.fccHandler=0, no handler specified\n"); TRACE("ash.dwFlags=%ld\n", wma->ash_audio.dwFlags); @@ -89,14 +83,8 @@ static BOOL MCIAVI_GetInfoVideo(WINE_MCIAVI* wma, const MMCKINFO* mmckList, MMCK { MMCKINFO mmckInfo; - TRACE("ash.fccType='%c%c%c%c'\n", LOBYTE(LOWORD(wma->ash_video.fccType)), - HIBYTE(LOWORD(wma->ash_video.fccType)), - LOBYTE(HIWORD(wma->ash_video.fccType)), - HIBYTE(HIWORD(wma->ash_video.fccType))); - TRACE("ash.fccHandler='%c%c%c%c'\n", LOBYTE(LOWORD(wma->ash_video.fccHandler)), - HIBYTE(LOWORD(wma->ash_video.fccHandler)), - LOBYTE(HIWORD(wma->ash_video.fccHandler)), - HIBYTE(HIWORD(wma->ash_video.fccHandler))); + TRACE("ash.fccType=%s\n", debugstr_fourcc(wma->ash_video.fccType)); + TRACE("ash.fccHandler=%s\n", debugstr_fourcc(wma->ash_video.fccHandler)); TRACE("ash.dwFlags=%ld\n", wma->ash_video.dwFlags); TRACE("ash.wPriority=%d\n", wma->ash_video.wPriority); TRACE("ash.wLanguage=%d\n", wma->ash_video.wLanguage); From edfd69d7a939f7f83941fc547c224d4370404edd Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Mon, 2 Oct 2023 21:38:30 -0600 Subject: [PATCH 0976/1148] msvfw32: Use the debugstr_fourcc function instead of reimplementing it. (cherry picked from commit 4c07f221073d798cec79fb48d3bd6161860fbdc1) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/msvfw32/msvideo_main.c | 48 ++++++++++++++----------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/dlls/msvfw32/msvideo_main.c b/dlls/msvfw32/msvideo_main.c index 8620f00920d..25eb1dc36d5 100644 --- a/dlls/msvfw32/msvideo_main.c +++ b/dlls/msvfw32/msvideo_main.c @@ -57,18 +57,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(msvideo); (str)[3] = HIBYTE(HIWORD(fcc)); \ } while(0) -static inline const char *wine_dbgstr_fcc( DWORD fcc ) -{ - char fcc_str[5]; - fourcc_to_string(fcc_str, fcc); - fcc_str[4] = '\0'; - /* Last byte may be ' ' in some cases like "DIB " */ - if (isalnum(fcc_str[0]) && isalnum(fcc_str[1]) && isalnum(fcc_str[2]) - && (isalnum(fcc_str[3]) || isspace(fcc_str[3]))) - return wine_dbg_sprintf("%s", fcc_str); - return wine_dbg_sprintf("0x%08lx", fcc); -} - static const char *wine_dbgstr_icerr( int ret ) { const char *str; @@ -277,7 +265,7 @@ BOOL VFWAPI ICInfo(DWORD type, DWORD handler, ICINFO *info) HKEY key; TRACE("type %s, handler %s, info %p.\n", - wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler), info); + debugstr_fourcc(type), debugstr_fourcc(handler), info); memset(info, 0, sizeof(*info)); info->dwSize = sizeof(*info); @@ -337,7 +325,7 @@ BOOL VFWAPI ICInfo(DWORD type, DWORD handler, ICINFO *info) info->fccType = type; info->fccHandler = handler; - WARN("No driver found for codec %s.%s.\n", wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler)); + WARN("No driver found for codec %s.%s.\n", debugstr_fourcc(type), debugstr_fourcc(handler)); return FALSE; } @@ -351,7 +339,7 @@ BOOL VFWAPI ICInstall(DWORD type, DWORD handler, LPARAM lparam, char *desc, UINT struct reg_driver *driver; TRACE("type %s, handler %s, lparam %#Ix, desc %s, flags %#x.\n", - wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler), lparam, debugstr_a(desc), flags); + debugstr_fourcc(type), debugstr_fourcc(handler), lparam, debugstr_a(desc), flags); LIST_FOR_EACH_ENTRY(driver, ®_driver_list, struct reg_driver, entry) { @@ -406,7 +394,7 @@ BOOL VFWAPI ICRemove(DWORD type, DWORD handler, UINT flags) LONG res; TRACE("type %s, handler %s, flags %#x.\n", - wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler), flags); + debugstr_fourcc(type), debugstr_fourcc(handler), flags); LIST_FOR_EACH_ENTRY(driver, ®_driver_list, struct reg_driver, entry) { @@ -447,7 +435,7 @@ HIC VFWAPI ICOpen(DWORD fccType, DWORD fccHandler, UINT wMode) struct reg_driver *driver; HDRVR hdrv = NULL; - TRACE("(%s,%s,0x%08x)\n", wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), wMode); + TRACE("(%s,%s,0x%08x)\n", debugstr_fourcc(fccType), debugstr_fourcc(fccHandler), wMode); if (!fccHandler) /* No specific handler, return the first valid for wMode */ { @@ -464,7 +452,7 @@ HIC VFWAPI ICOpen(DWORD fccType, DWORD fccHandler, UINT wMode) if (local != 0) { TRACE("Returning %s as default handler for %s\n", - wine_dbgstr_fcc(info.fccHandler), wine_dbgstr_fcc(fccType)); + debugstr_fourcc(info.fccHandler), debugstr_fourcc(fccType)); return local; } } @@ -538,7 +526,7 @@ HIC VFWAPI ICOpenFunction(DWORD fccType, DWORD fccHandler, UINT wMode, DRIVERPRO WINE_HIC* whic; TRACE("(%s,%s,%d,%p)\n", - wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), wMode, lpfnHandler); + debugstr_fourcc(fccType), debugstr_fourcc(fccHandler), wMode, lpfnHandler); icopen.dwSize = sizeof(ICOPEN); icopen.fccType = fccType; @@ -639,7 +627,7 @@ HIC VFWAPI ICLocate(DWORD type, DWORD handler, BITMAPINFOHEADER *in, DWORD i; TRACE("type %s, handler %s, in %p, out %p, mode %u.\n", - wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler), in, out, mode); + debugstr_fourcc(type), debugstr_fourcc(handler), in, out, mode); switch (mode) { @@ -663,8 +651,8 @@ HIC VFWAPI ICLocate(DWORD type, DWORD handler, BITMAPINFOHEADER *in, { if (!ICSendMessage(hic, msg, (DWORD_PTR)in, (DWORD_PTR)out)) { - TRACE("Found codec %s.%s.\n", wine_dbgstr_fcc(type), - wine_dbgstr_fcc(handler)); + TRACE("Found codec %s.%s.\n", debugstr_fourcc(type), + debugstr_fourcc(handler)); return hic; } ICClose(hic); @@ -676,8 +664,8 @@ HIC VFWAPI ICLocate(DWORD type, DWORD handler, BITMAPINFOHEADER *in, { if (!ICSendMessage(hic, msg, (DWORD_PTR)in, (DWORD_PTR)out)) { - TRACE("Found codec %s.%s.\n", wine_dbgstr_fcc(info.fccType), - wine_dbgstr_fcc(info.fccHandler)); + TRACE("Found codec %s.%s.\n", debugstr_fourcc(info.fccType), + debugstr_fourcc(info.fccHandler)); return hic; } ICClose(hic); @@ -688,7 +676,7 @@ HIC VFWAPI ICLocate(DWORD type, DWORD handler, BITMAPINFOHEADER *in, return ICLocate(ICTYPE_VIDEO, handler, in, out, mode); WARN("Could not find a driver for codec %s.%s.\n", - wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler)); + debugstr_fourcc(type), debugstr_fourcc(handler)); return 0; } @@ -887,7 +875,7 @@ static BOOL enum_compressors(HWND list, COMPVARS *pcv, BOOL enum_all) if (ICCompressQuery(hic, pcv->lpbiIn, NULL) != ICERR_OK) { TRACE("fccHandler %s doesn't support input DIB format %ld\n", - wine_dbgstr_fcc(icinfo.fccHandler), pcv->lpbiIn->bmiHeader.biCompression); + debugstr_fourcc(icinfo.fccHandler), pcv->lpbiIn->bmiHeader.biCompression); ICClose(hic); continue; } @@ -1581,12 +1569,12 @@ BOOL VFWAPI ICSeqCompressFrameStart(PCOMPVARS pc, LPBITMAPINFO lpbiIn) TRACE("Input: %lux%lu, fcc %s, bpp %u, size %lu\n", pc->lpbiIn->bmiHeader.biWidth, pc->lpbiIn->bmiHeader.biHeight, - wine_dbgstr_fcc(pc->lpbiIn->bmiHeader.biCompression), + debugstr_fourcc(pc->lpbiIn->bmiHeader.biCompression), pc->lpbiIn->bmiHeader.biBitCount, pc->lpbiIn->bmiHeader.biSizeImage); TRACE("Output: %lux%lu, fcc %s, bpp %u, size %lu\n", pc->lpbiOut->bmiHeader.biWidth, pc->lpbiOut->bmiHeader.biHeight, - wine_dbgstr_fcc(pc->lpbiOut->bmiHeader.biCompression), + debugstr_fourcc(pc->lpbiOut->bmiHeader.biCompression), pc->lpbiOut->bmiHeader.biBitCount, pc->lpbiOut->bmiHeader.biSizeImage); @@ -1606,8 +1594,8 @@ BOOL VFWAPI ICSeqCompressFrameStart(PCOMPVARS pc, LPBITMAPINFO lpbiIn) "\thandler: %s\n" "\tin/out: %p/%p\n" "\tkey/data/quality: %li/%li/%li\n", - pc->cbSize, pc->dwFlags, pc->hic, wine_dbgstr_fcc(pc->fccType), - wine_dbgstr_fcc(pc->fccHandler), pc->lpbiIn, pc->lpbiOut, pc->lKey, + pc->cbSize, pc->dwFlags, pc->hic, debugstr_fourcc(pc->fccType), + debugstr_fourcc(pc->fccHandler), pc->lpbiIn, pc->lpbiOut, pc->lKey, pc->lDataRate, pc->lQ); ret = ICSendMessage(pc->hic, ICM_COMPRESS_BEGIN, (DWORD_PTR)pc->lpbiIn, (DWORD_PTR)pc->lpbiOut); From dc5feb5341cbdde71ae90c97f2b5c9c4f96cb9a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 6 Oct 2023 16:15:08 +0200 Subject: [PATCH 0977/1148] dmime/tests: Tests interaction between CloseDown and notifications. (cherry picked from commit b201cf1bcaa30cb0019023316607e72e8bd66ad2) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 87d938a9015..c892f25c884 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3107,6 +3107,8 @@ static void test_notification_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_AddNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_AddNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); + todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, NULL); ok(hr == S_OK, "got %#lx\n", hr); @@ -3178,8 +3180,6 @@ static void test_notification_pmsg(void) ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); ok(!msg, "got %p\n", msg); - IDirectMusicSegment_Release(segment); - hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_PERFORMANCE); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); @@ -3237,8 +3237,34 @@ static void test_notification_pmsg(void) ok(hr == S_FALSE, "got %#lx\n", hr); + /* RemoveNotificationType returns S_FALSE if already removed */ + + hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_PERFORMANCE); + todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + + + /* CloseDown removes all notifications and notification messages */ + + hr = IDirectMusicPerformance_AddNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + hr = IDirectMusicPerformance_CloseDown(performance); ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); + todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + IDirectMusicSegment_Release(segment); + IDirectMusicPerformance_Release(performance); IDirectMusicTool_Release(tool); From 9c48164a20426f107d0f6b0a51cad674acaf7125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 13:54:32 +0200 Subject: [PATCH 0978/1148] dmime: Keep messages with the same time ordered. (cherry picked from commit 299698140a7b7ce8e3868d934ab4553eb2219418) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 9f293358ca6..a5655f4b2df 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -497,7 +497,7 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS } LIST_FOR_EACH_ENTRY(next, &This->messages, struct message, entry) - if (next->msg.rtTime >= message->msg.rtTime) break; + if (next->msg.rtTime > message->msg.rtTime) break; list_add_before(&next->entry, &message->entry); PostThreadMessageW(This->procThreadId, PROCESSMSG_ADD, 0, 0); From ed520422603d517eb58b6943f0e8c09b67c9d58b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 6 Oct 2023 15:28:05 +0200 Subject: [PATCH 0979/1148] dmime: Free all pending messages after CloseDown. (cherry picked from commit ac832b97aeb639d178fdea4986a83c7f935fd9b8) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index a5655f4b2df..5c3e1f17a97 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -948,6 +948,8 @@ static HRESULT WINAPI performance_AdjustTime(IDirectMusicPerformance8 *iface, RE static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct message *message, *next; + HRESULT hr; FIXME("(%p): semi-stub\n", This); @@ -956,6 +958,16 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) This->procThreadTicStarted = FALSE; CloseHandle(This->procThread); } + + LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry) + { + list_remove(&message->entry); + list_init(&message->entry); + + if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg))) + WARN("Failed to free message %p, hr %#lx\n", message, hr); + } + if (This->master_clock) { IReferenceClock_Release(This->master_clock); From fa2dae5391ecb04dd98743c9a0a78f6a710e9cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 22 Sep 2023 18:49:16 +0200 Subject: [PATCH 0980/1148] dmime: Send DMUS_PMSGT_DIRTY messages from the performance. (cherry picked from commit 664caf6ed70a31c0e3f0a71e7523dece921a202c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 37 ++++++++++++++++++-- dlls/dmime/tests/dmime.c | 73 ++++++++++++++-------------------------- 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 5c3e1f17a97..0215b19a23c 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -204,6 +204,27 @@ static BOOL PostMessageToProcessMsgThread(struct performance *This, UINT iMsg) { return PostThreadMessageA(This->procThreadId, iMsg, 0, 0); } +static HRESULT performance_send_dirty_pmsg(struct performance *This, MUSIC_TIME music_time) +{ + IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface; + IDirectMusicGraph *graph = &This->IDirectMusicGraph_iface; + DMUS_PMSG *msg; + HRESULT hr; + + if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*msg), &msg))) + return hr; + + msg->mtTime = music_time; + msg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE; + msg->dwType = DMUS_PMSGT_DIRTY; + + if (FAILED(hr = IDirectMusicGraph_StampPMsg(graph, msg)) + || FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, msg))) + IDirectMusicPerformance8_FreePMsg(performance, msg); + + return hr; +} + static int pchannel_block_compare(const void *key, const struct wine_rb_entry *entry) { const struct pchannel_block *b = WINE_RB_ENTRY_VALUE(entry, const struct pchannel_block, entry); @@ -1082,20 +1103,32 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, struct performance *This = impl_from_IDirectMusicPerformance8(iface); IDirectMusicSegmentState *state; IDirectMusicSegment *segment; + MUSIC_TIME length; HRESULT hr; FIXME("(%p, %p, %s, %p, %#lx, %I64d, %p, %p, %p): stub\n", This, source, debugstr_w(segment_name), transition, segment_flags, start_time, segment_state, from, audio_path); + /* NOTE: The time is in music time unless the DMUS_SEGF_REFTIME flag is set. */ + if (segment_flags) FIXME("flags %#lx not implemented\n", segment_flags); + if (start_time) FIXME("start_time %I64d not implemented\n", start_time); + if (FAILED(hr = IUnknown_QueryInterface(source, &IID_IDirectMusicSegment, (void **)&segment))) return hr; - if (FAILED(hr = segment_state_create((IDirectMusicSegment *)segment, start_time, &state))) + if (FAILED(hr = segment_state_create(segment, start_time, &state))) { IDirectMusicSegment_Release(segment); return hr; } - if (segment_state) + hr = IDirectMusicSegment_GetLength(segment, &length); + if (SUCCEEDED(hr)) + hr = performance_send_dirty_pmsg(This, start_time); + + if (SUCCEEDED(hr)) + hr = performance_send_dirty_pmsg(This, start_time + length); + + if (SUCCEEDED(hr) && segment_state) { *segment_state = state; IDirectMusicSegmentState_AddRef(state); diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index c892f25c884..2b7f08eef7a 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3079,22 +3079,16 @@ static void test_notification_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - } + ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 100, &msg); ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); @@ -3114,22 +3108,22 @@ static void test_notification_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) + ok(!ret, "got %#lx\n", ret); + if (notif->dwType == DMUS_PMSGT_NOTIFICATION) { check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED); + } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) + ok(!ret, "got %#lx\n", ret); + if (notif->dwType == DMUS_PMSGT_NOTIFICATION) { check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); + } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, &msg); todo_wine ok(!ret, "got %#lx\n", ret); @@ -3251,13 +3245,13 @@ static void test_notification_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) + ok(!ret, "got %#lx\n", ret); + if (notif->dwType == DMUS_PMSGT_NOTIFICATION) { check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); + } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } hr = IDirectMusicPerformance_CloseDown(performance); ok(hr == S_OK, "got %#lx\n", hr); @@ -3333,22 +3327,16 @@ static void test_wave_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %p\n", msg); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %p\n", msg); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 100, &msg); ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); @@ -3364,17 +3352,14 @@ static void test_wave_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %p\n", msg); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&wave); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) + ok(!ret, "got %#lx\n", ret); + if (wave->dwType == DMUS_PMSGT_WAVE) { ok(wave->dwType == DMUS_PMSGT_WAVE, "got %p\n", wave); ok(!!wave->punkUser, "got %p\n", wave->punkUser); @@ -3384,9 +3369,9 @@ static void test_wave_pmsg(void) ok(wave->lVolume == 0, "got %lu\n", wave->lVolume); ok(wave->lPitch == 0, "got %lu\n", wave->lPitch); ok(wave->bFlags == 0, "got %#x\n", wave->bFlags); + } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)wave); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, &msg); todo_wine ok(!ret, "got %#lx\n", ret); @@ -3555,22 +3540,19 @@ static void test_sequence_track(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬e); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) + ok(!ret, "got %#lx\n", ret); + if (note->dwType == DMUS_PMSGT_NOTE) { check_dmus_note_pmsg(note, 0, 0, 500, 60, 120); + } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)note); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬e); todo_wine ok(!ret, "got %#lx\n", ret); @@ -3821,22 +3803,19 @@ static void test_band_track_play(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&patch); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) + ok(!ret, "got %#lx\n", ret); + if (patch->dwType == DMUS_PMSGT_PATCH) { check_dmus_patch_pmsg(patch, 0, 1, 0, 1); + } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)patch); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&patch); todo_wine ok(!ret, "got %#lx\n", ret); From 39f140be8f5e323f06c387a3442e5ae647069cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 7 Oct 2023 10:48:36 +0200 Subject: [PATCH 0981/1148] dmime: Send DMUS_PMSGT_NOTIFICATION messages from the performance. Keeping the segment state referenced until its playback ends. (cherry picked from commit 3de1bc035ede848b5661da7d4ad8af5220a5ae28) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 72 +++++++++++++++++++++++++++++++++++----- dlls/dmime/tests/dmime.c | 34 +++---------------- 2 files changed, 69 insertions(+), 37 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 0215b19a23c..40a78bc40ce 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -66,6 +66,9 @@ struct performance IReferenceClock *master_clock; REFERENCE_TIME init_time; struct list messages; + + BOOL notification_performance; + BOOL notification_segment; }; struct message @@ -225,6 +228,32 @@ static HRESULT performance_send_dirty_pmsg(struct performance *This, MUSIC_TIME return hr; } +static HRESULT performance_send_notification_pmsg(struct performance *This, MUSIC_TIME music_time, BOOL stamp, + GUID type, DWORD option, IUnknown *object) +{ + IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface; + IDirectMusicGraph *graph = &This->IDirectMusicGraph_iface; + DMUS_NOTIFICATION_PMSG *msg; + HRESULT hr; + + if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*msg), (DMUS_PMSG **)&msg))) + return hr; + + msg->mtTime = music_time; + msg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE; + msg->dwType = DMUS_PMSGT_NOTIFICATION; + if ((msg->punkUser = object)) IUnknown_AddRef(object); + msg->guidNotificationType = type; + msg->dwNotificationOption = option; + + /* only stamp the message if notifications are enabled, otherwise send them directly to the output tool */ + if ((stamp && FAILED(hr = IDirectMusicGraph_StampPMsg(graph, (DMUS_PMSG *)msg))) + || FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)msg))) + IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)msg); + + return hr; +} + static int pchannel_block_compare(const void *key, const struct wine_rb_entry *entry) { const struct pchannel_block *b = WINE_RB_ENTRY_VALUE(entry, const struct pchannel_block, entry); @@ -703,20 +732,32 @@ static HRESULT WINAPI performance_GetNotificationPMsg(IDirectMusicPerformance8 * /*return S_OK;*/ } -static HRESULT WINAPI performance_AddNotificationType(IDirectMusicPerformance8 *iface, REFGUID rguidNotificationType) +static HRESULT WINAPI performance_AddNotificationType(IDirectMusicPerformance8 *iface, REFGUID type) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); - FIXME("(%p, %s): stub\n", This, debugstr_dmguid(rguidNotificationType)); - return S_OK; + FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type)); + + if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE)) + This->notification_performance = TRUE; + if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT)) + This->notification_segment = TRUE; + + return S_OK; } -static HRESULT WINAPI performance_RemoveNotificationType(IDirectMusicPerformance8 *iface, REFGUID rguidNotificationType) +static HRESULT WINAPI performance_RemoveNotificationType(IDirectMusicPerformance8 *iface, REFGUID type) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); - FIXME("(%p, %s): stub\n", This, debugstr_dmguid(rguidNotificationType)); - return S_OK; + FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type)); + + if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE)) + This->notification_performance = FALSE; + if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT)) + This->notification_segment = FALSE; + + return S_OK; } static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *params) @@ -1122,11 +1163,26 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, } hr = IDirectMusicSegment_GetLength(segment, &length); + if (SUCCEEDED(hr)) + hr = performance_send_notification_pmsg(This, start_time, This->notification_performance, + GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED, NULL); + if (SUCCEEDED(hr)) + hr = performance_send_notification_pmsg(This, start_time, This->notification_segment, + GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART, (IUnknown *)state); if (SUCCEEDED(hr)) hr = performance_send_dirty_pmsg(This, start_time); + if (SUCCEEDED(hr)) + hr = performance_send_notification_pmsg(This, start_time + length, This->notification_segment, + GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state); + if (SUCCEEDED(hr)) + hr = performance_send_notification_pmsg(This, start_time + length, This->notification_segment, + GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state); if (SUCCEEDED(hr)) hr = performance_send_dirty_pmsg(This, start_time + length); + if (SUCCEEDED(hr)) + hr = performance_send_notification_pmsg(This, start_time + length, This->notification_performance, + GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED, NULL); if (SUCCEEDED(hr) && segment_state) { diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 2b7f08eef7a..c8c330860a8 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3109,66 +3109,45 @@ static void test_notification_pmsg(void) ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); - if (notif->dwType == DMUS_PMSGT_NOTIFICATION) - { check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED); - } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); - if (notif->dwType == DMUS_PMSGT_NOTIFICATION) - { check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); - } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 100, &msg); ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); @@ -3246,10 +3225,7 @@ static void test_notification_pmsg(void) ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); - if (notif->dwType == DMUS_PMSGT_NOTIFICATION) - { check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); - } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); From 43887db1235679877c8dff7e9fb97c48f891b465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 6 Oct 2023 15:55:13 +0200 Subject: [PATCH 0982/1148] dmime: Implement IDirectMusicPerformance_GetNotificationPMsg. (cherry picked from commit 7a678903fbe9b384e97b23bc8cb73c9239616054) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 108 ++++++++++++++++++++++++++++++--------- dlls/dmime/tests/dmime.c | 31 +++-------- 2 files changed, 93 insertions(+), 46 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 40a78bc40ce..3d53d754d11 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -52,8 +52,6 @@ struct performance BOOL audio_paths_enabled; IDirectMusicAudioPath *pDefaultPath; - HANDLE hNotification; - REFERENCE_TIME rtMinimum; REFERENCE_TIME rtLatencyTime; DWORD dwBumperLength; DWORD dwPrepareTime; @@ -67,6 +65,9 @@ struct performance REFERENCE_TIME init_time; struct list messages; + struct list notifications; + REFERENCE_TIME notification_timeout; + HANDLE notification_event; BOOL notification_performance; BOOL notification_segment; }; @@ -702,62 +703,84 @@ static HRESULT WINAPI performance_SetGraph(IDirectMusicPerformance8 *iface, IDir } static HRESULT WINAPI performance_SetNotificationHandle(IDirectMusicPerformance8 *iface, - HANDLE hNotification, REFERENCE_TIME rtMinimum) + HANDLE notification_event, REFERENCE_TIME minimum_time) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); - TRACE("(%p, %p, 0x%s)\n", This, hNotification, wine_dbgstr_longlong(rtMinimum)); + TRACE("(%p, %p, %I64d)\n", This, notification_event, minimum_time); + + This->notification_event = notification_event; + if (minimum_time) + This->notification_timeout = minimum_time; + else if (!This->notification_timeout) + This->notification_timeout = 20000000; /* 2 seconds */ - This->hNotification = hNotification; - if (rtMinimum) - This->rtMinimum = rtMinimum; - else if (!This->rtMinimum) - This->rtMinimum = 20000000; /* 2 seconds */ return S_OK; } static HRESULT WINAPI performance_GetNotificationPMsg(IDirectMusicPerformance8 *iface, - DMUS_NOTIFICATION_PMSG **ppNotificationPMsg) + DMUS_NOTIFICATION_PMSG **ret_msg) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct list *entry; - FIXME("(%p, %p): stub\n", This, ppNotificationPMsg); - if (NULL == ppNotificationPMsg) { - return E_POINTER; - } - - + TRACE("(%p, %p)\n", This, ret_msg); + + if (!ret_msg) return E_POINTER; - return S_FALSE; - /*return S_OK;*/ + EnterCriticalSection(&This->safe); + if ((entry = list_head(&This->notifications))) + { + struct message *message = LIST_ENTRY(entry, struct message, entry); + list_remove(&message->entry); + list_init(&message->entry); + *ret_msg = (DMUS_NOTIFICATION_PMSG *)&message->msg; + } + LeaveCriticalSection(&This->safe); + + return entry ? S_OK : S_FALSE; } static HRESULT WINAPI performance_AddNotificationType(IDirectMusicPerformance8 *iface, REFGUID type) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); + HRESULT hr = S_OK; FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type)); if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE)) + { + hr = This->notification_performance ? S_FALSE : S_OK; This->notification_performance = TRUE; + } if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT)) + { + hr = This->notification_segment ? S_FALSE : S_OK; This->notification_segment = TRUE; + } - return S_OK; + return hr; } static HRESULT WINAPI performance_RemoveNotificationType(IDirectMusicPerformance8 *iface, REFGUID type) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); + HRESULT hr = S_FALSE; FIXME("(%p, %s): stub\n", This, debugstr_dmguid(type)); if (IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE)) + { + hr = This->notification_performance ? S_OK : S_FALSE; This->notification_performance = FALSE; + } if (IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT)) + { + hr = This->notification_segment ? S_OK : S_FALSE; This->notification_segment = FALSE; + } - return S_OK; + return hr; } static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *params) @@ -1021,6 +1044,9 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) CloseHandle(This->procThread); } + This->notification_performance = FALSE; + This->notification_segment = FALSE; + LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry) { list_remove(&message->entry); @@ -1030,6 +1056,15 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) WARN("Failed to free message %p, hr %#lx\n", message, hr); } + LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->notifications, struct message, entry) + { + list_remove(&message->entry); + list_init(&message->entry); + + if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg))) + WARN("Failed to free message %p, hr %#lx\n", message, hr); + } + if (This->master_clock) { IReferenceClock_Release(This->master_clock); @@ -1586,14 +1621,40 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, IDirectMusicPerformance *performance, DMUS_PMSG *msg) { struct performance *This = impl_from_IDirectMusicTool(iface); + struct message *message = message_from_DMUS_PMSG(msg); FIXME("(%p, %p, %p): semi-stub\n", This, performance, msg); switch (msg->dwType) { case DMUS_PMSGT_NOTIFICATION: - SetEvent(This->hNotification); - /* fallthrough */ + { + DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)msg; + struct message *previous; + BOOL enabled = FALSE; + HRESULT hr; + + if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_PERFORMANCE)) + enabled = This->notification_performance; + if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_SEGMENT)) + enabled = This->notification_segment; + if (!enabled) return DMUS_S_FREE; + + list_add_tail(&This->notifications, &message->entry); + + /* discard old notification messages */ + do + { + previous = LIST_ENTRY(list_head(&This->notifications), struct message, entry); + if (message->msg.rtTime - previous->msg.rtTime <= This->notification_timeout) break; + list_remove(&previous->entry); + list_init(&previous->entry); + } while (SUCCEEDED(hr = IDirectMusicPerformance_FreePMsg(performance, &previous->msg))); + + SetEvent(This->notification_event); + return S_OK; + } + default: FIXME("Unhandled message type %#lx\n", msg->dwType); break; @@ -1644,6 +1705,7 @@ HRESULT create_dmperformance(REFIID iid, void **ret_iface) wine_rb_init(&obj->pchannels, pchannel_block_compare); list_init(&obj->messages); + list_init(&obj->notifications); obj->rtLatencyTime = 100; /* 100 ms TO FIX */ obj->dwBumperLength = 50; /* 50 ms default */ diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index c8c330860a8..f9143176594 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3102,7 +3102,7 @@ static void test_notification_pmsg(void) hr = IDirectMusicPerformance_AddNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_AddNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); - todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + ok(hr == S_FALSE, "got %#lx\n", hr); hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, NULL); ok(hr == S_OK, "got %#lx\n", hr); @@ -3162,49 +3162,34 @@ static void test_notification_pmsg(void) /* notification messages are also queued for direct access */ hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (hr == S_OK) - { + ok(hr == S_OK, "got %#lx\n", hr); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (hr == S_OK) - { + ok(hr == S_OK, "got %#lx\n", hr); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (hr == S_OK) - { + ok(hr == S_OK, "got %#lx\n", hr); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (hr == S_OK) - { + ok(hr == S_OK, "got %#lx\n", hr); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (hr == S_OK) - { + ok(hr == S_OK, "got %#lx\n", hr); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); ok(hr == S_FALSE, "got %#lx\n", hr); @@ -3213,7 +3198,7 @@ static void test_notification_pmsg(void) /* RemoveNotificationType returns S_FALSE if already removed */ hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_PERFORMANCE); - todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + ok(hr == S_FALSE, "got %#lx\n", hr); /* CloseDown removes all notifications and notification messages */ @@ -3232,7 +3217,7 @@ static void test_notification_pmsg(void) hr = IDirectMusicPerformance_CloseDown(performance); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); - todo_wine ok(hr == S_FALSE, "got %#lx\n", hr); + ok(hr == S_FALSE, "got %#lx\n", hr); IDirectMusicSegment_Release(segment); From 8835c213a5b107e597fbf8b0468c92845a682d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 22 Sep 2023 17:30:51 +0200 Subject: [PATCH 0983/1148] dmime: Call IDirectMusicTrack_(Init|End)Play from the segment state. (cherry picked from commit 3e19ca928e8479edce9f9484dc841d08edbdf5f8) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 4 +- dlls/dmime/performance.c | 21 +++++++- dlls/dmime/segmentstate.c | 98 +++++++++++++++++++++++++++++++++++++- dlls/dmime/tests/dmime.c | 14 +++--- 4 files changed, 125 insertions(+), 12 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index c35c52eb066..e67797def30 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -71,7 +71,9 @@ extern void set_audiopath_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffe extern void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*); extern HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, - IDirectMusicSegmentState **ret_iface); + IDirectMusicPerformance *performance, IDirectMusicSegmentState **ret_iface); +extern HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance *performance); +extern HRESULT segment_state_end_play(IDirectMusicSegmentState *iface); /***************************************************************************** * Auxiliary definitions diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 3d53d754d11..7d077db16cb 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1052,7 +1052,14 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) list_remove(&message->entry); list_init(&message->entry); - if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg))) + /* process notifications to end any pending segment states */ + if (message->msg.dwType == DMUS_PMSGT_NOTIFICATION) + hr = IDirectMusicTool_ProcessPMsg(&This->IDirectMusicTool_iface, + (IDirectMusicPerformance *)iface, &message->msg); + else + hr = DMUS_S_FREE; + + if (hr == DMUS_S_FREE && FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg))) WARN("Failed to free message %p, hr %#lx\n", message, hr); } @@ -1191,7 +1198,7 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, if (FAILED(hr = IUnknown_QueryInterface(source, &IID_IDirectMusicSegment, (void **)&segment))) return hr; - if (FAILED(hr = segment_state_create(segment, start_time, &state))) + if (FAILED(hr = segment_state_create(segment, start_time, (IDirectMusicPerformance *)iface, &state))) { IDirectMusicSegment_Release(segment); return hr; @@ -1207,6 +1214,9 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, if (SUCCEEDED(hr)) hr = performance_send_dirty_pmsg(This, start_time); + if (SUCCEEDED(hr)) + hr = segment_state_play(state, (IDirectMusicPerformance *)iface); + if (SUCCEEDED(hr)) hr = performance_send_notification_pmsg(This, start_time + length, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state); @@ -1634,6 +1644,13 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, BOOL enabled = FALSE; HRESULT hr; + if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_SEGMENT) + && notif->dwNotificationOption == DMUS_NOTIFICATION_SEGEND) + { + if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)notif->punkUser))) + WARN("Failed to end segment state %p, hr %#lx\n", notif->punkUser, hr); + } + if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_PERFORMANCE)) enabled = This->notification_performance; if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_SEGMENT)) diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index 1a09bed5dd3..de47d85e9b8 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -21,14 +21,39 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmime); +static DWORD next_track_id; + +struct track_entry +{ + struct list entry; + + IDirectMusicTrack *track; + void *state_data; + DWORD track_id; +}; + +static void track_entry_destroy(struct track_entry *entry) +{ + HRESULT hr; + + if (FAILED(hr = IDirectMusicTrack_EndPlay(entry->track, entry->state_data))) + WARN("track %p EndPlay failed, hr %#lx\n", entry->track, hr); + + IDirectMusicTrack_Release(entry->track); + free(entry); +} + struct segment_state { IDirectMusicSegmentState8 IDirectMusicSegmentState8_iface; LONG ref; IDirectMusicSegment *segment; - MUSIC_TIME start_point; MUSIC_TIME start_time; + MUSIC_TIME start_point; + MUSIC_TIME end_point; + + struct list tracks; }; static inline struct segment_state *impl_from_IDirectMusicSegmentState8(IDirectMusicSegmentState8 *iface) @@ -79,6 +104,7 @@ static ULONG WINAPI segment_state_Release(IDirectMusicSegmentState8 *iface) if (!ref) { + segment_state_end_play((IDirectMusicSegmentState *)iface); if (This->segment) IDirectMusicSegment_Release(This->segment); free(This); } @@ -174,6 +200,7 @@ HRESULT create_dmsegmentstate(REFIID riid, void **ret_iface) obj->IDirectMusicSegmentState8_iface.lpVtbl = &segment_state_vtbl; obj->ref = 1; obj->start_time = -1; + list_init(&obj->tracks); TRACE("Created IDirectMusicSegmentState %p\n", obj); hr = IDirectMusicSegmentState8_QueryInterface(&obj->IDirectMusicSegmentState8_iface, riid, ret_iface); @@ -182,11 +209,13 @@ HRESULT create_dmsegmentstate(REFIID riid, void **ret_iface) } HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, - IDirectMusicSegmentState **ret_iface) + IDirectMusicPerformance *performance, IDirectMusicSegmentState **ret_iface) { IDirectMusicSegmentState *iface; struct segment_state *This; + IDirectMusicTrack *track; HRESULT hr; + UINT i; TRACE("(%p, %lu, %p)\n", segment, start_time, ret_iface); @@ -198,8 +227,73 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time This->start_time = start_time; hr = IDirectMusicSegment_GetStartPoint(segment, &This->start_point); + if (SUCCEEDED(hr)) hr = IDirectMusicSegment_GetLength(segment, &This->end_point); + + for (i = 0; SUCCEEDED(hr); i++) + { + DWORD track_id = ++next_track_id; + struct track_entry *entry; + + if ((hr = IDirectMusicSegment_GetTrack(segment, &GUID_NULL, -1, i, &track)) != S_OK) + { + if (hr == DMUS_E_NOT_FOUND) hr = S_OK; + break; + } + + if (!(entry = malloc(sizeof(*entry)))) + hr = E_OUTOFMEMORY; + else if (SUCCEEDED(hr = IDirectMusicTrack_InitPlay(track, iface, performance, + &entry->state_data, track_id, 0))) + { + entry->track = track; + entry->track_id = track_id; + list_add_tail(&This->tracks, &entry->entry); + } + + if (FAILED(hr)) + { + WARN("Failed to initialize track %p, hr %#lx\n", track, hr); + IDirectMusicTrack_Release(track); + free(entry); + } + } if (SUCCEEDED(hr)) *ret_iface = iface; else IDirectMusicSegmentState_Release(iface); return hr; } + +HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance *performance) +{ + struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); + DWORD track_flags = DMUS_TRACKF_DIRTY | DMUS_TRACKF_START | DMUS_TRACKF_SEEK; + MUSIC_TIME start_time = This->start_point, end_time = This->end_point; + struct track_entry *entry; + HRESULT hr = S_OK; + + LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) + { + if (FAILED(hr = IDirectMusicTrack_Play(entry->track, entry->state_data, start_time, + end_time, 0, track_flags, performance, iface, entry->track_id))) + { + WARN("Failed to play track %p, hr %#lx\n", entry->track, hr); + break; + } + } + + return hr; +} + +HRESULT segment_state_end_play(IDirectMusicSegmentState *iface) +{ + struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); + struct track_entry *entry, *next; + + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &This->tracks, struct track_entry, entry) + { + list_remove(&entry->entry); + track_entry_destroy(entry); + } + + return S_OK; +} diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index f9143176594..cb35ef64ead 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -662,7 +662,7 @@ static HRESULT WINAPI test_track_Play(IDirectMusicTrack *iface, void *state_data ok(state_data == &This->data, "got %p\n", state_data); ok(start_time == 50, "got %lu\n", start_time); ok(end_time == 100, "got %lu\n", end_time); - ok(time_offset < 0, "got %lu\n", time_offset); + todo_wine ok(time_offset < 0, "got %lu\n", time_offset); ok(segment_flags == (DMUS_TRACKF_DIRTY|DMUS_TRACKF_START|DMUS_TRACKF_SEEK), "got %#lx\n", segment_flags); ok(!!performance, "got %p\n", performance); @@ -3990,7 +3990,7 @@ static void test_segment_state(void) IDirectMusicSegmentState_Release(tmp_state); check_track_state(track, downloaded, FALSE); - todo_wine check_track_state(track, initialized, TRUE); + check_track_state(track, initialized, TRUE); /* The track can be removed from the segment */ @@ -4001,10 +4001,10 @@ static void test_segment_state(void) /* This might be timing dependent and if PlaySegment is already * late, the tracks are played synchronously and right away. */ - check_track_state(track, playing, FALSE); + todo_wine check_track_state(track, playing, FALSE); ret = test_track_wait_playing(track, 50); - todo_wine ok(ret == 0, "got %#lx\n", ret); + ok(ret == 0, "got %#lx\n", ret); tmp_segment = (void *)0xdeadbeef; @@ -4064,14 +4064,14 @@ static void test_segment_state(void) check_track_state(track, downloaded, FALSE); - todo_wine check_track_state(track, initialized, TRUE); - todo_wine check_track_state(track, playing, TRUE); + check_track_state(track, initialized, TRUE); + check_track_state(track, playing, TRUE); hr = IDirectMusicPerformance_CloseDown(performance); ok(hr == S_OK, "got %#lx\n", hr); check_track_state(track, downloaded, FALSE); - todo_wine check_track_state(track, initialized, TRUE); + check_track_state(track, initialized, TRUE); check_track_state(track, playing, FALSE); From 316137339769bb0a0dec4ca16e2b1f303b5a4a54 Mon Sep 17 00:00:00 2001 From: Francois Gouget Date: Fri, 6 Oct 2023 09:37:47 +0200 Subject: [PATCH 0984/1148] dmsynth: Add a trailing linefeed to TRACE() messages. (cherry picked from commit 622daeaf89a08a6073fbb2202b57e64e305d5213) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index d87afd245e9..c36196c0407 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -82,8 +82,8 @@ static void dump_connectionlist(CONNECTIONLIST *list) UINT i; TRACE("CONNECTIONLIST:\n"); - TRACE(" - cbSize = %lu", list->cbSize); - TRACE(" - cConnections = %lu", list->cConnections); + TRACE(" - cbSize = %lu\n", list->cbSize); + TRACE(" - cConnections = %lu\n", list->cConnections); for (i = 0; i < list->cConnections; i++) { From 5f565bdfd92230c9a7bc7a097d0a61204546eff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 3 Oct 2023 15:13:28 +0200 Subject: [PATCH 0985/1148] dmime/tests: Queue the message before calling SendPMsg twice. (cherry picked from commit d0c3a0e03d557498195a70693003deae5b3e4588) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index cb35ef64ead..e390f86f1bf 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -2849,7 +2849,7 @@ static void test_performance_pmsg(void) ok(clone != NULL, "got %p\n", clone); msg->mtTime = 500; - msg->dwFlags = DMUS_PMSGF_MUSICTIME; + msg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE; hr = IDirectMusicPerformance_SendPMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_SendPMsg(performance, msg); From cd7ee3318fafa9aab250a7eff3be05180c518d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 10 Oct 2023 23:45:10 +0200 Subject: [PATCH 0986/1148] dmsynth: Import and use FluidSynth 2.3.3. (cherry picked from commit f768d6b31bebc35fbaf751d0cd57c8bd302a8d60) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- configure.ac | 11 + dlls/dmsynth/Makefile.in | 3 +- dlls/dmsynth/synth.c | 11 + libs/fluidsynth/AUTHORS | 157 + libs/fluidsynth/COPYING.md | 7 + libs/fluidsynth/Makefile.in | 42 + libs/fluidsynth/config.h | 271 + libs/fluidsynth/fluid_conv_tables.inc.h | 3915 ++++++++ libs/fluidsynth/fluid_rvoice_dsp_tables.inc.h | 4109 ++++++++ libs/fluidsynth/glib.c | 106 + libs/fluidsynth/glib.h | 107 + libs/fluidsynth/include/fluidsynth.h | 125 + libs/fluidsynth/include/fluidsynth/audio.h | 155 + libs/fluidsynth/include/fluidsynth/event.h | 143 + libs/fluidsynth/include/fluidsynth/gen.h | 133 + libs/fluidsynth/include/fluidsynth/ladspa.h | 68 + libs/fluidsynth/include/fluidsynth/log.h | 97 + libs/fluidsynth/include/fluidsynth/midi.h | 295 + libs/fluidsynth/include/fluidsynth/misc.h | 77 + libs/fluidsynth/include/fluidsynth/mod.h | 104 + libs/fluidsynth/include/fluidsynth/seq.h | 92 + libs/fluidsynth/include/fluidsynth/seqbind.h | 44 + libs/fluidsynth/include/fluidsynth/settings.h | 194 + libs/fluidsynth/include/fluidsynth/sfont.h | 362 + libs/fluidsynth/include/fluidsynth/shell.h | 150 + libs/fluidsynth/include/fluidsynth/synth.h | 552 ++ libs/fluidsynth/include/fluidsynth/types.h | 85 + libs/fluidsynth/include/fluidsynth/version.h | 47 + libs/fluidsynth/include/fluidsynth/voice.h | 76 + libs/fluidsynth/src/bindings/fluid_ladspa.h | 36 + libs/fluidsynth/src/midi/fluid_midi.c | 2851 ++++++ libs/fluidsynth/src/midi/fluid_midi.h | 384 + libs/fluidsynth/src/midi/fluid_midi_router.h | 32 + libs/fluidsynth/src/rvoice/fluid_adsr_env.c | 38 + libs/fluidsynth/src/rvoice/fluid_adsr_env.h | 166 + libs/fluidsynth/src/rvoice/fluid_chorus.c | 1071 +++ libs/fluidsynth/src/rvoice/fluid_chorus.h | 80 + libs/fluidsynth/src/rvoice/fluid_iir_filter.c | 418 + libs/fluidsynth/src/rvoice/fluid_iir_filter.h | 74 + libs/fluidsynth/src/rvoice/fluid_lfo.c | 17 + libs/fluidsynth/src/rvoice/fluid_lfo.h | 74 + libs/fluidsynth/src/rvoice/fluid_phase.h | 113 + libs/fluidsynth/src/rvoice/fluid_rev.c | 1523 +++ libs/fluidsynth/src/rvoice/fluid_rev.h | 91 + libs/fluidsynth/src/rvoice/fluid_rvoice.c | 935 ++ libs/fluidsynth/src/rvoice/fluid_rvoice.h | 231 + libs/fluidsynth/src/rvoice/fluid_rvoice_dsp.c | 636 ++ .../src/rvoice/fluid_rvoice_event.c | 202 + .../src/rvoice/fluid_rvoice_event.h | 114 + .../src/rvoice/fluid_rvoice_mixer.c | 1727 ++++ .../src/rvoice/fluid_rvoice_mixer.h | 87 + libs/fluidsynth/src/sfloader/fluid_defsfont.c | 2372 +++++ libs/fluidsynth/src/sfloader/fluid_defsfont.h | 232 + .../fluidsynth/src/sfloader/fluid_instpatch.h | 14 + .../src/sfloader/fluid_samplecache.c | 313 + .../src/sfloader/fluid_samplecache.h | 37 + libs/fluidsynth/src/sfloader/fluid_sffile.c | 2527 +++++ libs/fluidsynth/src/sfloader/fluid_sffile.h | 194 + libs/fluidsynth/src/sfloader/fluid_sfont.c | 850 ++ libs/fluidsynth/src/sfloader/fluid_sfont.h | 189 + libs/fluidsynth/src/synth/fluid_chan.c | 732 ++ libs/fluidsynth/src/synth/fluid_chan.h | 276 + libs/fluidsynth/src/synth/fluid_event.c | 863 ++ libs/fluidsynth/src/synth/fluid_event.h | 65 + libs/fluidsynth/src/synth/fluid_gen.c | 133 + libs/fluidsynth/src/synth/fluid_gen.h | 67 + libs/fluidsynth/src/synth/fluid_mod.c | 880 ++ libs/fluidsynth/src/synth/fluid_mod.h | 54 + libs/fluidsynth/src/synth/fluid_synth.c | 8445 +++++++++++++++++ libs/fluidsynth/src/synth/fluid_synth.h | 263 + .../src/synth/fluid_synth_monopoly.c | 722 ++ libs/fluidsynth/src/synth/fluid_tuning.c | 190 + libs/fluidsynth/src/synth/fluid_tuning.h | 69 + libs/fluidsynth/src/synth/fluid_voice.c | 2051 ++++ libs/fluidsynth/src/synth/fluid_voice.h | 198 + libs/fluidsynth/src/utils/fluid_conv.c | 333 + libs/fluidsynth/src/utils/fluid_conv.h | 40 + libs/fluidsynth/src/utils/fluid_conv_tables.h | 41 + libs/fluidsynth/src/utils/fluid_hash.c | 1407 +++ libs/fluidsynth/src/utils/fluid_hash.h | 130 + libs/fluidsynth/src/utils/fluid_list.c | 337 + libs/fluidsynth/src/utils/fluid_list.h | 63 + libs/fluidsynth/src/utils/fluid_ringbuffer.c | 90 + libs/fluidsynth/src/utils/fluid_ringbuffer.h | 133 + libs/fluidsynth/src/utils/fluid_settings.c | 2004 ++++ libs/fluidsynth/src/utils/fluid_settings.h | 65 + libs/fluidsynth/src/utils/fluid_sys.c | 1803 ++++ libs/fluidsynth/src/utils/fluid_sys.h | 794 ++ libs/fluidsynth/src/utils/fluidsynth_priv.h | 331 + 89 files changed, 51744 insertions(+), 1 deletion(-) create mode 100644 libs/fluidsynth/AUTHORS create mode 100644 libs/fluidsynth/COPYING.md create mode 100644 libs/fluidsynth/Makefile.in create mode 100644 libs/fluidsynth/config.h create mode 100644 libs/fluidsynth/fluid_conv_tables.inc.h create mode 100644 libs/fluidsynth/fluid_rvoice_dsp_tables.inc.h create mode 100644 libs/fluidsynth/glib.c create mode 100644 libs/fluidsynth/glib.h create mode 100644 libs/fluidsynth/include/fluidsynth.h create mode 100644 libs/fluidsynth/include/fluidsynth/audio.h create mode 100644 libs/fluidsynth/include/fluidsynth/event.h create mode 100644 libs/fluidsynth/include/fluidsynth/gen.h create mode 100644 libs/fluidsynth/include/fluidsynth/ladspa.h create mode 100644 libs/fluidsynth/include/fluidsynth/log.h create mode 100644 libs/fluidsynth/include/fluidsynth/midi.h create mode 100644 libs/fluidsynth/include/fluidsynth/misc.h create mode 100644 libs/fluidsynth/include/fluidsynth/mod.h create mode 100644 libs/fluidsynth/include/fluidsynth/seq.h create mode 100644 libs/fluidsynth/include/fluidsynth/seqbind.h create mode 100644 libs/fluidsynth/include/fluidsynth/settings.h create mode 100644 libs/fluidsynth/include/fluidsynth/sfont.h create mode 100644 libs/fluidsynth/include/fluidsynth/shell.h create mode 100644 libs/fluidsynth/include/fluidsynth/synth.h create mode 100644 libs/fluidsynth/include/fluidsynth/types.h create mode 100644 libs/fluidsynth/include/fluidsynth/version.h create mode 100644 libs/fluidsynth/include/fluidsynth/voice.h create mode 100644 libs/fluidsynth/src/bindings/fluid_ladspa.h create mode 100644 libs/fluidsynth/src/midi/fluid_midi.c create mode 100644 libs/fluidsynth/src/midi/fluid_midi.h create mode 100644 libs/fluidsynth/src/midi/fluid_midi_router.h create mode 100644 libs/fluidsynth/src/rvoice/fluid_adsr_env.c create mode 100644 libs/fluidsynth/src/rvoice/fluid_adsr_env.h create mode 100644 libs/fluidsynth/src/rvoice/fluid_chorus.c create mode 100644 libs/fluidsynth/src/rvoice/fluid_chorus.h create mode 100644 libs/fluidsynth/src/rvoice/fluid_iir_filter.c create mode 100644 libs/fluidsynth/src/rvoice/fluid_iir_filter.h create mode 100644 libs/fluidsynth/src/rvoice/fluid_lfo.c create mode 100644 libs/fluidsynth/src/rvoice/fluid_lfo.h create mode 100644 libs/fluidsynth/src/rvoice/fluid_phase.h create mode 100644 libs/fluidsynth/src/rvoice/fluid_rev.c create mode 100644 libs/fluidsynth/src/rvoice/fluid_rev.h create mode 100644 libs/fluidsynth/src/rvoice/fluid_rvoice.c create mode 100644 libs/fluidsynth/src/rvoice/fluid_rvoice.h create mode 100644 libs/fluidsynth/src/rvoice/fluid_rvoice_dsp.c create mode 100644 libs/fluidsynth/src/rvoice/fluid_rvoice_event.c create mode 100644 libs/fluidsynth/src/rvoice/fluid_rvoice_event.h create mode 100644 libs/fluidsynth/src/rvoice/fluid_rvoice_mixer.c create mode 100644 libs/fluidsynth/src/rvoice/fluid_rvoice_mixer.h create mode 100644 libs/fluidsynth/src/sfloader/fluid_defsfont.c create mode 100644 libs/fluidsynth/src/sfloader/fluid_defsfont.h create mode 100644 libs/fluidsynth/src/sfloader/fluid_instpatch.h create mode 100644 libs/fluidsynth/src/sfloader/fluid_samplecache.c create mode 100644 libs/fluidsynth/src/sfloader/fluid_samplecache.h create mode 100644 libs/fluidsynth/src/sfloader/fluid_sffile.c create mode 100644 libs/fluidsynth/src/sfloader/fluid_sffile.h create mode 100644 libs/fluidsynth/src/sfloader/fluid_sfont.c create mode 100644 libs/fluidsynth/src/sfloader/fluid_sfont.h create mode 100644 libs/fluidsynth/src/synth/fluid_chan.c create mode 100644 libs/fluidsynth/src/synth/fluid_chan.h create mode 100644 libs/fluidsynth/src/synth/fluid_event.c create mode 100644 libs/fluidsynth/src/synth/fluid_event.h create mode 100644 libs/fluidsynth/src/synth/fluid_gen.c create mode 100644 libs/fluidsynth/src/synth/fluid_gen.h create mode 100644 libs/fluidsynth/src/synth/fluid_mod.c create mode 100644 libs/fluidsynth/src/synth/fluid_mod.h create mode 100644 libs/fluidsynth/src/synth/fluid_synth.c create mode 100644 libs/fluidsynth/src/synth/fluid_synth.h create mode 100644 libs/fluidsynth/src/synth/fluid_synth_monopoly.c create mode 100644 libs/fluidsynth/src/synth/fluid_tuning.c create mode 100644 libs/fluidsynth/src/synth/fluid_tuning.h create mode 100644 libs/fluidsynth/src/synth/fluid_voice.c create mode 100644 libs/fluidsynth/src/synth/fluid_voice.h create mode 100644 libs/fluidsynth/src/utils/fluid_conv.c create mode 100644 libs/fluidsynth/src/utils/fluid_conv.h create mode 100644 libs/fluidsynth/src/utils/fluid_conv_tables.h create mode 100644 libs/fluidsynth/src/utils/fluid_hash.c create mode 100644 libs/fluidsynth/src/utils/fluid_hash.h create mode 100644 libs/fluidsynth/src/utils/fluid_list.c create mode 100644 libs/fluidsynth/src/utils/fluid_list.h create mode 100644 libs/fluidsynth/src/utils/fluid_ringbuffer.c create mode 100644 libs/fluidsynth/src/utils/fluid_ringbuffer.h create mode 100644 libs/fluidsynth/src/utils/fluid_settings.c create mode 100644 libs/fluidsynth/src/utils/fluid_settings.h create mode 100644 libs/fluidsynth/src/utils/fluid_sys.c create mode 100644 libs/fluidsynth/src/utils/fluid_sys.h create mode 100644 libs/fluidsynth/src/utils/fluidsynth_priv.h diff --git a/configure.ac b/configure.ac index c58fced1539..360d4ec5e25 100644 --- a/configure.ac +++ b/configure.ac @@ -1029,6 +1029,15 @@ then WINE_NOTICE([FAudio ${notice_platform}MinGW development files not found (or too old); using bundled version.]) fi + WINE_MINGW_PACKAGE_FLAGS(FLUIDSYNTH,[fluidsynth],[-lfluidsynth], + [WINE_CHECK_MINGW_HEADER(fluidsynth.h, + [WINE_CHECK_MINGW_LIB(fluidsynth,new_fluid_synth,[:],[FLUIDSYNTH_PE_CFLAGS=""; FLUIDSYNTH_PE_LIBS=""],[$FLUIDSYNTH_PE_LIBS])], + [FLUIDSYNTH_PE_CFLAGS=""; FLUIDSYNTH_PE_LIBS=""])]) + if test "x$FLUIDSYNTH_PE_LIBS" = "x" + then + WINE_NOTICE([Fluidsynth ${notice_platform}MinGW development files not found (or too old); using bundled version.]) + fi + WINE_MINGW_PACKAGE_FLAGS(JPEG,[libjpeg],, [WINE_CHECK_MINGW_HEADER(jpeglib.h, [WINE_CHECK_MINGW_LIB(jpeg,jpeg_start_decompress,[:],[JPEG_PE_CFLAGS=""; JPEG_PE_LIBS=""],[$JPEG_PE_LIBS])], @@ -1139,6 +1148,7 @@ then fi WINE_EXTLIB_FLAGS(FAUDIO, faudio, "faudio mfplat mfreadwrite mfuuid propsys", "-I\$(top_srcdir)/libs/faudio/include") +WINE_EXTLIB_FLAGS(FLUIDSYNTH, fluidsynth, "fluidsynth", "-I\$(top_srcdir)/libs/fluidsynth/include") WINE_EXTLIB_FLAGS(GSM, gsm, gsm, "-I\$(top_srcdir)/libs/gsm/inc") WINE_EXTLIB_FLAGS(JPEG, jpeg, jpeg, "-I\$(top_srcdir)/libs/jpeg") WINE_EXTLIB_FLAGS(JXR, jxr, jxr, "-I\$(top_srcdir)/libs/jxr/jxrgluelib -I\$(top_srcdir)/libs/jxr/image/sys") @@ -3328,6 +3338,7 @@ WINE_CONFIG_MAKEFILE(libs/dxerr8) WINE_CONFIG_MAKEFILE(libs/dxerr9) WINE_CONFIG_MAKEFILE(libs/dxguid) WINE_CONFIG_MAKEFILE(libs/faudio) +WINE_CONFIG_MAKEFILE(libs/fluidsynth) WINE_CONFIG_MAKEFILE(libs/gsm) WINE_CONFIG_MAKEFILE(libs/jpeg) WINE_CONFIG_MAKEFILE(libs/jxr) diff --git a/dlls/dmsynth/Makefile.in b/dlls/dmsynth/Makefile.in index 276597db5ad..7c6d2db721b 100644 --- a/dlls/dmsynth/Makefile.in +++ b/dlls/dmsynth/Makefile.in @@ -1,5 +1,6 @@ MODULE = dmsynth.dll -IMPORTS = dxguid uuid ole32 advapi32 +IMPORTS = $(FLUIDSYNTH_PE_LIBS) dxguid uuid ole32 advapi32 user32 +EXTRAINCL = $(FLUIDSYNTH_PE_CFLAGS) C_SRCS = \ dmsynth_main.c \ diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index c36196c0407..2b5a0905685 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -29,6 +29,8 @@ #include "dmsynth_private.h" +#include + WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); static void dump_dmus_instrument(DMUS_INSTRUMENT *instrument) @@ -231,6 +233,8 @@ struct synth struct list instruments; struct list waves; + + fluid_settings_t *fluid_settings; }; static inline struct synth *impl_from_IDirectMusicSynth8(IDirectMusicSynth8 *iface) @@ -302,6 +306,7 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) wave_release(wave); } + delete_fluid_settings(This->fluid_settings); free(This); } @@ -1087,7 +1092,13 @@ HRESULT synth_create(IUnknown **ret_iface) list_init(&obj->instruments); list_init(&obj->waves); + if (!(obj->fluid_settings = new_fluid_settings())) goto failed; + TRACE("Created DirectMusicSynth %p\n", obj); *ret_iface = (IUnknown *)&obj->IDirectMusicSynth8_iface; return S_OK; + +failed: + free(obj); + return E_OUTOFMEMORY; } diff --git a/libs/fluidsynth/AUTHORS b/libs/fluidsynth/AUTHORS new file mode 100644 index 00000000000..151b7073355 --- /dev/null +++ b/libs/fluidsynth/AUTHORS @@ -0,0 +1,157 @@ +[:Team:] +Current development team + +Tom Moebert + + +Former development team + +Josh Green +Pedro Lopez-Cabanillas +David Henningsson + + +[:Idea:] + +* Samuel Bianchini, Peter Hanappe and Johnathan Lee + + +[:Development:] + +Many people contributed to FluidSynth, sent suggestions or bug +fixes. The project was started by Peter Hanappe who is the main +author. Josh Green is the current maintainer. Below you'll find a +summary of contributions. + + +* Peter Hanappe. Initiated the project. files: stuck his nose in all + files. + +* Josh Green is the former maintainer and contributed a lot of code + directly or indirectly through the Swami and Smurf code base. + The SoundFont loader is completely based on his code. He also wrote + the alsa sequencer driver. He made many changes and bug fixes, + but above all, he's one of the driving forces behind the synthesizer. + He also created the current FluidSynth graphic logo with Blender + (the blue waves with FluidSynth letters partially submerged). + +* Markus Nentwig (re-)designed the resonant filter, the chorus, the + LADSPA subsystem, the MIDI router, optimized for SSE, made many + changes and bug fixes and got the synthesizer to actually work. Most + importantly, he used it on stage to make music. + +* S. Christian Collins did much testing of FluidSynth in regards to + EMU10K1 compatibility and provided many synthesis fixes in that regard. + +* Stephane Letz from Grame wrote most of the MidiShare driver, all of + the PortAudio driver, ported iiwusynth to MacOS X, and sent in many + fixes. files: iiwu_midishare.c, iiwu_portaudio.c + +* Antoine Schmitt added the sequencer support, support for sample + loading (RAM Sfont), developed the + MacroMedia Director Xtra, and send in many many bug reports. Thanks + to Antoine, the synthesizer finds its way to multi-media + developers. files: in bindings/director/ and iiwu_seq.{c,h}, + iiwu_event.{c,h}, iiwu_event_priv.h, iiwu_seqbind.{c,h}, + iiwu_ramsfont.{c,h} + +* Bob Ham added the code for "bank select" MIDI messages and send code + to define the synth's ALSA sequencer client name. files: + iiwu_midi.c, iiwu_alsa.c, iiwusynth.c, iiwusynth.h. + +* Tim Goetze sent many patches and implemented the all_notes_off. He + also sent his code for the new ALSA driver. files: iiwu_synth.c, + iiwu_chan.c, iiwu_voice.c, iiwu_alsa.c + +* Norbert Schnell from Ircam's jMax Team wrote most of the jMax/FTS + interface in a record time. He also pointed me to the technique of + using a lookup table for the interpolation coefficients. file: + iiwu_fts.c, iiwu_synth.c + +* The initial alsa driver was based on the jMax alsa driver by + Francois Dechelle and his Real-time Team at Ircam + (https://www.ircam.fr/jmax). The jMax code was based upon Ardour's + alsa_device.cc by Paul Barton-Davis. file: iiwu_alsa.c + +* Code was borrowed from the glib library to the smurf files. The goal was + to make iiwusynth independent from any library for maximum + portability. + +* David Henningsson added code for fast rendering of MIDI files, + rewrote the thread safety for 1.1.2, and fixed many bugs. + +* The midi device uses code from jMax's alsarawmidi.c file and from + Smurf's midi_alsaraw.c by Josh Green. file: iiwu_alsa.c + +* The reverb algorithm was written by Jezar + (https://www.dreampoint.co.uk). His code is public domain. The code + was translated to C by Peter Hanappe. file: iiwu_synth.c + +* The original code for the chorus effect was written by Juergen + Mueller and sundry contributors. + +* Bob Ham added LADCCA support. + +* Ebrahim Mayat made big efforts for compiling and running FluidSynth + on MacOS X. He also wrote the README-OSX file. + +* Martin Uddén's midi package was used. His files are integrated into + the iiwu_midi file. Martin Uddén file: + iiwu_midi.c + +* Ken Ellinwood send in a patch to add bank offsets to SoundFonts. An + adapted version was integrated in the source code. files: + fluid_cmd.c, fluidsynth/synth.h, fluid_synth.c. + +* Some interpolation algorithms were used that were found in + the music-dsp archives (http://www.smartelectronix.com/musicdsp). + They were written by Joshua Scholar and others. file: iiwu_synth.c + +* Macros to {increment,decrement} the 64-bit fixed point phase were + borrowed from Mozilla's macros to handle the Long-long type (64-bit + signed integer type). Mozilla NSPR library, www.mozilla.org. file: + iiwu_phase.h + +* KO Myung-Hun for OS/2 support with Dart audio driver. + +* Pedro Lopez-Cabanillas wrote the CoreMIDI driver for MacOSX, the CMake based + build system, revised the doxygen documentation, sequencer examples, fixes. + +* Matt Giuca improved the midi player by letting it load midi files from RAM, + and by making it handle EOT events. + +* Tom Moebert (fluidsynth's maintainer since Jun 2017) cleaned up and refactored + fluidsynth's API and revised its documentation, added support for 24 bit sample + soundfonts, added support for DLS soundfonts, fixed various bugs, implemented + unit tests and CI builds for Windows, Linux, MacOSX and BSD. + +* Fabian Greffrath added initial support of vorbis-compressed sf3 sound fonts. + +* Growing list of individuals who contributed bug fixes, corrections and minor features: +Nicolas Boulicault for ALSA sequencer midi.portname setting. +Werner Schweer +Dave Philips +Anthony Green +Jake Commander +Fernando Pablo Lopez-Lezcano +Raoul Bonisch +Sergey Pavlishin +Eric Van Buggenhaut +Ken Ellinwood +Takashi Iwai +Bob Ham +Gerald Pye +Rui Nuno Capela +Frieder Bürzele +Henri Manson +Mihail Zenkov +Paul Millar +Nick Daly +David Hilvert +Bernat Arlandis i Mañó +Sven Meier +Marcus Weseloh +Jean-jacques Ceresa +Vladimir Davidovich +Tamás Korodi +Evan Miller diff --git a/libs/fluidsynth/COPYING.md b/libs/fluidsynth/COPYING.md new file mode 100644 index 00000000000..5934164a2f9 --- /dev/null +++ b/libs/fluidsynth/COPYING.md @@ -0,0 +1,7 @@ +The source code for FluidSynth is distributed under the terms of the +[GNU Lesser General Public License](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html), +see the [LICENSE](https://github.com/FluidSynth/fluidsynth/blob/master/LICENSE) file. + +To better understand the conditions how FluidSynth can be used in +e.g. commercial or closed-source projects, please refer to the +[LicensingFAQ in our wiki](https://github.com/FluidSynth/fluidsynth/wiki/LicensingFAQ). diff --git a/libs/fluidsynth/Makefile.in b/libs/fluidsynth/Makefile.in new file mode 100644 index 00000000000..489d296754b --- /dev/null +++ b/libs/fluidsynth/Makefile.in @@ -0,0 +1,42 @@ +EXTLIB = libfluidsynth.a +EXTRADEFS = -DNDEBUG -DWITH_PROFILING +EXTRAINCL = -I$(srcdir)/src \ + -I$(srcdir)/src/bindings \ + -I$(srcdir)/src/drivers \ + -I$(srcdir)/src/midi \ + -I$(srcdir)/src/rvoice \ + -I$(srcdir)/src/sfloader \ + -I$(srcdir)/src/synth \ + -I$(srcdir)/src/utils \ + $(FLUIDSYNTH_PE_CFLAGS) + +C_SRCS = \ + glib.c \ + src/midi/fluid_midi.c \ + src/rvoice/fluid_adsr_env.c \ + src/rvoice/fluid_chorus.c \ + src/rvoice/fluid_iir_filter.c \ + src/rvoice/fluid_lfo.c \ + src/rvoice/fluid_rev.c \ + src/rvoice/fluid_rvoice.c \ + src/rvoice/fluid_rvoice_dsp.c \ + src/rvoice/fluid_rvoice_event.c \ + src/rvoice/fluid_rvoice_mixer.c \ + src/sfloader/fluid_defsfont.c \ + src/sfloader/fluid_samplecache.c \ + src/sfloader/fluid_sffile.c \ + src/sfloader/fluid_sfont.c \ + src/synth/fluid_chan.c \ + src/synth/fluid_event.c \ + src/synth/fluid_gen.c \ + src/synth/fluid_mod.c \ + src/synth/fluid_synth.c \ + src/synth/fluid_synth_monopoly.c \ + src/synth/fluid_tuning.c \ + src/synth/fluid_voice.c \ + src/utils/fluid_conv.c \ + src/utils/fluid_hash.c \ + src/utils/fluid_list.c \ + src/utils/fluid_ringbuffer.c \ + src/utils/fluid_settings.c \ + src/utils/fluid_sys.c diff --git a/libs/fluidsynth/config.h b/libs/fluidsynth/config.h new file mode 100644 index 00000000000..2170ad098ed --- /dev/null +++ b/libs/fluidsynth/config.h @@ -0,0 +1,271 @@ +#ifndef CONFIG_H +#define CONFIG_H + +/* Define to enable ALSA driver */ +/* #undef ALSA_SUPPORT TRUE */ + +/* Define to activate sound output to files */ +/* #undef AUFILE_SUPPORT 1 */ + +/* whether or not we are supporting CoreAudio */ +/* #undef COREAUDIO_SUPPORT */ + +/* whether or not we are supporting CoreMIDI */ +/* #undef COREMIDI_SUPPORT */ + +/* whether or not we are supporting DART */ +/* #undef DART_SUPPORT */ + +/* Define if building for Mac OS X Darwin */ +/* #undef DARWIN */ + +/* Define if D-Bus support is enabled */ +/* #undef DBUS_SUPPORT 1 */ + +/* Soundfont to load automatically in some use cases */ +/* #undef DEFAULT_SOUNDFONT "/usr/local/share/soundfonts/default.sf2" */ + +/* Define to enable FPE checks */ +/* #undef FPE_CHECK */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARPA_INET_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FCNTL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_IO_H 1 + +/* whether or not we are supporting lash */ +/* #undef HAVE_LASH */ + +/* Define if systemd support is enabled */ +/* #undef SYSTEMD_SUPPORT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_SOUNDCARD_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MACHINE_SOUNDCARD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_IN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_TCP_H */ + +/* Define if compiling the mixer with multi-thread support */ +#define ENABLE_MIXER_THREADS 1 + +/* Define if compiling with openMP to enable parallel audio rendering */ +/* #undef HAVE_OPENMP */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PTHREAD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOUNDCARD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_TIME_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UNISTD_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GETOPT_H */ + +/* Define to 1 if you have the inet_ntop() function. */ +/* #undef HAVE_INETNTOP */ + +/* Define to enable JACK driver */ +/* #undef JACK_SUPPORT */ + +/* Define to enable PipeWire driver */ +/* #undef PIPEWIRE_SUPPORT */ + +/* Include the LADSPA Fx unit */ +/* #undef LADSPA */ + +/* Define to enable IPV6 support */ +/* #undef IPV6_SUPPORT */ + +/* Define to enable network support */ +/* #undef NETWORK_SUPPORT */ + +/* Defined when fluidsynth is build in an automated environment, where no MSVC++ Runtime Debug Assertion dialogs should pop up */ +#define NO_GUI 1 + +/* libinstpatch for DLS and GIG */ +/* #undef LIBINSTPATCH_SUPPORT */ + +/* libsndfile has ogg vorbis support */ +/* #undef LIBSNDFILE_HASVORBIS */ + +/* Define to enable libsndfile support */ +/* #undef LIBSNDFILE_SUPPORT */ + +/* Define to enable MidiShare driver */ +/* #undef MIDISHARE_SUPPORT */ + +/* Define if using the MinGW32 environment */ +#define MINGW32 1 + +/* Define to enable OSS driver */ +/* #undef OSS_SUPPORT */ + +/* Define to enable OPENSLES driver */ +/* #undef OPENSLES_SUPPORT */ + +/* Define to enable Oboe driver */ +/* #undef OBOE_SUPPORT */ + +/* Name of package */ +#define PACKAGE "fluidsynth" + +/* Define to the address where bug reports for this package should be sent. */ +/* #undef PACKAGE_BUGREPORT */ + +/* Define to the full name of this package. */ +/* #undef PACKAGE_NAME */ + +/* Define to the full name and version of this package. */ +/* #undef PACKAGE_STRING */ + +/* Define to the one symbol short name of this package. */ +/* #undef PACKAGE_TARNAME */ + +/* Define to the version of this package. */ +/* #undef PACKAGE_VERSION */ + +/* Define to enable PortAudio driver */ +/* #undef PORTAUDIO_SUPPORT */ + +/* Define to enable PulseAudio driver */ +/* #undef PULSE_SUPPORT */ + +/* Define to enable DirectSound driver */ +/* #undef DSOUND_SUPPORT 1 */ + +/* Define to enable Windows WASAPI driver */ +/* #undef WASAPI_SUPPORT 1 */ + +/* Define to enable Windows WaveOut driver */ +/* #undef WAVEOUT_SUPPORT 1 */ + +/* Define to enable Windows MIDI driver */ +/* #undef WINMIDI_SUPPORT */ + +/* Define to enable SDL2 audio driver */ +/* #undef SDL2_SUPPORT */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Soundfont to load for unit testing */ +/* #undef TEST_SOUNDFONT */ + +/* Soundfont to load for UTF-8 unit testing */ +/* #undef TEST_SOUNDFONT_UTF8_1 */ +/* #undef TEST_SOUNDFONT_UTF8_2 */ +/* #undef TEST_MIDI_UTF8 */ + +/* SF3 Soundfont to load for unit testing */ +/* #undef TEST_SOUNDFONT_SF3 */ + +/* Define to enable SIGFPE assertions */ +/* #undef TRAP_ON_FPE */ + +/* Define to do all DSP in single floating point precision */ +/* #undef WITH_FLOAT */ + +/* Define to profile the DSP code */ +/* #undef WITH_PROFILING */ + +/* Define to use the readline library for line editing */ +/* #undef READLINE_SUPPORT */ + +/* Define if the compiler supports VLA */ +#define SUPPORTS_VLA 1 + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to 1 if you have the sinf() function. */ +#define HAVE_SINF 1 + +/* Define to 1 if you have the cosf() function. */ +#define HAVE_COSF 1 + +/* Define to 1 if you have the fabsf() function. */ +#define HAVE_FABSF 1 + +/* Define to 1 if you have the powf() function. */ +#define HAVE_POWF 1 + +/* Define to 1 if you have the sqrtf() function. */ +#define HAVE_SQRTF 1 + +/* Define to 1 if you have the logf() function. */ +#define HAVE_LOGF 1 + +/* Define to 1 if you have the socklen_t type. */ +/* #undef HAVE_SOCKLEN_T */ + +#endif /* CONFIG_H */ diff --git a/libs/fluidsynth/fluid_conv_tables.inc.h b/libs/fluidsynth/fluid_conv_tables.inc.h new file mode 100644 index 00000000000..51575e86598 --- /dev/null +++ b/libs/fluidsynth/fluid_conv_tables.inc.h @@ -0,0 +1,3915 @@ +/* THIS FILE HAS BEEN AUTOMATICALLY GENERATED. DO NOT EDIT. */ + +static const fluid_real_t fluid_ct2hz_tab[1200] = { + 6.875000000000000e+00, /* 0 */ + 6.878972302857565e+00, /* 1 */ + 6.882946900870038e+00, /* 2 */ + 6.886923795363534e+00, /* 3 */ + 6.890902987664938e+00, /* 4 */ + 6.894884479101899e+00, /* 5 */ + 6.898868271002832e+00, /* 6 */ + 6.902854364696921e+00, /* 7 */ + 6.906842761514119e+00, /* 8 */ + 6.910833462785147e+00, /* 9 */ + 6.914826469841493e+00, /* 10 */ + 6.918821784015415e+00, /* 11 */ + 6.922819406639942e+00, /* 12 */ + 6.926819339048873e+00, /* 13 */ + 6.930821582576776e+00, /* 14 */ + 6.934826138558993e+00, /* 15 */ + 6.938833008331635e+00, /* 16 */ + 6.942842193231585e+00, /* 17 */ + 6.946853694596501e+00, /* 18 */ + 6.950867513764811e+00, /* 19 */ + 6.954883652075717e+00, /* 20 */ + 6.958902110869197e+00, /* 21 */ + 6.962922891485999e+00, /* 22 */ + 6.966945995267650e+00, /* 23 */ + 6.970971423556450e+00, /* 24 */ + 6.974999177695475e+00, /* 25 */ + 6.979029259028576e+00, /* 26 */ + 6.983061668900382e+00, /* 27 */ + 6.987096408656298e+00, /* 28 */ + 6.991133479642508e+00, /* 29 */ + 6.995172883205969e+00, /* 30 */ + 6.999214620694422e+00, /* 31 */ + 7.003258693456385e+00, /* 32 */ + 7.007305102841153e+00, /* 33 */ + 7.011353850198804e+00, /* 34 */ + 7.015404936880191e+00, /* 35 */ + 7.019458364236954e+00, /* 36 */ + 7.023514133621508e+00, /* 37 */ + 7.027572246387055e+00, /* 38 */ + 7.031632703887573e+00, /* 39 */ + 7.035695507477827e+00, /* 40 */ + 7.039760658513363e+00, /* 41 */ + 7.043828158350510e+00, /* 42 */ + 7.047898008346380e+00, /* 43 */ + 7.051970209858872e+00, /* 44 */ + 7.056044764246666e+00, /* 45 */ + 7.060121672869229e+00, /* 46 */ + 7.064200937086813e+00, /* 47 */ + 7.068282558260457e+00, /* 48 */ + 7.072366537751985e+00, /* 49 */ + 7.076452876924008e+00, /* 50 */ + 7.080541577139924e+00, /* 51 */ + 7.084632639763921e+00, /* 52 */ + 7.088726066160973e+00, /* 53 */ + 7.092821857696842e+00, /* 54 */ + 7.096920015738083e+00, /* 55 */ + 7.101020541652035e+00, /* 56 */ + 7.105123436806832e+00, /* 57 */ + 7.109228702571396e+00, /* 58 */ + 7.113336340315441e+00, /* 59 */ + 7.117446351409471e+00, /* 60 */ + 7.121558737224782e+00, /* 61 */ + 7.125673499133464e+00, /* 62 */ + 7.129790638508400e+00, /* 63 */ + 7.133910156723263e+00, /* 64 */ + 7.138032055152522e+00, /* 65 */ + 7.142156335171443e+00, /* 66 */ + 7.146282998156079e+00, /* 67 */ + 7.150412045483285e+00, /* 68 */ + 7.154543478530709e+00, /* 69 */ + 7.158677298676793e+00, /* 70 */ + 7.162813507300782e+00, /* 71 */ + 7.166952105782710e+00, /* 72 */ + 7.171093095503412e+00, /* 73 */ + 7.175236477844522e+00, /* 74 */ + 7.179382254188470e+00, /* 75 */ + 7.183530425918486e+00, /* 76 */ + 7.187680994418599e+00, /* 77 */ + 7.191833961073638e+00, /* 78 */ + 7.195989327269232e+00, /* 79 */ + 7.200147094391808e+00, /* 80 */ + 7.204307263828600e+00, /* 81 */ + 7.208469836967637e+00, /* 82 */ + 7.212634815197754e+00, /* 83 */ + 7.216802199908588e+00, /* 84 */ + 7.220971992490576e+00, /* 85 */ + 7.225144194334964e+00, /* 86 */ + 7.229318806833796e+00, /* 87 */ + 7.233495831379925e+00, /* 88 */ + 7.237675269367005e+00, /* 89 */ + 7.241857122189496e+00, /* 90 */ + 7.246041391242668e+00, /* 91 */ + 7.250228077922589e+00, /* 92 */ + 7.254417183626143e+00, /* 93 */ + 7.258608709751013e+00, /* 94 */ + 7.262802657695695e+00, /* 95 */ + 7.266999028859490e+00, /* 96 */ + 7.271197824642510e+00, /* 97 */ + 7.275399046445672e+00, /* 98 */ + 7.279602695670708e+00, /* 99 */ + 7.283808773720155e+00, /* 100 */ + 7.288017281997362e+00, /* 101 */ + 7.292228221906491e+00, /* 102 */ + 7.296441594852512e+00, /* 103 */ + 7.300657402241209e+00, /* 104 */ + 7.304875645479175e+00, /* 105 */ + 7.309096325973822e+00, /* 106 */ + 7.313319445133367e+00, /* 107 */ + 7.317545004366849e+00, /* 108 */ + 7.321773005084116e+00, /* 109 */ + 7.326003448695830e+00, /* 110 */ + 7.330236336613471e+00, /* 111 */ + 7.334471670249333e+00, /* 112 */ + 7.338709451016527e+00, /* 113 */ + 7.342949680328980e+00, /* 114 */ + 7.347192359601434e+00, /* 115 */ + 7.351437490249451e+00, /* 116 */ + 7.355685073689412e+00, /* 117 */ + 7.359935111338512e+00, /* 118 */ + 7.364187604614767e+00, /* 119 */ + 7.368442554937015e+00, /* 120 */ + 7.372699963724910e+00, /* 121 */ + 7.376959832398928e+00, /* 122 */ + 7.381222162380364e+00, /* 123 */ + 7.385486955091339e+00, /* 124 */ + 7.389754211954788e+00, /* 125 */ + 7.394023934394475e+00, /* 126 */ + 7.398296123834983e+00, /* 127 */ + 7.402570781701721e+00, /* 128 */ + 7.406847909420918e+00, /* 129 */ + 7.411127508419629e+00, /* 130 */ + 7.415409580125734e+00, /* 131 */ + 7.419694125967937e+00, /* 132 */ + 7.423981147375768e+00, /* 133 */ + 7.428270645779583e+00, /* 134 */ + 7.432562622610564e+00, /* 135 */ + 7.436857079300720e+00, /* 136 */ + 7.441154017282888e+00, /* 137 */ + 7.445453437990733e+00, /* 138 */ + 7.449755342858746e+00, /* 139 */ + 7.454059733322251e+00, /* 140 */ + 7.458366610817398e+00, /* 141 */ + 7.462675976781168e+00, /* 142 */ + 7.466987832651371e+00, /* 143 */ + 7.471302179866649e+00, /* 144 */ + 7.475619019866476e+00, /* 145 */ + 7.479938354091158e+00, /* 146 */ + 7.484260183981829e+00, /* 147 */ + 7.488584510980460e+00, /* 148 */ + 7.492911336529853e+00, /* 149 */ + 7.497240662073646e+00, /* 150 */ + 7.501572489056309e+00, /* 151 */ + 7.505906818923147e+00, /* 152 */ + 7.510243653120298e+00, /* 153 */ + 7.514582993094741e+00, /* 154 */ + 7.518924840294288e+00, /* 155 */ + 7.523269196167584e+00, /* 156 */ + 7.527616062164117e+00, /* 157 */ + 7.531965439734210e+00, /* 158 */ + 7.536317330329021e+00, /* 159 */ + 7.540671735400553e+00, /* 160 */ + 7.545028656401643e+00, /* 161 */ + 7.549388094785967e+00, /* 162 */ + 7.553750052008045e+00, /* 163 */ + 7.558114529523233e+00, /* 164 */ + 7.562481528787732e+00, /* 165 */ + 7.566851051258580e+00, /* 166 */ + 7.571223098393661e+00, /* 167 */ + 7.575597671651699e+00, /* 168 */ + 7.579974772492260e+00, /* 169 */ + 7.584354402375757e+00, /* 170 */ + 7.588736562763443e+00, /* 171 */ + 7.593121255117417e+00, /* 172 */ + 7.597508480900622e+00, /* 173 */ + 7.601898241576849e+00, /* 174 */ + 7.606290538610729e+00, /* 175 */ + 7.610685373467746e+00, /* 176 */ + 7.615082747614226e+00, /* 177 */ + 7.619482662517345e+00, /* 178 */ + 7.623885119645124e+00, /* 179 */ + 7.628290120466435e+00, /* 180 */ + 7.632697666450996e+00, /* 181 */ + 7.637107759069377e+00, /* 182 */ + 7.641520399792996e+00, /* 183 */ + 7.645935590094122e+00, /* 184 */ + 7.650353331445872e+00, /* 185 */ + 7.654773625322218e+00, /* 186 */ + 7.659196473197983e+00, /* 187 */ + 7.663621876548839e+00, /* 188 */ + 7.668049836851313e+00, /* 189 */ + 7.672480355582785e+00, /* 190 */ + 7.676913434221489e+00, /* 191 */ + 7.681349074246512e+00, /* 192 */ + 7.685787277137797e+00, /* 193 */ + 7.690228044376139e+00, /* 194 */ + 7.694671377443195e+00, /* 195 */ + 7.699117277821469e+00, /* 196 */ + 7.703565746994330e+00, /* 197 */ + 7.708016786445998e+00, /* 198 */ + 7.712470397661556e+00, /* 199 */ + 7.716926582126939e+00, /* 200 */ + 7.721385341328946e+00, /* 201 */ + 7.725846676755233e+00, /* 202 */ + 7.730310589894314e+00, /* 203 */ + 7.734777082235564e+00, /* 204 */ + 7.739246155269221e+00, /* 205 */ + 7.743717810486381e+00, /* 206 */ + 7.748192049379002e+00, /* 207 */ + 7.752668873439905e+00, /* 208 */ + 7.757148284162773e+00, /* 209 */ + 7.761630283042153e+00, /* 210 */ + 7.766114871573452e+00, /* 211 */ + 7.770602051252947e+00, /* 212 */ + 7.775091823577775e+00, /* 213 */ + 7.779584190045939e+00, /* 214 */ + 7.784079152156307e+00, /* 215 */ + 7.788576711408616e+00, /* 216 */ + 7.793076869303465e+00, /* 217 */ + 7.797579627342324e+00, /* 218 */ + 7.802084987027528e+00, /* 219 */ + 7.806592949862282e+00, /* 220 */ + 7.811103517350658e+00, /* 221 */ + 7.815616690997596e+00, /* 222 */ + 7.820132472308910e+00, /* 223 */ + 7.824650862791279e+00, /* 224 */ + 7.829171863952255e+00, /* 225 */ + 7.833695477300261e+00, /* 226 */ + 7.838221704344591e+00, /* 227 */ + 7.842750546595412e+00, /* 228 */ + 7.847282005563763e+00, /* 229 */ + 7.851816082761554e+00, /* 230 */ + 7.856352779701573e+00, /* 231 */ + 7.860892097897477e+00, /* 232 */ + 7.865434038863802e+00, /* 233 */ + 7.869978604115957e+00, /* 234 */ + 7.874525795170227e+00, /* 235 */ + 7.879075613543772e+00, /* 236 */ + 7.883628060754630e+00, /* 237 */ + 7.888183138321715e+00, /* 238 */ + 7.892740847764820e+00, /* 239 */ + 7.897301190604615e+00, /* 240 */ + 7.901864168362650e+00, /* 241 */ + 7.906429782561352e+00, /* 242 */ + 7.910998034724029e+00, /* 243 */ + 7.915568926374869e+00, /* 244 */ + 7.920142459038940e+00, /* 245 */ + 7.924718634242192e+00, /* 246 */ + 7.929297453511457e+00, /* 247 */ + 7.933878918374448e+00, /* 248 */ + 7.938463030359761e+00, /* 249 */ + 7.943049790996877e+00, /* 250 */ + 7.947639201816158e+00, /* 251 */ + 7.952231264348852e+00, /* 252 */ + 7.956825980127089e+00, /* 253 */ + 7.961423350683890e+00, /* 254 */ + 7.966023377553156e+00, /* 255 */ + 7.970626062269677e+00, /* 256 */ + 7.975231406369129e+00, /* 257 */ + 7.979839411388076e+00, /* 258 */ + 7.984450078863969e+00, /* 259 */ + 7.989063410335147e+00, /* 260 */ + 7.993679407340840e+00, /* 261 */ + 7.998298071421166e+00, /* 262 */ + 8.002919404117131e+00, /* 263 */ + 8.007543406970633e+00, /* 264 */ + 8.012170081524465e+00, /* 265 */ + 8.016799429322301e+00, /* 266 */ + 8.021431451908718e+00, /* 267 */ + 8.026066150829180e+00, /* 268 */ + 8.030703527630045e+00, /* 269 */ + 8.035343583858563e+00, /* 270 */ + 8.039986321062880e+00, /* 271 */ + 8.044631740792035e+00, /* 272 */ + 8.049279844595961e+00, /* 273 */ + 8.053930634025493e+00, /* 274 */ + 8.058584110632353e+00, /* 275 */ + 8.063240275969166e+00, /* 276 */ + 8.067899131589453e+00, /* 277 */ + 8.072560679047628e+00, /* 278 */ + 8.077224919899010e+00, /* 279 */ + 8.081891855699810e+00, /* 280 */ + 8.086561488007144e+00, /* 281 */ + 8.091233818379026e+00, /* 282 */ + 8.095908848374366e+00, /* 283 */ + 8.100586579552981e+00, /* 284 */ + 8.105267013475586e+00, /* 285 */ + 8.109950151703798e+00, /* 286 */ + 8.114635995800136e+00, /* 287 */ + 8.119324547328022e+00, /* 288 */ + 8.124015807851780e+00, /* 289 */ + 8.128709778936644e+00, /* 290 */ + 8.133406462148743e+00, /* 291 */ + 8.138105859055118e+00, /* 292 */ + 8.142807971223712e+00, /* 293 */ + 8.147512800223376e+00, /* 294 */ + 8.152220347623867e+00, /* 295 */ + 8.156930614995847e+00, /* 296 */ + 8.161643603910887e+00, /* 297 */ + 8.166359315941468e+00, /* 298 */ + 8.171077752660976e+00, /* 299 */ + 8.175798915643707e+00, /* 300 */ + 8.180522806464868e+00, /* 301 */ + 8.185249426700578e+00, /* 302 */ + 8.189978777927859e+00, /* 303 */ + 8.194710861724653e+00, /* 304 */ + 8.199445679669807e+00, /* 305 */ + 8.204183233343088e+00, /* 306 */ + 8.208923524325167e+00, /* 307 */ + 8.213666554197633e+00, /* 308 */ + 8.218412324542989e+00, /* 309 */ + 8.223160836944650e+00, /* 310 */ + 8.227912092986951e+00, /* 311 */ + 8.232666094255135e+00, /* 312 */ + 8.237422842335365e+00, /* 313 */ + 8.242182338814722e+00, /* 314 */ + 8.246944585281200e+00, /* 315 */ + 8.251709583323716e+00, /* 316 */ + 8.256477334532098e+00, /* 317 */ + 8.261247840497099e+00, /* 318 */ + 8.266021102810386e+00, /* 319 */ + 8.270797123064552e+00, /* 320 */ + 8.275575902853102e+00, /* 321 */ + 8.280357443770470e+00, /* 322 */ + 8.285141747412004e+00, /* 323 */ + 8.289928815373978e+00, /* 324 */ + 8.294718649253587e+00, /* 325 */ + 8.299511250648951e+00, /* 326 */ + 8.304306621159110e+00, /* 327 */ + 8.309104762384029e+00, /* 328 */ + 8.313905675924600e+00, /* 329 */ + 8.318709363382636e+00, /* 330 */ + 8.323515826360879e+00, /* 331 */ + 8.328325066462993e+00, /* 332 */ + 8.333137085293574e+00, /* 333 */ + 8.337951884458139e+00, /* 334 */ + 8.342769465563139e+00, /* 335 */ + 8.347589830215947e+00, /* 336 */ + 8.352412980024869e+00, /* 337 */ + 8.357238916599140e+00, /* 338 */ + 8.362067641548924e+00, /* 339 */ + 8.366899156485312e+00, /* 340 */ + 8.371733463020332e+00, /* 341 */ + 8.376570562766940e+00, /* 342 */ + 8.381410457339022e+00, /* 343 */ + 8.386253148351402e+00, /* 344 */ + 8.391098637419832e+00, /* 345 */ + 8.395946926161001e+00, /* 346 */ + 8.400798016192528e+00, /* 347 */ + 8.405651909132970e+00, /* 348 */ + 8.410508606601821e+00, /* 349 */ + 8.415368110219505e+00, /* 350 */ + 8.420230421607386e+00, /* 351 */ + 8.425095542387766e+00, /* 352 */ + 8.429963474183879e+00, /* 353 */ + 8.434834218619903e+00, /* 354 */ + 8.439707777320951e+00, /* 355 */ + 8.444584151913077e+00, /* 356 */ + 8.449463344023272e+00, /* 357 */ + 8.454345355279468e+00, /* 358 */ + 8.459230187310540e+00, /* 359 */ + 8.464117841746299e+00, /* 360 */ + 8.469008320217505e+00, /* 361 */ + 8.473901624355852e+00, /* 362 */ + 8.478797755793982e+00, /* 363 */ + 8.483696716165481e+00, /* 364 */ + 8.488598507104875e+00, /* 365 */ + 8.493503130247639e+00, /* 366 */ + 8.498410587230186e+00, /* 367 */ + 8.503320879689882e+00, /* 368 */ + 8.508234009265037e+00, /* 369 */ + 8.513149977594903e+00, /* 370 */ + 8.518068786319684e+00, /* 371 */ + 8.522990437080532e+00, /* 372 */ + 8.527914931519545e+00, /* 373 */ + 8.532842271279769e+00, /* 374 */ + 8.537772458005202e+00, /* 375 */ + 8.542705493340792e+00, /* 376 */ + 8.547641378932433e+00, /* 377 */ + 8.552580116426974e+00, /* 378 */ + 8.557521707472215e+00, /* 379 */ + 8.562466153716908e+00, /* 380 */ + 8.567413456810757e+00, /* 381 */ + 8.572363618404419e+00, /* 382 */ + 8.577316640149505e+00, /* 383 */ + 8.582272523698583e+00, /* 384 */ + 8.587231270705169e+00, /* 385 */ + 8.592192882823742e+00, /* 386 */ + 8.597157361709733e+00, /* 387 */ + 8.602124709019529e+00, /* 388 */ + 8.607094926410477e+00, /* 389 */ + 8.612068015540880e+00, /* 390 */ + 8.617043978069995e+00, /* 391 */ + 8.622022815658045e+00, /* 392 */ + 8.627004529966209e+00, /* 393 */ + 8.631989122656625e+00, /* 394 */ + 8.636976595392392e+00, /* 395 */ + 8.641966949837570e+00, /* 396 */ + 8.646960187657180e+00, /* 397 */ + 8.651956310517207e+00, /* 398 */ + 8.656955320084593e+00, /* 399 */ + 8.661957218027252e+00, /* 400 */ + 8.666962006014057e+00, /* 401 */ + 8.671969685714840e+00, /* 402 */ + 8.676980258800409e+00, /* 403 */ + 8.681993726942528e+00, /* 404 */ + 8.687010091813930e+00, /* 405 */ + 8.692029355088316e+00, /* 406 */ + 8.697051518440352e+00, /* 407 */ + 8.702076583545674e+00, /* 408 */ + 8.707104552080883e+00, /* 409 */ + 8.712135425723552e+00, /* 410 */ + 8.717169206152221e+00, /* 411 */ + 8.722205895046399e+00, /* 412 */ + 8.727245494086567e+00, /* 413 */ + 8.732288004954178e+00, /* 414 */ + 8.737333429331656e+00, /* 415 */ + 8.742381768902394e+00, /* 416 */ + 8.747433025350762e+00, /* 417 */ + 8.752487200362102e+00, /* 418 */ + 8.757544295622727e+00, /* 419 */ + 8.762604312819928e+00, /* 420 */ + 8.767667253641967e+00, /* 421 */ + 8.772733119778087e+00, /* 422 */ + 8.777801912918500e+00, /* 423 */ + 8.782873634754402e+00, /* 424 */ + 8.787948286977960e+00, /* 425 */ + 8.793025871282323e+00, /* 426 */ + 8.798106389361616e+00, /* 427 */ + 8.803189842910941e+00, /* 428 */ + 8.808276233626385e+00, /* 429 */ + 8.813365563205011e+00, /* 430 */ + 8.818457833344864e+00, /* 431 */ + 8.823553045744966e+00, /* 432 */ + 8.828651202105329e+00, /* 433 */ + 8.833752304126937e+00, /* 434 */ + 8.838856353511767e+00, /* 435 */ + 8.843963351962772e+00, /* 436 */ + 8.849073301183891e+00, /* 437 */ + 8.854186202880051e+00, /* 438 */ + 8.859302058757157e+00, /* 439 */ + 8.864420870522107e+00, /* 440 */ + 8.869542639882781e+00, /* 441 */ + 8.874667368548046e+00, /* 442 */ + 8.879795058227758e+00, /* 443 */ + 8.884925710632759e+00, /* 444 */ + 8.890059327474882e+00, /* 445 */ + 8.895195910466947e+00, /* 446 */ + 8.900335461322765e+00, /* 447 */ + 8.905477981757135e+00, /* 448 */ + 8.910623473485851e+00, /* 449 */ + 8.915771938225692e+00, /* 450 */ + 8.920923377694434e+00, /* 451 */ + 8.926077793610846e+00, /* 452 */ + 8.931235187694687e+00, /* 453 */ + 8.936395561666711e+00, /* 454 */ + 8.941558917248665e+00, /* 455 */ + 8.946725256163294e+00, /* 456 */ + 8.951894580134335e+00, /* 457 */ + 8.957066890886521e+00, /* 458 */ + 8.962242190145584e+00, /* 459 */ + 8.967420479638255e+00, /* 460 */ + 8.972601761092255e+00, /* 461 */ + 8.977786036236310e+00, /* 462 */ + 8.982973306800142e+00, /* 463 */ + 8.988163574514473e+00, /* 464 */ + 8.993356841111027e+00, /* 465 */ + 8.998553108322524e+00, /* 466 */ + 9.003752377882689e+00, /* 467 */ + 9.008954651526247e+00, /* 468 */ + 9.014159930988928e+00, /* 469 */ + 9.019368218007461e+00, /* 470 */ + 9.024579514319580e+00, /* 471 */ + 9.029793821664024e+00, /* 472 */ + 9.035011141780535e+00, /* 473 */ + 9.040231476409861e+00, /* 474 */ + 9.045454827293758e+00, /* 475 */ + 9.050681196174985e+00, /* 476 */ + 9.055910584797308e+00, /* 477 */ + 9.061142994905502e+00, /* 478 */ + 9.066378428245352e+00, /* 479 */ + 9.071616886563648e+00, /* 480 */ + 9.076858371608191e+00, /* 481 */ + 9.082102885127791e+00, /* 482 */ + 9.087350428872268e+00, /* 483 */ + 9.092601004592458e+00, /* 484 */ + 9.097854614040202e+00, /* 485 */ + 9.103111258968356e+00, /* 486 */ + 9.108370941130788e+00, /* 487 */ + 9.113633662282384e+00, /* 488 */ + 9.118899424179036e+00, /* 489 */ + 9.124168228577656e+00, /* 490 */ + 9.129440077236168e+00, /* 491 */ + 9.134714971913517e+00, /* 492 */ + 9.139992914369659e+00, /* 493 */ + 9.145273906365567e+00, /* 494 */ + 9.150557949663234e+00, /* 495 */ + 9.155845046025673e+00, /* 496 */ + 9.161135197216907e+00, /* 497 */ + 9.166428405001991e+00, /* 498 */ + 9.171724671146988e+00, /* 499 */ + 9.177023997418987e+00, /* 500 */ + 9.182326385586098e+00, /* 501 */ + 9.187631837417451e+00, /* 502 */ + 9.192940354683200e+00, /* 503 */ + 9.198251939154520e+00, /* 504 */ + 9.203566592603611e+00, /* 505 */ + 9.208884316803697e+00, /* 506 */ + 9.214205113529024e+00, /* 507 */ + 9.219528984554865e+00, /* 508 */ + 9.224855931657519e+00, /* 509 */ + 9.230185956614312e+00, /* 510 */ + 9.235519061203593e+00, /* 511 */ + 9.240855247204744e+00, /* 512 */ + 9.246194516398171e+00, /* 513 */ + 9.251536870565310e+00, /* 514 */ + 9.256882311488630e+00, /* 515 */ + 9.262230840951620e+00, /* 516 */ + 9.267582460738812e+00, /* 517 */ + 9.272937172635757e+00, /* 518 */ + 9.278294978429049e+00, /* 519 */ + 9.283655879906306e+00, /* 520 */ + 9.289019878856182e+00, /* 521 */ + 9.294386977068365e+00, /* 522 */ + 9.299757176333575e+00, /* 523 */ + 9.305130478443569e+00, /* 524 */ + 9.310506885191138e+00, /* 525 */ + 9.315886398370107e+00, /* 526 */ + 9.321269019775343e+00, /* 527 */ + 9.326654751202744e+00, /* 528 */ + 9.332043594449249e+00, /* 529 */ + 9.337435551312835e+00, /* 530 */ + 9.342830623592516e+00, /* 531 */ + 9.348228813088346e+00, /* 532 */ + 9.353630121601423e+00, /* 533 */ + 9.359034550933879e+00, /* 534 */ + 9.364442102888894e+00, /* 535 */ + 9.369852779270683e+00, /* 536 */ + 9.375266581884510e+00, /* 537 */ + 9.380683512536677e+00, /* 538 */ + 9.386103573034532e+00, /* 539 */ + 9.391526765186470e+00, /* 540 */ + 9.396953090801922e+00, /* 541 */ + 9.402382551691376e+00, /* 542 */ + 9.407815149666359e+00, /* 543 */ + 9.413250886539444e+00, /* 544 */ + 9.418689764124254e+00, /* 545 */ + 9.424131784235461e+00, /* 546 */ + 9.429576948688782e+00, /* 547 */ + 9.435025259300986e+00, /* 548 */ + 9.440476717889890e+00, /* 549 */ + 9.445931326274362e+00, /* 550 */ + 9.451389086274322e+00, /* 551 */ + 9.456849999710737e+00, /* 552 */ + 9.462314068405634e+00, /* 553 */ + 9.467781294182085e+00, /* 554 */ + 9.473251678864221e+00, /* 555 */ + 9.478725224277222e+00, /* 556 */ + 9.484201932247325e+00, /* 557 */ + 9.489681804601826e+00, /* 558 */ + 9.495164843169070e+00, /* 559 */ + 9.500651049778460e+00, /* 560 */ + 9.506140426260462e+00, /* 561 */ + 9.511632974446592e+00, /* 562 */ + 9.517128696169429e+00, /* 563 */ + 9.522627593262607e+00, /* 564 */ + 9.528129667560824e+00, /* 565 */ + 9.533634920899836e+00, /* 566 */ + 9.539143355116456e+00, /* 567 */ + 9.544654972048564e+00, /* 568 */ + 9.550169773535101e+00, /* 569 */ + 9.555687761416067e+00, /* 570 */ + 9.561208937532529e+00, /* 571 */ + 9.566733303726613e+00, /* 572 */ + 9.572260861841515e+00, /* 573 */ + 9.577791613721493e+00, /* 574 */ + 9.583325561211870e+00, /* 575 */ + 9.588862706159038e+00, /* 576 */ + 9.594403050410451e+00, /* 577 */ + 9.599946595814636e+00, /* 578 */ + 9.605493344221186e+00, /* 579 */ + 9.611043297480759e+00, /* 580 */ + 9.616596457445088e+00, /* 581 */ + 9.622152825966971e+00, /* 582 */ + 9.627712404900283e+00, /* 583 */ + 9.633275196099962e+00, /* 584 */ + 9.638841201422023e+00, /* 585 */ + 9.644410422723555e+00, /* 586 */ + 9.649982861862712e+00, /* 587 */ + 9.655558520698731e+00, /* 588 */ + 9.661137401091917e+00, /* 589 */ + 9.666719504903652e+00, /* 590 */ + 9.672304833996394e+00, /* 591 */ + 9.677893390233677e+00, /* 592 */ + 9.683485175480111e+00, /* 593 */ + 9.689080191601384e+00, /* 594 */ + 9.694678440464259e+00, /* 595 */ + 9.700279923936582e+00, /* 596 */ + 9.705884643887279e+00, /* 597 */ + 9.711492602186349e+00, /* 598 */ + 9.717103800704876e+00, /* 599 */ + 9.722718241315029e+00, /* 600 */ + 9.728335925890050e+00, /* 601 */ + 9.733956856304269e+00, /* 602 */ + 9.739581034433099e+00, /* 603 */ + 9.745208462153036e+00, /* 604 */ + 9.750839141341658e+00, /* 605 */ + 9.756473073877629e+00, /* 606 */ + 9.762110261640702e+00, /* 607 */ + 9.767750706511709e+00, /* 608 */ + 9.773394410372575e+00, /* 609 */ + 9.779041375106310e+00, /* 610 */ + 9.784691602597013e+00, /* 611 */ + 9.790345094729869e+00, /* 612 */ + 9.796001853391154e+00, /* 613 */ + 9.801661880468235e+00, /* 614 */ + 9.807325177849568e+00, /* 615 */ + 9.812991747424702e+00, /* 616 */ + 9.818661591084274e+00, /* 617 */ + 9.824334710720015e+00, /* 618 */ + 9.830011108224751e+00, /* 619 */ + 9.835690785492401e+00, /* 620 */ + 9.841373744417977e+00, /* 621 */ + 9.847059986897586e+00, /* 622 */ + 9.852749514828432e+00, /* 623 */ + 9.858442330108813e+00, /* 624 */ + 9.864138434638127e+00, /* 625 */ + 9.869837830316865e+00, /* 626 */ + 9.875540519046620e+00, /* 627 */ + 9.881246502730082e+00, /* 628 */ + 9.886955783271041e+00, /* 629 */ + 9.892668362574387e+00, /* 630 */ + 9.898384242546111e+00, /* 631 */ + 9.904103425093302e+00, /* 632 */ + 9.909825912124154e+00, /* 633 */ + 9.915551705547966e+00, /* 634 */ + 9.921280807275133e+00, /* 635 */ + 9.927013219217161e+00, /* 636 */ + 9.932748943286656e+00, /* 637 */ + 9.938487981397330e+00, /* 638 */ + 9.944230335464004e+00, /* 639 */ + 9.949976007402599e+00, /* 640 */ + 9.955724999130149e+00, /* 641 */ + 9.961477312564792e+00, /* 642 */ + 9.967232949625776e+00, /* 643 */ + 9.972991912233459e+00, /* 644 */ + 9.978754202309304e+00, /* 645 */ + 9.984519821775887e+00, /* 646 */ + 9.990288772556898e+00, /* 647 */ + 9.996061056577135e+00, /* 648 */ + 1.000183667576251e+01, /* 649 */ + 1.000761563204004e+01, /* 650 */ + 1.001339792733786e+01, /* 651 */ + 1.001918356358524e+01, /* 652 */ + 1.002497254271253e+01, /* 653 */ + 1.003076486665121e+01, /* 654 */ + 1.003656053733387e+01, /* 655 */ + 1.004235955669425e+01, /* 656 */ + 1.004816192666716e+01, /* 657 */ + 1.005396764918855e+01, /* 658 */ + 1.005977672619549e+01, /* 659 */ + 1.006558915962617e+01, /* 660 */ + 1.007140495141990e+01, /* 661 */ + 1.007722410351709e+01, /* 662 */ + 1.008304661785931e+01, /* 663 */ + 1.008887249638921e+01, /* 664 */ + 1.009470174105059e+01, /* 665 */ + 1.010053435378837e+01, /* 666 */ + 1.010637033654859e+01, /* 667 */ + 1.011220969127841e+01, /* 668 */ + 1.011805241992611e+01, /* 669 */ + 1.012389852444111e+01, /* 670 */ + 1.012974800677396e+01, /* 671 */ + 1.013560086887632e+01, /* 672 */ + 1.014145711270099e+01, /* 673 */ + 1.014731674020188e+01, /* 674 */ + 1.015317975333406e+01, /* 675 */ + 1.015904615405370e+01, /* 676 */ + 1.016491594431812e+01, /* 677 */ + 1.017078912608576e+01, /* 678 */ + 1.017666570131619e+01, /* 679 */ + 1.018254567197013e+01, /* 680 */ + 1.018842904000941e+01, /* 681 */ + 1.019431580739701e+01, /* 682 */ + 1.020020597609703e+01, /* 683 */ + 1.020609954807471e+01, /* 684 */ + 1.021199652529644e+01, /* 685 */ + 1.021789690972973e+01, /* 686 */ + 1.022380070334324e+01, /* 687 */ + 1.022970790810674e+01, /* 688 */ + 1.023561852599116e+01, /* 689 */ + 1.024153255896858e+01, /* 690 */ + 1.024745000901219e+01, /* 691 */ + 1.025337087809634e+01, /* 692 */ + 1.025929516819652e+01, /* 693 */ + 1.026522288128936e+01, /* 694 */ + 1.027115401935261e+01, /* 695 */ + 1.027708858436520e+01, /* 696 */ + 1.028302657830718e+01, /* 697 */ + 1.028896800315975e+01, /* 698 */ + 1.029491286090526e+01, /* 699 */ + 1.030086115352719e+01, /* 700 */ + 1.030681288301017e+01, /* 701 */ + 1.031276805134000e+01, /* 702 */ + 1.031872666050360e+01, /* 703 */ + 1.032468871248905e+01, /* 704 */ + 1.033065420928557e+01, /* 705 */ + 1.033662315288354e+01, /* 706 */ + 1.034259554527449e+01, /* 707 */ + 1.034857138845109e+01, /* 708 */ + 1.035455068440717e+01, /* 709 */ + 1.036053343513771e+01, /* 710 */ + 1.036651964263884e+01, /* 711 */ + 1.037250930890785e+01, /* 712 */ + 1.037850243594318e+01, /* 713 */ + 1.038449902574443e+01, /* 714 */ + 1.039049908031233e+01, /* 715 */ + 1.039650260164880e+01, /* 716 */ + 1.040250959175691e+01, /* 717 */ + 1.040852005264086e+01, /* 718 */ + 1.041453398630604e+01, /* 719 */ + 1.042055139475899e+01, /* 720 */ + 1.042657228000739e+01, /* 721 */ + 1.043259664406012e+01, /* 722 */ + 1.043862448892718e+01, /* 723 */ + 1.044465581661974e+01, /* 724 */ + 1.045069062915016e+01, /* 725 */ + 1.045672892853194e+01, /* 726 */ + 1.046277071677973e+01, /* 727 */ + 1.046881599590938e+01, /* 728 */ + 1.047486476793787e+01, /* 729 */ + 1.048091703488336e+01, /* 730 */ + 1.048697279876519e+01, /* 731 */ + 1.049303206160384e+01, /* 732 */ + 1.049909482542098e+01, /* 733 */ + 1.050516109223943e+01, /* 734 */ + 1.051123086408320e+01, /* 735 */ + 1.051730414297744e+01, /* 736 */ + 1.052338093094850e+01, /* 737 */ + 1.052946123002388e+01, /* 738 */ + 1.053554504223227e+01, /* 739 */ + 1.054163236960350e+01, /* 740 */ + 1.054772321416862e+01, /* 741 */ + 1.055381757795981e+01, /* 742 */ + 1.055991546301045e+01, /* 743 */ + 1.056601687135509e+01, /* 744 */ + 1.057212180502944e+01, /* 745 */ + 1.057823026607040e+01, /* 746 */ + 1.058434225651606e+01, /* 747 */ + 1.059045777840566e+01, /* 748 */ + 1.059657683377963e+01, /* 749 */ + 1.060269942467959e+01, /* 750 */ + 1.060882555314833e+01, /* 751 */ + 1.061495522122981e+01, /* 752 */ + 1.062108843096918e+01, /* 753 */ + 1.062722518441279e+01, /* 754 */ + 1.063336548360814e+01, /* 755 */ + 1.063950933060393e+01, /* 756 */ + 1.064565672745005e+01, /* 757 */ + 1.065180767619755e+01, /* 758 */ + 1.065796217889870e+01, /* 759 */ + 1.066412023760692e+01, /* 760 */ + 1.067028185437685e+01, /* 761 */ + 1.067644703126430e+01, /* 762 */ + 1.068261577032625e+01, /* 763 */ + 1.068878807362090e+01, /* 764 */ + 1.069496394320763e+01, /* 765 */ + 1.070114338114700e+01, /* 766 */ + 1.070732638950076e+01, /* 767 */ + 1.071351297033187e+01, /* 768 */ + 1.071970312570447e+01, /* 769 */ + 1.072589685768389e+01, /* 770 */ + 1.073209416833664e+01, /* 771 */ + 1.073829505973047e+01, /* 772 */ + 1.074449953393427e+01, /* 773 */ + 1.075070759301816e+01, /* 774 */ + 1.075691923905345e+01, /* 775 */ + 1.076313447411263e+01, /* 776 */ + 1.076935330026941e+01, /* 777 */ + 1.077557571959869e+01, /* 778 */ + 1.078180173417656e+01, /* 779 */ + 1.078803134608032e+01, /* 780 */ + 1.079426455738847e+01, /* 781 */ + 1.080050137018071e+01, /* 782 */ + 1.080674178653793e+01, /* 783 */ + 1.081298580854224e+01, /* 784 */ + 1.081923343827694e+01, /* 785 */ + 1.082548467782655e+01, /* 786 */ + 1.083173952927677e+01, /* 787 */ + 1.083799799471452e+01, /* 788 */ + 1.084426007622793e+01, /* 789 */ + 1.085052577590632e+01, /* 790 */ + 1.085679509584024e+01, /* 791 */ + 1.086306803812144e+01, /* 792 */ + 1.086934460484285e+01, /* 793 */ + 1.087562479809866e+01, /* 794 */ + 1.088190861998423e+01, /* 795 */ + 1.088819607259615e+01, /* 796 */ + 1.089448715803220e+01, /* 797 */ + 1.090078187839141e+01, /* 798 */ + 1.090708023577399e+01, /* 799 */ + 1.091338223228137e+01, /* 800 */ + 1.091968787001621e+01, /* 801 */ + 1.092599715108236e+01, /* 802 */ + 1.093231007758490e+01, /* 803 */ + 1.093862665163013e+01, /* 804 */ + 1.094494687532556e+01, /* 805 */ + 1.095127075077993e+01, /* 806 */ + 1.095759828010317e+01, /* 807 */ + 1.096392946540646e+01, /* 808 */ + 1.097026430880218e+01, /* 809 */ + 1.097660281240394e+01, /* 810 */ + 1.098294497832656e+01, /* 811 */ + 1.098929080868611e+01, /* 812 */ + 1.099564030559985e+01, /* 813 */ + 1.100199347118628e+01, /* 814 */ + 1.100835030756511e+01, /* 815 */ + 1.101471081685730e+01, /* 816 */ + 1.102107500118502e+01, /* 817 */ + 1.102744286267166e+01, /* 818 */ + 1.103381440344184e+01, /* 819 */ + 1.104018962562143e+01, /* 820 */ + 1.104656853133749e+01, /* 821 */ + 1.105295112271833e+01, /* 822 */ + 1.105933740189350e+01, /* 823 */ + 1.106572737099377e+01, /* 824 */ + 1.107212103215112e+01, /* 825 */ + 1.107851838749881e+01, /* 826 */ + 1.108491943917128e+01, /* 827 */ + 1.109132418930424e+01, /* 828 */ + 1.109773264003461e+01, /* 829 */ + 1.110414479350058e+01, /* 830 */ + 1.111056065184153e+01, /* 831 */ + 1.111698021719810e+01, /* 832 */ + 1.112340349171218e+01, /* 833 */ + 1.112983047752687e+01, /* 834 */ + 1.113626117678652e+01, /* 835 */ + 1.114269559163672e+01, /* 836 */ + 1.114913372422430e+01, /* 837 */ + 1.115557557669733e+01, /* 838 */ + 1.116202115120513e+01, /* 839 */ + 1.116847044989824e+01, /* 840 */ + 1.117492347492846e+01, /* 841 */ + 1.118138022844883e+01, /* 842 */ + 1.118784071261362e+01, /* 843 */ + 1.119430492957838e+01, /* 844 */ + 1.120077288149986e+01, /* 845 */ + 1.120724457053610e+01, /* 846 */ + 1.121371999884635e+01, /* 847 */ + 1.122019916859113e+01, /* 848 */ + 1.122668208193219e+01, /* 849 */ + 1.123316874103256e+01, /* 850 */ + 1.123965914805649e+01, /* 851 */ + 1.124615330516949e+01, /* 852 */ + 1.125265121453833e+01, /* 853 */ + 1.125915287833101e+01, /* 854 */ + 1.126565829871680e+01, /* 855 */ + 1.127216747786624e+01, /* 856 */ + 1.127868041795107e+01, /* 857 */ + 1.128519712114435e+01, /* 858 */ + 1.129171758962035e+01, /* 859 */ + 1.129824182555462e+01, /* 860 */ + 1.130476983112394e+01, /* 861 */ + 1.131130160850638e+01, /* 862 */ + 1.131783715988125e+01, /* 863 */ + 1.132437648742913e+01, /* 864 */ + 1.133091959333184e+01, /* 865 */ + 1.133746647977249e+01, /* 866 */ + 1.134401714893542e+01, /* 867 */ + 1.135057160300625e+01, /* 868 */ + 1.135712984417187e+01, /* 869 */ + 1.136369187462041e+01, /* 870 */ + 1.137025769654129e+01, /* 871 */ + 1.137682731212518e+01, /* 872 */ + 1.138340072356401e+01, /* 873 */ + 1.138997793305099e+01, /* 874 */ + 1.139655894278060e+01, /* 875 */ + 1.140314375494857e+01, /* 876 */ + 1.140973237175192e+01, /* 877 */ + 1.141632479538892e+01, /* 878 */ + 1.142292102805911e+01, /* 879 */ + 1.142952107196333e+01, /* 880 */ + 1.143612492930366e+01, /* 881 */ + 1.144273260228346e+01, /* 882 */ + 1.144934409310737e+01, /* 883 */ + 1.145595940398131e+01, /* 884 */ + 1.146257853711245e+01, /* 885 */ + 1.146920149470925e+01, /* 886 */ + 1.147582827898146e+01, /* 887 */ + 1.148245889214008e+01, /* 888 */ + 1.148909333639740e+01, /* 889 */ + 1.149573161396700e+01, /* 890 */ + 1.150237372706373e+01, /* 891 */ + 1.150901967790370e+01, /* 892 */ + 1.151566946870432e+01, /* 893 */ + 1.152232310168429e+01, /* 894 */ + 1.152898057906358e+01, /* 895 */ + 1.153564190306344e+01, /* 896 */ + 1.154230707590640e+01, /* 897 */ + 1.154897609981630e+01, /* 898 */ + 1.155564897701822e+01, /* 899 */ + 1.156232570973857e+01, /* 900 */ + 1.156900630020503e+01, /* 901 */ + 1.157569075064656e+01, /* 902 */ + 1.158237906329340e+01, /* 903 */ + 1.158907124037712e+01, /* 904 */ + 1.159576728413052e+01, /* 905 */ + 1.160246719678775e+01, /* 906 */ + 1.160917098058420e+01, /* 907 */ + 1.161587863775658e+01, /* 908 */ + 1.162259017054289e+01, /* 909 */ + 1.162930558118242e+01, /* 910 */ + 1.163602487191574e+01, /* 911 */ + 1.164274804498475e+01, /* 912 */ + 1.164947510263260e+01, /* 913 */ + 1.165620604710378e+01, /* 914 */ + 1.166294088064403e+01, /* 915 */ + 1.166967960550044e+01, /* 916 */ + 1.167642222392135e+01, /* 917 */ + 1.168316873815644e+01, /* 918 */ + 1.168991915045666e+01, /* 919 */ + 1.169667346307427e+01, /* 920 */ + 1.170343167826283e+01, /* 921 */ + 1.171019379827721e+01, /* 922 */ + 1.171695982537358e+01, /* 923 */ + 1.172372976180941e+01, /* 924 */ + 1.173050360984346e+01, /* 925 */ + 1.173728137173583e+01, /* 926 */ + 1.174406304974791e+01, /* 927 */ + 1.175084864614237e+01, /* 928 */ + 1.175763816318322e+01, /* 929 */ + 1.176443160313578e+01, /* 930 */ + 1.177122896826666e+01, /* 931 */ + 1.177803026084377e+01, /* 932 */ + 1.178483548313637e+01, /* 933 */ + 1.179164463741501e+01, /* 934 */ + 1.179845772595153e+01, /* 935 */ + 1.180527475101911e+01, /* 936 */ + 1.181209571489225e+01, /* 937 */ + 1.181892061984674e+01, /* 938 */ + 1.182574946815969e+01, /* 939 */ + 1.183258226210954e+01, /* 940 */ + 1.183941900397603e+01, /* 941 */ + 1.184625969604024e+01, /* 942 */ + 1.185310434058453e+01, /* 943 */ + 1.185995293989262e+01, /* 944 */ + 1.186680549624953e+01, /* 945 */ + 1.187366201194159e+01, /* 946 */ + 1.188052248925647e+01, /* 947 */ + 1.188738693048315e+01, /* 948 */ + 1.189425533791194e+01, /* 949 */ + 1.190112771383447e+01, /* 950 */ + 1.190800406054369e+01, /* 951 */ + 1.191488438033388e+01, /* 952 */ + 1.192176867550065e+01, /* 953 */ + 1.192865694834093e+01, /* 954 */ + 1.193554920115298e+01, /* 955 */ + 1.194244543623637e+01, /* 956 */ + 1.194934565589204e+01, /* 957 */ + 1.195624986242221e+01, /* 958 */ + 1.196315805813046e+01, /* 959 */ + 1.197007024532171e+01, /* 960 */ + 1.197698642630218e+01, /* 961 */ + 1.198390660337945e+01, /* 962 */ + 1.199083077886241e+01, /* 963 */ + 1.199775895506131e+01, /* 964 */ + 1.200469113428772e+01, /* 965 */ + 1.201162731885455e+01, /* 966 */ + 1.201856751107603e+01, /* 967 */ + 1.202551171326775e+01, /* 968 */ + 1.203245992774663e+01, /* 969 */ + 1.203941215683092e+01, /* 970 */ + 1.204636840284023e+01, /* 971 */ + 1.205332866809548e+01, /* 972 */ + 1.206029295491897e+01, /* 973 */ + 1.206726126563430e+01, /* 974 */ + 1.207423360256643e+01, /* 975 */ + 1.208120996804169e+01, /* 976 */ + 1.208819036438771e+01, /* 977 */ + 1.209517479393349e+01, /* 978 */ + 1.210216325900937e+01, /* 979 */ + 1.210915576194704e+01, /* 980 */ + 1.211615230507953e+01, /* 981 */ + 1.212315289074123e+01, /* 982 */ + 1.213015752126786e+01, /* 983 */ + 1.213716619899651e+01, /* 984 */ + 1.214417892626560e+01, /* 985 */ + 1.215119570541492e+01, /* 986 */ + 1.215821653878560e+01, /* 987 */ + 1.216524142872013e+01, /* 988 */ + 1.217227037756235e+01, /* 989 */ + 1.217930338765746e+01, /* 990 */ + 1.218634046135199e+01, /* 991 */ + 1.219338160099387e+01, /* 992 */ + 1.220042680893234e+01, /* 993 */ + 1.220747608751803e+01, /* 994 */ + 1.221452943910292e+01, /* 995 */ + 1.222158686604034e+01, /* 996 */ + 1.222864837068499e+01, /* 997 */ + 1.223571395539292e+01, /* 998 */ + 1.224278362252155e+01, /* 999 */ + 1.224985737442966e+01, /* 1000 */ + 1.225693521347741e+01, /* 1001 */ + 1.226401714202627e+01, /* 1002 */ + 1.227110316243915e+01, /* 1003 */ + 1.227819327708026e+01, /* 1004 */ + 1.228528748831521e+01, /* 1005 */ + 1.229238579851096e+01, /* 1006 */ + 1.229948821003587e+01, /* 1007 */ + 1.230659472525962e+01, /* 1008 */ + 1.231370534655330e+01, /* 1009 */ + 1.232082007628935e+01, /* 1010 */ + 1.232793891684158e+01, /* 1011 */ + 1.233506187058518e+01, /* 1012 */ + 1.234218893989671e+01, /* 1013 */ + 1.234932012715410e+01, /* 1014 */ + 1.235645543473665e+01, /* 1015 */ + 1.236359486502506e+01, /* 1016 */ + 1.237073842040136e+01, /* 1017 */ + 1.237788610324901e+01, /* 1018 */ + 1.238503791595280e+01, /* 1019 */ + 1.239219386089892e+01, /* 1020 */ + 1.239935394047494e+01, /* 1021 */ + 1.240651815706980e+01, /* 1022 */ + 1.241368651307384e+01, /* 1023 */ + 1.242085901087876e+01, /* 1024 */ + 1.242803565287764e+01, /* 1025 */ + 1.243521644146496e+01, /* 1026 */ + 1.244240137903658e+01, /* 1027 */ + 1.244959046798973e+01, /* 1028 */ + 1.245678371072304e+01, /* 1029 */ + 1.246398110963652e+01, /* 1030 */ + 1.247118266713156e+01, /* 1031 */ + 1.247838838561096e+01, /* 1032 */ + 1.248559826747888e+01, /* 1033 */ + 1.249281231514089e+01, /* 1034 */ + 1.250003053100394e+01, /* 1035 */ + 1.250725291747637e+01, /* 1036 */ + 1.251447947696792e+01, /* 1037 */ + 1.252171021188970e+01, /* 1038 */ + 1.252894512465425e+01, /* 1039 */ + 1.253618421767548e+01, /* 1040 */ + 1.254342749336869e+01, /* 1041 */ + 1.255067495415059e+01, /* 1042 */ + 1.255792660243928e+01, /* 1043 */ + 1.256518244065426e+01, /* 1044 */ + 1.257244247121641e+01, /* 1045 */ + 1.257970669654805e+01, /* 1046 */ + 1.258697511907285e+01, /* 1047 */ + 1.259424774121592e+01, /* 1048 */ + 1.260152456540375e+01, /* 1049 */ + 1.260880559406423e+01, /* 1050 */ + 1.261609082962667e+01, /* 1051 */ + 1.262338027452177e+01, /* 1052 */ + 1.263067393118164e+01, /* 1053 */ + 1.263797180203979e+01, /* 1054 */ + 1.264527388953115e+01, /* 1055 */ + 1.265258019609203e+01, /* 1056 */ + 1.265989072416018e+01, /* 1057 */ + 1.266720547617473e+01, /* 1058 */ + 1.267452445457624e+01, /* 1059 */ + 1.268184766180666e+01, /* 1060 */ + 1.268917510030938e+01, /* 1061 */ + 1.269650677252918e+01, /* 1062 */ + 1.270384268091225e+01, /* 1063 */ + 1.271118282790620e+01, /* 1064 */ + 1.271852721596007e+01, /* 1065 */ + 1.272587584752428e+01, /* 1066 */ + 1.273322872505070e+01, /* 1067 */ + 1.274058585099260e+01, /* 1068 */ + 1.274794722780466e+01, /* 1069 */ + 1.275531285794301e+01, /* 1070 */ + 1.276268274386515e+01, /* 1071 */ + 1.277005688803004e+01, /* 1072 */ + 1.277743529289805e+01, /* 1073 */ + 1.278481796093098e+01, /* 1074 */ + 1.279220489459201e+01, /* 1075 */ + 1.279959609634581e+01, /* 1076 */ + 1.280699156865842e+01, /* 1077 */ + 1.281439131399733e+01, /* 1078 */ + 1.282179533483144e+01, /* 1079 */ + 1.282920363363110e+01, /* 1080 */ + 1.283661621286807e+01, /* 1081 */ + 1.284403307501554e+01, /* 1082 */ + 1.285145422254812e+01, /* 1083 */ + 1.285887965794188e+01, /* 1084 */ + 1.286630938367429e+01, /* 1085 */ + 1.287374340222427e+01, /* 1086 */ + 1.288118171607215e+01, /* 1087 */ + 1.288862432769973e+01, /* 1088 */ + 1.289607123959020e+01, /* 1089 */ + 1.290352245422822e+01, /* 1090 */ + 1.291097797409987e+01, /* 1091 */ + 1.291843780169266e+01, /* 1092 */ + 1.292590193949556e+01, /* 1093 */ + 1.293337038999896e+01, /* 1094 */ + 1.294084315569469e+01, /* 1095 */ + 1.294832023907602e+01, /* 1096 */ + 1.295580164263767e+01, /* 1097 */ + 1.296328736887579e+01, /* 1098 */ + 1.297077742028798e+01, /* 1099 */ + 1.297827179937329e+01, /* 1100 */ + 1.298577050863218e+01, /* 1101 */ + 1.299327355056660e+01, /* 1102 */ + 1.300078092767991e+01, /* 1103 */ + 1.300829264247694e+01, /* 1104 */ + 1.301580869746396e+01, /* 1105 */ + 1.302332909514868e+01, /* 1106 */ + 1.303085383804027e+01, /* 1107 */ + 1.303838292864934e+01, /* 1108 */ + 1.304591636948796e+01, /* 1109 */ + 1.305345416306964e+01, /* 1110 */ + 1.306099631190936e+01, /* 1111 */ + 1.306854281852353e+01, /* 1112 */ + 1.307609368543003e+01, /* 1113 */ + 1.308364891514820e+01, /* 1114 */ + 1.309120851019883e+01, /* 1115 */ + 1.309877247310414e+01, /* 1116 */ + 1.310634080638785e+01, /* 1117 */ + 1.311391351257511e+01, /* 1118 */ + 1.312149059419255e+01, /* 1119 */ + 1.312907205376823e+01, /* 1120 */ + 1.313665789383170e+01, /* 1121 */ + 1.314424811691396e+01, /* 1122 */ + 1.315184272554746e+01, /* 1123 */ + 1.315944172226614e+01, /* 1124 */ + 1.316704510960539e+01, /* 1125 */ + 1.317465289010205e+01, /* 1126 */ + 1.318226506629446e+01, /* 1127 */ + 1.318988164072239e+01, /* 1128 */ + 1.319750261592710e+01, /* 1129 */ + 1.320512799445131e+01, /* 1130 */ + 1.321275777883922e+01, /* 1131 */ + 1.322039197163648e+01, /* 1132 */ + 1.322803057539023e+01, /* 1133 */ + 1.323567359264908e+01, /* 1134 */ + 1.324332102596310e+01, /* 1135 */ + 1.325097287788384e+01, /* 1136 */ + 1.325862915096432e+01, /* 1137 */ + 1.326628984775905e+01, /* 1138 */ + 1.327395497082400e+01, /* 1139 */ + 1.328162452271663e+01, /* 1140 */ + 1.328929850599585e+01, /* 1141 */ + 1.329697692322209e+01, /* 1142 */ + 1.330465977695723e+01, /* 1143 */ + 1.331234706976464e+01, /* 1144 */ + 1.332003880420917e+01, /* 1145 */ + 1.332773498285714e+01, /* 1146 */ + 1.333543560827638e+01, /* 1147 */ + 1.334314068303618e+01, /* 1148 */ + 1.335085020970733e+01, /* 1149 */ + 1.335856419086208e+01, /* 1150 */ + 1.336628262907420e+01, /* 1151 */ + 1.337400552691893e+01, /* 1152 */ + 1.338173288697299e+01, /* 1153 */ + 1.338946471181460e+01, /* 1154 */ + 1.339720100402347e+01, /* 1155 */ + 1.340494176618080e+01, /* 1156 */ + 1.341268700086928e+01, /* 1157 */ + 1.342043671067309e+01, /* 1158 */ + 1.342819089817790e+01, /* 1159 */ + 1.343594956597088e+01, /* 1160 */ + 1.344371271664070e+01, /* 1161 */ + 1.345148035277751e+01, /* 1162 */ + 1.345925247697298e+01, /* 1163 */ + 1.346702909182024e+01, /* 1164 */ + 1.347481019991397e+01, /* 1165 */ + 1.348259580385030e+01, /* 1166 */ + 1.349038590622688e+01, /* 1167 */ + 1.349818050964288e+01, /* 1168 */ + 1.350597961669893e+01, /* 1169 */ + 1.351378322999720e+01, /* 1170 */ + 1.352159135214135e+01, /* 1171 */ + 1.352940398573654e+01, /* 1172 */ + 1.353722113338944e+01, /* 1173 */ + 1.354504279770823e+01, /* 1174 */ + 1.355286898130258e+01, /* 1175 */ + 1.356069968678369e+01, /* 1176 */ + 1.356853491676425e+01, /* 1177 */ + 1.357637467385848e+01, /* 1178 */ + 1.358421896068210e+01, /* 1179 */ + 1.359206777985232e+01, /* 1180 */ + 1.359992113398790e+01, /* 1181 */ + 1.360777902570909e+01, /* 1182 */ + 1.361564145763767e+01, /* 1183 */ + 1.362350843239690e+01, /* 1184 */ + 1.363137995261160e+01, /* 1185 */ + 1.363925602090809e+01, /* 1186 */ + 1.364713663991418e+01, /* 1187 */ + 1.365502181225924e+01, /* 1188 */ + 1.366291154057414e+01, /* 1189 */ + 1.367080582749128e+01, /* 1190 */ + 1.367870467564455e+01, /* 1191 */ + 1.368660808766940e+01, /* 1192 */ + 1.369451606620278e+01, /* 1193 */ + 1.370242861388318e+01, /* 1194 */ + 1.371034573335060e+01, /* 1195 */ + 1.371826742724657e+01, /* 1196 */ + 1.372619369821415e+01, /* 1197 */ + 1.373412454889792e+01, /* 1198 */ + 1.374205998194399e+01, /* 1199 */ +}; + +static const fluid_real_t fluid_cb2amp_tab[1441] = { + 1.000000000000000e+00, /* 0 */ + 9.885530946569389e-01, /* 1 */ + 9.772372209558107e-01, /* 2 */ + 9.660508789898133e-01, /* 3 */ + 9.549925860214360e-01, /* 4 */ + 9.440608762859234e-01, /* 5 */ + 9.332543007969910e-01, /* 6 */ + 9.225714271547631e-01, /* 7 */ + 9.120108393559098e-01, /* 8 */ + 9.015711376059569e-01, /* 9 */ + 8.912509381337456e-01, /* 10 */ + 8.810488730080140e-01, /* 11 */ + 8.709635899560806e-01, /* 12 */ + 8.609937521846006e-01, /* 13 */ + 8.511380382023764e-01, /* 14 */ + 8.413951416451951e-01, /* 15 */ + 8.317637711026710e-01, /* 16 */ + 8.222426499470712e-01, /* 17 */ + 8.128305161640993e-01, /* 18 */ + 8.035261221856173e-01, /* 19 */ + 7.943282347242815e-01, /* 20 */ + 7.852356346100717e-01, /* 21 */ + 7.762471166286917e-01, /* 22 */ + 7.673614893618189e-01, /* 23 */ + 7.585775750291838e-01, /* 24 */ + 7.498942093324559e-01, /* 25 */ + 7.413102413009175e-01, /* 26 */ + 7.328245331389041e-01, /* 27 */ + 7.244359600749901e-01, /* 28 */ + 7.161434102129021e-01, /* 29 */ + 7.079457843841379e-01, /* 30 */ + 6.998419960022735e-01, /* 31 */ + 6.918309709189365e-01, /* 32 */ + 6.839116472814293e-01, /* 33 */ + 6.760829753919818e-01, /* 34 */ + 6.683439175686146e-01, /* 35 */ + 6.606934480075960e-01, /* 36 */ + 6.531305526474723e-01, /* 37 */ + 6.456542290346555e-01, /* 38 */ + 6.382634861905487e-01, /* 39 */ + 6.309573444801932e-01, /* 40 */ + 6.237348354824193e-01, /* 41 */ + 6.165950018614822e-01, /* 42 */ + 6.095368972401691e-01, /* 43 */ + 6.025595860743578e-01, /* 44 */ + 5.956621435290105e-01, /* 45 */ + 5.888436553555889e-01, /* 46 */ + 5.821032177708714e-01, /* 47 */ + 5.754399373371569e-01, /* 48 */ + 5.688529308438415e-01, /* 49 */ + 5.623413251903491e-01, /* 50 */ + 5.559042572704036e-01, /* 51 */ + 5.495408738576245e-01, /* 52 */ + 5.432503314924332e-01, /* 53 */ + 5.370317963702528e-01, /* 54 */ + 5.308844442309884e-01, /* 55 */ + 5.248074602497727e-01, /* 56 */ + 5.188000389289611e-01, /* 57 */ + 5.128613839913648e-01, /* 58 */ + 5.069907082747044e-01, /* 59 */ + 5.011872336272722e-01, /* 60 */ + 4.954501908047902e-01, /* 61 */ + 4.897788193684462e-01, /* 62 */ + 4.841723675840993e-01, /* 63 */ + 4.786300923226384e-01, /* 64 */ + 4.731512589614805e-01, /* 65 */ + 4.677351412871982e-01, /* 66 */ + 4.623810213992603e-01, /* 67 */ + 4.570881896148750e-01, /* 68 */ + 4.518559443749224e-01, /* 69 */ + 4.466835921509631e-01, /* 70 */ + 4.415704473533125e-01, /* 71 */ + 4.365158322401660e-01, /* 72 */ + 4.315190768277652e-01, /* 73 */ + 4.265795188015927e-01, /* 74 */ + 4.216965034285822e-01, /* 75 */ + 4.168693834703354e-01, /* 76 */ + 4.120975190973302e-01, /* 77 */ + 4.073802778041127e-01, /* 78 */ + 4.027170343254591e-01, /* 79 */ + 3.981071705534973e-01, /* 80 */ + 3.935500754557775e-01, /* 81 */ + 3.890451449942806e-01, /* 82 */ + 3.845917820453535e-01, /* 83 */ + 3.801893963205612e-01, /* 84 */ + 3.758374042884441e-01, /* 85 */ + 3.715352290971725e-01, /* 86 */ + 3.672823004980846e-01, /* 87 */ + 3.630780547701014e-01, /* 88 */ + 3.589219346450052e-01, /* 89 */ + 3.548133892335755e-01, /* 90 */ + 3.507518739525680e-01, /* 91 */ + 3.467368504525317e-01, /* 92 */ + 3.427677865464503e-01, /* 93 */ + 3.388441561392025e-01, /* 94 */ + 3.349654391578277e-01, /* 95 */ + 3.311311214825911e-01, /* 96 */ + 3.273406948788382e-01, /* 97 */ + 3.235936569296283e-01, /* 98 */ + 3.198895109691398e-01, /* 99 */ + 3.162277660168379e-01, /* 100 */ + 3.126079367123955e-01, /* 101 */ + 3.090295432513591e-01, /* 102 */ + 3.054921113215513e-01, /* 103 */ + 3.019951720402016e-01, /* 104 */ + 2.985382618917959e-01, /* 105 */ + 2.951209226666386e-01, /* 106 */ + 2.917427014001167e-01, /* 107 */ + 2.884031503126606e-01, /* 108 */ + 2.851018267503909e-01, /* 109 */ + 2.818382931264454e-01, /* 110 */ + 2.786121168629770e-01, /* 111 */ + 2.754228703338166e-01, /* 112 */ + 2.722701308077912e-01, /* 113 */ + 2.691534803926915e-01, /* 114 */ + 2.660725059798810e-01, /* 115 */ + 2.630267991895382e-01, /* 116 */ + 2.600159563165272e-01, /* 117 */ + 2.570395782768864e-01, /* 118 */ + 2.540972705549305e-01, /* 119 */ + 2.511886431509580e-01, /* 120 */ + 2.483133105295570e-01, /* 121 */ + 2.454708915685030e-01, /* 122 */ + 2.426610095082415e-01, /* 123 */ + 2.398832919019490e-01, /* 124 */ + 2.371373705661655e-01, /* 125 */ + 2.344228815319922e-01, /* 126 */ + 2.317394649968479e-01, /* 127 */ + 2.290867652767773e-01, /* 128 */ + 2.264644307593060e-01, /* 129 */ + 2.238721138568339e-01, /* 130 */ + 2.213094709605638e-01, /* 131 */ + 2.187761623949553e-01, /* 132 */ + 2.162718523727020e-01, /* 133 */ + 2.137962089502232e-01, /* 134 */ + 2.113489039836647e-01, /* 135 */ + 2.089296130854039e-01, /* 136 */ + 2.065380155810529e-01, /* 137 */ + 2.041737944669529e-01, /* 138 */ + 2.018366363681561e-01, /* 139 */ + 1.995262314968880e-01, /* 140 */ + 1.972422736114854e-01, /* 141 */ + 1.949844599758045e-01, /* 142 */ + 1.927524913190936e-01, /* 143 */ + 1.905460717963247e-01, /* 144 */ + 1.883649089489801e-01, /* 145 */ + 1.862087136662867e-01, /* 146 */ + 1.840772001468956e-01, /* 147 */ + 1.819700858609983e-01, /* 148 */ + 1.798870915128788e-01, /* 149 */ + 1.778279410038923e-01, /* 150 */ + 1.757923613958693e-01, /* 151 */ + 1.737800828749375e-01, /* 152 */ + 1.717908387157588e-01, /* 153 */ + 1.698243652461744e-01, /* 154 */ + 1.678804018122560e-01, /* 155 */ + 1.659586907437561e-01, /* 156 */ + 1.640589773199539e-01, /* 157 */ + 1.621810097358930e-01, /* 158 */ + 1.603245390690042e-01, /* 159 */ + 1.584893192461113e-01, /* 160 */ + 1.566751070108149e-01, /* 161 */ + 1.548816618912481e-01, /* 162 */ + 1.531087461682030e-01, /* 163 */ + 1.513561248436208e-01, /* 164 */ + 1.496235656094433e-01, /* 165 */ + 1.479108388168207e-01, /* 166 */ + 1.462177174456718e-01, /* 167 */ + 1.445439770745927e-01, /* 168 */ + 1.428893958511103e-01, /* 169 */ + 1.412537544622754e-01, /* 170 */ + 1.396368361055938e-01, /* 171 */ + 1.380384264602885e-01, /* 172 */ + 1.364583136588925e-01, /* 173 */ + 1.348962882591654e-01, /* 174 */ + 1.333521432163324e-01, /* 175 */ + 1.318256738556407e-01, /* 176 */ + 1.303166778452299e-01, /* 177 */ + 1.288249551693134e-01, /* 178 */ + 1.273503081016662e-01, /* 179 */ + 1.258925411794167e-01, /* 180 */ + 1.244514611771385e-01, /* 181 */ + 1.230268770812381e-01, /* 182 */ + 1.216186000646368e-01, /* 183 */ + 1.202264434617413e-01, /* 184 */ + 1.188502227437018e-01, /* 185 */ + 1.174897554939529e-01, /* 186 */ + 1.161448613840343e-01, /* 187 */ + 1.148153621496883e-01, /* 188 */ + 1.135010815672315e-01, /* 189 */ + 1.122018454301963e-01, /* 190 */ + 1.109174815262401e-01, /* 191 */ + 1.096478196143185e-01, /* 192 */ + 1.083926914021204e-01, /* 193 */ + 1.071519305237606e-01, /* 194 */ + 1.059253725177289e-01, /* 195 */ + 1.047128548050899e-01, /* 196 */ + 1.035142166679344e-01, /* 197 */ + 1.023292992280754e-01, /* 198 */ + 1.011579454259899e-01, /* 199 */ + 1.000000000000000e-01, /* 200 */ + 9.885530946569389e-02, /* 201 */ + 9.772372209558107e-02, /* 202 */ + 9.660508789898134e-02, /* 203 */ + 9.549925860214359e-02, /* 204 */ + 9.440608762859234e-02, /* 205 */ + 9.332543007969911e-02, /* 206 */ + 9.225714271547632e-02, /* 207 */ + 9.120108393559097e-02, /* 208 */ + 9.015711376059569e-02, /* 209 */ + 8.912509381337455e-02, /* 210 */ + 8.810488730080140e-02, /* 211 */ + 8.709635899560807e-02, /* 212 */ + 8.609937521846006e-02, /* 213 */ + 8.511380382023764e-02, /* 214 */ + 8.413951416451951e-02, /* 215 */ + 8.317637711026710e-02, /* 216 */ + 8.222426499470711e-02, /* 217 */ + 8.128305161640992e-02, /* 218 */ + 8.035261221856173e-02, /* 219 */ + 7.943282347242815e-02, /* 220 */ + 7.852356346100718e-02, /* 221 */ + 7.762471166286918e-02, /* 222 */ + 7.673614893618190e-02, /* 223 */ + 7.585775750291837e-02, /* 224 */ + 7.498942093324558e-02, /* 225 */ + 7.413102413009175e-02, /* 226 */ + 7.328245331389041e-02, /* 227 */ + 7.244359600749900e-02, /* 228 */ + 7.161434102129020e-02, /* 229 */ + 7.079457843841379e-02, /* 230 */ + 6.998419960022735e-02, /* 231 */ + 6.918309709189364e-02, /* 232 */ + 6.839116472814294e-02, /* 233 */ + 6.760829753919818e-02, /* 234 */ + 6.683439175686146e-02, /* 235 */ + 6.606934480075960e-02, /* 236 */ + 6.531305526474723e-02, /* 237 */ + 6.456542290346555e-02, /* 238 */ + 6.382634861905487e-02, /* 239 */ + 6.309573444801933e-02, /* 240 */ + 6.237348354824192e-02, /* 241 */ + 6.165950018614821e-02, /* 242 */ + 6.095368972401691e-02, /* 243 */ + 6.025595860743577e-02, /* 244 */ + 5.956621435290105e-02, /* 245 */ + 5.888436553555890e-02, /* 246 */ + 5.821032177708714e-02, /* 247 */ + 5.754399373371569e-02, /* 248 */ + 5.688529308438414e-02, /* 249 */ + 5.623413251903491e-02, /* 250 */ + 5.559042572704036e-02, /* 251 */ + 5.495408738576246e-02, /* 252 */ + 5.432503314924332e-02, /* 253 */ + 5.370317963702528e-02, /* 254 */ + 5.308844442309883e-02, /* 255 */ + 5.248074602497726e-02, /* 256 */ + 5.188000389289611e-02, /* 257 */ + 5.128613839913648e-02, /* 258 */ + 5.069907082747044e-02, /* 259 */ + 5.011872336272723e-02, /* 260 */ + 4.954501908047902e-02, /* 261 */ + 4.897788193684462e-02, /* 262 */ + 4.841723675840993e-02, /* 263 */ + 4.786300923226383e-02, /* 264 */ + 4.731512589614805e-02, /* 265 */ + 4.677351412871982e-02, /* 266 */ + 4.623810213992603e-02, /* 267 */ + 4.570881896148751e-02, /* 268 */ + 4.518559443749224e-02, /* 269 */ + 4.466835921509631e-02, /* 270 */ + 4.415704473533125e-02, /* 271 */ + 4.365158322401660e-02, /* 272 */ + 4.315190768277652e-02, /* 273 */ + 4.265795188015926e-02, /* 274 */ + 4.216965034285822e-02, /* 275 */ + 4.168693834703354e-02, /* 276 */ + 4.120975190973302e-02, /* 277 */ + 4.073802778041127e-02, /* 278 */ + 4.027170343254591e-02, /* 279 */ + 3.981071705534973e-02, /* 280 */ + 3.935500754557775e-02, /* 281 */ + 3.890451449942806e-02, /* 282 */ + 3.845917820453536e-02, /* 283 */ + 3.801893963205612e-02, /* 284 */ + 3.758374042884442e-02, /* 285 */ + 3.715352290971725e-02, /* 286 */ + 3.672823004980846e-02, /* 287 */ + 3.630780547701013e-02, /* 288 */ + 3.589219346450052e-02, /* 289 */ + 3.548133892335754e-02, /* 290 */ + 3.507518739525680e-02, /* 291 */ + 3.467368504525317e-02, /* 292 */ + 3.427677865464503e-02, /* 293 */ + 3.388441561392026e-02, /* 294 */ + 3.349654391578277e-02, /* 295 */ + 3.311311214825911e-02, /* 296 */ + 3.273406948788382e-02, /* 297 */ + 3.235936569296283e-02, /* 298 */ + 3.198895109691398e-02, /* 299 */ + 3.162277660168379e-02, /* 300 */ + 3.126079367123955e-02, /* 301 */ + 3.090295432513590e-02, /* 302 */ + 3.054921113215513e-02, /* 303 */ + 3.019951720402016e-02, /* 304 */ + 2.985382618917960e-02, /* 305 */ + 2.951209226666386e-02, /* 306 */ + 2.917427014001167e-02, /* 307 */ + 2.884031503126606e-02, /* 308 */ + 2.851018267503909e-02, /* 309 */ + 2.818382931264454e-02, /* 310 */ + 2.786121168629770e-02, /* 311 */ + 2.754228703338166e-02, /* 312 */ + 2.722701308077912e-02, /* 313 */ + 2.691534803926916e-02, /* 314 */ + 2.660725059798810e-02, /* 315 */ + 2.630267991895382e-02, /* 316 */ + 2.600159563165272e-02, /* 317 */ + 2.570395782768864e-02, /* 318 */ + 2.540972705549305e-02, /* 319 */ + 2.511886431509580e-02, /* 320 */ + 2.483133105295570e-02, /* 321 */ + 2.454708915685030e-02, /* 322 */ + 2.426610095082415e-02, /* 323 */ + 2.398832919019490e-02, /* 324 */ + 2.371373705661655e-02, /* 325 */ + 2.344228815319922e-02, /* 326 */ + 2.317394649968479e-02, /* 327 */ + 2.290867652767773e-02, /* 328 */ + 2.264644307593060e-02, /* 329 */ + 2.238721138568340e-02, /* 330 */ + 2.213094709605638e-02, /* 331 */ + 2.187761623949553e-02, /* 332 */ + 2.162718523727020e-02, /* 333 */ + 2.137962089502232e-02, /* 334 */ + 2.113489039836647e-02, /* 335 */ + 2.089296130854040e-02, /* 336 */ + 2.065380155810529e-02, /* 337 */ + 2.041737944669529e-02, /* 338 */ + 2.018366363681561e-02, /* 339 */ + 1.995262314968880e-02, /* 340 */ + 1.972422736114854e-02, /* 341 */ + 1.949844599758045e-02, /* 342 */ + 1.927524913190936e-02, /* 343 */ + 1.905460717963247e-02, /* 344 */ + 1.883649089489800e-02, /* 345 */ + 1.862087136662868e-02, /* 346 */ + 1.840772001468956e-02, /* 347 */ + 1.819700858609984e-02, /* 348 */ + 1.798870915128788e-02, /* 349 */ + 1.778279410038923e-02, /* 350 */ + 1.757923613958693e-02, /* 351 */ + 1.737800828749375e-02, /* 352 */ + 1.717908387157588e-02, /* 353 */ + 1.698243652461744e-02, /* 354 */ + 1.678804018122560e-02, /* 355 */ + 1.659586907437561e-02, /* 356 */ + 1.640589773199539e-02, /* 357 */ + 1.621810097358930e-02, /* 358 */ + 1.603245390690041e-02, /* 359 */ + 1.584893192461113e-02, /* 360 */ + 1.566751070108149e-02, /* 361 */ + 1.548816618912481e-02, /* 362 */ + 1.531087461682030e-02, /* 363 */ + 1.513561248436208e-02, /* 364 */ + 1.496235656094433e-02, /* 365 */ + 1.479108388168207e-02, /* 366 */ + 1.462177174456718e-02, /* 367 */ + 1.445439770745928e-02, /* 368 */ + 1.428893958511103e-02, /* 369 */ + 1.412537544622754e-02, /* 370 */ + 1.396368361055938e-02, /* 371 */ + 1.380384264602885e-02, /* 372 */ + 1.364583136588924e-02, /* 373 */ + 1.348962882591654e-02, /* 374 */ + 1.333521432163324e-02, /* 375 */ + 1.318256738556407e-02, /* 376 */ + 1.303166778452299e-02, /* 377 */ + 1.288249551693134e-02, /* 378 */ + 1.273503081016662e-02, /* 379 */ + 1.258925411794167e-02, /* 380 */ + 1.244514611771385e-02, /* 381 */ + 1.230268770812382e-02, /* 382 */ + 1.216186000646368e-02, /* 383 */ + 1.202264434617413e-02, /* 384 */ + 1.188502227437018e-02, /* 385 */ + 1.174897554939529e-02, /* 386 */ + 1.161448613840343e-02, /* 387 */ + 1.148153621496883e-02, /* 388 */ + 1.135010815672315e-02, /* 389 */ + 1.122018454301963e-02, /* 390 */ + 1.109174815262401e-02, /* 391 */ + 1.096478196143185e-02, /* 392 */ + 1.083926914021204e-02, /* 393 */ + 1.071519305237606e-02, /* 394 */ + 1.059253725177289e-02, /* 395 */ + 1.047128548050900e-02, /* 396 */ + 1.035142166679344e-02, /* 397 */ + 1.023292992280754e-02, /* 398 */ + 1.011579454259899e-02, /* 399 */ + 1.000000000000000e-02, /* 400 */ + 9.885530946569389e-03, /* 401 */ + 9.772372209558107e-03, /* 402 */ + 9.660508789898133e-03, /* 403 */ + 9.549925860214359e-03, /* 404 */ + 9.440608762859234e-03, /* 405 */ + 9.332543007969910e-03, /* 406 */ + 9.225714271547631e-03, /* 407 */ + 9.120108393559097e-03, /* 408 */ + 9.015711376059568e-03, /* 409 */ + 8.912509381337455e-03, /* 410 */ + 8.810488730080140e-03, /* 411 */ + 8.709635899560806e-03, /* 412 */ + 8.609937521846007e-03, /* 413 */ + 8.511380382023764e-03, /* 414 */ + 8.413951416451950e-03, /* 415 */ + 8.317637711026711e-03, /* 416 */ + 8.222426499470711e-03, /* 417 */ + 8.128305161640993e-03, /* 418 */ + 8.035261221856172e-03, /* 419 */ + 7.943282347242816e-03, /* 420 */ + 7.852356346100717e-03, /* 421 */ + 7.762471166286917e-03, /* 422 */ + 7.673614893618189e-03, /* 423 */ + 7.585775750291838e-03, /* 424 */ + 7.498942093324558e-03, /* 425 */ + 7.413102413009175e-03, /* 426 */ + 7.328245331389041e-03, /* 427 */ + 7.244359600749901e-03, /* 428 */ + 7.161434102129020e-03, /* 429 */ + 7.079457843841380e-03, /* 430 */ + 6.998419960022735e-03, /* 431 */ + 6.918309709189364e-03, /* 432 */ + 6.839116472814293e-03, /* 433 */ + 6.760829753919818e-03, /* 434 */ + 6.683439175686146e-03, /* 435 */ + 6.606934480075960e-03, /* 436 */ + 6.531305526474723e-03, /* 437 */ + 6.456542290346555e-03, /* 438 */ + 6.382634861905487e-03, /* 439 */ + 6.309573444801933e-03, /* 440 */ + 6.237348354824193e-03, /* 441 */ + 6.165950018614821e-03, /* 442 */ + 6.095368972401692e-03, /* 443 */ + 6.025595860743578e-03, /* 444 */ + 5.956621435290105e-03, /* 445 */ + 5.888436553555890e-03, /* 446 */ + 5.821032177708714e-03, /* 447 */ + 5.754399373371569e-03, /* 448 */ + 5.688529308438415e-03, /* 449 */ + 5.623413251903491e-03, /* 450 */ + 5.559042572704035e-03, /* 451 */ + 5.495408738576246e-03, /* 452 */ + 5.432503314924332e-03, /* 453 */ + 5.370317963702527e-03, /* 454 */ + 5.308844442309883e-03, /* 455 */ + 5.248074602497726e-03, /* 456 */ + 5.188000389289611e-03, /* 457 */ + 5.128613839913649e-03, /* 458 */ + 5.069907082747044e-03, /* 459 */ + 5.011872336272723e-03, /* 460 */ + 4.954501908047903e-03, /* 461 */ + 4.897788193684462e-03, /* 462 */ + 4.841723675840993e-03, /* 463 */ + 4.786300923226384e-03, /* 464 */ + 4.731512589614805e-03, /* 465 */ + 4.677351412871982e-03, /* 466 */ + 4.623810213992603e-03, /* 467 */ + 4.570881896148750e-03, /* 468 */ + 4.518559443749223e-03, /* 469 */ + 4.466835921509631e-03, /* 470 */ + 4.415704473533125e-03, /* 471 */ + 4.365158322401659e-03, /* 472 */ + 4.315190768277652e-03, /* 473 */ + 4.265795188015927e-03, /* 474 */ + 4.216965034285823e-03, /* 475 */ + 4.168693834703354e-03, /* 476 */ + 4.120975190973302e-03, /* 477 */ + 4.073802778041128e-03, /* 478 */ + 4.027170343254591e-03, /* 479 */ + 3.981071705534973e-03, /* 480 */ + 3.935500754557775e-03, /* 481 */ + 3.890451449942806e-03, /* 482 */ + 3.845917820453536e-03, /* 483 */ + 3.801893963205612e-03, /* 484 */ + 3.758374042884442e-03, /* 485 */ + 3.715352290971725e-03, /* 486 */ + 3.672823004980846e-03, /* 487 */ + 3.630780547701014e-03, /* 488 */ + 3.589219346450052e-03, /* 489 */ + 3.548133892335755e-03, /* 490 */ + 3.507518739525680e-03, /* 491 */ + 3.467368504525316e-03, /* 492 */ + 3.427677865464504e-03, /* 493 */ + 3.388441561392026e-03, /* 494 */ + 3.349654391578276e-03, /* 495 */ + 3.311311214825911e-03, /* 496 */ + 3.273406948788382e-03, /* 497 */ + 3.235936569296282e-03, /* 498 */ + 3.198895109691398e-03, /* 499 */ + 3.162277660168379e-03, /* 500 */ + 3.126079367123955e-03, /* 501 */ + 3.090295432513590e-03, /* 502 */ + 3.054921113215513e-03, /* 503 */ + 3.019951720402016e-03, /* 504 */ + 2.985382618917960e-03, /* 505 */ + 2.951209226666386e-03, /* 506 */ + 2.917427014001167e-03, /* 507 */ + 2.884031503126606e-03, /* 508 */ + 2.851018267503909e-03, /* 509 */ + 2.818382931264454e-03, /* 510 */ + 2.786121168629770e-03, /* 511 */ + 2.754228703338166e-03, /* 512 */ + 2.722701308077912e-03, /* 513 */ + 2.691534803926916e-03, /* 514 */ + 2.660725059798810e-03, /* 515 */ + 2.630267991895382e-03, /* 516 */ + 2.600159563165272e-03, /* 517 */ + 2.570395782768864e-03, /* 518 */ + 2.540972705549305e-03, /* 519 */ + 2.511886431509580e-03, /* 520 */ + 2.483133105295570e-03, /* 521 */ + 2.454708915685030e-03, /* 522 */ + 2.426610095082416e-03, /* 523 */ + 2.398832919019490e-03, /* 524 */ + 2.371373705661655e-03, /* 525 */ + 2.344228815319922e-03, /* 526 */ + 2.317394649968479e-03, /* 527 */ + 2.290867652767773e-03, /* 528 */ + 2.264644307593060e-03, /* 529 */ + 2.238721138568339e-03, /* 530 */ + 2.213094709605638e-03, /* 531 */ + 2.187761623949552e-03, /* 532 */ + 2.162718523727020e-03, /* 533 */ + 2.137962089502232e-03, /* 534 */ + 2.113489039836647e-03, /* 535 */ + 2.089296130854039e-03, /* 536 */ + 2.065380155810529e-03, /* 537 */ + 2.041737944669529e-03, /* 538 */ + 2.018366363681561e-03, /* 539 */ + 1.995262314968880e-03, /* 540 */ + 1.972422736114854e-03, /* 541 */ + 1.949844599758045e-03, /* 542 */ + 1.927524913190936e-03, /* 543 */ + 1.905460717963247e-03, /* 544 */ + 1.883649089489801e-03, /* 545 */ + 1.862087136662868e-03, /* 546 */ + 1.840772001468956e-03, /* 547 */ + 1.819700858609984e-03, /* 548 */ + 1.798870915128788e-03, /* 549 */ + 1.778279410038923e-03, /* 550 */ + 1.757923613958693e-03, /* 551 */ + 1.737800828749375e-03, /* 552 */ + 1.717908387157588e-03, /* 553 */ + 1.698243652461744e-03, /* 554 */ + 1.678804018122560e-03, /* 555 */ + 1.659586907437561e-03, /* 556 */ + 1.640589773199539e-03, /* 557 */ + 1.621810097358930e-03, /* 558 */ + 1.603245390690041e-03, /* 559 */ + 1.584893192461113e-03, /* 560 */ + 1.566751070108149e-03, /* 561 */ + 1.548816618912481e-03, /* 562 */ + 1.531087461682030e-03, /* 563 */ + 1.513561248436208e-03, /* 564 */ + 1.496235656094433e-03, /* 565 */ + 1.479108388168207e-03, /* 566 */ + 1.462177174456718e-03, /* 567 */ + 1.445439770745928e-03, /* 568 */ + 1.428893958511103e-03, /* 569 */ + 1.412537544622754e-03, /* 570 */ + 1.396368361055938e-03, /* 571 */ + 1.380384264602885e-03, /* 572 */ + 1.364583136588925e-03, /* 573 */ + 1.348962882591654e-03, /* 574 */ + 1.333521432163324e-03, /* 575 */ + 1.318256738556407e-03, /* 576 */ + 1.303166778452299e-03, /* 577 */ + 1.288249551693134e-03, /* 578 */ + 1.273503081016662e-03, /* 579 */ + 1.258925411794167e-03, /* 580 */ + 1.244514611771385e-03, /* 581 */ + 1.230268770812381e-03, /* 582 */ + 1.216186000646368e-03, /* 583 */ + 1.202264434617413e-03, /* 584 */ + 1.188502227437018e-03, /* 585 */ + 1.174897554939529e-03, /* 586 */ + 1.161448613840343e-03, /* 587 */ + 1.148153621496883e-03, /* 588 */ + 1.135010815672315e-03, /* 589 */ + 1.122018454301963e-03, /* 590 */ + 1.109174815262401e-03, /* 591 */ + 1.096478196143185e-03, /* 592 */ + 1.083926914021204e-03, /* 593 */ + 1.071519305237606e-03, /* 594 */ + 1.059253725177289e-03, /* 595 */ + 1.047128548050900e-03, /* 596 */ + 1.035142166679344e-03, /* 597 */ + 1.023292992280754e-03, /* 598 */ + 1.011579454259898e-03, /* 599 */ + 1.000000000000000e-03, /* 600 */ + 9.885530946569388e-04, /* 601 */ + 9.772372209558107e-04, /* 602 */ + 9.660508789898134e-04, /* 603 */ + 9.549925860214359e-04, /* 604 */ + 9.440608762859234e-04, /* 605 */ + 9.332543007969911e-04, /* 606 */ + 9.225714271547631e-04, /* 607 */ + 9.120108393559097e-04, /* 608 */ + 9.015711376059569e-04, /* 609 */ + 8.912509381337455e-04, /* 610 */ + 8.810488730080141e-04, /* 611 */ + 8.709635899560806e-04, /* 612 */ + 8.609937521846006e-04, /* 613 */ + 8.511380382023765e-04, /* 614 */ + 8.413951416451951e-04, /* 615 */ + 8.317637711026710e-04, /* 616 */ + 8.222426499470711e-04, /* 617 */ + 8.128305161640993e-04, /* 618 */ + 8.035261221856172e-04, /* 619 */ + 7.943282347242815e-04, /* 620 */ + 7.852356346100718e-04, /* 621 */ + 7.762471166286917e-04, /* 622 */ + 7.673614893618189e-04, /* 623 */ + 7.585775750291837e-04, /* 624 */ + 7.498942093324559e-04, /* 625 */ + 7.413102413009175e-04, /* 626 */ + 7.328245331389041e-04, /* 627 */ + 7.244359600749900e-04, /* 628 */ + 7.161434102129020e-04, /* 629 */ + 7.079457843841379e-04, /* 630 */ + 6.998419960022735e-04, /* 631 */ + 6.918309709189364e-04, /* 632 */ + 6.839116472814293e-04, /* 633 */ + 6.760829753919818e-04, /* 634 */ + 6.683439175686146e-04, /* 635 */ + 6.606934480075960e-04, /* 636 */ + 6.531305526474723e-04, /* 637 */ + 6.456542290346556e-04, /* 638 */ + 6.382634861905487e-04, /* 639 */ + 6.309573444801932e-04, /* 640 */ + 6.237348354824193e-04, /* 641 */ + 6.165950018614822e-04, /* 642 */ + 6.095368972401691e-04, /* 643 */ + 6.025595860743578e-04, /* 644 */ + 5.956621435290105e-04, /* 645 */ + 5.888436553555889e-04, /* 646 */ + 5.821032177708714e-04, /* 647 */ + 5.754399373371570e-04, /* 648 */ + 5.688529308438415e-04, /* 649 */ + 5.623413251903491e-04, /* 650 */ + 5.559042572704036e-04, /* 651 */ + 5.495408738576246e-04, /* 652 */ + 5.432503314924332e-04, /* 653 */ + 5.370317963702527e-04, /* 654 */ + 5.308844442309884e-04, /* 655 */ + 5.248074602497726e-04, /* 656 */ + 5.188000389289611e-04, /* 657 */ + 5.128613839913648e-04, /* 658 */ + 5.069907082747043e-04, /* 659 */ + 5.011872336272723e-04, /* 660 */ + 4.954501908047902e-04, /* 661 */ + 4.897788193684462e-04, /* 662 */ + 4.841723675840993e-04, /* 663 */ + 4.786300923226383e-04, /* 664 */ + 4.731512589614805e-04, /* 665 */ + 4.677351412871982e-04, /* 666 */ + 4.623810213992603e-04, /* 667 */ + 4.570881896148750e-04, /* 668 */ + 4.518559443749224e-04, /* 669 */ + 4.466835921509631e-04, /* 670 */ + 4.415704473533125e-04, /* 671 */ + 4.365158322401659e-04, /* 672 */ + 4.315190768277652e-04, /* 673 */ + 4.265795188015927e-04, /* 674 */ + 4.216965034285822e-04, /* 675 */ + 4.168693834703354e-04, /* 676 */ + 4.120975190973302e-04, /* 677 */ + 4.073802778041127e-04, /* 678 */ + 4.027170343254591e-04, /* 679 */ + 3.981071705534972e-04, /* 680 */ + 3.935500754557775e-04, /* 681 */ + 3.890451449942806e-04, /* 682 */ + 3.845917820453535e-04, /* 683 */ + 3.801893963205612e-04, /* 684 */ + 3.758374042884442e-04, /* 685 */ + 3.715352290971725e-04, /* 686 */ + 3.672823004980847e-04, /* 687 */ + 3.630780547701013e-04, /* 688 */ + 3.589219346450052e-04, /* 689 */ + 3.548133892335755e-04, /* 690 */ + 3.507518739525680e-04, /* 691 */ + 3.467368504525316e-04, /* 692 */ + 3.427677865464504e-04, /* 693 */ + 3.388441561392026e-04, /* 694 */ + 3.349654391578277e-04, /* 695 */ + 3.311311214825911e-04, /* 696 */ + 3.273406948788382e-04, /* 697 */ + 3.235936569296283e-04, /* 698 */ + 3.198895109691398e-04, /* 699 */ + 3.162277660168379e-04, /* 700 */ + 3.126079367123955e-04, /* 701 */ + 3.090295432513590e-04, /* 702 */ + 3.054921113215513e-04, /* 703 */ + 3.019951720402016e-04, /* 704 */ + 2.985382618917959e-04, /* 705 */ + 2.951209226666386e-04, /* 706 */ + 2.917427014001167e-04, /* 707 */ + 2.884031503126606e-04, /* 708 */ + 2.851018267503909e-04, /* 709 */ + 2.818382931264454e-04, /* 710 */ + 2.786121168629771e-04, /* 711 */ + 2.754228703338166e-04, /* 712 */ + 2.722701308077912e-04, /* 713 */ + 2.691534803926916e-04, /* 714 */ + 2.660725059798809e-04, /* 715 */ + 2.630267991895382e-04, /* 716 */ + 2.600159563165272e-04, /* 717 */ + 2.570395782768864e-04, /* 718 */ + 2.540972705549305e-04, /* 719 */ + 2.511886431509580e-04, /* 720 */ + 2.483133105295570e-04, /* 721 */ + 2.454708915685030e-04, /* 722 */ + 2.426610095082416e-04, /* 723 */ + 2.398832919019490e-04, /* 724 */ + 2.371373705661655e-04, /* 725 */ + 2.344228815319922e-04, /* 726 */ + 2.317394649968479e-04, /* 727 */ + 2.290867652767773e-04, /* 728 */ + 2.264644307593060e-04, /* 729 */ + 2.238721138568340e-04, /* 730 */ + 2.213094709605638e-04, /* 731 */ + 2.187761623949553e-04, /* 732 */ + 2.162718523727020e-04, /* 733 */ + 2.137962089502232e-04, /* 734 */ + 2.113489039836647e-04, /* 735 */ + 2.089296130854040e-04, /* 736 */ + 2.065380155810529e-04, /* 737 */ + 2.041737944669529e-04, /* 738 */ + 2.018366363681561e-04, /* 739 */ + 1.995262314968880e-04, /* 740 */ + 1.972422736114854e-04, /* 741 */ + 1.949844599758045e-04, /* 742 */ + 1.927524913190936e-04, /* 743 */ + 1.905460717963247e-04, /* 744 */ + 1.883649089489800e-04, /* 745 */ + 1.862087136662868e-04, /* 746 */ + 1.840772001468956e-04, /* 747 */ + 1.819700858609983e-04, /* 748 */ + 1.798870915128788e-04, /* 749 */ + 1.778279410038923e-04, /* 750 */ + 1.757923613958693e-04, /* 751 */ + 1.737800828749376e-04, /* 752 */ + 1.717908387157588e-04, /* 753 */ + 1.698243652461744e-04, /* 754 */ + 1.678804018122560e-04, /* 755 */ + 1.659586907437561e-04, /* 756 */ + 1.640589773199539e-04, /* 757 */ + 1.621810097358930e-04, /* 758 */ + 1.603245390690042e-04, /* 759 */ + 1.584893192461113e-04, /* 760 */ + 1.566751070108149e-04, /* 761 */ + 1.548816618912481e-04, /* 762 */ + 1.531087461682030e-04, /* 763 */ + 1.513561248436208e-04, /* 764 */ + 1.496235656094433e-04, /* 765 */ + 1.479108388168207e-04, /* 766 */ + 1.462177174456718e-04, /* 767 */ + 1.445439770745927e-04, /* 768 */ + 1.428893958511103e-04, /* 769 */ + 1.412537544622754e-04, /* 770 */ + 1.396368361055938e-04, /* 771 */ + 1.380384264602885e-04, /* 772 */ + 1.364583136588925e-04, /* 773 */ + 1.348962882591654e-04, /* 774 */ + 1.333521432163324e-04, /* 775 */ + 1.318256738556407e-04, /* 776 */ + 1.303166778452299e-04, /* 777 */ + 1.288249551693134e-04, /* 778 */ + 1.273503081016662e-04, /* 779 */ + 1.258925411794167e-04, /* 780 */ + 1.244514611771385e-04, /* 781 */ + 1.230268770812382e-04, /* 782 */ + 1.216186000646368e-04, /* 783 */ + 1.202264434617413e-04, /* 784 */ + 1.188502227437018e-04, /* 785 */ + 1.174897554939530e-04, /* 786 */ + 1.161448613840343e-04, /* 787 */ + 1.148153621496883e-04, /* 788 */ + 1.135010815672315e-04, /* 789 */ + 1.122018454301963e-04, /* 790 */ + 1.109174815262401e-04, /* 791 */ + 1.096478196143185e-04, /* 792 */ + 1.083926914021204e-04, /* 793 */ + 1.071519305237606e-04, /* 794 */ + 1.059253725177289e-04, /* 795 */ + 1.047128548050899e-04, /* 796 */ + 1.035142166679344e-04, /* 797 */ + 1.023292992280754e-04, /* 798 */ + 1.011579454259898e-04, /* 799 */ + 1.000000000000000e-04, /* 800 */ + 9.885530946569389e-05, /* 801 */ + 9.772372209558107e-05, /* 802 */ + 9.660508789898133e-05, /* 803 */ + 9.549925860214359e-05, /* 804 */ + 9.440608762859233e-05, /* 805 */ + 9.332543007969910e-05, /* 806 */ + 9.225714271547631e-05, /* 807 */ + 9.120108393559098e-05, /* 808 */ + 9.015711376059569e-05, /* 809 */ + 8.912509381337455e-05, /* 810 */ + 8.810488730080140e-05, /* 811 */ + 8.709635899560807e-05, /* 812 */ + 8.609937521846007e-05, /* 813 */ + 8.511380382023765e-05, /* 814 */ + 8.413951416451952e-05, /* 815 */ + 8.317637711026710e-05, /* 816 */ + 8.222426499470712e-05, /* 817 */ + 8.128305161640992e-05, /* 818 */ + 8.035261221856173e-05, /* 819 */ + 7.943282347242815e-05, /* 820 */ + 7.852356346100718e-05, /* 821 */ + 7.762471166286918e-05, /* 822 */ + 7.673614893618189e-05, /* 823 */ + 7.585775750291837e-05, /* 824 */ + 7.498942093324559e-05, /* 825 */ + 7.413102413009175e-05, /* 826 */ + 7.328245331389041e-05, /* 827 */ + 7.244359600749901e-05, /* 828 */ + 7.161434102129020e-05, /* 829 */ + 7.079457843841379e-05, /* 830 */ + 6.998419960022735e-05, /* 831 */ + 6.918309709189365e-05, /* 832 */ + 6.839116472814293e-05, /* 833 */ + 6.760829753919818e-05, /* 834 */ + 6.683439175686147e-05, /* 835 */ + 6.606934480075961e-05, /* 836 */ + 6.531305526474724e-05, /* 837 */ + 6.456542290346555e-05, /* 838 */ + 6.382634861905487e-05, /* 839 */ + 6.309573444801932e-05, /* 840 */ + 6.237348354824193e-05, /* 841 */ + 6.165950018614821e-05, /* 842 */ + 6.095368972401692e-05, /* 843 */ + 6.025595860743577e-05, /* 844 */ + 5.956621435290105e-05, /* 845 */ + 5.888436553555889e-05, /* 846 */ + 5.821032177708714e-05, /* 847 */ + 5.754399373371569e-05, /* 848 */ + 5.688529308438414e-05, /* 849 */ + 5.623413251903491e-05, /* 850 */ + 5.559042572704036e-05, /* 851 */ + 5.495408738576245e-05, /* 852 */ + 5.432503314924332e-05, /* 853 */ + 5.370317963702527e-05, /* 854 */ + 5.308844442309884e-05, /* 855 */ + 5.248074602497726e-05, /* 856 */ + 5.188000389289611e-05, /* 857 */ + 5.128613839913649e-05, /* 858 */ + 5.069907082747044e-05, /* 859 */ + 5.011872336272723e-05, /* 860 */ + 4.954501908047902e-05, /* 861 */ + 4.897788193684462e-05, /* 862 */ + 4.841723675840994e-05, /* 863 */ + 4.786300923226384e-05, /* 864 */ + 4.731512589614805e-05, /* 865 */ + 4.677351412871982e-05, /* 866 */ + 4.623810213992603e-05, /* 867 */ + 4.570881896148750e-05, /* 868 */ + 4.518559443749224e-05, /* 869 */ + 4.466835921509631e-05, /* 870 */ + 4.415704473533125e-05, /* 871 */ + 4.365158322401660e-05, /* 872 */ + 4.315190768277652e-05, /* 873 */ + 4.265795188015926e-05, /* 874 */ + 4.216965034285822e-05, /* 875 */ + 4.168693834703354e-05, /* 876 */ + 4.120975190973302e-05, /* 877 */ + 4.073802778041127e-05, /* 878 */ + 4.027170343254591e-05, /* 879 */ + 3.981071705534972e-05, /* 880 */ + 3.935500754557775e-05, /* 881 */ + 3.890451449942806e-05, /* 882 */ + 3.845917820453536e-05, /* 883 */ + 3.801893963205612e-05, /* 884 */ + 3.758374042884442e-05, /* 885 */ + 3.715352290971725e-05, /* 886 */ + 3.672823004980847e-05, /* 887 */ + 3.630780547701013e-05, /* 888 */ + 3.589219346450052e-05, /* 889 */ + 3.548133892335755e-05, /* 890 */ + 3.507518739525680e-05, /* 891 */ + 3.467368504525316e-05, /* 892 */ + 3.427677865464504e-05, /* 893 */ + 3.388441561392026e-05, /* 894 */ + 3.349654391578277e-05, /* 895 */ + 3.311311214825911e-05, /* 896 */ + 3.273406948788382e-05, /* 897 */ + 3.235936569296283e-05, /* 898 */ + 3.198895109691398e-05, /* 899 */ + 3.162277660168380e-05, /* 900 */ + 3.126079367123955e-05, /* 901 */ + 3.090295432513591e-05, /* 902 */ + 3.054921113215513e-05, /* 903 */ + 3.019951720402016e-05, /* 904 */ + 2.985382618917960e-05, /* 905 */ + 2.951209226666386e-05, /* 906 */ + 2.917427014001167e-05, /* 907 */ + 2.884031503126606e-05, /* 908 */ + 2.851018267503909e-05, /* 909 */ + 2.818382931264454e-05, /* 910 */ + 2.786121168629771e-05, /* 911 */ + 2.754228703338166e-05, /* 912 */ + 2.722701308077912e-05, /* 913 */ + 2.691534803926916e-05, /* 914 */ + 2.660725059798809e-05, /* 915 */ + 2.630267991895382e-05, /* 916 */ + 2.600159563165272e-05, /* 917 */ + 2.570395782768864e-05, /* 918 */ + 2.540972705549305e-05, /* 919 */ + 2.511886431509580e-05, /* 920 */ + 2.483133105295570e-05, /* 921 */ + 2.454708915685030e-05, /* 922 */ + 2.426610095082415e-05, /* 923 */ + 2.398832919019490e-05, /* 924 */ + 2.371373705661655e-05, /* 925 */ + 2.344228815319922e-05, /* 926 */ + 2.317394649968478e-05, /* 927 */ + 2.290867652767773e-05, /* 928 */ + 2.264644307593060e-05, /* 929 */ + 2.238721138568340e-05, /* 930 */ + 2.213094709605638e-05, /* 931 */ + 2.187761623949553e-05, /* 932 */ + 2.162718523727020e-05, /* 933 */ + 2.137962089502232e-05, /* 934 */ + 2.113489039836647e-05, /* 935 */ + 2.089296130854040e-05, /* 936 */ + 2.065380155810529e-05, /* 937 */ + 2.041737944669529e-05, /* 938 */ + 2.018366363681561e-05, /* 939 */ + 1.995262314968880e-05, /* 940 */ + 1.972422736114854e-05, /* 941 */ + 1.949844599758045e-05, /* 942 */ + 1.927524913190936e-05, /* 943 */ + 1.905460717963247e-05, /* 944 */ + 1.883649089489800e-05, /* 945 */ + 1.862087136662867e-05, /* 946 */ + 1.840772001468956e-05, /* 947 */ + 1.819700858609983e-05, /* 948 */ + 1.798870915128788e-05, /* 949 */ + 1.778279410038923e-05, /* 950 */ + 1.757923613958692e-05, /* 951 */ + 1.737800828749375e-05, /* 952 */ + 1.717908387157588e-05, /* 953 */ + 1.698243652461744e-05, /* 954 */ + 1.678804018122560e-05, /* 955 */ + 1.659586907437560e-05, /* 956 */ + 1.640589773199539e-05, /* 957 */ + 1.621810097358930e-05, /* 958 */ + 1.603245390690041e-05, /* 959 */ + 1.584893192461113e-05, /* 960 */ + 1.566751070108149e-05, /* 961 */ + 1.548816618912481e-05, /* 962 */ + 1.531087461682030e-05, /* 963 */ + 1.513561248436208e-05, /* 964 */ + 1.496235656094433e-05, /* 965 */ + 1.479108388168207e-05, /* 966 */ + 1.462177174456718e-05, /* 967 */ + 1.445439770745928e-05, /* 968 */ + 1.428893958511103e-05, /* 969 */ + 1.412537544622754e-05, /* 970 */ + 1.396368361055938e-05, /* 971 */ + 1.380384264602885e-05, /* 972 */ + 1.364583136588924e-05, /* 973 */ + 1.348962882591654e-05, /* 974 */ + 1.333521432163324e-05, /* 975 */ + 1.318256738556407e-05, /* 976 */ + 1.303166778452299e-05, /* 977 */ + 1.288249551693134e-05, /* 978 */ + 1.273503081016662e-05, /* 979 */ + 1.258925411794167e-05, /* 980 */ + 1.244514611771385e-05, /* 981 */ + 1.230268770812382e-05, /* 982 */ + 1.216186000646368e-05, /* 983 */ + 1.202264434617413e-05, /* 984 */ + 1.188502227437018e-05, /* 985 */ + 1.174897554939530e-05, /* 986 */ + 1.161448613840343e-05, /* 987 */ + 1.148153621496883e-05, /* 988 */ + 1.135010815672315e-05, /* 989 */ + 1.122018454301964e-05, /* 990 */ + 1.109174815262401e-05, /* 991 */ + 1.096478196143185e-05, /* 992 */ + 1.083926914021204e-05, /* 993 */ + 1.071519305237606e-05, /* 994 */ + 1.059253725177289e-05, /* 995 */ + 1.047128548050900e-05, /* 996 */ + 1.035142166679344e-05, /* 997 */ + 1.023292992280754e-05, /* 998 */ + 1.011579454259898e-05, /* 999 */ + 1.000000000000000e-05, /* 1000 */ + 9.885530946569389e-06, /* 1001 */ + 9.772372209558108e-06, /* 1002 */ + 9.660508789898134e-06, /* 1003 */ + 9.549925860214359e-06, /* 1004 */ + 9.440608762859234e-06, /* 1005 */ + 9.332543007969911e-06, /* 1006 */ + 9.225714271547632e-06, /* 1007 */ + 9.120108393559098e-06, /* 1008 */ + 9.015711376059568e-06, /* 1009 */ + 8.912509381337456e-06, /* 1010 */ + 8.810488730080140e-06, /* 1011 */ + 8.709635899560806e-06, /* 1012 */ + 8.609937521846006e-06, /* 1013 */ + 8.511380382023765e-06, /* 1014 */ + 8.413951416451952e-06, /* 1015 */ + 8.317637711026709e-06, /* 1016 */ + 8.222426499470711e-06, /* 1017 */ + 8.128305161640993e-06, /* 1018 */ + 8.035261221856173e-06, /* 1019 */ + 7.943282347242815e-06, /* 1020 */ + 7.852356346100718e-06, /* 1021 */ + 7.762471166286918e-06, /* 1022 */ + 7.673614893618189e-06, /* 1023 */ + 7.585775750291837e-06, /* 1024 */ + 7.498942093324558e-06, /* 1025 */ + 7.413102413009175e-06, /* 1026 */ + 7.328245331389041e-06, /* 1027 */ + 7.244359600749901e-06, /* 1028 */ + 7.161434102129020e-06, /* 1029 */ + 7.079457843841379e-06, /* 1030 */ + 6.998419960022735e-06, /* 1031 */ + 6.918309709189365e-06, /* 1032 */ + 6.839116472814293e-06, /* 1033 */ + 6.760829753919818e-06, /* 1034 */ + 6.683439175686146e-06, /* 1035 */ + 6.606934480075960e-06, /* 1036 */ + 6.531305526474723e-06, /* 1037 */ + 6.456542290346555e-06, /* 1038 */ + 6.382634861905487e-06, /* 1039 */ + 6.309573444801932e-06, /* 1040 */ + 6.237348354824192e-06, /* 1041 */ + 6.165950018614822e-06, /* 1042 */ + 6.095368972401692e-06, /* 1043 */ + 6.025595860743577e-06, /* 1044 */ + 5.956621435290104e-06, /* 1045 */ + 5.888436553555889e-06, /* 1046 */ + 5.821032177708714e-06, /* 1047 */ + 5.754399373371569e-06, /* 1048 */ + 5.688529308438415e-06, /* 1049 */ + 5.623413251903491e-06, /* 1050 */ + 5.559042572704035e-06, /* 1051 */ + 5.495408738576246e-06, /* 1052 */ + 5.432503314924332e-06, /* 1053 */ + 5.370317963702528e-06, /* 1054 */ + 5.308844442309884e-06, /* 1055 */ + 5.248074602497726e-06, /* 1056 */ + 5.188000389289611e-06, /* 1057 */ + 5.128613839913649e-06, /* 1058 */ + 5.069907082747044e-06, /* 1059 */ + 5.011872336272722e-06, /* 1060 */ + 4.954501908047903e-06, /* 1061 */ + 4.897788193684462e-06, /* 1062 */ + 4.841723675840994e-06, /* 1063 */ + 4.786300923226383e-06, /* 1064 */ + 4.731512589614805e-06, /* 1065 */ + 4.677351412871982e-06, /* 1066 */ + 4.623810213992603e-06, /* 1067 */ + 4.570881896148750e-06, /* 1068 */ + 4.518559443749223e-06, /* 1069 */ + 4.466835921509631e-06, /* 1070 */ + 4.415704473533125e-06, /* 1071 */ + 4.365158322401660e-06, /* 1072 */ + 4.315190768277652e-06, /* 1073 */ + 4.265795188015927e-06, /* 1074 */ + 4.216965034285822e-06, /* 1075 */ + 4.168693834703354e-06, /* 1076 */ + 4.120975190973302e-06, /* 1077 */ + 4.073802778041127e-06, /* 1078 */ + 4.027170343254591e-06, /* 1079 */ + 3.981071705534973e-06, /* 1080 */ + 3.935500754557774e-06, /* 1081 */ + 3.890451449942806e-06, /* 1082 */ + 3.845917820453536e-06, /* 1083 */ + 3.801893963205612e-06, /* 1084 */ + 3.758374042884442e-06, /* 1085 */ + 3.715352290971725e-06, /* 1086 */ + 3.672823004980847e-06, /* 1087 */ + 3.630780547701013e-06, /* 1088 */ + 3.589219346450052e-06, /* 1089 */ + 3.548133892335755e-06, /* 1090 */ + 3.507518739525680e-06, /* 1091 */ + 3.467368504525316e-06, /* 1092 */ + 3.427677865464503e-06, /* 1093 */ + 3.388441561392025e-06, /* 1094 */ + 3.349654391578277e-06, /* 1095 */ + 3.311311214825911e-06, /* 1096 */ + 3.273406948788382e-06, /* 1097 */ + 3.235936569296283e-06, /* 1098 */ + 3.198895109691398e-06, /* 1099 */ + 3.162277660168379e-06, /* 1100 */ + 3.126079367123955e-06, /* 1101 */ + 3.090295432513591e-06, /* 1102 */ + 3.054921113215513e-06, /* 1103 */ + 3.019951720402016e-06, /* 1104 */ + 2.985382618917960e-06, /* 1105 */ + 2.951209226666386e-06, /* 1106 */ + 2.917427014001167e-06, /* 1107 */ + 2.884031503126606e-06, /* 1108 */ + 2.851018267503909e-06, /* 1109 */ + 2.818382931264454e-06, /* 1110 */ + 2.786121168629770e-06, /* 1111 */ + 2.754228703338166e-06, /* 1112 */ + 2.722701308077913e-06, /* 1113 */ + 2.691534803926916e-06, /* 1114 */ + 2.660725059798810e-06, /* 1115 */ + 2.630267991895382e-06, /* 1116 */ + 2.600159563165272e-06, /* 1117 */ + 2.570395782768864e-06, /* 1118 */ + 2.540972705549305e-06, /* 1119 */ + 2.511886431509580e-06, /* 1120 */ + 2.483133105295570e-06, /* 1121 */ + 2.454708915685031e-06, /* 1122 */ + 2.426610095082416e-06, /* 1123 */ + 2.398832919019491e-06, /* 1124 */ + 2.371373705661655e-06, /* 1125 */ + 2.344228815319922e-06, /* 1126 */ + 2.317394649968478e-06, /* 1127 */ + 2.290867652767773e-06, /* 1128 */ + 2.264644307593060e-06, /* 1129 */ + 2.238721138568340e-06, /* 1130 */ + 2.213094709605638e-06, /* 1131 */ + 2.187761623949553e-06, /* 1132 */ + 2.162718523727020e-06, /* 1133 */ + 2.137962089502232e-06, /* 1134 */ + 2.113489039836647e-06, /* 1135 */ + 2.089296130854039e-06, /* 1136 */ + 2.065380155810529e-06, /* 1137 */ + 2.041737944669529e-06, /* 1138 */ + 2.018366363681561e-06, /* 1139 */ + 1.995262314968880e-06, /* 1140 */ + 1.972422736114854e-06, /* 1141 */ + 1.949844599758045e-06, /* 1142 */ + 1.927524913190936e-06, /* 1143 */ + 1.905460717963247e-06, /* 1144 */ + 1.883649089489801e-06, /* 1145 */ + 1.862087136662868e-06, /* 1146 */ + 1.840772001468956e-06, /* 1147 */ + 1.819700858609983e-06, /* 1148 */ + 1.798870915128788e-06, /* 1149 */ + 1.778279410038923e-06, /* 1150 */ + 1.757923613958693e-06, /* 1151 */ + 1.737800828749375e-06, /* 1152 */ + 1.717908387157588e-06, /* 1153 */ + 1.698243652461744e-06, /* 1154 */ + 1.678804018122560e-06, /* 1155 */ + 1.659586907437561e-06, /* 1156 */ + 1.640589773199539e-06, /* 1157 */ + 1.621810097358930e-06, /* 1158 */ + 1.603245390690041e-06, /* 1159 */ + 1.584893192461113e-06, /* 1160 */ + 1.566751070108149e-06, /* 1161 */ + 1.548816618912481e-06, /* 1162 */ + 1.531087461682030e-06, /* 1163 */ + 1.513561248436208e-06, /* 1164 */ + 1.496235656094434e-06, /* 1165 */ + 1.479108388168207e-06, /* 1166 */ + 1.462177174456718e-06, /* 1167 */ + 1.445439770745928e-06, /* 1168 */ + 1.428893958511103e-06, /* 1169 */ + 1.412537544622754e-06, /* 1170 */ + 1.396368361055938e-06, /* 1171 */ + 1.380384264602885e-06, /* 1172 */ + 1.364583136588924e-06, /* 1173 */ + 1.348962882591654e-06, /* 1174 */ + 1.333521432163324e-06, /* 1175 */ + 1.318256738556407e-06, /* 1176 */ + 1.303166778452299e-06, /* 1177 */ + 1.288249551693134e-06, /* 1178 */ + 1.273503081016662e-06, /* 1179 */ + 1.258925411794167e-06, /* 1180 */ + 1.244514611771385e-06, /* 1181 */ + 1.230268770812382e-06, /* 1182 */ + 1.216186000646368e-06, /* 1183 */ + 1.202264434617413e-06, /* 1184 */ + 1.188502227437018e-06, /* 1185 */ + 1.174897554939530e-06, /* 1186 */ + 1.161448613840343e-06, /* 1187 */ + 1.148153621496883e-06, /* 1188 */ + 1.135010815672315e-06, /* 1189 */ + 1.122018454301963e-06, /* 1190 */ + 1.109174815262401e-06, /* 1191 */ + 1.096478196143185e-06, /* 1192 */ + 1.083926914021203e-06, /* 1193 */ + 1.071519305237606e-06, /* 1194 */ + 1.059253725177289e-06, /* 1195 */ + 1.047128548050900e-06, /* 1196 */ + 1.035142166679344e-06, /* 1197 */ + 1.023292992280754e-06, /* 1198 */ + 1.011579454259898e-06, /* 1199 */ + 1.000000000000000e-06, /* 1200 */ + 9.885530946569389e-07, /* 1201 */ + 9.772372209558107e-07, /* 1202 */ + 9.660508789898133e-07, /* 1203 */ + 9.549925860214360e-07, /* 1204 */ + 9.440608762859233e-07, /* 1205 */ + 9.332543007969910e-07, /* 1206 */ + 9.225714271547632e-07, /* 1207 */ + 9.120108393559097e-07, /* 1208 */ + 9.015711376059569e-07, /* 1209 */ + 8.912509381337455e-07, /* 1210 */ + 8.810488730080140e-07, /* 1211 */ + 8.709635899560807e-07, /* 1212 */ + 8.609937521846007e-07, /* 1213 */ + 8.511380382023765e-07, /* 1214 */ + 8.413951416451951e-07, /* 1215 */ + 8.317637711026710e-07, /* 1216 */ + 8.222426499470711e-07, /* 1217 */ + 8.128305161640992e-07, /* 1218 */ + 8.035261221856172e-07, /* 1219 */ + 7.943282347242815e-07, /* 1220 */ + 7.852356346100718e-07, /* 1221 */ + 7.762471166286918e-07, /* 1222 */ + 7.673614893618189e-07, /* 1223 */ + 7.585775750291838e-07, /* 1224 */ + 7.498942093324558e-07, /* 1225 */ + 7.413102413009175e-07, /* 1226 */ + 7.328245331389040e-07, /* 1227 */ + 7.244359600749901e-07, /* 1228 */ + 7.161434102129020e-07, /* 1229 */ + 7.079457843841379e-07, /* 1230 */ + 6.998419960022735e-07, /* 1231 */ + 6.918309709189365e-07, /* 1232 */ + 6.839116472814294e-07, /* 1233 */ + 6.760829753919818e-07, /* 1234 */ + 6.683439175686146e-07, /* 1235 */ + 6.606934480075960e-07, /* 1236 */ + 6.531305526474723e-07, /* 1237 */ + 6.456542290346555e-07, /* 1238 */ + 6.382634861905487e-07, /* 1239 */ + 6.309573444801933e-07, /* 1240 */ + 6.237348354824193e-07, /* 1241 */ + 6.165950018614822e-07, /* 1242 */ + 6.095368972401692e-07, /* 1243 */ + 6.025595860743577e-07, /* 1244 */ + 5.956621435290105e-07, /* 1245 */ + 5.888436553555889e-07, /* 1246 */ + 5.821032177708714e-07, /* 1247 */ + 5.754399373371570e-07, /* 1248 */ + 5.688529308438414e-07, /* 1249 */ + 5.623413251903490e-07, /* 1250 */ + 5.559042572704035e-07, /* 1251 */ + 5.495408738576246e-07, /* 1252 */ + 5.432503314924332e-07, /* 1253 */ + 5.370317963702528e-07, /* 1254 */ + 5.308844442309884e-07, /* 1255 */ + 5.248074602497726e-07, /* 1256 */ + 5.188000389289611e-07, /* 1257 */ + 5.128613839913648e-07, /* 1258 */ + 5.069907082747044e-07, /* 1259 */ + 5.011872336272723e-07, /* 1260 */ + 4.954501908047903e-07, /* 1261 */ + 4.897788193684462e-07, /* 1262 */ + 4.841723675840993e-07, /* 1263 */ + 4.786300923226383e-07, /* 1264 */ + 4.731512589614805e-07, /* 1265 */ + 4.677351412871982e-07, /* 1266 */ + 4.623810213992603e-07, /* 1267 */ + 4.570881896148751e-07, /* 1268 */ + 4.518559443749224e-07, /* 1269 */ + 4.466835921509631e-07, /* 1270 */ + 4.415704473533125e-07, /* 1271 */ + 4.365158322401660e-07, /* 1272 */ + 4.315190768277652e-07, /* 1273 */ + 4.265795188015926e-07, /* 1274 */ + 4.216965034285823e-07, /* 1275 */ + 4.168693834703354e-07, /* 1276 */ + 4.120975190973302e-07, /* 1277 */ + 4.073802778041127e-07, /* 1278 */ + 4.027170343254591e-07, /* 1279 */ + 3.981071705534972e-07, /* 1280 */ + 3.935500754557775e-07, /* 1281 */ + 3.890451449942806e-07, /* 1282 */ + 3.845917820453536e-07, /* 1283 */ + 3.801893963205612e-07, /* 1284 */ + 3.758374042884442e-07, /* 1285 */ + 3.715352290971725e-07, /* 1286 */ + 3.672823004980847e-07, /* 1287 */ + 3.630780547701014e-07, /* 1288 */ + 3.589219346450052e-07, /* 1289 */ + 3.548133892335755e-07, /* 1290 */ + 3.507518739525680e-07, /* 1291 */ + 3.467368504525316e-07, /* 1292 */ + 3.427677865464503e-07, /* 1293 */ + 3.388441561392025e-07, /* 1294 */ + 3.349654391578277e-07, /* 1295 */ + 3.311311214825911e-07, /* 1296 */ + 3.273406948788382e-07, /* 1297 */ + 3.235936569296283e-07, /* 1298 */ + 3.198895109691398e-07, /* 1299 */ + 3.162277660168379e-07, /* 1300 */ + 3.126079367123955e-07, /* 1301 */ + 3.090295432513590e-07, /* 1302 */ + 3.054921113215513e-07, /* 1303 */ + 3.019951720402016e-07, /* 1304 */ + 2.985382618917960e-07, /* 1305 */ + 2.951209226666386e-07, /* 1306 */ + 2.917427014001167e-07, /* 1307 */ + 2.884031503126606e-07, /* 1308 */ + 2.851018267503909e-07, /* 1309 */ + 2.818382931264454e-07, /* 1310 */ + 2.786121168629770e-07, /* 1311 */ + 2.754228703338166e-07, /* 1312 */ + 2.722701308077912e-07, /* 1313 */ + 2.691534803926916e-07, /* 1314 */ + 2.660725059798809e-07, /* 1315 */ + 2.630267991895382e-07, /* 1316 */ + 2.600159563165272e-07, /* 1317 */ + 2.570395782768864e-07, /* 1318 */ + 2.540972705549305e-07, /* 1319 */ + 2.511886431509580e-07, /* 1320 */ + 2.483133105295570e-07, /* 1321 */ + 2.454708915685030e-07, /* 1322 */ + 2.426610095082416e-07, /* 1323 */ + 2.398832919019490e-07, /* 1324 */ + 2.371373705661655e-07, /* 1325 */ + 2.344228815319922e-07, /* 1326 */ + 2.317394649968478e-07, /* 1327 */ + 2.290867652767773e-07, /* 1328 */ + 2.264644307593060e-07, /* 1329 */ + 2.238721138568340e-07, /* 1330 */ + 2.213094709605638e-07, /* 1331 */ + 2.187761623949553e-07, /* 1332 */ + 2.162718523727020e-07, /* 1333 */ + 2.137962089502232e-07, /* 1334 */ + 2.113489039836647e-07, /* 1335 */ + 2.089296130854040e-07, /* 1336 */ + 2.065380155810529e-07, /* 1337 */ + 2.041737944669529e-07, /* 1338 */ + 2.018366363681561e-07, /* 1339 */ + 1.995262314968880e-07, /* 1340 */ + 1.972422736114854e-07, /* 1341 */ + 1.949844599758045e-07, /* 1342 */ + 1.927524913190936e-07, /* 1343 */ + 1.905460717963247e-07, /* 1344 */ + 1.883649089489800e-07, /* 1345 */ + 1.862087136662867e-07, /* 1346 */ + 1.840772001468956e-07, /* 1347 */ + 1.819700858609983e-07, /* 1348 */ + 1.798870915128788e-07, /* 1349 */ + 1.778279410038923e-07, /* 1350 */ + 1.757923613958693e-07, /* 1351 */ + 1.737800828749375e-07, /* 1352 */ + 1.717908387157588e-07, /* 1353 */ + 1.698243652461744e-07, /* 1354 */ + 1.678804018122560e-07, /* 1355 */ + 1.659586907437561e-07, /* 1356 */ + 1.640589773199539e-07, /* 1357 */ + 1.621810097358930e-07, /* 1358 */ + 1.603245390690041e-07, /* 1359 */ + 1.584893192461114e-07, /* 1360 */ + 1.566751070108149e-07, /* 1361 */ + 1.548816618912481e-07, /* 1362 */ + 1.531087461682030e-07, /* 1363 */ + 1.513561248436208e-07, /* 1364 */ + 1.496235656094433e-07, /* 1365 */ + 1.479108388168207e-07, /* 1366 */ + 1.462177174456718e-07, /* 1367 */ + 1.445439770745928e-07, /* 1368 */ + 1.428893958511103e-07, /* 1369 */ + 1.412537544622754e-07, /* 1370 */ + 1.396368361055938e-07, /* 1371 */ + 1.380384264602885e-07, /* 1372 */ + 1.364583136588925e-07, /* 1373 */ + 1.348962882591654e-07, /* 1374 */ + 1.333521432163324e-07, /* 1375 */ + 1.318256738556407e-07, /* 1376 */ + 1.303166778452299e-07, /* 1377 */ + 1.288249551693134e-07, /* 1378 */ + 1.273503081016662e-07, /* 1379 */ + 1.258925411794167e-07, /* 1380 */ + 1.244514611771385e-07, /* 1381 */ + 1.230268770812381e-07, /* 1382 */ + 1.216186000646368e-07, /* 1383 */ + 1.202264434617413e-07, /* 1384 */ + 1.188502227437018e-07, /* 1385 */ + 1.174897554939530e-07, /* 1386 */ + 1.161448613840343e-07, /* 1387 */ + 1.148153621496883e-07, /* 1388 */ + 1.135010815672315e-07, /* 1389 */ + 1.122018454301963e-07, /* 1390 */ + 1.109174815262401e-07, /* 1391 */ + 1.096478196143185e-07, /* 1392 */ + 1.083926914021204e-07, /* 1393 */ + 1.071519305237606e-07, /* 1394 */ + 1.059253725177289e-07, /* 1395 */ + 1.047128548050900e-07, /* 1396 */ + 1.035142166679344e-07, /* 1397 */ + 1.023292992280754e-07, /* 1398 */ + 1.011579454259898e-07, /* 1399 */ + 1.000000000000000e-07, /* 1400 */ + 9.885530946569389e-08, /* 1401 */ + 9.772372209558107e-08, /* 1402 */ + 9.660508789898134e-08, /* 1403 */ + 9.549925860214360e-08, /* 1404 */ + 9.440608762859234e-08, /* 1405 */ + 9.332543007969910e-08, /* 1406 */ + 9.225714271547632e-08, /* 1407 */ + 9.120108393559098e-08, /* 1408 */ + 9.015711376059569e-08, /* 1409 */ + 8.912509381337455e-08, /* 1410 */ + 8.810488730080140e-08, /* 1411 */ + 8.709635899560806e-08, /* 1412 */ + 8.609937521846006e-08, /* 1413 */ + 8.511380382023765e-08, /* 1414 */ + 8.413951416451951e-08, /* 1415 */ + 8.317637711026710e-08, /* 1416 */ + 8.222426499470712e-08, /* 1417 */ + 8.128305161640992e-08, /* 1418 */ + 8.035261221856172e-08, /* 1419 */ + 7.943282347242815e-08, /* 1420 */ + 7.852356346100718e-08, /* 1421 */ + 7.762471166286917e-08, /* 1422 */ + 7.673614893618189e-08, /* 1423 */ + 7.585775750291838e-08, /* 1424 */ + 7.498942093324559e-08, /* 1425 */ + 7.413102413009175e-08, /* 1426 */ + 7.328245331389041e-08, /* 1427 */ + 7.244359600749901e-08, /* 1428 */ + 7.161434102129020e-08, /* 1429 */ + 7.079457843841380e-08, /* 1430 */ + 6.998419960022735e-08, /* 1431 */ + 6.918309709189365e-08, /* 1432 */ + 6.839116472814293e-08, /* 1433 */ + 6.760829753919818e-08, /* 1434 */ + 6.683439175686146e-08, /* 1435 */ + 6.606934480075960e-08, /* 1436 */ + 6.531305526474724e-08, /* 1437 */ + 6.456542290346556e-08, /* 1438 */ + 6.382634861905487e-08, /* 1439 */ + 6.309573444801932e-08, /* 1440 */ +}; + +static const fluid_real_t fluid_concave_tab[128] = { + 0.000000000000000e+00, /* 0 */ + 1.430489932664151e-03, /* 1 */ + 2.872378311625187e-03, /* 2 */ + 4.325848247384080e-03, /* 3 */ + 5.791087298566222e-03, /* 4 */ + 7.268287617170264e-03, /* 5 */ + 8.757646099794491e-03, /* 6 */ + 1.025936454513835e-02, /* 7 */ + 1.177364981809421e-02, /* 8 */ + 1.330071402076312e-02, /* 9 */ + 1.484077467074801e-02, /* 10 */ + 1.639405488709933e-02, /* 11 */ + 1.796078358431049e-02, /* 12 */ + 1.954119567478511e-02, /* 13 */ + 2.113553228022381e-02, /* 14 */ + 2.274404095240635e-02, /* 15 */ + 2.436697590387476e-02, /* 16 */ + 2.600459824905492e-02, /* 17 */ + 2.765717625638884e-02, /* 18 */ + 2.932498561208631e-02, /* 19 */ + 3.100830969614467e-02, /* 20 */ + 3.270743987132776e-02, /* 21 */ + 3.442267578584116e-02, /* 22 */ + 3.615432569049021e-02, /* 23 */ + 3.790270677116027e-02, /* 24 */ + 3.966814549751637e-02, /* 25 */ + 4.145097798888095e-02, /* 26 */ + 4.325155039831535e-02, /* 27 */ + 4.507021931600289e-02, /* 28 */ + 4.690735219310917e-02, /* 29 */ + 4.876332778738000e-02, /* 30 */ + 5.063853663182852e-02, /* 31 */ + 5.253338152796212e-02, /* 32 */ + 5.444827806510758e-02, /* 33 */ + 5.638365516750905e-02, /* 34 */ + 5.833995567100066e-02, /* 35 */ + 6.031763693119302e-02, /* 36 */ + 6.231717146526333e-02, /* 37 */ + 6.433904762960170e-02, /* 38 */ + 6.638377033574509e-02, /* 39 */ + 6.845186180722430e-02, /* 40 */ + 7.054386238016214e-02, /* 41 */ + 7.266033135069339e-02, /* 42 */ + 7.480184787253133e-02, /* 43 */ + 7.696901190828456e-02, /* 44 */ + 7.916244523843340e-02, /* 45 */ + 8.138279253221128e-02, /* 46 */ + 8.363072248500553e-02, /* 47 */ + 8.590692902729809e-02, /* 48 */ + 8.821213261061518e-02, /* 49 */ + 9.054708157644790e-02, /* 50 */ + 9.291255361465228e-02, /* 51 */ + 9.530935731844033e-02, /* 52 */ + 9.773833384374193e-02, /* 53 */ + 1.002003586814587e-01, /* 54 */ + 1.026963435519535e-01, /* 55 */ + 1.052272384320340e-01, /* 56 */ + 1.077940337257083e-01, /* 57 */ + 1.103977625911256e-01, /* 58 */ + 1.130395034373835e-01, /* 59 */ + 1.157203826063043e-01, /* 60 */ + 1.184415772558701e-01, /* 61 */ + 1.212043184637922e-01, /* 62 */ + 1.240098945716957e-01, /* 63 */ + 1.268596547926563e-01, /* 64 */ + 1.297550131073762e-01, /* 65 */ + 1.326974524771624e-01, /* 66 */ + 1.356885294051305e-01, /* 67 */ + 1.387298788807553e-01, /* 68 */ + 1.418232197470915e-01, /* 69 */ + 1.449703605347773e-01, /* 70 */ + 1.481732058123985e-01, /* 71 */ + 1.514337631090471e-01, /* 72 */ + 1.547541504720785e-01, /* 73 */ + 1.581366047313199e-01, /* 74 */ + 1.615834905504824e-01, /* 75 */ + 1.650973103575085e-01, /* 76 */ + 1.686807152583075e-01, /* 77 */ + 1.723365170531013e-01, /* 78 */ + 1.760677014918207e-01, /* 79 */ + 1.798774429250997e-01, /* 80 */ + 1.837691205309928e-01, /* 81 */ + 1.877463363252555e-01, /* 82 */ + 1.918129351957372e-01, /* 83 */ + 1.959730272401543e-01, /* 84 */ + 2.002310127325235e-01, /* 85 */ + 2.045916100984256e-01, /* 86 */ + 2.090598873449977e-01, /* 87 */ + 2.136412974706073e-01, /* 88 */ + 2.183417184746445e-01, /* 89 */ + 2.231674987037341e-01, /* 90 */ + 2.281255084119456e-01, /* 91 */ + 2.332231985857005e-01, /* 92 */ + 2.384686682973757e-01, /* 93 */ + 2.438707421158622e-01, /* 94 */ + 2.494390594316878e-01, /* 95 */ + 2.551841779673684e-01, /* 96 */ + 2.611176942651227e-01, /* 97 */ + 2.672523846070836e-01, /* 98 */ + 2.736023706723907e-01, /* 99 */ + 2.801833153320706e-01, /* 100 */ + 2.870126554104745e-01, /* 101 */ + 2.941098801182996e-01, /* 102 */ + 3.014968663518128e-01, /* 103 */ + 3.091982853909850e-01, /* 104 */ + 3.172421000557294e-01, /* 105 */ + 3.256601775925156e-01, /* 106 */ + 3.344890522049898e-01, /* 107 */ + 3.437708833346366e-01, /* 108 */ + 3.535546732719378e-01, /* 109 */ + 3.638978331573678e-01, /* 110 */ + 3.748682242916800e-01, /* 111 */ + 3.865468591251148e-01, /* 112 */ + 3.990315355323828e-01, /* 113 */ + 4.124418202704667e-01, /* 114 */ + 4.269260312118050e-01, /* 115 */ + 4.426712649157216e-01, /* 116 */ + 4.599182170649820e-01, /* 117 */ + 4.789838381319300e-01, /* 118 */ + 5.002973891516721e-01, /* 119 */ + 5.244607003923749e-01, /* 120 */ + 5.523551960717972e-01, /* 121 */ + 5.853473819249742e-01, /* 122 */ + 6.257265540116643e-01, /* 123 */ + 6.777843609317893e-01, /* 124 */ + 7.511557188716564e-01, /* 125 */ + 8.765848837316486e-01, /* 126 */ + 1.000000000000000e+00, /* 127 */ +}; + +static const fluid_real_t fluid_convex_tab[128] = { + 0.000000000000000e+00, /* 0 */ + 1.234151162683514e-01, /* 1 */ + 2.488442811283435e-01, /* 2 */ + 3.222156390682107e-01, /* 3 */ + 3.742734459883357e-01, /* 4 */ + 4.146526180750258e-01, /* 5 */ + 4.476448039282029e-01, /* 6 */ + 4.755392996076250e-01, /* 7 */ + 4.997026108483278e-01, /* 8 */ + 5.210161618680701e-01, /* 9 */ + 5.400817829350180e-01, /* 10 */ + 5.573287350842785e-01, /* 11 */ + 5.730739687881951e-01, /* 12 */ + 5.875581797295333e-01, /* 13 */ + 6.009684644676172e-01, /* 14 */ + 6.134531408748852e-01, /* 15 */ + 6.251317757083200e-01, /* 16 */ + 6.361021668426321e-01, /* 17 */ + 6.464453267280622e-01, /* 18 */ + 6.562291166653634e-01, /* 19 */ + 6.655109477950102e-01, /* 20 */ + 6.743398224074844e-01, /* 21 */ + 6.827578999442706e-01, /* 22 */ + 6.908017146090151e-01, /* 23 */ + 6.985031336481872e-01, /* 24 */ + 7.058901198817004e-01, /* 25 */ + 7.129873445895255e-01, /* 26 */ + 7.198166846679294e-01, /* 27 */ + 7.263976293276093e-01, /* 28 */ + 7.327476153929163e-01, /* 29 */ + 7.388823057348773e-01, /* 30 */ + 7.448158220326316e-01, /* 31 */ + 7.505609405683121e-01, /* 32 */ + 7.561292578841378e-01, /* 33 */ + 7.615313317026243e-01, /* 34 */ + 7.667768014142995e-01, /* 35 */ + 7.718744915880543e-01, /* 36 */ + 7.768325012962659e-01, /* 37 */ + 7.816582815253555e-01, /* 38 */ + 7.863587025293927e-01, /* 39 */ + 7.909401126550023e-01, /* 40 */ + 7.954083899015745e-01, /* 41 */ + 7.997689872674765e-01, /* 42 */ + 8.040269727598457e-01, /* 43 */ + 8.081870648042627e-01, /* 44 */ + 8.122536636747445e-01, /* 45 */ + 8.162308794690072e-01, /* 46 */ + 8.201225570749002e-01, /* 47 */ + 8.239322985081793e-01, /* 48 */ + 8.276634829468987e-01, /* 49 */ + 8.313192847416925e-01, /* 50 */ + 8.349026896424915e-01, /* 51 */ + 8.384165094495176e-01, /* 52 */ + 8.418633952686800e-01, /* 53 */ + 8.452458495279215e-01, /* 54 */ + 8.485662368909529e-01, /* 55 */ + 8.518267941876015e-01, /* 56 */ + 8.550296394652227e-01, /* 57 */ + 8.581767802529086e-01, /* 58 */ + 8.612701211192447e-01, /* 59 */ + 8.643114705948695e-01, /* 60 */ + 8.673025475228375e-01, /* 61 */ + 8.702449868926238e-01, /* 62 */ + 8.731403452073437e-01, /* 63 */ + 8.759901054283044e-01, /* 64 */ + 8.787956815362078e-01, /* 65 */ + 8.815584227441299e-01, /* 66 */ + 8.842796173936956e-01, /* 67 */ + 8.869604965626164e-01, /* 68 */ + 8.896022374088743e-01, /* 69 */ + 8.922059662742917e-01, /* 70 */ + 8.947727615679660e-01, /* 71 */ + 8.973036564480465e-01, /* 72 */ + 8.997996413185413e-01, /* 73 */ + 9.022616661562580e-01, /* 74 */ + 9.046906426815596e-01, /* 75 */ + 9.070874463853477e-01, /* 76 */ + 9.094529184235521e-01, /* 77 */ + 9.117878673893848e-01, /* 78 */ + 9.140930709727019e-01, /* 79 */ + 9.163692775149945e-01, /* 80 */ + 9.186172074677887e-01, /* 81 */ + 9.208375547615666e-01, /* 82 */ + 9.230309880917155e-01, /* 83 */ + 9.251981521274687e-01, /* 84 */ + 9.273396686493066e-01, /* 85 */ + 9.294561376198379e-01, /* 86 */ + 9.315481381927757e-01, /* 87 */ + 9.336162296642549e-01, /* 88 */ + 9.356609523703983e-01, /* 89 */ + 9.376828285347367e-01, /* 90 */ + 9.396823630688069e-01, /* 91 */ + 9.416600443289993e-01, /* 92 */ + 9.436163448324909e-01, /* 93 */ + 9.455517219348925e-01, /* 94 */ + 9.474666184720378e-01, /* 95 */ + 9.493614633681715e-01, /* 96 */ + 9.512366722126200e-01, /* 97 */ + 9.530926478068908e-01, /* 98 */ + 9.549297806839971e-01, /* 99 */ + 9.567484496016846e-01, /* 100 */ + 9.585490220111190e-01, /* 101 */ + 9.603318545024836e-01, /* 102 */ + 9.620972932288397e-01, /* 103 */ + 9.638456743095097e-01, /* 104 */ + 9.655773242141589e-01, /* 105 */ + 9.672925601286723e-01, /* 106 */ + 9.689916903038553e-01, /* 107 */ + 9.706750143879137e-01, /* 108 */ + 9.723428237436111e-01, /* 109 */ + 9.739954017509451e-01, /* 110 */ + 9.756330240961253e-01, /* 111 */ + 9.772559590475937e-01, /* 112 */ + 9.788644677197762e-01, /* 113 */ + 9.804588043252149e-01, /* 114 */ + 9.820392164156895e-01, /* 115 */ + 9.836059451129007e-01, /* 116 */ + 9.851592253292520e-01, /* 117 */ + 9.866992859792368e-01, /* 118 */ + 9.882263501819057e-01, /* 119 */ + 9.897406354548617e-01, /* 120 */ + 9.912423539002055e-01, /* 121 */ + 9.927317123828298e-01, /* 122 */ + 9.942089127014337e-01, /* 123 */ + 9.956741517526159e-01, /* 124 */ + 9.971276216883748e-01, /* 125 */ + 9.985695100673359e-01, /* 126 */ + 1.000000000000000e+00, /* 127 */ +}; + +static const fluid_real_t fluid_pan_tab[1002] = { + 0.000000000000000e+00, /* 0 */ + 1.569226455665206e-03, /* 1 */ + 3.138449047152344e-03, /* 2 */ + 4.707663910292860e-03, /* 3 */ + 6.276867180937232e-03, /* 4 */ + 7.846054994964486e-03, /* 5 */ + 9.415223488291704e-03, /* 6 */ + 1.098436879688355e-02, /* 7 */ + 1.255348705676178e-02, /* 8 */ + 1.412257440401476e-02, /* 9 */ + 1.569162697480695e-02, /* 10 */ + 1.726064090538850e-02, /* 11 */ + 1.882961233210465e-02, /* 12 */ + 2.039853739140535e-02, /* 13 */ + 2.196741221985471e-02, /* 14 */ + 2.353623295414053e-02, /* 15 */ + 2.510499573108383e-02, /* 16 */ + 2.667369668764832e-02, /* 17 */ + 2.824233196094998e-02, /* 18 */ + 2.981089768826650e-02, /* 19 */ + 3.137939000704683e-02, /* 20 */ + 3.294780505492070e-02, /* 21 */ + 3.451613896970813e-02, /* 22 */ + 3.608438788942888e-02, /* 23 */ + 3.765254795231206e-02, /* 24 */ + 3.922061529680555e-02, /* 25 */ + 4.078858606158557e-02, /* 26 */ + 4.235645638556616e-02, /* 27 */ + 4.392422240790868e-02, /* 28 */ + 4.549188026803134e-02, /* 29 */ + 4.705942610561871e-02, /* 30 */ + 4.862685606063118e-02, /* 31 */ + 5.019416627331453e-02, /* 32 */ + 5.176135288420938e-02, /* 33 */ + 5.332841203416073e-02, /* 34 */ + 5.489533986432744e-02, /* 35 */ + 5.646213251619175e-02, /* 36 */ + 5.802878613156876e-02, /* 37 */ + 5.959529685261596e-02, /* 38 */ + 6.116166082184270e-02, /* 39 */ + 6.272787418211971e-02, /* 40 */ + 6.429393307668860e-02, /* 41 */ + 6.585983364917131e-02, /* 42 */ + 6.742557204357968e-02, /* 43 */ + 6.899114440432493e-02, /* 44 */ + 7.055654687622705e-02, /* 45 */ + 7.212177560452446e-02, /* 46 */ + 7.368682673488337e-02, /* 47 */ + 7.525169641340737e-02, /* 48 */ + 7.681638078664681e-02, /* 49 */ + 7.838087600160838e-02, /* 50 */ + 7.994517820576458e-02, /* 51 */ + 8.150928354706316e-02, /* 52 */ + 8.307318817393668e-02, /* 53 */ + 8.463688823531192e-02, /* 54 */ + 8.620037988061939e-02, /* 55 */ + 8.776365925980288e-02, /* 56 */ + 8.932672252332881e-02, /* 57 */ + 9.088956582219582e-02, /* 58 */ + 9.245218530794418e-02, /* 59 */ + 9.401457713266531e-02, /* 60 */ + 9.557673744901124e-02, /* 61 */ + 9.713866241020409e-02, /* 62 */ + 9.870034817004553e-02, /* 63 */ + 1.002617908829262e-01, /* 64 */ + 1.018229867038354e-01, /* 65 */ + 1.033839317883702e-01, /* 66 */ + 1.049446222927451e-01, /* 67 */ + 1.065050543738018e-01, /* 68 */ + 1.080652241890180e-01, /* 69 */ + 1.096251278965173e-01, /* 70 */ + 1.111847616550789e-01, /* 71 */ + 1.127441216241462e-01, /* 72 */ + 1.143032039638373e-01, /* 73 */ + 1.158620048349536e-01, /* 74 */ + 1.174205203989899e-01, /* 75 */ + 1.189787468181433e-01, /* 76 */ + 1.205366802553230e-01, /* 77 */ + 1.220943168741599e-01, /* 78 */ + 1.236516528390153e-01, /* 79 */ + 1.252086843149914e-01, /* 80 */ + 1.267654074679397e-01, /* 81 */ + 1.283218184644714e-01, /* 82 */ + 1.298779134719661e-01, /* 83 */ + 1.314336886585815e-01, /* 84 */ + 1.329891401932629e-01, /* 85 */ + 1.345442642457527e-01, /* 86 */ + 1.360990569865997e-01, /* 87 */ + 1.376535145871682e-01, /* 88 */ + 1.392076332196483e-01, /* 89 */ + 1.407614090570644e-01, /* 90 */ + 1.423148382732851e-01, /* 91 */ + 1.438679170430328e-01, /* 92 */ + 1.454206415418926e-01, /* 93 */ + 1.469730079463220e-01, /* 94 */ + 1.485250124336605e-01, /* 95 */ + 1.500766511821384e-01, /* 96 */ + 1.516279203708872e-01, /* 97 */ + 1.531788161799479e-01, /* 98 */ + 1.547293347902812e-01, /* 99 */ + 1.562794723837767e-01, /* 100 */ + 1.578292251432621e-01, /* 101 */ + 1.593785892525127e-01, /* 102 */ + 1.609275608962610e-01, /* 103 */ + 1.624761362602058e-01, /* 104 */ + 1.640243115310219e-01, /* 105 */ + 1.655720828963691e-01, /* 106 */ + 1.671194465449020e-01, /* 107 */ + 1.686663986662791e-01, /* 108 */ + 1.702129354511722e-01, /* 109 */ + 1.717590530912760e-01, /* 110 */ + 1.733047477793173e-01, /* 111 */ + 1.748500157090643e-01, /* 112 */ + 1.763948530753363e-01, /* 113 */ + 1.779392560740125e-01, /* 114 */ + 1.794832209020421e-01, /* 115 */ + 1.810267437574530e-01, /* 116 */ + 1.825698208393617e-01, /* 117 */ + 1.841124483479821e-01, /* 118 */ + 1.856546224846354e-01, /* 119 */ + 1.871963394517592e-01, /* 120 */ + 1.887375954529167e-01, /* 121 */ + 1.902783866928064e-01, /* 122 */ + 1.918187093772711e-01, /* 123 */ + 1.933585597133076e-01, /* 124 */ + 1.948979339090757e-01, /* 125 */ + 1.964368281739078e-01, /* 126 */ + 1.979752387183178e-01, /* 127 */ + 1.995131617540112e-01, /* 128 */ + 2.010505934938938e-01, /* 129 */ + 2.025875301520809e-01, /* 130 */ + 2.041239679439075e-01, /* 131 */ + 2.056599030859366e-01, /* 132 */ + 2.071953317959691e-01, /* 133 */ + 2.087302502930529e-01, /* 134 */ + 2.102646547974925e-01, /* 135 */ + 2.117985415308578e-01, /* 136 */ + 2.133319067159940e-01, /* 137 */ + 2.148647465770304e-01, /* 138 */ + 2.163970573393899e-01, /* 139 */ + 2.179288352297983e-01, /* 140 */ + 2.194600764762938e-01, /* 141 */ + 2.209907773082357e-01, /* 142 */ + 2.225209339563144e-01, /* 143 */ + 2.240505426525601e-01, /* 144 */ + 2.255795996303523e-01, /* 145 */ + 2.271081011244294e-01, /* 146 */ + 2.286360433708974e-01, /* 147 */ + 2.301634226072393e-01, /* 148 */ + 2.316902350723250e-01, /* 149 */ + 2.332164770064195e-01, /* 150 */ + 2.347421446511931e-01, /* 151 */ + 2.362672342497300e-01, /* 152 */ + 2.377917420465381e-01, /* 153 */ + 2.393156642875578e-01, /* 154 */ + 2.408389972201713e-01, /* 155 */ + 2.423617370932123e-01, /* 156 */ + 2.438838801569746e-01, /* 157 */ + 2.454054226632218e-01, /* 158 */ + 2.469263608651961e-01, /* 159 */ + 2.484466910176281e-01, /* 160 */ + 2.499664093767456e-01, /* 161 */ + 2.514855122002828e-01, /* 162 */ + 2.530039957474898e-01, /* 163 */ + 2.545218562791415e-01, /* 164 */ + 2.560390900575471e-01, /* 165 */ + 2.575556933465591e-01, /* 166 */ + 2.590716624115826e-01, /* 167 */ + 2.605869935195844e-01, /* 168 */ + 2.621016829391022e-01, /* 169 */ + 2.636157269402540e-01, /* 170 */ + 2.651291217947471e-01, /* 171 */ + 2.666418637758871e-01, /* 172 */ + 2.681539491585876e-01, /* 173 */ + 2.696653742193788e-01, /* 174 */ + 2.711761352364170e-01, /* 175 */ + 2.726862284894938e-01, /* 176 */ + 2.741956502600449e-01, /* 177 */ + 2.757043968311598e-01, /* 178 */ + 2.772124644875906e-01, /* 179 */ + 2.787198495157609e-01, /* 180 */ + 2.802265482037756e-01, /* 181 */ + 2.817325568414297e-01, /* 182 */ + 2.832378717202171e-01, /* 183 */ + 2.847424891333405e-01, /* 184 */ + 2.862464053757197e-01, /* 185 */ + 2.877496167440013e-01, /* 186 */ + 2.892521195365677e-01, /* 187 */ + 2.907539100535459e-01, /* 188 */ + 2.922549845968172e-01, /* 189 */ + 2.937553394700257e-01, /* 190 */ + 2.952549709785878e-01, /* 191 */ + 2.967538754297011e-01, /* 192 */ + 2.982520491323535e-01, /* 193 */ + 2.997494883973326e-01, /* 194 */ + 3.012461895372343e-01, /* 195 */ + 3.027421488664720e-01, /* 196 */ + 3.042373627012863e-01, /* 197 */ + 3.057318273597529e-01, /* 198 */ + 3.072255391617928e-01, /* 199 */ + 3.087184944291808e-01, /* 200 */ + 3.102106894855545e-01, /* 201 */ + 3.117021206564237e-01, /* 202 */ + 3.131927842691789e-01, /* 203 */ + 3.146826766531011e-01, /* 204 */ + 3.161717941393703e-01, /* 205 */ + 3.176601330610745e-01, /* 206 */ + 3.191476897532191e-01, /* 207 */ + 3.206344605527355e-01, /* 208 */ + 3.221204417984906e-01, /* 209 */ + 3.236056298312954e-01, /* 210 */ + 3.250900209939143e-01, /* 211 */ + 3.265736116310736e-01, /* 212 */ + 3.280563980894714e-01, /* 213 */ + 3.295383767177856e-01, /* 214 */ + 3.310195438666838e-01, /* 215 */ + 3.324998958888314e-01, /* 216 */ + 3.339794291389013e-01, /* 217 */ + 3.354581399735826e-01, /* 218 */ + 3.369360247515896e-01, /* 219 */ + 3.384130798336704e-01, /* 220 */ + 3.398893015826167e-01, /* 221 */ + 3.413646863632719e-01, /* 222 */ + 3.428392305425407e-01, /* 223 */ + 3.443129304893974e-01, /* 224 */ + 3.457857825748955e-01, /* 225 */ + 3.472577831721762e-01, /* 226 */ + 3.487289286564775e-01, /* 227 */ + 3.501992154051431e-01, /* 228 */ + 3.516686397976314e-01, /* 229 */ + 3.531371982155241e-01, /* 230 */ + 3.546048870425356e-01, /* 231 */ + 3.560717026645214e-01, /* 232 */ + 3.575376414694875e-01, /* 233 */ + 3.590026998475987e-01, /* 234 */ + 3.604668741911882e-01, /* 235 */ + 3.619301608947658e-01, /* 236 */ + 3.633925563550274e-01, /* 237 */ + 3.648540569708633e-01, /* 238 */ + 3.663146591433675e-01, /* 239 */ + 3.677743592758461e-01, /* 240 */ + 3.692331537738269e-01, /* 241 */ + 3.706910390450675e-01, /* 242 */ + 3.721480114995644e-01, /* 243 */ + 3.736040675495622e-01, /* 244 */ + 3.750592036095618e-01, /* 245 */ + 3.765134160963297e-01, /* 246 */ + 3.779667014289065e-01, /* 247 */ + 3.794190560286163e-01, /* 248 */ + 3.808704763190747e-01, /* 249 */ + 3.823209587261981e-01, /* 250 */ + 3.837704996782126e-01, /* 251 */ + 3.852190956056624e-01, /* 252 */ + 3.866667429414188e-01, /* 253 */ + 3.881134381206892e-01, /* 254 */ + 3.895591775810255e-01, /* 255 */ + 3.910039577623329e-01, /* 256 */ + 3.924477751068791e-01, /* 257 */ + 3.938906260593025e-01, /* 258 */ + 3.953325070666214e-01, /* 259 */ + 3.967734145782425e-01, /* 260 */ + 3.982133450459696e-01, /* 261 */ + 3.996522949240126e-01, /* 262 */ + 4.010902606689959e-01, /* 263 */ + 4.025272387399675e-01, /* 264 */ + 4.039632255984075e-01, /* 265 */ + 4.053982177082366e-01, /* 266 */ + 4.068322115358254e-01, /* 267 */ + 4.082652035500025e-01, /* 268 */ + 4.096971902220634e-01, /* 269 */ + 4.111281680257793e-01, /* 270 */ + 4.125581334374058e-01, /* 271 */ + 4.139870829356915e-01, /* 272 */ + 4.154150130018864e-01, /* 273 */ + 4.168419201197511e-01, /* 274 */ + 4.182678007755651e-01, /* 275 */ + 4.196926514581356e-01, /* 276 */ + 4.211164686588058e-01, /* 277 */ + 4.225392488714641e-01, /* 278 */ + 4.239609885925524e-01, /* 279 */ + 4.253816843210749e-01, /* 280 */ + 4.268013325586062e-01, /* 281 */ + 4.282199298093007e-01, /* 282 */ + 4.296374725799008e-01, /* 283 */ + 4.310539573797453e-01, /* 284 */ + 4.324693807207784e-01, /* 285 */ + 4.338837391175581e-01, /* 286 */ + 4.352970290872648e-01, /* 287 */ + 4.367092471497098e-01, /* 288 */ + 4.381203898273440e-01, /* 289 */ + 4.395304536452664e-01, /* 290 */ + 4.409394351312327e-01, /* 291 */ + 4.423473308156637e-01, /* 292 */ + 4.437541372316541e-01, /* 293 */ + 4.451598509149808e-01, /* 294 */ + 4.465644684041115e-01, /* 295 */ + 4.479679862402133e-01, /* 296 */ + 4.493704009671613e-01, /* 297 */ + 4.507717091315467e-01, /* 298 */ + 4.521719072826857e-01, /* 299 */ + 4.535709919726280e-01, /* 300 */ + 4.549689597561650e-01, /* 301 */ + 4.563658071908386e-01, /* 302 */ + 4.577615308369494e-01, /* 303 */ + 4.591561272575653e-01, /* 304 */ + 4.605495930185300e-01, /* 305 */ + 4.619419246884716e-01, /* 306 */ + 4.633331188388105e-01, /* 307 */ + 4.647231720437685e-01, /* 308 */ + 4.661120808803769e-01, /* 309 */ + 4.674998419284848e-01, /* 310 */ + 4.688864517707680e-01, /* 311 */ + 4.702719069927368e-01, /* 312 */ + 4.716562041827449e-01, /* 313 */ + 4.730393399319976e-01, /* 314 */ + 4.744213108345603e-01, /* 315 */ + 4.758021134873666e-01, /* 316 */ + 4.771817444902270e-01, /* 317 */ + 4.785602004458372e-01, /* 318 */ + 4.799374779597863e-01, /* 319 */ + 4.813135736405654e-01, /* 320 */ + 4.826884840995759e-01, /* 321 */ + 4.840622059511374e-01, /* 322 */ + 4.854347358124969e-01, /* 323 */ + 4.868060703038363e-01, /* 324 */ + 4.881762060482813e-01, /* 325 */ + 4.895451396719092e-01, /* 326 */ + 4.909128678037579e-01, /* 327 */ + 4.922793870758333e-01, /* 328 */ + 4.936446941231185e-01, /* 329 */ + 4.950087855835814e-01, /* 330 */ + 4.963716580981834e-01, /* 331 */ + 4.977333083108875e-01, /* 332 */ + 4.990937328686666e-01, /* 333 */ + 5.004529284215117e-01, /* 334 */ + 5.018108916224401e-01, /* 335 */ + 5.031676191275039e-01, /* 336 */ + 5.045231075957979e-01, /* 337 */ + 5.058773536894682e-01, /* 338 */ + 5.072303540737202e-01, /* 339 */ + 5.085821054168265e-01, /* 340 */ + 5.099326043901359e-01, /* 341 */ + 5.112818476680807e-01, /* 342 */ + 5.126298319281856e-01, /* 343 */ + 5.139765538510755e-01, /* 344 */ + 5.153220101204837e-01, /* 345 */ + 5.166661974232605e-01, /* 346 */ + 5.180091124493803e-01, /* 347 */ + 5.193507518919511e-01, /* 348 */ + 5.206911124472217e-01, /* 349 */ + 5.220301908145902e-01, /* 350 */ + 5.233679836966120e-01, /* 351 */ + 5.247044877990080e-01, /* 352 */ + 5.260396998306727e-01, /* 353 */ + 5.273736165036822e-01, /* 354 */ + 5.287062345333027e-01, /* 355 */ + 5.300375506379977e-01, /* 356 */ + 5.313675615394372e-01, /* 357 */ + 5.326962639625050e-01, /* 358 */ + 5.340236546353070e-01, /* 359 */ + 5.353497302891792e-01, /* 360 */ + 5.366744876586959e-01, /* 361 */ + 5.379979234816776e-01, /* 362 */ + 5.393200344991992e-01, /* 363 */ + 5.406408174555976e-01, /* 364 */ + 5.419602690984802e-01, /* 365 */ + 5.432783861787328e-01, /* 366 */ + 5.445951654505273e-01, /* 367 */ + 5.459106036713303e-01, /* 368 */ + 5.472246976019102e-01, /* 369 */ + 5.485374440063460e-01, /* 370 */ + 5.498488396520349e-01, /* 371 */ + 5.511588813097004e-01, /* 372 */ + 5.524675657533998e-01, /* 373 */ + 5.537748897605330e-01, /* 374 */ + 5.550808501118496e-01, /* 375 */ + 5.563854435914573e-01, /* 376 */ + 5.576886669868294e-01, /* 377 */ + 5.589905170888135e-01, /* 378 */ + 5.602909906916385e-01, /* 379 */ + 5.615900845929231e-01, /* 380 */ + 5.628877955936834e-01, /* 381 */ + 5.641841204983408e-01, /* 382 */ + 5.654790561147299e-01, /* 383 */ + 5.667725992541067e-01, /* 384 */ + 5.680647467311558e-01, /* 385 */ + 5.693554953639987e-01, /* 386 */ + 5.706448419742013e-01, /* 387 */ + 5.719327833867824e-01, /* 388 */ + 5.732193164302208e-01, /* 389 */ + 5.745044379364633e-01, /* 390 */ + 5.757881447409327e-01, /* 391 */ + 5.770704336825352e-01, /* 392 */ + 5.783513016036690e-01, /* 393 */ + 5.796307453502310e-01, /* 394 */ + 5.809087617716252e-01, /* 395 */ + 5.821853477207707e-01, /* 396 */ + 5.834605000541085e-01, /* 397 */ + 5.847342156316105e-01, /* 398 */ + 5.860064913167862e-01, /* 399 */ + 5.872773239766905e-01, /* 400 */ + 5.885467104819324e-01, /* 401 */ + 5.898146477066816e-01, /* 402 */ + 5.910811325286766e-01, /* 403 */ + 5.923461618292324e-01, /* 404 */ + 5.936097324932486e-01, /* 405 */ + 5.948718414092160e-01, /* 406 */ + 5.961324854692254e-01, /* 407 */ + 5.973916615689745e-01, /* 408 */ + 5.986493666077760e-01, /* 409 */ + 5.999055974885650e-01, /* 410 */ + 6.011603511179066e-01, /* 411 */ + 6.024136244060035e-01, /* 412 */ + 6.036654142667041e-01, /* 413 */ + 6.049157176175093e-01, /* 414 */ + 6.061645313795805e-01, /* 415 */ + 6.074118524777475e-01, /* 416 */ + 6.086576778405154e-01, /* 417 */ + 6.099020044000728e-01, /* 418 */ + 6.111448290922987e-01, /* 419 */ + 6.123861488567709e-01, /* 420 */ + 6.136259606367725e-01, /* 421 */ + 6.148642613793004e-01, /* 422 */ + 6.161010480350722e-01, /* 423 */ + 6.173363175585338e-01, /* 424 */ + 6.185700669078673e-01, /* 425 */ + 6.198022930449979e-01, /* 426 */ + 6.210329929356019e-01, /* 427 */ + 6.222621635491136e-01, /* 428 */ + 6.234898018587335e-01, /* 429 */ + 6.247159048414351e-01, /* 430 */ + 6.259404694779729e-01, /* 431 */ + 6.271634927528890e-01, /* 432 */ + 6.283849716545215e-01, /* 433 */ + 6.296049031750114e-01, /* 434 */ + 6.308232843103100e-01, /* 435 */ + 6.320401120601865e-01, /* 436 */ + 6.332553834282351e-01, /* 437 */ + 6.344690954218827e-01, /* 438 */ + 6.356812450523961e-01, /* 439 */ + 6.368918293348892e-01, /* 440 */ + 6.381008452883308e-01, /* 441 */ + 6.393082899355514e-01, /* 442 */ + 6.405141603032511e-01, /* 443 */ + 6.417184534220064e-01, /* 444 */ + 6.429211663262777e-01, /* 445 */ + 6.441222960544168e-01, /* 446 */ + 6.453218396486741e-01, /* 447 */ + 6.465197941552053e-01, /* 448 */ + 6.477161566240798e-01, /* 449 */ + 6.489109241092871e-01, /* 450 */ + 6.501040936687442e-01, /* 451 */ + 6.512956623643031e-01, /* 452 */ + 6.524856272617580e-01, /* 453 */ + 6.536739854308520e-01, /* 454 */ + 6.548607339452850e-01, /* 455 */ + 6.560458698827208e-01, /* 456 */ + 6.572293903247938e-01, /* 457 */ + 6.584112923571166e-01, /* 458 */ + 6.595915730692873e-01, /* 459 */ + 6.607702295548961e-01, /* 460 */ + 6.619472589115332e-01, /* 461 */ + 6.631226582407952e-01, /* 462 */ + 6.642964246482929e-01, /* 463 */ + 6.654685552436579e-01, /* 464 */ + 6.666390471405501e-01, /* 465 */ + 6.678078974566646e-01, /* 466 */ + 6.689751033137388e-01, /* 467 */ + 6.701406618375595e-01, /* 468 */ + 6.713045701579703e-01, /* 469 */ + 6.724668254088779e-01, /* 470 */ + 6.736274247282602e-01, /* 471 */ + 6.747863652581724e-01, /* 472 */ + 6.759436441447543e-01, /* 473 */ + 6.770992585382379e-01, /* 474 */ + 6.782532055929538e-01, /* 475 */ + 6.794054824673382e-01, /* 476 */ + 6.805560863239402e-01, /* 477 */ + 6.817050143294286e-01, /* 478 */ + 6.828522636545991e-01, /* 479 */ + 6.839978314743810e-01, /* 480 */ + 6.851417149678442e-01, /* 481 */ + 6.862839113182062e-01, /* 482 */ + 6.874244177128394e-01, /* 483 */ + 6.885632313432770e-01, /* 484 */ + 6.897003494052213e-01, /* 485 */ + 6.908357690985494e-01, /* 486 */ + 6.919694876273208e-01, /* 487 */ + 6.931015021997841e-01, /* 488 */ + 6.942318100283835e-01, /* 489 */ + 6.953604083297665e-01, /* 490 */ + 6.964872943247901e-01, /* 491 */ + 6.976124652385276e-01, /* 492 */ + 6.987359183002758e-01, /* 493 */ + 6.998576507435618e-01, /* 494 */ + 7.009776598061495e-01, /* 495 */ + 7.020959427300464e-01, /* 496 */ + 7.032124967615111e-01, /* 497 */ + 7.043273191510590e-01, /* 498 */ + 7.054404071534700e-01, /* 499 */ + 7.065517580277947e-01, /* 500 */ + 7.076613690373614e-01, /* 501 */ + 7.087692374497827e-01, /* 502 */ + 7.098753605369623e-01, /* 503 */ + 7.109797355751019e-01, /* 504 */ + 7.120823598447074e-01, /* 505 */ + 7.131832306305962e-01, /* 506 */ + 7.142823452219036e-01, /* 507 */ + 7.153797009120892e-01, /* 508 */ + 7.164752949989442e-01, /* 509 */ + 7.175691247845974e-01, /* 510 */ + 7.186611875755224e-01, /* 511 */ + 7.197514806825439e-01, /* 512 */ + 7.208400014208443e-01, /* 513 */ + 7.219267471099703e-01, /* 514 */ + 7.230117150738400e-01, /* 515 */ + 7.240949026407488e-01, /* 516 */ + 7.251763071433764e-01, /* 517 */ + 7.262559259187933e-01, /* 518 */ + 7.273337563084670e-01, /* 519 */ + 7.284097956582691e-01, /* 520 */ + 7.294840413184817e-01, /* 521 */ + 7.305564906438036e-01, /* 522 */ + 7.316271409933570e-01, /* 523 */ + 7.326959897306943e-01, /* 524 */ + 7.337630342238040e-01, /* 525 */ + 7.348282718451178e-01, /* 526 */ + 7.358916999715164e-01, /* 527 */ + 7.369533159843368e-01, /* 528 */ + 7.380131172693778e-01, /* 529 */ + 7.390711012169073e-01, /* 530 */ + 7.401272652216682e-01, /* 531 */ + 7.411816066828849e-01, /* 532 */ + 7.422341230042699e-01, /* 533 */ + 7.432848115940299e-01, /* 534 */ + 7.443336698648725e-01, /* 535 */ + 7.453806952340122e-01, /* 536 */ + 7.464258851231773e-01, /* 537 */ + 7.474692369586156e-01, /* 538 */ + 7.485107481711011e-01, /* 539 */ + 7.495504161959405e-01, /* 540 */ + 7.505882384729792e-01, /* 541 */ + 7.516242124466075e-01, /* 542 */ + 7.526583355657674e-01, /* 543 */ + 7.536906052839586e-01, /* 544 */ + 7.547210190592443e-01, /* 545 */ + 7.557495743542583e-01, /* 546 */ + 7.567762686362108e-01, /* 547 */ + 7.578010993768947e-01, /* 548 */ + 7.588240640526916e-01, /* 549 */ + 7.598451601445788e-01, /* 550 */ + 7.608643851381341e-01, /* 551 */ + 7.618817365235437e-01, /* 552 */ + 7.628972117956068e-01, /* 553 */ + 7.639108084537428e-01, /* 554 */ + 7.649225240019972e-01, /* 555 */ + 7.659323559490476e-01, /* 556 */ + 7.669403018082099e-01, /* 557 */ + 7.679463590974444e-01, /* 558 */ + 7.689505253393620e-01, /* 559 */ + 7.699527980612303e-01, /* 560 */ + 7.709531747949796e-01, /* 561 */ + 7.719516530772089e-01, /* 562 */ + 7.729482304491924e-01, /* 563 */ + 7.739429044568850e-01, /* 564 */ + 7.749356726509284e-01, /* 565 */ + 7.759265325866578e-01, /* 566 */ + 7.769154818241071e-01, /* 567 */ + 7.779025179280153e-01, /* 568 */ + 7.788876384678325e-01, /* 569 */ + 7.798708410177257e-01, /* 570 */ + 7.808521231565851e-01, /* 571 */ + 7.818314824680298e-01, /* 572 */ + 7.828089165404135e-01, /* 573 */ + 7.837844229668313e-01, /* 574 */ + 7.847579993451246e-01, /* 575 */ + 7.857296432778876e-01, /* 576 */ + 7.866993523724733e-01, /* 577 */ + 7.876671242409992e-01, /* 578 */ + 7.886329565003528e-01, /* 579 */ + 7.895968467721981e-01, /* 580 */ + 7.905587926829811e-01, /* 581 */ + 7.915187918639360e-01, /* 582 */ + 7.924768419510904e-01, /* 583 */ + 7.934329405852717e-01, /* 584 */ + 7.943870854121126e-01, /* 585 */ + 7.953392740820571e-01, /* 586 */ + 7.962895042503660e-01, /* 587 */ + 7.972377735771232e-01, /* 588 */ + 7.981840797272409e-01, /* 589 */ + 7.991284203704654e-01, /* 590 */ + 8.000707931813833e-01, /* 591 */ + 8.010111958394268e-01, /* 592 */ + 8.019496260288795e-01, /* 593 */ + 8.028860814388824e-01, /* 594 */ + 8.038205597634391e-01, /* 595 */ + 8.047530587014217e-01, /* 596 */ + 8.056835759565766e-01, /* 597 */ + 8.066121092375300e-01, /* 598 */ + 8.075386562577938e-01, /* 599 */ + 8.084632147357704e-01, /* 600 */ + 8.093857823947597e-01, /* 601 */ + 8.103063569629634e-01, /* 602 */ + 8.112249361734913e-01, /* 603 */ + 8.121415177643669e-01, /* 604 */ + 8.130560994785324e-01, /* 605 */ + 8.139686790638551e-01, /* 606 */ + 8.148792542731320e-01, /* 607 */ + 8.157878228640961e-01, /* 608 */ + 8.166943825994217e-01, /* 609 */ + 8.175989312467298e-01, /* 610 */ + 8.185014665785935e-01, /* 611 */ + 8.194019863725437e-01, /* 612 */ + 8.203004884110747e-01, /* 613 */ + 8.211969704816493e-01, /* 614 */ + 8.220914303767043e-01, /* 615 */ + 8.229838658936564e-01, /* 616 */ + 8.238742748349069e-01, /* 617 */ + 8.247626550078477e-01, /* 618 */ + 8.256490042248665e-01, /* 619 */ + 8.265333203033521e-01, /* 620 */ + 8.274156010656999e-01, /* 621 */ + 8.282958443393171e-01, /* 622 */ + 8.291740479566283e-01, /* 623 */ + 8.300502097550806e-01, /* 624 */ + 8.309243275771491e-01, /* 625 */ + 8.317963992703420e-01, /* 626 */ + 8.326664226872064e-01, /* 627 */ + 8.335343956853326e-01, /* 628 */ + 8.344003161273608e-01, /* 629 */ + 8.352641818809847e-01, /* 630 */ + 8.361259908189583e-01, /* 631 */ + 8.369857408191002e-01, /* 632 */ + 8.378434297642989e-01, /* 633 */ + 8.386990555425186e-01, /* 634 */ + 8.395526160468036e-01, /* 635 */ + 8.404041091752841e-01, /* 636 */ + 8.412535328311811e-01, /* 637 */ + 8.421008849228117e-01, /* 638 */ + 8.429461633635940e-01, /* 639 */ + 8.437893660720525e-01, /* 640 */ + 8.446304909718232e-01, /* 641 */ + 8.454695359916585e-01, /* 642 */ + 8.463064990654326e-01, /* 643 */ + 8.471413781321465e-01, /* 644 */ + 8.479741711359327e-01, /* 645 */ + 8.488048760260607e-01, /* 646 */ + 8.496334907569423e-01, /* 647 */ + 8.504600132881356e-01, /* 648 */ + 8.512844415843511e-01, /* 649 */ + 8.521067736154565e-01, /* 650 */ + 8.529270073564810e-01, /* 651 */ + 8.537451407876209e-01, /* 652 */ + 8.545611718942447e-01, /* 653 */ + 8.553750986668979e-01, /* 654 */ + 8.561869191013073e-01, /* 655 */ + 8.569966311983869e-01, /* 656 */ + 8.578042329642425e-01, /* 657 */ + 8.586097224101764e-01, /* 658 */ + 8.594130975526924e-01, /* 659 */ + 8.602143564135006e-01, /* 660 */ + 8.610134970195229e-01, /* 661 */ + 8.618105174028966e-01, /* 662 */ + 8.626054156009807e-01, /* 663 */ + 8.633981896563595e-01, /* 664 */ + 8.641888376168482e-01, /* 665 */ + 8.649773575354974e-01, /* 666 */ + 8.657637474705979e-01, /* 667 */ + 8.665480054856857e-01, /* 668 */ + 8.673301296495464e-01, /* 669 */ + 8.681101180362202e-01, /* 670 */ + 8.688879687250065e-01, /* 671 */ + 8.696636798004691e-01, /* 672 */ + 8.704372493524400e-01, /* 673 */ + 8.712086754760252e-01, /* 674 */ + 8.719779562716083e-01, /* 675 */ + 8.727450898448561e-01, /* 676 */ + 8.735100743067228e-01, /* 677 */ + 8.742729077734545e-01, /* 678 */ + 8.750335883665944e-01, /* 679 */ + 8.757921142129869e-01, /* 680 */ + 8.765484834447824e-01, /* 681 */ + 8.773026941994420e-01, /* 682 */ + 8.780547446197419e-01, /* 683 */ + 8.788046328537781e-01, /* 684 */ + 8.795523570549709e-01, /* 685 */ + 8.802979153820697e-01, /* 686 */ + 8.810413059991569e-01, /* 687 */ + 8.817825270756531e-01, /* 688 */ + 8.825215767863213e-01, /* 689 */ + 8.832584533112713e-01, /* 690 */ + 8.839931548359646e-01, /* 691 */ + 8.847256795512183e-01, /* 692 */ + 8.854560256532099e-01, /* 693 */ + 8.861841913434817e-01, /* 694 */ + 8.869101748289453e-01, /* 695 */ + 8.876339743218858e-01, /* 696 */ + 8.883555880399664e-01, /* 697 */ + 8.890750142062326e-01, /* 698 */ + 8.897922510491166e-01, /* 699 */ + 8.905072968024423e-01, /* 700 */ + 8.912201497054284e-01, /* 701 */ + 8.919308080026938e-01, /* 702 */ + 8.926392699442616e-01, /* 703 */ + 8.933455337855631e-01, /* 704 */ + 8.940495977874426e-01, /* 705 */ + 8.947514602161615e-01, /* 706 */ + 8.954511193434023e-01, /* 707 */ + 8.961485734462731e-01, /* 708 */ + 8.968438208073118e-01, /* 709 */ + 8.975368597144907e-01, /* 710 */ + 8.982276884612198e-01, /* 711 */ + 8.989163053463520e-01, /* 712 */ + 8.996027086741867e-01, /* 713 */ + 9.002868967544739e-01, /* 714 */ + 9.009688679024191e-01, /* 715 */ + 9.016486204386864e-01, /* 716 */ + 9.023261526894035e-01, /* 717 */ + 9.030014629861653e-01, /* 718 */ + 9.036745496660386e-01, /* 719 */ + 9.043454110715651e-01, /* 720 */ + 9.050140455507668e-01, /* 721 */ + 9.056804514571491e-01, /* 722 */ + 9.063446271497057e-01, /* 723 */ + 9.070065709929211e-01, /* 724 */ + 9.076662813567770e-01, /* 725 */ + 9.083237566167539e-01, /* 726 */ + 9.089789951538368e-01, /* 727 */ + 9.096319953545183e-01, /* 728 */ + 9.102827556108030e-01, /* 729 */ + 9.109312743202110e-01, /* 730 */ + 9.115775498857827e-01, /* 731 */ + 9.122215807160815e-01, /* 732 */ + 9.128633652251990e-01, /* 733 */ + 9.135029018327580e-01, /* 734 */ + 9.141401889639166e-01, /* 735 */ + 9.147752250493725e-01, /* 736 */ + 9.154080085253663e-01, /* 737 */ + 9.160385378336857e-01, /* 738 */ + 9.166668114216692e-01, /* 739 */ + 9.172928277422099e-01, /* 740 */ + 9.179165852537593e-01, /* 741 */ + 9.185380824203315e-01, /* 742 */ + 9.191573177115061e-01, /* 743 */ + 9.197742896024330e-01, /* 744 */ + 9.203889965738354e-01, /* 745 */ + 9.210014371120140e-01, /* 746 */ + 9.216116097088501e-01, /* 747 */ + 9.222195128618103e-01, /* 748 */ + 9.228251450739493e-01, /* 749 */ + 9.234285048539139e-01, /* 750 */ + 9.240295907159470e-01, /* 751 */ + 9.246284011798909e-01, /* 752 */ + 9.252249347711905e-01, /* 753 */ + 9.258191900208981e-01, /* 754 */ + 9.264111654656760e-01, /* 755 */ + 9.270008596478005e-01, /* 756 */ + 9.275882711151657e-01, /* 757 */ + 9.281733984212863e-01, /* 758 */ + 9.287562401253023e-01, /* 759 */ + 9.293367947919815e-01, /* 760 */ + 9.299150609917235e-01, /* 761 */ + 9.304910373005634e-01, /* 762 */ + 9.310647223001750e-01, /* 763 */ + 9.316361145778743e-01, /* 764 */ + 9.322052127266233e-01, /* 765 */ + 9.327720153450327e-01, /* 766 */ + 9.333365210373668e-01, /* 767 */ + 9.338987284135449e-01, /* 768 */ + 9.344586360891468e-01, /* 769 */ + 9.350162426854148e-01, /* 770 */ + 9.355715468292575e-01, /* 771 */ + 9.361245471532534e-01, /* 772 */ + 9.366752422956540e-01, /* 773 */ + 9.372236309003873e-01, /* 774 */ + 9.377697116170610e-01, /* 775 */ + 9.383134831009662e-01, /* 776 */ + 9.388549440130799e-01, /* 777 */ + 9.393940930200693e-01, /* 778 */ + 9.399309287942944e-01, /* 779 */ + 9.404654500138115e-01, /* 780 */ + 9.409976553623765e-01, /* 781 */ + 9.415275435294478e-01, /* 782 */ + 9.420551132101902e-01, /* 783 */ + 9.425803631054773e-01, /* 784 */ + 9.431032919218956e-01, /* 785 */ + 9.436238983717468e-01, /* 786 */ + 9.441421811730513e-01, /* 787 */ + 9.446581390495518e-01, /* 788 */ + 9.451717707307158e-01, /* 789 */ + 9.456830749517390e-01, /* 790 */ + 9.461920504535486e-01, /* 791 */ + 9.466986959828059e-01, /* 792 */ + 9.472030102919100e-01, /* 793 */ + 9.477049921390005e-01, /* 794 */ + 9.482046402879605e-01, /* 795 */ + 9.487019535084197e-01, /* 796 */ + 9.491969305757577e-01, /* 797 */ + 9.496895702711068e-01, /* 798 */ + 9.501798713813550e-01, /* 799 */ + 9.506678326991488e-01, /* 800 */ + 9.511534530228968e-01, /* 801 */ + 9.516367311567716e-01, /* 802 */ + 9.521176659107141e-01, /* 803 */ + 9.525962561004352e-01, /* 804 */ + 9.530725005474194e-01, /* 805 */ + 9.535463980789276e-01, /* 806 */ + 9.540179475279997e-01, /* 807 */ + 9.544871477334580e-01, /* 808 */ + 9.549539975399095e-01, /* 809 */ + 9.554184957977490e-01, /* 810 */ + 9.558806413631620e-01, /* 811 */ + 9.563404330981276e-01, /* 812 */ + 9.567978698704207e-01, /* 813 */ + 9.572529505536158e-01, /* 814 */ + 9.577056740270887e-01, /* 815 */ + 9.581560391760202e-01, /* 816 */ + 9.586040448913981e-01, /* 817 */ + 9.590496900700202e-01, /* 818 */ + 9.594929736144974e-01, /* 819 */ + 9.599338944332557e-01, /* 820 */ + 9.603724514405396e-01, /* 821 */ + 9.608086435564140e-01, /* 822 */ + 9.612424697067677e-01, /* 823 */ + 9.616739288233154e-01, /* 824 */ + 9.621030198436005e-01, /* 825 */ + 9.625297417109979e-01, /* 826 */ + 9.629540933747166e-01, /* 827 */ + 9.633760737898017e-01, /* 828 */ + 9.637956819171380e-01, /* 829 */ + 9.642129167234518e-01, /* 830 */ + 9.646277771813133e-01, /* 831 */ + 9.650402622691399e-01, /* 832 */ + 9.654503709711981e-01, /* 833 */ + 9.658581022776063e-01, /* 834 */ + 9.662634551843370e-01, /* 835 */ + 9.666664286932195e-01, /* 836 */ + 9.670670218119425e-01, /* 837 */ + 9.674652335540560e-01, /* 838 */ + 9.678610629389744e-01, /* 839 */ + 9.682545089919784e-01, /* 840 */ + 9.686455707442176e-01, /* 841 */ + 9.690342472327130e-01, /* 842 */ + 9.694205375003593e-01, /* 843 */ + 9.698044405959267e-01, /* 844 */ + 9.701859555740645e-01, /* 845 */ + 9.705650814953021e-01, /* 846 */ + 9.709418174260520e-01, /* 847 */ + 9.713161624386123e-01, /* 848 */ + 9.716881156111683e-01, /* 849 */ + 9.720576760277954e-01, /* 850 */ + 9.724248427784608e-01, /* 851 */ + 9.727896149590264e-01, /* 852 */ + 9.731519916712503e-01, /* 853 */ + 9.735119720227898e-01, /* 854 */ + 9.738695551272029e-01, /* 855 */ + 9.742247401039505e-01, /* 856 */ + 9.745775260783994e-01, /* 857 */ + 9.749279121818236e-01, /* 858 */ + 9.752758975514066e-01, /* 859 */ + 9.756214813302438e-01, /* 860 */ + 9.759646626673444e-01, /* 861 */ + 9.763054407176336e-01, /* 862 */ + 9.766438146419546e-01, /* 863 */ + 9.769797836070706e-01, /* 864 */ + 9.773133467856671e-01, /* 865 */ + 9.776445033563537e-01, /* 866 */ + 9.779732525036661e-01, /* 867 */ + 9.782995934180686e-01, /* 868 */ + 9.786235252959552e-01, /* 869 */ + 9.789450473396526e-01, /* 870 */ + 9.792641587574211e-01, /* 871 */ + 9.795808587634576e-01, /* 872 */ + 9.798951465778968e-01, /* 873 */ + 9.802070214268133e-01, /* 874 */ + 9.805164825422236e-01, /* 875 */ + 9.808235291620881e-01, /* 876 */ + 9.811281605303128e-01, /* 877 */ + 9.814303758967510e-01, /* 878 */ + 9.817301745172056e-01, /* 879 */ + 9.820275556534303e-01, /* 880 */ + 9.823225185731322e-01, /* 881 */ + 9.826150625499731e-01, /* 882 */ + 9.829051868635711e-01, /* 883 */ + 9.831928907995030e-01, /* 884 */ + 9.834781736493055e-01, /* 885 */ + 9.837610347104772e-01, /* 886 */ + 9.840414732864804e-01, /* 887 */ + 9.843194886867427e-01, /* 888 */ + 9.845950802266584e-01, /* 889 */ + 9.848682472275909e-01, /* 890 */ + 9.851389890168738e-01, /* 891 */ + 9.854073049278126e-01, /* 892 */ + 9.856731942996866e-01, /* 893 */ + 9.859366564777504e-01, /* 894 */ + 9.861976908132354e-01, /* 895 */ + 9.864562966633516e-01, /* 896 */ + 9.867124733912889e-01, /* 897 */ + 9.869662203662192e-01, /* 898 */ + 9.872175369632971e-01, /* 899 */ + 9.874664225636623e-01, /* 900 */ + 9.877128765544410e-01, /* 901 */ + 9.879568983287464e-01, /* 902 */ + 9.881984872856817e-01, /* 903 */ + 9.884376428303405e-01, /* 904 */ + 9.886743643738087e-01, /* 905 */ + 9.889086513331659e-01, /* 906 */ + 9.891405031314865e-01, /* 907 */ + 9.893699191978420e-01, /* 908 */ + 9.895968989673012e-01, /* 909 */ + 9.898214418809327e-01, /* 910 */ + 9.900435473858056e-01, /* 911 */ + 9.902632149349908e-01, /* 912 */ + 9.904804439875632e-01, /* 913 */ + 9.906952340086018e-01, /* 914 */ + 9.909075844691920e-01, /* 915 */ + 9.911174948464266e-01, /* 916 */ + 9.913249646234069e-01, /* 917 */ + 9.915299932892441e-01, /* 918 */ + 9.917325803390605e-01, /* 919 */ + 9.919327252739911e-01, /* 920 */ + 9.921304276011843e-01, /* 921 */ + 9.923256868338034e-01, /* 922 */ + 9.925185024910277e-01, /* 923 */ + 9.927088740980540e-01, /* 924 */ + 9.928968011860971e-01, /* 925 */ + 9.930822832923918e-01, /* 926 */ + 9.932653199601933e-01, /* 927 */ + 9.934459107387786e-01, /* 928 */ + 9.936240551834480e-01, /* 929 */ + 9.937997528555254e-01, /* 930 */ + 9.939730033223599e-01, /* 931 */ + 9.941438061573271e-01, /* 932 */ + 9.943121609398295e-01, /* 933 */ + 9.944780672552980e-01, /* 934 */ + 9.946415246951927e-01, /* 935 */ + 9.948025328570040e-01, /* 936 */ + 9.949610913442537e-01, /* 937 */ + 9.951171997664957e-01, /* 938 */ + 9.952708577393173e-01, /* 939 */ + 9.954220648843398e-01, /* 940 */ + 9.955708208292197e-01, /* 941 */ + 9.957171252076493e-01, /* 942 */ + 9.958609776593582e-01, /* 943 */ + 9.960023778301135e-01, /* 944 */ + 9.961413253717212e-01, /* 945 */ + 9.962778199420265e-01, /* 946 */ + 9.964118612049152e-01, /* 947 */ + 9.965434488303145e-01, /* 948 */ + 9.966725824941932e-01, /* 949 */ + 9.967992618785633e-01, /* 950 */ + 9.969234866714800e-01, /* 951 */ + 9.970452565670431e-01, /* 952 */ + 9.971645712653977e-01, /* 953 */ + 9.972814304727343e-01, /* 954 */ + 9.973958339012905e-01, /* 955 */ + 9.975077812693507e-01, /* 956 */ + 9.976172723012476e-01, /* 957 */ + 9.977243067273625e-01, /* 958 */ + 9.978288842841259e-01, /* 959 */ + 9.979310047140184e-01, /* 960 */ + 9.980306677655713e-01, /* 961 */ + 9.981278731933668e-01, /* 962 */ + 9.982226207580394e-01, /* 963 */ + 9.983149102262756e-01, /* 964 */ + 9.984047413708150e-01, /* 965 */ + 9.984921139704509e-01, /* 966 */ + 9.985770278100307e-01, /* 967 */ + 9.986594826804561e-01, /* 968 */ + 9.987394783786845e-01, /* 969 */ + 9.988170147077284e-01, /* 970 */ + 9.988920914766568e-01, /* 971 */ + 9.989647085005952e-01, /* 972 */ + 9.990348656007260e-01, /* 973 */ + 9.991025626042892e-01, /* 974 */ + 9.991677993445829e-01, /* 975 */ + 9.992305756609632e-01, /* 976 */ + 9.992908913988453e-01, /* 977 */ + 9.993487464097032e-01, /* 978 */ + 9.994041405510704e-01, /* 979 */ + 9.994570736865406e-01, /* 980 */ + 9.995075456857671e-01, /* 981 */ + 9.995555564244639e-01, /* 982 */ + 9.996011057844061e-01, /* 983 */ + 9.996441936534295e-01, /* 984 */ + 9.996848199254315e-01, /* 985 */ + 9.997229845003707e-01, /* 986 */ + 9.997586872842681e-01, /* 987 */ + 9.997919281892065e-01, /* 988 */ + 9.998227071333310e-01, /* 989 */ + 9.998510240408494e-01, /* 990 */ + 9.998768788420320e-01, /* 991 */ + 9.999002714732120e-01, /* 992 */ + 9.999212018767858e-01, /* 993 */ + 9.999396700012126e-01, /* 994 */ + 9.999556758010154e-01, /* 995 */ + 9.999692192367803e-01, /* 996 */ + 9.999803002751568e-01, /* 997 */ + 9.999889188888583e-01, /* 998 */ + 9.999950750566616e-01, /* 999 */ + 9.999987687634074e-01, /* 1000 */ + 1.000000000000000e+00, /* 1001 */ +}; diff --git a/libs/fluidsynth/fluid_rvoice_dsp_tables.inc.h b/libs/fluidsynth/fluid_rvoice_dsp_tables.inc.h new file mode 100644 index 00000000000..062eaac5935 --- /dev/null +++ b/libs/fluidsynth/fluid_rvoice_dsp_tables.inc.h @@ -0,0 +1,4109 @@ +/* THIS FILE HAS BEEN AUTOMATICALLY GENERATED. DO NOT EDIT. */ + +static const fluid_real_t interp_coeff_linear[256][2] = { + { + 1.000000000000000e+00, /* 0 */ + 0.000000000000000e+00, /* 1 */ + }, { + 9.960937500000000e-01, /* 2 */ + 3.906250000000000e-03, /* 3 */ + }, { + 9.921875000000000e-01, /* 4 */ + 7.812500000000000e-03, /* 5 */ + }, { + 9.882812500000000e-01, /* 6 */ + 1.171875000000000e-02, /* 7 */ + }, { + 9.843750000000000e-01, /* 8 */ + 1.562500000000000e-02, /* 9 */ + }, { + 9.804687500000000e-01, /* 10 */ + 1.953125000000000e-02, /* 11 */ + }, { + 9.765625000000000e-01, /* 12 */ + 2.343750000000000e-02, /* 13 */ + }, { + 9.726562500000000e-01, /* 14 */ + 2.734375000000000e-02, /* 15 */ + }, { + 9.687500000000000e-01, /* 16 */ + 3.125000000000000e-02, /* 17 */ + }, { + 9.648437500000000e-01, /* 18 */ + 3.515625000000000e-02, /* 19 */ + }, { + 9.609375000000000e-01, /* 20 */ + 3.906250000000000e-02, /* 21 */ + }, { + 9.570312500000000e-01, /* 22 */ + 4.296875000000000e-02, /* 23 */ + }, { + 9.531250000000000e-01, /* 24 */ + 4.687500000000000e-02, /* 25 */ + }, { + 9.492187500000000e-01, /* 26 */ + 5.078125000000000e-02, /* 27 */ + }, { + 9.453125000000000e-01, /* 28 */ + 5.468750000000000e-02, /* 29 */ + }, { + 9.414062500000000e-01, /* 30 */ + 5.859375000000000e-02, /* 31 */ + }, { + 9.375000000000000e-01, /* 32 */ + 6.250000000000000e-02, /* 33 */ + }, { + 9.335937500000000e-01, /* 34 */ + 6.640625000000000e-02, /* 35 */ + }, { + 9.296875000000000e-01, /* 36 */ + 7.031250000000000e-02, /* 37 */ + }, { + 9.257812500000000e-01, /* 38 */ + 7.421875000000000e-02, /* 39 */ + }, { + 9.218750000000000e-01, /* 40 */ + 7.812500000000000e-02, /* 41 */ + }, { + 9.179687500000000e-01, /* 42 */ + 8.203125000000000e-02, /* 43 */ + }, { + 9.140625000000000e-01, /* 44 */ + 8.593750000000000e-02, /* 45 */ + }, { + 9.101562500000000e-01, /* 46 */ + 8.984375000000000e-02, /* 47 */ + }, { + 9.062500000000000e-01, /* 48 */ + 9.375000000000000e-02, /* 49 */ + }, { + 9.023437500000000e-01, /* 50 */ + 9.765625000000000e-02, /* 51 */ + }, { + 8.984375000000000e-01, /* 52 */ + 1.015625000000000e-01, /* 53 */ + }, { + 8.945312500000000e-01, /* 54 */ + 1.054687500000000e-01, /* 55 */ + }, { + 8.906250000000000e-01, /* 56 */ + 1.093750000000000e-01, /* 57 */ + }, { + 8.867187500000000e-01, /* 58 */ + 1.132812500000000e-01, /* 59 */ + }, { + 8.828125000000000e-01, /* 60 */ + 1.171875000000000e-01, /* 61 */ + }, { + 8.789062500000000e-01, /* 62 */ + 1.210937500000000e-01, /* 63 */ + }, { + 8.750000000000000e-01, /* 64 */ + 1.250000000000000e-01, /* 65 */ + }, { + 8.710937500000000e-01, /* 66 */ + 1.289062500000000e-01, /* 67 */ + }, { + 8.671875000000000e-01, /* 68 */ + 1.328125000000000e-01, /* 69 */ + }, { + 8.632812500000000e-01, /* 70 */ + 1.367187500000000e-01, /* 71 */ + }, { + 8.593750000000000e-01, /* 72 */ + 1.406250000000000e-01, /* 73 */ + }, { + 8.554687500000000e-01, /* 74 */ + 1.445312500000000e-01, /* 75 */ + }, { + 8.515625000000000e-01, /* 76 */ + 1.484375000000000e-01, /* 77 */ + }, { + 8.476562500000000e-01, /* 78 */ + 1.523437500000000e-01, /* 79 */ + }, { + 8.437500000000000e-01, /* 80 */ + 1.562500000000000e-01, /* 81 */ + }, { + 8.398437500000000e-01, /* 82 */ + 1.601562500000000e-01, /* 83 */ + }, { + 8.359375000000000e-01, /* 84 */ + 1.640625000000000e-01, /* 85 */ + }, { + 8.320312500000000e-01, /* 86 */ + 1.679687500000000e-01, /* 87 */ + }, { + 8.281250000000000e-01, /* 88 */ + 1.718750000000000e-01, /* 89 */ + }, { + 8.242187500000000e-01, /* 90 */ + 1.757812500000000e-01, /* 91 */ + }, { + 8.203125000000000e-01, /* 92 */ + 1.796875000000000e-01, /* 93 */ + }, { + 8.164062500000000e-01, /* 94 */ + 1.835937500000000e-01, /* 95 */ + }, { + 8.125000000000000e-01, /* 96 */ + 1.875000000000000e-01, /* 97 */ + }, { + 8.085937500000000e-01, /* 98 */ + 1.914062500000000e-01, /* 99 */ + }, { + 8.046875000000000e-01, /* 100 */ + 1.953125000000000e-01, /* 101 */ + }, { + 8.007812500000000e-01, /* 102 */ + 1.992187500000000e-01, /* 103 */ + }, { + 7.968750000000000e-01, /* 104 */ + 2.031250000000000e-01, /* 105 */ + }, { + 7.929687500000000e-01, /* 106 */ + 2.070312500000000e-01, /* 107 */ + }, { + 7.890625000000000e-01, /* 108 */ + 2.109375000000000e-01, /* 109 */ + }, { + 7.851562500000000e-01, /* 110 */ + 2.148437500000000e-01, /* 111 */ + }, { + 7.812500000000000e-01, /* 112 */ + 2.187500000000000e-01, /* 113 */ + }, { + 7.773437500000000e-01, /* 114 */ + 2.226562500000000e-01, /* 115 */ + }, { + 7.734375000000000e-01, /* 116 */ + 2.265625000000000e-01, /* 117 */ + }, { + 7.695312500000000e-01, /* 118 */ + 2.304687500000000e-01, /* 119 */ + }, { + 7.656250000000000e-01, /* 120 */ + 2.343750000000000e-01, /* 121 */ + }, { + 7.617187500000000e-01, /* 122 */ + 2.382812500000000e-01, /* 123 */ + }, { + 7.578125000000000e-01, /* 124 */ + 2.421875000000000e-01, /* 125 */ + }, { + 7.539062500000000e-01, /* 126 */ + 2.460937500000000e-01, /* 127 */ + }, { + 7.500000000000000e-01, /* 128 */ + 2.500000000000000e-01, /* 129 */ + }, { + 7.460937500000000e-01, /* 130 */ + 2.539062500000000e-01, /* 131 */ + }, { + 7.421875000000000e-01, /* 132 */ + 2.578125000000000e-01, /* 133 */ + }, { + 7.382812500000000e-01, /* 134 */ + 2.617187500000000e-01, /* 135 */ + }, { + 7.343750000000000e-01, /* 136 */ + 2.656250000000000e-01, /* 137 */ + }, { + 7.304687500000000e-01, /* 138 */ + 2.695312500000000e-01, /* 139 */ + }, { + 7.265625000000000e-01, /* 140 */ + 2.734375000000000e-01, /* 141 */ + }, { + 7.226562500000000e-01, /* 142 */ + 2.773437500000000e-01, /* 143 */ + }, { + 7.187500000000000e-01, /* 144 */ + 2.812500000000000e-01, /* 145 */ + }, { + 7.148437500000000e-01, /* 146 */ + 2.851562500000000e-01, /* 147 */ + }, { + 7.109375000000000e-01, /* 148 */ + 2.890625000000000e-01, /* 149 */ + }, { + 7.070312500000000e-01, /* 150 */ + 2.929687500000000e-01, /* 151 */ + }, { + 7.031250000000000e-01, /* 152 */ + 2.968750000000000e-01, /* 153 */ + }, { + 6.992187500000000e-01, /* 154 */ + 3.007812500000000e-01, /* 155 */ + }, { + 6.953125000000000e-01, /* 156 */ + 3.046875000000000e-01, /* 157 */ + }, { + 6.914062500000000e-01, /* 158 */ + 3.085937500000000e-01, /* 159 */ + }, { + 6.875000000000000e-01, /* 160 */ + 3.125000000000000e-01, /* 161 */ + }, { + 6.835937500000000e-01, /* 162 */ + 3.164062500000000e-01, /* 163 */ + }, { + 6.796875000000000e-01, /* 164 */ + 3.203125000000000e-01, /* 165 */ + }, { + 6.757812500000000e-01, /* 166 */ + 3.242187500000000e-01, /* 167 */ + }, { + 6.718750000000000e-01, /* 168 */ + 3.281250000000000e-01, /* 169 */ + }, { + 6.679687500000000e-01, /* 170 */ + 3.320312500000000e-01, /* 171 */ + }, { + 6.640625000000000e-01, /* 172 */ + 3.359375000000000e-01, /* 173 */ + }, { + 6.601562500000000e-01, /* 174 */ + 3.398437500000000e-01, /* 175 */ + }, { + 6.562500000000000e-01, /* 176 */ + 3.437500000000000e-01, /* 177 */ + }, { + 6.523437500000000e-01, /* 178 */ + 3.476562500000000e-01, /* 179 */ + }, { + 6.484375000000000e-01, /* 180 */ + 3.515625000000000e-01, /* 181 */ + }, { + 6.445312500000000e-01, /* 182 */ + 3.554687500000000e-01, /* 183 */ + }, { + 6.406250000000000e-01, /* 184 */ + 3.593750000000000e-01, /* 185 */ + }, { + 6.367187500000000e-01, /* 186 */ + 3.632812500000000e-01, /* 187 */ + }, { + 6.328125000000000e-01, /* 188 */ + 3.671875000000000e-01, /* 189 */ + }, { + 6.289062500000000e-01, /* 190 */ + 3.710937500000000e-01, /* 191 */ + }, { + 6.250000000000000e-01, /* 192 */ + 3.750000000000000e-01, /* 193 */ + }, { + 6.210937500000000e-01, /* 194 */ + 3.789062500000000e-01, /* 195 */ + }, { + 6.171875000000000e-01, /* 196 */ + 3.828125000000000e-01, /* 197 */ + }, { + 6.132812500000000e-01, /* 198 */ + 3.867187500000000e-01, /* 199 */ + }, { + 6.093750000000000e-01, /* 200 */ + 3.906250000000000e-01, /* 201 */ + }, { + 6.054687500000000e-01, /* 202 */ + 3.945312500000000e-01, /* 203 */ + }, { + 6.015625000000000e-01, /* 204 */ + 3.984375000000000e-01, /* 205 */ + }, { + 5.976562500000000e-01, /* 206 */ + 4.023437500000000e-01, /* 207 */ + }, { + 5.937500000000000e-01, /* 208 */ + 4.062500000000000e-01, /* 209 */ + }, { + 5.898437500000000e-01, /* 210 */ + 4.101562500000000e-01, /* 211 */ + }, { + 5.859375000000000e-01, /* 212 */ + 4.140625000000000e-01, /* 213 */ + }, { + 5.820312500000000e-01, /* 214 */ + 4.179687500000000e-01, /* 215 */ + }, { + 5.781250000000000e-01, /* 216 */ + 4.218750000000000e-01, /* 217 */ + }, { + 5.742187500000000e-01, /* 218 */ + 4.257812500000000e-01, /* 219 */ + }, { + 5.703125000000000e-01, /* 220 */ + 4.296875000000000e-01, /* 221 */ + }, { + 5.664062500000000e-01, /* 222 */ + 4.335937500000000e-01, /* 223 */ + }, { + 5.625000000000000e-01, /* 224 */ + 4.375000000000000e-01, /* 225 */ + }, { + 5.585937500000000e-01, /* 226 */ + 4.414062500000000e-01, /* 227 */ + }, { + 5.546875000000000e-01, /* 228 */ + 4.453125000000000e-01, /* 229 */ + }, { + 5.507812500000000e-01, /* 230 */ + 4.492187500000000e-01, /* 231 */ + }, { + 5.468750000000000e-01, /* 232 */ + 4.531250000000000e-01, /* 233 */ + }, { + 5.429687500000000e-01, /* 234 */ + 4.570312500000000e-01, /* 235 */ + }, { + 5.390625000000000e-01, /* 236 */ + 4.609375000000000e-01, /* 237 */ + }, { + 5.351562500000000e-01, /* 238 */ + 4.648437500000000e-01, /* 239 */ + }, { + 5.312500000000000e-01, /* 240 */ + 4.687500000000000e-01, /* 241 */ + }, { + 5.273437500000000e-01, /* 242 */ + 4.726562500000000e-01, /* 243 */ + }, { + 5.234375000000000e-01, /* 244 */ + 4.765625000000000e-01, /* 245 */ + }, { + 5.195312500000000e-01, /* 246 */ + 4.804687500000000e-01, /* 247 */ + }, { + 5.156250000000000e-01, /* 248 */ + 4.843750000000000e-01, /* 249 */ + }, { + 5.117187500000000e-01, /* 250 */ + 4.882812500000000e-01, /* 251 */ + }, { + 5.078125000000000e-01, /* 252 */ + 4.921875000000000e-01, /* 253 */ + }, { + 5.039062500000000e-01, /* 254 */ + 4.960937500000000e-01, /* 255 */ + }, { + 5.000000000000000e-01, /* 256 */ + 5.000000000000000e-01, /* 257 */ + }, { + 4.960937500000000e-01, /* 258 */ + 5.039062500000000e-01, /* 259 */ + }, { + 4.921875000000000e-01, /* 260 */ + 5.078125000000000e-01, /* 261 */ + }, { + 4.882812500000000e-01, /* 262 */ + 5.117187500000000e-01, /* 263 */ + }, { + 4.843750000000000e-01, /* 264 */ + 5.156250000000000e-01, /* 265 */ + }, { + 4.804687500000000e-01, /* 266 */ + 5.195312500000000e-01, /* 267 */ + }, { + 4.765625000000000e-01, /* 268 */ + 5.234375000000000e-01, /* 269 */ + }, { + 4.726562500000000e-01, /* 270 */ + 5.273437500000000e-01, /* 271 */ + }, { + 4.687500000000000e-01, /* 272 */ + 5.312500000000000e-01, /* 273 */ + }, { + 4.648437500000000e-01, /* 274 */ + 5.351562500000000e-01, /* 275 */ + }, { + 4.609375000000000e-01, /* 276 */ + 5.390625000000000e-01, /* 277 */ + }, { + 4.570312500000000e-01, /* 278 */ + 5.429687500000000e-01, /* 279 */ + }, { + 4.531250000000000e-01, /* 280 */ + 5.468750000000000e-01, /* 281 */ + }, { + 4.492187500000000e-01, /* 282 */ + 5.507812500000000e-01, /* 283 */ + }, { + 4.453125000000000e-01, /* 284 */ + 5.546875000000000e-01, /* 285 */ + }, { + 4.414062500000000e-01, /* 286 */ + 5.585937500000000e-01, /* 287 */ + }, { + 4.375000000000000e-01, /* 288 */ + 5.625000000000000e-01, /* 289 */ + }, { + 4.335937500000000e-01, /* 290 */ + 5.664062500000000e-01, /* 291 */ + }, { + 4.296875000000000e-01, /* 292 */ + 5.703125000000000e-01, /* 293 */ + }, { + 4.257812500000000e-01, /* 294 */ + 5.742187500000000e-01, /* 295 */ + }, { + 4.218750000000000e-01, /* 296 */ + 5.781250000000000e-01, /* 297 */ + }, { + 4.179687500000000e-01, /* 298 */ + 5.820312500000000e-01, /* 299 */ + }, { + 4.140625000000000e-01, /* 300 */ + 5.859375000000000e-01, /* 301 */ + }, { + 4.101562500000000e-01, /* 302 */ + 5.898437500000000e-01, /* 303 */ + }, { + 4.062500000000000e-01, /* 304 */ + 5.937500000000000e-01, /* 305 */ + }, { + 4.023437500000000e-01, /* 306 */ + 5.976562500000000e-01, /* 307 */ + }, { + 3.984375000000000e-01, /* 308 */ + 6.015625000000000e-01, /* 309 */ + }, { + 3.945312500000000e-01, /* 310 */ + 6.054687500000000e-01, /* 311 */ + }, { + 3.906250000000000e-01, /* 312 */ + 6.093750000000000e-01, /* 313 */ + }, { + 3.867187500000000e-01, /* 314 */ + 6.132812500000000e-01, /* 315 */ + }, { + 3.828125000000000e-01, /* 316 */ + 6.171875000000000e-01, /* 317 */ + }, { + 3.789062500000000e-01, /* 318 */ + 6.210937500000000e-01, /* 319 */ + }, { + 3.750000000000000e-01, /* 320 */ + 6.250000000000000e-01, /* 321 */ + }, { + 3.710937500000000e-01, /* 322 */ + 6.289062500000000e-01, /* 323 */ + }, { + 3.671875000000000e-01, /* 324 */ + 6.328125000000000e-01, /* 325 */ + }, { + 3.632812500000000e-01, /* 326 */ + 6.367187500000000e-01, /* 327 */ + }, { + 3.593750000000000e-01, /* 328 */ + 6.406250000000000e-01, /* 329 */ + }, { + 3.554687500000000e-01, /* 330 */ + 6.445312500000000e-01, /* 331 */ + }, { + 3.515625000000000e-01, /* 332 */ + 6.484375000000000e-01, /* 333 */ + }, { + 3.476562500000000e-01, /* 334 */ + 6.523437500000000e-01, /* 335 */ + }, { + 3.437500000000000e-01, /* 336 */ + 6.562500000000000e-01, /* 337 */ + }, { + 3.398437500000000e-01, /* 338 */ + 6.601562500000000e-01, /* 339 */ + }, { + 3.359375000000000e-01, /* 340 */ + 6.640625000000000e-01, /* 341 */ + }, { + 3.320312500000000e-01, /* 342 */ + 6.679687500000000e-01, /* 343 */ + }, { + 3.281250000000000e-01, /* 344 */ + 6.718750000000000e-01, /* 345 */ + }, { + 3.242187500000000e-01, /* 346 */ + 6.757812500000000e-01, /* 347 */ + }, { + 3.203125000000000e-01, /* 348 */ + 6.796875000000000e-01, /* 349 */ + }, { + 3.164062500000000e-01, /* 350 */ + 6.835937500000000e-01, /* 351 */ + }, { + 3.125000000000000e-01, /* 352 */ + 6.875000000000000e-01, /* 353 */ + }, { + 3.085937500000000e-01, /* 354 */ + 6.914062500000000e-01, /* 355 */ + }, { + 3.046875000000000e-01, /* 356 */ + 6.953125000000000e-01, /* 357 */ + }, { + 3.007812500000000e-01, /* 358 */ + 6.992187500000000e-01, /* 359 */ + }, { + 2.968750000000000e-01, /* 360 */ + 7.031250000000000e-01, /* 361 */ + }, { + 2.929687500000000e-01, /* 362 */ + 7.070312500000000e-01, /* 363 */ + }, { + 2.890625000000000e-01, /* 364 */ + 7.109375000000000e-01, /* 365 */ + }, { + 2.851562500000000e-01, /* 366 */ + 7.148437500000000e-01, /* 367 */ + }, { + 2.812500000000000e-01, /* 368 */ + 7.187500000000000e-01, /* 369 */ + }, { + 2.773437500000000e-01, /* 370 */ + 7.226562500000000e-01, /* 371 */ + }, { + 2.734375000000000e-01, /* 372 */ + 7.265625000000000e-01, /* 373 */ + }, { + 2.695312500000000e-01, /* 374 */ + 7.304687500000000e-01, /* 375 */ + }, { + 2.656250000000000e-01, /* 376 */ + 7.343750000000000e-01, /* 377 */ + }, { + 2.617187500000000e-01, /* 378 */ + 7.382812500000000e-01, /* 379 */ + }, { + 2.578125000000000e-01, /* 380 */ + 7.421875000000000e-01, /* 381 */ + }, { + 2.539062500000000e-01, /* 382 */ + 7.460937500000000e-01, /* 383 */ + }, { + 2.500000000000000e-01, /* 384 */ + 7.500000000000000e-01, /* 385 */ + }, { + 2.460937500000000e-01, /* 386 */ + 7.539062500000000e-01, /* 387 */ + }, { + 2.421875000000000e-01, /* 388 */ + 7.578125000000000e-01, /* 389 */ + }, { + 2.382812500000000e-01, /* 390 */ + 7.617187500000000e-01, /* 391 */ + }, { + 2.343750000000000e-01, /* 392 */ + 7.656250000000000e-01, /* 393 */ + }, { + 2.304687500000000e-01, /* 394 */ + 7.695312500000000e-01, /* 395 */ + }, { + 2.265625000000000e-01, /* 396 */ + 7.734375000000000e-01, /* 397 */ + }, { + 2.226562500000000e-01, /* 398 */ + 7.773437500000000e-01, /* 399 */ + }, { + 2.187500000000000e-01, /* 400 */ + 7.812500000000000e-01, /* 401 */ + }, { + 2.148437500000000e-01, /* 402 */ + 7.851562500000000e-01, /* 403 */ + }, { + 2.109375000000000e-01, /* 404 */ + 7.890625000000000e-01, /* 405 */ + }, { + 2.070312500000000e-01, /* 406 */ + 7.929687500000000e-01, /* 407 */ + }, { + 2.031250000000000e-01, /* 408 */ + 7.968750000000000e-01, /* 409 */ + }, { + 1.992187500000000e-01, /* 410 */ + 8.007812500000000e-01, /* 411 */ + }, { + 1.953125000000000e-01, /* 412 */ + 8.046875000000000e-01, /* 413 */ + }, { + 1.914062500000000e-01, /* 414 */ + 8.085937500000000e-01, /* 415 */ + }, { + 1.875000000000000e-01, /* 416 */ + 8.125000000000000e-01, /* 417 */ + }, { + 1.835937500000000e-01, /* 418 */ + 8.164062500000000e-01, /* 419 */ + }, { + 1.796875000000000e-01, /* 420 */ + 8.203125000000000e-01, /* 421 */ + }, { + 1.757812500000000e-01, /* 422 */ + 8.242187500000000e-01, /* 423 */ + }, { + 1.718750000000000e-01, /* 424 */ + 8.281250000000000e-01, /* 425 */ + }, { + 1.679687500000000e-01, /* 426 */ + 8.320312500000000e-01, /* 427 */ + }, { + 1.640625000000000e-01, /* 428 */ + 8.359375000000000e-01, /* 429 */ + }, { + 1.601562500000000e-01, /* 430 */ + 8.398437500000000e-01, /* 431 */ + }, { + 1.562500000000000e-01, /* 432 */ + 8.437500000000000e-01, /* 433 */ + }, { + 1.523437500000000e-01, /* 434 */ + 8.476562500000000e-01, /* 435 */ + }, { + 1.484375000000000e-01, /* 436 */ + 8.515625000000000e-01, /* 437 */ + }, { + 1.445312500000000e-01, /* 438 */ + 8.554687500000000e-01, /* 439 */ + }, { + 1.406250000000000e-01, /* 440 */ + 8.593750000000000e-01, /* 441 */ + }, { + 1.367187500000000e-01, /* 442 */ + 8.632812500000000e-01, /* 443 */ + }, { + 1.328125000000000e-01, /* 444 */ + 8.671875000000000e-01, /* 445 */ + }, { + 1.289062500000000e-01, /* 446 */ + 8.710937500000000e-01, /* 447 */ + }, { + 1.250000000000000e-01, /* 448 */ + 8.750000000000000e-01, /* 449 */ + }, { + 1.210937500000000e-01, /* 450 */ + 8.789062500000000e-01, /* 451 */ + }, { + 1.171875000000000e-01, /* 452 */ + 8.828125000000000e-01, /* 453 */ + }, { + 1.132812500000000e-01, /* 454 */ + 8.867187500000000e-01, /* 455 */ + }, { + 1.093750000000000e-01, /* 456 */ + 8.906250000000000e-01, /* 457 */ + }, { + 1.054687500000000e-01, /* 458 */ + 8.945312500000000e-01, /* 459 */ + }, { + 1.015625000000000e-01, /* 460 */ + 8.984375000000000e-01, /* 461 */ + }, { + 9.765625000000000e-02, /* 462 */ + 9.023437500000000e-01, /* 463 */ + }, { + 9.375000000000000e-02, /* 464 */ + 9.062500000000000e-01, /* 465 */ + }, { + 8.984375000000000e-02, /* 466 */ + 9.101562500000000e-01, /* 467 */ + }, { + 8.593750000000000e-02, /* 468 */ + 9.140625000000000e-01, /* 469 */ + }, { + 8.203125000000000e-02, /* 470 */ + 9.179687500000000e-01, /* 471 */ + }, { + 7.812500000000000e-02, /* 472 */ + 9.218750000000000e-01, /* 473 */ + }, { + 7.421875000000000e-02, /* 474 */ + 9.257812500000000e-01, /* 475 */ + }, { + 7.031250000000000e-02, /* 476 */ + 9.296875000000000e-01, /* 477 */ + }, { + 6.640625000000000e-02, /* 478 */ + 9.335937500000000e-01, /* 479 */ + }, { + 6.250000000000000e-02, /* 480 */ + 9.375000000000000e-01, /* 481 */ + }, { + 5.859375000000000e-02, /* 482 */ + 9.414062500000000e-01, /* 483 */ + }, { + 5.468750000000000e-02, /* 484 */ + 9.453125000000000e-01, /* 485 */ + }, { + 5.078125000000000e-02, /* 486 */ + 9.492187500000000e-01, /* 487 */ + }, { + 4.687500000000000e-02, /* 488 */ + 9.531250000000000e-01, /* 489 */ + }, { + 4.296875000000000e-02, /* 490 */ + 9.570312500000000e-01, /* 491 */ + }, { + 3.906250000000000e-02, /* 492 */ + 9.609375000000000e-01, /* 493 */ + }, { + 3.515625000000000e-02, /* 494 */ + 9.648437500000000e-01, /* 495 */ + }, { + 3.125000000000000e-02, /* 496 */ + 9.687500000000000e-01, /* 497 */ + }, { + 2.734375000000000e-02, /* 498 */ + 9.726562500000000e-01, /* 499 */ + }, { + 2.343750000000000e-02, /* 500 */ + 9.765625000000000e-01, /* 501 */ + }, { + 1.953125000000000e-02, /* 502 */ + 9.804687500000000e-01, /* 503 */ + }, { + 1.562500000000000e-02, /* 504 */ + 9.843750000000000e-01, /* 505 */ + }, { + 1.171875000000000e-02, /* 506 */ + 9.882812500000000e-01, /* 507 */ + }, { + 7.812500000000000e-03, /* 508 */ + 9.921875000000000e-01, /* 509 */ + }, { + 3.906250000000000e-03, /* 510 */ + 9.960937500000000e-01, /* 511 */ + } +}; + +static const fluid_real_t interp_coeff[256][4] = { + { + -0.000000000000000e+00, /* 0 */ + 1.000000000000000e+00, /* 1 */ + 0.000000000000000e+00, /* 2 */ + -0.000000000000000e+00, /* 3 */ + }, { + -1.937896013259888e-03, /* 4 */ + 9.999619424343109e-01, /* 5 */ + 1.983553171157837e-03, /* 6 */ + -7.599592208862305e-06, /* 7 */ + }, { + -3.845453262329102e-03, /* 8 */ + 9.998481273651123e-01, /* 9 */ + 4.027605056762695e-03, /* 10 */ + -3.027915954589844e-05, /* 11 */ + }, { + -5.722850561141968e-03, /* 12 */ + 9.996590912342072e-01, /* 13 */ + 6.131619215011597e-03, /* 14 */ + -6.785988807678223e-05, /* 15 */ + }, { + -7.570266723632812e-03, /* 16 */ + 9.993953704833984e-01, /* 17 */ + 8.295059204101562e-03, /* 18 */ + -1.201629638671875e-04, /* 19 */ + }, { + -9.387880563735962e-03, /* 20 */ + 9.990575015544891e-01, /* 21 */ + 1.051738858222961e-02, /* 22 */ + -1.870095729827881e-04, /* 23 */ + }, { + -1.117587089538574e-02, /* 24 */ + 9.986460208892822e-01, /* 25 */ + 1.279807090759277e-02, /* 26 */ + -2.682209014892578e-04, /* 27 */ + }, { + -1.293441653251648e-02, /* 28 */ + 9.981614649295807e-01, /* 29 */ + 1.513656973838806e-02, /* 30 */ + -3.636181354522705e-04, /* 31 */ + }, { + -1.466369628906250e-02, /* 32 */ + 9.976043701171875e-01, /* 33 */ + 1.753234863281250e-02, /* 34 */ + -4.730224609375000e-04, /* 35 */ + }, { + -1.636388897895813e-02, /* 36 */ + 9.969752728939056e-01, /* 37 */ + 1.998487114906311e-02, /* 38 */ + -5.962550640106201e-04, /* 39 */ + }, { + -1.803517341613770e-02, /* 40 */ + 9.962747097015381e-01, /* 41 */ + 2.249360084533691e-02, /* 42 */ + -7.331371307373047e-04, /* 43 */ + }, { + -1.967772841453552e-02, /* 44 */ + 9.955032169818878e-01, /* 45 */ + 2.505800127983093e-02, /* 46 */ + -8.834898471832275e-04, /* 47 */ + }, { + -2.129173278808594e-02, /* 48 */ + 9.946613311767578e-01, /* 49 */ + 2.767753601074219e-02, /* 50 */ + -1.047134399414062e-03, /* 51 */ + }, { + -2.287736535072327e-02, /* 52 */ + 9.937495887279510e-01, /* 53 */ + 3.035166859626770e-02, /* 54 */ + -1.223891973495483e-03, /* 55 */ + }, { + -2.443480491638184e-02, /* 56 */ + 9.927685260772705e-01, /* 57 */ + 3.307986259460449e-02, /* 58 */ + -1.413583755493164e-03, /* 59 */ + }, { + -2.596423029899597e-02, /* 60 */ + 9.917186796665192e-01, /* 61 */ + 3.586158156394958e-02, /* 62 */ + -1.616030931472778e-03, /* 63 */ + }, { + -2.746582031250000e-02, /* 64 */ + 9.906005859375000e-01, /* 65 */ + 3.869628906250000e-02, /* 66 */ + -1.831054687500000e-03, /* 67 */ + }, { + -2.893975377082825e-02, /* 68 */ + 9.894147813320160e-01, /* 69 */ + 4.158344864845276e-02, /* 70 */ + -2.058476209640503e-03, /* 71 */ + }, { + -3.038620948791504e-02, /* 72 */ + 9.881618022918701e-01, /* 73 */ + 4.452252388000488e-02, /* 74 */ + -2.298116683959961e-03, /* 75 */ + }, { + -3.180536627769470e-02, /* 76 */ + 9.868421852588654e-01, /* 77 */ + 4.751297831535339e-02, /* 78 */ + -2.549797296524048e-03, /* 79 */ + }, { + -3.319740295410156e-02, /* 80 */ + 9.854564666748047e-01, /* 81 */ + 5.055427551269531e-02, /* 82 */ + -2.813339233398438e-03, /* 83 */ + }, { + -3.456249833106995e-02, /* 84 */ + 9.840051829814911e-01, /* 85 */ + 5.364587903022766e-02, /* 86 */ + -3.088563680648804e-03, /* 87 */ + }, { + -3.590083122253418e-02, /* 88 */ + 9.824888706207275e-01, /* 89 */ + 5.678725242614746e-02, /* 90 */ + -3.375291824340820e-03, /* 91 */ + }, { + -3.721258044242859e-02, /* 92 */ + 9.809080660343170e-01, /* 93 */ + 5.997785925865173e-02, /* 94 */ + -3.673344850540161e-03, /* 95 */ + }, { + -3.849792480468750e-02, /* 96 */ + 9.792633056640625e-01, /* 97 */ + 6.321716308593750e-02, /* 98 */ + -3.982543945312500e-03, /* 99 */ + }, { + -3.975704312324524e-02, /* 100 */ + 9.775551259517670e-01, /* 101 */ + 6.650462746620178e-02, /* 102 */ + -4.302710294723511e-03, /* 103 */ + }, { + -4.099011421203613e-02, /* 104 */ + 9.757840633392334e-01, /* 105 */ + 6.983971595764160e-02, /* 106 */ + -4.633665084838867e-03, /* 107 */ + }, { + -4.219731688499451e-02, /* 108 */ + 9.739506542682648e-01, /* 109 */ + 7.322189211845398e-02, /* 110 */ + -4.975229501724243e-03, /* 111 */ + }, { + -4.337882995605469e-02, /* 112 */ + 9.720554351806641e-01, /* 113 */ + 7.665061950683594e-02, /* 114 */ + -5.327224731445312e-03, /* 115 */ + }, { + -4.453483223915100e-02, /* 116 */ + 9.700989425182343e-01, /* 117 */ + 8.012536168098450e-02, /* 118 */ + -5.689471960067749e-03, /* 119 */ + }, { + -4.566550254821777e-02, /* 120 */ + 9.680817127227783e-01, /* 121 */ + 8.364558219909668e-02, /* 122 */ + -6.061792373657227e-03, /* 123 */ + }, { + -4.677101969718933e-02, /* 124 */ + 9.660042822360992e-01, /* 125 */ + 8.721074461936951e-02, /* 126 */ + -6.444007158279419e-03, /* 127 */ + }, { + -4.785156250000000e-02, /* 128 */ + 9.638671875000000e-01, /* 129 */ + 9.082031250000000e-02, /* 130 */ + -6.835937500000000e-03, /* 131 */ + }, { + -4.890730977058411e-02, /* 132 */ + 9.616709649562836e-01, /* 133 */ + 9.447374939918518e-02, /* 134 */ + -7.237404584884644e-03, /* 135 */ + }, { + -4.993844032287598e-02, /* 136 */ + 9.594161510467529e-01, /* 137 */ + 9.817051887512207e-02, /* 138 */ + -7.648229598999023e-03, /* 139 */ + }, { + -5.094513297080994e-02, /* 140 */ + 9.571032822132111e-01, /* 141 */ + 1.019100844860077e-01, /* 142 */ + -8.068233728408813e-03, /* 143 */ + }, { + -5.192756652832031e-02, /* 144 */ + 9.547328948974609e-01, /* 145 */ + 1.056919097900391e-01, /* 146 */ + -8.497238159179688e-03, /* 147 */ + }, { + -5.288591980934143e-02, /* 148 */ + 9.523055255413055e-01, /* 149 */ + 1.095154583454132e-01, /* 150 */ + -8.935064077377319e-03, /* 151 */ + }, { + -5.382037162780762e-02, /* 152 */ + 9.498217105865479e-01, /* 153 */ + 1.133801937103271e-01, /* 154 */ + -9.381532669067383e-03, /* 155 */ + }, { + -5.473110079765320e-02, /* 156 */ + 9.472819864749908e-01, /* 157 */ + 1.172855794429779e-01, /* 158 */ + -9.836465120315552e-03, /* 159 */ + }, { + -5.561828613281250e-02, /* 160 */ + 9.446868896484375e-01, /* 161 */ + 1.212310791015625e-01, /* 162 */ + -1.029968261718750e-02, /* 163 */ + }, { + -5.648210644721985e-02, /* 164 */ + 9.420369565486908e-01, /* 165 */ + 1.252161562442780e-01, /* 166 */ + -1.077100634574890e-02, /* 167 */ + }, { + -5.732274055480957e-02, /* 168 */ + 9.393327236175537e-01, /* 169 */ + 1.292402744293213e-01, /* 170 */ + -1.125025749206543e-02, /* 171 */ + }, { + -5.814036726951599e-02, /* 172 */ + 9.365747272968292e-01, /* 173 */ + 1.333028972148895e-01, /* 174 */ + -1.173725724220276e-02, /* 175 */ + }, { + -5.893516540527344e-02, /* 176 */ + 9.337635040283203e-01, /* 177 */ + 1.374034881591797e-01, /* 178 */ + -1.223182678222656e-02, /* 179 */ + }, { + -5.970731377601624e-02, /* 180 */ + 9.308995902538300e-01, /* 181 */ + 1.415415108203888e-01, /* 182 */ + -1.273378729820251e-02, /* 183 */ + }, { + -6.045699119567871e-02, /* 184 */ + 9.279835224151611e-01, /* 185 */ + 1.457164287567139e-01, /* 186 */ + -1.324295997619629e-02, /* 187 */ + }, { + -6.118437647819519e-02, /* 188 */ + 9.250158369541168e-01, /* 189 */ + 1.499277055263519e-01, /* 190 */ + -1.375916600227356e-02, /* 191 */ + }, { + -6.188964843750000e-02, /* 192 */ + 9.219970703125000e-01, /* 193 */ + 1.541748046875000e-01, /* 194 */ + -1.428222656250000e-02, /* 195 */ + }, { + -6.257298588752747e-02, /* 196 */ + 9.189277589321136e-01, /* 197 */ + 1.584571897983551e-01, /* 198 */ + -1.481196284294128e-02, /* 199 */ + }, { + -6.323456764221191e-02, /* 200 */ + 9.158084392547607e-01, /* 201 */ + 1.627743244171143e-01, /* 202 */ + -1.534819602966309e-02, /* 203 */ + }, { + -6.387457251548767e-02, /* 204 */ + 9.126396477222443e-01, /* 205 */ + 1.671256721019745e-01, /* 206 */ + -1.589074730873108e-02, /* 207 */ + }, { + -6.449317932128906e-02, /* 208 */ + 9.094219207763672e-01, /* 209 */ + 1.715106964111328e-01, /* 210 */ + -1.643943786621094e-02, /* 211 */ + }, { + -6.509056687355042e-02, /* 212 */ + 9.061557948589325e-01, /* 213 */ + 1.759288609027863e-01, /* 214 */ + -1.699408888816833e-02, /* 215 */ + }, { + -6.566691398620605e-02, /* 216 */ + 9.028418064117432e-01, /* 217 */ + 1.803796291351318e-01, /* 218 */ + -1.755452156066895e-02, /* 219 */ + }, { + -6.622239947319031e-02, /* 220 */ + 8.994804918766022e-01, /* 221 */ + 1.848624646663666e-01, /* 222 */ + -1.812055706977844e-02, /* 223 */ + }, { + -6.675720214843750e-02, /* 224 */ + 8.960723876953125e-01, /* 225 */ + 1.893768310546875e-01, /* 226 */ + -1.869201660156250e-02, /* 227 */ + }, { + -6.727150082588196e-02, /* 228 */ + 8.926180303096771e-01, /* 229 */ + 1.939221918582916e-01, /* 230 */ + -1.926872134208679e-02, /* 231 */ + }, { + -6.776547431945801e-02, /* 232 */ + 8.891179561614990e-01, /* 233 */ + 1.984980106353760e-01, /* 234 */ + -1.985049247741699e-02, /* 235 */ + }, { + -6.823930144309998e-02, /* 236 */ + 8.855727016925812e-01, /* 237 */ + 2.031037509441376e-01, /* 238 */ + -2.043715119361877e-02, /* 239 */ + }, { + -6.869316101074219e-02, /* 240 */ + 8.819828033447266e-01, /* 241 */ + 2.077388763427734e-01, /* 242 */ + -2.102851867675781e-02, /* 243 */ + }, { + -6.912723183631897e-02, /* 244 */ + 8.783487975597382e-01, /* 245 */ + 2.124028503894806e-01, /* 246 */ + -2.162441611289978e-02, /* 247 */ + }, { + -6.954169273376465e-02, /* 248 */ + 8.746712207794189e-01, /* 249 */ + 2.170951366424561e-01, /* 250 */ + -2.222466468811035e-02, /* 251 */ + }, { + -6.993672251701355e-02, /* 252 */ + 8.709506094455719e-01, /* 253 */ + 2.218151986598969e-01, /* 254 */ + -2.282908558845520e-02, /* 255 */ + }, { + -7.031250000000000e-02, /* 256 */ + 8.671875000000000e-01, /* 257 */ + 2.265625000000000e-01, /* 258 */ + -2.343750000000000e-02, /* 259 */ + }, { + -7.066920399665833e-02, /* 260 */ + 8.633824288845062e-01, /* 261 */ + 2.313365042209625e-01, /* 262 */ + -2.404972910881042e-02, /* 263 */ + }, { + -7.100701332092285e-02, /* 264 */ + 8.595359325408936e-01, /* 265 */ + 2.361366748809814e-01, /* 266 */ + -2.466559410095215e-02, /* 267 */ + }, { + -7.132610678672791e-02, /* 268 */ + 8.556485474109650e-01, /* 269 */ + 2.409624755382538e-01, /* 270 */ + -2.528491616249084e-02, /* 271 */ + }, { + -7.162666320800781e-02, /* 272 */ + 8.517208099365234e-01, /* 273 */ + 2.458133697509766e-01, /* 274 */ + -2.590751647949219e-02, /* 275 */ + }, { + -7.190886139869690e-02, /* 276 */ + 8.477532565593719e-01, /* 277 */ + 2.506888210773468e-01, /* 278 */ + -2.653321623802185e-02, /* 279 */ + }, { + -7.217288017272949e-02, /* 280 */ + 8.437464237213135e-01, /* 281 */ + 2.555882930755615e-01, /* 282 */ + -2.716183662414551e-02, /* 283 */ + }, { + -7.241889834403992e-02, /* 284 */ + 8.397008478641510e-01, /* 285 */ + 2.605112493038177e-01, /* 286 */ + -2.779319882392883e-02, /* 287 */ + }, { + -7.264709472656250e-02, /* 288 */ + 8.356170654296875e-01, /* 289 */ + 2.654571533203125e-01, /* 290 */ + -2.842712402343750e-02, /* 291 */ + }, { + -7.285764813423157e-02, /* 292 */ + 8.314956128597260e-01, /* 293 */ + 2.704254686832428e-01, /* 294 */ + -2.906343340873718e-02, /* 295 */ + }, { + -7.305073738098145e-02, /* 296 */ + 8.273370265960693e-01, /* 297 */ + 2.754156589508057e-01, /* 298 */ + -2.970194816589355e-02, /* 299 */ + }, { + -7.322654128074646e-02, /* 300 */ + 8.231418430805206e-01, /* 301 */ + 2.804271876811981e-01, /* 302 */ + -3.034248948097229e-02, /* 303 */ + }, { + -7.338523864746094e-02, /* 304 */ + 8.189105987548828e-01, /* 305 */ + 2.854595184326172e-01, /* 306 */ + -3.098487854003906e-02, /* 307 */ + }, { + -7.352700829505920e-02, /* 308 */ + 8.146438300609589e-01, /* 309 */ + 2.905121147632599e-01, /* 310 */ + -3.162893652915955e-02, /* 311 */ + }, { + -7.365202903747559e-02, /* 312 */ + 8.103420734405518e-01, /* 313 */ + 2.955844402313232e-01, /* 314 */ + -3.227448463439941e-02, /* 315 */ + }, { + -7.376047968864441e-02, /* 316 */ + 8.060058653354645e-01, /* 317 */ + 3.006759583950043e-01, /* 318 */ + -3.292134404182434e-02, /* 319 */ + }, { + -7.385253906250000e-02, /* 320 */ + 8.016357421875000e-01, /* 321 */ + 3.057861328125000e-01, /* 322 */ + -3.356933593750000e-02, /* 323 */ + }, { + -7.392838597297668e-02, /* 324 */ + 7.972322404384613e-01, /* 325 */ + 3.109144270420074e-01, /* 326 */ + -3.421828150749207e-02, /* 327 */ + }, { + -7.398819923400879e-02, /* 328 */ + 7.927958965301514e-01, /* 329 */ + 3.160603046417236e-01, /* 330 */ + -3.486800193786621e-02, /* 331 */ + }, { + -7.403215765953064e-02, /* 332 */ + 7.883272469043732e-01, /* 333 */ + 3.212232291698456e-01, /* 334 */ + -3.551831841468811e-02, /* 335 */ + }, { + -7.406044006347656e-02, /* 336 */ + 7.838268280029297e-01, /* 337 */ + 3.264026641845703e-01, /* 338 */ + -3.616905212402344e-02, /* 339 */ + }, { + -7.407322525978088e-02, /* 340 */ + 7.792951762676239e-01, /* 341 */ + 3.315980732440948e-01, /* 342 */ + -3.682002425193787e-02, /* 343 */ + }, { + -7.407069206237793e-02, /* 344 */ + 7.747328281402588e-01, /* 345 */ + 3.368089199066162e-01, /* 346 */ + -3.747105598449707e-02, /* 347 */ + }, { + -7.405301928520203e-02, /* 348 */ + 7.701403200626373e-01, /* 349 */ + 3.420346677303314e-01, /* 350 */ + -3.812196850776672e-02, /* 351 */ + }, { + -7.402038574218750e-02, /* 352 */ + 7.655181884765625e-01, /* 353 */ + 3.472747802734375e-01, /* 354 */ + -3.877258300781250e-02, /* 355 */ + }, { + -7.397297024726868e-02, /* 356 */ + 7.608669698238373e-01, /* 357 */ + 3.525287210941315e-01, /* 358 */ + -3.942272067070007e-02, /* 359 */ + }, { + -7.391095161437988e-02, /* 360 */ + 7.561872005462646e-01, /* 361 */ + 3.577959537506104e-01, /* 362 */ + -4.007220268249512e-02, /* 363 */ + }, { + -7.383450865745544e-02, /* 364 */ + 7.514794170856476e-01, /* 365 */ + 3.630759418010712e-01, /* 366 */ + -4.072085022926331e-02, /* 367 */ + }, { + -7.374382019042969e-02, /* 368 */ + 7.467441558837891e-01, /* 369 */ + 3.683681488037109e-01, /* 370 */ + -4.136848449707031e-02, /* 371 */ + }, { + -7.363906502723694e-02, /* 372 */ + 7.419819533824921e-01, /* 373 */ + 3.736720383167267e-01, /* 374 */ + -4.201492667198181e-02, /* 375 */ + }, { + -7.352042198181152e-02, /* 376 */ + 7.371933460235596e-01, /* 377 */ + 3.789870738983154e-01, /* 378 */ + -4.265999794006348e-02, /* 379 */ + }, { + -7.338806986808777e-02, /* 380 */ + 7.323788702487946e-01, /* 381 */ + 3.843127191066742e-01, /* 382 */ + -4.330351948738098e-02, /* 383 */ + }, { + -7.324218750000000e-02, /* 384 */ + 7.275390625000000e-01, /* 385 */ + 3.896484375000000e-01, /* 386 */ + -4.394531250000000e-02, /* 387 */ + }, { + -7.308295369148254e-02, /* 388 */ + 7.226744592189789e-01, /* 389 */ + 3.949936926364899e-01, /* 390 */ + -4.458519816398621e-02, /* 391 */ + }, { + -7.291054725646973e-02, /* 392 */ + 7.177855968475342e-01, /* 393 */ + 4.003479480743408e-01, /* 394 */ + -4.522299766540527e-02, /* 395 */ + }, { + -7.272514700889587e-02, /* 396 */ + 7.128730118274689e-01, /* 397 */ + 4.057106673717499e-01, /* 398 */ + -4.585853219032288e-02, /* 399 */ + }, { + -7.252693176269531e-02, /* 400 */ + 7.079372406005859e-01, /* 401 */ + 4.110813140869141e-01, /* 402 */ + -4.649162292480469e-02, /* 403 */ + }, { + -7.231608033180237e-02, /* 404 */ + 7.029788196086884e-01, /* 405 */ + 4.164593517780304e-01, /* 406 */ + -4.712209105491638e-02, /* 407 */ + }, { + -7.209277153015137e-02, /* 408 */ + 6.979982852935791e-01, /* 409 */ + 4.218442440032959e-01, /* 410 */ + -4.774975776672363e-02, /* 411 */ + }, { + -7.185718417167664e-02, /* 412 */ + 6.929961740970612e-01, /* 413 */ + 4.272354543209076e-01, /* 414 */ + -4.837444424629211e-02, /* 415 */ + }, { + -7.160949707031250e-02, /* 416 */ + 6.879730224609375e-01, /* 417 */ + 4.326324462890625e-01, /* 418 */ + -4.899597167968750e-02, /* 419 */ + }, { + -7.134988903999329e-02, /* 420 */ + 6.829293668270111e-01, /* 421 */ + 4.380346834659576e-01, /* 422 */ + -4.961416125297546e-02, /* 423 */ + }, { + -7.107853889465332e-02, /* 424 */ + 6.778657436370850e-01, /* 425 */ + 4.434416294097900e-01, /* 426 */ + -5.022883415222168e-02, /* 427 */ + }, { + -7.079562544822693e-02, /* 428 */ + 6.727826893329620e-01, /* 429 */ + 4.488527476787567e-01, /* 430 */ + -5.083981156349182e-02, /* 431 */ + }, { + -7.050132751464844e-02, /* 432 */ + 6.676807403564453e-01, /* 433 */ + 4.542675018310547e-01, /* 434 */ + -5.144691467285156e-02, /* 435 */ + }, { + -7.019582390785217e-02, /* 436 */ + 6.625604331493378e-01, /* 437 */ + 4.596853554248810e-01, /* 438 */ + -5.204996466636658e-02, /* 439 */ + }, { + -6.987929344177246e-02, /* 440 */ + 6.574223041534424e-01, /* 441 */ + 4.651057720184326e-01, /* 442 */ + -5.264878273010254e-02, /* 443 */ + }, { + -6.955191493034363e-02, /* 444 */ + 6.522668898105621e-01, /* 445 */ + 4.705282151699066e-01, /* 446 */ + -5.324319005012512e-02, /* 447 */ + }, { + -6.921386718750000e-02, /* 448 */ + 6.470947265625000e-01, /* 449 */ + 4.759521484375000e-01, /* 450 */ + -5.383300781250000e-02, /* 451 */ + }, { + -6.886532902717590e-02, /* 452 */ + 6.419063508510590e-01, /* 453 */ + 4.813770353794098e-01, /* 454 */ + -5.441805720329285e-02, /* 455 */ + }, { + -6.850647926330566e-02, /* 456 */ + 6.367022991180420e-01, /* 457 */ + 4.868023395538330e-01, /* 458 */ + -5.499815940856934e-02, /* 459 */ + }, { + -6.813749670982361e-02, /* 460 */ + 6.314831078052521e-01, /* 461 */ + 4.922275245189667e-01, /* 462 */ + -5.557313561439514e-02, /* 463 */ + }, { + -6.775856018066406e-02, /* 464 */ + 6.262493133544922e-01, /* 465 */ + 4.976520538330078e-01, /* 466 */ + -5.614280700683594e-02, /* 467 */ + }, { + -6.736984848976135e-02, /* 468 */ + 6.210014522075653e-01, /* 469 */ + 5.030753910541534e-01, /* 470 */ + -5.670699477195740e-02, /* 471 */ + }, { + -6.697154045104980e-02, /* 472 */ + 6.157400608062744e-01, /* 473 */ + 5.084969997406006e-01, /* 474 */ + -5.726552009582520e-02, /* 475 */ + }, { + -6.656381487846375e-02, /* 476 */ + 6.104656755924225e-01, /* 477 */ + 5.139163434505463e-01, /* 478 */ + -5.781820416450500e-02, /* 479 */ + }, { + -6.614685058593750e-02, /* 480 */ + 6.051788330078125e-01, /* 481 */ + 5.193328857421875e-01, /* 482 */ + -5.836486816406250e-02, /* 483 */ + }, { + -6.572082638740540e-02, /* 484 */ + 5.998800694942474e-01, /* 485 */ + 5.247460901737213e-01, /* 486 */ + -5.890533328056335e-02, /* 487 */ + }, { + -6.528592109680176e-02, /* 488 */ + 5.945699214935303e-01, /* 489 */ + 5.301554203033447e-01, /* 490 */ + -5.943942070007324e-02, /* 491 */ + }, { + -6.484231352806091e-02, /* 492 */ + 5.892489254474640e-01, /* 493 */ + 5.355603396892548e-01, /* 494 */ + -5.996695160865784e-02, /* 495 */ + }, { + -6.439018249511719e-02, /* 496 */ + 5.839176177978516e-01, /* 497 */ + 5.409603118896484e-01, /* 498 */ + -6.048774719238281e-02, /* 499 */ + }, { + -6.392970681190491e-02, /* 500 */ + 5.785765349864960e-01, /* 501 */ + 5.463548004627228e-01, /* 502 */ + -6.100162863731384e-02, /* 503 */ + }, { + -6.346106529235840e-02, /* 504 */ + 5.732262134552002e-01, /* 505 */ + 5.517432689666748e-01, /* 506 */ + -6.150841712951660e-02, /* 507 */ + }, { + -6.298443675041199e-02, /* 508 */ + 5.678671896457672e-01, /* 509 */ + 5.571251809597015e-01, /* 510 */ + -6.200793385505676e-02, /* 511 */ + }, { + -6.250000000000000e-02, /* 512 */ + 5.625000000000000e-01, /* 513 */ + 5.625000000000000e-01, /* 514 */ + -6.250000000000000e-02, /* 515 */ + }, { + -6.200793385505676e-02, /* 516 */ + 5.571251809597015e-01, /* 517 */ + 5.678671896457672e-01, /* 518 */ + -6.298443675041199e-02, /* 519 */ + }, { + -6.150841712951660e-02, /* 520 */ + 5.517432689666748e-01, /* 521 */ + 5.732262134552002e-01, /* 522 */ + -6.346106529235840e-02, /* 523 */ + }, { + -6.100162863731384e-02, /* 524 */ + 5.463548004627228e-01, /* 525 */ + 5.785765349864960e-01, /* 526 */ + -6.392970681190491e-02, /* 527 */ + }, { + -6.048774719238281e-02, /* 528 */ + 5.409603118896484e-01, /* 529 */ + 5.839176177978516e-01, /* 530 */ + -6.439018249511719e-02, /* 531 */ + }, { + -5.996695160865784e-02, /* 532 */ + 5.355603396892548e-01, /* 533 */ + 5.892489254474640e-01, /* 534 */ + -6.484231352806091e-02, /* 535 */ + }, { + -5.943942070007324e-02, /* 536 */ + 5.301554203033447e-01, /* 537 */ + 5.945699214935303e-01, /* 538 */ + -6.528592109680176e-02, /* 539 */ + }, { + -5.890533328056335e-02, /* 540 */ + 5.247460901737213e-01, /* 541 */ + 5.998800694942474e-01, /* 542 */ + -6.572082638740540e-02, /* 543 */ + }, { + -5.836486816406250e-02, /* 544 */ + 5.193328857421875e-01, /* 545 */ + 6.051788330078125e-01, /* 546 */ + -6.614685058593750e-02, /* 547 */ + }, { + -5.781820416450500e-02, /* 548 */ + 5.139163434505463e-01, /* 549 */ + 6.104656755924225e-01, /* 550 */ + -6.656381487846375e-02, /* 551 */ + }, { + -5.726552009582520e-02, /* 552 */ + 5.084969997406006e-01, /* 553 */ + 6.157400608062744e-01, /* 554 */ + -6.697154045104980e-02, /* 555 */ + }, { + -5.670699477195740e-02, /* 556 */ + 5.030753910541534e-01, /* 557 */ + 6.210014522075653e-01, /* 558 */ + -6.736984848976135e-02, /* 559 */ + }, { + -5.614280700683594e-02, /* 560 */ + 4.976520538330078e-01, /* 561 */ + 6.262493133544922e-01, /* 562 */ + -6.775856018066406e-02, /* 563 */ + }, { + -5.557313561439514e-02, /* 564 */ + 4.922275245189667e-01, /* 565 */ + 6.314831078052521e-01, /* 566 */ + -6.813749670982361e-02, /* 567 */ + }, { + -5.499815940856934e-02, /* 568 */ + 4.868023395538330e-01, /* 569 */ + 6.367022991180420e-01, /* 570 */ + -6.850647926330566e-02, /* 571 */ + }, { + -5.441805720329285e-02, /* 572 */ + 4.813770353794098e-01, /* 573 */ + 6.419063508510590e-01, /* 574 */ + -6.886532902717590e-02, /* 575 */ + }, { + -5.383300781250000e-02, /* 576 */ + 4.759521484375000e-01, /* 577 */ + 6.470947265625000e-01, /* 578 */ + -6.921386718750000e-02, /* 579 */ + }, { + -5.324319005012512e-02, /* 580 */ + 4.705282151699066e-01, /* 581 */ + 6.522668898105621e-01, /* 582 */ + -6.955191493034363e-02, /* 583 */ + }, { + -5.264878273010254e-02, /* 584 */ + 4.651057720184326e-01, /* 585 */ + 6.574223041534424e-01, /* 586 */ + -6.987929344177246e-02, /* 587 */ + }, { + -5.204996466636658e-02, /* 588 */ + 4.596853554248810e-01, /* 589 */ + 6.625604331493378e-01, /* 590 */ + -7.019582390785217e-02, /* 591 */ + }, { + -5.144691467285156e-02, /* 592 */ + 4.542675018310547e-01, /* 593 */ + 6.676807403564453e-01, /* 594 */ + -7.050132751464844e-02, /* 595 */ + }, { + -5.083981156349182e-02, /* 596 */ + 4.488527476787567e-01, /* 597 */ + 6.727826893329620e-01, /* 598 */ + -7.079562544822693e-02, /* 599 */ + }, { + -5.022883415222168e-02, /* 600 */ + 4.434416294097900e-01, /* 601 */ + 6.778657436370850e-01, /* 602 */ + -7.107853889465332e-02, /* 603 */ + }, { + -4.961416125297546e-02, /* 604 */ + 4.380346834659576e-01, /* 605 */ + 6.829293668270111e-01, /* 606 */ + -7.134988903999329e-02, /* 607 */ + }, { + -4.899597167968750e-02, /* 608 */ + 4.326324462890625e-01, /* 609 */ + 6.879730224609375e-01, /* 610 */ + -7.160949707031250e-02, /* 611 */ + }, { + -4.837444424629211e-02, /* 612 */ + 4.272354543209076e-01, /* 613 */ + 6.929961740970612e-01, /* 614 */ + -7.185718417167664e-02, /* 615 */ + }, { + -4.774975776672363e-02, /* 616 */ + 4.218442440032959e-01, /* 617 */ + 6.979982852935791e-01, /* 618 */ + -7.209277153015137e-02, /* 619 */ + }, { + -4.712209105491638e-02, /* 620 */ + 4.164593517780304e-01, /* 621 */ + 7.029788196086884e-01, /* 622 */ + -7.231608033180237e-02, /* 623 */ + }, { + -4.649162292480469e-02, /* 624 */ + 4.110813140869141e-01, /* 625 */ + 7.079372406005859e-01, /* 626 */ + -7.252693176269531e-02, /* 627 */ + }, { + -4.585853219032288e-02, /* 628 */ + 4.057106673717499e-01, /* 629 */ + 7.128730118274689e-01, /* 630 */ + -7.272514700889587e-02, /* 631 */ + }, { + -4.522299766540527e-02, /* 632 */ + 4.003479480743408e-01, /* 633 */ + 7.177855968475342e-01, /* 634 */ + -7.291054725646973e-02, /* 635 */ + }, { + -4.458519816398621e-02, /* 636 */ + 3.949936926364899e-01, /* 637 */ + 7.226744592189789e-01, /* 638 */ + -7.308295369148254e-02, /* 639 */ + }, { + -4.394531250000000e-02, /* 640 */ + 3.896484375000000e-01, /* 641 */ + 7.275390625000000e-01, /* 642 */ + -7.324218750000000e-02, /* 643 */ + }, { + -4.330351948738098e-02, /* 644 */ + 3.843127191066742e-01, /* 645 */ + 7.323788702487946e-01, /* 646 */ + -7.338806986808777e-02, /* 647 */ + }, { + -4.265999794006348e-02, /* 648 */ + 3.789870738983154e-01, /* 649 */ + 7.371933460235596e-01, /* 650 */ + -7.352042198181152e-02, /* 651 */ + }, { + -4.201492667198181e-02, /* 652 */ + 3.736720383167267e-01, /* 653 */ + 7.419819533824921e-01, /* 654 */ + -7.363906502723694e-02, /* 655 */ + }, { + -4.136848449707031e-02, /* 656 */ + 3.683681488037109e-01, /* 657 */ + 7.467441558837891e-01, /* 658 */ + -7.374382019042969e-02, /* 659 */ + }, { + -4.072085022926331e-02, /* 660 */ + 3.630759418010712e-01, /* 661 */ + 7.514794170856476e-01, /* 662 */ + -7.383450865745544e-02, /* 663 */ + }, { + -4.007220268249512e-02, /* 664 */ + 3.577959537506104e-01, /* 665 */ + 7.561872005462646e-01, /* 666 */ + -7.391095161437988e-02, /* 667 */ + }, { + -3.942272067070007e-02, /* 668 */ + 3.525287210941315e-01, /* 669 */ + 7.608669698238373e-01, /* 670 */ + -7.397297024726868e-02, /* 671 */ + }, { + -3.877258300781250e-02, /* 672 */ + 3.472747802734375e-01, /* 673 */ + 7.655181884765625e-01, /* 674 */ + -7.402038574218750e-02, /* 675 */ + }, { + -3.812196850776672e-02, /* 676 */ + 3.420346677303314e-01, /* 677 */ + 7.701403200626373e-01, /* 678 */ + -7.405301928520203e-02, /* 679 */ + }, { + -3.747105598449707e-02, /* 680 */ + 3.368089199066162e-01, /* 681 */ + 7.747328281402588e-01, /* 682 */ + -7.407069206237793e-02, /* 683 */ + }, { + -3.682002425193787e-02, /* 684 */ + 3.315980732440948e-01, /* 685 */ + 7.792951762676239e-01, /* 686 */ + -7.407322525978088e-02, /* 687 */ + }, { + -3.616905212402344e-02, /* 688 */ + 3.264026641845703e-01, /* 689 */ + 7.838268280029297e-01, /* 690 */ + -7.406044006347656e-02, /* 691 */ + }, { + -3.551831841468811e-02, /* 692 */ + 3.212232291698456e-01, /* 693 */ + 7.883272469043732e-01, /* 694 */ + -7.403215765953064e-02, /* 695 */ + }, { + -3.486800193786621e-02, /* 696 */ + 3.160603046417236e-01, /* 697 */ + 7.927958965301514e-01, /* 698 */ + -7.398819923400879e-02, /* 699 */ + }, { + -3.421828150749207e-02, /* 700 */ + 3.109144270420074e-01, /* 701 */ + 7.972322404384613e-01, /* 702 */ + -7.392838597297668e-02, /* 703 */ + }, { + -3.356933593750000e-02, /* 704 */ + 3.057861328125000e-01, /* 705 */ + 8.016357421875000e-01, /* 706 */ + -7.385253906250000e-02, /* 707 */ + }, { + -3.292134404182434e-02, /* 708 */ + 3.006759583950043e-01, /* 709 */ + 8.060058653354645e-01, /* 710 */ + -7.376047968864441e-02, /* 711 */ + }, { + -3.227448463439941e-02, /* 712 */ + 2.955844402313232e-01, /* 713 */ + 8.103420734405518e-01, /* 714 */ + -7.365202903747559e-02, /* 715 */ + }, { + -3.162893652915955e-02, /* 716 */ + 2.905121147632599e-01, /* 717 */ + 8.146438300609589e-01, /* 718 */ + -7.352700829505920e-02, /* 719 */ + }, { + -3.098487854003906e-02, /* 720 */ + 2.854595184326172e-01, /* 721 */ + 8.189105987548828e-01, /* 722 */ + -7.338523864746094e-02, /* 723 */ + }, { + -3.034248948097229e-02, /* 724 */ + 2.804271876811981e-01, /* 725 */ + 8.231418430805206e-01, /* 726 */ + -7.322654128074646e-02, /* 727 */ + }, { + -2.970194816589355e-02, /* 728 */ + 2.754156589508057e-01, /* 729 */ + 8.273370265960693e-01, /* 730 */ + -7.305073738098145e-02, /* 731 */ + }, { + -2.906343340873718e-02, /* 732 */ + 2.704254686832428e-01, /* 733 */ + 8.314956128597260e-01, /* 734 */ + -7.285764813423157e-02, /* 735 */ + }, { + -2.842712402343750e-02, /* 736 */ + 2.654571533203125e-01, /* 737 */ + 8.356170654296875e-01, /* 738 */ + -7.264709472656250e-02, /* 739 */ + }, { + -2.779319882392883e-02, /* 740 */ + 2.605112493038177e-01, /* 741 */ + 8.397008478641510e-01, /* 742 */ + -7.241889834403992e-02, /* 743 */ + }, { + -2.716183662414551e-02, /* 744 */ + 2.555882930755615e-01, /* 745 */ + 8.437464237213135e-01, /* 746 */ + -7.217288017272949e-02, /* 747 */ + }, { + -2.653321623802185e-02, /* 748 */ + 2.506888210773468e-01, /* 749 */ + 8.477532565593719e-01, /* 750 */ + -7.190886139869690e-02, /* 751 */ + }, { + -2.590751647949219e-02, /* 752 */ + 2.458133697509766e-01, /* 753 */ + 8.517208099365234e-01, /* 754 */ + -7.162666320800781e-02, /* 755 */ + }, { + -2.528491616249084e-02, /* 756 */ + 2.409624755382538e-01, /* 757 */ + 8.556485474109650e-01, /* 758 */ + -7.132610678672791e-02, /* 759 */ + }, { + -2.466559410095215e-02, /* 760 */ + 2.361366748809814e-01, /* 761 */ + 8.595359325408936e-01, /* 762 */ + -7.100701332092285e-02, /* 763 */ + }, { + -2.404972910881042e-02, /* 764 */ + 2.313365042209625e-01, /* 765 */ + 8.633824288845062e-01, /* 766 */ + -7.066920399665833e-02, /* 767 */ + }, { + -2.343750000000000e-02, /* 768 */ + 2.265625000000000e-01, /* 769 */ + 8.671875000000000e-01, /* 770 */ + -7.031250000000000e-02, /* 771 */ + }, { + -2.282908558845520e-02, /* 772 */ + 2.218151986598969e-01, /* 773 */ + 8.709506094455719e-01, /* 774 */ + -6.993672251701355e-02, /* 775 */ + }, { + -2.222466468811035e-02, /* 776 */ + 2.170951366424561e-01, /* 777 */ + 8.746712207794189e-01, /* 778 */ + -6.954169273376465e-02, /* 779 */ + }, { + -2.162441611289978e-02, /* 780 */ + 2.124028503894806e-01, /* 781 */ + 8.783487975597382e-01, /* 782 */ + -6.912723183631897e-02, /* 783 */ + }, { + -2.102851867675781e-02, /* 784 */ + 2.077388763427734e-01, /* 785 */ + 8.819828033447266e-01, /* 786 */ + -6.869316101074219e-02, /* 787 */ + }, { + -2.043715119361877e-02, /* 788 */ + 2.031037509441376e-01, /* 789 */ + 8.855727016925812e-01, /* 790 */ + -6.823930144309998e-02, /* 791 */ + }, { + -1.985049247741699e-02, /* 792 */ + 1.984980106353760e-01, /* 793 */ + 8.891179561614990e-01, /* 794 */ + -6.776547431945801e-02, /* 795 */ + }, { + -1.926872134208679e-02, /* 796 */ + 1.939221918582916e-01, /* 797 */ + 8.926180303096771e-01, /* 798 */ + -6.727150082588196e-02, /* 799 */ + }, { + -1.869201660156250e-02, /* 800 */ + 1.893768310546875e-01, /* 801 */ + 8.960723876953125e-01, /* 802 */ + -6.675720214843750e-02, /* 803 */ + }, { + -1.812055706977844e-02, /* 804 */ + 1.848624646663666e-01, /* 805 */ + 8.994804918766022e-01, /* 806 */ + -6.622239947319031e-02, /* 807 */ + }, { + -1.755452156066895e-02, /* 808 */ + 1.803796291351318e-01, /* 809 */ + 9.028418064117432e-01, /* 810 */ + -6.566691398620605e-02, /* 811 */ + }, { + -1.699408888816833e-02, /* 812 */ + 1.759288609027863e-01, /* 813 */ + 9.061557948589325e-01, /* 814 */ + -6.509056687355042e-02, /* 815 */ + }, { + -1.643943786621094e-02, /* 816 */ + 1.715106964111328e-01, /* 817 */ + 9.094219207763672e-01, /* 818 */ + -6.449317932128906e-02, /* 819 */ + }, { + -1.589074730873108e-02, /* 820 */ + 1.671256721019745e-01, /* 821 */ + 9.126396477222443e-01, /* 822 */ + -6.387457251548767e-02, /* 823 */ + }, { + -1.534819602966309e-02, /* 824 */ + 1.627743244171143e-01, /* 825 */ + 9.158084392547607e-01, /* 826 */ + -6.323456764221191e-02, /* 827 */ + }, { + -1.481196284294128e-02, /* 828 */ + 1.584571897983551e-01, /* 829 */ + 9.189277589321136e-01, /* 830 */ + -6.257298588752747e-02, /* 831 */ + }, { + -1.428222656250000e-02, /* 832 */ + 1.541748046875000e-01, /* 833 */ + 9.219970703125000e-01, /* 834 */ + -6.188964843750000e-02, /* 835 */ + }, { + -1.375916600227356e-02, /* 836 */ + 1.499277055263519e-01, /* 837 */ + 9.250158369541168e-01, /* 838 */ + -6.118437647819519e-02, /* 839 */ + }, { + -1.324295997619629e-02, /* 840 */ + 1.457164287567139e-01, /* 841 */ + 9.279835224151611e-01, /* 842 */ + -6.045699119567871e-02, /* 843 */ + }, { + -1.273378729820251e-02, /* 844 */ + 1.415415108203888e-01, /* 845 */ + 9.308995902538300e-01, /* 846 */ + -5.970731377601624e-02, /* 847 */ + }, { + -1.223182678222656e-02, /* 848 */ + 1.374034881591797e-01, /* 849 */ + 9.337635040283203e-01, /* 850 */ + -5.893516540527344e-02, /* 851 */ + }, { + -1.173725724220276e-02, /* 852 */ + 1.333028972148895e-01, /* 853 */ + 9.365747272968292e-01, /* 854 */ + -5.814036726951599e-02, /* 855 */ + }, { + -1.125025749206543e-02, /* 856 */ + 1.292402744293213e-01, /* 857 */ + 9.393327236175537e-01, /* 858 */ + -5.732274055480957e-02, /* 859 */ + }, { + -1.077100634574890e-02, /* 860 */ + 1.252161562442780e-01, /* 861 */ + 9.420369565486908e-01, /* 862 */ + -5.648210644721985e-02, /* 863 */ + }, { + -1.029968261718750e-02, /* 864 */ + 1.212310791015625e-01, /* 865 */ + 9.446868896484375e-01, /* 866 */ + -5.561828613281250e-02, /* 867 */ + }, { + -9.836465120315552e-03, /* 868 */ + 1.172855794429779e-01, /* 869 */ + 9.472819864749908e-01, /* 870 */ + -5.473110079765320e-02, /* 871 */ + }, { + -9.381532669067383e-03, /* 872 */ + 1.133801937103271e-01, /* 873 */ + 9.498217105865479e-01, /* 874 */ + -5.382037162780762e-02, /* 875 */ + }, { + -8.935064077377319e-03, /* 876 */ + 1.095154583454132e-01, /* 877 */ + 9.523055255413055e-01, /* 878 */ + -5.288591980934143e-02, /* 879 */ + }, { + -8.497238159179688e-03, /* 880 */ + 1.056919097900391e-01, /* 881 */ + 9.547328948974609e-01, /* 882 */ + -5.192756652832031e-02, /* 883 */ + }, { + -8.068233728408813e-03, /* 884 */ + 1.019100844860077e-01, /* 885 */ + 9.571032822132111e-01, /* 886 */ + -5.094513297080994e-02, /* 887 */ + }, { + -7.648229598999023e-03, /* 888 */ + 9.817051887512207e-02, /* 889 */ + 9.594161510467529e-01, /* 890 */ + -4.993844032287598e-02, /* 891 */ + }, { + -7.237404584884644e-03, /* 892 */ + 9.447374939918518e-02, /* 893 */ + 9.616709649562836e-01, /* 894 */ + -4.890730977058411e-02, /* 895 */ + }, { + -6.835937500000000e-03, /* 896 */ + 9.082031250000000e-02, /* 897 */ + 9.638671875000000e-01, /* 898 */ + -4.785156250000000e-02, /* 899 */ + }, { + -6.444007158279419e-03, /* 900 */ + 8.721074461936951e-02, /* 901 */ + 9.660042822360992e-01, /* 902 */ + -4.677101969718933e-02, /* 903 */ + }, { + -6.061792373657227e-03, /* 904 */ + 8.364558219909668e-02, /* 905 */ + 9.680817127227783e-01, /* 906 */ + -4.566550254821777e-02, /* 907 */ + }, { + -5.689471960067749e-03, /* 908 */ + 8.012536168098450e-02, /* 909 */ + 9.700989425182343e-01, /* 910 */ + -4.453483223915100e-02, /* 911 */ + }, { + -5.327224731445312e-03, /* 912 */ + 7.665061950683594e-02, /* 913 */ + 9.720554351806641e-01, /* 914 */ + -4.337882995605469e-02, /* 915 */ + }, { + -4.975229501724243e-03, /* 916 */ + 7.322189211845398e-02, /* 917 */ + 9.739506542682648e-01, /* 918 */ + -4.219731688499451e-02, /* 919 */ + }, { + -4.633665084838867e-03, /* 920 */ + 6.983971595764160e-02, /* 921 */ + 9.757840633392334e-01, /* 922 */ + -4.099011421203613e-02, /* 923 */ + }, { + -4.302710294723511e-03, /* 924 */ + 6.650462746620178e-02, /* 925 */ + 9.775551259517670e-01, /* 926 */ + -3.975704312324524e-02, /* 927 */ + }, { + -3.982543945312500e-03, /* 928 */ + 6.321716308593750e-02, /* 929 */ + 9.792633056640625e-01, /* 930 */ + -3.849792480468750e-02, /* 931 */ + }, { + -3.673344850540161e-03, /* 932 */ + 5.997785925865173e-02, /* 933 */ + 9.809080660343170e-01, /* 934 */ + -3.721258044242859e-02, /* 935 */ + }, { + -3.375291824340820e-03, /* 936 */ + 5.678725242614746e-02, /* 937 */ + 9.824888706207275e-01, /* 938 */ + -3.590083122253418e-02, /* 939 */ + }, { + -3.088563680648804e-03, /* 940 */ + 5.364587903022766e-02, /* 941 */ + 9.840051829814911e-01, /* 942 */ + -3.456249833106995e-02, /* 943 */ + }, { + -2.813339233398438e-03, /* 944 */ + 5.055427551269531e-02, /* 945 */ + 9.854564666748047e-01, /* 946 */ + -3.319740295410156e-02, /* 947 */ + }, { + -2.549797296524048e-03, /* 948 */ + 4.751297831535339e-02, /* 949 */ + 9.868421852588654e-01, /* 950 */ + -3.180536627769470e-02, /* 951 */ + }, { + -2.298116683959961e-03, /* 952 */ + 4.452252388000488e-02, /* 953 */ + 9.881618022918701e-01, /* 954 */ + -3.038620948791504e-02, /* 955 */ + }, { + -2.058476209640503e-03, /* 956 */ + 4.158344864845276e-02, /* 957 */ + 9.894147813320160e-01, /* 958 */ + -2.893975377082825e-02, /* 959 */ + }, { + -1.831054687500000e-03, /* 960 */ + 3.869628906250000e-02, /* 961 */ + 9.906005859375000e-01, /* 962 */ + -2.746582031250000e-02, /* 963 */ + }, { + -1.616030931472778e-03, /* 964 */ + 3.586158156394958e-02, /* 965 */ + 9.917186796665192e-01, /* 966 */ + -2.596423029899597e-02, /* 967 */ + }, { + -1.413583755493164e-03, /* 968 */ + 3.307986259460449e-02, /* 969 */ + 9.927685260772705e-01, /* 970 */ + -2.443480491638184e-02, /* 971 */ + }, { + -1.223891973495483e-03, /* 972 */ + 3.035166859626770e-02, /* 973 */ + 9.937495887279510e-01, /* 974 */ + -2.287736535072327e-02, /* 975 */ + }, { + -1.047134399414062e-03, /* 976 */ + 2.767753601074219e-02, /* 977 */ + 9.946613311767578e-01, /* 978 */ + -2.129173278808594e-02, /* 979 */ + }, { + -8.834898471832275e-04, /* 980 */ + 2.505800127983093e-02, /* 981 */ + 9.955032169818878e-01, /* 982 */ + -1.967772841453552e-02, /* 983 */ + }, { + -7.331371307373047e-04, /* 984 */ + 2.249360084533691e-02, /* 985 */ + 9.962747097015381e-01, /* 986 */ + -1.803517341613770e-02, /* 987 */ + }, { + -5.962550640106201e-04, /* 988 */ + 1.998487114906311e-02, /* 989 */ + 9.969752728939056e-01, /* 990 */ + -1.636388897895813e-02, /* 991 */ + }, { + -4.730224609375000e-04, /* 992 */ + 1.753234863281250e-02, /* 993 */ + 9.976043701171875e-01, /* 994 */ + -1.466369628906250e-02, /* 995 */ + }, { + -3.636181354522705e-04, /* 996 */ + 1.513656973838806e-02, /* 997 */ + 9.981614649295807e-01, /* 998 */ + -1.293441653251648e-02, /* 999 */ + }, { + -2.682209014892578e-04, /* 1000 */ + 1.279807090759277e-02, /* 1001 */ + 9.986460208892822e-01, /* 1002 */ + -1.117587089538574e-02, /* 1003 */ + }, { + -1.870095729827881e-04, /* 1004 */ + 1.051738858222961e-02, /* 1005 */ + 9.990575015544891e-01, /* 1006 */ + -9.387880563735962e-03, /* 1007 */ + }, { + -1.201629638671875e-04, /* 1008 */ + 8.295059204101562e-03, /* 1009 */ + 9.993953704833984e-01, /* 1010 */ + -7.570266723632812e-03, /* 1011 */ + }, { + -6.785988807678223e-05, /* 1012 */ + 6.131619215011597e-03, /* 1013 */ + 9.996590912342072e-01, /* 1014 */ + -5.722850561141968e-03, /* 1015 */ + }, { + -3.027915954589844e-05, /* 1016 */ + 4.027605056762695e-03, /* 1017 */ + 9.998481273651123e-01, /* 1018 */ + -3.845453262329102e-03, /* 1019 */ + }, { + -7.599592208862305e-06, /* 1020 */ + 1.983553171157837e-03, /* 1021 */ + 9.999619424343109e-01, /* 1022 */ + -1.937896013259888e-03, /* 1023 */ + } +}; + +static const fluid_real_t sinc_table7[256][7] = { + { + 2.375620125729980e-02, /* 0 */ + -1.290049686942476e-01, /* 1 */ + 5.998790953453343e-01, /* 2 */ + 6.103020511314905e-01, /* 3 */ + -1.304058543975903e-01, /* 4 */ + 2.418010665366927e-02, /* 5 */ + -2.798064002790454e-07, /* 6 */ + }, { + 2.354065170871666e-02, /* 7 */ + -1.282805558938982e-01, /* 8 */ + 5.946483199408858e-01, /* 9 */ + 6.154931623267351e-01, /* 10 */ + -1.310817379215285e-01, /* 11 */ + 2.438827747279956e-02, /* 12 */ + -1.120220972351802e-06, /* 13 */ + }, { + 2.332282679065955e-02, /* 14 */ + -1.275405562274653e-01, /* 15 */ + 5.894053926563386e-01, /* 16 */ + 6.206699834160688e-01, /* 17 */ + -1.317408559206581e-01, /* 18 */ + 2.459380290371239e-02, /* 19 */ + -2.522356622734990e-06, /* 20 */ + }, { + 2.310281775655174e-02, /* 21 */ + -1.267852645808413e-01, /* 22 */ + 5.841508484795060e-01, /* 23 */ + 6.258319806421593e-01, /* 24 */ + -1.323829141999531e-01, /* 25 */ + 2.479658928687493e-02, /* 26 */ + -4.486817393493708e-06, /* 27 */ + }, { + 2.288071532172811e-02, /* 28 */ + -1.260149758298408e-01, /* 29 */ + 5.788852224239674e-01, /* 30 */ + 6.309786207146856e-01, /* 31 */ + -1.330076188523975e-01, /* 32 */ + 2.499654254034837e-02, /* 33 */ + -7.013696123785544e-06, /* 34 */ + }, { + 2.265660964514263e-02, /* 35 */ + -1.252299847913509e-01, /* 36 */ + 5.736090494558752e-01, /* 37 */ + 6.361093708841161e-01, /* 38 */ + -1.336146763094198e-01, /* 39 */ + 2.519356818002896e-02, /* 40 */ + -1.010257230258042e-05, /* 41 */ + }, { + 2.243059031136537e-02, /* 42 */ + -1.244305861747393e-01, /* 43 */ + 5.683228644208926e-01, /* 44 */ + 6.412236990155202e-01, /* 45 */ + -1.342037933915221e-01, /* 46 */ + 2.538757134015553e-02, /* 47 */ + -1.375251011368080e-05, /* 48 */ + }, { + 2.220274631287172e-02, /* 49 */ + -1.236170745335310e-01, /* 50 */ + 5.630272019712755e-01, /* 51 */ + 6.463210736624057e-01, /* 52 */ + -1.347746773590917e-01, /* 53 */ + 2.557845679407980e-02, /* 54 */ + -1.796205667443242e-05, /* 55 */ + }, { + 2.197316603262602e-02, /* 56 */ + -1.227897442173580e-01, /* 57 */ + 5.577225964931086e-01, /* 58 */ + 6.514009641405650e-01, /* 59 */ + -1.353270359633906e-01, /* 60 */ + 2.576612897529643e-02, /* 61 */ + -2.272924046911682e-05, /* 62 */ + }, { + 2.174193722696236e-02, /* 63 */ + -1.219488893241928e-01, /* 64 */ + 5.524095820337069e-01, /* 65 */ + 6.564628406019232e-01, /* 66 */ + -1.358605774977103e-01, /* 67 */ + 2.595049199872917e-02, /* 68 */ + -2.805156997831240e-05, /* 69 */ + }, { + 2.150914700876424e-02, /* 70 */ + -1.210948036528713e-01, /* 71 */ + 5.470886922291980e-01, /* 72 */ + 6.615061741083712e-01, /* 73 */ + -1.363750108486864e-01, /* 74 */ + 2.613144968226988e-02, /* 75 */ + -3.392603250514114e-05, /* 76 */ + }, { + 2.127488183094605e-02, /* 77 */ + -1.202277806559141e-01, /* 78 */ + 5.417604602322907e-01, /* 79 */ + 6.665304367055752e-01, /* 80 */ + -1.368700455477614e-01, /* 81 */ + 2.630890556856650e-02, /* 82 */ + -4.034909319948082e-05, /* 83 */ + }, { + 2.103922747023789e-02, /* 84 */ + -1.193481133926526e-01, /* 85 */ + 5.364254186402488e-01, /* 86 */ + 6.715351014967476e-01, /* 87 */ + -1.373453918227895e-01, /* 88 */ + 2.648276294705649e-02, /* 89 */ + -4.731669428110093e-05, /* 90 */ + }, { + 2.080226901127631e-02, /* 91 */ + -1.184560944826678e-01, /* 92 */ + 5.310840994230748e-01, /* 93 */ + 6.765196427163698e-01, /* 94 */ + -1.378007606497726e-01, /* 95 */ + 2.665292487624222e-02, /* 96 */ + -5.482425446254296e-05, /* 97 */ + }, { + 2.056409083100217e-02, /* 98 */ + -1.175520160595501e-01, /* 99 */ + 5.257370338519197e-01, /* 100 */ + 6.814835358038533e-01, /* 101 */ + -1.382358638047184e-01, /* 102 */ + 2.681929420620386e-02, /* 103 */ + -6.286666857267136e-05, /* 104 */ + }, { + 2.032477658336845e-02, /* 105 */ + -1.166361697249849e-01, /* 106 */ + 5.203847524277286e-01, /* 107 */ + 6.864262574771270e-01, /* 108 */ + -1.386504139156142e-01, /* 109 */ + 2.698177360134680e-02, /* 110 */ + -7.143830738156712e-05, /* 111 */ + }, { + 2.008440918435909e-02, /* 112 */ + -1.157088465031742e-01, /* 113 */ + 5.150277848101327e-01, /* 114 */ + 6.913472858061384e-01, /* 115 */ + -1.390441245145027e-01, /* 116 */ + 2.714026556337870e-02, /* 117 */ + -8.053301762762107e-05, /* 118 */ + }, { + 1.984307079732106e-02, /* 119 */ + -1.147703367955984e-01, /* 120 */ + 5.096666597466010e-01, /* 121 */ + 6.962461002862578e-01, /* 122 */ + -1.394167100896560e-01, /* 123 */ + 2.729467245451285e-02, /* 124 */ + -9.014412224732896e-05, /* 125 */ + }, { + 1.960084281861067e-02, /* 126 */ + -1.138209303361282e-01, /* 127 */ + 5.043019050018616e-01, /* 128 */ + 7.011221819115717e-01, /* 129 */ + -1.397678861378340e-01, /* 130 */ + 2.744489652089330e-02, /* 131 */ + -1.002644208085391e-04, /* 132 */ + }, { + 1.935780586355643e-02, /* 133 */ + -1.128609161464899e-01, /* 134 */ + 4.989340472876037e-01, /* 135 */ + 7.059750132480542e-01, /* 136 */ + -1.400973692166212e-01, /* 137 */ + 2.759083991623796e-02, /* 138 */ + -1.108861901476344e-04, /* 139 */ + }, { + 1.911403975273935e-02, /* 140 */ + -1.118905824920952e-01, /* 141 */ + 4.935636121924722e-01, /* 142 */ + 7.108040785066047e-01, /* 143 */ + -1.404048769968304e-01, /* 144 */ + 2.773240472569498e-02, /* 145 */ + -1.220011852110915e-04, /* 146 */ + }, { + 1.886962349859249e-02, /* 147 */ + -1.109102168382377e-01, /* 148 */ + 4.881911241123659e-01, /* 149 */ + 7.156088636159380e-01, /* 150 */ + -1.406901283149657e-01, /* 151 */ + 2.786949298990845e-02, /* 152 */ + -1.336006401019428e-04, /* 153 */ + }, { + 1.862463529232039e-02, /* 154 */ + -1.099201058066671e-01, /* 155 */ + 4.828171061810496e-01, /* 156 */ + 7.203888562953171e-01, /* 157 */ + -1.409528432257348e-01, /* 158 */ + 2.800200672928881e-02, /* 159 */ + -1.456752693314445e-04, /* 160 */ + }, { + 1.837915249114045e-02, /* 161 */ + -1.089205351325436e-01, /* 162 */ + 4.774420802010910e-01, /* 163 */ + 7.251435461271148e-01, /* 164 */ + -1.411927430545998e-01, /* 165 */ + 2.812984796848375e-02, /* 166 */ + -1.582152692763085e-04, /* 167 */ + }, { + 1.813325160584695e-02, /* 168 */ + -1.079117896217818e-01, /* 169 */ + 4.720665665751356e-01, /* 170 */ + 7.298724246291924e-01, /* 171 */ + -1.414095504503600e-01, /* 172 */ + 2.825291876104482e-02, /* 173 */ + -1.712103198416544e-04, /* 174 */ + }, { + 1.788700828869835e-02, /* 175 */ + -1.068941531087891e-01, /* 176 */ + 4.666910842375266e-01, /* 177 */ + 7.345749853270843e-01, /* 178 */ + -1.416029894377547e-01, /* 179 */ + 2.837112121428517e-02, /* 180 */ + -1.846495863300355e-04, /* 181 */ + }, { + 1.764049732162978e-02, /* 182 */ + -1.058679084146052e-01, /* 183 */ + 4.613161505862837e-01, /* 184 */ + 7.392507238259753e-01, /* 185 */ + -1.417727854700776e-01, /* 186 */ + 2.848435751432410e-02, /* 187 */ + -1.985217215164836e-04, /* 188 */ + }, { + 1.739379260479069e-02, /* 189 */ + -1.048333373054486e-01, /* 190 */ + 4.559422814154492e-01, /* 191 */ + 7.438991378824603e-01, /* 192 */ + -1.419186654817930e-01, /* 193 */ + 2.859252995131316e-02, /* 194 */ + -2.128148679298167e-04, /* 195 */ + }, { + 1.714696714540915e-02, /* 196 */ + -1.037907204516760e-01, /* 197 */ + 4.505699908478140e-01, /* 198 */ + 7.485197274760710e-01, /* 199 */ + -1.420403579411441e-01, /* 200 */ + 2.869554094483946e-02, /* 201 */ + -2.275166603400509e-04, /* 202 */ + }, { + 1.690009304698279e-02, /* 203 */ + -1.027403373871614e-01, /* 204 */ + 4.451997912680325e-01, /* 205 */ + 7.531119948805626e-01, /* 206 */ + -1.421375929027446e-01, /* 207 */ + 2.879329306950119e-02, /* 208 */ + -2.426142284520608e-04, /* 209 */ + }, { + 1.665324149879781e-02, /* 210 */ + -1.016824664690990e-01, /* 211 */ + 4.398321932561375e-01, /* 212 */ + 7.576754447349440e-01, /* 213 */ + -1.422101020601427e-01, /* 214 */ + 2.888568908065016e-02, /* 215 */ + -2.580941998051696e-04, /* 216 */ + }, { + 1.640648276577594e-02, /* 217 */ + -1.006173848382378e-01, /* 218 */ + 4.344677055214644e-01, /* 219 */ + 7.622095841142430e-01, /* 220 */ + -1.422576187983489e-01, /* 221 */ + 2.897263194029685e-02, /* 222 */ + -2.739427028786713e-04, /* 223 */ + }, { + 1.615988617865028e-02, /* 224 */ + -9.954536837955191e-02, /* 225 */ + 4.291068348369969e-01, /* 226 */ + 7.667139225999916e-01, /* 227 */ + -1.422798782463173e-01, /* 228 */ + 2.905402484317260e-02, /* 229 */ + -2.901453704029424e-04, /* 230 */ + }, { + 1.591352012446994e-02, /* 231 */ + -9.846669168335127e-02, /* 232 */ + 4.237500859741424e-01, /* 233 */ + 7.711879723504229e-01, /* 234 */ + -1.422766173293711e-01, /* 235 */ + 2.912977124294393e-02, /* 236 */ + -3.066873428758958e-04, /* 237 */ + }, { + 1.566745203743416e-02, /* 238 */ + -9.738162800684201e-02, /* 239 */ + 4.183979616379481e-01, /* 240 */ + 7.756312481703652e-01, /* 241 */ + -1.422475748215626e-01, /* 242 */ + 2.919977487857369e-02, /* 243 */ + -3.235532722844501e-04, /* 244 */ + }, { + 1.542174839005620e-02, /* 245 */ + -9.629044923613632e-02, /* 246 */ + 4.130509624027673e-01, /* 247 */ + 7.800432675808223e-01, /* 248 */ + -1.421924913979572e-01, /* 249 */ + 2.926393980082415e-02, /* 250 */ + -3.407273260304884e-04, /* 251 */ + }, { + 1.517647468465644e-02, /* 252 */ + -9.519342584872197e-02, /* 253 */ + 4.077095866483865e-01, /* 254 */ + 7.844235508882289e-01, /* 255 */ + -1.421111096868331e-01, /* 256 */ + 2.932217039889637e-02, /* 257 */ + -3.581931910609877e-04, /* 258 */ + }, { + 1.493169544518567e-02, /* 259 */ + -9.409082687639277e-02, /* 260 */ + 4.023743304966226e-01, /* 261 */ + 7.887716212533704e-01, /* 262 */ + -1.420031743217853e-01, /* 263 */ + 2.937437142720069e-02, /* 264 */ + -3.759340782016456e-04, /* 265 */ + }, { + 1.468747420937781e-02, /* 266 */ + -9.298291986864780e-02, /* 267 */ + 3.970456877483993e-01, /* 268 */ + 7.930870047599514e-01, /* 269 */ + -1.418684319937252e-01, /* 270 */ + 2.942044803225294e-02, /* 271 */ + -3.939327266934505e-04, /* 272 */ + }, { + 1.444387352123256e-02, /* 273 */ + -9.186997085656183e-02, /* 274 */ + 3.917241498213130e-01, /* 275 */ + 7.973692304828067e-01, /* 276 */ + -1.417066315027663e-01, /* 277 */ + 2.946030577969092e-02, /* 278 */ + -4.121714089315939e-04, /* 279 */ + }, { + 1.420095492382687e-02, /* 280 */ + -9.075224431713386e-02, /* 281 */ + 3.864102056876984e-01, /* 282 */ + 8.016178305557399e-01, /* 283 */ + -1.415175238099844e-01, /* 284 */ + 2.949385068140553e-02, /* 285 */ + -4.306319354058826e-04, /* 286 */ + }, { + 1.395877895245621e-02, /* 287 */ + -8.963000313811628e-02, /* 288 */ + 3.811043418132007e-01, /* 289 */ + 8.058323402389781e-01, /* 290 */ + -1.413008620890449e-01, /* 291 */ + 2.952098922278122e-02, /* 292 */ + -4.492956598419907e-04, /* 293 */ + }, { + 1.371740512810407e-02, /* 294 */ + -8.850350858333141e-02, /* 295 */ + 3.758070420958670e-01, /* 296 */ + 8.100122979862356e-01, /* 297 */ + -1.410564017776856e-01, /* 298 */ + 2.954162839003991e-02, /* 299 */ + -4.681434845426153e-04, /* 300 */ + }, { + 1.347689195124034e-02, /* 301 */ + -8.737302025847754e-02, /* 302 */ + 3.705187878057628e-01, /* 303 */ + 8.141572455113678e-01, /* 304 */ + -1.407839006290460e-01, /* 305 */ + 2.955567569768274e-02, /* 306 */ + -4.871558659276469e-04, /* 307 */ + }, { + 1.323729689594689e-02, /* 308 */ + -8.623879607743025e-02, /* 309 */ + 3.652400575251253e-01, /* 310 */ + 8.182667278546104e-01, /* 311 */ + -1.404831187628331e-01, /* 312 */ + 2.956303921602418e-02, /* 313 */ + -5.063128202724816e-04, /* 314 */ + }, { + 1.299867640437090e-02, /* 315 */ + -8.510109222904338e-02, /* 316 */ + 3.599713270890607e-01, /* 317 */ + 8.223402934483920e-01, /* 318 */ + -1.401538187163136e-01, /* 319 */ + 2.956362759881255e-02, /* 320 */ + -5.255939296432825e-04, /* 321 */ + }, { + 1.276108588150466e-02, /* 322 */ + -8.396016314445182e-02, /* 323 */ + 3.547130695267950e-01, /* 324 */ + 8.263774941827043e-01, /* 325 */ + -1.397957654951237e-01, /* 326 */ + 2.955735011093100e-02, /* 327 */ + -5.449783480282249e-04, /* 328 */ + }, { + 1.252457969029095e-02, /* 329 */ + -8.281626146488272e-02, /* 330 */ + 3.494657550034874e-01, /* 331 */ + 8.303778854700258e-01, /* 332 */ + -1.394087266238854e-01, /* 333 */ + 2.954411665617338e-02, /* 334 */ + -5.644448076635753e-04, /* 335 */ + }, { + 1.228921114705370e-02, /* 336 */ + -8.166963800997633e-02, /* 337 */ + 3.442298507626138e-01, /* 338 */ + 8.343410263097801e-01, /* 339 */ + -1.389924721966199e-01, /* 340 */ + 2.952383780508903e-02, /* 341 */ + -5.839716255532901e-04, /* 342 */ + }, { + 1.205503251725260e-02, /* 343 */ + -8.052054174662254e-02, /* 344 */ + 3.390058210689310e-01, /* 345 */ + 8.382664793523271e-01, /* 346 */ + -1.385467749269494e-01, /* 347 */ + 2.949642482289040e-02, /* 348 */ + -6.035367101809801e-04, /* 349 */ + }, { + 1.182209501156100e-02, /* 350 */ + -7.936921975831460e-02, /* 351 */ + 3.337941271520264e-01, /* 352 */ + 8.421538109624669e-01, /* 353 */ + -1.380714101980755e-01, /* 354 */ + 2.946178969741759e-02, /* 355 */ + -6.231175684128511e-04, /* 356 */ + }, { + 1.159044878226562e-02, /* 357 */ + -7.821591721502538e-02, /* 358 */ + 3.285952271504655e-01, /* 359 */ + 8.460025912824529e-01, /* 360 */ + -1.375661561125266e-01, /* 361 */ + 2.941984516715388e-02, /* 362 */ + -6.426913125902382e-04, /* 363 */ + }, { + 1.136014291998739e-02, /* 364 */ + -7.706087734360774e-02, /* 365 */ + 3.234095760565429e-01, /* 366 */ + 8.498123942944995e-01, /* 367 */ + -1.370307935416629e-01, /* 368 */ + 2.937050474928606e-02, /* 369 */ + -6.622346678102776e-04, /* 370 */ + }, { + 1.113122545072202e-02, /* 371 */ + -7.590434139872407e-02, /* 372 */ + 3.182376256616457e-01, /* 373 */ + 8.535827978827749e-01, /* 374 */ + -1.364651061749298e-01, /* 375 */ + 2.931368276780341e-02, /* 376 */ + -6.817239793932266e-04, /* 377 */ + }, { + 1.090374333319907e-02, /* 378 */ + -7.474654863430663e-02, /* 379 */ + 3.130798245022357e-01, /* 380 */ + 8.573133838948686e-01, /* 381 */ + -1.358688805688508e-01, /* 382 */ + 2.924929438162952e-02, /* 383 */ + -7.011352205348302e-04, /* 384 */ + }, { + 1.067774245655789e-02, /* 385 */ + -7.358773627555218e-02, /* 386 */ + 3.079366178064618e-01, /* 387 */ + 8.610037382027232e-01, /* 388 */ + -1.352419061957486e-01, /* 389 */ + 2.917725561278015e-02, /* 390 */ + -7.204440001421441e-04, /* 391 */ + }, { + 1.045326763833955e-02, /* 392 */ + -7.242813949145467e-02, /* 393 */ + 3.028084474414053e-01, /* 394 */ + 8.646534507630197e-01, /* 395 */ + -1.345839754921861e-01, /* 396 */ + 2.909748337454149e-02, /* 397 */ + -7.396255708510786e-04, /* 398 */ + }, { + 1.023036262279292e-02, /* 399 */ + -7.126799136787691e-02, /* 400 */ + 2.976957518609703e-01, /* 401 */ + 8.682621156770067e-01, /* 402 */ + -1.338948839071171e-01, /* 403 */ + 2.900989549966232e-02, /* 404 */ + -7.586548372239626e-04, /* 405 */ + }, { + 1.000907007949311e-02, /* 406 */ + -7.010752288116626e-02, /* 407 */ + 2.925989660544231e-01, /* 408 */ + 8.718293312497631e-01, /* 409 */ + -1.331744299497369e-01, /* 410 */ + 2.891441076855387e-02, /* 411 */ + -7.775063641252938e-04, /* 412 */ + }, { + 9.789431602271271e-03, /* 413 */ + -6.894696287231404e-02, /* 414 */ + 2.875185214955909e-01, /* 415 */ + 8.753547000488832e-01, /* 416 */ + -1.324224152370240e-01, /* 417 */ + 2.881094893749082e-02, /* 418 */ + -7.961543852738185e-04, /* 419 */ + }, { + 9.571487708453395e-03, /* 420 */ + -6.778653802166430e-02, /* 421 */ + 2.824548460927230e-01, /* 422 */ + 8.788378289625756e-01, /* 423 */ + -1.316386445409620e-01, /* 424 */ + 2.869943076680737e-02, /* 425 */ + -8.145728119690237e-04, /* 426 */ + }, { + 9.355277838406877e-03, /* 427 */ + -6.662647282417096e-02, /* 428 */ + 2.774083641390264e-01, /* 429 */ + 8.822783292571661e-01, /* 430 */ + -1.308229258354336e-01, /* 431 */ + 2.857977804908198e-02, /* 432 */ + -8.327352419900550e-04, /* 433 */ + }, { + 9.140840355392437e-03, /* 434 */ + -6.546698956520861e-02, /* 435 */ + 2.723794962638790e-01, /* 436 */ + 8.856758166339943e-01, /* 437 */ + -1.299750703427760e-01, /* 438 */ + 2.845191363730427e-02, /* 439 */ + -8.506149686650558e-04, /* 440 */ + }, { + 8.928212545720045e-03, /* 441 */ + -6.430830829693616e-02, /* 442 */ + 2.673686593847286e-01, /* 443 */ + 8.890299112856920e-01, /* 444 */ + -1.290948925799897e-01, /* 445 */ + 2.831576147301751e-02, /* 446 */ + -8.681849901087664e-04, /* 447 */ + }, { + 8.717430619206452e-03, /* 448 */ + -6.315064681521791e-02, /* 449 */ + 2.623762666596838e-01, /* 450 */ + 8.923402379518393e-01, /* 451 */ + -1.281822104045887e-01, /* 452 */ + 2.817124661443064e-02, /* 453 */ + -8.854180186263388e-04, /* 454 */ + }, { + 8.508529709932666e-03, /* 455 */ + -6.199422063710185e-02, /* 456 */ + 2.574027274408040e-01, /* 457 */ + 8.956064259739822e-01, /* 458 */ + -1.272368450600864e-01, /* 459 */ + 2.801829526449300e-02, /* 460 */ + -9.022864902810273e-04, /* 461 */ + }, { + 8.301543877298635e-03, /* 462 */ + -6.083924297885757e-02, /* 463 */ + 2.524484472280946e-01, /* 464 */ + 8.988281093500092e-01, /* 465 */ + -1.262586212211042e-01, /* 466 */ + 2.785683479892532e-02, /* 467 */ + -9.187625746236699e-04, /* 468 */ + }, { + 8.096506107373698e-03, /* 469 */ + -5.968592473457642e-02, /* 470 */ + 2.475138276242130e-01, /* 471 */ + 9.020049267878701e-01, /* 472 */ + -1.252473670380959e-01, /* 473 */ + 2.768679379420070e-02, /* 474 */ + -9.348181845814541e-04, /* 475 */ + }, { + 7.893448314540197e-03, /* 476 */ + -5.853447445533320e-02, /* 477 */ + 2.425992662898926e-01, /* 478 */ + 9.051365217586359e-01, /* 479 */ + -1.242029141816779e-01, /* 480 */ + 2.750810205546858e-02, /* 481 */ + -9.504249865037702e-04, /* 482 */ + }, { + 7.692401343427691e-03, /* 483 */ + -5.738509832891316e-02, /* 484 */ + 2.377051569000891e-01, /* 485 */ + 9.082225425488828e-01, /* 486 */ + -1.231250978865554e-01, /* 487 */ + 2.732069064441559e-02, /* 488 */ + -9.655544103626029e-04, /* 489 */ + }, { + 7.493394971135955e-03, /* 490 */ + -5.623800016010321e-02, /* 491 */ + 2.328318891008579e-01, /* 492 */ + 9.112626423123993e-01, /* 493 */ + -1.220137569950366e-01, /* 494 */ + 2.712449190705641e-02, /* 495 */ + -9.801776601050557e-04, /* 496 */ + }, { + 7.296457909743918e-03, /* 497 */ + -5.509338135155091e-02, /* 498 */ + 2.279798484669641e-01, /* 499 */ + 9.142564791211991e-01, /* 500 */ + -1.208687340001249e-01, /* 501 */ + 2.691943950144822e-02, /* 502 */ + -9.942657241554641e-04, /* 503 */ + }, { + 7.101617809102288e-03, /* 504 */ + -5.395144088518943e-02, /* 505 */ + 2.231494164602341e-01, /* 506 */ + 9.172037160158394e-01, /* 507 */ + -1.196898750881803e-01, /* 508 */ + 2.670546842532187e-02, /* 509 */ + -1.007789386064493e-03, /* 510 */ + }, { + 6.908901259906972e-03, /* 511 */ + -5.281237530423245e-02, /* 512 */ + 2.183409703886530e-01, /* 513 */ + 9.201040210550299e-01, /* 514 */ + -1.184770301811412e-01, /* 515 */ + 2.648251504362370e-02, /* 516 */ + -1.020719235302619e-03, /* 517 */ + }, { + 6.718333797051150e-03, /* 518 */ + -5.167637869573667e-02, /* 519 */ + 2.135548833662130e-01, /* 520 */ + 9.229570673645258e-01, /* 521 */ + -1.172300529782974e-01, /* 522 */ + 2.625051711596052e-02, /* 523 */ + -1.033025678195301e-03, /* 524 */ + }, { + 6.529939903253073e-03, /* 525 */ + -5.054364267373540e-02, /* 526 */ + 2.087915242735169e-01, /* 527 */ + 9.257625331852983e-01, /* 528 */ + -1.159488009976052e-01, /* 529 */ + 2.600941382394202e-02, /* 530 */ + -1.044678948997064e-03, /* 531 */ + }, { + 6.343743012956645e-03, /* 532 */ + -4.941435636294156e-02, /* 533 */ + 2.040512577191446e-01, /* 534 */ + 9.285201019209733e-01, /* 535 */ + -1.146331356165368e-01, /* 536 */ + 2.575914579841332e-02, /* 537 */ + -1.055649121101780e-03, /* 538 */ + }, { + 6.159765516502505e-03, /* 539 */ + -4.828870638302148e-02, /* 540 */ + 1.993344440017834e-01, /* 541 */ + 9.312294621845263e-01, /* 542 */ + -1.132829221124536e-01, /* 543 */ + 2.549965514657118e-02, /* 544 */ + -1.065906118386268e-03, /* 545 */ + }, { + 5.978028764566336e-03, /* 546 */ + -4.716687683344033e-02, /* 547 */ + 1.946414390731309e-01, /* 548 */ + 9.338903078442332e-01, /* 549 */ + -1.118980297024964e-01, /* 550 */ + 2.523088547895752e-02, /* 551 */ + -1.075419726684316e-03, /* 552 */ + }, { + 5.798553072861818e-03, /* 553 */ + -4.604904927887769e-02, /* 554 */ + 1.899725945015709e-01, /* 555 */ + 9.365023380688623e-01, /* 556 */ + -1.104783315829824e-01, /* 557 */ + 2.495278193632289e-02, /* 558 */ + -1.084159605388224e-03, /* 559 */ + }, { + 5.621357727104864e-03, /* 560 */ + -4.493540273521546e-02, /* 561 */ + 1.853282574366316e-01, /* 562 */ + 9.390652573721028e-01, /* 563 */ + -1.090237049683018e-01, /* 564 */ + 2.466529121635406e-02, /* 565 */ + -1.092095299174878e-03, /* 566 */ + }, { + 5.446460988236670e-03, /* 567 */ + -4.382611365609584e-02, /* 568 */ + 1.807087705742239e-01, /* 569 */ + 9.415787756562223e-01, /* 570 */ + -1.075340311293039e-01, /* 571 */ + 2.436836160025825e-02, /* 572 */ + -1.099196249853307e-03, /* 573 */ + }, { + 5.273880097902076e-03, /* 574 */ + -4.272135592005138e-02, /* 575 */ + 1.761144721226713e-01, /* 576 */ + 9.440426082549445e-01, /* 577 */ + -1.060091954311663e-01, /* 578 */ + 2.406194297919816e-02, /* 579 */ + -1.105431808330667e-03, /* 580 */ + }, { + 5.103631284180478e-03, /* 581 */ + -4.162130081820462e-02, /* 582 */ + 1.715456957695286e-01, /* 583 */ + 9.464564759755407e-01, /* 584 */ + -1.044490873707362e-01, /* 585 */ + 2.374598688057016e-02, /* 586 */ + -1.110771246693530e-03, /* 587 */ + }, { + 4.935729767565705e-03, /* 588 */ + -4.052611704253924e-02, /* 589 */ + 1.670027706491982e-01, /* 590 */ + 9.488201051401287e-01, /* 591 */ + -1.028536006133390e-01, /* 592 */ + 2.342044649412001e-02, /* 593 */ + -1.115183770401296e-03, /* 594 */ + }, { + 4.770189767192131e-03, /* 595 */ + -3.943597067473980e-02, /* 596 */ + 1.624860213113444e-01, /* 597 */ + 9.511332276261677e-01, /* 598 */ + -1.012226330290420e-01, /* 599 */ + 2.308527669788855e-02, /* 600 */ + -1.118638530588553e-03, /* 601 */ + }, { + 4.607024507303565e-03, /* 602 */ + -3.835102517560170e-02, /* 603 */ + 1.579957676901108e-01, /* 604 */ + 9.533955809061508e-01, /* 605 */ + -9.955608672836883e-02, /* 606 */ + 2.274043408398138e-02, /* 607 */ + -1.121104636473070e-03, /* 608 */ + }, { + 4.446246223961389e-03, /* 609 */ + -3.727144137500895e-02, /* 610 */ + 1.535323250741442e-01, /* 611 */ + 9.556069080864779e-01, /* 612 */ + -9.785386809745342e-02, /* 613 */ + 2.238587698415551e-02, /* 614 */ + -1.122551167866203e-03, /* 615 */ + }, { + 4.287866171989037e-03, /* 616 */ + -3.619737746247935e-02, /* 617 */ + 1.490960040774270e-01, /* 618 */ + 9.577669579455091e-01, /* 619 */ + -9.611588783262809e-02, /* 620 */ + 2.202156549521620e-02, /* 621 */ + -1.122947187782295e-03, /* 622 */ + }, { + 4.131894632149082e-03, /* 623 */ + -3.512898897827717e-02, /* 624 */ + 1.446871106109222e-01, /* 625 */ + 9.598754849707929e-01, /* 626 */ + -9.434206097443590e-02, /* 627 */ + 2.164746150421816e-02, /* 628 */ + -1.122261755143747e-03, /* 629 */ + }, { + 3.978340918549750e-03, /* 630 */ + -3.406642880509015e-02, /* 631 */ + 1.403059458550339e-01, /* 632 */ + 9.619322493954562e-01, /* 633 */ + -9.253230694106100e-02, /* 634 */ + 2.126352871346326e-02, /* 635 */ + -1.120463937578314e-03, /* 636 */ + }, { + 3.827213386276986e-03, /* 637 */ + -3.300984716027202e-02, /* 638 */ + 1.359528062328858e-01, /* 639 */ + 9.639370172337561e-01, /* 640 */ + -9.068654956116820e-02, /* 641 */ + 2.086973266528943e-02, /* 642 */ + -1.117522824305172e-03, /* 643 */ + }, { + 3.678519439249078e-03, /* 644 */ + -3.195939158864695e-02, /* 645 */ + 1.316279833844193e-01, /* 646 */ + 9.658895603157857e-01, /* 647 */ + -8.880471710614442e-02, /* 648 */ + 2.046604076664311e-02, /* 649 */ + -1.113407539106230e-03, /* 650 */ + }, { + 3.532265538289867e-03, /* 651 */ + -3.091520695587671e-02, /* 652 */ + 1.273317641413156e-01, /* 653 */ + 9.677896563213259e-01, /* 654 */ + -8.688674232173496e-02, /* 655 */ + 2.005242231342951e-02, /* 656 */ + -1.108087253379183e-03, /* 657 */ + }, { + 3.388457209417186e-03, /* 658 */ + -2.987743544238687e-02, /* 659 */ + 1.230644305027427e-01, /* 660 */ + 9.696370888128401e-01, /* 661 */ + -8.493256245906648e-02, /* 662 */ + 1.962884851463336e-02, /* 663 */ + -1.101531199268716e-03, /* 664 */ + }, { + 3.247099052342608e-03, /* 665 */ + -2.884621653785256e-02, /* 666 */ + 1.188262596119302e-01, /* 667 */ + 9.714316472676046e-01, /* 668 */ + -8.294211930504999e-02, /* 669 */ + 1.919529251620448e-02, /* 670 */ + -1.093708682872210e-03, /* 671 */ + }, { + 3.108194749179169e-03, /* 672 */ + -2.782168703623990e-02, /* 673 */ + 1.146175237335723e-01, /* 674 */ + 9.731731271089683e-01, /* 675 */ + -8.091535921215573e-02, /* 676 */ + 1.875172942470069e-02, /* 677 */ + -1.084589097516370e-03, /* 678 */ + }, { + 2.971747073353185e-03, /* 679 */ + -2.680398103140338e-02, /* 680 */ + 1.104384902320638e-01, /* 681 */ + 9.748613297367402e-01, /* 682 */ + -7.885223312755388e-02, /* 683 */ + 1.829813633068257e-02, /* 684 */ + -1.074141937101013e-03, /* 685 */ + }, { + 2.837757898716223e-03, /* 686 */ + -2.579322991323553e-02, /* 687 */ + 1.062894215505674e-01, /* 688 */ + 9.764960625566944e-01, /* 689 */ + -7.675269662161305e-02, /* 690 */ + 1.783449233185286e-02, /* 691 */ + -1.062336809506350e-03, /* 692 */ + }, { + 2.706228208853840e-03, /* 693 */ + -2.478956236436741e-02, /* 694 */ + 1.021705751909168e-01, /* 695 */ + 9.780771390091908e-01, /* 696 */ + -7.461670991575037e-02, /* 697 */ + 1.736077855593420e-02, /* 698 */ + -1.049143450059986e-03, /* 699 */ + }, { + 2.577158106586916e-03, /* 700 */ + -2.379310435741876e-02, /* 701 */ + 9.808220369435408e-02, /* 702 */ + 9.796043785969055e-01, /* 703 */ + -7.244423790962552e-02, /* 704 */ + 1.687697818327941e-02, /* 705 */ + -1.034531735059858e-03, /* 706 */ + }, { + 2.450546823661987e-03, /* 707 */ + -2.280397915279337e-02, /* 708 */ + 9.402455462310588e-02, /* 709 */ + 9.810776069116667e-01, /* 710 */ + -7.023525020767289e-02, /* 711 */ + 1.638307646920682e-02, /* 712 */ + -1.018471695349289e-03, /* 713 */ + }, { + 2.326392730626404e-03, /* 714 */ + -2.182230729701992e-02, /* 715 */ + 8.999787054279679e-02, /* 716 */ + 9.824966556603907e-01, /* 717 */ + -6.798972114496460e-02, /* 718 */ + 1.587906076605559e-02, /* 719 */ + -1.000933529940309e-03, /* 720 */ + }, { + 2.204693346884742e-03, /* 721 */ + -2.084820662163316e-02, /* 722 */ + 8.600238900570113e-02, /* 723 */ + 9.838613626901140e-01, /* 724 */ + -6.570762981239750e-02, /* 725 */ + 1.536492054495343e-02, /* 726 */ + -9.818876196813738e-04, /* 727 */ + }, { + 2.085445350932246e-03, /* 728 */ + -1.988179224259554e-02, /* 729 */ + 8.203834253483586e-02, /* 730 */ + 9.851715720121177e-01, /* 731 */ + -6.338896008119892e-02, /* 732 */ + 1.484064741729164e-02, /* 733 */ + -9.613045409655687e-04, /* 734 */ + }, { + 1.968644590761548e-03, /* 735 */ + -1.892317656025402e-02, /* 736 */ + 7.810595860889319e-02, /* 737 */ + 9.864271338251378e-01, /* 738 */ + -6.103370062674356e-02, /* 739 */ + 1.430623515590003e-02, /* 740 */ + -9.391550794753652e-04, /* 741 */ + }, { + 1.854286094438386e-03, /* 742 */ + -1.797246925983164e-02, /* 743 */ + 7.420545964801417e-02, /* 744 */ + 9.876279045376605e-01, /* 745 */ + -5.864184495167603e-02, /* 746 */ + 1.376167971591675e-02, /* 747 */ + -9.154102439599487e-04, /* 748 */ + }, { + 1.742364080842615e-03, /* 749 */ + -1.702977731244917e-02, /* 750 */ + 7.033706300040359e-02, /* 751 */ + 9.887737467892981e-01, /* 752 */ + -5.621339140833288e-02, /* 753 */ + 1.320697925534576e-02, /* 754 */ + -8.900412800411573e-04, /* 755 */ + }, { + 1.632871970570320e-03, /* 756 */ + -1.609520497667476e-02, /* 757 */ + 6.650098092978608e-02, /* 758 */ + 9.898645294712379e-01, /* 759 */ + -5.374834322045772e-02, /* 760 */ + 1.264213415529629e-02, /* 761 */ + -8.630196840440353e-04, /* 762 */ + }, { + 1.525802396992804e-03, /* 763 */ + -1.516885380059888e-02, /* 764 */ + 6.269742060370384e-02, /* 765 */ + 9.909001277457663e-01, /* 766 */ + -5.124670850420399e-02, /* 767 */ + 1.206714703989862e-02, /* 768 */ + -8.343172168478687e-04, /* 769 */ + }, { + 1.421147217468663e-03, /* 770 */ + -1.425082262442998e-02, /* 771 */ + 5.892658408265536e-02, /* 772 */ + 9.918804230648608e-01, /* 773 */ + -4.870850028841950e-02, /* 774 */ + 1.148202279588897e-02, /* 775 */ + -8.039059177537709e-04, /* 776 */ + }, { + 1.318897524704578e-03, /* 777 */ + -1.334120758360962e-02, /* 778 */ + 5.518866831007525e-02, /* 779 */ + 9.928053031878483e-01, /* 780 */ + -4.613373653420693e-02, /* 781 */ + 1.088676859185900e-02, /* 782 */ + -7.717581183646440e-04, /* 783 */ + }, { + 1.219043658260877e-03, /* 784 */ + -1.244010211244157e-02, /* 785 */ + 5.148386510315437e-02, /* 786 */ + 9.936746621981263e-01, /* 787 */ + -4.352244015375482e-02, /* 788 */ + 1.028139389716235e-02, /* 789 */ + -7.378464564734607e-04, /* 790 */ + }, { + 1.121575216197463e-03, /* 791 */ + -1.154759694823357e-02, /* 792 */ + 4.781236114450060e-02, /* 793 */ + 9.944884005189432e-01, /* 794 */ + -4.087463902843420e-02, /* 795 */ + 9.665910500473909e-03, /* 796 */ + -7.021438899556850e-04, /* 797 */ + }, { + 1.026481066856231e-03, /* 798 */ + -1.066378013594614e-02, /* 799 */ + 4.417433797463730e-02, /* 800 */ + 9.952464249282380e-01, /* 801 */ + -3.819036602615411e-02, /* 802 */ + 9.040332527994370e-03, /* 803 */ + -6.646237106617679e-04, /* 804 */ + }, { + 9.337493607755644e-04, /* 805 */ + -9.788737033346933e-03, /* 806 */ + 4.056997198534163e-02, /* 807 */ + 9.959486485725322e-01, /* 808 */ + -3.546965901797275e-02, /* 809 */ + 8.404676461295810e-03, /* 810 */ + -6.252595583054893e-04, /* 811 */ + }, { + 8.433675427328470e-04, /* 812 */ + -8.922550316664762e-03, /* 813 */ + 3.699943441381923e-02, /* 814 */ + 9.965949909798753e-01, /* 815 */ + -3.271256089395777e-02, /* 816 */ + 7.758961154800972e-03, /* 817 */ + -5.840254343440151e-04, /* 818 */ + }, { + 7.553223639105668e-04, /* 819 */ + -8.065299986741607e-03, /* 820 */ + 3.346289133771520e-02, /* 821 */ + 9.971853780718406e-01, /* 822 */ + -2.991911957829145e-02, /* 823 */ + 7.103207852892033e-03, /* 824 */ + -5.408957158454127e-04, /* 825 */ + }, { + 6.695998941820378e-04, /* 826 */ + -7.217063375677063e-03, /* 827 */ + 2.996050367095970e-02, /* 828 */ + 9.977197421745680e-01, /* 829 */ + -2.708938804361575e-02, /* 830 */ + 6.437440206642007e-03, /* 831 */ + -4.958451693394876e-04, /* 832 */ + }, { + 5.861855345123588e-04, /* 833 */ + -6.377915153961920e-03, /* 834 */ + 2.649242716044695e-02, /* 835 */ + 9.981980220288543e-01, /* 836 */ + -2.422342432461263e-02, /* 837 */ + 5.761684290163624e-03, /* 838 */ + -4.488489646476928e-04, /* 839 */ + }, { + 5.050640294702417e-04, /* 840 */ + -5.547927338097411e-03, /* 841 */ + 2.305881238354565e-02, /* 842 */ + 9.986201627992864e-01, /* 843 */ + -2.132129153081513e-02, /* 844 */ + 5.075968616570653e-03, /* 845 */ + -3.998826886878056e-04, /* 846 */ + }, { + 4.262194798466813e-04, /* 847 */ + -4.727169298694500e-03, /* 848 */ + 1.965980474643937e-02, /* 849 */ + 9.989861160824184e-01, /* 850 */ + -1.838305785864478e-02, /* 851 */ + 4.380324153544939e-03, /* 852 */ + -3.489223592492433e-04, /* 853 */ + }, { + 3.496353553759901e-04, /* 854 */ + -3.915707769050846e-03, /* 855 */ + 1.629554448329466e-02, /* 856 */ + 9.992958399139888e-01, /* 857 */ + -1.540879660267084e-02, /* 858 */ + 3.674784338505265e-03, /* 859 */ + -2.959444387346577e-04, /* 860 */ + }, { + 2.752945075550529e-04, /* 861 */ + -3.113606854199215e-03, /* 862 */ + 1.296616665625557e-02, /* 863 */ + 9.995492987751797e-01, /* 864 */ + -1.239858616608813e-02, /* 865 */ + 2.959385093371052e-03, /* 866 */ + -2.409258478636185e-04, /* 867 */ + }, { + 2.031791825563283e-04, /* 868 */ + -2.320928040424877e-03, /* 869 */ + 9.671801156260738e-03, /* 870 */ + 9.997464635979135e-01, /* 871 */ + -9.352510070407542e-03, /* 872 */ + 2.234164838917225e-03, /* 873 */ + -1.838439793339892e-04, /* 874 */ + }, { + 1.332710342305255e-04, /* 875 */ + -1.537730205245651e-03, /* 876 */ + 6.412572704682764e-03, /* 877 */ + 9.998873117691878e-01, /* 878 */ + -6.270656964357692e-03, /* 879 */ + 1.499164508713334e-03, /* 880 */ + -1.246767114368607e-04, /* 881 */ + }, { + 6.555113719447324e-05, /* 882 */ + -7.640696278518945e-04, /* 883 */ + 3.188600855785918e-03, /* 884 */ + 9.999718271344488e-01, /* 885 */ + -3.153120631992235e-03, /* 886 */ + 7.544275626434484e-04, /* 887 */ + -6.340242162062585e-05, /* 888 */ + }, { + 1.930201848426478e-18, /* 889 */ + -1.515373497812483e-17, /* 890 */ + 3.164321107994089e-17, /* 891 */ + 1.000000000000000e+00, /* 892 */ + 3.164321107994089e-17, /* 893 */ + -1.515373497812483e-17, /* 894 */ + 1.930201848426478e-18, /* 895 */ + }, { + -6.340242162062585e-05, /* 896 */ + 7.544275626434484e-04, /* 897 */ + -3.153120631992235e-03, /* 898 */ + 9.999718271344488e-01, /* 899 */ + 3.188600855785918e-03, /* 900 */ + -7.640696278518945e-04, /* 901 */ + 6.555113719447324e-05, /* 902 */ + }, { + -1.246767114368607e-04, /* 903 */ + 1.499164508713334e-03, /* 904 */ + -6.270656964357692e-03, /* 905 */ + 9.998873117691878e-01, /* 906 */ + 6.412572704682764e-03, /* 907 */ + -1.537730205245651e-03, /* 908 */ + 1.332710342305255e-04, /* 909 */ + }, { + -1.838439793339892e-04, /* 910 */ + 2.234164838917225e-03, /* 911 */ + -9.352510070407542e-03, /* 912 */ + 9.997464635979135e-01, /* 913 */ + 9.671801156260738e-03, /* 914 */ + -2.320928040424877e-03, /* 915 */ + 2.031791825563283e-04, /* 916 */ + }, { + -2.409258478636185e-04, /* 917 */ + 2.959385093371052e-03, /* 918 */ + -1.239858616608813e-02, /* 919 */ + 9.995492987751797e-01, /* 920 */ + 1.296616665625557e-02, /* 921 */ + -3.113606854199215e-03, /* 922 */ + 2.752945075550529e-04, /* 923 */ + }, { + -2.959444387346577e-04, /* 924 */ + 3.674784338505265e-03, /* 925 */ + -1.540879660267084e-02, /* 926 */ + 9.992958399139888e-01, /* 927 */ + 1.629554448329466e-02, /* 928 */ + -3.915707769050846e-03, /* 929 */ + 3.496353553759901e-04, /* 930 */ + }, { + -3.489223592492433e-04, /* 931 */ + 4.380324153544939e-03, /* 932 */ + -1.838305785864478e-02, /* 933 */ + 9.989861160824184e-01, /* 934 */ + 1.965980474643937e-02, /* 935 */ + -4.727169298694500e-03, /* 936 */ + 4.262194798466813e-04, /* 937 */ + }, { + -3.998826886878056e-04, /* 938 */ + 5.075968616570653e-03, /* 939 */ + -2.132129153081513e-02, /* 940 */ + 9.986201627992864e-01, /* 941 */ + 2.305881238354565e-02, /* 942 */ + -5.547927338097411e-03, /* 943 */ + 5.050640294702417e-04, /* 944 */ + }, { + -4.488489646476928e-04, /* 945 */ + 5.761684290163624e-03, /* 946 */ + -2.422342432461263e-02, /* 947 */ + 9.981980220288543e-01, /* 948 */ + 2.649242716044695e-02, /* 949 */ + -6.377915153961920e-03, /* 950 */ + 5.861855345123588e-04, /* 951 */ + }, { + -4.958451693394876e-04, /* 952 */ + 6.437440206642007e-03, /* 953 */ + -2.708938804361575e-02, /* 954 */ + 9.977197421745680e-01, /* 955 */ + 2.996050367095970e-02, /* 956 */ + -7.217063375677063e-03, /* 957 */ + 6.695998941820378e-04, /* 958 */ + }, { + -5.408957158454127e-04, /* 959 */ + 7.103207852892033e-03, /* 960 */ + -2.991911957829145e-02, /* 961 */ + 9.971853780718406e-01, /* 962 */ + 3.346289133771520e-02, /* 963 */ + -8.065299986741607e-03, /* 964 */ + 7.553223639105668e-04, /* 965 */ + }, { + -5.840254343440151e-04, /* 966 */ + 7.758961154800972e-03, /* 967 */ + -3.271256089395777e-02, /* 968 */ + 9.965949909798753e-01, /* 969 */ + 3.699943441381923e-02, /* 970 */ + -8.922550316664762e-03, /* 971 */ + 8.433675427328470e-04, /* 972 */ + }, { + -6.252595583054893e-04, /* 973 */ + 8.404676461295810e-03, /* 974 */ + -3.546965901797275e-02, /* 975 */ + 9.959486485725322e-01, /* 976 */ + 4.056997198534163e-02, /* 977 */ + -9.788737033346933e-03, /* 978 */ + 9.337493607755644e-04, /* 979 */ + }, { + -6.646237106617679e-04, /* 980 */ + 9.040332527994370e-03, /* 981 */ + -3.819036602615411e-02, /* 982 */ + 9.952464249282380e-01, /* 983 */ + 4.417433797463730e-02, /* 984 */ + -1.066378013594614e-02, /* 985 */ + 1.026481066856231e-03, /* 986 */ + }, { + -7.021438899556850e-04, /* 987 */ + 9.665910500473909e-03, /* 988 */ + -4.087463902843420e-02, /* 989 */ + 9.944884005189432e-01, /* 990 */ + 4.781236114450060e-02, /* 991 */ + -1.154759694823357e-02, /* 992 */ + 1.121575216197463e-03, /* 993 */ + }, { + -7.378464564734607e-04, /* 994 */ + 1.028139389716235e-02, /* 995 */ + -4.352244015375482e-02, /* 996 */ + 9.936746621981263e-01, /* 997 */ + 5.148386510315437e-02, /* 998 */ + -1.244010211244157e-02, /* 999 */ + 1.219043658260877e-03, /* 1000 */ + }, { + -7.717581183646440e-04, /* 1001 */ + 1.088676859185900e-02, /* 1002 */ + -4.613373653420693e-02, /* 1003 */ + 9.928053031878483e-01, /* 1004 */ + 5.518866831007525e-02, /* 1005 */ + -1.334120758360962e-02, /* 1006 */ + 1.318897524704578e-03, /* 1007 */ + }, { + -8.039059177537709e-04, /* 1008 */ + 1.148202279588897e-02, /* 1009 */ + -4.870850028841950e-02, /* 1010 */ + 9.918804230648608e-01, /* 1011 */ + 5.892658408265536e-02, /* 1012 */ + -1.425082262442998e-02, /* 1013 */ + 1.421147217468663e-03, /* 1014 */ + }, { + -8.343172168478687e-04, /* 1015 */ + 1.206714703989862e-02, /* 1016 */ + -5.124670850420399e-02, /* 1017 */ + 9.909001277457663e-01, /* 1018 */ + 6.269742060370384e-02, /* 1019 */ + -1.516885380059888e-02, /* 1020 */ + 1.525802396992804e-03, /* 1021 */ + }, { + -8.630196840440353e-04, /* 1022 */ + 1.264213415529629e-02, /* 1023 */ + -5.374834322045772e-02, /* 1024 */ + 9.898645294712379e-01, /* 1025 */ + 6.650098092978608e-02, /* 1026 */ + -1.609520497667476e-02, /* 1027 */ + 1.632871970570320e-03, /* 1028 */ + }, { + -8.900412800411573e-04, /* 1029 */ + 1.320697925534576e-02, /* 1030 */ + -5.621339140833288e-02, /* 1031 */ + 9.887737467892981e-01, /* 1032 */ + 7.033706300040359e-02, /* 1033 */ + -1.702977731244917e-02, /* 1034 */ + 1.742364080842615e-03, /* 1035 */ + }, { + -9.154102439599487e-04, /* 1036 */ + 1.376167971591675e-02, /* 1037 */ + -5.864184495167603e-02, /* 1038 */ + 9.876279045376605e-01, /* 1039 */ + 7.420545964801417e-02, /* 1040 */ + -1.797246925983164e-02, /* 1041 */ + 1.854286094438386e-03, /* 1042 */ + }, { + -9.391550794753652e-04, /* 1043 */ + 1.430623515590003e-02, /* 1044 */ + -6.103370062674356e-02, /* 1045 */ + 9.864271338251378e-01, /* 1046 */ + 7.810595860889319e-02, /* 1047 */ + -1.892317656025402e-02, /* 1048 */ + 1.968644590761548e-03, /* 1049 */ + }, { + -9.613045409655687e-04, /* 1050 */ + 1.484064741729164e-02, /* 1051 */ + -6.338896008119892e-02, /* 1052 */ + 9.851715720121177e-01, /* 1053 */ + 8.203834253483586e-02, /* 1054 */ + -1.988179224259554e-02, /* 1055 */ + 2.085445350932246e-03, /* 1056 */ + }, { + -9.818876196813738e-04, /* 1057 */ + 1.536492054495343e-02, /* 1058 */ + -6.570762981239750e-02, /* 1059 */ + 9.838613626901140e-01, /* 1060 */ + 8.600238900570113e-02, /* 1061 */ + -2.084820662163316e-02, /* 1062 */ + 2.204693346884742e-03, /* 1063 */ + }, { + -1.000933529940309e-03, /* 1064 */ + 1.587906076605559e-02, /* 1065 */ + -6.798972114496460e-02, /* 1066 */ + 9.824966556603907e-01, /* 1067 */ + 8.999787054279679e-02, /* 1068 */ + -2.182230729701992e-02, /* 1069 */ + 2.326392730626404e-03, /* 1070 */ + }, { + -1.018471695349289e-03, /* 1071 */ + 1.638307646920682e-02, /* 1072 */ + -7.023525020767289e-02, /* 1073 */ + 9.810776069116667e-01, /* 1074 */ + 9.402455462310588e-02, /* 1075 */ + -2.280397915279337e-02, /* 1076 */ + 2.450546823661987e-03, /* 1077 */ + }, { + -1.034531735059858e-03, /* 1078 */ + 1.687697818327941e-02, /* 1079 */ + -7.244423790962552e-02, /* 1080 */ + 9.796043785969055e-01, /* 1081 */ + 9.808220369435408e-02, /* 1082 */ + -2.379310435741876e-02, /* 1083 */ + 2.577158106586916e-03, /* 1084 */ + }, { + -1.049143450059986e-03, /* 1085 */ + 1.736077855593420e-02, /* 1086 */ + -7.461670991575037e-02, /* 1087 */ + 9.780771390091908e-01, /* 1088 */ + 1.021705751909168e-01, /* 1089 */ + -2.478956236436741e-02, /* 1090 */ + 2.706228208853840e-03, /* 1091 */ + }, { + -1.062336809506350e-03, /* 1092 */ + 1.783449233185286e-02, /* 1093 */ + -7.675269662161305e-02, /* 1094 */ + 9.764960625566944e-01, /* 1095 */ + 1.062894215505674e-01, /* 1096 */ + -2.579322991323553e-02, /* 1097 */ + 2.837757898716223e-03, /* 1098 */ + }, { + -1.074141937101013e-03, /* 1099 */ + 1.829813633068257e-02, /* 1100 */ + -7.885223312755388e-02, /* 1101 */ + 9.748613297367402e-01, /* 1102 */ + 1.104384902320638e-01, /* 1103 */ + -2.680398103140338e-02, /* 1104 */ + 2.971747073353185e-03, /* 1105 */ + }, { + -1.084589097516370e-03, /* 1106 */ + 1.875172942470069e-02, /* 1107 */ + -8.091535921215573e-02, /* 1108 */ + 9.731731271089683e-01, /* 1109 */ + 1.146175237335723e-01, /* 1110 */ + -2.782168703623990e-02, /* 1111 */ + 3.108194749179169e-03, /* 1112 */ + }, { + -1.093708682872210e-03, /* 1113 */ + 1.919529251620448e-02, /* 1114 */ + -8.294211930504999e-02, /* 1115 */ + 9.714316472676046e-01, /* 1116 */ + 1.188262596119302e-01, /* 1117 */ + -2.884621653785256e-02, /* 1118 */ + 3.247099052342608e-03, /* 1119 */ + }, { + -1.101531199268716e-03, /* 1120 */ + 1.962884851463336e-02, /* 1121 */ + -8.493256245906648e-02, /* 1122 */ + 9.696370888128401e-01, /* 1123 */ + 1.230644305027427e-01, /* 1124 */ + -2.987743544238687e-02, /* 1125 */ + 3.388457209417186e-03, /* 1126 */ + }, { + -1.108087253379183e-03, /* 1127 */ + 2.005242231342951e-02, /* 1128 */ + -8.688674232173496e-02, /* 1129 */ + 9.677896563213259e-01, /* 1130 */ + 1.273317641413156e-01, /* 1131 */ + -3.091520695587671e-02, /* 1132 */ + 3.532265538289867e-03, /* 1133 */ + }, { + -1.113407539106230e-03, /* 1134 */ + 2.046604076664311e-02, /* 1135 */ + -8.880471710614442e-02, /* 1136 */ + 9.658895603157857e-01, /* 1137 */ + 1.316279833844193e-01, /* 1138 */ + -3.195939158864695e-02, /* 1139 */ + 3.678519439249078e-03, /* 1140 */ + }, { + -1.117522824305172e-03, /* 1141 */ + 2.086973266528943e-02, /* 1142 */ + -9.068654956116820e-02, /* 1143 */ + 9.639370172337561e-01, /* 1144 */ + 1.359528062328858e-01, /* 1145 */ + -3.300984716027202e-02, /* 1146 */ + 3.827213386276986e-03, /* 1147 */ + }, { + -1.120463937578314e-03, /* 1148 */ + 2.126352871346326e-02, /* 1149 */ + -9.253230694106100e-02, /* 1150 */ + 9.619322493954562e-01, /* 1151 */ + 1.403059458550339e-01, /* 1152 */ + -3.406642880509015e-02, /* 1153 */ + 3.978340918549750e-03, /* 1154 */ + }, { + -1.122261755143747e-03, /* 1155 */ + 2.164746150421816e-02, /* 1156 */ + -9.434206097443590e-02, /* 1157 */ + 9.598754849707929e-01, /* 1158 */ + 1.446871106109222e-01, /* 1159 */ + -3.512898897827717e-02, /* 1160 */ + 4.131894632149082e-03, /* 1161 */ + }, { + -1.122947187782295e-03, /* 1162 */ + 2.202156549521620e-02, /* 1163 */ + -9.611588783262809e-02, /* 1164 */ + 9.577669579455091e-01, /* 1165 */ + 1.490960040774270e-01, /* 1166 */ + -3.619737746247935e-02, /* 1167 */ + 4.287866171989037e-03, /* 1168 */ + }, { + -1.122551167866203e-03, /* 1169 */ + 2.238587698415551e-02, /* 1170 */ + -9.785386809745342e-02, /* 1171 */ + 9.556069080864779e-01, /* 1172 */ + 1.535323250741442e-01, /* 1173 */ + -3.727144137500895e-02, /* 1174 */ + 4.446246223961389e-03, /* 1175 */ + }, { + -1.121104636473070e-03, /* 1176 */ + 2.274043408398138e-02, /* 1177 */ + -9.955608672836883e-02, /* 1178 */ + 9.533955809061508e-01, /* 1179 */ + 1.579957676901108e-01, /* 1180 */ + -3.835102517560170e-02, /* 1181 */ + 4.607024507303565e-03, /* 1182 */ + }, { + -1.118638530588553e-03, /* 1183 */ + 2.308527669788855e-02, /* 1184 */ + -1.012226330290420e-01, /* 1185 */ + 9.511332276261677e-01, /* 1186 */ + 1.624860213113444e-01, /* 1187 */ + -3.943597067473980e-02, /* 1188 */ + 4.770189767192131e-03, /* 1189 */ + }, { + -1.115183770401296e-03, /* 1190 */ + 2.342044649412001e-02, /* 1191 */ + -1.028536006133390e-01, /* 1192 */ + 9.488201051401287e-01, /* 1193 */ + 1.670027706491982e-01, /* 1194 */ + -4.052611704253924e-02, /* 1195 */ + 4.935729767565705e-03, /* 1196 */ + }, { + -1.110771246693530e-03, /* 1197 */ + 2.374598688057016e-02, /* 1198 */ + -1.044490873707362e-01, /* 1199 */ + 9.464564759755407e-01, /* 1200 */ + 1.715456957695286e-01, /* 1201 */ + -4.162130081820462e-02, /* 1202 */ + 5.103631284180478e-03, /* 1203 */ + }, { + -1.105431808330667e-03, /* 1204 */ + 2.406194297919816e-02, /* 1205 */ + -1.060091954311663e-01, /* 1206 */ + 9.440426082549445e-01, /* 1207 */ + 1.761144721226713e-01, /* 1208 */ + -4.272135592005138e-02, /* 1209 */ + 5.273880097902076e-03, /* 1210 */ + }, { + -1.099196249853307e-03, /* 1211 */ + 2.436836160025825e-02, /* 1212 */ + -1.075340311293039e-01, /* 1213 */ + 9.415787756562223e-01, /* 1214 */ + 1.807087705742239e-01, /* 1215 */ + -4.382611365609584e-02, /* 1216 */ + 5.446460988236670e-03, /* 1217 */ + }, { + -1.092095299174878e-03, /* 1218 */ + 2.466529121635406e-02, /* 1219 */ + -1.090237049683018e-01, /* 1220 */ + 9.390652573721028e-01, /* 1221 */ + 1.853282574366316e-01, /* 1222 */ + -4.493540273521546e-02, /* 1223 */ + 5.621357727104864e-03, /* 1224 */ + }, { + -1.084159605388224e-03, /* 1225 */ + 2.495278193632289e-02, /* 1226 */ + -1.104783315829824e-01, /* 1227 */ + 9.365023380688623e-01, /* 1228 */ + 1.899725945015709e-01, /* 1229 */ + -4.604904927887769e-02, /* 1230 */ + 5.798553072861818e-03, /* 1231 */ + }, { + -1.075419726684316e-03, /* 1232 */ + 2.523088547895752e-02, /* 1233 */ + -1.118980297024964e-01, /* 1234 */ + 9.338903078442332e-01, /* 1235 */ + 1.946414390731309e-01, /* 1236 */ + -4.716687683344033e-02, /* 1237 */ + 5.978028764566336e-03, /* 1238 */ + }, { + -1.065906118386268e-03, /* 1239 */ + 2.549965514657118e-02, /* 1240 */ + -1.132829221124536e-01, /* 1241 */ + 9.312294621845263e-01, /* 1242 */ + 1.993344440017834e-01, /* 1243 */ + -4.828870638302148e-02, /* 1244 */ + 6.159765516502505e-03, /* 1245 */ + }, { + -1.055649121101780e-03, /* 1246 */ + 2.575914579841332e-02, /* 1247 */ + -1.146331356165368e-01, /* 1248 */ + 9.285201019209733e-01, /* 1249 */ + 2.040512577191446e-01, /* 1250 */ + -4.941435636294156e-02, /* 1251 */ + 6.343743012956645e-03, /* 1252 */ + }, { + -1.044678948997064e-03, /* 1253 */ + 2.600941382394202e-02, /* 1254 */ + -1.159488009976052e-01, /* 1255 */ + 9.257625331852983e-01, /* 1256 */ + 2.087915242735169e-01, /* 1257 */ + -5.054364267373540e-02, /* 1258 */ + 6.529939903253073e-03, /* 1259 */ + }, { + -1.033025678195301e-03, /* 1260 */ + 2.625051711596052e-02, /* 1261 */ + -1.172300529782974e-01, /* 1262 */ + 9.229570673645258e-01, /* 1263 */ + 2.135548833662130e-01, /* 1264 */ + -5.167637869573667e-02, /* 1265 */ + 6.718333797051150e-03, /* 1266 */ + }, { + -1.020719235302619e-03, /* 1267 */ + 2.648251504362370e-02, /* 1268 */ + -1.184770301811412e-01, /* 1269 */ + 9.201040210550299e-01, /* 1270 */ + 2.183409703886530e-01, /* 1271 */ + -5.281237530423245e-02, /* 1272 */ + 6.908901259906972e-03, /* 1273 */ + }, { + -1.007789386064493e-03, /* 1274 */ + 2.670546842532187e-02, /* 1275 */ + -1.196898750881803e-01, /* 1276 */ + 9.172037160158394e-01, /* 1277 */ + 2.231494164602341e-01, /* 1278 */ + -5.395144088518943e-02, /* 1279 */ + 7.101617809102288e-03, /* 1280 */ + }, { + -9.942657241554641e-04, /* 1281 */ + 2.691943950144822e-02, /* 1282 */ + -1.208687340001249e-01, /* 1283 */ + 9.142564791211991e-01, /* 1284 */ + 2.279798484669641e-01, /* 1285 */ + -5.509338135155091e-02, /* 1286 */ + 7.296457909743918e-03, /* 1287 */ + }, { + -9.801776601050557e-04, /* 1288 */ + 2.712449190705641e-02, /* 1289 */ + -1.220137569950366e-01, /* 1290 */ + 9.112626423123993e-01, /* 1291 */ + 2.328318891008579e-01, /* 1292 */ + -5.623800016010321e-02, /* 1293 */ + 7.493394971135955e-03, /* 1294 */ + }, { + -9.655544103626029e-04, /* 1295 */ + 2.732069064441559e-02, /* 1296 */ + -1.231250978865554e-01, /* 1297 */ + 9.082225425488828e-01, /* 1298 */ + 2.377051569000891e-01, /* 1299 */ + -5.738509832891316e-02, /* 1300 */ + 7.692401343427691e-03, /* 1301 */ + }, { + -9.504249865037702e-04, /* 1302 */ + 2.750810205546858e-02, /* 1303 */ + -1.242029141816779e-01, /* 1304 */ + 9.051365217586359e-01, /* 1305 */ + 2.425992662898926e-01, /* 1306 */ + -5.853447445533320e-02, /* 1307 */ + 7.893448314540197e-03, /* 1308 */ + }, { + -9.348181845814541e-04, /* 1309 */ + 2.768679379420070e-02, /* 1310 */ + -1.252473670380959e-01, /* 1311 */ + 9.020049267878701e-01, /* 1312 */ + 2.475138276242130e-01, /* 1313 */ + -5.968592473457642e-02, /* 1314 */ + 8.096506107373698e-03, /* 1315 */ + }, { + -9.187625746236699e-04, /* 1316 */ + 2.785683479892532e-02, /* 1317 */ + -1.262586212211042e-01, /* 1318 */ + 8.988281093500092e-01, /* 1319 */ + 2.524484472280946e-01, /* 1320 */ + -6.083924297885757e-02, /* 1321 */ + 8.301543877298635e-03, /* 1322 */ + }, { + -9.022864902810273e-04, /* 1323 */ + 2.801829526449300e-02, /* 1324 */ + -1.272368450600864e-01, /* 1325 */ + 8.956064259739822e-01, /* 1326 */ + 2.574027274408040e-01, /* 1327 */ + -6.199422063710185e-02, /* 1328 */ + 8.508529709932666e-03, /* 1329 */ + }, { + -8.854180186263388e-04, /* 1330 */ + 2.817124661443064e-02, /* 1331 */ + -1.281822104045887e-01, /* 1332 */ + 8.923402379518393e-01, /* 1333 */ + 2.623762666596838e-01, /* 1334 */ + -6.315064681521791e-02, /* 1335 */ + 8.717430619206452e-03, /* 1336 */ + }, { + -8.681849901087664e-04, /* 1337 */ + 2.831576147301751e-02, /* 1338 */ + -1.290948925799897e-01, /* 1339 */ + 8.890299112856920e-01, /* 1340 */ + 2.673686593847286e-01, /* 1341 */ + -6.430830829693616e-02, /* 1342 */ + 8.928212545720045e-03, /* 1343 */ + }, { + -8.506149686650558e-04, /* 1344 */ + 2.845191363730427e-02, /* 1345 */ + -1.299750703427760e-01, /* 1346 */ + 8.856758166339943e-01, /* 1347 */ + 2.723794962638790e-01, /* 1348 */ + -6.546698956520861e-02, /* 1349 */ + 9.140840355392437e-03, /* 1350 */ + }, { + -8.327352419900550e-04, /* 1351 */ + 2.857977804908198e-02, /* 1352 */ + -1.308229258354336e-01, /* 1353 */ + 8.822783292571661e-01, /* 1354 */ + 2.774083641390264e-01, /* 1355 */ + -6.662647282417096e-02, /* 1356 */ + 9.355277838406877e-03, /* 1357 */ + }, { + -8.145728119690237e-04, /* 1358 */ + 2.869943076680737e-02, /* 1359 */ + -1.316386445409620e-01, /* 1360 */ + 8.788378289625756e-01, /* 1361 */ + 2.824548460927230e-01, /* 1362 */ + -6.778653802166430e-02, /* 1363 */ + 9.571487708453395e-03, /* 1364 */ + }, { + -7.961543852738185e-04, /* 1365 */ + 2.881094893749082e-02, /* 1366 */ + -1.324224152370240e-01, /* 1367 */ + 8.753547000488832e-01, /* 1368 */ + 2.875185214955909e-01, /* 1369 */ + -6.894696287231404e-02, /* 1370 */ + 9.789431602271271e-03, /* 1371 */ + }, { + -7.775063641252938e-04, /* 1372 */ + 2.891441076855387e-02, /* 1373 */ + -1.331744299497369e-01, /* 1374 */ + 8.718293312497631e-01, /* 1375 */ + 2.925989660544231e-01, /* 1376 */ + -7.010752288116626e-02, /* 1377 */ + 1.000907007949311e-02, /* 1378 */ + }, { + -7.586548372239626e-04, /* 1379 */ + 2.900989549966232e-02, /* 1380 */ + -1.338948839071171e-01, /* 1381 */ + 8.682621156770067e-01, /* 1382 */ + 2.976957518609703e-01, /* 1383 */ + -7.126799136787691e-02, /* 1384 */ + 1.023036262279292e-02, /* 1385 */ + }, { + -7.396255708510786e-04, /* 1386 */ + 2.909748337454149e-02, /* 1387 */ + -1.345839754921861e-01, /* 1388 */ + 8.646534507630197e-01, /* 1389 */ + 3.028084474414053e-01, /* 1390 */ + -7.242813949145467e-02, /* 1391 */ + 1.045326763833955e-02, /* 1392 */ + }, { + -7.204440001421441e-04, /* 1393 */ + 2.917725561278015e-02, /* 1394 */ + -1.352419061957486e-01, /* 1395 */ + 8.610037382027232e-01, /* 1396 */ + 3.079366178064618e-01, /* 1397 */ + -7.358773627555218e-02, /* 1398 */ + 1.067774245655789e-02, /* 1399 */ + }, { + -7.011352205348302e-04, /* 1400 */ + 2.924929438162952e-02, /* 1401 */ + -1.358688805688508e-01, /* 1402 */ + 8.573133838948686e-01, /* 1403 */ + 3.130798245022357e-01, /* 1404 */ + -7.474654863430663e-02, /* 1405 */ + 1.090374333319907e-02, /* 1406 */ + }, { + -6.817239793932266e-04, /* 1407 */ + 2.931368276780341e-02, /* 1408 */ + -1.364651061749298e-01, /* 1409 */ + 8.535827978827749e-01, /* 1410 */ + 3.182376256616457e-01, /* 1411 */ + -7.590434139872407e-02, /* 1412 */ + 1.113122545072202e-02, /* 1413 */ + }, { + -6.622346678102776e-04, /* 1414 */ + 2.937050474928606e-02, /* 1415 */ + -1.370307935416629e-01, /* 1416 */ + 8.498123942944995e-01, /* 1417 */ + 3.234095760565429e-01, /* 1418 */ + -7.706087734360774e-02, /* 1419 */ + 1.136014291998739e-02, /* 1420 */ + }, { + -6.426913125902382e-04, /* 1421 */ + 2.941984516715388e-02, /* 1422 */ + -1.375661561125266e-01, /* 1423 */ + 8.460025912824529e-01, /* 1424 */ + 3.285952271504655e-01, /* 1425 */ + -7.821591721502538e-02, /* 1426 */ + 1.159044878226562e-02, /* 1427 */ + }, { + -6.231175684128511e-04, /* 1428 */ + 2.946178969741759e-02, /* 1429 */ + -1.380714101980755e-01, /* 1430 */ + 8.421538109624669e-01, /* 1431 */ + 3.337941271520264e-01, /* 1432 */ + -7.936921975831460e-02, /* 1433 */ + 1.182209501156100e-02, /* 1434 */ + }, { + -6.035367101809801e-04, /* 1435 */ + 2.949642482289040e-02, /* 1436 */ + -1.385467749269494e-01, /* 1437 */ + 8.382664793523271e-01, /* 1438 */ + 3.390058210689310e-01, /* 1439 */ + -8.052054174662254e-02, /* 1440 */ + 1.205503251725260e-02, /* 1441 */ + }, { + -5.839716255532901e-04, /* 1442 */ + 2.952383780508903e-02, /* 1443 */ + -1.389924721966199e-01, /* 1444 */ + 8.343410263097801e-01, /* 1445 */ + 3.442298507626138e-01, /* 1446 */ + -8.166963800997633e-02, /* 1447 */ + 1.228921114705370e-02, /* 1448 */ + }, { + -5.644448076635753e-04, /* 1449 */ + 2.954411665617338e-02, /* 1450 */ + -1.394087266238854e-01, /* 1451 */ + 8.303778854700258e-01, /* 1452 */ + 3.494657550034874e-01, /* 1453 */ + -8.281626146488272e-02, /* 1454 */ + 1.252457969029095e-02, /* 1455 */ + }, { + -5.449783480282249e-04, /* 1456 */ + 2.955735011093100e-02, /* 1457 */ + -1.397957654951237e-01, /* 1458 */ + 8.263774941827043e-01, /* 1459 */ + 3.547130695267950e-01, /* 1460 */ + -8.396016314445182e-02, /* 1461 */ + 1.276108588150466e-02, /* 1462 */ + }, { + -5.255939296432825e-04, /* 1463 */ + 2.956362759881255e-02, /* 1464 */ + -1.401538187163136e-01, /* 1465 */ + 8.223402934483920e-01, /* 1466 */ + 3.599713270890607e-01, /* 1467 */ + -8.510109222904338e-02, /* 1468 */ + 1.299867640437090e-02, /* 1469 */ + }, { + -5.063128202724816e-04, /* 1470 */ + 2.956303921602418e-02, /* 1471 */ + -1.404831187628331e-01, /* 1472 */ + 8.182667278546104e-01, /* 1473 */ + 3.652400575251253e-01, /* 1474 */ + -8.623879607743025e-02, /* 1475 */ + 1.323729689594689e-02, /* 1476 */ + }, { + -4.871558659276469e-04, /* 1477 */ + 2.955567569768274e-02, /* 1478 */ + -1.407839006290460e-01, /* 1479 */ + 8.141572455113678e-01, /* 1480 */ + 3.705187878057628e-01, /* 1481 */ + -8.737302025847754e-02, /* 1482 */ + 1.347689195124034e-02, /* 1483 */ + }, { + -4.681434845426153e-04, /* 1484 */ + 2.954162839003991e-02, /* 1485 */ + -1.410564017776856e-01, /* 1486 */ + 8.100122979862356e-01, /* 1487 */ + 3.758070420958670e-01, /* 1488 */ + -8.850350858333141e-02, /* 1489 */ + 1.371740512810407e-02, /* 1490 */ + }, { + -4.492956598419907e-04, /* 1491 */ + 2.952098922278122e-02, /* 1492 */ + -1.413008620890449e-01, /* 1493 */ + 8.058323402389781e-01, /* 1494 */ + 3.811043418132007e-01, /* 1495 */ + -8.963000313811628e-02, /* 1496 */ + 1.395877895245621e-02, /* 1497 */ + }, { + -4.306319354058826e-04, /* 1498 */ + 2.949385068140553e-02, /* 1499 */ + -1.415175238099844e-01, /* 1500 */ + 8.016178305557399e-01, /* 1501 */ + 3.864102056876984e-01, /* 1502 */ + -9.075224431713386e-02, /* 1503 */ + 1.420095492382687e-02, /* 1504 */ + }, { + -4.121714089315939e-04, /* 1505 */ + 2.946030577969092e-02, /* 1506 */ + -1.417066315027663e-01, /* 1507 */ + 7.973692304828067e-01, /* 1508 */ + 3.917241498213130e-01, /* 1509 */ + -9.186997085656183e-02, /* 1510 */ + 1.444387352123256e-02, /* 1511 */ + }, { + -3.939327266934505e-04, /* 1512 */ + 2.942044803225294e-02, /* 1513 */ + -1.418684319937252e-01, /* 1514 */ + 7.930870047599514e-01, /* 1515 */ + 3.970456877483993e-01, /* 1516 */ + -9.298291986864780e-02, /* 1517 */ + 1.468747420937781e-02, /* 1518 */ + }, { + -3.759340782016456e-04, /* 1519 */ + 2.937437142720069e-02, /* 1520 */ + -1.420031743217853e-01, /* 1521 */ + 7.887716212533704e-01, /* 1522 */ + 4.023743304966226e-01, /* 1523 */ + -9.409082687639277e-02, /* 1524 */ + 1.493169544518567e-02, /* 1525 */ + }, { + -3.581931910609877e-04, /* 1526 */ + 2.932217039889637e-02, /* 1527 */ + -1.421111096868331e-01, /* 1528 */ + 7.844235508882289e-01, /* 1529 */ + 4.077095866483865e-01, /* 1530 */ + -9.519342584872197e-02, /* 1531 */ + 1.517647468465644e-02, /* 1532 */ + }, { + -3.407273260304884e-04, /* 1533 */ + 2.926393980082415e-02, /* 1534 */ + -1.421924913979572e-01, /* 1535 */ + 7.800432675808223e-01, /* 1536 */ + 4.130509624027673e-01, /* 1537 */ + -9.629044923613632e-02, /* 1538 */ + 1.542174839005620e-02, /* 1539 */ + }, { + -3.235532722844501e-04, /* 1540 */ + 2.919977487857369e-02, /* 1541 */ + -1.422475748215626e-01, /* 1542 */ + 7.756312481703652e-01, /* 1543 */ + 4.183979616379481e-01, /* 1544 */ + -9.738162800684201e-02, /* 1545 */ + 1.566745203743416e-02, /* 1546 */ + }, { + -3.066873428758958e-04, /* 1547 */ + 2.912977124294393e-02, /* 1548 */ + -1.422766173293711e-01, /* 1549 */ + 7.711879723504229e-01, /* 1550 */ + 4.237500859741424e-01, /* 1551 */ + -9.846669168335127e-02, /* 1552 */ + 1.591352012446994e-02, /* 1553 */ + }, { + -2.901453704029424e-04, /* 1554 */ + 2.905402484317260e-02, /* 1555 */ + -1.422798782463173e-01, /* 1556 */ + 7.667139225999916e-01, /* 1557 */ + 4.291068348369969e-01, /* 1558 */ + -9.954536837955191e-02, /* 1559 */ + 1.615988617865028e-02, /* 1560 */ + }, { + -2.739427028786713e-04, /* 1561 */ + 2.897263194029685e-02, /* 1562 */ + -1.422576187983489e-01, /* 1563 */ + 7.622095841142430e-01, /* 1564 */ + 4.344677055214644e-01, /* 1565 */ + -1.006173848382378e-01, /* 1566 */ + 1.640648276577594e-02, /* 1567 */ + }, { + -2.580941998051696e-04, /* 1568 */ + 2.888568908065016e-02, /* 1569 */ + -1.422101020601427e-01, /* 1570 */ + 7.576754447349440e-01, /* 1571 */ + 4.398321932561375e-01, /* 1572 */ + -1.016824664690990e-01, /* 1573 */ + 1.665324149879781e-02, /* 1574 */ + }, { + -2.426142284520608e-04, /* 1575 */ + 2.879329306950119e-02, /* 1576 */ + -1.421375929027446e-01, /* 1577 */ + 7.531119948805626e-01, /* 1578 */ + 4.451997912680325e-01, /* 1579 */ + -1.027403373871614e-01, /* 1580 */ + 1.690009304698279e-02, /* 1581 */ + }, { + -2.275166603400509e-04, /* 1582 */ + 2.869554094483946e-02, /* 1583 */ + -1.420403579411441e-01, /* 1584 */ + 7.485197274760710e-01, /* 1585 */ + 4.505699908478140e-01, /* 1586 */ + -1.037907204516760e-01, /* 1587 */ + 1.714696714540915e-02, /* 1588 */ + }, { + -2.128148679298167e-04, /* 1589 */ + 2.859252995131316e-02, /* 1590 */ + -1.419186654817930e-01, /* 1591 */ + 7.438991378824603e-01, /* 1592 */ + 4.559422814154492e-01, /* 1593 */ + -1.048333373054486e-01, /* 1594 */ + 1.739379260479069e-02, /* 1595 */ + }, { + -1.985217215164836e-04, /* 1596 */ + 2.848435751432410e-02, /* 1597 */ + -1.417727854700776e-01, /* 1598 */ + 7.392507238259753e-01, /* 1599 */ + 4.613161505862837e-01, /* 1600 */ + -1.058679084146052e-01, /* 1601 */ + 1.764049732162978e-02, /* 1602 */ + }, { + -1.846495863300355e-04, /* 1603 */ + 2.837112121428517e-02, /* 1604 */ + -1.416029894377547e-01, /* 1605 */ + 7.345749853270843e-01, /* 1606 */ + 4.666910842375266e-01, /* 1607 */ + -1.068941531087891e-01, /* 1608 */ + 1.788700828869835e-02, /* 1609 */ + }, { + -1.712103198416544e-04, /* 1610 */ + 2.825291876104482e-02, /* 1611 */ + -1.414095504503600e-01, /* 1612 */ + 7.298724246291924e-01, /* 1613 */ + 4.720665665751356e-01, /* 1614 */ + -1.079117896217818e-01, /* 1615 */ + 1.813325160584695e-02, /* 1616 */ + }, { + -1.582152692763085e-04, /* 1617 */ + 2.812984796848375e-02, /* 1618 */ + -1.411927430545998e-01, /* 1619 */ + 7.251435461271148e-01, /* 1620 */ + 4.774420802010910e-01, /* 1621 */ + -1.089205351325436e-01, /* 1622 */ + 1.837915249114045e-02, /* 1623 */ + }, { + -1.456752693314445e-04, /* 1624 */ + 2.800200672928881e-02, /* 1625 */ + -1.409528432257348e-01, /* 1626 */ + 7.203888562953171e-01, /* 1627 */ + 4.828171061810496e-01, /* 1628 */ + -1.099201058066671e-01, /* 1629 */ + 1.862463529232039e-02, /* 1630 */ + }, { + -1.336006401019428e-04, /* 1631 */ + 2.786949298990845e-02, /* 1632 */ + -1.406901283149657e-01, /* 1633 */ + 7.156088636159380e-01, /* 1634 */ + 4.881911241123659e-01, /* 1635 */ + -1.109102168382377e-01, /* 1636 */ + 1.886962349859249e-02, /* 1637 */ + }, { + -1.220011852110915e-04, /* 1638 */ + 2.773240472569498e-02, /* 1639 */ + -1.404048769968304e-01, /* 1640 */ + 7.108040785066047e-01, /* 1641 */ + 4.935636121924722e-01, /* 1642 */ + -1.118905824920952e-01, /* 1643 */ + 1.911403975273935e-02, /* 1644 */ + }, { + -1.108861901476344e-04, /* 1645 */ + 2.759083991623796e-02, /* 1646 */ + -1.400973692166212e-01, /* 1647 */ + 7.059750132480542e-01, /* 1648 */ + 4.989340472876037e-01, /* 1649 */ + -1.128609161464899e-01, /* 1650 */ + 1.935780586355643e-02, /* 1651 */ + }, { + -1.002644208085391e-04, /* 1652 */ + 2.744489652089330e-02, /* 1653 */ + -1.397678861378340e-01, /* 1654 */ + 7.011221819115717e-01, /* 1655 */ + 5.043019050018616e-01, /* 1656 */ + -1.138209303361282e-01, /* 1657 */ + 1.960084281861067e-02, /* 1658 */ + }, { + -9.014412224732896e-05, /* 1659 */ + 2.729467245451285e-02, /* 1660 */ + -1.394167100896560e-01, /* 1661 */ + 6.962461002862578e-01, /* 1662 */ + 5.096666597466010e-01, /* 1663 */ + -1.147703367955984e-01, /* 1664 */ + 1.984307079732106e-02, /* 1665 */ + }, { + -8.053301762762107e-05, /* 1666 */ + 2.714026556337870e-02, /* 1667 */ + -1.390441245145027e-01, /* 1668 */ + 6.913472858061384e-01, /* 1669 */ + 5.150277848101327e-01, /* 1670 */ + -1.157088465031742e-01, /* 1671 */ + 2.008440918435909e-02, /* 1672 */ + }, { + -7.143830738156712e-05, /* 1673 */ + 2.698177360134680e-02, /* 1674 */ + -1.386504139156142e-01, /* 1675 */ + 6.864262574771270e-01, /* 1676 */ + 5.203847524277286e-01, /* 1677 */ + -1.166361697249849e-01, /* 1678 */ + 2.032477658336845e-02, /* 1679 */ + }, { + -6.286666857267136e-05, /* 1680 */ + 2.681929420620386e-02, /* 1681 */ + -1.382358638047184e-01, /* 1682 */ + 6.814835358038533e-01, /* 1683 */ + 5.257370338519197e-01, /* 1684 */ + -1.175520160595501e-01, /* 1685 */ + 2.056409083100217e-02, /* 1686 */ + }, { + -5.482425446254296e-05, /* 1687 */ + 2.665292487624222e-02, /* 1688 */ + -1.378007606497726e-01, /* 1689 */ + 6.765196427163698e-01, /* 1690 */ + 5.310840994230748e-01, /* 1691 */ + -1.184560944826678e-01, /* 1692 */ + 2.080226901127631e-02, /* 1693 */ + }, { + -4.731669428110093e-05, /* 1694 */ + 2.648276294705649e-02, /* 1695 */ + -1.373453918227895e-01, /* 1696 */ + 6.715351014967476e-01, /* 1697 */ + 5.364254186402488e-01, /* 1698 */ + -1.193481133926526e-01, /* 1699 */ + 2.103922747023789e-02, /* 1700 */ + }, { + -4.034909319948082e-05, /* 1701 */ + 2.630890556856650e-02, /* 1702 */ + -1.368700455477614e-01, /* 1703 */ + 6.665304367055752e-01, /* 1704 */ + 5.417604602322907e-01, /* 1705 */ + -1.202277806559141e-01, /* 1706 */ + 2.127488183094605e-02, /* 1707 */ + }, { + -3.392603250514114e-05, /* 1708 */ + 2.613144968226988e-02, /* 1709 */ + -1.363750108486864e-01, /* 1710 */ + 6.615061741083712e-01, /* 1711 */ + 5.470886922291980e-01, /* 1712 */ + -1.210948036528713e-01, /* 1713 */ + 2.150914700876424e-02, /* 1714 */ + }, { + -2.805156997831240e-05, /* 1715 */ + 2.595049199872917e-02, /* 1716 */ + -1.358605774977103e-01, /* 1717 */ + 6.564628406019232e-01, /* 1718 */ + 5.524095820337069e-01, /* 1719 */ + -1.219488893241928e-01, /* 1720 */ + 2.174193722696236e-02, /* 1721 */ + }, { + -2.272924046911682e-05, /* 1722 */ + 2.576612897529643e-02, /* 1723 */ + -1.353270359633906e-01, /* 1724 */ + 6.514009641405650e-01, /* 1725 */ + 5.577225964931086e-01, /* 1726 */ + -1.227897442173580e-01, /* 1727 */ + 2.197316603262602e-02, /* 1728 */ + }, { + -1.796205667443242e-05, /* 1729 */ + 2.557845679407980e-02, /* 1730 */ + -1.347746773590917e-01, /* 1731 */ + 6.463210736624057e-01, /* 1732 */ + 5.630272019712755e-01, /* 1733 */ + -1.236170745335310e-01, /* 1734 */ + 2.220274631287172e-02, /* 1735 */ + }, { + -1.375251011368080e-05, /* 1736 */ + 2.538757134015553e-02, /* 1737 */ + -1.342037933915221e-01, /* 1738 */ + 6.412236990155202e-01, /* 1739 */ + 5.683228644208926e-01, /* 1740 */ + -1.244305861747393e-01, /* 1741 */ + 2.243059031136537e-02, /* 1742 */ + }, { + -1.010257230258042e-05, /* 1743 */ + 2.519356818002896e-02, /* 1744 */ + -1.336146763094198e-01, /* 1745 */ + 6.361093708841161e-01, /* 1746 */ + 5.736090494558752e-01, /* 1747 */ + -1.252299847913509e-01, /* 1748 */ + 2.265660964514263e-02, /* 1749 */ + }, { + -7.013696123785544e-06, /* 1750 */ + 2.499654254034837e-02, /* 1751 */ + -1.330076188523975e-01, /* 1752 */ + 6.309786207146856e-01, /* 1753 */ + 5.788852224239674e-01, /* 1754 */ + -1.260149758298408e-01, /* 1755 */ + 2.288071532172811e-02, /* 1756 */ + }, { + -4.486817393493708e-06, /* 1757 */ + 2.479658928687493e-02, /* 1758 */ + -1.323829141999531e-01, /* 1759 */ + 6.258319806421593e-01, /* 1760 */ + 5.841508484795060e-01, /* 1761 */ + -1.267852645808413e-01, /* 1762 */ + 2.310281775655174e-02, /* 1763 */ + }, { + -2.522356622734990e-06, /* 1764 */ + 2.459380290371239e-02, /* 1765 */ + -1.317408559206581e-01, /* 1766 */ + 6.206699834160688e-01, /* 1767 */ + 5.894053926563386e-01, /* 1768 */ + -1.275405562274653e-01, /* 1769 */ + 2.332282679065955e-02, /* 1770 */ + }, { + -1.120220972351802e-06, /* 1771 */ + 2.438827747279956e-02, /* 1772 */ + -1.310817379215285e-01, /* 1773 */ + 6.154931623267351e-01, /* 1774 */ + 5.946483199408858e-01, /* 1775 */ + -1.282805558938982e-01, /* 1776 */ + 2.354065170871666e-02, /* 1777 */ + }, { + -2.798064002790454e-07, /* 1778 */ + 2.418010665366927e-02, /* 1779 */ + -1.304058543975903e-01, /* 1780 */ + 6.103020511314905e-01, /* 1781 */ + 5.998790953453343e-01, /* 1782 */ + -1.290049686942476e-01, /* 1783 */ + 2.375620125729980e-02, /* 1784 */ + }, { + -0.000000000000000e+00, /* 1785 */ + 2.396938366347660e-02, /* 1786 */ + -1.297134997816453e-01, /* 1787 */ + 6.050971839809485e-01, /* 1788 */ + 6.050971839809485e-01, /* 1789 */ + -1.297134997816453e-01, /* 1790 */ + 2.396938366347660e-02, /* 1791 */ + } +}; diff --git a/libs/fluidsynth/glib.c b/libs/fluidsynth/glib.c new file mode 100644 index 00000000000..cd938a4fb6b --- /dev/null +++ b/libs/fluidsynth/glib.c @@ -0,0 +1,106 @@ +/* + * 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 + +int g_vsnprintf( char *buffer, size_t size, const char *format, va_list args ) +{ + int ret = _vsnprintf( buffer, size - 1, format, args ); + if (ret >= 0 && ret < size) buffer[ret] = 0; + else buffer[0] = 0; + return ret; +} + +int g_snprintf( char *buffer, size_t size, const char *format, ... ) +{ + va_list args; + int ret; + + va_start( args, format ); + ret = g_vsnprintf( buffer, size, format, args ); + va_end( args ); + + return ret; +} + +double g_get_monotonic_time(void) +{ + static LARGE_INTEGER frequency = {0}; + LARGE_INTEGER counter; + + if (!frequency.QuadPart) QueryPerformanceFrequency( &frequency ); + QueryPerformanceCounter( &counter ); + + return counter.QuadPart * 1000000.0 / frequency.QuadPart; /* time in micros */ +} + +void g_usleep( unsigned int micros ) +{ + Sleep( micros / 1000 ); +} + +static DWORD CALLBACK g_thread_wrapper( void *args ) +{ + GThread *thread = args; + gpointer ret = thread->func( thread->data ); + if (!InterlockedDecrement( &thread->ref )) free( thread ); + return (UINT_PTR)ret; +} + +GThread *g_thread_try_new( const char *name, GThreadFunc func, gpointer data, GError **err ) +{ + GThread *thread; + + if (!(thread = calloc( 1, sizeof(*thread) ))) return NULL; + thread->ref = 2; + thread->func = func; + thread->data = data; + + if (!(thread->handle = CreateThread( NULL, 0, g_thread_wrapper, thread, 0, NULL ))) + { + free( thread ); + return NULL; + } + + return thread; +} + +void g_thread_unref( GThread *thread ) +{ + CloseHandle( thread->handle ); + if (!InterlockedDecrement( &thread->ref )) free( thread ); +} + +void g_thread_join( GThread *thread ) +{ + WaitForSingleObject( thread->handle, INFINITE ); + g_thread_unref( thread ); +} + +void g_clear_error( GError **error ) +{ + *error = NULL; +} + +int g_file_test( const char *path, int test ) +{ + DWORD attrs = GetFileAttributesA( path ); + if (test == G_FILE_TEST_EXISTS) return attrs != INVALID_FILE_ATTRIBUTES; + if (test == G_FILE_TEST_IS_REGULAR) return attrs == FILE_ATTRIBUTE_NORMAL; + return 0; +} diff --git a/libs/fluidsynth/glib.h b/libs/fluidsynth/glib.h new file mode 100644 index 00000000000..722173d825a --- /dev/null +++ b/libs/fluidsynth/glib.h @@ -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 +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define GLIB_CHECK_VERSION(x, y, z) ((x) < GLIB_MAJOR_VERSION || ((x) == GLIB_MAJOR_VERSION && (y) <= GLIB_MINOR_VERSION)) +#define GLIB_MAJOR_VERSION 2 +#define GLIB_MINOR_VERSION 54 + +#define G_BYTE_ORDER 1234 +#define G_BIG_ENDIAN 4321 +#define GINT32_FROM_LE(val) ((INT32)(val)) +#define GINT16_FROM_LE(val) ((INT16)(val)) + +#define G_UNLIKELY(expr) (expr) +#define G_LIKELY(expr) (expr) + +#define g_return_val_if_fail( cond, val ) do { if (!(cond)) return (val); } while (0) + +typedef void *gpointer; +typedef gpointer (*GThreadFunc)( gpointer data ); + +typedef struct _stat64 GStatBuf; +#define g_stat _stat64 + +typedef struct +{ + const char *message; +} GError; + +typedef struct +{ + LONG ref; + HANDLE handle; + GThreadFunc func; + gpointer data; +} GThread; + +extern int g_vsnprintf( char *buffer, size_t size, const char *format, va_list args ) __WINE_CRT_PRINTF_ATTR(3, 0); +extern int g_snprintf( char *buffer, size_t size, const char *format, ... ) __WINE_CRT_PRINTF_ATTR(3, 4); + +extern double g_get_monotonic_time(void); +extern void g_usleep( unsigned int micros ); + +extern GThread *g_thread_try_new( const char *name, GThreadFunc func, gpointer data, GError **err ); +extern void g_thread_unref( GThread *thread ); +extern void g_thread_join( GThread *thread ); +extern void g_clear_error( GError **error ); + +#define G_FILE_TEST_EXISTS 1 +#define G_FILE_TEST_IS_REGULAR 2 + +extern int g_file_test( const char *path, int test ); + +#define g_new( type, count ) calloc( (count), sizeof(type) ) +static void g_free( void *ptr ) { free( ptr ); } + +typedef SRWLOCK GMutex; +static void g_mutex_init( GMutex *mutex ) {} +static void g_mutex_clear( GMutex *mutex ) {} +static void g_mutex_lock( GMutex *mutex ) { AcquireSRWLockExclusive( mutex ); } +static void g_mutex_unlock( GMutex *mutex ) { ReleaseSRWLockExclusive( mutex ); } + +typedef CRITICAL_SECTION GRecMutex; +static void g_rec_mutex_init( GRecMutex *mutex ) { InitializeCriticalSection( mutex ); } +static void g_rec_mutex_clear( GRecMutex *mutex ) { DeleteCriticalSection( mutex ); } +static void g_rec_mutex_lock( GRecMutex *mutex ) { EnterCriticalSection( mutex ); } +static void g_rec_mutex_unlock( GRecMutex *mutex ) { LeaveCriticalSection( mutex ); } + +typedef CONDITION_VARIABLE GCond; +static void g_cond_init( GCond *cond ) {} +static void g_cond_clear( GCond *cond ) {} +static void g_cond_signal( GCond *cond ) { WakeConditionVariable( cond ); } +static void g_cond_broadcast( GCond *cond ) { WakeAllConditionVariable( cond ); } +static void g_cond_wait( GCond *cond, GMutex *mutex ) { SleepConditionVariableSRW( cond, mutex, INFINITE, 0 ); } + +static void g_atomic_int_inc( int *ptr ) { InterlockedIncrement( (LONG *)ptr ); } +static int g_atomic_int_add( int *ptr, int val ) { return InterlockedAdd( (LONG *)ptr, val ) - 1; } +static int g_atomic_int_get( int *ptr ) { return ReadAcquire( (LONG *)ptr ); } +static void g_atomic_int_set( int *ptr, int val ) { InterlockedExchange( (LONG *)ptr, val ); } +static int g_atomic_int_dec_and_test( int *ptr, int val ) { return !InterlockedAdd( (LONG *)ptr, -val ); } +static int g_atomic_int_compare_and_exchange( int *ptr, int cmp, int val ) { return InterlockedCompareExchange( (LONG *)ptr, val, cmp ) == cmp; } diff --git a/libs/fluidsynth/include/fluidsynth.h b/libs/fluidsynth/include/fluidsynth.h new file mode 100644 index 00000000000..5577072ccaf --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth.h @@ -0,0 +1,125 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_H +#define _FLUIDSYNTH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BUILD_SHARED_LIBS 0 + +#if (BUILD_SHARED_LIBS == 0) + #define FLUIDSYNTH_API // building static lib? no visibility control then +#elif defined(WIN32) + #if defined(FLUIDSYNTH_NOT_A_DLL) + #define FLUIDSYNTH_API + #elif defined(FLUIDSYNTH_DLL_EXPORTS) + #define FLUIDSYNTH_API __declspec(dllexport) + #else + #define FLUIDSYNTH_API __declspec(dllimport) + #endif + +#elif defined(MACOS9) +#define FLUIDSYNTH_API __declspec(export) + +#elif defined(__OS2__) +#define FLUIDSYNTH_API __declspec(dllexport) + +#elif defined(__GNUC__) +#define FLUIDSYNTH_API __attribute__ ((visibility ("default"))) + +#else +#define FLUIDSYNTH_API + +#endif + +#if 1 /* Wine-specific code */ +# define FLUID_DEPRECATED +#else /* Wine-specific code */ +#if defined(__GNUC__) || defined(__clang__) +# define FLUID_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) && _MSC_VER > 1200 +# define FLUID_DEPRECATED __declspec(deprecated) +#else +# define FLUID_DEPRECATED +#endif +#endif /* Wine-specific code */ + + +/** + * @file fluidsynth.h + * @brief FluidSynth is a real-time synthesizer designed for SoundFont(R) files. + * + * This is the header of the fluidsynth library and contains the + * synthesizer's public API. + * + * Depending on how you want to use or extend the synthesizer you + * will need different API functions. You probably do not need all + * of them. Here is what you might want to do: + * + * - Embedded synthesizer: create a new synthesizer and send MIDI + * events to it. The sound goes directly to the audio output of + * your system. + * + * - Plugin synthesizer: create a synthesizer and send MIDI events + * but pull the audio back into your application. + * + * - SoundFont plugin: create a new type of "SoundFont" and allow + * the synthesizer to load your type of SoundFonts. + * + * - MIDI input: Create a MIDI handler to read the MIDI input on your + * machine and send the MIDI events directly to the synthesizer. + * + * - MIDI files: Open MIDI files and send the MIDI events to the + * synthesizer. + * + * - Command lines: You can send textual commands to the synthesizer. + * + * SoundFont(R) is a registered trademark of E-mu Systems, Inc. + */ + +#include "fluidsynth/types.h" +#include "fluidsynth/settings.h" +#include "fluidsynth/synth.h" +#include "fluidsynth/shell.h" +#include "fluidsynth/sfont.h" +#include "fluidsynth/audio.h" +#include "fluidsynth/event.h" +#include "fluidsynth/midi.h" +#include "fluidsynth/seq.h" +#include "fluidsynth/seqbind.h" +#include "fluidsynth/log.h" +#include "fluidsynth/misc.h" +#include "fluidsynth/mod.h" +#include "fluidsynth/gen.h" +#include "fluidsynth/voice.h" +#include "fluidsynth/version.h" +#include "fluidsynth/ladspa.h" + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_H */ diff --git a/libs/fluidsynth/include/fluidsynth/audio.h b/libs/fluidsynth/include/fluidsynth/audio.h new file mode 100644 index 00000000000..1c12ff792cc --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/audio.h @@ -0,0 +1,155 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_AUDIO_H +#define _FLUIDSYNTH_AUDIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup audio_output Audio Output + * + * Functions for managing audio drivers and file renderers. + * + * The file renderer is used for fast rendering of MIDI files to + * audio files. The audio drivers are a high-level interface to + * connect the synthesizer with external audio sinks or to render + * real-time audio to files. + */ + +/** + * @defgroup audio_driver Audio Driver + * @ingroup audio_output + * + * Functions for managing audio drivers. + * + * Defines functions for creating audio driver output. Use + * new_fluid_audio_driver() to create a new audio driver for a given synth + * and configuration settings. + * + * The function new_fluid_audio_driver2() can be + * used if custom audio processing is desired before the audio is sent to the + * audio driver (although it is not as efficient). + * + * @sa @ref CreatingAudioDriver + * + * @{ + */ + +/** + * Callback function type used with new_fluid_audio_driver2() to allow for + * custom user audio processing before the audio is sent to the driver. + * + * @param data The user data parameter as passed to new_fluid_audio_driver2(). + * @param len Count of audio frames to synthesize. + * @param nfx Count of arrays in \c fx. + * @param fx Array of buffers to store effects audio to. Buffers may alias with buffers of \c out. + * @param nout Count of arrays in \c out. + * @param out Array of buffers to store (dry) audio to. Buffers may alias with buffers of \c fx. + * @return Should return #FLUID_OK on success, #FLUID_FAILED if an error occurred. + * + * This function is responsible for rendering audio to the buffers. + * The buffers passed to this function are allocated and owned by the respective + * audio driver and are only valid during that specific call (do not cache them). + * The buffers have already been zeroed-out. + * For further details please refer to fluid_synth_process(). + * + * @parblock + * @note Whereas fluid_synth_process() allows aliasing buffers, there is the guarantee that @p out + * and @p fx buffers provided by fluidsynth's audio drivers never alias. This prevents downstream + * applications from e.g. applying a custom effect accidentally to the same buffer multiple times. + * @endparblock + * + * @parblock + * @note Also note that the Jack driver is currently the only driver that has dedicated @p fx buffers + * (but only if \setting{audio_jack_multi} is true). All other drivers do not provide @p fx buffers. + * In this case, users are encouraged to mix the effects into the provided dry buffers when calling + * fluid_synth_process(). + * @code{.cpp} +int myCallback(void *, int len, int nfx, float *fx[], int nout, float *out[]) +{ + int ret; + if(nfx == 0) + { + float *fxb[4] = {out[0], out[1], out[0], out[1]}; + ret = fluid_synth_process(synth, len, sizeof(fxb) / sizeof(fxb[0]), fxb, nout, out); + } + else + { + ret = fluid_synth_process(synth, len, nfx, fx, nout, out); + } + // ... client-code ... + return ret; +} + * @endcode + * For other possible use-cases refer to \ref fluidsynth_process.c . + * @endparblock + */ +typedef int (*fluid_audio_func_t)(void *data, int len, + int nfx, float *fx[], + int nout, float *out[]); + +/** @startlifecycle{Audio Driver} */ +FLUIDSYNTH_API fluid_audio_driver_t *new_fluid_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); + +FLUIDSYNTH_API fluid_audio_driver_t *new_fluid_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, + void *data); + +FLUIDSYNTH_API void delete_fluid_audio_driver(fluid_audio_driver_t *driver); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_audio_driver_register(const char **adrivers); +/** @} */ + +/** + * @defgroup file_renderer File Renderer + * @ingroup audio_output + * + * Functions for managing file renderers and triggering the rendering. + * + * The file renderer is only used to render a MIDI file to audio as fast + * as possible. Please see \ref FileRenderer for a full example. + * + * If you are looking for a way to write audio generated + * from real-time events (for example from an external sequencer or a MIDI controller) to a file, + * please have a look at the \c file \ref audio_driver instead. + * + * + * @{ + */ + +/** @startlifecycle{File Renderer} */ +FLUIDSYNTH_API fluid_file_renderer_t *new_fluid_file_renderer(fluid_synth_t *synth); +FLUIDSYNTH_API void delete_fluid_file_renderer(fluid_file_renderer_t *dev); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_file_renderer_process_block(fluid_file_renderer_t *dev); +FLUIDSYNTH_API int fluid_file_set_encoding_quality(fluid_file_renderer_t *dev, double q); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_AUDIO_H */ diff --git a/libs/fluidsynth/include/fluidsynth/event.h b/libs/fluidsynth/include/fluidsynth/event.h new file mode 100644 index 00000000000..e46084f0e40 --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/event.h @@ -0,0 +1,143 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_EVENT_H +#define _FLUIDSYNTH_EVENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup sequencer_events Sequencer Events + * @ingroup sequencer + * + * Create, modify, query and destroy sequencer events. + * + * @{ + */ + +/** + * Sequencer event type enumeration. + */ +enum fluid_seq_event_type +{ + FLUID_SEQ_NOTE = 0, /**< Note event with duration */ + FLUID_SEQ_NOTEON, /**< Note on event */ + FLUID_SEQ_NOTEOFF, /**< Note off event */ + FLUID_SEQ_ALLSOUNDSOFF, /**< All sounds off event */ + FLUID_SEQ_ALLNOTESOFF, /**< All notes off event */ + FLUID_SEQ_BANKSELECT, /**< Bank select message */ + FLUID_SEQ_PROGRAMCHANGE, /**< Program change message */ + FLUID_SEQ_PROGRAMSELECT, /**< Program select message */ + FLUID_SEQ_PITCHBEND, /**< Pitch bend message */ + FLUID_SEQ_PITCHWHEELSENS, /**< Pitch wheel sensitivity set message @since 1.1.0 was misspelled previously */ + FLUID_SEQ_MODULATION, /**< Modulation controller event */ + FLUID_SEQ_SUSTAIN, /**< Sustain controller event */ + FLUID_SEQ_CONTROLCHANGE, /**< MIDI control change event */ + FLUID_SEQ_PAN, /**< Stereo pan set event */ + FLUID_SEQ_VOLUME, /**< Volume set event */ + FLUID_SEQ_REVERBSEND, /**< Reverb send set event */ + FLUID_SEQ_CHORUSSEND, /**< Chorus send set event */ + FLUID_SEQ_TIMER, /**< Timer event (useful for giving a callback at a certain time) */ + FLUID_SEQ_CHANNELPRESSURE, /**< Channel aftertouch event @since 1.1.0 */ + FLUID_SEQ_KEYPRESSURE, /**< Polyphonic aftertouch event @since 2.0.0 */ + FLUID_SEQ_SYSTEMRESET, /**< System reset event @since 1.1.0 */ + FLUID_SEQ_UNREGISTERING, /**< Called when a sequencer client is being unregistered. @since 1.1.0 */ + FLUID_SEQ_SCALE, /**< Sets a new time scale for the sequencer @since 2.2.0 */ + FLUID_SEQ_LASTEVENT /**< @internal Defines the count of events enums @warning This symbol + is not part of the public API and ABI stability guarantee and + may change at any time! */ +}; + +/* Event alloc/free */ +/** @startlifecycle{Sequencer Event} */ +FLUIDSYNTH_API fluid_event_t *new_fluid_event(void); +FLUIDSYNTH_API void delete_fluid_event(fluid_event_t *evt); +/** @endlifecycle */ + +/* Initializing events */ +FLUIDSYNTH_API void fluid_event_set_source(fluid_event_t *evt, fluid_seq_id_t src); +FLUIDSYNTH_API void fluid_event_set_dest(fluid_event_t *evt, fluid_seq_id_t dest); + +/* Timer events */ +FLUIDSYNTH_API void fluid_event_timer(fluid_event_t *evt, void *data); + +/* Note events */ +FLUIDSYNTH_API void fluid_event_note(fluid_event_t *evt, int channel, + short key, short vel, + unsigned int duration); + +FLUIDSYNTH_API void fluid_event_noteon(fluid_event_t *evt, int channel, short key, short vel); +FLUIDSYNTH_API void fluid_event_noteoff(fluid_event_t *evt, int channel, short key); +FLUIDSYNTH_API void fluid_event_all_sounds_off(fluid_event_t *evt, int channel); +FLUIDSYNTH_API void fluid_event_all_notes_off(fluid_event_t *evt, int channel); + +/* Instrument selection */ +FLUIDSYNTH_API void fluid_event_bank_select(fluid_event_t *evt, int channel, short bank_num); +FLUIDSYNTH_API void fluid_event_program_change(fluid_event_t *evt, int channel, int preset_num); +FLUIDSYNTH_API void fluid_event_program_select(fluid_event_t *evt, int channel, unsigned int sfont_id, short bank_num, short preset_num); + +/* Real-time generic instrument controllers */ +FLUIDSYNTH_API +void fluid_event_control_change(fluid_event_t *evt, int channel, short control, int val); + +/* Real-time instrument controllers shortcuts */ +FLUIDSYNTH_API void fluid_event_pitch_bend(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_modulation(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_sustain(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_pan(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_volume(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_reverb_send(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t *evt, int channel, int val); + +FLUIDSYNTH_API void fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, int val); +FLUIDSYNTH_API void fluid_event_channel_pressure(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_system_reset(fluid_event_t *evt); + +/* Only when unregistering clients */ +FLUIDSYNTH_API void fluid_event_unregistering(fluid_event_t *evt); + +FLUIDSYNTH_API void fluid_event_scale(fluid_event_t *evt, double new_scale); +FLUIDSYNTH_API int fluid_event_from_midi_event(fluid_event_t *, const fluid_midi_event_t *); + +/* Accessing event data */ +FLUIDSYNTH_API int fluid_event_get_type(fluid_event_t *evt); +FLUIDSYNTH_API fluid_seq_id_t fluid_event_get_source(fluid_event_t *evt); +FLUIDSYNTH_API fluid_seq_id_t fluid_event_get_dest(fluid_event_t *evt); +FLUIDSYNTH_API int fluid_event_get_channel(fluid_event_t *evt); +FLUIDSYNTH_API short fluid_event_get_key(fluid_event_t *evt); +FLUIDSYNTH_API short fluid_event_get_velocity(fluid_event_t *evt); +FLUIDSYNTH_API short fluid_event_get_control(fluid_event_t *evt); +FLUIDSYNTH_API int fluid_event_get_value(fluid_event_t *evt); +FLUIDSYNTH_API int fluid_event_get_program(fluid_event_t *evt); +FLUIDSYNTH_API void *fluid_event_get_data(fluid_event_t *evt); +FLUIDSYNTH_API unsigned int fluid_event_get_duration(fluid_event_t *evt); +FLUIDSYNTH_API short fluid_event_get_bank(fluid_event_t *evt); +FLUIDSYNTH_API int fluid_event_get_pitch(fluid_event_t *evt); +FLUIDSYNTH_API double fluid_event_get_scale(fluid_event_t *evt); +FLUIDSYNTH_API unsigned int fluid_event_get_sfont_id(fluid_event_t *evt); +/** @} */ + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_EVENT_H */ diff --git a/libs/fluidsynth/include/fluidsynth/gen.h b/libs/fluidsynth/include/fluidsynth/gen.h new file mode 100644 index 00000000000..5a61e0bffda --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/gen.h @@ -0,0 +1,133 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_GEN_H +#define _FLUIDSYNTH_GEN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup generators SoundFont Generators + * @ingroup soundfonts + * + * Functions and defines for SoundFont generator effects. + * + * @{ + */ + +/** + * Generator (effect) numbers (Soundfont 2.01 specifications section 8.1.3) + */ +enum fluid_gen_type +{ + GEN_STARTADDROFS, /**< Sample start address offset (0-32767) */ + GEN_ENDADDROFS, /**< Sample end address offset (-32767-0) */ + GEN_STARTLOOPADDROFS, /**< Sample loop start address offset (-32767-32767) */ + GEN_ENDLOOPADDROFS, /**< Sample loop end address offset (-32767-32767) */ + GEN_STARTADDRCOARSEOFS, /**< Sample start address coarse offset (X 32768) */ + GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */ + GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */ + GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */ + GEN_FILTERFC, /**< Filter cutoff */ + GEN_FILTERQ, /**< Filter Q */ + GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */ + GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */ + GEN_ENDADDRCOARSEOFS, /**< Sample end address coarse offset (X 32768) */ + GEN_MODLFOTOVOL, /**< Modulation LFO to volume */ + GEN_UNUSED1, /**< Unused */ + GEN_CHORUSSEND, /**< Chorus send amount */ + GEN_REVERBSEND, /**< Reverb send amount */ + GEN_PAN, /**< Stereo panning */ + GEN_UNUSED2, /**< Unused */ + GEN_UNUSED3, /**< Unused */ + GEN_UNUSED4, /**< Unused */ + GEN_MODLFODELAY, /**< Modulation LFO delay */ + GEN_MODLFOFREQ, /**< Modulation LFO frequency */ + GEN_VIBLFODELAY, /**< Vibrato LFO delay */ + GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */ + GEN_MODENVDELAY, /**< Modulation envelope delay */ + GEN_MODENVATTACK, /**< Modulation envelope attack */ + GEN_MODENVHOLD, /**< Modulation envelope hold */ + GEN_MODENVDECAY, /**< Modulation envelope decay */ + GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */ + GEN_MODENVRELEASE, /**< Modulation envelope release */ + GEN_KEYTOMODENVHOLD, /**< Key to modulation envelope hold */ + GEN_KEYTOMODENVDECAY, /**< Key to modulation envelope decay */ + GEN_VOLENVDELAY, /**< Volume envelope delay */ + GEN_VOLENVATTACK, /**< Volume envelope attack */ + GEN_VOLENVHOLD, /**< Volume envelope hold */ + GEN_VOLENVDECAY, /**< Volume envelope decay */ + GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */ + GEN_VOLENVRELEASE, /**< Volume envelope release */ + GEN_KEYTOVOLENVHOLD, /**< Key to volume envelope hold */ + GEN_KEYTOVOLENVDECAY, /**< Key to volume envelope decay */ + GEN_INSTRUMENT, /**< Instrument ID (shouldn't be set by user) */ + GEN_RESERVED1, /**< Reserved */ + GEN_KEYRANGE, /**< MIDI note range */ + GEN_VELRANGE, /**< MIDI velocity range */ + GEN_STARTLOOPADDRCOARSEOFS, /**< Sample start loop address coarse offset (X 32768) */ + GEN_KEYNUM, /**< Fixed MIDI note number */ + GEN_VELOCITY, /**< Fixed MIDI velocity value */ + GEN_ATTENUATION, /**< Initial volume attenuation */ + GEN_RESERVED2, /**< Reserved */ + GEN_ENDLOOPADDRCOARSEOFS, /**< Sample end loop address coarse offset (X 32768) */ + GEN_COARSETUNE, /**< Coarse tuning */ + GEN_FINETUNE, /**< Fine tuning */ + GEN_SAMPLEID, /**< Sample ID (shouldn't be set by user) */ + GEN_SAMPLEMODE, /**< Sample mode flags */ + GEN_RESERVED3, /**< Reserved */ + GEN_SCALETUNE, /**< Scale tuning */ + GEN_EXCLUSIVECLASS, /**< Exclusive class number */ + GEN_OVERRIDEROOTKEY, /**< Sample root note override */ + + /** + * Initial Pitch + * + * @note This is not "standard" SoundFont generator, because it is not + * mentioned in the list of generators in the SF2 specifications. + * It is used by FluidSynth internally to compute the nominal pitch of + * a note on note-on event. By nature it shouldn't be allowed to be modulated, + * however the specification defines a default modulator having "Initial Pitch" + * as destination (cf. SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch). + * Thus it is impossible to cancel this default modulator, which would be required + * to let the MIDI Pitch Wheel controller modulate a different generator. + * In order to provide this flexibility, FluidSynth >= 2.1.0 uses a default modulator + * "Pitch Wheel to Fine Tune", rather than Initial Pitch. The same "compromise" can + * be found on the Audigy 2 ZS for instance. + */ + GEN_PITCH, + + GEN_CUSTOM_BALANCE, /**< Balance @note Not a real SoundFont generator */ + /* non-standard generator for an additional custom high- or low-pass filter */ + GEN_CUSTOM_FILTERFC, /**< Custom filter cutoff frequency */ + GEN_CUSTOM_FILTERQ, /**< Custom filter Q */ + + GEN_LAST /**< @internal Value defines the count of generators (#fluid_gen_type) + @warning This symbol is not part of the public API and ABI + stability guarantee and may change at any time! */ +}; +/** @} */ + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_GEN_H */ diff --git a/libs/fluidsynth/include/fluidsynth/ladspa.h b/libs/fluidsynth/include/fluidsynth/ladspa.h new file mode 100644 index 00000000000..24cbd6f650f --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/ladspa.h @@ -0,0 +1,68 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_LADSPA_H +#define _FLUIDSYNTH_LADSPA_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup ladspa Effect - LADSPA + * @ingroup synth + * + * Functions for configuring the LADSPA effects unit + * + * This header defines useful functions for programmatically manipulating the ladspa + * effects unit of the synth that can be retrieved via fluid_synth_get_ladspa_fx(). + * + * Using any of those functions requires fluidsynth to be compiled with LADSPA support. + * Else all of those functions are useless dummies. + * + * @{ + */ +FLUIDSYNTH_API int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx); +FLUIDSYNTH_API int fluid_ladspa_activate(fluid_ladspa_fx_t *fx); +FLUIDSYNTH_API int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx); +FLUIDSYNTH_API int fluid_ladspa_reset(fluid_ladspa_fx_t *fx); +FLUIDSYNTH_API int fluid_ladspa_check(fluid_ladspa_fx_t *fx, char *err, int err_size); + +FLUIDSYNTH_API int fluid_ladspa_host_port_exists(fluid_ladspa_fx_t *fx, const char *name); + +FLUIDSYNTH_API int fluid_ladspa_add_buffer(fluid_ladspa_fx_t *fx, const char *name); +FLUIDSYNTH_API int fluid_ladspa_buffer_exists(fluid_ladspa_fx_t *fx, const char *name); + +FLUIDSYNTH_API int fluid_ladspa_add_effect(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *lib_name, const char *plugin_name); +FLUIDSYNTH_API int fluid_ladspa_effect_can_mix(fluid_ladspa_fx_t *fx, const char *name); +FLUIDSYNTH_API int fluid_ladspa_effect_set_mix(fluid_ladspa_fx_t *fx, const char *name, int mix, float gain); +FLUIDSYNTH_API int fluid_ladspa_effect_port_exists(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name); +FLUIDSYNTH_API int fluid_ladspa_effect_set_control(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *port_name, float val); +FLUIDSYNTH_API int fluid_ladspa_effect_link(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *port_name, const char *name); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_LADSPA_H */ diff --git a/libs/fluidsynth/include/fluidsynth/log.h b/libs/fluidsynth/include/fluidsynth/log.h new file mode 100644 index 00000000000..ab5911e011c --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/log.h @@ -0,0 +1,97 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_LOG_H +#define _FLUIDSYNTH_LOG_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup logging Logging + * + * Logging interface + * + * The default logging function of the fluidsynth prints its messages to the + * stderr. The synthesizer uses five level of messages: #FLUID_PANIC, + * #FLUID_ERR, #FLUID_WARN, #FLUID_INFO, and #FLUID_DBG. + * + * A client application can install a new log function to handle the messages + * differently. In the following example, the application sets a callback + * function to display #FLUID_PANIC messages in a dialog, and ignores all other + * messages by setting the log function to NULL: + * + * @code + * fluid_set_log_function(FLUID_PANIC, show_dialog, (void*) root_window); + * fluid_set_log_function(FLUID_ERR, NULL, NULL); + * fluid_set_log_function(FLUID_WARN, NULL, NULL); + * fluid_set_log_function(FLUID_DBG, NULL, NULL); + * @endcode + * + * @note The logging configuration is global and not tied to a specific + * synthesizer instance. That means that all synthesizer instances created in + * the same process share the same logging configuration. + * + * @{ + */ + +/** + * FluidSynth log levels. + */ +enum fluid_log_level +{ + FLUID_PANIC, /**< The synth can't function correctly any more */ + FLUID_ERR, /**< Serious error occurred */ + FLUID_WARN, /**< Warning */ + FLUID_INFO, /**< Verbose informational messages */ + FLUID_DBG, /**< Debugging messages */ + LAST_LOG_LEVEL /**< @internal This symbol is not part of the public API and ABI + stability guarantee and may change at any time! */ +}; + +/** + * Log function handler callback type used by fluid_set_log_function(). + * + * @param level Log level (#fluid_log_level) + * @param message Log message text + * @param data User data pointer supplied to fluid_set_log_function(). + */ +typedef void (*fluid_log_function_t)(int level, const char *message, void *data); + +FLUIDSYNTH_API +fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void *data); + +FLUIDSYNTH_API void fluid_default_log_function(int level, const char *message, void *data); + +FLUIDSYNTH_API int fluid_log(int level, const char *fmt, ...) +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +__attribute__ ((format (printf, 2, 3))) +#endif +; +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_LOG_H */ diff --git a/libs/fluidsynth/include/fluidsynth/midi.h b/libs/fluidsynth/include/fluidsynth/midi.h new file mode 100644 index 00000000000..044eeebd9a7 --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/midi.h @@ -0,0 +1,295 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_MIDI_H +#define _FLUIDSYNTH_MIDI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup midi_input MIDI Input + * + * MIDI Input Subsystem + * + * There are multiple ways to send MIDI events to the synthesizer. They can come + * from MIDI files, from external MIDI sequencers or raw MIDI event sources, + * can be modified via MIDI routers and also generated manually. + * + * The interface connecting all sources and sinks of MIDI events in libfluidsynth + * is \ref handle_midi_event_func_t. + * + * @{ + */ + +/** + * Generic callback function for MIDI event handler. + * + * @param data User defined data pointer + * @param event The MIDI event + * @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * This callback is used to pass MIDI events + * - from \ref midi_player, \ref midi_router or \ref midi_driver + * - to \ref midi_router via fluid_midi_router_handle_midi_event() + * - or to \ref synth via fluid_synth_handle_midi_event(). + * + * Additionally, there is a translation layer to pass MIDI events to + * a \ref sequencer via fluid_sequencer_add_midi_event_to_buffer(). + */ +typedef int (*handle_midi_event_func_t)(void *data, fluid_midi_event_t *event); + +/** + * Generic callback function fired once by MIDI tick change. + * + * @param data User defined data pointer + * @param tick The current (zero-based) tick, which triggered the callback + * @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * This callback is fired at a constant rate depending on the current BPM and PPQ. + * e.g. for PPQ = 192 and BPM = 140 the callback is fired 192 * 140 times per minute (448/sec). + * + * It can be used to sync external elements with the beat, + * or stop / loop the song on a given tick. + * Ticks being BPM-dependent, you can manipulate values such as bars or beats, + * without having to care about BPM. + * + * For example, this callback loops the song whenever it reaches the 5th bar : + * + * @code{.cpp} +int handle_tick(void *data, int tick) +{ + fluid_player_t *player = (fluid_player_t *)data; + int ppq = 192; // From MIDI header + int beatsPerBar = 4; // From the song's time signature + int loopBar = 5; + int loopTick = (loopBar - 1) * ppq * beatsPerBar; + + if (tick == loopTick) + { + return fluid_player_seek(player, 0); + } + + return FLUID_OK; +} + * @endcode + */ +typedef int (*handle_midi_tick_func_t)(void *data, int tick); +/** @} */ + +/** + * @defgroup midi_events MIDI Events + * @ingroup midi_input + * + * Functions to create, modify, query and delete MIDI events. + * + * These functions are intended to be used in MIDI routers and other filtering + * and processing functions in the MIDI event path. If you want to simply + * send MIDI messages to the synthesizer, you can use the more convenient + * \ref midi_messages interface. + * + * @{ + */ +/** @startlifecycle{MIDI Event} */ +FLUIDSYNTH_API fluid_midi_event_t *new_fluid_midi_event(void); +FLUIDSYNTH_API void delete_fluid_midi_event(fluid_midi_event_t *event); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_midi_event_set_type(fluid_midi_event_t *evt, int type); +FLUIDSYNTH_API int fluid_midi_event_get_type(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_channel(fluid_midi_event_t *evt, int chan); +FLUIDSYNTH_API int fluid_midi_event_get_channel(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_get_key(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_key(fluid_midi_event_t *evt, int key); +FLUIDSYNTH_API int fluid_midi_event_get_velocity(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_velocity(fluid_midi_event_t *evt, int vel); +FLUIDSYNTH_API int fluid_midi_event_get_control(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_control(fluid_midi_event_t *evt, int ctrl); +FLUIDSYNTH_API int fluid_midi_event_get_value(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_value(fluid_midi_event_t *evt, int val); +FLUIDSYNTH_API int fluid_midi_event_get_program(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_program(fluid_midi_event_t *evt, int val); +FLUIDSYNTH_API int fluid_midi_event_get_pitch(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val); +FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, + int size, int dynamic); +FLUIDSYNTH_API int fluid_midi_event_set_text(fluid_midi_event_t *evt, + void *data, int size, int dynamic); +FLUIDSYNTH_API int fluid_midi_event_get_text(fluid_midi_event_t *evt, + void **data, int *size); +FLUIDSYNTH_API int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, + void *data, int size, int dynamic); +FLUIDSYNTH_API int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt, + void **data, int *size); +/** @} */ + +/** + * @defgroup midi_router MIDI Router + * @ingroup midi_input + * + * Rule based transformation and filtering of MIDI events. + * + * @{ + */ + +/** + * MIDI router rule type. + * + * @since 1.1.0 + */ +typedef enum +{ + FLUID_MIDI_ROUTER_RULE_NOTE, /**< MIDI note rule */ + FLUID_MIDI_ROUTER_RULE_CC, /**< MIDI controller rule */ + FLUID_MIDI_ROUTER_RULE_PROG_CHANGE, /**< MIDI program change rule */ + FLUID_MIDI_ROUTER_RULE_PITCH_BEND, /**< MIDI pitch bend rule */ + FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE, /**< MIDI channel pressure rule */ + FLUID_MIDI_ROUTER_RULE_KEY_PRESSURE, /**< MIDI key pressure rule */ + FLUID_MIDI_ROUTER_RULE_COUNT /**< @internal Total count of rule types. This symbol + is not part of the public API and ABI stability + guarantee and may change at any time!*/ +} fluid_midi_router_rule_type; + + +/** @startlifecycle{MIDI Router} */ +FLUIDSYNTH_API fluid_midi_router_t *new_fluid_midi_router(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *event_handler_data); +FLUIDSYNTH_API void delete_fluid_midi_router(fluid_midi_router_t *handler); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_midi_router_set_default_rules(fluid_midi_router_t *router); +FLUIDSYNTH_API int fluid_midi_router_clear_rules(fluid_midi_router_t *router); +FLUIDSYNTH_API int fluid_midi_router_add_rule(fluid_midi_router_t *router, + fluid_midi_router_rule_t *rule, int type); + + +/** @startlifecycle{MIDI Router Rule} */ +FLUIDSYNTH_API fluid_midi_router_rule_t *new_fluid_midi_router_rule(void); +FLUIDSYNTH_API void delete_fluid_midi_router_rule(fluid_midi_router_rule_t *rule); +/** @endlifecycle */ + +FLUIDSYNTH_API void fluid_midi_router_rule_set_chan(fluid_midi_router_rule_t *rule, + int min, int max, float mul, int add); +FLUIDSYNTH_API void fluid_midi_router_rule_set_param1(fluid_midi_router_rule_t *rule, + int min, int max, float mul, int add); +FLUIDSYNTH_API void fluid_midi_router_rule_set_param2(fluid_midi_router_rule_t *rule, + int min, int max, float mul, int add); +FLUIDSYNTH_API int fluid_midi_router_handle_midi_event(void *data, fluid_midi_event_t *event); +FLUIDSYNTH_API int fluid_midi_dump_prerouter(void *data, fluid_midi_event_t *event); +FLUIDSYNTH_API int fluid_midi_dump_postrouter(void *data, fluid_midi_event_t *event); +/** @} */ + +/** + * @defgroup midi_driver MIDI Driver + * @ingroup midi_input + * + * Functions for managing MIDI drivers. + * + * The available MIDI drivers depend on your platform. See \ref settings_midi for all + * available configuration options. + * + * To create a MIDI driver, you need to specify a source for the MIDI events to be + * forwarded to via the \ref fluid_midi_event_t callback. Normally this will be + * either a \ref midi_router via fluid_midi_router_handle_midi_event() or the synthesizer + * via fluid_synth_handle_midi_event(). + * + * But you can also write your own handler function that preprocesses the events and + * forwards them on to the router or synthesizer instead. + * + * @{ + */ + +/** @startlifecycle{MIDI Driver} */ +FLUIDSYNTH_API +fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *event_handler_data); + +FLUIDSYNTH_API void delete_fluid_midi_driver(fluid_midi_driver_t *driver); +/** @endlifecycle */ + +/** @} */ + +/** + * @defgroup midi_player MIDI File Player + * @ingroup midi_input + * + * Parse standard MIDI files and emit MIDI events. + * + * @{ + */ + +/** + * MIDI File Player status enum. + * @since 1.1.0 + */ +enum fluid_player_status +{ + FLUID_PLAYER_READY, /**< Player is ready */ + FLUID_PLAYER_PLAYING, /**< Player is currently playing */ + FLUID_PLAYER_STOPPING, /**< Player is stopping, but hasn't finished yet (currently unused) */ + FLUID_PLAYER_DONE /**< Player is finished playing */ +}; + +/** + * MIDI File Player tempo enum. + * @since 2.2.0 + */ +enum fluid_player_set_tempo_type +{ + FLUID_PLAYER_TEMPO_INTERNAL, /**< Use midi file tempo set in midi file (120 bpm by default). Multiplied by a factor */ + FLUID_PLAYER_TEMPO_EXTERNAL_BPM, /**< Set player tempo in bpm, supersede midi file tempo */ + FLUID_PLAYER_TEMPO_EXTERNAL_MIDI, /**< Set player tempo in us per quarter note, supersede midi file tempo */ + FLUID_PLAYER_TEMPO_NBR /**< @internal Value defines the count of player tempo type (#fluid_player_set_tempo_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +/** @startlifecycle{MIDI File Player} */ +FLUIDSYNTH_API fluid_player_t *new_fluid_player(fluid_synth_t *synth); +FLUIDSYNTH_API void delete_fluid_player(fluid_player_t *player); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_player_add(fluid_player_t *player, const char *midifile); +FLUIDSYNTH_API int fluid_player_add_mem(fluid_player_t *player, const void *buffer, size_t len); +FLUIDSYNTH_API int fluid_player_play(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_stop(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_join(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_set_loop(fluid_player_t *player, int loop); +FLUIDSYNTH_API int fluid_player_set_tempo(fluid_player_t *player, int tempo_type, double tempo); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_player_set_bpm(fluid_player_t *player, int bpm); +FLUIDSYNTH_API int fluid_player_set_playback_callback(fluid_player_t *player, handle_midi_event_func_t handler, void *handler_data); +FLUIDSYNTH_API int fluid_player_set_tick_callback(fluid_player_t *player, handle_midi_tick_func_t handler, void *handler_data); + +FLUIDSYNTH_API int fluid_player_get_status(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_current_tick(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_total_ticks(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_bpm(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_division(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_midi_tempo(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_seek(fluid_player_t *player, int ticks); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_MIDI_H */ diff --git a/libs/fluidsynth/include/fluidsynth/misc.h b/libs/fluidsynth/include/fluidsynth/misc.h new file mode 100644 index 00000000000..f60bf61e014 --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/misc.h @@ -0,0 +1,77 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_MISC_H +#define _FLUIDSYNTH_MISC_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup misc Miscellaneous + * + * Miscellaneous utility functions and defines + * + * @{ + */ + +/** + * Value that indicates success, used by most libfluidsynth functions. + * + * @note This was not publicly defined prior to libfluidsynth 1.1.0. When + * writing code which should also be compatible with older versions, something + * like the following can be used: + * + * @code + * #include + * + * #ifndef FLUID_OK + * #define FLUID_OK (0) + * #define FLUID_FAILED (-1) + * #endif + * @endcode + * + * @since 1.1.0 + */ +#define FLUID_OK (0) + +/** + * Value that indicates failure, used by most libfluidsynth functions. + * + * @note See #FLUID_OK for more details. + * + * @since 1.1.0 + */ +#define FLUID_FAILED (-1) + + +FLUIDSYNTH_API int fluid_is_soundfont(const char *filename); +FLUIDSYNTH_API int fluid_is_midifile(const char *filename); +FLUIDSYNTH_API void fluid_free(void* ptr); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_MISC_H */ diff --git a/libs/fluidsynth/include/fluidsynth/mod.h b/libs/fluidsynth/include/fluidsynth/mod.h new file mode 100644 index 00000000000..55f23579b97 --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/mod.h @@ -0,0 +1,104 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_MOD_H +#define _FLUIDSYNTH_MOD_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup modulators SoundFont Modulators + * @ingroup soundfonts + * + * SoundFont modulator functions and constants. + * + * @{ + */ + +/** + * Flags defining the polarity, mapping function and type of a modulator source. + * Compare with SoundFont 2.04 PDF section 8.2. + * + * Note: Bit values do not correspond to the SoundFont spec! Also note that + * #FLUID_MOD_GC and #FLUID_MOD_CC are in the flags field instead of the source field. + */ +enum fluid_mod_flags +{ + FLUID_MOD_POSITIVE = 0, /**< Mapping function is positive */ + FLUID_MOD_NEGATIVE = 1, /**< Mapping function is negative */ + FLUID_MOD_UNIPOLAR = 0, /**< Mapping function is unipolar */ + FLUID_MOD_BIPOLAR = 2, /**< Mapping function is bipolar */ + FLUID_MOD_LINEAR = 0, /**< Linear mapping function */ + FLUID_MOD_CONCAVE = 4, /**< Concave mapping function */ + FLUID_MOD_CONVEX = 8, /**< Convex mapping function */ + FLUID_MOD_SWITCH = 12, /**< Switch (on/off) mapping function */ + FLUID_MOD_GC = 0, /**< General controller source type (#fluid_mod_src) */ + FLUID_MOD_CC = 16, /**< MIDI CC controller (source will be a MIDI CC number) */ + + FLUID_MOD_SIN = 0x80, /**< Custom non-standard sinus mapping function */ +}; + +/** + * General controller (if #FLUID_MOD_GC in flags). This + * corresponds to SoundFont 2.04 PDF section 8.2.1 + */ +enum fluid_mod_src +{ + FLUID_MOD_NONE = 0, /**< No source controller */ + FLUID_MOD_VELOCITY = 2, /**< MIDI note-on velocity */ + FLUID_MOD_KEY = 3, /**< MIDI note-on note number */ + FLUID_MOD_KEYPRESSURE = 10, /**< MIDI key pressure */ + FLUID_MOD_CHANNELPRESSURE = 13, /**< MIDI channel pressure */ + FLUID_MOD_PITCHWHEEL = 14, /**< Pitch wheel */ + FLUID_MOD_PITCHWHEELSENS = 16 /**< Pitch wheel sensitivity */ +}; + +/** @startlifecycle{Modulator} */ +FLUIDSYNTH_API fluid_mod_t *new_fluid_mod(void); +FLUIDSYNTH_API void delete_fluid_mod(fluid_mod_t *mod); +/** @endlifecycle */ + +FLUIDSYNTH_API size_t fluid_mod_sizeof(void); + +FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t *mod, int src, int flags); +FLUIDSYNTH_API void fluid_mod_set_source2(fluid_mod_t *mod, int src, int flags); +FLUIDSYNTH_API void fluid_mod_set_dest(fluid_mod_t *mod, int dst); +FLUIDSYNTH_API void fluid_mod_set_amount(fluid_mod_t *mod, double amount); + +FLUIDSYNTH_API int fluid_mod_get_source1(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_flags1(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_source2(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_flags2(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_dest(const fluid_mod_t *mod); +FLUIDSYNTH_API double fluid_mod_get_amount(const fluid_mod_t *mod); + +FLUIDSYNTH_API int fluid_mod_test_identity(const fluid_mod_t *mod1, const fluid_mod_t *mod2); +FLUIDSYNTH_API int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl); +FLUIDSYNTH_API int fluid_mod_has_dest(const fluid_mod_t *mod, int gen); + +FLUIDSYNTH_API void fluid_mod_clone(fluid_mod_t *mod, const fluid_mod_t *src); +/** @} */ + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_MOD_H */ diff --git a/libs/fluidsynth/include/fluidsynth/seq.h b/libs/fluidsynth/include/fluidsynth/seq.h new file mode 100644 index 00000000000..cdcc959010f --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/seq.h @@ -0,0 +1,92 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SEQ_H +#define _FLUIDSYNTH_SEQ_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup sequencer MIDI Sequencer + * + * MIDI event sequencer. + * + * The MIDI sequencer can be used to play MIDI events in a more flexible way than + * using the MIDI file player, which expects the events to be stored as + * Standard MIDI Files. Using the sequencer, you can provide the events one by + * one, with an optional timestamp for scheduling. + * + * @{ + */ + +/** + * Event callback prototype for destination clients. + * + * @param time Current sequencer tick value (see fluid_sequencer_get_tick()). + * @param event The event being received + * @param seq The sequencer instance + * @param data User defined data registered with the client + * + * @note @p time may not be of the same tick value as the scheduled event! In fact, depending on + * the sequencer's scale and the synth's sample-rate, @p time may be a few ticks too late. Although this + * itself is inaudible, it is important to consider, + * when you use this callback for enqueuing additional events over and over again with + * fluid_sequencer_send_at(): If you enqueue new events with a relative tick value you might introduce + * a timing error, which causes your sequence to sound e.g. slower than it's supposed to be. If this is + * your use-case, make sure to enqueue events with an absolute tick value. + */ +typedef void (*fluid_event_callback_t)(unsigned int time, fluid_event_t *event, + fluid_sequencer_t *seq, void *data); + + +/** @startlifecycle{MIDI Sequencer} */ +FLUID_DEPRECATED FLUIDSYNTH_API fluid_sequencer_t *new_fluid_sequencer(void); +FLUIDSYNTH_API fluid_sequencer_t *new_fluid_sequencer2(int use_system_timer); +FLUIDSYNTH_API void delete_fluid_sequencer(fluid_sequencer_t *seq); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_sequencer_get_use_system_timer(fluid_sequencer_t *seq); +FLUIDSYNTH_API +fluid_seq_id_t fluid_sequencer_register_client(fluid_sequencer_t *seq, const char *name, + fluid_event_callback_t callback, void *data); +FLUIDSYNTH_API void fluid_sequencer_unregister_client(fluid_sequencer_t *seq, fluid_seq_id_t id); +FLUIDSYNTH_API int fluid_sequencer_count_clients(fluid_sequencer_t *seq); +FLUIDSYNTH_API fluid_seq_id_t fluid_sequencer_get_client_id(fluid_sequencer_t *seq, int index); +FLUIDSYNTH_API char *fluid_sequencer_get_client_name(fluid_sequencer_t *seq, fluid_seq_id_t id); +FLUIDSYNTH_API int fluid_sequencer_client_is_dest(fluid_sequencer_t *seq, fluid_seq_id_t id); +FLUIDSYNTH_API void fluid_sequencer_process(fluid_sequencer_t *seq, unsigned int msec); +FLUIDSYNTH_API void fluid_sequencer_send_now(fluid_sequencer_t *seq, fluid_event_t *evt); +FLUIDSYNTH_API +int fluid_sequencer_send_at(fluid_sequencer_t *seq, fluid_event_t *evt, + unsigned int time, int absolute); +FLUIDSYNTH_API +void fluid_sequencer_remove_events(fluid_sequencer_t *seq, fluid_seq_id_t source, fluid_seq_id_t dest, int type); +FLUIDSYNTH_API unsigned int fluid_sequencer_get_tick(fluid_sequencer_t *seq); +FLUIDSYNTH_API void fluid_sequencer_set_time_scale(fluid_sequencer_t *seq, double scale); +FLUIDSYNTH_API double fluid_sequencer_get_time_scale(fluid_sequencer_t *seq); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SEQ_H */ diff --git a/libs/fluidsynth/include/fluidsynth/seqbind.h b/libs/fluidsynth/include/fluidsynth/seqbind.h new file mode 100644 index 00000000000..876a2b419b0 --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/seqbind.h @@ -0,0 +1,44 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SEQBIND_H +#define _FLUIDSYNTH_SEQBIND_H + +#include "seq.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup sequencer + * + * @{ + */ +FLUIDSYNTH_API +fluid_seq_id_t fluid_sequencer_register_fluidsynth(fluid_sequencer_t *seq, fluid_synth_t *synth); +FLUIDSYNTH_API +int fluid_sequencer_add_midi_event_to_buffer(void *data, fluid_midi_event_t *event); +/** @} */ + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_SEQBIND_H */ diff --git a/libs/fluidsynth/include/fluidsynth/settings.h b/libs/fluidsynth/include/fluidsynth/settings.h new file mode 100644 index 00000000000..78db7dc32b9 --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/settings.h @@ -0,0 +1,194 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SETTINGS_H +#define _FLUIDSYNTH_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup settings Settings + * + * Functions for settings management + * + * To create a synthesizer object you will have to specify its + * settings. These settings are stored in a fluid_settings_t object. + * @code + * void + * my_synthesizer () + * { + * fluid_settings_t *settings; + * fluid_synth_t *synth; + * fluid_audio_driver_t *adriver; + * + * settings = new_fluid_settings (); + * fluid_settings_setstr(settings, "audio.driver", "alsa"); + * // ... change settings ... + * synth = new_fluid_synth (settings); + * adriver = new_fluid_audio_driver (settings, synth); + * // ... + * } + * @endcode + * @sa @ref CreatingSettings + * + * @{ + */ + +/** + * Hint FLUID_HINT_BOUNDED_BELOW indicates that the LowerBound field + * of the FLUID_PortRangeHint should be considered meaningful. The + * value in this field should be considered the (inclusive) lower + * bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also + * specified then the value of LowerBound should be multiplied by the + * sample rate. + */ +#define FLUID_HINT_BOUNDED_BELOW 0x1 + +/** Hint FLUID_HINT_BOUNDED_ABOVE indicates that the UpperBound field + of the FLUID_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) upper + bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also + specified then the value of UpperBound should be multiplied by the + sample rate. */ +#define FLUID_HINT_BOUNDED_ABOVE 0x2 + +/** + * Hint FLUID_HINT_TOGGLED indicates that the data item should be + * considered a Boolean toggle. Data less than or equal to zero should + * be considered `off' or `false,' and data above zero should be + * considered `on' or `true.' FLUID_HINT_TOGGLED may not be used in + * conjunction with any other hint. + */ +#define FLUID_HINT_TOGGLED 0x4 + +#define FLUID_HINT_OPTIONLIST 0x02 /**< Setting is a list of string options */ + + +/** + * Settings type + * + * Each setting has a defined type: numeric (double), integer, string or a + * set of values. The type of each setting can be retrieved using the + * function fluid_settings_get_type() + */ +enum fluid_types_enum +{ + FLUID_NO_TYPE = -1, /**< Undefined type */ + FLUID_NUM_TYPE, /**< Numeric (double) */ + FLUID_INT_TYPE, /**< Integer */ + FLUID_STR_TYPE, /**< String */ + FLUID_SET_TYPE /**< Set of values */ +}; + +/** @startlifecycle{Settings} */ +FLUIDSYNTH_API fluid_settings_t *new_fluid_settings(void); +FLUIDSYNTH_API void delete_fluid_settings(fluid_settings_t *settings); +/** @endlifecycle */ + +FLUIDSYNTH_API +int fluid_settings_get_type(fluid_settings_t *settings, const char *name); + +FLUIDSYNTH_API +int fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *val); + +FLUIDSYNTH_API +int fluid_settings_is_realtime(fluid_settings_t *settings, const char *name); + +FLUIDSYNTH_API +int fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char *str); + +FLUIDSYNTH_API +int fluid_settings_copystr(fluid_settings_t *settings, const char *name, char *str, int len); + +FLUIDSYNTH_API +int fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str); + +FLUIDSYNTH_API +int fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def); + +FLUIDSYNTH_API +int fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *value); + +FLUIDSYNTH_API +int fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val); + +FLUIDSYNTH_API +int fluid_settings_getnum(fluid_settings_t *settings, const char *name, double *val); + +FLUIDSYNTH_API +int fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val); + +FLUIDSYNTH_API +int fluid_settings_getnum_range(fluid_settings_t *settings, const char *name, + double *min, double *max); + +FLUIDSYNTH_API +int fluid_settings_setint(fluid_settings_t *settings, const char *name, int val); + +FLUIDSYNTH_API +int fluid_settings_getint(fluid_settings_t *settings, const char *name, int *val); + +FLUIDSYNTH_API +int fluid_settings_getint_default(fluid_settings_t *settings, const char *name, int *val); + +FLUIDSYNTH_API +int fluid_settings_getint_range(fluid_settings_t *settings, const char *name, + int *min, int *max); + +/** + * Callback function type used with fluid_settings_foreach_option() + * + * @param data User defined data pointer + * @param name Setting name + * @param option A string option for this setting (iterates through the list) + */ +typedef void (*fluid_settings_foreach_option_t)(void *data, const char *name, const char *option); + +FLUIDSYNTH_API +void fluid_settings_foreach_option(fluid_settings_t *settings, + const char *name, void *data, + fluid_settings_foreach_option_t func); +FLUIDSYNTH_API +int fluid_settings_option_count(fluid_settings_t *settings, const char *name); +FLUIDSYNTH_API char *fluid_settings_option_concat(fluid_settings_t *settings, + const char *name, + const char *separator); + +/** + * Callback function type used with fluid_settings_foreach() + * + * @param data User defined data pointer + * @param name Setting name + * @param type Setting type (#fluid_types_enum) + */ +typedef void (*fluid_settings_foreach_t)(void *data, const char *name, int type); + +FLUIDSYNTH_API +void fluid_settings_foreach(fluid_settings_t *settings, void *data, + fluid_settings_foreach_t func); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SETTINGS_H */ diff --git a/libs/fluidsynth/include/fluidsynth/sfont.h b/libs/fluidsynth/include/fluidsynth/sfont.h new file mode 100644 index 00000000000..6d0fd4c7a3e --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/sfont.h @@ -0,0 +1,362 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SFONT_H +#define _FLUIDSYNTH_SFONT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup soundfonts SoundFonts + * + * SoundFont related functions + * + * This part of the API contains functions, defines and types that are mostly + * only used by internal or custom SoundFont loaders or client code that + * modifies loaded presets, SoundFonts or voices directly. + */ + +/** + * @defgroup soundfont_loader SoundFont Loader + * @ingroup soundfonts + * + * Create custom SoundFont loaders + * + * It is possible to add new SoundFont loaders to the + * synthesizer. This API allows for virtual SoundFont files to be loaded + * and synthesized, which may not actually be SoundFont files, as long as they + * can be represented by the SoundFont synthesis model. + * + * To add a new SoundFont loader to the synthesizer, call + * fluid_synth_add_sfloader() and pass a pointer to an + * #fluid_sfloader_t instance created by new_fluid_sfloader(). + * On creation, you must specify a callback function \p load + * that will be called for every file attempting to load it and + * if successful returns a #fluid_sfont_t instance, or NULL if it fails. + * + * The #fluid_sfont_t structure contains a callback to obtain the + * name of the SoundFont. It contains two functions to iterate + * though the contained presets, and one function to obtain a + * preset corresponding to a bank and preset number. This + * function should return a #fluid_preset_t instance. + * + * The #fluid_preset_t instance contains some functions to obtain + * information from the preset (name, bank, number). The most + * important callback is the noteon function. The noteon function + * is called by fluidsynth internally and + * should call fluid_synth_alloc_voice() for every sample that has + * to be played. fluid_synth_alloc_voice() expects a pointer to a + * #fluid_sample_t instance and returns a pointer to the opaque + * #fluid_voice_t structure. To set or increment the values of a + * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are + * finished initializing the voice call fluid_voice_start() to + * start playing the synthesis voice. + * + * @{ + */ + +/** + * Some notification enums for presets and samples. + */ +enum +{ + FLUID_PRESET_SELECTED, /**< Preset selected notify */ + FLUID_PRESET_UNSELECTED, /**< Preset unselected notify */ + FLUID_SAMPLE_DONE, /**< Sample no longer needed notify */ + FLUID_PRESET_PIN, /**< Request to pin preset samples to cache */ + FLUID_PRESET_UNPIN /**< Request to unpin preset samples from cache */ +}; + +/** + * Indicates the type of a sample used by the _fluid_sample_t::sampletype field. + * + * This enum corresponds to the \c SFSampleLink enum in the SoundFont spec. + * One \c flag may be bit-wise OR-ed with one \c value. + */ +enum fluid_sample_type +{ + FLUID_SAMPLETYPE_MONO = 0x1, /**< Value used for mono samples */ + FLUID_SAMPLETYPE_RIGHT = 0x2, /**< Value used for right samples of a stereo pair */ + FLUID_SAMPLETYPE_LEFT = 0x4, /**< Value used for left samples of a stereo pair */ + FLUID_SAMPLETYPE_LINKED = 0x8, /**< Value used for linked sample, which is currently not supported */ + FLUID_SAMPLETYPE_OGG_VORBIS = 0x10, /**< Flag used for Ogg Vorbis compressed samples (non-standard compliant extension) as found in the program "sftools" developed by Werner Schweer from MuseScore @since 1.1.7 */ + FLUID_SAMPLETYPE_ROM = 0x8000 /**< Flag that indicates ROM samples, causing the sample to be ignored */ +}; + + +/** + * Method to load an instrument file (does not actually need to be a real file name, + * could be another type of string identifier that the \a loader understands). + * + * @param loader SoundFont loader + * @param filename File name or other string identifier + * @return The loaded instrument file (SoundFont) or NULL if an error occurred. + */ +typedef fluid_sfont_t *(*fluid_sfloader_load_t)(fluid_sfloader_t *loader, const char *filename); + +/** + * The free method should free the memory allocated for a fluid_sfloader_t instance in + * addition to any private data. + * + * @param loader SoundFont loader + * + * Any custom user provided cleanup function must ultimately call + * delete_fluid_sfloader() to ensure proper cleanup of the #fluid_sfloader_t struct. If no private data + * needs to be freed, setting this to delete_fluid_sfloader() is sufficient. + * + */ +typedef void (*fluid_sfloader_free_t)(fluid_sfloader_t *loader); + + +/** @startlifecycle{SoundFont Loader} */ +FLUIDSYNTH_API fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free); +FLUIDSYNTH_API void delete_fluid_sfloader(fluid_sfloader_t *loader); + +FLUIDSYNTH_API fluid_sfloader_t *new_fluid_defsfloader(fluid_settings_t *settings); +/** @endlifecycle */ + +/** + * Opens the file or memory indicated by \c filename in binary read mode. + * + * @return returns a file handle on success, NULL otherwise + * + * \c filename matches the string provided during the fluid_synth_sfload() call. + */ +typedef void *(* fluid_sfloader_callback_open_t)(const char *filename); + +/** + * Reads \c count bytes to the specified buffer \c buf. + * + * @return returns #FLUID_OK if exactly \c count bytes were successfully read, else returns #FLUID_FAILED and leaves \a buf unmodified. + */ +typedef int (* fluid_sfloader_callback_read_t)(void *buf, fluid_long_long_t count, void *handle); + +/** + * Same purpose and behaviour as fseek. + * + * @param origin either \c SEEK_SET, \c SEEK_CUR or \c SEEK_END + * @return returns #FLUID_OK if the seek was successfully performed while not seeking beyond a buffer or file, #FLUID_FAILED otherwise + */ +typedef int (* fluid_sfloader_callback_seek_t)(void *handle, fluid_long_long_t offset, int origin); + +/** + * Closes the handle returned by #fluid_sfloader_callback_open_t and frees used resources. + * + * @return returns #FLUID_OK on success, #FLUID_FAILED on error + */ +typedef int (* fluid_sfloader_callback_close_t)(void *handle); + +/** @return returns current file offset or #FLUID_FAILED on error */ +typedef fluid_long_long_t (* fluid_sfloader_callback_tell_t)(void *handle); + + +FLUIDSYNTH_API int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader, + fluid_sfloader_callback_open_t open, + fluid_sfloader_callback_read_t read, + fluid_sfloader_callback_seek_t seek, + fluid_sfloader_callback_tell_t tell, + fluid_sfloader_callback_close_t close); + +FLUIDSYNTH_API int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data); +FLUIDSYNTH_API void *fluid_sfloader_get_data(fluid_sfloader_t *loader); + + + +/** + * Method to return the name of a virtual SoundFont. + * + * @param sfont Virtual SoundFont + * @return The name of the virtual SoundFont. + */ +typedef const char *(*fluid_sfont_get_name_t)(fluid_sfont_t *sfont); + +/** + * Get a virtual SoundFont preset by bank and program numbers. + * + * @param sfont Virtual SoundFont + * @param bank MIDI bank number (0-16383) + * @param prenum MIDI preset number (0-127) + * @return Should return an allocated virtual preset or NULL if it could not + * be found. + */ +typedef fluid_preset_t *(*fluid_sfont_get_preset_t)(fluid_sfont_t *sfont, int bank, int prenum); + +/** + * Start virtual SoundFont preset iteration method. + * + * @param sfont Virtual SoundFont + * + * Starts/re-starts virtual preset iteration in a SoundFont. + */ +typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t *sfont); + +/** + * Virtual SoundFont preset iteration function. + * + * @param sfont Virtual SoundFont + * @return NULL when no more presets are available, otherwise the a pointer to the current preset + * + * Returns preset information to the caller. The returned buffer is only valid until a subsequent + * call to this function. + */ +typedef fluid_preset_t *(*fluid_sfont_iteration_next_t)(fluid_sfont_t *sfont); + +/** + * Method to free a virtual SoundFont bank. + * + * @param sfont Virtual SoundFont to free. + * @return Should return 0 when it was able to free all resources or non-zero + * if some of the samples could not be freed because they are still in use, + * in which case the free will be tried again later, until success. + * + * Any custom user provided cleanup function must ultimately call + * delete_fluid_sfont() to ensure proper cleanup of the #fluid_sfont_t struct. If no private data + * needs to be freed, setting this to delete_fluid_sfont() is sufficient. + */ +typedef int (*fluid_sfont_free_t)(fluid_sfont_t *sfont); + + +/** @startlifecycle{SoundFont} */ +FLUIDSYNTH_API fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name, + fluid_sfont_get_preset_t get_preset, + fluid_sfont_iteration_start_t iter_start, + fluid_sfont_iteration_next_t iter_next, + fluid_sfont_free_t free); + +FLUIDSYNTH_API int delete_fluid_sfont(fluid_sfont_t *sfont); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data); +FLUIDSYNTH_API void *fluid_sfont_get_data(fluid_sfont_t *sfont); + +FLUIDSYNTH_API int fluid_sfont_get_id(fluid_sfont_t *sfont); +FLUIDSYNTH_API const char *fluid_sfont_get_name(fluid_sfont_t *sfont); +FLUIDSYNTH_API fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum); +FLUIDSYNTH_API void fluid_sfont_iteration_start(fluid_sfont_t *sfont); +FLUIDSYNTH_API fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont); + +/** + * Method to get a virtual SoundFont preset name. + * + * @param preset Virtual SoundFont preset + * @return Should return the name of the preset. The returned string must be + * valid for the duration of the virtual preset (or the duration of the + * SoundFont, in the case of preset iteration). + */ +typedef const char *(*fluid_preset_get_name_t)(fluid_preset_t *preset); + +/** + * Method to get a virtual SoundFont preset MIDI bank number. + * + * @param preset Virtual SoundFont preset + * @param return The bank number of the preset + */ +typedef int (*fluid_preset_get_banknum_t)(fluid_preset_t *preset); + +/** + * Method to get a virtual SoundFont preset MIDI program number. + * + * @param preset Virtual SoundFont preset + * @param return The program number of the preset + */ +typedef int (*fluid_preset_get_num_t)(fluid_preset_t *preset); + +/** + * Method to handle a noteon event (synthesize the instrument). + * + * @param preset Virtual SoundFont preset + * @param synth Synthesizer instance + * @param chan MIDI channel number of the note on event + * @param key MIDI note number (0-127) + * @param vel MIDI velocity (0-127) + * @return #FLUID_OK on success (0) or #FLUID_FAILED (-1) otherwise + * + * This method may be called from within synthesis context and therefore + * should be as efficient as possible and not perform any operations considered + * bad for realtime audio output (memory allocations and other OS calls). + * + * Call fluid_synth_alloc_voice() for every sample that has + * to be played. fluid_synth_alloc_voice() expects a pointer to a + * #fluid_sample_t structure and returns a pointer to the opaque + * #fluid_voice_t structure. To set or increment the values of a + * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are + * finished initializing the voice call fluid_voice_start() to + * start playing the synthesis voice. Starting with FluidSynth 1.1.0 all voices + * created will be started at the same time. + */ +typedef int (*fluid_preset_noteon_t)(fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel); + +/** + * Method to free a virtual SoundFont preset. + * + * @param preset Virtual SoundFont preset + * @return Should return 0 + * + * Any custom user provided cleanup function must ultimately call + * delete_fluid_preset() to ensure proper cleanup of the #fluid_preset_t struct. If no private data + * needs to be freed, setting this to delete_fluid_preset() is sufficient. + */ +typedef void (*fluid_preset_free_t)(fluid_preset_t *preset); + +/** @startlifecycle{Preset} */ +FLUIDSYNTH_API fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont, + fluid_preset_get_name_t get_name, + fluid_preset_get_banknum_t get_bank, + fluid_preset_get_num_t get_num, + fluid_preset_noteon_t noteon, + fluid_preset_free_t free); +FLUIDSYNTH_API void delete_fluid_preset(fluid_preset_t *preset); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_preset_set_data(fluid_preset_t *preset, void *data); +FLUIDSYNTH_API void *fluid_preset_get_data(fluid_preset_t *preset); + +FLUIDSYNTH_API const char *fluid_preset_get_name(fluid_preset_t *preset); +FLUIDSYNTH_API int fluid_preset_get_banknum(fluid_preset_t *preset); +FLUIDSYNTH_API int fluid_preset_get_num(fluid_preset_t *preset); +FLUIDSYNTH_API fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset); + +/** @startlifecycle{Sample} */ +FLUIDSYNTH_API fluid_sample_t *new_fluid_sample(void); +FLUIDSYNTH_API void delete_fluid_sample(fluid_sample_t *sample); +/** @endlifecycle */ + +FLUIDSYNTH_API size_t fluid_sample_sizeof(void); + +FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t *sample, const char *name); +FLUIDSYNTH_API int fluid_sample_set_sound_data(fluid_sample_t *sample, + short *data, + char *data24, + unsigned int nbframes, + unsigned int sample_rate, + short copy_data); + +FLUIDSYNTH_API int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end); +FLUIDSYNTH_API int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SFONT_H */ diff --git a/libs/fluidsynth/include/fluidsynth/shell.h b/libs/fluidsynth/include/fluidsynth/shell.h new file mode 100644 index 00000000000..5eed0878a5c --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/shell.h @@ -0,0 +1,150 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SHELL_H +#define _FLUIDSYNTH_SHELL_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup command_interface Command Interface + * + * Control and configuration interface + * + * The command interface allows you to send textual commands to + * the synthesizer, to parse a command file, or to read commands + * from the stdin or other input streams (like a TCP socket). + * + * For a full list of available commands, type \c help in the + * \ref command_shell or send the same command via a command handler. + * Further documentation can be found at + * https://github.com/FluidSynth/fluidsynth/wiki/UserManual#shell-commands + * + * @{ + */ +FLUIDSYNTH_API fluid_istream_t fluid_get_stdin(void); +FLUIDSYNTH_API fluid_ostream_t fluid_get_stdout(void); +FLUIDSYNTH_API char *fluid_get_userconf(char *buf, int len); +FLUIDSYNTH_API char *fluid_get_sysconf(char *buf, int len); +/** @} */ + + +/** + * @defgroup command_handler Command Handler + * @ingroup command_interface + * @brief Handles text commands and reading of configuration files + * + * @{ + */ + +/** @startlifecycle{Command Handler} */ +FLUIDSYNTH_API +fluid_cmd_handler_t *new_fluid_cmd_handler(fluid_synth_t *synth, fluid_midi_router_t *router); + +FLUIDSYNTH_API +fluid_cmd_handler_t *new_fluid_cmd_handler2(fluid_settings_t *settings, fluid_synth_t *synth, + fluid_midi_router_t *router, fluid_player_t *player); + +FLUIDSYNTH_API +void delete_fluid_cmd_handler(fluid_cmd_handler_t *handler); +/** @endlifecycle */ + +FLUIDSYNTH_API +void fluid_cmd_handler_set_synth(fluid_cmd_handler_t *handler, fluid_synth_t *synth); + +FLUIDSYNTH_API +int fluid_command(fluid_cmd_handler_t *handler, const char *cmd, fluid_ostream_t out); + +FLUIDSYNTH_API +int fluid_source(fluid_cmd_handler_t *handler, const char *filename); +/** @} */ + + +/** + * @defgroup command_shell Command Shell + * @ingroup command_interface + * + * Interactive shell to control and configure a synthesizer instance. + * + * If you need a platform independent way to get the standard input + * and output streams, use fluid_get_stdin() and fluid_get_stdout(). + * + * For a full list of available commands, type \c help in the shell. + * + * @{ + */ + +/** @startlifecycle{Command Shell} */ +FLUIDSYNTH_API +fluid_shell_t *new_fluid_shell(fluid_settings_t *settings, fluid_cmd_handler_t *handler, + fluid_istream_t in, fluid_ostream_t out, int thread); + +FLUIDSYNTH_API +void fluid_usershell(fluid_settings_t *settings, fluid_cmd_handler_t *handler); + +FLUIDSYNTH_API void delete_fluid_shell(fluid_shell_t *shell); +/** @endlifecycle */ + +/** @} */ + + +/** + * @defgroup command_server Command Server + * @ingroup command_interface + * + * TCP socket server for a command handler. + * + * The socket server will open the TCP port set by \ref settings_shell_port + * (default 9800) and starts a new thread and \ref command_handler for each + * incoming connection. + * + * @note The server is only available if libfluidsynth has been compiled + * with network support (enable-network). Without network support, all related + * functions will return FLUID_FAILED or NULL. + * + * @{ + */ + +/** @startlifecycle{Command Server} */ +FLUIDSYNTH_API +fluid_server_t *new_fluid_server(fluid_settings_t *settings, + fluid_synth_t *synth, fluid_midi_router_t *router); + +FLUIDSYNTH_API +fluid_server_t *new_fluid_server2(fluid_settings_t *settings, + fluid_synth_t *synth, fluid_midi_router_t *router, + fluid_player_t *player); + +FLUIDSYNTH_API void delete_fluid_server(fluid_server_t *server); + +FLUIDSYNTH_API int fluid_server_join(fluid_server_t *server); +/** @endlifecycle */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SHELL_H */ diff --git a/libs/fluidsynth/include/fluidsynth/synth.h b/libs/fluidsynth/include/fluidsynth/synth.h new file mode 100644 index 00000000000..84861ebd648 --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/synth.h @@ -0,0 +1,552 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SYNTH_H +#define _FLUIDSYNTH_SYNTH_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup synth Synthesizer + * + * SoundFont synthesizer + * + * You create a new synthesizer with new_fluid_synth() and you destroy + * it with delete_fluid_synth(). Use the fluid_settings_t structure to specify + * the synthesizer characteristics. + * + * You have to load a SoundFont in order to hear any sound. For that + * you use the fluid_synth_sfload() function. + * + * You can use the audio driver functions to open + * the audio device and create a background audio thread. + * + * The API for sending MIDI events is probably what you expect: + * fluid_synth_noteon(), fluid_synth_noteoff(), ... + * + * @{ + */ + +/** @startlifecycle{Synthesizer} */ +FLUIDSYNTH_API fluid_synth_t *new_fluid_synth(fluid_settings_t *settings); +FLUIDSYNTH_API void delete_fluid_synth(fluid_synth_t *synth); +/** @endlifecycle */ + +FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API const char *fluid_synth_error(fluid_synth_t *synth); +/** @} */ + +/** + * @defgroup midi_messages MIDI Channel Messages + * @ingroup synth + * + * The MIDI channel message functions are mostly directly named after their + * counterpart MIDI messages. They are a high-level interface to controlling + * the synthesizer, playing notes and changing note and channel parameters. + * + * @{ + */ +FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t *synth, int chan, int key, int vel); +FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t *synth, int chan, int key); +FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t *synth, int chan, int ctrl, int val); +FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t *synth, int chan, int ctrl, int *pval); +FLUIDSYNTH_API int fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int *handled, int dryrun); +FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t *synth, int chan, int val); +FLUIDSYNTH_API int fluid_synth_get_pitch_bend(fluid_synth_t *synth, int chan, int *ppitch_bend); +FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t *synth, int chan, int val); +FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t *synth, int chan, int *pval); +FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t *synth, int chan, int program); +FLUIDSYNTH_API int fluid_synth_channel_pressure(fluid_synth_t *synth, int chan, int val); +FLUIDSYNTH_API int fluid_synth_key_pressure(fluid_synth_t *synth, int chan, int key, int val); +FLUIDSYNTH_API int fluid_synth_bank_select(fluid_synth_t *synth, int chan, int bank); +FLUIDSYNTH_API int fluid_synth_sfont_select(fluid_synth_t *synth, int chan, int sfont_id); +FLUIDSYNTH_API +int fluid_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id, + int bank_num, int preset_num); +FLUIDSYNTH_API int +fluid_synth_program_select_by_sfont_name(fluid_synth_t *synth, int chan, + const char *sfont_name, int bank_num, + int preset_num); +FLUIDSYNTH_API +int fluid_synth_get_program(fluid_synth_t *synth, int chan, int *sfont_id, + int *bank_num, int *preset_num); +FLUIDSYNTH_API int fluid_synth_unset_program(fluid_synth_t *synth, int chan); +FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t *synth); + +FLUIDSYNTH_API int fluid_synth_all_notes_off(fluid_synth_t *synth, int chan); +FLUIDSYNTH_API int fluid_synth_all_sounds_off(fluid_synth_t *synth, int chan); + +FLUIDSYNTH_API int fluid_synth_set_gen(fluid_synth_t *synth, int chan, + int param, float value); +FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param); +/** @} MIDI Channel Messages */ + + +/** + * @defgroup voice_control Synthesis Voice Control + * @ingroup synth + * + * Low-level access to synthesis voices. + * + * @{ + */ +FLUIDSYNTH_API int fluid_synth_start(fluid_synth_t *synth, unsigned int id, + fluid_preset_t *preset, int audio_chan, + int midi_chan, int key, int vel); +FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t *synth, unsigned int id); + +FLUIDSYNTH_API fluid_voice_t *fluid_synth_alloc_voice(fluid_synth_t *synth, + fluid_sample_t *sample, + int channum, int key, int vel); +FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t *synth, fluid_voice_t *voice); +FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t *synth, + fluid_voice_t *buf[], int bufsize, int ID); +/** @} Voice Control */ + + +/** + * @defgroup soundfont_management SoundFont Management + * @ingroup synth + * + * Functions to load and unload SoundFonts. + * + * @{ + */ +FLUIDSYNTH_API +int fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets); +FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t *synth, int id); +FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets); +FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont); +FLUIDSYNTH_API int fluid_synth_remove_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont); +FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t *synth); +FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont(fluid_synth_t *synth, unsigned int num); +FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont_by_id(fluid_synth_t *synth, int id); +FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont_by_name(fluid_synth_t *synth, + const char *name); +FLUIDSYNTH_API int fluid_synth_set_bank_offset(fluid_synth_t *synth, int sfont_id, int offset); +FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t *synth, int sfont_id); +/** @} Soundfont Management */ + + +/** + * @defgroup reverb_effect Effect - Reverb + * @ingroup synth + * + * Functions for configuring the built-in reverb effect + * + * @{ + */ +FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t *synth, int on); +FLUIDSYNTH_API int fluid_synth_reverb_on(fluid_synth_t *synth, int fx_group, int on); + +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, + double damping, double width, double level); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level); + +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_roomsize(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_damp(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_level(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t *synth); + +FLUIDSYNTH_API int fluid_synth_set_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, double roomsize); +FLUIDSYNTH_API int fluid_synth_set_reverb_group_damp(fluid_synth_t *synth, int fx_group, double damping); +FLUIDSYNTH_API int fluid_synth_set_reverb_group_width(fluid_synth_t *synth, int fx_group, double width); +FLUIDSYNTH_API int fluid_synth_set_reverb_group_level(fluid_synth_t *synth, int fx_group, double level); + +FLUIDSYNTH_API int fluid_synth_get_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, double *roomsize); +FLUIDSYNTH_API int fluid_synth_get_reverb_group_damp(fluid_synth_t *synth, int fx_group, double *damping); +FLUIDSYNTH_API int fluid_synth_get_reverb_group_width(fluid_synth_t *synth, int fx_group, double *width); +FLUIDSYNTH_API int fluid_synth_get_reverb_group_level(fluid_synth_t *synth, int fx_group, double *level); + /** @} Reverb */ + + +/** + * @defgroup chorus_effect Effect - Chorus + * @ingroup synth + * + * Functions for configuring the built-in chorus effect + * + * @{ + */ + +/** + * Chorus modulation waveform type. + */ +enum fluid_chorus_mod +{ + FLUID_CHORUS_MOD_SINE = 0, /**< Sine wave chorus modulation */ + FLUID_CHORUS_MOD_TRIANGLE = 1 /**< Triangle wave chorus modulation */ +}; + + +FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t *synth, int on); +FLUIDSYNTH_API int fluid_synth_chorus_on(fluid_synth_t *synth, int fx_group, int on); + +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level, + double speed, double depth_ms, int type); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type); + +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_get_chorus_nr(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_level(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_speed(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_depth(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t *synth); /* see fluid_chorus_mod */ + +FLUIDSYNTH_API int fluid_synth_set_chorus_group_nr(fluid_synth_t *synth, int fx_group, int nr); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_level(fluid_synth_t *synth, int fx_group, double level); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_speed(fluid_synth_t *synth, int fx_group, double speed); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_depth(fluid_synth_t *synth, int fx_group, double depth_ms); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_type(fluid_synth_t *synth, int fx_group, int type); + +FLUIDSYNTH_API int fluid_synth_get_chorus_group_nr(fluid_synth_t *synth, int fx_group, int *nr); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_level(fluid_synth_t *synth, int fx_group, double *level); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_speed(fluid_synth_t *synth, int fx_group, double *speed); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_depth(fluid_synth_t *synth, int fx_group, double *depth_ms); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_type(fluid_synth_t *synth, int fx_group, int *type); +/** @} Chorus */ + +/** + * @defgroup synthesis_params Synthesis Parameters + * @ingroup synth + * + * Functions to control and query synthesis parameters like gain and + * polyphony count. + * + * @{ + */ +FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_effects_groups(fluid_synth_t *synth); + +FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate); +FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t *synth, float gain); +FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t *synth, int polyphony); +FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_get_active_voice_count(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_t *synth); + +FLUIDSYNTH_API +int fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method); + +/** + * Synthesis interpolation method. + */ +enum fluid_interp +{ + FLUID_INTERP_NONE = 0, /**< No interpolation: Fastest, but questionable audio quality */ + FLUID_INTERP_LINEAR = 1, /**< Straight-line interpolation: A bit slower, reasonable audio quality */ + FLUID_INTERP_4THORDER = 4, /**< Fourth-order interpolation, good quality, the default */ + FLUID_INTERP_7THORDER = 7, /**< Seventh-order interpolation */ + + FLUID_INTERP_DEFAULT = FLUID_INTERP_4THORDER, /**< Default interpolation method */ + FLUID_INTERP_HIGHEST = FLUID_INTERP_7THORDER, /**< Highest interpolation method */ +}; + +/** + * Enum used with fluid_synth_add_default_mod() to specify how to handle duplicate modulators. + */ +enum fluid_synth_add_mod +{ + FLUID_SYNTH_OVERWRITE, /**< Overwrite any existing matching modulator */ + FLUID_SYNTH_ADD, /**< Sum up modulator amounts */ +}; + +FLUIDSYNTH_API int fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode); +FLUIDSYNTH_API int fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod); +/** @} Synthesis Parameters */ + + +/** + * @defgroup tuning MIDI Tuning + * @ingroup synth + * + * The functions in this section implement the MIDI Tuning Standard interface. + * + * @{ + */ +FLUIDSYNTH_API +int fluid_synth_activate_key_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply); +FLUIDSYNTH_API +int fluid_synth_activate_octave_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply); +FLUIDSYNTH_API +int fluid_synth_tune_notes(fluid_synth_t *synth, int bank, int prog, + int len, const int *keys, const double *pitch, int apply); +FLUIDSYNTH_API +int fluid_synth_activate_tuning(fluid_synth_t *synth, int chan, int bank, int prog, + int apply); +FLUIDSYNTH_API +int fluid_synth_deactivate_tuning(fluid_synth_t *synth, int chan, int apply); +FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t *synth); +FLUIDSYNTH_API +int fluid_synth_tuning_iteration_next(fluid_synth_t *synth, int *bank, int *prog); +FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int prog, + char *name, int len, double *pitch); +/** @} MIDI Tuning */ + + +/** + * @defgroup audio_rendering Audio Rendering + * @ingroup synth + * + * The functions in this section can be used to render audio directly to + * memory buffers. They are used internally by the \ref audio_driver and \ref file_renderer, + * but can also be used manually for custom processing of the rendered audio. + * + * @note Please note that all following functions block during rendering. If your goal is to + * render real-time audio, ensure that you call these functions from a high-priority + * thread with little to no other duties other than calling the rendering functions. + * + * @warning + * If a concurrently running thread calls any other sound affecting synth function + * (e.g. fluid_synth_noteon(), fluid_synth_cc(), etc.) it is unspecified whether the event triggered by such a call + * will be effective in the recently synthesized audio. While this is inaudible when only requesting small chunks from the + * synth with every call (cf. fluid_synth_get_internal_bufsize()), it will become evident when requesting larger sample chunks: + * With larger sample chunks it will get harder for the synth to react on those spontaneously occurring events in time + * (like events received from a MIDI driver, or directly made synth API calls). + * In those real-time scenarios, prefer requesting smaller + * sample chunks from the synth with each call, to avoid poor quantization of your events in the synthesized audio. + * This issue is not applicable when using the MIDI player or sequencer for event dispatching. Also + * refer to the documentation of \setting{audio_period-size}. + * + * @{ + */ +FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr); +FLUIDSYNTH_API int fluid_synth_write_float(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t *synth, int len, + float **left, float **right, + float **fx_left, float **fx_right); +FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t *synth, int len, + int nfx, float *fx[], + int nout, float *out[]); +/** @} Audio Rendering */ + + +/** + * @defgroup iir_filter Effect - IIR Filter + * @ingroup synth + * + * Functions for configuring the built-in IIR filter effect + * + * @{ + */ + +/** + * Specifies the type of filter to use for the custom IIR filter + */ +enum fluid_iir_filter_type +{ + FLUID_IIR_DISABLED = 0, /**< Custom IIR filter is not operating */ + FLUID_IIR_LOWPASS, /**< Custom IIR filter is operating as low-pass filter */ + FLUID_IIR_HIGHPASS, /**< Custom IIR filter is operating as high-pass filter */ + FLUID_IIR_LAST /**< @internal Value defines the count of filter types (#fluid_iir_filter_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +/** + * Specifies optional settings to use for the custom IIR filter. Can be bitwise ORed. + */ +enum fluid_iir_filter_flags +{ + FLUID_IIR_Q_LINEAR = 1 << 0, /**< The Soundfont spec requires the filter Q to be interpreted in dB. If this flag is set the filter Q is instead assumed to be in a linear range */ + FLUID_IIR_Q_ZERO_OFF = 1 << 1, /**< If this flag the filter is switched off if Q == 0 (prior to any transformation) */ + FLUID_IIR_NO_GAIN_AMP = 1 << 2 /**< The Soundfont spec requires to correct the gain of the filter depending on the filter's Q. If this flag is set the filter gain will not be corrected. */ +}; + +FLUIDSYNTH_API int fluid_synth_set_custom_filter(fluid_synth_t *, int type, int flags); +/** @} IIR Filter */ + + + + +/** + * @defgroup channel_setup MIDI Channel Setup + * @ingroup synth + * + * The functions in this section provide interfaces to change the channel type + * and to configure basic channels, legato and portamento setups. + * + * @{ + */ + +/** @name Channel Type + * @{ + */ + +/** + * The midi channel type used by fluid_synth_set_channel_type() + */ +enum fluid_midi_channel_type +{ + CHANNEL_TYPE_MELODIC = 0, /**< Melodic midi channel */ + CHANNEL_TYPE_DRUM = 1 /**< Drum midi channel */ +}; + +FLUIDSYNTH_API int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type); +/** @} Channel Type */ + + +/** @name Basic Channel Mode + * @{ + */ + +/** + * Channel mode bits OR-ed together so that it matches with the midi spec: poly omnion (0), mono omnion (1), poly omnioff (2), mono omnioff (3) + */ +enum fluid_channel_mode_flags +{ + FLUID_CHANNEL_POLY_OFF = 0x01, /**< if flag is set, the basic channel is in mono on state, if not set poly is on */ + FLUID_CHANNEL_OMNI_OFF = 0x02, /**< if flag is set, the basic channel is in omni off state, if not set omni is on */ +}; + +/** + * Indicates the mode a basic channel is set to + */ +enum fluid_basic_channel_modes +{ + FLUID_CHANNEL_MODE_MASK = (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< Mask Poly and Omni bits of #fluid_channel_mode_flags, usually only used internally */ + FLUID_CHANNEL_MODE_OMNION_POLY = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 0 */ + FLUID_CHANNEL_MODE_OMNION_MONO = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 1 */ + FLUID_CHANNEL_MODE_OMNIOFF_POLY = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 2 */ + FLUID_CHANNEL_MODE_OMNIOFF_MONO = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 3 */ + FLUID_CHANNEL_MODE_LAST /**< @internal Value defines the count of basic channel modes (#fluid_basic_channel_modes) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +FLUIDSYNTH_API int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan); + +FLUIDSYNTH_API int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan, + int *basic_chan_out, + int *mode_chan_out, + int *basic_val_out); +FLUIDSYNTH_API int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val); + +/** @} Basic Channel Mode */ + +/** @name Legato Mode + * @{ + */ + +/** + * Indicates the legato mode a channel is set to + * n1,n2,n3,.. is a legato passage. n1 is the first note, and n2,n3,n4 are played legato with previous note. */ +enum fluid_channel_legato_mode +{ + FLUID_CHANNEL_LEGATO_MODE_RETRIGGER, /**< Mode 0 - Release previous note, start a new note */ + FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER, /**< Mode 1 - On contiguous notes retrigger in attack section using current value, shape attack using current dynamic and make use of previous voices if any */ + FLUID_CHANNEL_LEGATO_MODE_LAST /**< @internal Value defines the count of legato modes (#fluid_channel_legato_mode) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +FLUIDSYNTH_API int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode); +FLUIDSYNTH_API int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode); +/** @} Legato Mode */ + +/** @name Portamento Mode + * @{ + */ + +/** + * Indicates the portamento mode a channel is set to + */ +enum fluid_channel_portamento_mode +{ + FLUID_CHANNEL_PORTAMENTO_MODE_EACH_NOTE, /**< Mode 0 - Portamento on each note (staccato or legato) */ + FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY, /**< Mode 1 - Portamento only on legato note */ + FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY, /**< Mode 2 - Portamento only on staccato note */ + FLUID_CHANNEL_PORTAMENTO_MODE_LAST /**< @internal Value defines the count of portamento modes + @warning This symbol is not part of the public API and ABI + stability guarantee and may change at any time! */ +}; + +FLUIDSYNTH_API int fluid_synth_set_portamento_mode(fluid_synth_t *synth, + int chan, int portamentomode); +FLUIDSYNTH_API int fluid_synth_get_portamento_mode(fluid_synth_t *synth, + int chan, int *portamentomode); +/** @} Portamento Mode */ + +/**@name Breath Mode + * @{ + */ + +/** + * Indicates the breath mode a channel is set to + */ +enum fluid_channel_breath_flags +{ + FLUID_CHANNEL_BREATH_POLY = 0x10, /**< when channel is poly, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath to initial attenuation modulator */ + FLUID_CHANNEL_BREATH_MONO = 0x20, /**< when channel is mono, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath modulator */ + FLUID_CHANNEL_BREATH_SYNC = 0x40, /**< when channel is mono, this flag indicates that the breath controller(MSB)triggers noteon/noteoff on the running note */ +}; + +FLUIDSYNTH_API int fluid_synth_set_breath_mode(fluid_synth_t *synth, + int chan, int breathmode); +FLUIDSYNTH_API int fluid_synth_get_breath_mode(fluid_synth_t *synth, + int chan, int *breathmode); +/** @} Breath Mode */ +/** @} MIDI Channel Setup */ + + +/** @ingroup settings */ +FLUIDSYNTH_API fluid_settings_t *fluid_synth_get_settings(fluid_synth_t *synth); + +/** @ingroup soundfont_loader */ +FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader); + +/** @ingroup soundfont_loader */ +FLUIDSYNTH_API fluid_preset_t *fluid_synth_get_channel_preset(fluid_synth_t *synth, int chan); + +/** @ingroup midi_input */ +FLUIDSYNTH_API int fluid_synth_handle_midi_event(void *data, fluid_midi_event_t *event); + +/** @ingroup soundfonts */ +FLUIDSYNTH_API +int fluid_synth_pin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num); + +/** @ingroup soundfonts */ +FLUIDSYNTH_API +int fluid_synth_unpin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num); + +/** @ingroup ladspa */ +FLUIDSYNTH_API fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SYNTH_H */ diff --git a/libs/fluidsynth/include/fluidsynth/types.h b/libs/fluidsynth/include/fluidsynth/types.h new file mode 100644 index 00000000000..9c2aaadacaa --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/types.h @@ -0,0 +1,85 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_TYPES_H +#define _FLUIDSYNTH_TYPES_H + + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup Types Types + * @brief Type declarations + * + * @{ + */ + +typedef struct _fluid_hashtable_t fluid_settings_t; /**< Configuration settings instance */ +typedef struct _fluid_synth_t fluid_synth_t; /**< Synthesizer instance */ +typedef struct _fluid_voice_t fluid_voice_t; /**< Synthesis voice instance */ +typedef struct _fluid_sfloader_t fluid_sfloader_t; /**< SoundFont loader plugin */ +typedef struct _fluid_sfont_t fluid_sfont_t; /**< SoundFont */ +typedef struct _fluid_preset_t fluid_preset_t; /**< SoundFont preset */ +typedef struct _fluid_sample_t fluid_sample_t; /**< SoundFont sample */ +typedef struct _fluid_mod_t fluid_mod_t; /**< SoundFont modulator */ +typedef struct _fluid_audio_driver_t fluid_audio_driver_t; /**< Audio driver instance */ +typedef struct _fluid_file_renderer_t fluid_file_renderer_t; /**< Audio file renderer instance */ +typedef struct _fluid_player_t fluid_player_t; /**< MIDI player instance */ +typedef struct _fluid_midi_event_t fluid_midi_event_t; /**< MIDI event */ +typedef struct _fluid_midi_driver_t fluid_midi_driver_t; /**< MIDI driver instance */ +typedef struct _fluid_midi_router_t fluid_midi_router_t; /**< MIDI router instance */ +typedef struct _fluid_midi_router_rule_t fluid_midi_router_rule_t; /**< MIDI router rule */ +typedef struct _fluid_hashtable_t fluid_cmd_hash_t; /**< Command handler hash table */ +typedef struct _fluid_shell_t fluid_shell_t; /**< Command shell */ +typedef struct _fluid_server_t fluid_server_t; /**< TCP/IP shell server instance */ +typedef struct _fluid_event_t fluid_event_t; /**< Sequencer event */ +typedef struct _fluid_sequencer_t fluid_sequencer_t; /**< Sequencer instance */ +typedef struct _fluid_ramsfont_t fluid_ramsfont_t; /**< RAM SoundFont */ +typedef struct _fluid_rampreset_t fluid_rampreset_t; /**< RAM SoundFont preset */ +typedef struct _fluid_cmd_handler_t fluid_cmd_handler_t; /**< Shell Command Handler */ +typedef struct _fluid_ladspa_fx_t fluid_ladspa_fx_t; /**< LADSPA effects instance */ +typedef struct _fluid_file_callbacks_t fluid_file_callbacks_t; /**< Callback struct to perform custom file loading of soundfonts */ + +typedef int fluid_istream_t; /**< Input stream descriptor */ +typedef int fluid_ostream_t; /**< Output stream descriptor */ + +typedef short fluid_seq_id_t; /**< Unique client IDs used by the sequencer and #fluid_event_t, obtained by fluid_sequencer_register_client() and fluid_sequencer_register_fluidsynth() */ + +#if defined(_MSC_VER) && (_MSC_VER < 1800) +typedef __int64 fluid_long_long_t; // even on 32bit windows +#else +/** + * A typedef for C99's type long long, which is at least 64-bit wide, as guaranteed by the C99. + * @p __int64 will be used as replacement for VisualStudio 2010 and older. + */ +typedef long long fluid_long_long_t; +#endif + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_TYPES_H */ diff --git a/libs/fluidsynth/include/fluidsynth/version.h b/libs/fluidsynth/include/fluidsynth/version.h new file mode 100644 index 00000000000..2de6e9fbf4d --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/version.h @@ -0,0 +1,47 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_VERSION_H +#define _FLUIDSYNTH_VERSION_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup misc + * + * @{ + */ +#define FLUIDSYNTH_VERSION "2.3.3" /**< String constant of libfluidsynth version. */ +#define FLUIDSYNTH_VERSION_MAJOR 2 /**< libfluidsynth major version integer constant. */ +#define FLUIDSYNTH_VERSION_MINOR 3 /**< libfluidsynth minor version integer constant. */ +#define FLUIDSYNTH_VERSION_MICRO 3 /**< libfluidsynth micro version integer constant. */ + +FLUIDSYNTH_API void fluid_version(int *major, int *minor, int *micro); +FLUIDSYNTH_API char* fluid_version_str(void); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_VERSION_H */ diff --git a/libs/fluidsynth/include/fluidsynth/voice.h b/libs/fluidsynth/include/fluidsynth/voice.h new file mode 100644 index 00000000000..44f31e5fe18 --- /dev/null +++ b/libs/fluidsynth/include/fluidsynth/voice.h @@ -0,0 +1,76 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_VOICE_H +#define _FLUIDSYNTH_VOICE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup voices Voice Manipulation + * @ingroup soundfonts + * + * Synthesis voice manipulation functions. + * + * The interface to the synthesizer's voices. + * Examples on using them can be found in the source code of the default SoundFont + * loader (fluid_defsfont.c). + * + * Most of these functions should only be called from within synthesis context, + * such as the SoundFont loader's noteon method. + * + * @{ + */ + +/** + * Enum used with fluid_voice_add_mod() to specify how to handle duplicate modulators. + */ +enum fluid_voice_add_mod +{ + FLUID_VOICE_OVERWRITE, /**< Overwrite any existing matching modulator */ + FLUID_VOICE_ADD, /**< Add (sum) modulator amounts */ + FLUID_VOICE_DEFAULT /**< For default modulators only, no need to check for duplicates */ +}; + +FLUIDSYNTH_API void fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode); +FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t *voice, int gen); +FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t *voice, int gen, float val); +FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t *voice, int gen, float val); + +FLUIDSYNTH_API unsigned int fluid_voice_get_id(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_channel(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_key(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_actual_key(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_velocity(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_actual_velocity(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_playing(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_on(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_sustained(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_sostenuto(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t *s); +FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t *voice, int gen); +/** @} */ + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_VOICE_H */ diff --git a/libs/fluidsynth/src/bindings/fluid_ladspa.h b/libs/fluidsynth/src/bindings/fluid_ladspa.h new file mode 100644 index 00000000000..80952d51ea9 --- /dev/null +++ b/libs/fluidsynth/src/bindings/fluid_ladspa.h @@ -0,0 +1,36 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_LADSPA_H +#define _FLUID_LADSPA_H + +#include "fluid_sys.h" + +fluid_ladspa_fx_t *new_fluid_ladspa_fx(fluid_real_t sample_rate, int buffer_size); +void delete_fluid_ladspa_fx(fluid_ladspa_fx_t *fx); + +int fluid_ladspa_set_sample_rate(fluid_ladspa_fx_t *fx, fluid_real_t sample_rate); + +void fluid_ladspa_run(fluid_ladspa_fx_t *fx, int block_count, int block_size); + +int fluid_ladspa_add_host_ports(fluid_ladspa_fx_t *fx, const char *prefix, + int num_buffers, fluid_real_t buffers[], int buf_stride); + +#endif /* _FLUID_LADSPA_H */ diff --git a/libs/fluidsynth/src/midi/fluid_midi.c b/libs/fluidsynth/src/midi/fluid_midi.c new file mode 100644 index 00000000000..6ea0216cfb3 --- /dev/null +++ b/libs/fluidsynth/src/midi/fluid_midi.c @@ -0,0 +1,2851 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_midi.h" +#include "fluid_sys.h" +#include "fluid_synth.h" +#include "fluid_settings.h" + + +static int fluid_midi_event_length(unsigned char event); +static int fluid_isasciistring(char *s); +static long fluid_getlength(const unsigned char *s); + + +/* Read the entire contents of a file into memory, allocating enough memory + * for the file, and returning the length and the buffer. + * Note: This rewinds the file to the start before reading. + * Returns NULL if there was an error reading or allocating memory. + */ +typedef FILE *fluid_file; +static char *fluid_file_read_full(fluid_file fp, size_t *length); +static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic); +static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size); +#define READ_FULL_INITIAL_BUFLEN 1024 + +static fluid_track_t *new_fluid_track(int num); +static void delete_fluid_track(fluid_track_t *track); +static int fluid_track_set_name(fluid_track_t *track, char *name); +static int fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt); +static fluid_midi_event_t *fluid_track_next_event(fluid_track_t *track); +static int fluid_track_get_duration(fluid_track_t *track); +static int fluid_track_reset(fluid_track_t *track); + +static int fluid_player_add_track(fluid_player_t *player, fluid_track_t *track); +static int fluid_player_callback(void *data, unsigned int msec); +static int fluid_player_reset(fluid_player_t *player); +static int fluid_player_load(fluid_player_t *player, fluid_playlist_item *item); +static void fluid_player_advancefile(fluid_player_t *player); +static void fluid_player_playlist_load(fluid_player_t *player, unsigned int msec); +static void fluid_player_update_tempo(fluid_player_t *player); + +static fluid_midi_file *new_fluid_midi_file(const char *buffer, size_t length); +static void delete_fluid_midi_file(fluid_midi_file *mf); +static int fluid_midi_file_read_mthd(fluid_midi_file *midifile); +static int fluid_midi_file_load_tracks(fluid_midi_file *midifile, fluid_player_t *player); +static int fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num); +static int fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track); +static int fluid_midi_file_read_varlen(fluid_midi_file *mf); +static int fluid_midi_file_getc(fluid_midi_file *mf); +static int fluid_midi_file_push(fluid_midi_file *mf, int c); +static int fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len); +static int fluid_midi_file_skip(fluid_midi_file *mf, int len); +static int fluid_midi_file_eof(fluid_midi_file *mf); +static int fluid_midi_file_read_tracklen(fluid_midi_file *mf); +static int fluid_midi_file_eot(fluid_midi_file *mf); +static int fluid_midi_file_get_division(fluid_midi_file *midifile); + + +/*************************************************************** + * + * MIDIFILE + */ + +/** + * Check if a file is a MIDI file. + * @param filename Path to the file to check + * @return TRUE if it could be a MIDI file, FALSE otherwise + * + * The current implementation only checks for the "MThd" header in the file. + * It is useful only to distinguish between SoundFont and MIDI files. + */ +int fluid_is_midifile(const char *filename) +{ + FILE *fp; + uint32_t id; + int retcode = FALSE; + + do + { + if((fp = fluid_file_open(filename, NULL)) == NULL) + { + return retcode; + } + + if(FLUID_FREAD(&id, sizeof(id), 1, fp) != 1) + { + break; + } + + retcode = (id == FLUID_FOURCC('M', 'T', 'h', 'd')); + } + while(0); + + FLUID_FCLOSE(fp); + + return retcode; +} + +/** + * Return a new MIDI file handle for parsing an already-loaded MIDI file. + * @internal + * @param buffer Pointer to full contents of MIDI file (borrows the pointer). + * The caller must not free buffer until after the fluid_midi_file is deleted. + * @param length Size of the buffer in bytes. + * @return New MIDI file handle or NULL on error. + */ +fluid_midi_file * +new_fluid_midi_file(const char *buffer, size_t length) +{ + fluid_midi_file *mf; + + if(length > INT_MAX) + { + FLUID_LOG(FLUID_ERR, "Refusing to open a MIDI file which is bigger than 2GiB"); + return NULL; + } + + mf = FLUID_NEW(fluid_midi_file); + if(mf == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file)); + + mf->c = -1; + mf->running_status = -1; + + mf->buffer = buffer; + mf->buf_len = (int)length; + mf->buf_pos = 0; + mf->eof = FALSE; + + if(fluid_midi_file_read_mthd(mf) != FLUID_OK) + { + FLUID_FREE(mf); + return NULL; + } + + return mf; +} + +static char * +fluid_file_read_full(fluid_file fp, size_t *length) +{ + size_t buflen; + char *buffer; + size_t n; + + /* Work out the length of the file in advance */ + if(FLUID_FSEEK(fp, 0, SEEK_END) != 0) + { + FLUID_LOG(FLUID_ERR, "File load: Could not seek within file"); + return NULL; + } + + buflen = ftell(fp); + + if(FLUID_FSEEK(fp, 0, SEEK_SET) != 0) + { + FLUID_LOG(FLUID_ERR, "File load: Could not seek within file"); + return NULL; + } + + FLUID_LOG(FLUID_DBG, "File load: Allocating %lu bytes", (unsigned long)buflen); + buffer = FLUID_MALLOC(buflen); + + if(buffer == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } + + n = FLUID_FREAD(buffer, 1, buflen, fp); + + if(n != buflen) + { + FLUID_LOG(FLUID_ERR, "Only read %lu bytes; expected %lu", (unsigned long)n, + (unsigned long)buflen); + FLUID_FREE(buffer); + return NULL; + }; + + *length = n; + + return buffer; +} + +/** + * Delete a MIDI file handle. + * @internal + * @param mf MIDI file handle to close and free. + */ +void +delete_fluid_midi_file(fluid_midi_file *mf) +{ + fluid_return_if_fail(mf != NULL); + + FLUID_FREE(mf); +} + +/* + * Gets the next byte in a MIDI file, taking into account previous running status. + * + * returns -1 if EOF or read error + */ +int +fluid_midi_file_getc(fluid_midi_file *mf) +{ + unsigned char c; + + if(mf->c >= 0) + { + c = mf->c; + mf->c = -1; + } + else + { + if(mf->buf_pos >= mf->buf_len) + { + mf->eof = TRUE; + return -1; + } + + c = mf->buffer[mf->buf_pos++]; + mf->trackpos++; + } + + return (int) c; +} + +/* + * Saves a byte to be returned the next time fluid_midi_file_getc() is called, + * when it is necessary according to running status. + */ +int +fluid_midi_file_push(fluid_midi_file *mf, int c) +{ + mf->c = c; + return FLUID_OK; +} + +/* + * fluid_midi_file_read + */ +int +fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len) +{ + int num = len < mf->buf_len - mf->buf_pos + ? len : mf->buf_len - mf->buf_pos; + + if(num != len) + { + mf->eof = TRUE; + } + + if(num < 0) + { + num = 0; + } + + /* Note: Read bytes, even if there aren't enough, but only increment + * trackpos if successful (emulates old behaviour of fluid_midi_file_read) + */ + FLUID_MEMCPY(buf, mf->buffer + mf->buf_pos, num); + mf->buf_pos += num; + + if(num == len) + { + mf->trackpos += num; + } + +#if DEBUG + else + { + FLUID_LOG(FLUID_DBG, "Could not read the requested number of bytes"); + } + +#endif + return (num != len) ? FLUID_FAILED : FLUID_OK; +} + +/* + * fluid_midi_file_skip + */ +int +fluid_midi_file_skip(fluid_midi_file *mf, int skip) +{ + int new_pos = mf->buf_pos + skip; + + /* Mimic the behaviour of fseek: Error to seek past the start of file, but + * OK to seek past end (this just puts it into the EOF state). */ + if(new_pos < 0) + { + FLUID_LOG(FLUID_ERR, "Failed to seek position in file"); + return FLUID_FAILED; + } + + /* Clear the EOF flag, even if moved past the end of the file (this is + * consistent with the behaviour of fseek). */ + mf->eof = FALSE; + mf->buf_pos = new_pos; + return FLUID_OK; +} + +/* + * fluid_midi_file_eof + */ +int fluid_midi_file_eof(fluid_midi_file *mf) +{ + /* Note: This does not simply test whether the file read pointer is past + * the end of the file. It mimics the behaviour of feof by actually + * testing the stateful EOF condition, which is set to TRUE if getc or + * fread have attempted to read past the end (but not if they have + * precisely reached the end), but reset to FALSE upon a successful seek. + */ + return mf->eof; +} + +/* + * fluid_midi_file_read_mthd + */ +int +fluid_midi_file_read_mthd(fluid_midi_file *mf) +{ + char mthd[14]; + + if(fluid_midi_file_read(mf, mthd, sizeof(mthd)) != FLUID_OK) + { + return FLUID_FAILED; + } + + if((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6) + || (mthd[9] > 2)) + { + FLUID_LOG(FLUID_ERR, + "Doesn't look like a MIDI file: invalid MThd header"); + return FLUID_FAILED; + } + + mf->type = mthd[9]; + mf->ntracks = (unsigned) mthd[11]; + mf->ntracks += (unsigned int)(mthd[10]) << 16; + + if((signed char)mthd[12] < 0) + { + mf->uses_smpte = 1; + mf->smpte_fps = -(signed char)mthd[12]; + mf->smpte_res = (unsigned) mthd[13]; + FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet"); + return FLUID_FAILED; + } + else + { + mf->uses_smpte = 0; + mf->division = ((unsigned)mthd[12] << 8) | ((unsigned)mthd[13] & 0xff); + FLUID_LOG(FLUID_DBG, "Division=%d", mf->division); + } + + return FLUID_OK; +} + +/* + * fluid_midi_file_load_tracks + */ +int +fluid_midi_file_load_tracks(fluid_midi_file *mf, fluid_player_t *player) +{ + int i; + + for(i = 0; i < mf->ntracks; i++) + { + if(fluid_midi_file_read_track(mf, player, i) != FLUID_OK) + { + return FLUID_FAILED; + } + } + + return FLUID_OK; +} + +/* + * fluid_isasciistring + */ +int +fluid_isasciistring(char *s) +{ + /* From ctype.h */ +#define fluid_isascii(c) (((c) & ~0x7f) == 0) + + size_t i, len = FLUID_STRLEN(s); + + for(i = 0; i < len; i++) + { + if(!fluid_isascii(s[i])) + { + return 0; + } + } + + return 1; + +#undef fluid_isascii +} + +/* + * fluid_getlength + */ +long +fluid_getlength(const unsigned char *s) +{ + long i = 0; + i = s[3] | (s[2] << 8) | (s[1] << 16) | (s[0] << 24); + return i; +} + +/* + * fluid_midi_file_read_tracklen + */ +int +fluid_midi_file_read_tracklen(fluid_midi_file *mf) +{ + unsigned char length[5]; + + if(fluid_midi_file_read(mf, length, 4) != FLUID_OK) + { + return FLUID_FAILED; + } + + mf->tracklen = fluid_getlength(length); + mf->trackpos = 0; + mf->eot = 0; + return FLUID_OK; +} + +/* + * fluid_midi_file_eot + */ +int +fluid_midi_file_eot(fluid_midi_file *mf) +{ +#if DEBUG + + if(mf->trackpos > mf->tracklen) + { + printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen); + } + +#endif + return mf->eot || (mf->trackpos >= mf->tracklen); +} + +/* + * fluid_midi_file_read_track + */ +int +fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num) +{ + fluid_track_t *track; + unsigned char id[5], length[5]; + int found_track = 0; + int skip; + + if(fluid_midi_file_read(mf, id, 4) != FLUID_OK) + { + return FLUID_FAILED; + } + + id[4] = '\0'; + mf->dtime = 0; + + while(!found_track) + { + + if(fluid_isasciistring((char *) id) == 0) + { + FLUID_LOG(FLUID_ERR, + "A non-ascii track header found, corrupt file"); + return FLUID_FAILED; + + } + else if(FLUID_STRCMP((char *) id, "MTrk") == 0) + { + + found_track = 1; + + if(fluid_midi_file_read_tracklen(mf) != FLUID_OK) + { + return FLUID_FAILED; + } + + track = new_fluid_track(num); + + if(track == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + while(!fluid_midi_file_eot(mf)) + { + if(fluid_midi_file_read_event(mf, track) != FLUID_OK) + { + delete_fluid_track(track); + return FLUID_FAILED; + } + } + + /* Skip remaining track data, if any */ + if(mf->trackpos < mf->tracklen) + { + if(fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos) != FLUID_OK) + { + delete_fluid_track(track); + return FLUID_FAILED; + } + } + + if(fluid_player_add_track(player, track) != FLUID_OK) + { + delete_fluid_track(track); + return FLUID_FAILED; + } + + } + else + { + found_track = 0; + + if(fluid_midi_file_read(mf, length, 4) != FLUID_OK) + { + return FLUID_FAILED; + } + + skip = fluid_getlength(length); + + /* fseek(mf->fp, skip, SEEK_CUR); */ + if(fluid_midi_file_skip(mf, skip) != FLUID_OK) + { + return FLUID_FAILED; + } + } + } + + if(fluid_midi_file_eof(mf)) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + return FLUID_OK; +} + +/* + * fluid_midi_file_read_varlen + */ +int +fluid_midi_file_read_varlen(fluid_midi_file *mf) +{ + int i; + int c; + mf->varlen = 0; + + for(i = 0;; i++) + { + if(i == 4) + { + FLUID_LOG(FLUID_ERR, "Invalid variable length number"); + return FLUID_FAILED; + } + + c = fluid_midi_file_getc(mf); + + if(c < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + if(c & 0x80) + { + mf->varlen |= (int)(c & 0x7F); + mf->varlen <<= 7; + } + else + { + mf->varlen += c; + break; + } + } + + return FLUID_OK; +} + +/* + * fluid_midi_file_read_event + */ +int +fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track) +{ + int status; + int type; + int tempo; + unsigned char *metadata = NULL; + unsigned char *dyn_buf = NULL; + unsigned char static_buf[256]; + int nominator, denominator, clocks, notes; + fluid_midi_event_t *evt; + int channel = 0; + int param1 = 0; + int param2 = 0; + int size; + + /* read the delta-time of the event */ + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { + return FLUID_FAILED; + } + + mf->dtime += mf->varlen; + + /* read the status byte */ + status = fluid_midi_file_getc(mf); + + if(status < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + /* not a valid status byte: use the running status instead */ + if((status & 0x80) == 0) + { + if((mf->running_status & 0x80) == 0) + { + FLUID_LOG(FLUID_ERR, "Undefined status and invalid running status"); + return FLUID_FAILED; + } + + fluid_midi_file_push(mf, status); + status = mf->running_status; + } + + /* check what message we have */ + + mf->running_status = status; + + if(status == MIDI_SYSEX) /* system exclusif */ + { + /* read the length of the message */ + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { + return FLUID_FAILED; + } + + if(mf->varlen) + { + FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, + __LINE__, mf->varlen); + metadata = FLUID_MALLOC(mf->varlen + 1); + + if(metadata == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + /* read the data of the message */ + if(fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) + { + FLUID_FREE(metadata); + return FLUID_FAILED; + } + + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(metadata); + return FLUID_FAILED; + } + + evt->dtime = mf->dtime; + size = mf->varlen; + + if(metadata[mf->varlen - 1] == MIDI_EOX) + { + size--; + } + + /* Add SYSEX event and indicate that its dynamically allocated and should be freed with event */ + fluid_midi_event_set_sysex(evt, metadata, size, TRUE); + fluid_track_add_event(track, evt); + mf->dtime = 0; + } + + return FLUID_OK; + + } + else if(status == MIDI_META_EVENT) /* meta events */ + { + + int result = FLUID_OK; + + /* get the type of the meta message */ + type = fluid_midi_file_getc(mf); + + if(type < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + /* get the length of the data part */ + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { + return FLUID_FAILED; + } + + if(mf->varlen < 255) + { + metadata = &static_buf[0]; + } + else + { + FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, + __LINE__, mf->varlen); + dyn_buf = FLUID_MALLOC(mf->varlen + 1); + + if(dyn_buf == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + metadata = dyn_buf; + } + + /* read the data */ + if(mf->varlen) + { + if(fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) + { + if(dyn_buf) + { + FLUID_FREE(dyn_buf); + } + + return FLUID_FAILED; + } + } + + /* handle meta data */ + switch(type) + { + + case MIDI_COPYRIGHT: + metadata[mf->varlen] = 0; + break; + + case MIDI_TRACK_NAME: + metadata[mf->varlen] = 0; + fluid_track_set_name(track, (char *) metadata); + break; + + case MIDI_INST_NAME: + metadata[mf->varlen] = 0; + break; + + case MIDI_LYRIC: + case MIDI_TEXT: + { + void *tmp; + int size = mf->varlen + 1; + + /* NULL terminate strings for safety */ + metadata[size - 1] = '\0'; + + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; + break; + } + + evt->dtime = mf->dtime; + + tmp = FLUID_MALLOC(size); + + if(tmp == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + delete_fluid_midi_event(evt); + evt = NULL; + result = FLUID_FAILED; + break; + } + + FLUID_MEMCPY(tmp, metadata, size); + + fluid_midi_event_set_sysex_LOCAL(evt, type, tmp, size, TRUE); + fluid_track_add_event(track, evt); + mf->dtime = 0; + } + break; + + case MIDI_MARKER: + break; + + case MIDI_CUE_POINT: + break; /* don't care much for text events */ + + case MIDI_EOT: + if(mf->varlen != 0) + { + FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event"); + result = FLUID_FAILED; + break; + } + + mf->eot = 1; + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; + break; + } + + evt->dtime = mf->dtime; + evt->type = MIDI_EOT; + fluid_track_add_event(track, evt); + mf->dtime = 0; + break; + + case MIDI_SET_TEMPO: + if(mf->varlen != 3) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for SetTempo meta event"); + result = FLUID_FAILED; + break; + } + + tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2]; + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; + break; + } + + evt->dtime = mf->dtime; + evt->type = MIDI_SET_TEMPO; + evt->channel = 0; + evt->param1 = tempo; + evt->param2 = 0; + fluid_track_add_event(track, evt); + mf->dtime = 0; + break; + + case MIDI_SMPTE_OFFSET: + if(mf->varlen != 5) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for SMPTE Offset meta event"); + result = FLUID_FAILED; + break; + } + + break; /* we don't use smtp */ + + case MIDI_TIME_SIGNATURE: + if(mf->varlen != 4) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for TimeSignature meta event"); + result = FLUID_FAILED; + break; + } + + nominator = metadata[0]; + denominator = pow(2.0, (double) metadata[1]); + clocks = metadata[2]; + notes = metadata[3]; + + FLUID_LOG(FLUID_DBG, + "signature=%d/%d, metronome=%d, 32nd-notes=%d", + nominator, denominator, clocks, notes); + + break; + + case MIDI_KEY_SIGNATURE: + if(mf->varlen != 2) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for KeySignature meta event"); + result = FLUID_FAILED; + break; + } + + /* We don't care about key signatures anyway */ + /* sf = metadata[0]; + mi = metadata[1]; */ + break; + + case MIDI_SEQUENCER_EVENT: + break; + + default: + break; + } + + if(dyn_buf) + { + FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__); + FLUID_FREE(dyn_buf); + } + + return result; + + } + else /* channel messages */ + { + + type = status & 0xf0; + channel = status & 0x0f; + + /* all channel message have at least 1 byte of associated data */ + if((param1 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + switch(type) + { + + case NOTE_ON: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + break; + + case NOTE_OFF: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + break; + + case KEY_PRESSURE: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + break; + + case CONTROL_CHANGE: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + break; + + case PROGRAM_CHANGE: + break; + + case CHANNEL_PRESSURE: + break; + + case PITCH_BEND: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f); + param2 = 0; + break; + + default: + /* Can't possibly happen !? */ + FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event"); + return FLUID_FAILED; + } + + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + evt->dtime = mf->dtime; + evt->type = type; + evt->channel = channel; + evt->param1 = param1; + evt->param2 = param2; + fluid_track_add_event(track, evt); + mf->dtime = 0; + } + + return FLUID_OK; +} + +/* + * fluid_midi_file_get_division + */ +int +fluid_midi_file_get_division(fluid_midi_file *midifile) +{ + return midifile->division; +} + +/****************************************************** + * + * fluid_track_t + */ + +/** + * Create a MIDI event structure. + * @return New MIDI event structure or NULL when out of memory. + */ +fluid_midi_event_t * +new_fluid_midi_event() +{ + fluid_midi_event_t *evt; + evt = FLUID_NEW(fluid_midi_event_t); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + evt->dtime = 0; + evt->type = 0; + evt->channel = 0; + evt->param1 = 0; + evt->param2 = 0; + evt->next = NULL; + evt->paramptr = NULL; + return evt; +} + +/** + * Delete MIDI event structure. + * @param evt MIDI event structure + */ +void +delete_fluid_midi_event(fluid_midi_event_t *evt) +{ + fluid_midi_event_t *temp; + fluid_return_if_fail(evt != NULL); + + while(evt) + { + temp = evt->next; + + /* Dynamic SYSEX event? - free (param2 indicates if dynamic) */ + if((evt->type == MIDI_SYSEX || (evt-> type == MIDI_TEXT) || (evt->type == MIDI_LYRIC)) && + evt->paramptr && evt->param2) + { + FLUID_FREE(evt->paramptr); + } + + FLUID_FREE(evt); + evt = temp; + } +} + +/** + * Get the event type field of a MIDI event structure. + * @param evt MIDI event structure + * @return Event type field (MIDI status byte without channel) + */ +int +fluid_midi_event_get_type(const fluid_midi_event_t *evt) +{ + return evt->type; +} + +/** + * Set the event type field of a MIDI event structure. + * @param evt MIDI event structure + * @param type Event type field (MIDI status byte without channel) + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_type(fluid_midi_event_t *evt, int type) +{ + evt->type = type; + return FLUID_OK; +} + +/** + * Get the channel field of a MIDI event structure. + * @param evt MIDI event structure + * @return Channel field + */ +int +fluid_midi_event_get_channel(const fluid_midi_event_t *evt) +{ + return evt->channel; +} + +/** + * Set the channel field of a MIDI event structure. + * @param evt MIDI event structure + * @param chan MIDI channel field + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_channel(fluid_midi_event_t *evt, int chan) +{ + evt->channel = chan; + return FLUID_OK; +} + +/** + * Get the key field of a MIDI event structure. + * @param evt MIDI event structure + * @return MIDI note number (0-127) + */ +int +fluid_midi_event_get_key(const fluid_midi_event_t *evt) +{ + return evt->param1; +} + +/** + * Set the key field of a MIDI event structure. + * @param evt MIDI event structure + * @param v MIDI note number (0-127) + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_key(fluid_midi_event_t *evt, int v) +{ + evt->param1 = v; + return FLUID_OK; +} + +/** + * Get the velocity field of a MIDI event structure. + * @param evt MIDI event structure + * @return MIDI velocity number (0-127) + */ +int +fluid_midi_event_get_velocity(const fluid_midi_event_t *evt) +{ + return evt->param2; +} + +/** + * Set the velocity field of a MIDI event structure. + * @param evt MIDI event structure + * @param v MIDI velocity value + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_velocity(fluid_midi_event_t *evt, int v) +{ + evt->param2 = v; + return FLUID_OK; +} + +/** + * Get the control number of a MIDI event structure. + * @param evt MIDI event structure + * @return MIDI control number + */ +int +fluid_midi_event_get_control(const fluid_midi_event_t *evt) +{ + return evt->param1; +} + +/** + * Set the control field of a MIDI event structure. + * @param evt MIDI event structure + * @param v MIDI control number + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_control(fluid_midi_event_t *evt, int v) +{ + evt->param1 = v; + return FLUID_OK; +} + +/** + * Get the value field from a MIDI event structure. + * @param evt MIDI event structure + * @return Value field + */ +int +fluid_midi_event_get_value(const fluid_midi_event_t *evt) +{ + return evt->param2; +} + +/** + * Set the value field of a MIDI event structure. + * @param evt MIDI event structure + * @param v Value to assign + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_value(fluid_midi_event_t *evt, int v) +{ + evt->param2 = v; + return FLUID_OK; +} + +/** + * Get the program field of a MIDI event structure. + * @param evt MIDI event structure + * @return MIDI program number (0-127) + */ +int +fluid_midi_event_get_program(const fluid_midi_event_t *evt) +{ + return evt->param1; +} + +/** + * Set the program field of a MIDI event structure. + * @param evt MIDI event structure + * @param val MIDI program number (0-127) + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_program(fluid_midi_event_t *evt, int val) +{ + evt->param1 = val; + return FLUID_OK; +} + +/** + * Get the pitch field of a MIDI event structure. + * @param evt MIDI event structure + * @return Pitch value (14 bit value, 0-16383, 8192 is center) + */ +int +fluid_midi_event_get_pitch(const fluid_midi_event_t *evt) +{ + return evt->param1; +} + +/** + * Set the pitch field of a MIDI event structure. + * @param evt MIDI event structure + * @param val Pitch value (14 bit value, 0-16383, 8192 is center) + * @return Always returns FLUID_OK + */ +int +fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val) +{ + evt->param1 = val; + return FLUID_OK; +} + +/** + * Assign sysex data to a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to SYSEX data + * @param size Size of SYSEX data in bytes + * @param dynamic TRUE if the SYSEX data has been dynamically allocated and + * should be freed when the event is freed (only applies if event gets destroyed + * with delete_fluid_midi_event()) + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic) +{ + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_SYSEX, data, size, dynamic); + return FLUID_OK; +} + +/** + * Assign text data to a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to text data + * @param size Size of text data in bytes + * @param dynamic TRUE if the data has been dynamically allocated and + * should be freed when the event is freed via delete_fluid_midi_event() + * @return Always returns #FLUID_OK + * + * @since 2.0.0 + */ +int +fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic) +{ + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_TEXT, data, size, dynamic); + return FLUID_OK; +} + +/** + * Get the text of a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to return text data on. + * @param size Pointer to return text size on. + * @return Returns #FLUID_OK if \p data and \p size previously set by + * fluid_midi_event_set_text() have been successfully retrieved. + * Else #FLUID_FAILED is returned and \p data and \p size are not changed. + * @since 2.0.3 + */ +int fluid_midi_event_get_text(fluid_midi_event_t *evt, void **data, int *size) +{ + fluid_return_val_if_fail(evt != NULL, FLUID_FAILED); + fluid_return_val_if_fail(evt->type == MIDI_TEXT, FLUID_FAILED); + + fluid_midi_event_get_sysex_LOCAL(evt, data, size); + return FLUID_OK; +} + +/** + * Assign lyric data to a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to lyric data + * @param size Size of lyric data in bytes + * @param dynamic TRUE if the data has been dynamically allocated and + * should be freed when the event is freed via delete_fluid_midi_event() + * @return Always returns #FLUID_OK + * + * @since 2.0.0 + */ +int +fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic) +{ + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_LYRIC, data, size, dynamic); + return FLUID_OK; +} + +/** + * Get the lyric of a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to return lyric data on. + * @param size Pointer to return lyric size on. + * @return Returns #FLUID_OK if \p data and \p size previously set by + * fluid_midi_event_set_lyrics() have been successfully retrieved. + * Else #FLUID_FAILED is returned and \p data and \p size are not changed. + * @since 2.0.3 + */ +int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt, void **data, int *size) +{ + fluid_return_val_if_fail(evt != NULL, FLUID_FAILED); + fluid_return_val_if_fail(evt->type == MIDI_LYRIC, FLUID_FAILED); + + fluid_midi_event_get_sysex_LOCAL(evt, data, size); + return FLUID_OK; +} + +static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic) +{ + evt->type = type; + evt->paramptr = data; + evt->param1 = size; + evt->param2 = dynamic; +} + +static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size) +{ + if(data) + { + *data = evt->paramptr; + } + + if(size) + { + *size = evt->param1; + } +} + +/****************************************************** + * + * fluid_track_t + */ + +/* + * new_fluid_track + */ +fluid_track_t * +new_fluid_track(int num) +{ + fluid_track_t *track; + track = FLUID_NEW(fluid_track_t); + + if(track == NULL) + { + return NULL; + } + + track->name = NULL; + track->num = num; + track->first = NULL; + track->cur = NULL; + track->last = NULL; + track->ticks = 0; + return track; +} + +/* + * delete_fluid_track + */ +void +delete_fluid_track(fluid_track_t *track) +{ + fluid_return_if_fail(track != NULL); + + FLUID_FREE(track->name); + delete_fluid_midi_event(track->first); + FLUID_FREE(track); +} + +/* + * fluid_track_set_name + */ +int +fluid_track_set_name(fluid_track_t *track, char *name) +{ + size_t len; + + if(track->name != NULL) + { + FLUID_FREE(track->name); + } + + if(name == NULL) + { + track->name = NULL; + return FLUID_OK; + } + + len = FLUID_STRLEN(name); + track->name = FLUID_MALLOC(len + 1); + + if(track->name == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + FLUID_STRCPY(track->name, name); + return FLUID_OK; +} + +/* + * fluid_track_get_duration + */ +int +fluid_track_get_duration(fluid_track_t *track) +{ + int time = 0; + fluid_midi_event_t *evt = track->first; + + while(evt != NULL) + { + time += evt->dtime; + evt = evt->next; + } + + return time; +} + +/* + * fluid_track_add_event + */ +int +fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt) +{ + evt->next = NULL; + + if(track->first == NULL) + { + track->first = evt; + track->cur = evt; + track->last = evt; + } + else + { + track->last->next = evt; + track->last = evt; + } + + return FLUID_OK; +} + +/* + * fluid_track_next_event + */ +fluid_midi_event_t * +fluid_track_next_event(fluid_track_t *track) +{ + if(track->cur != NULL) + { + track->cur = track->cur->next; + } + + return track->cur; +} + +/* + * fluid_track_reset + */ +int +fluid_track_reset(fluid_track_t *track) +{ + track->ticks = 0; + track->cur = track->first; + return FLUID_OK; +} + +/* + * fluid_track_send_events + */ +static void +fluid_track_send_events(fluid_track_t *track, + fluid_synth_t *synth, + fluid_player_t *player, + unsigned int ticks, + int seek_ticks + ) +{ + fluid_midi_event_t *event; + int seeking = seek_ticks >= 0; + + if(seeking) + { + ticks = seek_ticks; /* update target ticks */ + + if(track->ticks > ticks) + { + fluid_track_reset(track); /* reset track if seeking backwards */ + } + } + + while(1) + { + + event = track->cur; + + if(event == NULL) + { + return; + } + + /* printf("track=%02d\tticks=%05u\ttrack=%05u\tdtime=%05u\tnext=%05u\n", */ + /* track->num, */ + /* ticks, */ + /* track->ticks, */ + /* event->dtime, */ + /* track->ticks + event->dtime); */ + + if(track->ticks + event->dtime > ticks) + { + return; + } + + track->ticks += event->dtime; + + if(!player || event->type == MIDI_EOT) + { + /* don't send EOT events to the callback */ + } + else if(seeking && track->ticks != ticks && (event->type == NOTE_ON || event->type == NOTE_OFF)) + { + /* skip on/off messages */ + } + else + { + if(player->playback_callback) + { + player->playback_callback(player->playback_userdata, event); + if(event->type == NOTE_ON && event->param2 != 0 && !player->channel_isplaying[event->channel]) + { + player->channel_isplaying[event->channel] = TRUE; + } + } + } + + if(event->type == MIDI_SET_TEMPO && player != NULL) + { + /* memorize the tempo change value coming from the MIDI file */ + fluid_atomic_int_set(&player->miditempo, event->param1); + fluid_player_update_tempo(player); + } + + fluid_track_next_event(track); + + } +} + +/****************************************************** + * + * fluid_player + */ +static void +fluid_player_handle_reset_synth(void *data, const char *name, int value) +{ + fluid_player_t *player = data; + fluid_return_if_fail(player != NULL); + + player->reset_synth_between_songs = value; +} + +/** + * Create a new MIDI player. + * @param synth Fluid synthesizer instance to create player for + * @return New MIDI player instance or NULL on error (out of memory) + */ +fluid_player_t * +new_fluid_player(fluid_synth_t *synth) +{ + int i; + fluid_player_t *player; + player = FLUID_NEW(fluid_player_t); + + if(player == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + fluid_atomic_int_set(&player->status, FLUID_PLAYER_READY); + fluid_atomic_int_set(&player->stopping, 0); + player->loop = 1; + player->ntracks = 0; + + for(i = 0; i < MAX_NUMBER_OF_TRACKS; i++) + { + player->track[i] = NULL; + } + + player->synth = synth; + player->system_timer = NULL; + player->sample_timer = NULL; + player->playlist = NULL; + player->currentfile = NULL; + player->division = 0; + + /* internal tempo (from MIDI file) in micro seconds per quarter note */ + player->sync_mode = 1; /* the player follows internal tempo change */ + player->miditempo = 500000; + /* external tempo in micro seconds per quarter note */ + player->exttempo = 500000; + /* tempo multiplier */ + player->multempo = 1.0F; + + player->deltatime = 4.0; + player->cur_msec = 0; + player->cur_ticks = 0; + player->end_msec = -1; + player->end_pedals_disabled = 0; + player->last_callback_ticks = -1; + fluid_atomic_int_set(&player->seek_ticks, -1); + fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth); + fluid_player_set_tick_callback(player, NULL, NULL); + player->use_system_timer = fluid_settings_str_equal(synth->settings, + "player.timing-source", "system"); + if(player->use_system_timer) + { + player->system_timer = new_fluid_timer((int) player->deltatime, + fluid_player_callback, player, TRUE, FALSE, TRUE); + + if(player->system_timer == NULL) + { + goto err; + } + } + else + { + player->sample_timer = new_fluid_sample_timer(player->synth, + fluid_player_callback, player); + + if(player->sample_timer == NULL) + { + goto err; + } + } + + fluid_settings_getint(synth->settings, "player.reset-synth", &i); + fluid_player_handle_reset_synth(player, NULL, i); + + fluid_settings_callback_int(synth->settings, "player.reset-synth", + fluid_player_handle_reset_synth, player); + + return player; + +err: + delete_fluid_player(player); + return NULL; +} + +/** + * Delete a MIDI player instance. + * @param player MIDI player instance + * @warning Do not call when the synthesizer associated to this \p player renders audio, + * i.e. an audio driver is running or any other synthesizer thread concurrently calls + * fluid_synth_process() or fluid_synth_nwrite_float() or fluid_synth_write_*() ! + */ +void +delete_fluid_player(fluid_player_t *player) +{ + fluid_list_t *q; + fluid_playlist_item *pi; + + fluid_return_if_fail(player != NULL); + + fluid_settings_callback_int(player->synth->settings, "player.reset-synth", + NULL, NULL); + + fluid_player_stop(player); + fluid_player_reset(player); + + delete_fluid_timer(player->system_timer); + delete_fluid_sample_timer(player->synth, player->sample_timer); + + while(player->playlist != NULL) + { + q = player->playlist->next; + pi = (fluid_playlist_item *) player->playlist->data; + FLUID_FREE(pi->filename); + FLUID_FREE(pi->buffer); + FLUID_FREE(pi); + delete1_fluid_list(player->playlist); + player->playlist = q; + } + + FLUID_FREE(player); +} + +/** + * Registers settings related to the MIDI player + */ +void +fluid_player_settings(fluid_settings_t *settings) +{ + /* player.timing-source can be either "system" (use system timer) + or "sample" (use timer based on number of written samples) */ + fluid_settings_register_str(settings, "player.timing-source", "sample", 0); + fluid_settings_add_option(settings, "player.timing-source", "sample"); + fluid_settings_add_option(settings, "player.timing-source", "system"); + + /* Selects whether the player should reset the synth between songs, or not. */ + fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1, FLUID_HINT_TOGGLED); +} + + +int +fluid_player_reset(fluid_player_t *player) +{ + int i; + + for(i = 0; i < MAX_NUMBER_OF_TRACKS; i++) + { + if(player->track[i] != NULL) + { + delete_fluid_track(player->track[i]); + player->track[i] = NULL; + } + } + + for(i = 0; i < MAX_NUMBER_OF_CHANNELS; i++) + { + player->channel_isplaying[i] = FALSE; + } + + /* player->current_file = NULL; */ + /* player->status = FLUID_PLAYER_READY; */ + /* player->loop = 1; */ + player->ntracks = 0; + player->division = 0; + player->miditempo = 500000; + player->deltatime = 4.0; + return 0; +} + +/* + * fluid_player_add_track + */ +int +fluid_player_add_track(fluid_player_t *player, fluid_track_t *track) +{ + if(player->ntracks < MAX_NUMBER_OF_TRACKS) + { + player->track[player->ntracks++] = track; + return FLUID_OK; + } + else + { + return FLUID_FAILED; + } +} + +/** + * Change the MIDI callback function. + * + * @param player MIDI player instance + * @param handler Pointer to callback function + * @param handler_data Parameter sent to the callback function + * @returns FLUID_OK + * + * This is usually set to fluid_synth_handle_midi_event(), but can optionally + * be changed to a user-defined function instead, for intercepting all MIDI + * messages sent to the synth. You can also use a midi router as the callback + * function to modify the MIDI messages before sending them to the synth. + * + * @since 1.1.4 + */ +int +fluid_player_set_playback_callback(fluid_player_t *player, + handle_midi_event_func_t handler, void *handler_data) +{ + player->playback_callback = handler; + player->playback_userdata = handler_data; + return FLUID_OK; +} + +/** + * Add a listener function for every MIDI tick change. + * + * @param player MIDI player instance + * @param handler Pointer to callback function + * @param handler_data Opaque parameter to be sent to the callback function + * @returns #FLUID_OK + * + * This callback is not set by default, but can optionally + * be changed to a user-defined function for intercepting all MIDI + * tick changes and react to them with precision. + * + * @since 2.2.0 + */ +int +fluid_player_set_tick_callback(fluid_player_t *player, handle_midi_tick_func_t handler, void *handler_data) +{ + player->tick_callback = handler; + player->tick_userdata = handler_data; + return FLUID_OK; +} + +/** + * Add a MIDI file to a player queue. + * @param player MIDI player instance + * @param midifile File name of the MIDI file to add + * @return #FLUID_OK or #FLUID_FAILED + */ +int +fluid_player_add(fluid_player_t *player, const char *midifile) +{ + fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item)); + char *f = FLUID_STRDUP(midifile); + + if(!pi || !f) + { + FLUID_FREE(pi); + FLUID_FREE(f); + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + pi->filename = f; + pi->buffer = NULL; + pi->buffer_len = 0; + player->playlist = fluid_list_append(player->playlist, pi); + return FLUID_OK; +} + +/** + * Add a MIDI file to a player queue, from a buffer in memory. + * @param player MIDI player instance + * @param buffer Pointer to memory containing the bytes of a complete MIDI + * file. The data is copied, so the caller may free or modify it immediately + * without affecting the playlist. + * @param len Length of the buffer, in bytes. + * @return #FLUID_OK or #FLUID_FAILED + */ +int +fluid_player_add_mem(fluid_player_t *player, const void *buffer, size_t len) +{ + /* Take a copy of the buffer, so the caller can free immediately. */ + fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item)); + void *buf_copy = FLUID_MALLOC(len); + + if(!pi || !buf_copy) + { + FLUID_FREE(pi); + FLUID_FREE(buf_copy); + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + FLUID_MEMCPY(buf_copy, buffer, len); + pi->filename = NULL; + pi->buffer = buf_copy; + pi->buffer_len = len; + player->playlist = fluid_list_append(player->playlist, pi); + return FLUID_OK; +} + +/* + * fluid_player_load + */ +int +fluid_player_load(fluid_player_t *player, fluid_playlist_item *item) +{ + fluid_midi_file *midifile; + char *buffer; + size_t buffer_length; + int buffer_owned; + + if(item->filename != NULL) + { + fluid_file fp; + /* This file is specified by filename; load the file from disk */ + FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__, + item->filename); + /* Read the entire contents of the file into the buffer */ + fp = FLUID_FOPEN(item->filename, "rb"); + + if(fp == NULL) + { + FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file"); + return FLUID_FAILED; + } + + buffer = fluid_file_read_full(fp, &buffer_length); + + FLUID_FCLOSE(fp); + + if(buffer == NULL) + { + return FLUID_FAILED; + } + + buffer_owned = 1; + } + else + { + /* This file is specified by a pre-loaded buffer; load from memory */ + FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile from memory (%p)", + __FILE__, __LINE__, item->buffer); + buffer = (char *) item->buffer; + buffer_length = item->buffer_len; + /* Do not free the buffer (it is owned by the playlist) */ + buffer_owned = 0; + } + + midifile = new_fluid_midi_file(buffer, buffer_length); + + if(midifile == NULL) + { + if(buffer_owned) + { + FLUID_FREE(buffer); + } + + return FLUID_FAILED; + } + + player->division = fluid_midi_file_get_division(midifile); + fluid_player_update_tempo(player); // Update deltatime + /*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */ + + if(fluid_midi_file_load_tracks(midifile, player) != FLUID_OK) + { + if(buffer_owned) + { + FLUID_FREE(buffer); + } + + delete_fluid_midi_file(midifile); + return FLUID_FAILED; + } + + delete_fluid_midi_file(midifile); + + if(buffer_owned) + { + FLUID_FREE(buffer); + } + + return FLUID_OK; +} + +void +fluid_player_advancefile(fluid_player_t *player) +{ + if(player->playlist == NULL) + { + return; /* No files to play */ + } + + if(player->currentfile != NULL) + { + player->currentfile = fluid_list_next(player->currentfile); + } + + if(player->currentfile == NULL) + { + if(player->loop == 0) + { + return; /* We're done playing */ + } + + if(player->loop > 0) + { + player->loop--; + } + + player->currentfile = player->playlist; + } +} + +void +fluid_player_playlist_load(fluid_player_t *player, unsigned int msec) +{ + fluid_playlist_item *current_playitem; + int i; + + do + { + fluid_player_advancefile(player); + + if(player->currentfile == NULL) + { + /* Failed to find next song, probably since we're finished */ + fluid_atomic_int_set(&player->status, FLUID_PLAYER_DONE); + return; + } + + fluid_player_reset(player); + current_playitem = (fluid_playlist_item *) player->currentfile->data; + } + while(fluid_player_load(player, current_playitem) != FLUID_OK); + + /* Successfully loaded midi file */ + + player->begin_msec = msec; + player->start_msec = msec; + player->start_ticks = 0; + player->cur_ticks = 0; + + for(i = 0; i < player->ntracks; i++) + { + if(player->track[i] != NULL) + { + fluid_track_reset(player->track[i]); + } + } +} + +/* + * fluid_player_callback + */ +int +fluid_player_callback(void *data, unsigned int msec) +{ + int i; + int loadnextfile; + int status = FLUID_PLAYER_DONE; + fluid_midi_event_t mute_event; + fluid_player_t *player; + fluid_synth_t *synth; + player = (fluid_player_t *) data; + synth = player->synth; + + loadnextfile = player->currentfile == NULL ? 1 : 0; + + fluid_midi_event_set_type(&mute_event, CONTROL_CHANGE); + mute_event.param1 = ALL_SOUND_OFF; + mute_event.param2 = 1; + + if(fluid_player_get_status(player) != FLUID_PLAYER_PLAYING) + { + if(fluid_atomic_int_get(&player->stopping)) + { + for(i = 0; i < synth->midi_channels; i++) + { + if(player->channel_isplaying[i]) + { + fluid_midi_event_set_channel(&mute_event, i); + player->playback_callback(player->playback_userdata, &mute_event); + player->channel_isplaying[i] = FALSE; + } + } + fluid_atomic_int_set(&player->stopping, 0); + } + return 1; + } + do + { + float deltatime; + int seek_ticks; + + if(loadnextfile) + { + loadnextfile = 0; + fluid_player_playlist_load(player, msec); + + if(player->currentfile == NULL) + { + return 0; + } + } + + if(msec < player->cur_msec) + { + // overflow of fluid_synth_get_ticks() + FLUID_LOG(FLUID_ERR, "The maximum playback duration has been reached. Terminating player!"); + fluid_player_stop(player); + fluid_player_seek(player, 0); + player->cur_ticks = 0; + return 0; + } + + player->cur_msec = msec; + deltatime = fluid_atomic_float_get(&player->deltatime); + player->cur_ticks = (player->start_ticks + + (int)((double)(player->cur_msec - player->start_msec) + / deltatime + 0.5)); /* 0.5 to average overall error when casting */ + + seek_ticks = fluid_atomic_int_get(&player->seek_ticks); + if(seek_ticks >= 0) + { + for(i = 0; i < synth->midi_channels; i++) + { + if(player->channel_isplaying[i]) + { + fluid_midi_event_set_channel(&mute_event, i); + player->playback_callback(player->playback_userdata, &mute_event); + player->channel_isplaying[i] = FALSE; + } + } + } + + for(i = 0; i < player->ntracks; i++) + { + fluid_track_send_events(player->track[i], synth, player, player->cur_ticks, seek_ticks); + if(!fluid_track_eot(player->track[i])) + { + status = FLUID_PLAYER_PLAYING; + } + } + + if(seek_ticks >= 0) + { + player->start_ticks = seek_ticks; /* tick position of last tempo value (which is now) */ + player->cur_ticks = seek_ticks; + player->begin_msec = msec; /* only used to calculate the duration of playing */ + player->start_msec = msec; /* should be the (synth)-time of the last tempo change */ + fluid_atomic_int_set(&player->seek_ticks, -1); /* clear seek_ticks */ + } + + if(fluid_list_next(player->currentfile) == NULL && player->loop == 0) + { + /* Once we've run out of MIDI events, keep playing until no voices are active */ + if(status == FLUID_PLAYER_DONE && fluid_synth_get_active_voice_count(player->synth) > 0) + { + /* The first time we notice we've run out of MIDI events but there are still active voices, disable all hold pedals */ + if(!player->end_pedals_disabled) + { + for(i = 0; i < synth->midi_channels; i++) + { + fluid_synth_cc(player->synth, i, SUSTAIN_SWITCH, 0); + fluid_synth_cc(player->synth, i, SOSTENUTO_SWITCH, 0); + } + + player->end_pedals_disabled = 1; + } + + status = FLUID_PLAYER_PLAYING; + } + + /* Once no voices are active, if end_msec hasn't been scheduled, schedule it so we wait for reverb, etc to finish */ + if(status == FLUID_PLAYER_DONE && player->end_msec < 0) + { + player->end_msec = msec + FLUID_PLAYER_STOP_GRACE_MS; + } + /* If end_msec has been scheduled and is in the future, keep playing */ + if (player->end_msec >= 0 && msec < (unsigned int) player->end_msec) + { + status = FLUID_PLAYER_PLAYING; + } + } + + /* Once there's no reason to keep playing, we're actually done */ + if(status == FLUID_PLAYER_DONE) + { + FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__, + __LINE__, (msec - player->begin_msec) / 1000.0); + + if(player->reset_synth_between_songs) + { + fluid_synth_system_reset(player->synth); + } + + loadnextfile = 1; + } + + if (player->tick_callback != NULL && player->last_callback_ticks != player->cur_ticks) { + player->tick_callback(player->tick_userdata, player->cur_ticks); + player->last_callback_ticks = player->cur_ticks; + } + } + while(loadnextfile); + + /* do not update the status if the player has been stopped already */ + fluid_atomic_int_compare_and_exchange(&player->status, FLUID_PLAYER_PLAYING, status); + + return 1; +} + +/** + * Activates play mode for a MIDI player if not already playing. + * @param player MIDI player instance + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * If the list of files added to the player has completed its requested number of loops, + * the playlist will be restarted from the beginning with a loop count of 1. + */ +int +fluid_player_play(fluid_player_t *player) +{ + if(fluid_player_get_status(player) == FLUID_PLAYER_PLAYING || + player->playlist == NULL) + { + return FLUID_OK; + } + + if(!player->use_system_timer) + { + fluid_sample_timer_reset(player->synth, player->sample_timer); + } + + /* If we're at the end of the playlist and there are no loops left, loop once */ + if(player->currentfile == NULL && player->loop == 0) + { + player->loop = 1; + } + + player->end_msec = -1; + player->end_pedals_disabled = 0; + + fluid_atomic_int_set(&player->status, FLUID_PLAYER_PLAYING); + + return FLUID_OK; +} + +/** + * Pauses the MIDI playback. + * + * @param player MIDI player instance + * @return Always returns #FLUID_OK + * + * It will not rewind to the beginning of the file, use fluid_player_seek() for this purpose. + */ +int +fluid_player_stop(fluid_player_t *player) +{ + fluid_atomic_int_set(&player->status, FLUID_PLAYER_DONE); + fluid_atomic_int_set(&player->stopping, 1); + fluid_player_seek(player, fluid_player_get_current_tick(player)); + return FLUID_OK; +} + +/** + * Get MIDI player status. + * @param player MIDI player instance + * @return Player status (#fluid_player_status) + * @since 1.1.0 + */ +int +fluid_player_get_status(fluid_player_t *player) +{ + return fluid_atomic_int_get(&player->status); +} + +/** + * Seek in the currently playing file. + * + * @param player MIDI player instance + * @param ticks the position to seek to in the current file + * @return #FLUID_FAILED if ticks is negative or after the latest tick of the file + * [or, since 2.1.3, if another seek operation is currently in progress], + * #FLUID_OK otherwise. + * + * The actual seek will be performed when the synth calls back the player (i.e. a few + * levels above the player's callback set with fluid_player_set_playback_callback()). + * If the player's status is #FLUID_PLAYER_PLAYING and a previous seek operation has + * not been completed yet, #FLUID_FAILED is returned. + * + * @since 2.0.0 + */ +int fluid_player_seek(fluid_player_t *player, int ticks) +{ + if(ticks < 0 || (fluid_player_get_status(player) != FLUID_PLAYER_READY && ticks > fluid_player_get_total_ticks(player))) + { + return FLUID_FAILED; + } + + if(fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) + { + if(fluid_atomic_int_compare_and_exchange(&player->seek_ticks, -1, ticks)) + { + // new seek position has been set, as no previous seek was in progress + return FLUID_OK; + } + } + else + { + // If the player is not currently playing, a new seek position can be set at any time. This allows + // the user to do: + // fluid_player_stop(); + // fluid_player_seek(0); // to beginning + fluid_atomic_int_set(&player->seek_ticks, ticks); + return FLUID_OK; + } + + // a previous seek is still in progress or hasn't been processed yet + return FLUID_FAILED; +} + + +/** + * Enable looping of a MIDI player + * + * @param player MIDI player instance + * @param loop Times left to loop the playlist. -1 means loop infinitely. + * @return Always returns #FLUID_OK + * + * For example, if you want to loop the playlist twice, set loop to 2 + * and call this function before you start the player. + * + * @since 1.1.0 + */ +int fluid_player_set_loop(fluid_player_t *player, int loop) +{ + player->loop = loop; + return FLUID_OK; +} + +/** + * update the MIDI player internal deltatime dependent of actual tempo. + * @param player MIDI player instance + */ +static void fluid_player_update_tempo(fluid_player_t *player) +{ + int tempo; /* tempo in micro seconds by quarter note */ + float deltatime; + + /* do nothing if the division is still unknown to avoid a div by zero */ + if(player->division == 0) + { + return; + } + + if(fluid_atomic_int_get(&player->sync_mode)) + { + /* take internal tempo from MIDI file */ + tempo = fluid_atomic_int_get(&player->miditempo); + /* compute deltattime (in ms) from current tempo and apply tempo multiplier */ + deltatime = (float)tempo / (float)player->division / (float)1000.0; + deltatime /= fluid_atomic_float_get(&player->multempo); /* multiply tempo */ + } + else + { + /* take external tempo */ + tempo = fluid_atomic_int_get(&player->exttempo); + /* compute deltattime (in ms) from current tempo */ + deltatime = (float)tempo / (float)player->division / (float)1000.0; + } + + fluid_atomic_float_set(&player->deltatime, deltatime); + + player->start_msec = player->cur_msec; + player->start_ticks = player->cur_ticks; + + FLUID_LOG(FLUID_DBG, + "tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d", + tempo, player->deltatime, player->cur_msec, player->cur_ticks); + +} + +/** + * Set the tempo of a MIDI player. + * The player can be controlled by internal tempo coming from MIDI file tempo + * change or controlled by external tempo expressed in BPM or in micro seconds + * per quarter note. + * + * @param player MIDI player instance. Must be a valid pointer. + * @param tempo_type Must a be value of #fluid_player_set_tempo_type and indicates the + * meaning of tempo value and how the player will be controlled, see below. + * @param tempo Tempo value or multiplier. + * + * #FLUID_PLAYER_TEMPO_INTERNAL, the player will be controlled by internal + * MIDI file tempo changes. If there are no tempo change in the MIDI file a default + * value of 120 bpm is used. The @c tempo parameter is used as a multiplier factor + * that must be in the range (0.001 to 1000). + * For example, if the current MIDI file tempo is 120 bpm and the multiplier + * value is 0.5 then this tempo will be slowed down to 60 bpm. + * At creation, the player is set to be controlled by internal tempo with a + * multiplier factor set to 1.0. + * + * #FLUID_PLAYER_TEMPO_EXTERNAL_BPM, the player will be controlled by the + * external tempo value provided by the tempo parameter in bpm + * (i.e in quarter notes per minute) which must be in the range (1 to 60000000). + * + * #FLUID_PLAYER_TEMPO_EXTERNAL_MIDI, similar as FLUID_PLAYER_TEMPO_EXTERNAL_BPM, + * but the tempo parameter value is in micro seconds per quarter note which + * must be in the range (1 to 60000000). + * Using micro seconds per quarter note is convenient when the tempo value is + * derived from MIDI clock realtime messages. + * + * @note When the player is controlled by an external tempo + * (#FLUID_PLAYER_TEMPO_EXTERNAL_BPM or #FLUID_PLAYER_TEMPO_EXTERNAL_MIDI) it + * continues to memorize the most recent internal tempo change coming from the + * MIDI file so that next call to fluid_player_set_tempo() with + * #FLUID_PLAYER_TEMPO_INTERNAL will set the player to follow this internal + * tempo. + * + * @warning If the function is called when no MIDI file is loaded or currently playing, it + * would have caused a division by zero in fluidsynth 2.2.7 and earlier. Starting with 2.2.8, the + * new tempo change will be stashed and applied later. + * + * @return #FLUID_OK if success or #FLUID_FAILED otherwise (incorrect parameters). + * @since 2.2.0 + */ +int fluid_player_set_tempo(fluid_player_t *player, int tempo_type, double tempo) +{ + fluid_return_val_if_fail(player != NULL, FLUID_FAILED); + fluid_return_val_if_fail(tempo_type >= FLUID_PLAYER_TEMPO_INTERNAL, FLUID_FAILED); + fluid_return_val_if_fail(tempo_type < FLUID_PLAYER_TEMPO_NBR, FLUID_FAILED); + + switch(tempo_type) + { + /* set the player to be driven by internal tempo coming from MIDI file */ + case FLUID_PLAYER_TEMPO_INTERNAL: + /* check if the multiplier is in correct range */ + fluid_return_val_if_fail(tempo >= MIN_TEMPO_MULTIPLIER, FLUID_FAILED); + fluid_return_val_if_fail(tempo <= MAX_TEMPO_MULTIPLIER, FLUID_FAILED); + + /* set the tempo multiplier */ + fluid_atomic_float_set(&player->multempo, (float)tempo); + fluid_atomic_int_set(&player->sync_mode, 1); /* set internal mode */ + break; + + /* set the player to be driven by external tempo */ + case FLUID_PLAYER_TEMPO_EXTERNAL_BPM: /* value in bpm */ + case FLUID_PLAYER_TEMPO_EXTERNAL_MIDI: /* value in us/quarter note */ + /* check if tempo is in correct range */ + fluid_return_val_if_fail(tempo >= MIN_TEMPO_VALUE, FLUID_FAILED); + fluid_return_val_if_fail(tempo <= MAX_TEMPO_VALUE, FLUID_FAILED); + + /* set the tempo value */ + if(tempo_type == FLUID_PLAYER_TEMPO_EXTERNAL_BPM) + { + tempo = 60000000L / tempo; /* convert tempo in us/quarter note */ + } + fluid_atomic_int_set(&player->exttempo, (int)tempo); + fluid_atomic_int_set(&player->sync_mode, 0); /* set external mode */ + break; + + default: /* shouldn't happen */ + break; + } + + /* update deltatime depending of current tempo */ + fluid_player_update_tempo(player); + + return FLUID_OK; +} + +/** + * Set the tempo of a MIDI player. + * @param player MIDI player instance + * @param tempo Tempo to set playback speed to (in microseconds per quarter note, as per MIDI file spec) + * @return Always returns #FLUID_OK + * @note Tempo change events contained in the MIDI file can override the specified tempo at any time! + * @deprecated Use fluid_player_set_tempo() instead. + */ +int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) +{ + player->miditempo = tempo; + + fluid_player_update_tempo(player); + return FLUID_OK; +} + +/** + * Set the tempo of a MIDI player in beats per minute. + * @param player MIDI player instance + * @param bpm Tempo in beats per minute + * @return Always returns #FLUID_OK + * @note Tempo change events contained in the MIDI file can override the specified BPM at any time! + * @deprecated Use fluid_player_set_tempo() instead. + */ +int fluid_player_set_bpm(fluid_player_t *player, int bpm) +{ + if(bpm <= 0) + { + return FLUID_FAILED; /* to avoid a division by 0 */ + } + + return fluid_player_set_midi_tempo(player, 60000000L / bpm); +} + +/** + * Wait for a MIDI player until the playback has been stopped. + * @param player MIDI player instance + * @return Always #FLUID_OK + * + * @warning The player may still be used by a concurrently running synth context. Hence it is + * not safe to immediately delete the player afterwards. Also refer to delete_fluid_player(). + */ +int +fluid_player_join(fluid_player_t *player) +{ + while(fluid_player_get_status(player) != FLUID_PLAYER_DONE) + { + fluid_msleep(10); + } + return FLUID_OK; +} + +/** + * Get the number of tempo ticks passed. + * @param player MIDI player instance + * @return The number of tempo ticks passed + * @since 1.1.7 + */ +int fluid_player_get_current_tick(fluid_player_t *player) +{ + return player->cur_ticks; +} + +/** + * Looks through all available MIDI tracks and gets the absolute tick of the very last event to play. + * @param player MIDI player instance + * @return Total tick count of the sequence + * @since 1.1.7 + */ +int fluid_player_get_total_ticks(fluid_player_t *player) +{ + int i; + int maxTicks = 0; + + for(i = 0; i < player->ntracks; i++) + { + if(player->track[i] != NULL) + { + int ticks = fluid_track_get_duration(player->track[i]); + + if(ticks > maxTicks) + { + maxTicks = ticks; + } + } + } + + return maxTicks; +} + +/** + * Get the tempo currently used by a MIDI player. + * The player can be controlled by internal tempo coming from MIDI file tempo + * change or controlled by external tempo see fluid_player_set_tempo(). + * @param player MIDI player instance. Must be a valid pointer. + * @return MIDI player tempo in BPM or FLUID_FAILED if error. + * @since 1.1.7 + */ +int fluid_player_get_bpm(fluid_player_t *player) +{ + + int midi_tempo = fluid_player_get_midi_tempo(player); + + if(midi_tempo > 0) + { + midi_tempo = 60000000L / midi_tempo; /* convert in bpm */ + } + + return midi_tempo; +} + +/** + * Get the division currently used by a MIDI player. + * The player can be controlled by internal tempo coming from MIDI file tempo + * change or controlled by external tempo see fluid_player_set_tempo(). + * @param player MIDI player instance. Must be a valid pointer. + * @return MIDI player division or FLUID_FAILED if error. + * @since 2.3.2 + */ +int fluid_player_get_division(fluid_player_t *player) +{ + return player->division; +} + +/** + * Get the tempo currently used by a MIDI player. + * The player can be controlled by internal tempo coming from MIDI file tempo + * change or controlled by external tempo see fluid_player_set_tempo(). + + * @param player MIDI player instance. Must be a valid pointer. + * @return Tempo of the MIDI player (in microseconds per quarter note, as per + * MIDI file spec) or FLUID_FAILED if error. + * @since 1.1.7 + */ +int fluid_player_get_midi_tempo(fluid_player_t *player) +{ + int midi_tempo; /* value to return */ + + fluid_return_val_if_fail(player != NULL, FLUID_FAILED); + + midi_tempo = fluid_atomic_int_get(&player->exttempo); + /* look if the player is internally synced */ + if(fluid_atomic_int_get(&player->sync_mode)) + { + midi_tempo = (int)((float)fluid_atomic_int_get(&player->miditempo)/ + fluid_atomic_float_get(&player->multempo)); + } + + return midi_tempo; +} + +/************************************************************************ + * MIDI PARSER + * + */ + +/* + * new_fluid_midi_parser + */ +fluid_midi_parser_t * +new_fluid_midi_parser() +{ + fluid_midi_parser_t *parser; + parser = FLUID_NEW(fluid_midi_parser_t); + + if(parser == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + parser->status = 0; /* As long as the status is 0, the parser won't do anything -> no need to initialize all the fields. */ + return parser; +} + +/* + * delete_fluid_midi_parser + */ +void +delete_fluid_midi_parser(fluid_midi_parser_t *parser) +{ + fluid_return_if_fail(parser != NULL); + + FLUID_FREE(parser); +} + +/** + * Parse a MIDI stream one character at a time. + * @param parser Parser instance + * @param c Next character in MIDI stream + * @return A parsed MIDI event or NULL if none. Event is internal and should + * not be modified or freed and is only valid until next call to this function. + * @internal Do not expose this function to the public API. It would allow downstream + * apps to abuse fluidsynth as midi parser, e.g. feeding it with rawmidi and pull out + * the needed midi information using the getter functions of fluid_midi_event_t. + * This parser however is incomplete as it e.g. only provides a limited buffer to + * store and process SYSEX data (i.e. doesn't allow arbitrary lengths) + */ +fluid_midi_event_t * +fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) +{ + fluid_midi_event_t *event; + + /* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle + * of another message. */ + if(c >= 0xF8) + { + parser->event.type = c; + parser->status = 0; /* clear the status */ + return &parser->event; + } + + /* Status byte? - If previous message not yet complete, it is discarded (re-sync). */ + if(c & 0x80) + { + /* Any status byte terminates SYSEX messages (not just 0xF7) */ + if(parser->status == MIDI_SYSEX && parser->nr_bytes > 0) + { + event = &parser->event; + fluid_midi_event_set_sysex(event, parser->data, parser->nr_bytes, + FALSE); + } + else + { + event = NULL; + } + + if(c < 0xF0) /* Voice category message? */ + { + parser->channel = c & 0x0F; + parser->status = c & 0xF0; + + /* The event consumes x bytes of data... (subtract 1 for the status byte) */ + parser->nr_bytes_total = fluid_midi_event_length(parser->status) + - 1; + + parser->nr_bytes = 0; /* 0 bytes read so far */ + } + else if(c == MIDI_SYSEX) + { + parser->status = MIDI_SYSEX; + parser->nr_bytes = 0; + } + else + { + parser->status = 0; /* Discard other system messages (0xF1-0xF7) */ + } + + return event; /* Return SYSEX event or NULL */ + } + + /* Data/parameter byte */ + + /* Discard data bytes for events we don't care about */ + if(parser->status == 0) + { + return NULL; + } + + /* Max data size exceeded? (SYSEX messages only really) */ + if(parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE) + { + parser->status = 0; /* Discard the rest of the message */ + return NULL; + } + + /* Store next byte */ + parser->data[parser->nr_bytes++] = c; + + /* Do we still need more data to get this event complete? */ + if(parser->status == MIDI_SYSEX || parser->nr_bytes < parser->nr_bytes_total) + { + return NULL; + } + + /* Event is complete, return it. + * Running status byte MIDI feature is also handled here. */ + parser->event.type = parser->status; + parser->event.channel = parser->channel; + parser->nr_bytes = 0; /* Reset data size, in case there are additional running status messages */ + + switch(parser->status) + { + case NOTE_OFF: + case NOTE_ON: + case KEY_PRESSURE: + case CONTROL_CHANGE: + case PROGRAM_CHANGE: + case CHANNEL_PRESSURE: + parser->event.param1 = parser->data[0]; /* For example key number */ + parser->event.param2 = parser->data[1]; /* For example velocity */ + break; + + case PITCH_BEND: + /* Pitch-bend is transmitted with 14-bit precision. */ + parser->event.param1 = (parser->data[1] << 7) | parser->data[0]; + break; + + default: /* Unlikely */ + return NULL; + } + + return &parser->event; +} + +/* Purpose: + * Returns the length of a MIDI message. */ +static int +fluid_midi_event_length(unsigned char event) +{ + switch(event & 0xF0) + { + case NOTE_OFF: + case NOTE_ON: + case KEY_PRESSURE: + case CONTROL_CHANGE: + case PITCH_BEND: + return 3; + + case PROGRAM_CHANGE: + case CHANNEL_PRESSURE: + return 2; + } + + switch(event) + { + case MIDI_TIME_CODE: + case MIDI_SONG_SELECT: + case 0xF4: + case 0xF5: + return 2; + + case MIDI_TUNE_REQUEST: + return 1; + + case MIDI_SONG_POSITION: + return 3; + } + + return 1; +} diff --git a/libs/fluidsynth/src/midi/fluid_midi.h b/libs/fluidsynth/src/midi/fluid_midi.h new file mode 100644 index 00000000000..bd3b4e8a1b1 --- /dev/null +++ b/libs/fluidsynth/src/midi/fluid_midi.h @@ -0,0 +1,384 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_MIDI_H +#define _FLUID_MIDI_H + +#include "fluidsynth_priv.h" +#include "fluid_sys.h" +#include "fluid_list.h" + +typedef struct _fluid_midi_parser_t fluid_midi_parser_t; + +fluid_midi_parser_t *new_fluid_midi_parser(void); +void delete_fluid_midi_parser(fluid_midi_parser_t *parser); +fluid_midi_event_t *fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c); + + +/*************************************************************** + * + * CONSTANTS & ENUM + */ + + +#define MAX_NUMBER_OF_TRACKS 128 +#define MAX_NUMBER_OF_CHANNELS 16 + +enum fluid_midi_event_type +{ + /* channel messages */ + NOTE_OFF = 0x80, + NOTE_ON = 0x90, + KEY_PRESSURE = 0xa0, + CONTROL_CHANGE = 0xb0, + PROGRAM_CHANGE = 0xc0, + CHANNEL_PRESSURE = 0xd0, + PITCH_BEND = 0xe0, + /* system exclusive */ + MIDI_SYSEX = 0xf0, + /* system common - never in midi files */ + MIDI_TIME_CODE = 0xf1, + MIDI_SONG_POSITION = 0xf2, + MIDI_SONG_SELECT = 0xf3, + MIDI_TUNE_REQUEST = 0xf6, + MIDI_EOX = 0xf7, + /* system real-time - never in midi files */ + MIDI_SYNC = 0xf8, + MIDI_TICK = 0xf9, + MIDI_START = 0xfa, + MIDI_CONTINUE = 0xfb, + MIDI_STOP = 0xfc, + MIDI_ACTIVE_SENSING = 0xfe, + MIDI_SYSTEM_RESET = 0xff, + /* meta event - for midi files only */ + MIDI_META_EVENT = 0xff +}; + +enum fluid_midi_control_change +{ + BANK_SELECT_MSB = 0x00, + MODULATION_MSB = 0x01, + BREATH_MSB = 0x02, + FOOT_MSB = 0x04, + PORTAMENTO_TIME_MSB = 0x05, + DATA_ENTRY_MSB = 0x06, + VOLUME_MSB = 0x07, + BALANCE_MSB = 0x08, + PAN_MSB = 0x0A, + EXPRESSION_MSB = 0x0B, + EFFECTS1_MSB = 0x0C, + EFFECTS2_MSB = 0x0D, + GPC1_MSB = 0x10, /* general purpose controller */ + GPC2_MSB = 0x11, + GPC3_MSB = 0x12, + GPC4_MSB = 0x13, + BANK_SELECT_LSB = 0x20, + MODULATION_WHEEL_LSB = 0x21, + BREATH_LSB = 0x22, + FOOT_LSB = 0x24, + PORTAMENTO_TIME_LSB = 0x25, + DATA_ENTRY_LSB = 0x26, + VOLUME_LSB = 0x27, + BALANCE_LSB = 0x28, + PAN_LSB = 0x2A, + EXPRESSION_LSB = 0x2B, + EFFECTS1_LSB = 0x2C, + EFFECTS2_LSB = 0x2D, + GPC1_LSB = 0x30, + GPC2_LSB = 0x31, + GPC3_LSB = 0x32, + GPC4_LSB = 0x33, + SUSTAIN_SWITCH = 0x40, + PORTAMENTO_SWITCH = 0x41, + SOSTENUTO_SWITCH = 0x42, + SOFT_PEDAL_SWITCH = 0x43, + LEGATO_SWITCH = 0x44, + HOLD2_SWITCH = 0x45, + SOUND_CTRL1 = 0x46, + SOUND_CTRL2 = 0x47, + SOUND_CTRL3 = 0x48, + SOUND_CTRL4 = 0x49, + SOUND_CTRL5 = 0x4A, + SOUND_CTRL6 = 0x4B, + SOUND_CTRL7 = 0x4C, + SOUND_CTRL8 = 0x4D, + SOUND_CTRL9 = 0x4E, + SOUND_CTRL10 = 0x4F, + GPC5 = 0x50, + GPC6 = 0x51, + GPC7 = 0x52, + GPC8 = 0x53, + PORTAMENTO_CTRL = 0x54, + EFFECTS_DEPTH1 = 0x5B, + EFFECTS_DEPTH2 = 0x5C, + EFFECTS_DEPTH3 = 0x5D, + EFFECTS_DEPTH4 = 0x5E, + EFFECTS_DEPTH5 = 0x5F, + DATA_ENTRY_INCR = 0x60, + DATA_ENTRY_DECR = 0x61, + NRPN_LSB = 0x62, + NRPN_MSB = 0x63, + RPN_LSB = 0x64, + RPN_MSB = 0x65, + ALL_SOUND_OFF = 0x78, + ALL_CTRL_OFF = 0x79, + LOCAL_CONTROL = 0x7A, + ALL_NOTES_OFF = 0x7B, + OMNI_OFF = 0x7C, + OMNI_ON = 0x7D, + POLY_OFF = 0x7E, + POLY_ON = 0x7F +}; + +/* General MIDI RPN event numbers (LSB, MSB = 0) */ +enum midi_rpn_event +{ + RPN_PITCH_BEND_RANGE = 0x00, + RPN_CHANNEL_FINE_TUNE = 0x01, + RPN_CHANNEL_COARSE_TUNE = 0x02, + RPN_TUNING_PROGRAM_CHANGE = 0x03, + RPN_TUNING_BANK_SELECT = 0x04, + RPN_MODULATION_DEPTH_RANGE = 0x05 +}; + +enum midi_meta_event +{ + MIDI_TEXT = 0x01, + MIDI_COPYRIGHT = 0x02, + MIDI_TRACK_NAME = 0x03, + MIDI_INST_NAME = 0x04, + MIDI_LYRIC = 0x05, + MIDI_MARKER = 0x06, + MIDI_CUE_POINT = 0x07, + MIDI_EOT = 0x2f, + MIDI_SET_TEMPO = 0x51, + MIDI_SMPTE_OFFSET = 0x54, + MIDI_TIME_SIGNATURE = 0x58, + MIDI_KEY_SIGNATURE = 0x59, + MIDI_SEQUENCER_EVENT = 0x7f +}; + +/* MIDI SYSEX useful manufacturer values */ +enum midi_sysex_manuf +{ + MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */ + MIDI_SYSEX_MANUF_YAMAHA = 0x43, + MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */ + MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */ +}; + +#define MIDI_SYSEX_DEVICE_ID_ALL 0x7F /**< Device ID used in SYSEX messages to indicate all devices */ + +/* SYSEX sub-ID #1 which follows device ID */ +#define MIDI_SYSEX_MIDI_TUNING_ID 0x08 /**< Sysex sub-ID #1 for MIDI tuning messages */ +#define MIDI_SYSEX_GM_ID 0x09 /**< Sysex sub-ID #1 for General MIDI messages */ +#define MIDI_SYSEX_GS_ID 0x42 /**< Model ID (GS) serving as sub-ID #1 for GS messages*/ +#define MIDI_SYSEX_XG_ID 0x4C /**< Model ID (XG) serving as sub-ID #1 for XG messages*/ + +/** + * SYSEX tuning message IDs. + */ +enum midi_sysex_tuning_msg_id +{ + MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */ + MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */ + MIDI_SYSEX_TUNING_NOTE_TUNE = 0x02, /**< Tuning note change message (realtime) */ + MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK = 0x03, /**< Bulk tuning dump request (with bank, non-realtime) */ + MIDI_SYSEX_TUNING_BULK_DUMP_BANK = 0x04, /**< Bulk tuning dump response (with bank, non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_DUMP_1BYTE = 0x05, /**< Octave tuning dump using 1 byte values (non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_DUMP_2BYTE = 0x06, /**< Octave tuning dump using 2 byte values (non-realtime) */ + MIDI_SYSEX_TUNING_NOTE_TUNE_BANK = 0x07, /**< Tuning note change message (with bank, realtime/non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE = 0x08, /**< Octave tuning message using 1 byte values (realtime/non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */ +}; + +/* General MIDI sub-ID #2 */ +#define MIDI_SYSEX_GM_ON 0x01 /**< Enable GM mode */ +#define MIDI_SYSEX_GM_OFF 0x02 /**< Disable GM mode */ +#define MIDI_SYSEX_GM2_ON 0x03 /**< Enable GM2 mode */ +#define MIDI_SYSEX_GS_DT1 0x12 /**< GS DT1 command */ + +enum fluid_driver_status +{ + FLUID_MIDI_READY, + FLUID_MIDI_LISTENING, + FLUID_MIDI_DONE +}; + +/*************************************************************** + * + * TYPE DEFINITIONS & FUNCTION DECLARATIONS + */ + +/* + * fluid_midi_event_t + */ +struct _fluid_midi_event_t +{ + fluid_midi_event_t *next; /* Link to next event */ + void *paramptr; /* Pointer parameter (for SYSEX data), size is stored to param1, param2 indicates if pointer should be freed (dynamic if TRUE) */ + unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */ + unsigned int param1; /* First parameter */ + unsigned int param2; /* Second parameter */ + unsigned char type; /* MIDI event type */ + unsigned char channel; /* MIDI channel */ +}; + + +/* + * fluid_track_t + */ +struct _fluid_track_t +{ + char *name; + int num; + fluid_midi_event_t *first; + fluid_midi_event_t *cur; + fluid_midi_event_t *last; + unsigned int ticks; +}; + +typedef struct _fluid_track_t fluid_track_t; + +#define fluid_track_eot(track) ((track)->cur == NULL) + + +/* + * fluid_playlist_item + * Used as the `data' elements of the fluid_player.playlist. + * Represents either a filename or a pre-loaded memory buffer. + * Exactly one of `filename' and `buffer' is non-NULL. + */ +typedef struct +{ + char *filename; /** Name of file (owned); NULL if data pre-loaded */ + void *buffer; /** The MIDI file data (owned); NULL if filename */ + size_t buffer_len; /** Number of bytes in buffer; 0 if filename */ +} fluid_playlist_item; + +/* range of tempo values */ +#define MIN_TEMPO_VALUE (1.0f) +#define MAX_TEMPO_VALUE (60000000.0f) +/* range of tempo multiplier values */ +#define MIN_TEMPO_MULTIPLIER (0.001f) +#define MAX_TEMPO_MULTIPLIER (1000.0f) + +/* + * fluid_player + */ +struct _fluid_player_t +{ + fluid_atomic_int_t status; + fluid_atomic_int_t stopping; /* Flag for sending all_notes_off when player is stopped */ + int ntracks; + fluid_track_t *track[MAX_NUMBER_OF_TRACKS]; + fluid_synth_t *synth; + fluid_timer_t *system_timer; + fluid_sample_timer_t *sample_timer; + + int loop; /* -1 = loop infinitely, otherwise times left to loop the playlist */ + fluid_list_t *playlist; /* List of fluid_playlist_item* objects */ + fluid_list_t *currentfile; /* points to an item in files, or NULL if not playing */ + + char use_system_timer; /* if zero, use sample timers, otherwise use system clock timer */ + char reset_synth_between_songs; /* 1 if system reset should be sent to the synth between songs. */ + fluid_atomic_int_t seek_ticks; /* new position in tempo ticks (midi ticks) for seeking */ + int start_ticks; /* the number of tempo ticks passed at the last tempo change */ + int cur_ticks; /* the number of tempo ticks passed */ + int last_callback_ticks; /* the last tick number that was passed to player->tick_callback */ + int begin_msec; /* the time (msec) of the beginning of the file */ + int start_msec; /* the start time of the last tempo change */ + unsigned int cur_msec; /* the current time */ + int end_msec; /* when >=0, playback is extended until this time (for, e.g., reverb) */ + char end_pedals_disabled; /* 1 once the pedals have been released after the last midi event, 0 otherwise */ + /* sync mode: indicates the tempo mode the player is driven by (see fluid_player_set_tempo()): + 1, the player is driven by internal tempo (miditempo). This is the default. + 0, the player is driven by external tempo (exttempo) + */ + int sync_mode; + /* miditempo: internal tempo coming from MIDI file tempo change events + (in micro seconds per quarter note) + */ + int miditempo; /* as indicated by MIDI SetTempo: n 24th of a usec per midi-clock. bravo! */ + /* exttempo: external tempo set by fluid_player_set_tempo() (in micro seconds per quarter note) */ + int exttempo; + /* multempo: tempo multiplier set by fluid_player_set_tempo() */ + float multempo; + float deltatime; /* milliseconds per midi tick. depends on current tempo mode (see sync_mode) */ + unsigned int division; + + handle_midi_event_func_t playback_callback; /* function fired on each midi event as it is played */ + void *playback_userdata; /* pointer to user-defined data passed to playback_callback function */ + handle_midi_tick_func_t tick_callback; /* function fired on each tick change */ + void *tick_userdata; /* pointer to user-defined data passed to tick_callback function */ + + int channel_isplaying[MAX_NUMBER_OF_CHANNELS]; /* flags indicating channels on which notes have played */ +}; + +#define FLUID_PLAYER_STOP_GRACE_MS 2000 + +void fluid_player_settings(fluid_settings_t *settings); + + +/* + * fluid_midi_file + */ +typedef struct +{ + const char *buffer; /* Entire contents of MIDI file (borrowed) */ + int buf_len; /* Length of buffer, in bytes */ + int buf_pos; /* Current read position in contents buffer */ + int eof; /* The "end of file" condition */ + int running_status; + int c; + int type; + int ntracks; + int uses_smpte; + unsigned int smpte_fps; + unsigned int smpte_res; + unsigned int division; /* If uses_SMPTE == 0 then division is + ticks per beat (quarter-note) */ + double tempo; /* Beats per second (SI rules =) */ + int tracklen; + int trackpos; + int eot; + int varlen; + int dtime; +} fluid_midi_file; + + + +#define FLUID_MIDI_PARSER_MAX_DATA_SIZE 1024 /**< Maximum size of MIDI parameters/data (largest is SYSEX data) */ + +/* + * fluid_midi_parser_t + */ +struct _fluid_midi_parser_t +{ + unsigned char status; /* Identifies the type of event, that is currently received ('Noteon', 'Pitch Bend' etc). */ + unsigned char channel; /* The channel of the event that is received (in case of a channel event) */ + unsigned int nr_bytes; /* How many bytes have been read for the current event? */ + unsigned int nr_bytes_total; /* How many bytes does the current event type include? */ + unsigned char data[FLUID_MIDI_PARSER_MAX_DATA_SIZE]; /* The parameters or SYSEX data */ + fluid_midi_event_t event; /* The event, that is returned to the MIDI driver. */ +}; + + +#endif /* _FLUID_MIDI_H */ diff --git a/libs/fluidsynth/src/midi/fluid_midi_router.h b/libs/fluidsynth/src/midi/fluid_midi_router.h new file mode 100644 index 00000000000..5a5068d5ea9 --- /dev/null +++ b/libs/fluidsynth/src/midi/fluid_midi_router.h @@ -0,0 +1,32 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* Author: Markus Nentwig, nentwig@users.sourceforge.net + */ + +#ifndef _FLUID_MIDIROUTER_H +#define _FLUID_MIDIROUTER_H + +#include "fluidsynth_priv.h" +#include "fluid_midi.h" +#include "fluid_sys.h" + + +#endif diff --git a/libs/fluidsynth/src/rvoice/fluid_adsr_env.c b/libs/fluidsynth/src/rvoice/fluid_adsr_env.c new file mode 100644 index 00000000000..4b37754cb01 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_adsr_env.c @@ -0,0 +1,38 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_adsr_env.h" + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_adsr_env_set_data) +{ + fluid_adsr_env_t *env = obj; + fluid_adsr_env_section_t section = param[0].i; + unsigned int count = param[1].i; + fluid_real_t coeff = param[2].real; + fluid_real_t increment = param[3].real; + fluid_real_t min = param[4].real; + fluid_real_t max = param[5].real; + + env->data[section].count = count; + env->data[section].coeff = coeff; + env->data[section].increment = increment; + env->data[section].min = min; + env->data[section].max = max; +} diff --git a/libs/fluidsynth/src/rvoice/fluid_adsr_env.h b/libs/fluidsynth/src/rvoice/fluid_adsr_env.h new file mode 100644 index 00000000000..92c8fcd24c0 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_adsr_env.h @@ -0,0 +1,166 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_ADSR_ENVELOPE_H +#define _FLUID_ADSR_ENVELOPE_H + +#include "fluidsynth_priv.h" +#include "fluid_sys.h" + +/* + * envelope data + */ +struct _fluid_env_data_t +{ + unsigned int count; + fluid_real_t coeff; + fluid_real_t increment; + fluid_real_t min; + fluid_real_t max; +}; + +/* Indices for envelope tables */ +enum fluid_voice_envelope_index +{ + FLUID_VOICE_ENVDELAY, + FLUID_VOICE_ENVATTACK, + FLUID_VOICE_ENVHOLD, + FLUID_VOICE_ENVDECAY, + FLUID_VOICE_ENVSUSTAIN, + FLUID_VOICE_ENVRELEASE, + FLUID_VOICE_ENVFINISHED, + FLUID_VOICE_ENVLAST +}; + +typedef enum fluid_voice_envelope_index fluid_adsr_env_section_t; + +typedef struct _fluid_adsr_env_t fluid_adsr_env_t; + +struct _fluid_adsr_env_t +{ + fluid_env_data_t data[FLUID_VOICE_ENVLAST]; + unsigned int count; + fluid_real_t val; /* the current value of the envelope */ + fluid_adsr_env_section_t section; +}; + +/* For performance, all functions are inlined */ + +static FLUID_INLINE void +fluid_adsr_env_calc(fluid_adsr_env_t *env) +{ + fluid_env_data_t *env_data; + fluid_real_t x; + + env_data = &env->data[env->section]; + + /* skip to the next section of the envelope if necessary */ + while(env->count >= env_data->count) + { + // If we're switching envelope stages from decay to sustain, force the value to be the end value of the previous stage + // Hmm, should this only apply to volenv? It was so before refactoring, so keep it for now. [DH] + // No, must apply to both, otherwise some voices may sound detuned. [TM] (https://github.com/FluidSynth/fluidsynth/issues/1059) + if(env->section == FLUID_VOICE_ENVDECAY) + { + env->val = env_data->min * env_data->coeff; + } + + env_data = &env->data[++env->section]; + env->count = 0; + } + + /* calculate the envelope value and check for valid range */ + x = env_data->coeff * env->val + env_data->increment; + + if(x < env_data->min) + { + x = env_data->min; + env->section++; + env->count = 0; + } + else if(x > env_data->max) + { + x = env_data->max; + env->section++; + env->count = 0; + } + else + { + env->count++; + } + + env->val = x; +} + +/* This one cannot be inlined since it is referenced in + the event queue */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_adsr_env_set_data); + +static FLUID_INLINE void +fluid_adsr_env_reset(fluid_adsr_env_t *env) +{ + env->count = 0; + env->section = FLUID_VOICE_ENVDELAY; + env->val = 0.0f; +} + +static FLUID_INLINE fluid_real_t +fluid_adsr_env_get_val(fluid_adsr_env_t *env) +{ + return env->val; +} + +static FLUID_INLINE void +fluid_adsr_env_set_val(fluid_adsr_env_t *env, fluid_real_t val) +{ + env->val = val; +} + +static FLUID_INLINE fluid_adsr_env_section_t +fluid_adsr_env_get_section(fluid_adsr_env_t *env) +{ + return env->section; +} + +static FLUID_INLINE void +fluid_adsr_env_set_section(fluid_adsr_env_t *env, + fluid_adsr_env_section_t section) +{ + env->section = section; + env->count = 0; +} + +/* Used for determining which voice to kill. + Returns max amplitude from now, and forward in time. +*/ +static FLUID_INLINE fluid_real_t +fluid_adsr_env_get_max_val(fluid_adsr_env_t *env) +{ + if(env->section > FLUID_VOICE_ENVATTACK) + { + return env->val * 1000; + } + else + { + return env->data[FLUID_VOICE_ENVATTACK].max; + } +} + +#endif diff --git a/libs/fluidsynth/src/rvoice/fluid_chorus.c b/libs/fluidsynth/src/rvoice/fluid_chorus.c new file mode 100644 index 00000000000..c80dc9e98c1 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_chorus.c @@ -0,0 +1,1071 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe, Markus Nentwig and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* + based on a chorus implementation made by Juergen Mueller And Sundry Contributors in 1998 + + CHANGES + + - Adapted for fluidsynth, Peter Hanappe, March 2002 + + - Variable delay line implementation using bandlimited + interpolation, code reorganization: Markus Nentwig May 2002 + + - Complete rewrite using lfo computed on the fly, first order all-pass + interpolator and adding stereo unit: Jean-Jacques Ceresa, Jul 2019 + */ + + +/* + * Chorus effect. + * + * Flow diagram scheme for n delays ( 1 <= n <= MAX_CHORUS ): + * + * ________ + * direct signal (not implemented) >-->| | + * _________ | | + * mono | | | | + * input ---+---->| delay 1 |-------------------------->| Stereo |---> right + * | |_________| | | output + * | /|\ | Unit | + * : | | | + * : +-----------------+ |(width) | + * : | Delay control 1 |<-+ | | + * : +-----------------+ | | |---> left + * | _________ | | | output + * | | | | | | + * +---->| delay n |-------------------------->| | + * |_________| | | | + * /|\ | |________| + * | | +--------------+ /|\ + * +-----------------+ | |mod depth (ms)| | + * | Delay control n |<-*--|lfo speed (Hz)| gain-out + * +-----------------+ +--------------+ + * + * + * The delay i is controlled by a sine or triangle modulation i ( 1 <= i <= n). + * + * The chorus unit process a monophonic input signal and produces stereo output + * controlled by WIDTH macro. + * Actually WIDTH is fixed to maximum value. But in the future, we could add a + * setting (e.g "synth.chorus.width") allowing the user to get a gradually stereo + * effect from minimum (monophonic) to maximum stereo effect. + * + * Delays lines are implemented using only one line for all chorus blocks. + * Each chorus block has it own lfo (sinus/triangle). Each lfo are out of phase + * to produce uncorrelated signal at the output of the delay line (this simulates + * the presence of individual line for each block). Each lfo modulates the length + * of the line using a depth modulation value and lfo frequency value common to + * all lfos. + * + * LFO modulators are computed on the fly, instead of using lfo lookup table. + * The advantages are: + * - Avoiding a lost of 608272 memory bytes when lfo speed is low (0.3Hz). + * - Allows to diminish the lfo speed lower limit to 0.1Hz instead of 0.3Hz. + * A speed of 0.1 is interesting for chorus. Using a lookuptable for 0.1Hz + * would require too much memory (1824816 bytes). + * - Interpolation make use of first order all-pass interpolator instead of + * bandlimited interpolation. + * - Although lfo modulator is computed on the fly, cpu load is lower than + * using lfo lookup table with bandlimited interpolator. + */ + +#include "fluid_chorus.h" +#include "fluid_sys.h" + + +/*------------------------------------------------------------------------------------- + Private +--------------------------------------------------------------------------------------*/ +// #define DEBUG_PRINT // allows message to be printed on the console. + +#define MAX_CHORUS 99 /* number maximum of block */ +#define MAX_LEVEL 10 /* max output level */ +#define MIN_SPEED_HZ 0.1 /* min lfo frequency (Hz) */ +#define MAX_SPEED_HZ 5 /* max lfo frequency (Hz) */ + +/* WIDTH [0..10] value define a stereo separation between left and right. + When 0, the output is monophonic. When > 0 , the output is stereophonic. + Actually WIDTH is fixed to maximum value. But in the future we could add a setting to + allow a gradually stereo effect from minimum (monophonic) to maximum stereo effect. +*/ +#define WIDTH 10 + +/* SCALE_WET_WIDTH is a compensation weight factor to get an output + amplitude (wet) rather independent of the width setting. + 0: the output amplitude is fully dependent on the width setting. + >0: the output amplitude is less dependent on the width setting. + With a SCALE_WET_WIDTH of 0.2 the output amplitude is rather + independent of width setting (see fluid_chorus_set()). + */ +#define SCALE_WET_WIDTH 0.2f +#define SCALE_WET 1.0f + +#define MAX_SAMPLES 2048 /* delay length in sample (46.4 ms at sample rate: 44100Hz).*/ +#define LOW_MOD_DEPTH 176 /* low mod_depth/2 in samples */ +#define HIGH_MOD_DEPTH MAX_SAMPLES/2 /* high mod_depth in sample */ +#define RANGE_MOD_DEPTH (HIGH_MOD_DEPTH - LOW_MOD_DEPTH) + +/* Important min max values for MOD_RATE */ +/* mod rate define the rate at which the modulator is updated. Examples + 50: the modulator is updated every 50 samples (less cpu cycles expensive). + 1: the modulator is updated every sample (more cpu cycles expensive). +*/ +/* MOD_RATE acceptable for max lfo speed (5Hz) and max modulation depth (46.6 ms) */ +#define LOW_MOD_RATE 5 /* MOD_RATE acceptable for low modulation depth (8 ms) */ +#define HIGH_MOD_RATE 4 /* MOD_RATE acceptable for max modulation depth (46.6 ms) */ + /* and max lfo speed (5 Hz) */ +#define RANGE_MOD_RATE (HIGH_MOD_RATE - LOW_MOD_RATE) + +/* some chorus cpu_load measurement dependent of modulation rate: mod_rate + (number of chorus blocks: 2) + + No stero unit: + mod_rate | chorus cpu load(%) | one voice cpu load (%) + ---------------------------------------------------- + 50 | 0.204 | + 5 | 0.256 | 0.169 + 1 | 0.417 | + + With stero unit: + mod_rate | chorus cpu load(%) | one voice cpu load (%) + ---------------------------------------------------- + 50 | 0.220 | + 5 | 0.274 | 0.169 + 1 | 0.465 | + +*/ + +/* + Number of samples to add to the desired length of the delay line. This + allows to take account of rounding error interpolation when using large + modulation depth. + 1 is sufficient for max modulation depth (46.6 ms) and max lfo speed (5 Hz). +*/ +//#define INTERP_SAMPLES_NBR 0 +#define INTERP_SAMPLES_NBR 1 + + +/*----------------------------------------------------------------------------- + Sinusoidal modulator +-----------------------------------------------------------------------------*/ +/* modulator */ +typedef struct +{ + fluid_real_t a1; /* Coefficient: a1 = 2 * cos(w) */ + fluid_real_t buffer1; /* buffer1 */ + fluid_real_t buffer2; /* buffer2 */ + fluid_real_t reset_buffer2;/* reset value of buffer2 */ +} sinus_modulator; + +/*----------------------------------------------------------------------------- + Triangle modulator +-----------------------------------------------------------------------------*/ +typedef struct +{ + fluid_real_t freq; /* Osc. Frequency (in Hertz) */ + fluid_real_t val; /* internal current value */ + fluid_real_t inc; /* increment value */ +} triang_modulator; + +/*----------------------------------------------------------------------------- + modulator +-----------------------------------------------------------------------------*/ +typedef struct +{ + /*-------------*/ + int line_out; /* current line out position for this modulator */ + /*-------------*/ + sinus_modulator sinus; /* sinus lfo */ + triang_modulator triang; /* triangle lfo */ + /*-------------------------*/ + /* first order All-Pass interpolator members */ + fluid_real_t frac_pos_mod; /* fractional position part between samples */ + /* previous value used when interpolating using fractional */ + fluid_real_t buffer; +} modulator; + +/* Private data for SKEL file */ +struct _fluid_chorus_t +{ + int type; + fluid_real_t depth_ms; + fluid_real_t level; + fluid_real_t speed_Hz; + int number_blocks; + fluid_real_t sample_rate; + + /* width control: 0 monophonic, > 0 more stereophonic */ + fluid_real_t width; + fluid_real_t wet1, wet2; + + fluid_real_t *line; /* buffer line */ + int size; /* effective internal size (in samples) */ + + int line_in; /* line in position */ + + /* center output position members */ + fluid_real_t center_pos_mod; /* center output position modulated by modulator */ + int mod_depth; /* modulation depth (in samples) */ + + /* variable rate control of center output position */ + int index_rate; /* index rate to know when to update center_pos_mod */ + int mod_rate; /* rate at which center_pos_mod is updated */ + + /* modulator member */ + modulator mod[MAX_CHORUS]; /* sinus/triangle modulator */ +}; + +/*----------------------------------------------------------------------------- + Sets the frequency of sinus oscillator. + + @param mod pointer on modulator structure. + @param freq frequency of the oscillator in Hz. + @param sample_rate sample rate on audio output in Hz. + @param phase initial phase of the oscillator in degree (0 to 360). +-----------------------------------------------------------------------------*/ +static void set_sinus_frequency(sinus_modulator *mod, + float freq, float sample_rate, float phase) +{ + fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* initial angle */ + fluid_real_t a; + + mod->a1 = 2 * FLUID_COS(w); + + a = (2 * FLUID_M_PI / 360) * phase; + + mod->buffer2 = FLUID_SIN(a - w); /* y(n-1) = sin(-initial angle) */ + mod->buffer1 = FLUID_SIN(a); /* y(n) = sin(initial phase) */ + mod->reset_buffer2 = FLUID_SIN(FLUID_M_PI / 2 - w); /* reset value for PI/2 */ +} + +/*----------------------------------------------------------------------------- + Gets current value of sinus modulator: + y(n) = a1 . y(n-1) - y(n-2) + out = a1 . buffer1 - buffer2 + + @param mod pointer on modulator structure. + @return current value of the modulator sine wave. +-----------------------------------------------------------------------------*/ +static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod) +{ + fluid_real_t out; + out = mod->a1 * mod->buffer1 - mod->buffer2; + mod->buffer2 = mod->buffer1; + + if(out >= 1.0f) /* reset in case of instability near PI/2 */ + { + out = 1.0f; /* forces output to the right value */ + mod->buffer2 = mod->reset_buffer2; + } + + if(out <= -1.0f) /* reset in case of instability near -PI/2 */ + { + out = -1.0f; /* forces output to the right value */ + mod->buffer2 = - mod->reset_buffer2; + } + + mod->buffer1 = out; + return out; +} + +/*----------------------------------------------------------------------------- + Set the frequency of triangular oscillator + The frequency is converted in a slope value. + The initial value is set according to frac_phase which is a position + in the period relative to the beginning of the period. + For example: 0 is the beginning of the period, 1/4 is at 1/4 of the period + relative to the beginning. + + @param mod pointer on modulator structure. + @param freq frequency of the oscillator in Hz. + @param sample_rate sample rate on audio output in Hz. + @param frac_phase initial phase (see comment above). +-----------------------------------------------------------------------------*/ +static void set_triangle_frequency(triang_modulator *mod, float freq, + float sample_rate, float frac_phase) +{ + fluid_real_t ns_period; /* period in numbers of sample */ + + if(freq <= 0.0) + { + freq = 0.5f; + } + + mod->freq = freq; + + ns_period = sample_rate / freq; + + /* the slope of a triangular osc (0 up to +1 down to -1 up to 0....) is equivalent + to the slope of a saw osc (0 -> +4) */ + mod->inc = 4 / ns_period; /* positive slope */ + + /* The initial value and the sign of the slope depend of initial phase: + initial value = = (ns_period * frac_phase) * slope + */ + mod->val = ns_period * frac_phase * mod->inc; + + if(1.0 <= mod->val && mod->val < 3.0) + { + mod->val = 2.0 - mod->val; /* 1.0 down to -1.0 */ + mod->inc = -mod->inc; /* negative slope */ + } + else if(3.0 <= mod->val) + { + mod->val = mod->val - 4.0; /* -1.0 up to +1.0. */ + } + + /* else val < 1.0 */ +} + +/*----------------------------------------------------------------------------- + Get current value of triangular oscillator + y(n) = y(n-1) + dy + + @param mod pointer on triang_modulator structure. + @return current value. +-----------------------------------------------------------------------------*/ +static FLUID_INLINE fluid_real_t get_mod_triang(triang_modulator *mod) +{ + mod->val = mod->val + mod->inc ; + + if(mod->val >= 1.0) + { + mod->inc = -mod->inc; + return 1.0; + } + + if(mod->val <= -1.0) + { + mod->inc = -mod->inc; + return -1.0; + } + + return mod->val; +} +/*----------------------------------------------------------------------------- + Reads the sample value out of the modulated delay line. + + @param chorus pointer on chorus unit. + @param mod pointer on modulator structure. + @return current value. +-----------------------------------------------------------------------------*/ +static FLUID_INLINE fluid_real_t get_mod_delay(fluid_chorus_t *chorus, + modulator *mod) +{ + fluid_real_t out_index; /* new modulated index position */ + int int_out_index; /* integer part of out_index */ + fluid_real_t out; /* value to return */ + + /* Checks if the modulator must be updated (every mod_rate samples). */ + /* Important: center_pos_mod must be used immediately for the + first sample. So, mdl->index_rate must be initialized + to mdl->mod_rate (new_mod_delay_line()) */ + + if(chorus->index_rate >= chorus->mod_rate) + { + /* out_index = center position (center_pos_mod) + sinus waweform */ + if(chorus->type == FLUID_CHORUS_MOD_SINE) + { + out_index = chorus->center_pos_mod + + get_mod_sinus(&mod->sinus) * chorus->mod_depth; + } + else + { + out_index = chorus->center_pos_mod + + get_mod_triang(&mod->triang) * chorus->mod_depth; + } + + /* extracts integer part in int_out_index */ + if(out_index >= 0.0f) + { + int_out_index = (int)out_index; /* current integer part */ + + /* forces read index (line_out) with integer modulation value */ + /* Boundary check and circular motion as needed */ + if((mod->line_out = int_out_index) >= chorus->size) + { + mod->line_out -= chorus->size; + } + } + else /* negative */ + { + int_out_index = (int)(out_index - 1); /* previous integer part */ + /* forces read index (line_out) with integer modulation value */ + /* circular motion as needed */ + mod->line_out = int_out_index + chorus->size; + } + + /* extracts fractionnal part. (it will be used when interpolating + between line_out and line_out +1) and memorize it. + Memorizing is necessary for modulation rate above 1 */ + mod->frac_pos_mod = out_index - int_out_index; + } + + /* First order all-pass interpolation ----------------------------------*/ + /* https://ccrma.stanford.edu/~jos/pasp/First_Order_Allpass_Interpolation.html */ + /* begins interpolation: read current sample */ + out = chorus->line[mod->line_out]; + + /* updates line_out to the next sample. + Boundary check and circular motion as needed */ + if(++mod->line_out >= chorus->size) + { + mod->line_out -= chorus->size; + } + + /* Fractional interpolation between next sample (at next position) and + previous output added to current sample. + */ + out += mod->frac_pos_mod * (chorus->line[mod->line_out] - mod->buffer); + mod->buffer = out; /* memorizes current output */ + return out; +} + +/*----------------------------------------------------------------------------- + Push a sample val into the delay line + + @param dl delay line to push value into. + @param val the value to push into dl. +-----------------------------------------------------------------------------*/ +#define push_in_delay_line(dl, val) \ +{\ + dl->line[dl->line_in] = val;\ + /* Incrementation and circular motion if necessary */\ + if(++dl->line_in >= dl->size) dl->line_in -= dl->size;\ +}\ + +/*----------------------------------------------------------------------------- + Initialize : mod_rate, center_pos_mod, and index rate + + center_pos_mod is initialized so that the delay between center_pos_mod and + line_in is: mod_depth + INTERP_SAMPLES_NBR. + + @param chorus pointer on chorus unit. +-----------------------------------------------------------------------------*/ +static void set_center_position(fluid_chorus_t *chorus) +{ + int center; + + /* Sets the modulation rate. This rate defines how often + the center position (center_pos_mod ) is modulated . + The value is expressed in samples. The default value is 1 that means that + center_pos_mod is updated at every sample. + For example with a value of 2, the center position position will be + updated only one time every 2 samples only. + */ + chorus->mod_rate = LOW_MOD_RATE; /* default modulation rate */ + + /* compensate mod rate for high modulation depth */ + if(chorus->mod_depth > LOW_MOD_DEPTH) + { + int delta_mod_depth = (chorus->mod_depth - LOW_MOD_DEPTH); + chorus->mod_rate += (delta_mod_depth * RANGE_MOD_RATE) / RANGE_MOD_DEPTH; + } + + /* Initializes the modulated center position (center_pos_mod) so that: + - the delay between center_pos_mod and line_in is: + mod_depth + INTERP_SAMPLES_NBR. + */ + center = chorus->line_in - (INTERP_SAMPLES_NBR + chorus->mod_depth); + + if(center < 0) + { + center += chorus->size; + } + + chorus->center_pos_mod = (fluid_real_t)center; + + /* index rate to control when to update center_pos_mod */ + /* Important: must be set to get center_pos_mod immediately used for the + reading of first sample (see get_mod_delay()) */ + chorus->index_rate = chorus->mod_rate; +} + +/*----------------------------------------------------------------------------- + Update internal parameters dependent of sample rate. + - mod_depth. + - mod_rate, center_pos_mod, and index rate. + - modulators frequency. + + @param chorus, pointer on chorus unit. +-----------------------------------------------------------------------------*/ +static void update_parameters_from_sample_rate(fluid_chorus_t *chorus) +{ + int i; + + /* initialize modulation depth (peak to peak) (in samples) */ + /* convert modulation depth in ms to sample number */ + chorus->mod_depth = (int)(chorus->depth_ms / 1000.0 + * chorus->sample_rate); + + /* the delay line is fixed. So we reduce mod_depth (if necessary) */ + if(chorus->mod_depth > MAX_SAMPLES) + { + FLUID_LOG(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", + MAX_SAMPLES); + chorus->mod_depth = MAX_SAMPLES; + /* set depth_ms to maximum to avoid spamming console with above warning */ + chorus->depth_ms = (chorus->mod_depth * 1000) / chorus->sample_rate; + } + + chorus->mod_depth /= 2; /* amplitude is peak to peek / 2 */ +#ifdef DEBUG_PRINT + printf("depth_ms:%f, depth_samples/2:%d\n", chorus->depth_ms, chorus->mod_depth); +#endif + + /* Initializes the modulated center position: + mod_rate, center_pos_mod, and index rate. + */ + set_center_position(chorus); /* must be called before set_xxxx_frequency() */ +#ifdef DEBUG_PRINT + printf("mod_rate:%d\n", chorus->mod_rate); +#endif + + /* initialize modulator frequency */ + for(i = 0; i < chorus->number_blocks; i++) + { + set_sinus_frequency(&chorus->mod[i].sinus, + chorus->speed_Hz * chorus->mod_rate, + chorus->sample_rate, + /* phase offset between modulators waveform */ + (float)((360.0f / (float) chorus->number_blocks) * i)); + + set_triangle_frequency(&chorus->mod[i].triang, + chorus->speed_Hz * chorus->mod_rate, + chorus->sample_rate, + /* phase offset between modulators waveform */ + (float)i / chorus->number_blocks); + } +} + +/*----------------------------------------------------------------------------- + Modulated delay line initialization. + + Sets the length line ( alloc delay samples). + Remark: the function sets the internal size according to the length delay_length. + The size is augmented by INTERP_SAMPLES_NBR to take account of interpolation. + + @param chorus, pointer on chorus unit. + @param delay_length the length of the delay line in samples. + @return FLUID_OK if success , FLUID_FAILED if memory error. + + Return FLUID_OK if success, FLUID_FAILED if memory error. +-----------------------------------------------------------------------------*/ +static int new_mod_delay_line(fluid_chorus_t *chorus, int delay_length) +{ + /*-----------------------------------------------------------------------*/ + /* checks parameter */ + if(delay_length < 1) + { + return FLUID_FAILED; + } + + chorus->mod_depth = 0; + /*----------------------------------------------------------------------- + allocates delay_line and initialize members: - line, size, line_in... + */ + /* total size of the line: size = INTERP_SAMPLES_NBR + delay_length */ + chorus->size = delay_length + INTERP_SAMPLES_NBR; + chorus->line = FLUID_ARRAY(fluid_real_t, chorus->size); + + if(! chorus->line) + { + return FLUID_FAILED; + } + + /* clears the buffer: + - delay line + - interpolator member: buffer, frac_pos_mod + */ + fluid_chorus_reset(chorus); + + /* Initializes line_in to the start of the buffer */ + chorus->line_in = 0; + /*------------------------------------------------------------------------ + Initializes modulation members: + - modulation rate (the speed at which center_pos_mod is modulated: mod_rate + - modulated center position: center_pos_mod + - index rate to know when to update center_pos_mod:index_rate + -------------------------------------------------------------------------*/ + /* Initializes the modulated center position: + mod_rate, center_pos_mod, and index rate + */ + set_center_position(chorus); + + return FLUID_OK; +} + +/*----------------------------------------------------------------------------- + API +------------------------------------------------------------------------------*/ +/** + * Create the chorus unit. Once created the chorus have no parameters set, so + * fluid_chorus_set() must be called at least one time after calling + * new_fluid_chorus(). + * + * @param sample_rate, audio sample rate in Hz. + * @return pointer on chorus unit. + */ +fluid_chorus_t * +new_fluid_chorus(fluid_real_t sample_rate) +{ + fluid_chorus_t *chorus; + + chorus = FLUID_NEW(fluid_chorus_t); + + if(chorus == NULL) + { + FLUID_LOG(FLUID_PANIC, "chorus: Out of memory"); + return NULL; + } + + FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t)); + + chorus->sample_rate = sample_rate; + +#ifdef DEBUG_PRINT + printf("fluid_chorus_t:%d bytes\n", sizeof(fluid_chorus_t)); + printf("fluid_real_t:%d bytes\n", sizeof(fluid_real_t)); +#endif + +#ifdef DEBUG_PRINT + printf("NEW_MOD\n"); +#endif + + if(new_mod_delay_line(chorus, MAX_SAMPLES) == FLUID_FAILED) + { + delete_fluid_chorus(chorus); + return NULL; + } + + return chorus; +} + +/** + * Delete the chorus unit. + * @param chorus pointer on chorus unit returned by new_fluid_chorus(). + */ +void +delete_fluid_chorus(fluid_chorus_t *chorus) +{ + fluid_return_if_fail(chorus != NULL); + + FLUID_FREE(chorus->line); + FLUID_FREE(chorus); +} + +/** + * Clear the internal delay line and associate filter. + * @param chorus pointer on chorus unit returned by new_fluid_chorus(). + */ +void +fluid_chorus_reset(fluid_chorus_t *chorus) +{ + int i; + unsigned int u; + + /* reset delay line */ + for(i = 0; i < chorus->size; i++) + { + chorus->line[i] = 0; + } + + /* reset modulators's allpass filter */ + for(u = 0; u < FLUID_N_ELEMENTS(chorus->mod); u++) + { + /* initializes 1st order All-Pass interpolator members */ + chorus->mod[u].buffer = 0; /* previous delay sample value */ + chorus->mod[u].frac_pos_mod = 0; /* fractional position (between consecutives sample) */ + } +} + +/** + * Set one or more chorus parameters. + * + * @param chorus Chorus instance. + * @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t). + * @param nr Chorus voice count (0-99, CPU time consumption proportional to + * this value). + * @param level Chorus level (0.0-10.0). + * @param speed Chorus speed in Hz (0.1-5.0). + * @param depth_ms Chorus depth (max value depends on synth sample rate, + * 0.0-21.0 is safe for sample rate values up to 96KHz). + * @param type Chorus waveform type (#fluid_chorus_mod). + */ +void +fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, + fluid_real_t speed, fluid_real_t depth_ms, int type) +{ + if(set & FLUID_CHORUS_SET_NR) /* number of block */ + { + chorus->number_blocks = nr; + } + + if(set & FLUID_CHORUS_SET_LEVEL) /* output level */ + { + chorus->level = level; + } + + if(set & FLUID_CHORUS_SET_SPEED) /* lfo frequency (in Hz) */ + { + chorus->speed_Hz = speed; + } + + if(set & FLUID_CHORUS_SET_DEPTH) /* modulation depth (in ms) */ + { + chorus->depth_ms = depth_ms; + } + + if(set & FLUID_CHORUS_SET_TYPE) /* lfo shape (sinus, triangle) */ + { + chorus->type = type; + } + + /* check min , max parameters */ + if(chorus->number_blocks < 0) + { + FLUID_LOG(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0."); + chorus->number_blocks = 0; + } + else if(chorus->number_blocks > MAX_CHORUS) + { + FLUID_LOG(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.", + MAX_CHORUS); + chorus->number_blocks = MAX_CHORUS; + } + + if(chorus->speed_Hz < MIN_SPEED_HZ) + { + FLUID_LOG(FLUID_WARN, "chorus: speed is too low (min %f)! Setting value to min.", + (double) MIN_SPEED_HZ); + chorus->speed_Hz = MIN_SPEED_HZ; + } + else if(chorus->speed_Hz > MAX_SPEED_HZ) + { + FLUID_LOG(FLUID_WARN, "chorus: speed must be below %f Hz! Setting value to max.", + (double) MAX_SPEED_HZ); + chorus->speed_Hz = MAX_SPEED_HZ; + } + + if(chorus->depth_ms < 0.0) + { + FLUID_LOG(FLUID_WARN, "chorus: depth must be positive! Setting value to 0."); + chorus->depth_ms = 0.0; + } + + if(chorus->level < 0.0) + { + FLUID_LOG(FLUID_WARN, "chorus: level must be positive! Setting value to 0."); + chorus->level = 0.0; + } + else if(chorus->level > MAX_LEVEL) + { + FLUID_LOG(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! " + "Setting it to 0.1."); + chorus->level = 0.1; + } + + /* update parameters dependent of sample rate */ + update_parameters_from_sample_rate(chorus); + +#ifdef DEBUG_PRINT + printf("lfo type:%d\n", chorus->type); + printf("speed_Hz:%f\n", chorus->speed_Hz); +#endif + + /* Initialize the lfo waveform */ + if((chorus->type != FLUID_CHORUS_MOD_SINE) && + (chorus->type != FLUID_CHORUS_MOD_TRIANGLE)) + { + FLUID_LOG(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave."); + chorus->type = FLUID_CHORUS_MOD_SINE; + } + +#ifdef DEBUG_PRINT + + if(chorus->type == FLUID_CHORUS_MOD_SINE) + { + printf("lfo: sinus\n"); + } + else + { + printf("lfo: triangle\n"); + } + + printf("nr:%d\n", chorus->number_blocks); +#endif + + /* Recalculate internal values after parameters change */ + + /* + Note: + Actually WIDTH is fixed to maximum value. But in the future we could add a setting + "synth.chorus.width" to allow a gradually stereo effect from minimum (monophonic) to + maximum stereo effect. + If this setting will be added, remove the following instruction. + */ + chorus->width = WIDTH; + { + /* The stereo amplitude equation (wet1 and wet2 below) have a + tendency to produce high amplitude with high width values ( 1 < width < 10). + This results in an unwanted noisy output clipped by the audio card. + To avoid this dependency, we divide by (1 + chorus->width * SCALE_WET_WIDTH) + Actually, with a SCALE_WET_WIDTH of 0.2, (regardless of level setting), + the output amplitude (wet) seems rather independent of width setting */ + + fluid_real_t wet = chorus->level * SCALE_WET ; + + /* wet1 and wet2 are used by the stereo effect controlled by the width setting + for producing a stereo ouptput from a monophonic chorus signal. + Please see the note above about a side effect tendency */ + + if(chorus->number_blocks > 1) + { + wet = wet / (1.0f + chorus->width * SCALE_WET_WIDTH); + chorus->wet1 = wet * (chorus->width / 2.0f + 0.5f); + chorus->wet2 = wet * ((1.0f - chorus->width) / 2.0f); +#ifdef DEBUG_PRINT + printf("width:%f\n", chorus->width); + + if(chorus->width > 0) + { + printf("nr > 1, width > 0 => out stereo\n"); + } + else + { + printf("nr > 1, width:0 =>out mono\n"); + } + +#endif + } + else + { + /* only one chorus block */ + if(chorus->width == 0.0) + { + /* wet1 and wet2 should make stereo output monomophic */ + chorus->wet1 = chorus->wet2 = wet; + } + else + { + /* for width > 0, wet1 and wet2 should make stereo output stereo + with only one block. This will only possible by inverting + the unique signal on each left and right output. + Note however that with only one block, it isn't possible to + have a graduate width effect */ + chorus->wet1 = wet; + chorus->wet2 = -wet; /* inversion */ + } + +#ifdef DEBUG_PRINT + printf("width:%f\n", chorus->width); + + if(chorus->width != 0) + { + printf("one block, width > 0 => out stereo\n"); + } + else + { + printf("one block, width:0 => out mono\n"); + } + +#endif + } + } +} + +/* +* Applies a sample rate change on the chorus. +* Note that while the chorus is used by calling any fluid_chorus_processXXX() +* function, calling fluid_chorus_samplerate_change() isn't multi task safe. +* To deal properly with this issue follow the steps: +* 1) Stop chorus processing (i.e disable calling to any fluid_chorus_processXXX(). +* chorus functions. +* 2) Change sample rate by calling fluid_chorus_samplerate_change(). +* 3) Restart chorus processing (i.e enabling calling any fluid_chorus_processXXX() +* chorus functions. +* +* Another solution is to substitute step (2): +* 2.1) delete the chorus by calling delete_fluid_chorus(). +* 2.2) create the chorus by calling new_fluid_chorus(). +* +* @param chorus pointer on the chorus. +* @param sample_rate new sample rate value. +*/ +void +fluid_chorus_samplerate_change(fluid_chorus_t *chorus, fluid_real_t sample_rate) +{ + chorus->sample_rate = sample_rate; + + /* update parameters dependent of sample rate */ + update_parameters_from_sample_rate(chorus); +} + +/** + * Process chorus by mixing the result in output buffer. + * @param chorus pointer on chorus unit returned by new_fluid_chorus(). + * @param in, pointer on monophonic input buffer of FLUID_BUFSIZE samples. + * @param left_out, right_out, pointers on stereo output buffers of + * FLUID_BUFSIZE samples. + */ +void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int sample_index; + int i; + fluid_real_t d_out[2]; /* output stereo Left and Right */ + + /* foreach sample, process output sample then input sample */ + for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) + { + fluid_real_t out; /* block output */ + + d_out[0] = d_out[1] = 0.0f; /* clear stereo unit input */ + +#if 0 + /* Debug: Listen to the chorus signal only */ + left_out[sample_index] = 0; + right_out[sample_index] = 0; +#endif + + ++chorus->index_rate; /* modulator rate */ + + /* foreach chorus block, process output sample */ + for(i = 0; i < chorus->number_blocks; i++) + { + /* get sample from the output of modulated delay line */ + out = get_mod_delay(chorus, &chorus->mod[i]); + + /* accumulate out into stereo unit input */ + d_out[i & 1] += out; + } + + /* update modulator index rate and output center position */ + if(chorus->index_rate >= chorus->mod_rate) + { + chorus->index_rate = 0; /* clear modulator index rate */ + + /* updates center position (center_pos_mod) to the next position + specified by modulation rate */ + if((chorus->center_pos_mod += chorus->mod_rate) >= chorus->size) + { + chorus->center_pos_mod -= chorus->size; + } + } + + /* Adjust stereo input level in case of number_blocks odd: + In those case, d_out[1] level is lower than d_out[0], so we need to + add out value to d_out[1] to have d_out[0] and d_out[1] balanced. + */ + if((i & 1) && i > 2) // i = 3,5,7... + { + d_out[1] += out ; + } + + /* Write the current input sample into the circular buffer. + * Note that 'in' may be aliased with 'left_out'. Hence this must be done + * before "processing stereo unit" (below). This ensures input buffer + * not being overwritten by stereo unit output. + */ + push_in_delay_line(chorus, in[sample_index]); + + /* process stereo unit */ + /* Add the chorus stereo unit d_out to left and right output */ + left_out[sample_index] += d_out[0] * chorus->wet1 + d_out[1] * chorus->wet2; + right_out[sample_index] += d_out[1] * chorus->wet1 + d_out[0] * chorus->wet2; + } +} + +/** + * Process chorus by putting the result in output buffer (no mixing). + * @param chorus pointer on chorus unit returned by new_fluid_chorus(). + * @param in, pointer on monophonic input buffer of FLUID_BUFSIZE samples. + * @param left_out, right_out, pointers on stereo output buffers of + * FLUID_BUFSIZE samples. + */ +/* Duplication of code ... (replaces sample data instead of mixing) */ +void fluid_chorus_processreplace(fluid_chorus_t *chorus, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int sample_index; + int i; + fluid_real_t d_out[2]; /* output stereo Left and Right */ + + /* foreach sample, process output sample then input sample */ + for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) + { + fluid_real_t out; /* block output */ + + d_out[0] = d_out[1] = 0.0f; /* clear stereo unit input */ + +#if 0 + /* Debug: Listen to the chorus signal only */ + left_out[sample_index] = 0; + right_out[sample_index] = 0; +#endif + + ++chorus->index_rate; /* modulator rate */ + + /* foreach chorus block, process output sample */ + for(i = 0; i < chorus->number_blocks; i++) + { + /* get sample from the output of modulated delay line */ + out = get_mod_delay(chorus, &chorus->mod[i]); + + /* accumulate out into stereo unit input */ + d_out[i & 1] += out; + } + + /* update modulator index rate and output center position */ + if(chorus->index_rate >= chorus->mod_rate) + { + chorus->index_rate = 0; /* clear modulator index rate */ + + /* updates center position (center_pos_mod) to the next position + specified by modulation rate */ + if((chorus->center_pos_mod += chorus->mod_rate) >= chorus->size) + { + chorus->center_pos_mod -= chorus->size; + } + } + + /* Adjust stereo input level in case of number_blocks odd: + In those case, d_out[1] level is lower than d_out[0], so we need to + add out value to d_out[1] to have d_out[0] and d_out[1] balanced. + */ + if((i & 1) && i > 2) // i = 3,5,7... + { + d_out[1] += out ; + } + + /* Write the current input sample into the circular buffer. + * Note that 'in' may be aliased with 'left_out'. Hence this must be done + * before "processing stereo unit" (below). This ensures input buffer + * not being overwritten by stereo unit output. + */ + push_in_delay_line(chorus, in[sample_index]); + + /* process stereo unit */ + /* store the chorus stereo unit d_out to left and right output */ + left_out[sample_index] = d_out[0] * chorus->wet1 + d_out[1] * chorus->wet2; + right_out[sample_index] = d_out[1] * chorus->wet1 + d_out[0] * chorus->wet2; + } +} diff --git a/libs/fluidsynth/src/rvoice/fluid_chorus.h b/libs/fluidsynth/src/rvoice/fluid_chorus.h new file mode 100644 index 00000000000..c6d247fa5f3 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_chorus.h @@ -0,0 +1,80 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_CHORUS_H +#define _FLUID_CHORUS_H + +#include "fluidsynth_priv.h" + + +typedef struct _fluid_chorus_t fluid_chorus_t; + +/* enum describing each chorus parameter */ +enum fluid_chorus_param +{ + FLUID_CHORUS_NR, /**< number of delay line */ + FLUID_CHORUS_LEVEL, /**< output level */ + FLUID_CHORUS_SPEED, /**< lfo frequency */ + FLUID_CHORUS_DEPTH, /**< modulation depth */ + FLUID_CHORUS_TYPE, /**< type of waveform */ + FLUID_CHORUS_PARAM_LAST /* number of enum fluid_chorus_param */ +}; + +/* return a bit flag from param: 2^param */ +#define FLUID_CHORPARAM_TO_SETFLAG(param) (1 << param) + +/** Flags for fluid_chorus_set() */ +typedef enum +{ + FLUID_CHORUS_SET_NR = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_NR), + FLUID_CHORUS_SET_LEVEL = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_LEVEL), + FLUID_CHORUS_SET_SPEED = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_SPEED), + FLUID_CHORUS_SET_DEPTH = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_DEPTH), + FLUID_CHORUS_SET_TYPE = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_TYPE), + + /** Value for fluid_chorus_set() which sets all chorus parameters. */ + FLUID_CHORUS_SET_ALL = FLUID_CHORUS_SET_NR + | FLUID_CHORUS_SET_LEVEL + | FLUID_CHORUS_SET_SPEED + | FLUID_CHORUS_SET_DEPTH + | FLUID_CHORUS_SET_TYPE, +} fluid_chorus_set_t; + +/* + * chorus + */ +fluid_chorus_t *new_fluid_chorus(fluid_real_t sample_rate); +void delete_fluid_chorus(fluid_chorus_t *chorus); +void fluid_chorus_reset(fluid_chorus_t *chorus); + +void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, + fluid_real_t speed, fluid_real_t depth_ms, int type); +void +fluid_chorus_samplerate_change(fluid_chorus_t *chorus, fluid_real_t sample_rate); + +void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); +void fluid_chorus_processreplace(fluid_chorus_t *chorus, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); + + + +#endif /* _FLUID_CHORUS_H */ diff --git a/libs/fluidsynth/src/rvoice/fluid_iir_filter.c b/libs/fluidsynth/src/rvoice/fluid_iir_filter.c new file mode 100644 index 00000000000..764fcf4abd8 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_iir_filter.c @@ -0,0 +1,418 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_iir_filter.h" +#include "fluid_sys.h" +#include "fluid_conv.h" + +/** + * Applies a low- or high-pass filter with variable cutoff frequency and quality factor + * for a given biquad transfer function: + * b0 + b1*z^-1 + b2*z^-2 + * H(z) = ------------------------ + * a0 + a1*z^-1 + a2*z^-2 + * + * Also modifies filter state accordingly. + * @param iir_filter Filter parameter + * @param dsp_buf Pointer to the synthesized audio data + * @param count Count of samples in dsp_buf + */ +/* + * Variable description: + * - dsp_a1, dsp_a2: Filter coefficients for the the previously filtered output signal + * - dsp_b0, dsp_b1, dsp_b2: Filter coefficients for input signal + * - coefficients normalized to a0 + * + * A couple of variables are used internally, their results are discarded: + * - dsp_i: Index through the output buffer + * - dsp_centernode: delay line for the IIR filter + * - dsp_hist1: same + * - dsp_hist2: same + */ +void +fluid_iir_filter_apply(fluid_iir_filter_t *iir_filter, + fluid_real_t *dsp_buf, int count) +{ + if(iir_filter->type == FLUID_IIR_DISABLED || iir_filter->q_lin == 0) + { + return; + } + else + { + /* IIR filter sample history */ + fluid_real_t dsp_hist1 = iir_filter->hist1; + fluid_real_t dsp_hist2 = iir_filter->hist2; + + /* IIR filter coefficients */ + fluid_real_t dsp_a1 = iir_filter->a1; + fluid_real_t dsp_a2 = iir_filter->a2; + fluid_real_t dsp_b02 = iir_filter->b02; + fluid_real_t dsp_b1 = iir_filter->b1; + int dsp_filter_coeff_incr_count = iir_filter->filter_coeff_incr_count; + + fluid_real_t dsp_centernode; + int dsp_i; + + /* filter (implement the voice filter according to SoundFont standard) */ + + /* Check for denormal number (too close to zero). */ + if(FLUID_FABS(dsp_hist1) < 1e-20f) + { + dsp_hist1 = 0.0f; /* FIXME JMG - Is this even needed? */ + } + + /* Two versions of the filter loop. One, while the filter is + * changing towards its new setting. The other, if the filter + * doesn't change. + */ + + if(dsp_filter_coeff_incr_count > 0) + { + fluid_real_t dsp_a1_incr = iir_filter->a1_incr; + fluid_real_t dsp_a2_incr = iir_filter->a2_incr; + fluid_real_t dsp_b02_incr = iir_filter->b02_incr; + fluid_real_t dsp_b1_incr = iir_filter->b1_incr; + + + /* Increment is added to each filter coefficient filter_coeff_incr_count times. */ + for(dsp_i = 0; dsp_i < count; dsp_i++) + { + /* The filter is implemented in Direct-II form. */ + dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; + dsp_hist2 = dsp_hist1; + dsp_hist1 = dsp_centernode; + + if(dsp_filter_coeff_incr_count-- > 0) + { + fluid_real_t old_b02 = dsp_b02; + dsp_a1 += dsp_a1_incr; + dsp_a2 += dsp_a2_incr; + dsp_b02 += dsp_b02_incr; + dsp_b1 += dsp_b1_incr; + + /* Compensate history to avoid the filter going havoc with large frequency changes */ + if(iir_filter->compensate_incr && FLUID_FABS(dsp_b02) > 0.001f) + { + fluid_real_t compensate = old_b02 / dsp_b02; + dsp_hist1 *= compensate; + dsp_hist2 *= compensate; + } + } + } /* for dsp_i */ + } + else /* The filter parameters are constant. This is duplicated to save time. */ + { + for(dsp_i = 0; dsp_i < count; dsp_i++) + { + /* The filter is implemented in Direct-II form. */ + dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; + dsp_hist2 = dsp_hist1; + dsp_hist1 = dsp_centernode; + } + } + + iir_filter->hist1 = dsp_hist1; + iir_filter->hist2 = dsp_hist2; + iir_filter->a1 = dsp_a1; + iir_filter->a2 = dsp_a2; + iir_filter->b02 = dsp_b02; + iir_filter->b1 = dsp_b1; + iir_filter->filter_coeff_incr_count = dsp_filter_coeff_incr_count; + + fluid_check_fpe("voice_filter"); + } +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_init) +{ + fluid_iir_filter_t *iir_filter = obj; + enum fluid_iir_filter_type type = param[0].i; + enum fluid_iir_filter_flags flags = param[1].i; + + iir_filter->type = type; + iir_filter->flags = flags; + + if(type != FLUID_IIR_DISABLED) + { + fluid_iir_filter_reset(iir_filter); + } +} + +void +fluid_iir_filter_reset(fluid_iir_filter_t *iir_filter) +{ + iir_filter->hist1 = 0; + iir_filter->hist2 = 0; + iir_filter->last_fres = -1.; + iir_filter->q_lin = 0; + iir_filter->filter_startup = 1; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_fres) +{ + fluid_iir_filter_t *iir_filter = obj; + fluid_real_t fres = param[0].real; + + iir_filter->fres = fres; + iir_filter->last_fres = -1.; +} + +static fluid_real_t fluid_iir_filter_q_from_dB(fluid_real_t q_dB) +{ + /* The generator contains 'centibels' (1/10 dB) => divide by 10 to + * obtain dB */ + q_dB /= 10.0f; + + /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */ + fluid_clip(q_dB, 0.0f, 96.0f); + + /* Short version: Modify the Q definition in a way, that a Q of 0 + * dB leads to no resonance hump in the freq. response. + * + * Long version: From SF2.01, page 39, item 9 (initialFilterQ): + * "The gain at the cutoff frequency may be less than zero when + * zero is specified". Assume q_dB=0 / q_lin=1: If we would leave + * q as it is, then this results in a 3 dB hump slightly below + * fc. At fc, the gain is exactly the DC gain (0 dB). What is + * (probably) meant here is that the filter does not show a + * resonance hump for q_dB=0. In this case, the corresponding + * q_lin is 1/sqrt(2)=0.707. The filter should have 3 dB of + * attenuation at fc now. In this case Q_dB is the height of the + * resonance peak not over the DC gain, but over the frequency + * response of a non-resonant filter. This idea is implemented as + * follows: */ + q_dB -= 3.01f; + + /* The 'sound font' Q is defined in dB. The filter needs a linear + q. Convert. */ + return FLUID_POW(10.0f, q_dB / 20.0f); +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_q) +{ + fluid_iir_filter_t *iir_filter = obj; + fluid_real_t q = param[0].real; + int flags = iir_filter->flags; + + if(flags & FLUID_IIR_Q_ZERO_OFF && q <= 0.0) + { + q = 0; + } + else if(flags & FLUID_IIR_Q_LINEAR) + { + /* q is linear (only for user-defined filter) + * increase to avoid Q being somewhere between zero and one, + * which results in some strange amplified lowpass signal + */ + q++; + } + else + { + q = fluid_iir_filter_q_from_dB(q); + } + + iir_filter->q_lin = q; + iir_filter->filter_gain = 1.0; + + if(!(flags & FLUID_IIR_NO_GAIN_AMP)) + { + /* SF 2.01 page 59: + * + * The SoundFont specs ask for a gain reduction equal to half the + * height of the resonance peak (Q). For example, for a 10 dB + * resonance peak, the gain is reduced by 5 dB. This is done by + * multiplying the total gain with sqrt(1/Q). `Sqrt' divides dB + * by 2 (100 lin = 40 dB, 10 lin = 20 dB, 3.16 lin = 10 dB etc) + * The gain is later factored into the 'b' coefficients + * (numerator of the filter equation). This gain factor depends + * only on Q, so this is the right place to calculate it. + */ + iir_filter->filter_gain /= FLUID_SQRT(q); + } + + /* The synthesis loop will have to recalculate the filter coefficients. */ + iir_filter->last_fres = -1.; +} + +static FLUID_INLINE void +fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t *iir_filter, + int transition_samples, + fluid_real_t output_rate) +{ + /* FLUID_IIR_Q_LINEAR may switch the filter off by setting Q==0 */ + if(iir_filter->q_lin == 0) + { + return; + } + else + { + /* + * Those equations from Robert Bristow-Johnson's `Cookbook + * formulae for audio EQ biquad filter coefficients', obtained + * from Harmony-central.com / Computer / Programming. They are + * the result of the bilinear transform on an analogue filter + * prototype. To quote, `BLT frequency warping has been taken + * into account for both significant frequency relocation and for + * bandwidth readjustment'. */ + + fluid_real_t omega = (fluid_real_t)(2.0 * M_PI) * + (iir_filter->last_fres / output_rate); + fluid_real_t sin_coeff = FLUID_SIN(omega); + fluid_real_t cos_coeff = FLUID_COS(omega); + fluid_real_t alpha_coeff = sin_coeff / (2.0f * iir_filter->q_lin); + fluid_real_t a0_inv = 1.0f / (1.0f + alpha_coeff); + + /* Calculate the filter coefficients. All coefficients are + * normalized by a0. Think of `a1' as `a1/a0'. + * + * Here a couple of multiplications are saved by reusing common expressions. + * The original equations should be: + * iir_filter->b0=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; + * iir_filter->b1=(1.-cos_coeff)*a0_inv*iir_filter->filter_gain; + * iir_filter->b2=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; */ + + /* "a" coeffs are same for all 3 available filter types */ + fluid_real_t a1_temp = -2.0f * cos_coeff * a0_inv; + fluid_real_t a2_temp = (1.0f - alpha_coeff) * a0_inv; + + fluid_real_t b02_temp, b1_temp; + + switch(iir_filter->type) + { + case FLUID_IIR_HIGHPASS: + b1_temp = (1.0f + cos_coeff) * a0_inv * iir_filter->filter_gain; + + /* both b0 -and- b2 */ + b02_temp = b1_temp * 0.5f; + + b1_temp *= -1.0f; + break; + + case FLUID_IIR_LOWPASS: + b1_temp = (1.0f - cos_coeff) * a0_inv * iir_filter->filter_gain; + + /* both b0 -and- b2 */ + b02_temp = b1_temp * 0.5f; + break; + + default: + /* filter disabled, should never get here */ + return; + } + + iir_filter->compensate_incr = 0; + + if(iir_filter->filter_startup || (transition_samples == 0)) + { + /* The filter is calculated, because the voice was started up. + * In this case set the filter coefficients without delay. + */ + iir_filter->a1 = a1_temp; + iir_filter->a2 = a2_temp; + iir_filter->b02 = b02_temp; + iir_filter->b1 = b1_temp; + iir_filter->filter_coeff_incr_count = 0; + iir_filter->filter_startup = 0; +// printf("Setting initial filter coefficients.\n"); + } + else + { + + /* The filter frequency is changed. Calculate an increment + * factor, so that the new setting is reached after one buffer + * length. x_incr is added to the current value FLUID_BUFSIZE + * times. The length is arbitrarily chosen. Longer than one + * buffer will sacrifice some performance, though. Note: If + * the filter is still too 'grainy', then increase this number + * at will. + */ + + iir_filter->a1_incr = (a1_temp - iir_filter->a1) / transition_samples; + iir_filter->a2_incr = (a2_temp - iir_filter->a2) / transition_samples; + iir_filter->b02_incr = (b02_temp - iir_filter->b02) / transition_samples; + iir_filter->b1_incr = (b1_temp - iir_filter->b1) / transition_samples; + + if(FLUID_FABS(iir_filter->b02) > 0.0001f) + { + fluid_real_t quota = b02_temp / iir_filter->b02; + iir_filter->compensate_incr = quota < 0.5f || quota > 2.f; + } + + /* Have to add the increments filter_coeff_incr_count times. */ + iir_filter->filter_coeff_incr_count = transition_samples; + } + + fluid_check_fpe("voice_write filter calculation"); + } +} + + +void fluid_iir_filter_calc(fluid_iir_filter_t *iir_filter, + fluid_real_t output_rate, + fluid_real_t fres_mod) +{ + fluid_real_t fres; + + /* calculate the frequency of the resonant filter in Hz */ + fres = fluid_ct2hz(iir_filter->fres + fres_mod); + + /* FIXME - Still potential for a click during turn on, can we interpolate + between 20khz cutoff and 0 Q? */ + + /* I removed the optimization of turning the filter off when the + * resonance frequency is above the maximum frequency. Instead, the + * filter frequency is set to a maximum of 0.45 times the sampling + * rate. For a 44100 kHz sampling rate, this amounts to 19845 + * Hz. The reason is that there were problems with anti-aliasing when the + * synthesizer was run at lower sampling rates. Thanks to Stephan + * Tassart for pointing me to this bug. By turning the filter on and + * clipping the maximum filter frequency at 0.45*srate, the filter + * is used as an anti-aliasing filter. */ + + if(fres > 0.45f * output_rate) + { + fres = 0.45f * output_rate; + } + else if(fres < 5.f) + { + fres = 5.f; + } + + /* if filter enabled and there is a significant frequency change.. */ + if(iir_filter->type != FLUID_IIR_DISABLED && FLUID_FABS(fres - iir_filter->last_fres) > 0.01f) + { + /* The filter coefficients have to be recalculated (filter + * parameters have changed). Recalculation for various reasons is + * forced by setting last_fres to -1. The flag filter_startup + * indicates, that the DSP loop runs for the first time, in this + * case, the filter is set directly, instead of smoothly fading + * between old and new settings. */ + iir_filter->last_fres = fres; + fluid_iir_filter_calculate_coefficients(iir_filter, FLUID_BUFSIZE, + output_rate); + } + + + fluid_check_fpe("voice_write DSP coefficients"); + +} diff --git a/libs/fluidsynth/src/rvoice/fluid_iir_filter.h b/libs/fluidsynth/src/rvoice/fluid_iir_filter.h new file mode 100644 index 00000000000..571027897eb --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_iir_filter.h @@ -0,0 +1,74 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_IIR_FILTER_H +#define _FLUID_IIR_FILTER_H + +#include "fluidsynth_priv.h" + +typedef struct _fluid_iir_filter_t fluid_iir_filter_t; + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_init); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_fres); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_q); + +void fluid_iir_filter_apply(fluid_iir_filter_t *iir_filter, + fluid_real_t *dsp_buf, int dsp_buf_count); + +void fluid_iir_filter_reset(fluid_iir_filter_t *iir_filter); + +void fluid_iir_filter_calc(fluid_iir_filter_t *iir_filter, + fluid_real_t output_rate, + fluid_real_t fres_mod); + +/* We can't do information hiding here, as fluid_voice_t includes the struct + without a pointer. */ +struct _fluid_iir_filter_t +{ + enum fluid_iir_filter_type type; /* specifies the type of this filter */ + enum fluid_iir_filter_flags flags; /* additional flags to customize this filter */ + + /* filter coefficients */ + /* The coefficients are normalized to a0. */ + /* b0 and b2 are identical => b02 */ + fluid_real_t b02; /* b0 / a0 */ + fluid_real_t b1; /* b1 / a0 */ + fluid_real_t a1; /* a0 / a0 */ + fluid_real_t a2; /* a1 / a0 */ + + fluid_real_t b02_incr; + fluid_real_t b1_incr; + fluid_real_t a1_incr; + fluid_real_t a2_incr; + int filter_coeff_incr_count; + int compensate_incr; /* Flag: If set, must compensate history */ + fluid_real_t hist1, hist2; /* Sample history for the IIR filter */ + int filter_startup; /* Flag: If set, the filter will be set directly. + Else it changes smoothly. */ + + fluid_real_t fres; /* the resonance frequency, in cents (not absolute cents) */ + fluid_real_t last_fres; /* Current resonance frequency of the IIR filter */ + /* Serves as a flag: A deviation between fres and last_fres */ + /* indicates, that the filter has to be recalculated. */ + fluid_real_t q_lin; /* the q-factor on a linear scale */ + fluid_real_t filter_gain; /* Gain correction factor, depends on q */ +}; + +#endif diff --git a/libs/fluidsynth/src/rvoice/fluid_lfo.c b/libs/fluidsynth/src/rvoice/fluid_lfo.c new file mode 100644 index 00000000000..ae21cdd0f72 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_lfo.c @@ -0,0 +1,17 @@ +#include "fluid_lfo.h" + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_incr) +{ + fluid_lfo_t *lfo = obj; + fluid_real_t increment = param[0].real; + + lfo->increment = increment; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_delay) +{ + fluid_lfo_t *lfo = obj; + unsigned int delay = param[0].i; + + lfo->delay = delay; +} diff --git a/libs/fluidsynth/src/rvoice/fluid_lfo.h b/libs/fluidsynth/src/rvoice/fluid_lfo.h new file mode 100644 index 00000000000..9a439498213 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_lfo.h @@ -0,0 +1,74 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_LFO_H +#define _FLUID_LFO_H + +#include "fluid_sys.h" + +typedef struct _fluid_lfo_t fluid_lfo_t; + +struct _fluid_lfo_t +{ + fluid_real_t val; /* the current value of the LFO */ + unsigned int delay; /* the delay of the lfo in samples */ + fluid_real_t increment; /* the lfo frequency is converted to a per-buffer increment */ +}; + +static FLUID_INLINE void +fluid_lfo_reset(fluid_lfo_t *lfo) +{ + lfo->val = 0.0f; +} + +// These two cannot be inlined since they're used by event_dispatch +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_incr); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_delay); + +static FLUID_INLINE fluid_real_t +fluid_lfo_get_val(fluid_lfo_t *lfo) +{ + return lfo->val; +} + +static FLUID_INLINE void +fluid_lfo_calc(fluid_lfo_t *lfo, unsigned int cur_delay) +{ + if(cur_delay < lfo->delay) + { + return; + } + + lfo->val += lfo->increment; + + if(lfo->val > (fluid_real_t) 1.0) + { + lfo->increment = -lfo->increment; + lfo->val = (fluid_real_t) 2.0 - lfo->val; + } + else if(lfo->val < (fluid_real_t) -1.0) + { + lfo->increment = -lfo->increment; + lfo->val = (fluid_real_t) -2.0 - lfo->val; + } + +} + +#endif diff --git a/libs/fluidsynth/src/rvoice/fluid_phase.h b/libs/fluidsynth/src/rvoice/fluid_phase.h new file mode 100644 index 00000000000..44df6b249fc --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_phase.h @@ -0,0 +1,113 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_PHASE_H +#define _FLUID_PHASE_H + +/* + * phase + */ + +#define FLUID_INTERP_BITS 8 +#define FLUID_INTERP_BITS_MASK 0xff000000 +#define FLUID_INTERP_BITS_SHIFT 24 + + +#define FLUID_FRACT_MAX ((double)4294967296.0) + +/* fluid_phase_t +* Purpose: +* Playing pointer for voice playback +* +* When a sample is played back at a different pitch, the playing pointer in the +* source sample will not advance exactly one sample per output sample. +* This playing pointer is implemented using fluid_phase_t. +* It is a 64 bit number. The higher 32 bits contain the 'index' (number of +* the current sample), the lower 32 bits the fractional part. +*/ +typedef uint64_t fluid_phase_t; + +/* Purpose: + * Set a to b. + * a: fluid_phase_t + * b: fluid_phase_t + */ +#define fluid_phase_set(a,b) a=b; + +#define fluid_phase_set_int(a, b) ((a) = ((uint64_t)(b)) << 32) + +/* Purpose: + * Sets the phase a to a phase increment given in b. + * For example, assume b is 0.9. After setting a to it, adding a to + * the playing pointer will advance it by 0.9 samples. */ +#define fluid_phase_set_float(a, b) \ + (a) = (((uint64_t)(b)) << 32) \ + | (uint32_t) (((double)(b) - (int)(b)) * (double)FLUID_FRACT_MAX) + +/* create a fluid_phase_t from an index and a fraction value */ +#define fluid_phase_from_index_fract(index, fract) \ + ((((uint64_t)(index)) << 32) + (fract)) + +/* Purpose: + * Return the index and the fractional part, respectively. */ +#define fluid_phase_index(_x) \ + ((unsigned int)((_x) >> 32)) +#define fluid_phase_fract(_x) \ + ((uint32_t)((_x) & 0xFFFFFFFF)) + +/* Get the phase index with fractional rounding */ +#define fluid_phase_index_round(_x) \ + ((unsigned int)(((_x) + 0x80000000) >> 32)) + + +/* Purpose: + * Takes the fractional part of the argument phase and + * calculates the corresponding position in the interpolation table. + * The fractional position of the playing pointer is calculated with a quite high + * resolution (32 bits). It would be unpractical to keep a set of interpolation + * coefficients for each possible fractional part... + */ +#define fluid_phase_fract_to_tablerow(_x) \ + ((unsigned int)(fluid_phase_fract(_x) & FLUID_INTERP_BITS_MASK) >> FLUID_INTERP_BITS_SHIFT) + +#define fluid_phase_double(_x) \ + ((double)(fluid_phase_index(_x)) + ((double)fluid_phase_fract(_x) / FLUID_FRACT_MAX)) + +/* Purpose: + * Advance a by a step of b (both are fluid_phase_t). + */ +#define fluid_phase_incr(a, b) a += b + +/* Purpose: + * Subtract b from a (both are fluid_phase_t). + */ +#define fluid_phase_decr(a, b) a -= b + +/* Purpose: + * Subtract b samples from a. + */ +#define fluid_phase_sub_int(a, b) ((a) -= (uint64_t)(b) << 32) + +/* Purpose: + * Creates the expression a.index++. */ +#define fluid_phase_index_plusplus(a) (((a) += 0x100000000LL) + +#endif /* _FLUID_PHASE_H */ diff --git a/libs/fluidsynth/src/rvoice/fluid_rev.c b/libs/fluidsynth/src/rvoice/fluid_rev.c new file mode 100644 index 00000000000..11bc760832a --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_rev.c @@ -0,0 +1,1523 @@ +/****************************************************************************** + * FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + * + * FDN REVERB + * + * Freeverb used by fluidsynth (v.1.1.10 and previous) is based on + * Schroeder-Moorer reverberator: + * https://ccrma.stanford.edu/~jos/pasp/Freeverb.html + * + * This FDN reverberation is based on jot FDN reverberator. + * https://ccrma.stanford.edu/~jos/Reverb/FDN_Late_Reverberation.html + * Like Freeverb it is a late reverb which is convenient for Fluidsynth. + * + * + * .-------------------. + * .-----------------| | + * | - | Feedback | + * | .--------------| Matrix | + * | | |___________________| + * | | /|\ /|\ + * \|/ | .---------. .-------. | - | .------. + * .->+ ---->| Delay 0 |-|L.P.F 0|--*-------->| |-> out + * .---------. | | |_________| |_______| | | | left + * |Tone | | | - - | |Stereo| + * In ->|corrector|--* | - - | | unit | + * mono |_________| | \|/ .---------. .-------. | | |-> out + * ---->+ ->| Delay 7 |-|L.P.F 7|--------*-->| | right + * |_________| |_______| |______| + * /|\ /|\ /|\ /|\ + * | | | | + * roomsize --/ | width --/ | + * damp ------/ level ------/ + * + * It takes a monophonic input and produces a stereo output. + * + * The parameters are the same than for Freeverb. + * Also the default response of these parameters are the same than for Freeverb: + * - roomsize (0 to 1): control the reverb time from 0.7 to 12.5 s. + * This reverberation time is ofen called T60DC. + * + * - damp (0 to 1): controls the reverb time frequency dependency. + * This controls the reverb time for the frequency sample rate/2 + * + * When 0, the reverb time for high frequencies is the same as + * for DC frequency. + * When > 0, high frequencies have less reverb time than lower frequencies. + * + * - width (0 to 100): controls the left/right output separation. + * When 0, there are no separation and the signal on left and right. + * output is the same. This sounds like a monophonic signal. + * When 100, the separation between left and right is maximum. + * + * - level (0 to 1), controls the output level reverberation. + * + * This FDN reverb produces a better quality reverberation tail than Freeverb with + * far less ringing by using modulated delay lines that help to cancel + * the building of a lot of resonances in the reverberation tail even when + * using only 8 delays lines (NBR_DELAYS = 8) (default). + * + * The frequency density (often called "modal density" is one property that + * contributes to sound quality. Although 8 lines give good result, using 12 delays + * lines brings the overall frequency density quality a bit higher. + * This quality augmentation is noticeable particularly when using long reverb time + * (roomsize = 1) on solo instrument with long release time. Of course the cpu load + * augmentation is +50% relatively to 8 lines. + * + * As a general rule the reverberation tail quality is easier to perceive by ear + * when using: + * - percussive instruments (i.e piano and others). + * - long reverb time (roomsize = 1). + * - no damping (damp = 0). + * - Using headphone. Avoid using loud speaker, you will be quickly misguided by the + * natural reverberation of the room in which you are. + * + * The cpu load for 8 lines is a bit lower than for freeverb (- 3%), + * but higher for 12 lines (+ 41%). + * + * + * The memory consumption is less than for freeverb + * (see the results table below). + * + * Two macros are usable at compiler time: + * - NBR_DELAYS: number of delay lines. 8 (default) or 12. + * - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response of + * roomsize parameter. + * When this macro is not defined (the default), roomsize has the same + * response that Freeverb, that is: + * - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s). + * + * When this macro is defined, roomsize behaves linearly: + * - roomsize (0 to 1) controls reverb time linearly (0.7 to 12.5 s). + * This linear response is convenient when using GUI controls. + * + * -------------------------------------------------------------------------- + * Compare table: + * Note: the cpu load in % are relative each to other. These values are + * given by the fluidsynth profile commands. + * -------------------------------------------------------------------------- + * reverb | NBR_DELAYS | Performances | memory size | quality + * | | (cpu_load: %) | (bytes)(see note) | + * ========================================================================== + * freeverb | 2 x 8 comb | 0.670 % | 204616 | ringing + * | 2 x 4 all-pass | | | + * ----------|--------------------------------------------------------------- + * FDN | 8 | 0.650 % | 112480 | far less + * modulated | |(feeverb - 3%) | (56% freeverb) | ringing + * |--------------------------------------------------------------- + * | 12 | 0.942 % | 168720 | best than + * | |(freeverb + 41%) | (82 %freeverb) | 8 lines + *--------------------------------------------------------------------------- + * + * Note: + * Values in this column is the memory consumption for sample rate <= 44100Hz. + * For sample rate > 44100Hz , multiply these values by (sample rate / 44100Hz). + * For example: for sample rate 96000Hz, the memory consumed is 244760 bytes + * + *---------------------------------------------------------------------------- + * 'Denormalise' method to avoid loss of performance. + * -------------------------------------------------- + * According to music-dsp thread 'Denormalise', Pentium processors + * have a hardware 'feature', that is of interest here, related to + * numeric underflow. We have a recursive filter. The output decays + * exponentially, if the input stops. So the numbers get smaller and + * smaller... At some point, they reach 'denormal' level. This will + * lead to drastic spikes in the CPU load. The effect was reproduced + * with the reverb - sometimes the average load over 10 s doubles!!. + * + * The 'undenormalise' macro fixes the problem: As soon as the number + * is close enough to denormal level, the macro forces the number to + * 0.0f. The original macro is: + * + * #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f + * + * This will zero out a number when it reaches the denormal level. + * Advantage: Maximum dynamic range Disadvantage: We'll have to check + * every sample, expensive. The alternative macro comes from a later + * mail from Jon Watte. It will zap a number before it reaches + * denormal level. Jon suggests to run it once per block instead of + * every sample. + */ + +/* Denormalising part II: + * + * Another method fixes the problem cheaper: Use a small DC-offset in + * the filter calculations. Now the signals converge not against 0, + * but against the offset. The constant offset is invisible from the + * outside world (i.e. it does not appear at the output. There is a + * very small turn-on transient response, which should not cause + * problems. + */ +#include "fluid_rev.h" +#include "fluid_sys.h" + +/*---------------------------------------------------------------------------- + Configuration macros at compiler time. + + 3 macros are usable at compiler time: + - NBR_DELAYs: number of delay lines. 8 (default) or 12. + - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response for + roomsize parameter. + - DENORMALISING enable denormalising handling. +-----------------------------------------------------------------------------*/ +//#define INFOS_PRINT /* allows message to be printed on the console. */ + +/* Number of delay lines (must be only 8 or 12) + 8 is the default. + 12 produces a better quality but is +50% cpu expensive. +*/ +#define NBR_DELAYS 8 /* default*/ + +/* response curve of parameter roomsize */ +/* + The default response is the same as Freeverb: + - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s). + + when ROOMSIZE_RESPONSE_LINEAR is defined, the response is: + - roomsize (0 to 1) controls reverb time linearly (0.7 to 12.5 s). +*/ +//#define ROOMSIZE_RESPONSE_LINEAR + +/* DENORMALISING enable denormalising handling */ +#define DENORMALISING + +#ifdef DENORMALISING +#define DC_OFFSET 1e-8f +#else +#define DC_OFFSET 0.0f +#endif + +/*---------------------------------------------------------------------------- + Initial internal reverb settings (at reverb creation time) +-----------------------------------------------------------------------------*/ +/* SCALE_WET_WIDTH is a compensation weight factor to get an output + amplitude (wet) rather independent of the width setting. + 0: the output amplitude is fully dependent on the width setting. + >0: the output amplitude is less dependent on the width setting. + With a SCALE_WET_WIDTH of 0.2 the output amplitude is rather + independent of width setting (see fluid_revmodel_update()). + */ +#define SCALE_WET_WIDTH 0.2f + +/* It is best to inject the input signal less ofen. This contributes to obtain +a flatter response on comb filter. So the input gain is set to 0.1 rather 1.0. */ +#define FIXED_GAIN 0.1f /* input gain */ + +/* SCALE_WET is adjusted to 5.0 to get internal output level equivalent to freeverb */ +#define SCALE_WET 5.0f /* scale output gain */ + +/*---------------------------------------------------------------------------- + Internal FDN late reverb settings +-----------------------------------------------------------------------------*/ + +/*-- Reverberation time settings ---------------------------------- + MIN_DC_REV_TIME est defined egal to the minimum value of freeverb: + MAX_DC_REV_TIME est defined egal to the maximum value of freeverb: + T60DC is computed from gi and the longest delay line in freeverb: L8 = 1617 + T60 = -3 * Li * T / log10(gi) + T60 = -3 * Li * / (log10(gi) * sr) + + - Li: length of comb filter delay line. + - sr: sample rate. + - gi: the feedback gain. + + The minimum value for freeverb correspond to gi = 0.7. + with Mi = 1617, sr at 44100 Hz, and gi = 0.7 => MIN_DC_REV_TIME = 0.7 s + + The maximum value for freeverb correspond to gi = 0.98. + with Mi = 1617, sr at 44100 Hz, and gi = 0.98 => MAX_DC_REV_TIME = 12.5 s +*/ + +#define MIN_DC_REV_TIME 0.7f /* minimum T60DC reverb time: seconds */ +#define MAX_DC_REV_TIME 12.5f /* maximumm T60DC time in seconds */ +#define RANGE_REV_TIME (MAX_DC_REV_TIME - MIN_DC_REV_TIME) + +/* macro to compute internal reverberation time versus roomsize parameter */ +#define GET_DC_REV_TIME(roomsize) (MIN_DC_REV_TIME + RANGE_REV_TIME * roomsize) + +/*-- Modulation related settings ----------------------------------*/ +/* For many instruments, the range for MOD_FREQ and MOD_DEPTH should be: + + MOD_DEPTH: [3..6] (in samples). + MOD_FREQ: [0.5 ..2.0] (in Hz). + + Values below the lower limits are often not sufficient to cancel unwanted + "ringing"(resonant frequency). + Values above upper limits augment the unwanted "chorus". + + With NBR_DELAYS to 8: + MOD_DEPTH must be >= 4 to cancel the unwanted "ringing".[4..6]. + With NBR_DELAYS to 12: + MOD_DEPTH to 3 is sufficient to cancel the unwanted "ringing".[3..6] +*/ +#define MOD_DEPTH 4 /* modulation depth (samples)*/ +#define MOD_RATE 50 /* modulation rate (samples)*/ +#define MOD_FREQ 1.0f /* modulation frequency (Hz) */ +/* + Number of samples to add to the desired length of a delay line. This + allow to take account of modulation interpolation. + 1 is sufficient with MOD_DEPTH equal to 4. +*/ +#define INTERP_SAMPLES_NBR 1 + +/* phase offset between modulators waveform */ +#define MOD_PHASE (360.0f/(float) NBR_DELAYS) + +#if (NBR_DELAYS == 8) + #define DELAY_L0 601 + #define DELAY_L1 691 + #define DELAY_L2 773 + #define DELAY_L3 839 + #define DELAY_L4 919 + #define DELAY_L5 997 + #define DELAY_L6 1061 + #define DELAY_L7 1129 +#elif (NBR_DELAYS == 12) + #define DELAY_L0 601 + #define DELAY_L1 691 + #define DELAY_L2 773 + #define DELAY_L3 839 + #define DELAY_L4 919 + #define DELAY_L5 997 + #define DELAY_L6 1061 + #define DELAY_L7 1093 + #define DELAY_L8 1129 + #define DELAY_L9 1151 + #define DELAY_L10 1171 + #define DELAY_L11 1187 +#endif + + +/*---------------------------------------------------------------------------*/ +/* The FDN late feed back matrix: A + T + A = P - 2 / N * u * u + N N N N + + N: the matrix dimension (i.e NBR_DELAYS). + P: permutation matrix. + u: is a column vector of 1. + +*/ +#define FDN_MATRIX_FACTOR (fluid_real_t)(-2.0 / NBR_DELAYS) + +/*---------------------------------------------------------------------------- + Internal FDN late structures and static functions +-----------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------------------------- + Delay absorbent low pass filter +-----------------------------------------------------------------------------*/ +typedef struct +{ + fluid_real_t buffer; + fluid_real_t b0, a1; /* filter coefficients */ +} fdn_delay_lpf; + +/*----------------------------------------------------------------------------- + Sets coefficients for delay absorbent low pass filter. + @param lpf pointer on low pass filter structure. + @param b0,a1 coefficients. +-----------------------------------------------------------------------------*/ +static void set_fdn_delay_lpf(fdn_delay_lpf *lpf, + fluid_real_t b0, fluid_real_t a1) +{ + lpf->b0 = b0; + lpf->a1 = a1; +} + +/*----------------------------------------------------------------------------- + Process delay absorbent low pass filter. + @param mod_delay modulated delay line. + @param in, input sample. + @param out output sample. +-----------------------------------------------------------------------------*/ +/* process low pass damping filter (input, output, delay) */ +#define process_damping_filter(in,out,mod_delay) \ +{\ + out = in * mod_delay->dl.damping.b0 - mod_delay->dl.damping.buffer * \ + mod_delay->dl.damping.a1;\ + mod_delay->dl.damping.buffer = out;\ +}\ + + +/*----------------------------------------------------------------------------- + Delay line : + The delay line is composed of the line plus an absorbent low pass filter + to get frequency dependent reverb time. +-----------------------------------------------------------------------------*/ +typedef struct +{ + fluid_real_t *line; /* buffer line */ + int size; /* effective internal size (in samples) */ + /*-------------*/ + int line_in; /* line in position */ + int line_out; /* line out position */ + /*-------------*/ + fdn_delay_lpf damping; /* damping low pass filter */ +} delay_line; + + +/*----------------------------------------------------------------------------- + Clears a delay line to DC_OFFSET float value. + @param dl pointer on delay line structure +-----------------------------------------------------------------------------*/ +static void clear_delay_line(delay_line *dl) +{ + int i; + + for(i = 0; i < dl->size; i++) + { + dl->line[i] = DC_OFFSET; + } +} + +/*----------------------------------------------------------------------------- + Push a sample val into the delay line +-----------------------------------------------------------------------------*/ +#define push_in_delay_line(dl, val) \ +{\ + dl->line[dl->line_in] = val;\ + /* Incrementation and circular motion if necessary */\ + if(++dl->line_in >= dl->size) dl->line_in -= dl->size;\ +}\ + +/*----------------------------------------------------------------------------- + Modulator for modulated delay line +-----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + Sinusoidal modulator +-----------------------------------------------------------------------------*/ +/* modulator are integrated in modulated delay line */ +typedef struct +{ + fluid_real_t a1; /* Coefficient: a1 = 2 * cos(w) */ + fluid_real_t buffer1; /* buffer1 */ + fluid_real_t buffer2; /* buffer2 */ + fluid_real_t reset_buffer2;/* reset value of buffer2 */ +} sinus_modulator; + +/*----------------------------------------------------------------------------- + Sets the frequency of sinus oscillator. + + @param mod pointer on modulator structure. + @param freq frequency of the oscillator in Hz. + @param sample_rate sample rate on audio output in Hz. + @param phase initial phase of the oscillator in degree (0 to 360). +-----------------------------------------------------------------------------*/ +static void set_mod_frequency(sinus_modulator *mod, + float freq, float sample_rate, float phase) +{ + fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* initial angle */ + fluid_real_t a; + + mod->a1 = 2 * FLUID_COS(w); + + a = (2 * FLUID_M_PI / 360) * phase; + + mod->buffer2 = FLUID_SIN(a - w); /* y(n-1) = sin(-initial angle) */ + mod->buffer1 = FLUID_SIN(a); /* y(n) = sin(initial phase) */ + mod->reset_buffer2 = FLUID_SIN(FLUID_M_PI / 2 - w); /* reset value for PI/2 */ +} + +/*----------------------------------------------------------------------------- + Gets current value of sinus modulator: + y(n) = a1 . y(n-1) - y(n-2) + out = a1 . buffer1 - buffer2 + + @param pointer on modulator structure. + @return current value of the modulator sine wave. +-----------------------------------------------------------------------------*/ +static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod) +{ + fluid_real_t out; + out = mod->a1 * mod->buffer1 - mod->buffer2; + mod->buffer2 = mod->buffer1; + + if(out >= 1.0f) /* reset in case of instability near PI/2 */ + { + out = 1.0f; /* forces output to the right value */ + mod->buffer2 = mod->reset_buffer2; + } + + if(out <= -1.0f) /* reset in case of instability near -PI/2 */ + { + out = -1.0f; /* forces output to the right value */ + mod->buffer2 = - mod->reset_buffer2; + } + + mod->buffer1 = out; + return out; +} + +/*----------------------------------------------------------------------------- + Modulated delay line. The line is composed of: + - the delay line with its damping low pass filter. + - the sinusoidal modulator. + - center output position modulated by the modulator. + - variable rate control of center output position. + - first order All-Pass interpolator. +-----------------------------------------------------------------------------*/ +typedef struct +{ + /* delay line with damping low pass filter member */ + delay_line dl; /* delayed line */ + /*---------------------------*/ + /* Sinusoidal modulator member */ + sinus_modulator mod; /* sinus modulator */ + /*-------------------------*/ + /* center output position members */ + fluid_real_t center_pos_mod; /* center output position modulated by modulator */ + int mod_depth; /* modulation depth (in samples) */ + /*-------------------------*/ + /* variable rate control of center output position */ + int index_rate; /* index rate to know when to update center_pos_mod */ + int mod_rate; /* rate at which center_pos_mod is updated */ + /*-------------------------*/ + /* first order All-Pass interpolator members */ + fluid_real_t frac_pos_mod; /* fractional position part between samples) */ + /* previous value used when interpolating using fractional */ + fluid_real_t buffer; +} mod_delay_line; + +/*----------------------------------------------------------------------------- + Return norminal delay length + + @param mdl, pointer on modulated delay line. +-----------------------------------------------------------------------------*/ +static int get_mod_delay_line_length(mod_delay_line *mdl) +{ + return (mdl->dl.size - mdl->mod_depth - INTERP_SAMPLES_NBR); +} + +/*----------------------------------------------------------------------------- + Reads the sample value out of the modulated delay line. + @param mdl, pointer on modulated delay line. + @return the sample value. +-----------------------------------------------------------------------------*/ +static FLUID_INLINE fluid_real_t get_mod_delay(mod_delay_line *mdl) +{ + fluid_real_t out_index; /* new modulated index position */ + int int_out_index; /* integer part of out_index */ + fluid_real_t out; /* value to return */ + + /* Checks if the modulator must be updated (every mod_rate samples). */ + /* Important: center_pos_mod must be used immediately for the + first sample. So, mdl->index_rate must be initialized + to mdl->mod_rate (set_mod_delay_line()) */ + + if(++mdl->index_rate >= mdl->mod_rate) + { + mdl->index_rate = 0; + + /* out_index = center position (center_pos_mod) + sinus waweform */ + out_index = mdl->center_pos_mod + + get_mod_sinus(&mdl->mod) * mdl->mod_depth; + + /* extracts integer part in int_out_index */ + if(out_index >= 0.0f) + { + int_out_index = (int)out_index; /* current integer part */ + + /* forces read index (line_out) with integer modulation value */ + /* Boundary check and circular motion as needed */ + if((mdl->dl.line_out = int_out_index) >= mdl->dl.size) + { + mdl->dl.line_out -= mdl->dl.size; + } + } + else /* negative */ + { + int_out_index = (int)(out_index - 1); /* previous integer part */ + /* forces read index (line_out) with integer modulation value */ + /* circular motion as needed */ + mdl->dl.line_out = int_out_index + mdl->dl.size; + } + + /* extracts fractionnal part. (it will be used when interpolating + between line_out and line_out +1) and memorize it. + Memorizing is necessary for modulation rate above 1 */ + mdl->frac_pos_mod = out_index - int_out_index; + + /* updates center position (center_pos_mod) to the next position + specified by modulation rate */ + if((mdl->center_pos_mod += mdl->mod_rate) >= mdl->dl.size) + { + mdl->center_pos_mod -= mdl->dl.size; + } + } + + /* First order all-pass interpolation ----------------------------------*/ + /* https://ccrma.stanford.edu/~jos/pasp/First_Order_Allpass_Interpolation.html */ + /* begins interpolation: read current sample */ + out = mdl->dl.line[mdl->dl.line_out]; + + /* updates line_out to the next sample. + Boundary check and circular motion as needed */ + if(++mdl->dl.line_out >= mdl->dl.size) + { + mdl->dl.line_out -= mdl->dl.size; + } + + /* Fractional interpolation between next sample (at next position) and + previous output added to current sample. + */ + out += mdl->frac_pos_mod * (mdl->dl.line[mdl->dl.line_out] - mdl->buffer); + mdl->buffer = out; /* memorizes current output */ + return out; +} + +/*----------------------------------------------------------------------------- + Late structure +-----------------------------------------------------------------------------*/ +struct _fluid_late +{ + fluid_real_t samplerate; /* sample rate */ + fluid_real_t sample_rate_max; /* sample rate maximum */ + /*----- High pass tone corrector -------------------------------------*/ + fluid_real_t tone_buffer; + fluid_real_t b1, b2; + /*----- Modulated delay lines lines ----------------------------------*/ + mod_delay_line mod_delay_lines[NBR_DELAYS]; + /*-----------------------------------------------------------------------*/ + /* Output coefficients for separate Left and right stereo outputs */ + fluid_real_t out_left_gain[NBR_DELAYS]; /* Left delay lines' output gains */ + fluid_real_t out_right_gain[NBR_DELAYS];/* Right delay lines' output gains*/ +}; + +typedef struct _fluid_late fluid_late; +/*----------------------------------------------------------------------------- + fluidsynth reverb structure +-----------------------------------------------------------------------------*/ +struct _fluid_revmodel_t +{ + /* reverb parameters */ + fluid_real_t roomsize; /* acting on reverb time */ + fluid_real_t damp; /* acting on frequency dependent reverb time */ + fluid_real_t level, wet1, wet2; /* output level */ + fluid_real_t width; /* width stereo separation */ + + /* fdn reverberation structure */ + fluid_late late; +}; + +/*----------------------------------------------------------------------------- + Updates Reverb time and absorbent filters coefficients from parameters: + + @param late pointer on late structure. + @param roomsize (0 to 1): acting on reverb time. + @param damping (0 to 1): acting on absorbent damping filter. + + Design formulas: + https://ccrma.stanford.edu/~jos/Reverb/First_Order_Delay_Filter_Design.html + https://ccrma.stanford.edu/~jos/Reverb/Tonal_Correction_Filter.html +-----------------------------------------------------------------------------*/ +static void update_rev_time_damping(fluid_late *late, + fluid_real_t roomsize, fluid_real_t damp) +{ + int i; + fluid_real_t sample_period = 1 / late->samplerate; /* Sampling period */ + int delay_length; /* delay length */ + fluid_real_t dc_rev_time; /* Reverb time at 0 Hz (in seconds) */ + + fluid_real_t alpha, alpha2; + + /*-------------------------------------------- + Computes dc_rev_time and alpha + ----------------------------------------------*/ + { + fluid_real_t gi_tmp, ai_tmp; +#ifdef ROOMSIZE_RESPONSE_LINEAR + /* roomsize parameter behave linearly: + * - roomsize (0 to 1) controls reverb time linearly (0.7 to 10 s). + * This linear response is convenient when using GUI controls. + */ + /*----------------------------------------- + Computes dc_rev_time + ------------------------------------------*/ + dc_rev_time = GET_DC_REV_TIME(roomsize); + delay_length = get_mod_delay_line_length(&late->mod_delay_lines[NBR_DELAYS - 1]); + /* computes gi_tmp from dc_rev_time using relation E2 */ + gi_tmp = FLUID_POW(10, -3 * delay_length * + sample_period / dc_rev_time); /* E2 */ +#else + /* roomsize parameters have the same response that Freeverb, that is: + * - roomsize (0 to 1) controls concave reverb time (0.7 to 10 s). + */ + { + /*----------------------------------------- + Computes dc_rev_time + ------------------------------------------*/ + fluid_real_t gi_min, gi_max; + + /* values gi_min et gi_max are computed using E2 for the line with + maximum delay */ + delay_length = get_mod_delay_line_length(&late->mod_delay_lines[NBR_DELAYS - 1]); + gi_max = FLUID_POW(10, (-3 * delay_length / MAX_DC_REV_TIME) * + sample_period); /* E2 */ + gi_min = FLUID_POW(10, (-3 * delay_length / MIN_DC_REV_TIME) * + sample_period); /* E2 */ + /* gi = f(roomsize, gi_max, gi_min) */ + gi_tmp = gi_min + roomsize * (gi_max - gi_min); + /* Computes T60DC from gi using inverse of relation E2.*/ + dc_rev_time = -3 * FLUID_M_LN10 * delay_length * sample_period / FLUID_LOGF(gi_tmp); + } +#endif /* ROOMSIZE_RESPONSE_LINEAR */ + /*-------------------------------------------- + Computes alpha + ----------------------------------------------*/ + /* Computes alpha from damp,ai_tmp,gi_tmp using relation R */ + /* - damp (0 to 1) controls concave reverb time for fs/2 frequency (T60DC to 0) */ + ai_tmp = 1.0f * damp; + + /* Preserve the square of R */ + alpha2 = 1.f / (1.f - ai_tmp / ((20.f / 80.f) * FLUID_LOGF(gi_tmp))); + + alpha = FLUID_SQRT(alpha2); /* R */ + } + + /* updates tone corrector coefficients b1,b2 from alpha */ + { + /* + Beta = (1 - alpha) / (1 + alpha) + b1 = 1/(1-beta) + b2 = beta * b1 + */ + fluid_real_t beta = (1 - alpha) / (1 + alpha); + late->b1 = 1 / (1 - beta); + late->b2 = beta * late->b1; + late->tone_buffer = 0.0f; + } + + /* updates damping coefficients of all lines (gi , ai) from dc_rev_time, alpha */ + for(i = 0; i < NBR_DELAYS; i++) + { + fluid_real_t gi, ai; + + /* delay length */ + delay_length = get_mod_delay_line_length(&late->mod_delay_lines[i]); + + /* iir low pass filter gain */ + gi = FLUID_POW(10, -3 * delay_length * sample_period / dc_rev_time); + + /* iir low pass filter feedback gain */ + ai = (20.f / 80.f) * FLUID_LOGF(gi) * (1.f - 1.f / alpha2); + + /* b0 = gi * (1 - ai), a1 = - ai */ + set_fdn_delay_lpf(&late->mod_delay_lines[i].dl.damping, + gi * (1.f - ai), -ai); + } +} + +/*----------------------------------------------------------------------------- + Updates stereo coefficients + @param late pointer on late structure + @param wet level integrated in stereo coefficients. +-----------------------------------------------------------------------------*/ +static void update_stereo_coefficient(fluid_late *late, fluid_real_t wet1) +{ + int i; + fluid_real_t wet; + + for(i = 0; i < NBR_DELAYS; i++) + { + /* delay lines output gains vectors Left and Right + + L R + 0 | 1 1| + 1 |-1 1| + 2 | 1 -1| + 3 |-1 -1| + + 4 | 1 1| + 5 |-1 1| + stereo gain = 6 | 1 -1| + 7 |-1 -1| + + 8 | 1 1| + 9 |-1 1| + 10| 1 -1| + 11|-1 -1| + */ + + /* for left line: 00, ,02, ,04, ,06, ,08, ,10, ,12,... left_gain = +1 */ + /* for left line: ,01, ,03, ,05, ,07, ,09, ,11,... left_gain = -1 */ + wet = wet1; + if(i & 1) + { + wet = -wet1; + } + late->out_left_gain[i] = wet; + + /* for right line: 00,01, ,04,05, ,08,09, ,12,13 right_gain = +1 */ + /* for right line: ,02 ,03, ,06,07, ,10,11,... right_gain = -1 */ + wet = wet1; + if(i & 2) + { + wet = -wet1; + } + late->out_right_gain[i] = wet; + } +} + +/*----------------------------------------------------------------------------- + fluid_late destructor. + @param late pointer on late structure. +-----------------------------------------------------------------------------*/ +static void delete_fluid_rev_late(fluid_late *late) +{ + int i; + fluid_return_if_fail(late != NULL); + + /* free the delay lines */ + for(i = 0; i < NBR_DELAYS; i++) + { + FLUID_FREE(late->mod_delay_lines[i].dl.line); + } +} + + +/* Nominal delay lines length table (in samples) */ +static const int nom_delay_length[NBR_DELAYS] = +{ + DELAY_L0, DELAY_L1, DELAY_L2, DELAY_L3, + DELAY_L4, DELAY_L5, DELAY_L6, DELAY_L7, +#if (NBR_DELAYS == 12) + DELAY_L8, DELAY_L9, DELAY_L10, DELAY_L11 +#endif +}; + +/* + 1)"modal density" is one property that contributes to the quality of the reverb tail. + The more is the modal density, the less are unwanted resonant frequencies + build during the decay time: modal density = total delay / sample rate. + + Delay line's length given by static table delay_length[] are nominal + to get minimum modal density of 0.15 at sample rate 44100Hz. + Here we set length_factor to 2 to multiply this nominal modal + density by 2. This leads to a default modal density of 0.15 * 2 = 0.3 for + sample rate <= 44100. + + For sample rate > 44100, length_factor is multiplied by + sample_rate / 44100. This ensures that the default modal density keeps inchanged. + (Without this compensation, the default modal density would be diminished for + new sample rate change above 44100Hz). + + 2)Modulated delay line contributes to diminish resonnant frequencies (often called "ringing"). + Modulation depth (mod_depth) is set to nominal value of MOD_DEPTH at sample rate 44100Hz. + For sample rate > 44100, mod_depth is multiplied by sample_rate / 44100. This ensures + that the effect of modulated delay line remains inchanged. +*/ +static void compensate_from_sample_rate(fluid_real_t sample_rate, + fluid_real_t *mod_depth, + fluid_real_t *length_factor) +{ + *mod_depth = MOD_DEPTH; + *length_factor = 2.0f; + if(sample_rate > 44100.0f) + { + fluid_real_t sample_rate_factor = sample_rate/44100.0f; + *length_factor *= sample_rate_factor; + *mod_depth *= sample_rate_factor; + } +} + +/*----------------------------------------------------------------------------- + Creates all modulated lines. + @param late, pointer on the fnd late reverb to initialize. + @param sample_rate_max, the maximum audio sample rate expected. + @return FLUID_OK if success, FLUID_FAILED otherwise. +-----------------------------------------------------------------------------*/ +static int create_mod_delay_lines(fluid_late *late, + fluid_real_t sample_rate_max) +{ + int i; + + fluid_real_t mod_depth, length_factor; + + /* compute mod_depth, length factor */ + compensate_from_sample_rate(sample_rate_max, &mod_depth, &length_factor); + + late->sample_rate_max = sample_rate_max; + +#ifdef INFOS_PRINT // allows message to be printed on the console. + printf("length_factor:%f, mod_depth:%f\n", length_factor, mod_depth); + /* Print: modal density and total memory bytes */ + { + int i; + int total_delay = 0; /* total delay in samples */ + for (i = 0; i < NBR_DELAYS; i++) + { + int length = (length_factor * nom_delay_length[i]) + + mod_depth + INTERP_SAMPLES_NBR; + total_delay += length; + } + + /* modal density and total memory bytes */ + printf("modal density:%f, total delay:%d, total memory:%d bytes\n", + total_delay / sample_rate_max ,total_delay , + total_delay * sizeof(fluid_real_t)); + } +#endif + + for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */ + { + int delay_length = nom_delay_length[i] * length_factor; + mod_delay_line *mdl = &late->mod_delay_lines[i]; + + /*-------------------------------------------------------------------*/ + /* checks parameter */ + if(delay_length < 1) + { + return FLUID_FAILED; + } + + /* limits mod_depth to the requested delay length */ + if(mod_depth >= delay_length) + { + FLUID_LOG(FLUID_INFO, + "fdn reverb: modulation depth has been limited"); + mod_depth = delay_length - 1; + } + + /*--------------------------------------------------------------------- + allocates delay lines + */ + + /* real size of the line in use (in samples): + size = INTERP_SAMPLES_NBR + mod_depth + delay_length */ + mdl->dl.size = delay_length + mod_depth + INTERP_SAMPLES_NBR; + mdl->dl.line = FLUID_ARRAY(fluid_real_t, mdl->dl.size); + + if(! mdl->dl.line) + { + return FLUID_FAILED; + } + } + return FLUID_OK; +} + +/*----------------------------------------------------------------------------- + Initialize all modulated lines. + @param late, pointer on the fnd late reverb to initialize. + @param sample_rate, the audio sample rate. + @return FLUID_OK if success, FLUID_FAILED otherwise. +-----------------------------------------------------------------------------*/ +static void initialize_mod_delay_lines(fluid_late *late, fluid_real_t sample_rate) +{ + int i; + fluid_real_t mod_depth, length_factor; + + /* update delay line parameter dependent of sample rate */ + late->samplerate = sample_rate; + + /* compute mod_depth, length factor */ + compensate_from_sample_rate(sample_rate, &mod_depth, &length_factor); + + for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */ + { + mod_delay_line *mdl = &late->mod_delay_lines[i]; + int delay_length = nom_delay_length[i] * length_factor; + + /* limits mod_depth to the requested delay length */ + if(mod_depth >= delay_length) + { + mod_depth = delay_length - 1; + } + + mdl->mod_depth = mod_depth; + + clear_delay_line(&mdl->dl); /* clears the buffer */ + + /* Initializes line_in to the start of the buffer */ + mdl->dl.line_in = 0; + + /* Initializes line_out index INTERP_SAMPLES_NBR samples after + line_in so that the delay between line_out and line_in is: + mod_depth + delay_length + */ + mdl->dl.line_out = mdl->dl.line_in + INTERP_SAMPLES_NBR; + + /* Damping low pass filter ------------------------------------------*/ + mdl->dl.damping.buffer = 0; + + /*--------------------------------------------------------------------- + Initializes modulation members: + - modulated center position: center_pos_mod + - modulation rate (the speed at which center_pos_mod is modulated: mod_rate + - index rate to know when to update center_pos_mod:index_rate + - interpolator member: buffer, frac_pos_mod + ---------------------------------------------------------------------*/ + /* Initializes the modulated center position (center_pos_mod) so that: + - the delay between line_out and center_pos_mod is mod_depth. + - the delay between center_pos_mod and line_in is delay_length. + */ + mdl->center_pos_mod = (fluid_real_t) INTERP_SAMPLES_NBR + mod_depth; + + /* Sets the modulation rate. This rate defines how often + the center position (center_pos_mod ) is modulated . + The value is expressed in samples. The default value is 1 that means that + center_pos_mod is updated at every sample. + For example with a value of 2, the center position position will be + updated only one time every 2 samples only. + */ + if(MOD_RATE < 1 || MOD_RATE > mdl->dl.size) + { + FLUID_LOG(FLUID_INFO, "fdn reverb: modulation rate is out of range"); + mdl->mod_rate = 1; /* default modulation rate: every one sample */ + } + else + { + mdl->mod_rate = MOD_RATE; + } + + /* index rate to control when to update center_pos_mod. + Important: must be set to get center_pos_mod immediately used for + the reading of first sample (see get_mod_delay()) + */ + mdl->index_rate = mdl->mod_rate; + + /* initializes first order All-Pass interpolator members */ + mdl->buffer = 0; /* previous delay sample value */ + mdl->frac_pos_mod = 0; /* frac. position (between consecutives sample) */ + + + /* Sets local Modulators parameters: frequency and phase. + Each modulateur are shifted of MOD_PHASE degree + */ + set_mod_frequency(&mdl->mod, + MOD_FREQ * MOD_RATE, + sample_rate, + (float)(MOD_PHASE * i)); + } +} + +/* + Clears the delay lines. + + @param rev pointer on the reverb. +*/ +static void +fluid_revmodel_init(fluid_revmodel_t *rev) +{ + int i; + + /* clears all the delay lines */ + for(i = 0; i < NBR_DELAYS; i ++) + { + clear_delay_line(&rev->late.mod_delay_lines[i].dl); + } +} + + +/* + updates internal parameters. + + @param rev pointer on the reverb. +*/ +static void +fluid_revmodel_update(fluid_revmodel_t *rev) +{ + /* Recalculate internal values after parameters change */ + + /* The stereo amplitude equation (wet1 and wet2 below) have a + tendency to produce high amplitude with high width values ( 1 < width < 100). + This results in an unwanted noisy output clipped by the audio card. + To avoid this dependency, we divide by (1 + rev->width * SCALE_WET_WIDTH) + Actually, with a SCALE_WET_WIDTH of 0.2, (regardless of level setting), + the output amplitude (wet) seems rather independent of width setting */ + fluid_real_t wet = (rev->level * SCALE_WET) / + (1.0f + rev->width * SCALE_WET_WIDTH); + + /* wet1 and wet2 are used by the stereo effect controlled by the width setting + for producing a stereo ouptput from a monophonic reverb signal. + Please see the note above about a side effect tendency */ + + rev->wet1 = wet * (rev->width / 2.0f + 0.5f); + rev->wet2 = wet * ((1.0f - rev->width) / 2.0f); + + /* integrates wet1 in stereo coefficient (this will save one multiply) */ + update_stereo_coefficient(&rev->late, rev->wet1); + + if(rev->wet1 > 0.0f) + { + rev->wet2 /= rev->wet1; + } + + /* Reverberation time and damping */ + update_rev_time_damping(&rev->late, rev->roomsize, rev->damp); +} + +/*---------------------------------------------------------------------------- + Reverb API +-----------------------------------------------------------------------------*/ +/* +* Creates a reverb. Once created the reverb have no parameters set, so +* fluid_revmodel_set() must be called at least one time after calling +* new_fluid_revmodel(). +* +* @param sample_rate_max maximum sample rate expected in Hz. +* +* @param sample_rate actual sample rate needed in Hz. +* @return pointer on the new reverb or NULL if memory error. +* Reverb API. +*/ +fluid_revmodel_t * +new_fluid_revmodel(fluid_real_t sample_rate_max, fluid_real_t sample_rate) +{ + fluid_revmodel_t *rev; + + if(sample_rate <= 0) + { + return NULL; + } + + rev = FLUID_NEW(fluid_revmodel_t); + + if(rev == NULL) + { + return NULL; + } + + FLUID_MEMSET(&rev->late, 0, sizeof(fluid_late)); + + /*-------------------------------------------------------------------------- + Create fdn late reverb. + */ + + /* update minimum value for sample_rate_max */ + if(sample_rate > sample_rate_max) + { + sample_rate_max = sample_rate; + } + + /*-------------------------------------------------------------------------- + Allocate the modulated delay lines + */ + if(create_mod_delay_lines(&rev->late, sample_rate_max) == FLUID_FAILED) + { + delete_fluid_revmodel(rev); + return NULL; + } + + /*-------------------------------------------------------------------------- + Initialize the fdn reverb + */ + /* Initialize all modulated lines. */ + initialize_mod_delay_lines(&rev->late, sample_rate); + + return rev; +} + +/* +* free the reverb. +* Note that while the reverb is used by calling any fluid_revmodel_processXXX() +* function, calling delete_fluid_revmodel() isn't multi task safe because +* delay line are freed. To deal properly with this issue follow the steps: +* +* 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX(). +* reverb functions. +* 2) Delete the reverb by calling delete_fluid_revmodel(). +* +* @param rev pointer on reverb to free. +* Reverb API. +*/ +void +delete_fluid_revmodel(fluid_revmodel_t *rev) +{ + fluid_return_if_fail(rev != NULL); + delete_fluid_rev_late(&rev->late); + FLUID_FREE(rev); +} + +/* +* Sets one or more reverb parameters. Note this must be called at least one +* time after calling new_fluid_revmodel() and before any call to +* fluid_revmodel_processXXX() and fluid_revmodel_samplerate_change(). +* +* Note that while the reverb is used by calling any fluid_revmodel_processXXX() +* function, calling fluid_revmodel_set() could produce audible clics. +* If this is a problem, optionally call fluid_revmodel_reset() before calling +* fluid_revmodel_set(). +* +* @param rev Reverb instance. +* @param set One or more flags from #fluid_revmodel_set_t indicating what +* parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters). +* @param roomsize Reverb room size. +* @param damping Reverb damping. +* @param width Reverb width. +* @param level Reverb level. +* +* Reverb API. +*/ +void +fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize, + fluid_real_t damping, fluid_real_t width, fluid_real_t level) +{ + fluid_return_if_fail(rev != NULL); + + /*-----------------------------------*/ + if(set & FLUID_REVMODEL_SET_ROOMSIZE) + { + fluid_clip(roomsize, 0.0f, 1.0f); + rev->roomsize = roomsize; + } + + /*-----------------------------------*/ + if(set & FLUID_REVMODEL_SET_DAMPING) + { + fluid_clip(damping, 0.0f, 1.0f); + rev->damp = damping; + } + + /*-----------------------------------*/ + if(set & FLUID_REVMODEL_SET_WIDTH) + { + rev->width = width; + } + + /*-----------------------------------*/ + if(set & FLUID_REVMODEL_SET_LEVEL) + { + fluid_clip(level, 0.0f, 1.0f); + rev->level = level; + } + + /* updates internal parameters */ + fluid_revmodel_update(rev); +} + +/* +* Applies a sample rate change on the reverb. +* fluid_revmodel_set() must be called at least one time before calling +* this function. +* +* Note that while the reverb is used by calling any fluid_revmodel_processXXX() +* function, calling fluid_revmodel_samplerate_change() isn't multi task safe. +* To deal properly with this issue follow the steps: +* 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX(). +* reverb functions. +* Optionally, call fluid_revmodel_reset() to damp the reverb. +* 2) Change sample rate by calling fluid_revmodel_samplerate_change(). +* 3) Restart reverb processing (i.e enabling calling of any fluid_revmodel_processXXX() +* reverb functions. +* +* Another solution is to substitute step (2): +* 2.1) delete the reverb by calling delete_fluid_revmodel(). +* 2.2) create the reverb by calling new_fluid_revmodel(). +* +* The best solution would be that this function be called only by the same task +* calling fluid_revmodel_processXXX(). +* +* @param rev the reverb. +* @param sample_rate new sample rate value. Must be <= sample_rate_max +* @return FLUID_OK if success, FLUID_FAILED if new sample rate is greater +* then the maximumum sample rate set at creation time. The reverb will +* continue to work but with possible lost of quality. +* If this is a problem, the caller should follow steps 2.1 and 2.2. +* Reverb API. +*/ +int +fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate) +{ + int status = FLUID_OK; + + fluid_return_val_if_fail(rev != NULL, FLUID_FAILED); + + if(sample_rate > rev->late.sample_rate_max) + { + FLUID_LOG(FLUID_WARN, + "fdn reverb: sample rate %.0f Hz is deduced to %.0f Hz\n", + sample_rate, rev->late.sample_rate_max); + + /* Reduce sample rate to the maximum value set at creation time. + The reverb will continue to work with possible lost of quality. + */ + sample_rate = rev->late.sample_rate_max; + status = FLUID_FAILED; + } + + /* Initialize all modulated lines according to sample rate change. */ + initialize_mod_delay_lines(&rev->late, sample_rate); + + /* updates damping filter coefficients according to sample rate change */ + update_rev_time_damping(&rev->late, rev->roomsize, rev->damp); + + return status; +} + +/* +* Damps the reverb by clearing the delay lines. +* @param rev the reverb. +* +* Reverb API. +*/ +void +fluid_revmodel_reset(fluid_revmodel_t *rev) +{ + fluid_return_if_fail(rev != NULL); + + fluid_revmodel_init(rev); +} + +/*----------------------------------------------------------------------------- +* fdn reverb process replace. +* @param rev pointer on reverb. +* @param in monophonic buffer input (FLUID_BUFSIZE sample). +* @param left_out stereo left processed output (FLUID_BUFSIZE sample). +* @param right_out stereo right processed output (FLUID_BUFSIZE sample). +* +* The processed reverb is replacing anything there in out. +* Reverb API. +-----------------------------------------------------------------------------*/ +void +fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int i, k; + + fluid_real_t xn; /* mono input x(n) */ + fluid_real_t out_tone_filter; /* tone corrector output */ + fluid_real_t out_left, out_right; /* output stereo Left and Right */ + fluid_real_t matrix_factor; /* partial matrix computation */ + fluid_real_t delay_out_s; /* sample */ + fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */ + + for(k = 0; k < FLUID_BUFSIZE; k++) + { + /* stereo output */ + out_left = out_right = 0; + +#ifdef DENORMALISING + /* Input is adjusted by DC_OFFSET. */ + xn = (in[k]) * FIXED_GAIN + DC_OFFSET; +#else + xn = (in[k]) * FIXED_GAIN; +#endif + + /*-------------------------------------------------------------------- + tone correction. + */ + out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer; + rev->late.tone_buffer = xn; + xn = out_tone_filter; + /*-------------------------------------------------------------------- + process feedback delayed network: + - xn is the input signal. + - before inserting in the line input we first we get the delay lines + output, filter them and compute output in delay_out[]. + - also matrix_factor is computed (to simplify further matrix product) + ---------------------------------------------------------------------*/ + /* We begin with the modulated output delay line + damping filter */ + matrix_factor = 0; + + for(i = 0; i < NBR_DELAYS; i++) + { + mod_delay_line *mdl = &rev->late.mod_delay_lines[i]; + /* get current modulated output */ + delay_out_s = get_mod_delay(mdl); + + /* process low pass damping filter + (input:delay_out_s, output:delay_out_s) */ + process_damping_filter(delay_out_s, delay_out_s, mdl); + + /* Result in delay_out[], and matrix_factor. + These will be of use later during input line process */ + delay_out[i] = delay_out_s; /* result in delay_out[] */ + matrix_factor += delay_out_s; /* result in matrix_factor */ + + /* Process stereo output */ + /* stereo left = left + out_left_gain * delay_out */ + out_left += rev->late.out_left_gain[i] * delay_out_s; + /* stereo right= right+ out_right_gain * delay_out */ + out_right += rev->late.out_right_gain[i] * delay_out_s; + } + + /* now we process the input delay line.Each input is a combination of + - xn: input signal + - delay_out[] the output of a delay line given by a permutation matrix P + - and matrix_factor. + This computes: in_delay_line = xn + (delay_out[] * matrix A) with + an algorithm equivalent but faster than using a product with matrix A. + */ + /* matrix_factor = output sum * (-2.0)/N */ + matrix_factor *= FDN_MATRIX_FACTOR; + matrix_factor += xn; /* adds reverb input signal */ + + for(i = 1; i < NBR_DELAYS; i++) + { + /* delay_in[i-1] = delay_out[i] + matrix_factor */ + delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl; + push_in_delay_line(dl, delay_out[i] + matrix_factor); + } + + /* last line input (NB_DELAY-1) */ + /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */ + { + delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl; + push_in_delay_line(dl, delay_out[0] + matrix_factor); + } + + /*-------------------------------------------------------------------*/ +#ifdef DENORMALISING + /* Removes the DC offset */ + out_left -= DC_OFFSET; + out_right -= DC_OFFSET; +#endif + + /* Calculates stereo output REPLACING anything already there: */ + /* + left_out[k] = out_left * rev->wet1 + out_right * rev->wet2; + right_out[k] = out_right * rev->wet1 + out_left * rev->wet2; + + As wet1 is integrated in stereo coefficient wet 1 is now + integrated in out_left and out_right, so we simplify previous + relation by suppression of one multiply as this: + + left_out[k] = out_left + out_right * rev->wet2; + right_out[k] = out_right + out_left * rev->wet2; + */ + left_out[k] = out_left + out_right * rev->wet2; + right_out[k] = out_right + out_left * rev->wet2; + } +} + + +/*----------------------------------------------------------------------------- +* fdn reverb process mix. +* @param rev pointer on reverb. +* @param in monophonic buffer input (FLUID_BUFSIZE samples). +* @param left_out stereo left processed output (FLUID_BUFSIZE samples). +* @param right_out stereo right processed output (FLUID_BUFSIZE samples). +* +* The processed reverb is mixed in out with samples already there in out. +* Reverb API. +-----------------------------------------------------------------------------*/ +void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int i, k; + + fluid_real_t xn; /* mono input x(n) */ + fluid_real_t out_tone_filter; /* tone corrector output */ + fluid_real_t out_left, out_right; /* output stereo Left and Right */ + fluid_real_t matrix_factor; /* partial matrix term */ + fluid_real_t delay_out_s; /* sample */ + fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */ + + for(k = 0; k < FLUID_BUFSIZE; k++) + { + /* stereo output */ + out_left = out_right = 0; +#ifdef DENORMALISING + /* Input is adjusted by DC_OFFSET. */ + xn = (in[k]) * FIXED_GAIN + DC_OFFSET; +#else + xn = (in[k]) * FIXED_GAIN; +#endif + + /*-------------------------------------------------------------------- + tone correction + */ + out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer; + rev->late.tone_buffer = xn; + xn = out_tone_filter; + /*-------------------------------------------------------------------- + process feedback delayed network: + - xn is the input signal. + - before inserting in the line input we first we get the delay lines + output, filter them and compute output in local delay_out[]. + - also matrix_factor is computed (to simplify further matrix product). + ---------------------------------------------------------------------*/ + /* We begin with the modulated output delay line + damping filter */ + matrix_factor = 0; + + for(i = 0; i < NBR_DELAYS; i++) + { + mod_delay_line *mdl = &rev->late.mod_delay_lines[i]; + /* get current modulated output */ + delay_out_s = get_mod_delay(mdl); + + /* process low pass damping filter + (input:delay_out_s, output:delay_out_s) */ + process_damping_filter(delay_out_s, delay_out_s, mdl); + + /* Result in delay_out[], and matrix_factor. + These will be of use later during input line process */ + delay_out[i] = delay_out_s; /* result in delay_out[] */ + matrix_factor += delay_out_s; /* result in matrix_factor */ + + /* Process stereo output */ + /* stereo left = left + out_left_gain * delay_out */ + out_left += rev->late.out_left_gain[i] * delay_out_s; + /* stereo right= right+ out_right_gain * delay_out */ + out_right += rev->late.out_right_gain[i] * delay_out_s; + } + + /* now we process the input delay line. Each input is a combination of: + - xn: input signal + - delay_out[] the output of a delay line given by a permutation matrix P + - and matrix_factor. + This computes: in_delay_line = xn + (delay_out[] * matrix A) with + an algorithm equivalent but faster than using a product with matrix A. + */ + /* matrix_factor = output sum * (-2.0)/N */ + matrix_factor *= FDN_MATRIX_FACTOR; + matrix_factor += xn; /* adds reverb input signal */ + + for(i = 1; i < NBR_DELAYS; i++) + { + /* delay_in[i-1] = delay_out[i] + matrix_factor */ + delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl; + push_in_delay_line(dl, delay_out[i] + matrix_factor); + } + + /* last line input (NB_DELAY-1) */ + /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */ + { + delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl; + push_in_delay_line(dl, delay_out[0] + matrix_factor); + } + + /*-------------------------------------------------------------------*/ +#ifdef DENORMALISING + /* Removes the DC offset */ + out_left -= DC_OFFSET; + out_right -= DC_OFFSET; +#endif + /* Calculates stereo output MIXING anything already there: */ + /* + left_out[k] += out_left * rev->wet1 + out_right * rev->wet2; + right_out[k] += out_right * rev->wet1 + out_left * rev->wet2; + + As wet1 is integrated in stereo coefficient wet 1 is now + integrated in out_left and out_right, so we simplify previous + relation by suppression of one multiply as this: + + left_out[k] += out_left + out_right * rev->wet2; + right_out[k] += out_right + out_left * rev->wet2; + */ + left_out[k] += out_left + out_right * rev->wet2; + right_out[k] += out_right + out_left * rev->wet2; + } +} diff --git a/libs/fluidsynth/src/rvoice/fluid_rev.h b/libs/fluidsynth/src/rvoice/fluid_rev.h new file mode 100644 index 00000000000..35c9cf664f8 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_rev.h @@ -0,0 +1,91 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_REV_H +#define _FLUID_REV_H + +#include "fluidsynth_priv.h" + +typedef struct _fluid_revmodel_t fluid_revmodel_t; + +/* enum describing each reverb parameter */ +enum fluid_reverb_param +{ + FLUID_REVERB_ROOMSIZE, /**< reverb time */ + FLUID_REVERB_DAMP, /**< high frequency damping */ + FLUID_REVERB_WIDTH, /**< stereo width */ + FLUID_REVERB_LEVEL, /**< output level */ + FLUID_REVERB_PARAM_LAST /* number of enum fluid_reverb_param */ +}; + +/* return a bit flag from param: 2^param */ +#define FLUID_REVPARAM_TO_SETFLAG(param) (1 << param) + +/** Flags for fluid_revmodel_set() */ +typedef enum +{ + FLUID_REVMODEL_SET_ROOMSIZE = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_ROOMSIZE), + FLUID_REVMODEL_SET_DAMPING = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_DAMP), + FLUID_REVMODEL_SET_WIDTH = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_WIDTH), + FLUID_REVMODEL_SET_LEVEL = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_LEVEL), + + /** Value for fluid_revmodel_set() which sets all reverb parameters. */ + FLUID_REVMODEL_SET_ALL = FLUID_REVMODEL_SET_LEVEL + | FLUID_REVMODEL_SET_WIDTH + | FLUID_REVMODEL_SET_DAMPING + | FLUID_REVMODEL_SET_ROOMSIZE, +} fluid_revmodel_set_t; + +/* + * reverb preset + */ +typedef struct _fluid_revmodel_presets_t +{ + const char *name; + fluid_real_t roomsize; + fluid_real_t damp; + fluid_real_t width; + fluid_real_t level; +} fluid_revmodel_presets_t; + + +/* + * reverb + */ +fluid_revmodel_t * +new_fluid_revmodel(fluid_real_t sample_rate_max, fluid_real_t sample_rate); + +void delete_fluid_revmodel(fluid_revmodel_t *rev); + +void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); + +void fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); + +void fluid_revmodel_reset(fluid_revmodel_t *rev); + +void fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize, + fluid_real_t damping, fluid_real_t width, fluid_real_t level); + +int fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate); + +#endif /* _FLUID_REV_H */ diff --git a/libs/fluidsynth/src/rvoice/fluid_rvoice.c b/libs/fluidsynth/src/rvoice/fluid_rvoice.c new file mode 100644 index 00000000000..3972176f1e4 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_rvoice.c @@ -0,0 +1,935 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_rvoice.h" +#include "fluid_conv.h" +#include "fluid_sys.h" + + +static void fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks); + +/** + * @return -1 if voice is quiet, 0 if voice has finished, 1 otherwise + */ +static FLUID_INLINE int +fluid_rvoice_calc_amp(fluid_rvoice_t *voice) +{ + fluid_real_t target_amp; /* target amplitude */ + + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVDELAY) + { + return -1; /* The volume amplitude is in hold phase. No sound is produced. */ + } + + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK) + { + /* the envelope is in the attack section: ramp linearly to max value. + * A positive modlfo_to_vol should increase volume (negative attenuation). + */ + target_amp = fluid_cb2amp(voice->dsp.attenuation) + * fluid_cb2amp(fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol) + * fluid_adsr_env_get_val(&voice->envlfo.volenv); + } + else + { + fluid_real_t amplitude_that_reaches_noise_floor; + fluid_real_t amp_max; + + target_amp = fluid_cb2amp(voice->dsp.attenuation) + * fluid_cb2amp(FLUID_PEAK_ATTENUATION * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)) + + fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol); + + /* We turn off a voice, if the volume has dropped low enough. */ + + /* A voice can be turned off, when an estimate for the volume + * (upper bound) falls below that volume, that will drop the + * sample below the noise floor. + */ + + /* If the loop amplitude is known, we can use it if the voice loop is within + * the sample loop + */ + + /* Is the playing pointer already in the loop? */ + if(voice->dsp.has_looped) + { + amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_loop; + } + else + { + amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_nonloop; + } + + /* voice->attenuation_min is a lower boundary for the attenuation + * now and in the future (possibly 0 in the worst case). Now the + * amplitude of sample and volenv cannot exceed amp_max (since + * volenv_val can only drop): + */ + + amp_max = fluid_cb2amp(voice->dsp.min_attenuation_cB) * + fluid_adsr_env_get_val(&voice->envlfo.volenv); + + /* And if amp_max is already smaller than the known amplitude, + * which will attenuate the sample below the noise floor, then we + * can safely turn off the voice. Duh. */ + if(amp_max < amplitude_that_reaches_noise_floor) + { + return 0; + } + } + + /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */ + voice->dsp.amp_incr = (target_amp - voice->dsp.amp) / FLUID_BUFSIZE; + + fluid_check_fpe("voice_write amplitude calculation"); + + /* no volume and not changing? - No need to process */ + if((voice->dsp.amp == 0.0f) && (voice->dsp.amp_incr == 0.0f)) + { + return -1; + } + + return 1; +} + + +/* these should be the absolute minimum that FluidSynth can deal with */ +#define FLUID_MIN_LOOP_SIZE 2 +#define FLUID_MIN_LOOP_PAD 0 + +#define FLUID_SAMPLESANITY_CHECK (1 << 0) +#define FLUID_SAMPLESANITY_STARTUP (1 << 1) + +/* Purpose: + * + * Make sure, that sample start / end point and loop points are in + * proper order. When starting up, calculate the initial phase. + * TODO: Investigate whether this can be moved from rvoice to voice. + */ +static void +fluid_rvoice_check_sample_sanity(fluid_rvoice_t *voice) +{ + int min_index_nonloop = (int) voice->dsp.sample->start; + int max_index_nonloop = (int) voice->dsp.sample->end; + + /* make sure we have enough samples surrounding the loop */ + int min_index_loop = (int) voice->dsp.sample->start + FLUID_MIN_LOOP_PAD; + int max_index_loop = (int) voice->dsp.sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */ + fluid_check_fpe("voice_check_sample_sanity start"); + +#if 0 + printf("Sample from %i to %i\n", voice->dsp.sample->start, voice->dsp.sample->end); + printf("Sample loop from %i %i\n", voice->dsp.sample->loopstart, voice->dsp.sample->loopend); + printf("Playback from %i to %i\n", voice->dsp.start, voice->dsp.end); + printf("Playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend); +#endif + + /* Keep the start point within the sample data */ + if(voice->dsp.start < min_index_nonloop) + { + voice->dsp.start = min_index_nonloop; + } + else if(voice->dsp.start > max_index_nonloop) + { + voice->dsp.start = max_index_nonloop; + } + + /* Keep the end point within the sample data */ + if(voice->dsp.end < min_index_nonloop) + { + voice->dsp.end = min_index_nonloop; + } + else if(voice->dsp.end > max_index_nonloop) + { + voice->dsp.end = max_index_nonloop; + } + + /* Keep start and end point in the right order */ + if(voice->dsp.start > voice->dsp.end) + { + int temp = voice->dsp.start; + voice->dsp.start = voice->dsp.end; + voice->dsp.end = temp; + /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */ + } + + /* Zero length? */ + if(voice->dsp.start == voice->dsp.end) + { + fluid_rvoice_voiceoff(voice, NULL); + return; + } + + if((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) + || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) + { + /* Keep the loop start point within the sample data */ + if(voice->dsp.loopstart < min_index_loop) + { + voice->dsp.loopstart = min_index_loop; + } + else if(voice->dsp.loopstart > max_index_loop) + { + voice->dsp.loopstart = max_index_loop; + } + + /* Keep the loop end point within the sample data */ + if(voice->dsp.loopend < min_index_loop) + { + voice->dsp.loopend = min_index_loop; + } + else if(voice->dsp.loopend > max_index_loop) + { + voice->dsp.loopend = max_index_loop; + } + + /* Keep loop start and end point in the right order */ + if(voice->dsp.loopstart > voice->dsp.loopend) + { + int temp = voice->dsp.loopstart; + voice->dsp.loopstart = voice->dsp.loopend; + voice->dsp.loopend = temp; + /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */ + } + + /* Loop too short? Then don't loop. */ + if(voice->dsp.loopend < voice->dsp.loopstart + FLUID_MIN_LOOP_SIZE) + { + voice->dsp.samplemode = FLUID_UNLOOPED; + } + + /* The loop points may have changed. Obtain a new estimate for the loop volume. */ + /* Is the voice loop within the sample loop? */ + if((int)voice->dsp.loopstart >= (int)voice->dsp.sample->loopstart + && (int)voice->dsp.loopend <= (int)voice->dsp.sample->loopend) + { + /* Is there a valid peak amplitude available for the loop, and can we use it? */ + if(voice->dsp.sample->amplitude_that_reaches_noise_floor_is_valid && voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE) + { + voice->dsp.amplitude_that_reaches_noise_floor_loop = voice->dsp.sample->amplitude_that_reaches_noise_floor / voice->dsp.synth_gain; + } + else + { + /* Worst case */ + voice->dsp.amplitude_that_reaches_noise_floor_loop = voice->dsp.amplitude_that_reaches_noise_floor_nonloop; + }; + }; + + } /* if sample mode is looped */ + + /* Run startup specific code (only once, when the voice is started) */ + if(voice->dsp.check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP) + { + if(max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE) + { + if((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) + || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) + { + voice->dsp.samplemode = FLUID_UNLOOPED; + } + } + + /* Set the initial phase of the voice (using the result from the + start offset modulators). */ + fluid_phase_set_int(voice->dsp.phase, voice->dsp.start); + } /* if startup */ + + /* Is this voice run in loop mode, or does it run straight to the + end of the waveform data? */ + if(((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) && + (fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE)) + || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) + { + /* Yes, it will loop as soon as it reaches the loop point. In + * this case we must prevent, that the playback pointer (phase) + * happens to end up beyond the 2nd loop point, because the + * point has moved. The DSP algorithm is unable to cope with + * that situation. So if the phase is beyond the 2nd loop + * point, set it to the start of the loop. No way to avoid some + * noise here. Note: If the sample pointer ends up -before the + * first loop point- instead, then the DSP loop will just play + * the sample, enter the loop and proceed as expected => no + * actions required. + */ + int index_in_sample = fluid_phase_index(voice->dsp.phase); + + if(index_in_sample >= voice->dsp.loopend) + { + /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */ + fluid_phase_set_int(voice->dsp.phase, voice->dsp.loopstart); + } + } + + /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->dsp.start, voice->dsp.end, voice->dsp.loopstart, voice->dsp.loopend); */ + + /* Sample sanity has been assured. Don't check again, until some + sample parameter is changed by modulation. */ + voice->dsp.check_sample_sanity_flag = 0; +#if 0 + printf("Sane? playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend); +#endif + fluid_check_fpe("voice_check_sample_sanity"); +} + + +/** + * Synthesize a voice to a buffer. + * + * @param voice rvoice to synthesize + * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length) + * @return Count of samples written to dsp_buf. (-1 means voice is currently + * quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.) + * + * Panning, reverb and chorus are processed separately. The dsp interpolation + * routine is in (fluid_rvoice_dsp.c). + */ +int +fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf) +{ + int ticks = voice->envlfo.ticks; + int count, is_looping; + fluid_real_t modenv_val; + + /******************* sample sanity check **********/ + + if(!voice->dsp.sample) + { + return 0; + } + + if(voice->dsp.check_sample_sanity_flag) + { + fluid_rvoice_check_sample_sanity(voice); + } + + /******************* noteoff check ****************/ + + if(voice->envlfo.noteoff_ticks != 0 && + voice->envlfo.ticks >= voice->envlfo.noteoff_ticks) + { + fluid_rvoice_noteoff_LOCAL(voice, 0); + } + + voice->envlfo.ticks += FLUID_BUFSIZE; + + /******************* vol env **********************/ + + fluid_adsr_env_calc(&voice->envlfo.volenv); + fluid_check_fpe("voice_write vol env"); + + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVFINISHED) + { + return 0; + } + + /******************* mod env **********************/ + + fluid_adsr_env_calc(&voice->envlfo.modenv); + fluid_check_fpe("voice_write mod env"); + + /******************* lfo **********************/ + + fluid_lfo_calc(&voice->envlfo.modlfo, ticks); + fluid_check_fpe("voice_write mod LFO"); + fluid_lfo_calc(&voice->envlfo.viblfo, ticks); + fluid_check_fpe("voice_write vib LFO"); + + /******************* amplitude **********************/ + + count = fluid_rvoice_calc_amp(voice); + + if(count <= 0) + { + return count; /* return -1 if voice is quiet, 0 if voice has finished */ + } + + /******************* phase **********************/ + + /* SF2.04 section 8.1.2 #26: + * attack of modEnv is convex ?!? + */ + modenv_val = (fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK) + ? fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv)) + : fluid_adsr_env_get_val(&voice->envlfo.modenv); + /* Calculate the number of samples, that the DSP loop advances + * through the original waveform with each step in the output + * buffer. It is the ratio between the frequencies of original + * waveform and output waveform.*/ + voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch + + voice->dsp.pitchoffset + + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch + + fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch + + modenv_val * voice->envlfo.modenv_to_pitch) + / voice->dsp.root_pitch_hz; + + /******************* portamento ****************/ + /* pitchoffset is updated if enabled. + Pitchoffset will be added to dsp pitch at next phase calculation time */ + + /* In most cases portamento will be disabled. Thus first verify that portamento is + * enabled before updating pitchoffset and before disabling portamento when necessary, + * in order to keep the performance loss at minimum. + * If the algorithm would first update pitchoffset and then verify if portamento + * needs to be disabled, there would be a significant performance drop on a x87 FPU + */ + if(voice->dsp.pitchinc > 0.0f) + { + /* portamento is enabled, so update pitchoffset */ + voice->dsp.pitchoffset += voice->dsp.pitchinc; + + /* when pitchoffset reaches 0.0f, portamento is disabled */ + if(voice->dsp.pitchoffset > 0.0f) + { + voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; + } + } + else if(voice->dsp.pitchinc < 0.0f) + { + /* portamento is enabled, so update pitchoffset */ + voice->dsp.pitchoffset += voice->dsp.pitchinc; + + /* when pitchoffset reaches 0.0f, portamento is disabled */ + if(voice->dsp.pitchoffset < 0.0f) + { + voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; + } + } + + fluid_check_fpe("voice_write phase calculation"); + + /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */ + if(voice->dsp.phase_incr == 0) + { + voice->dsp.phase_incr = 1; + } + + /* voice is currently looping? */ + is_looping = voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE + || (voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE + && fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE); + + /*********************** run the dsp chain ************************ + * The sample is mixed with the output buffer. + * The buffer has to be filled from 0 to FLUID_BUFSIZE-1. + * Depending on the position in the loop and the loop size, this + * may require several runs. */ + + switch(voice->dsp.interp_method) + { + case FLUID_INTERP_NONE: + count = fluid_rvoice_dsp_interpolate_none(&voice->dsp, dsp_buf, is_looping); + break; + + case FLUID_INTERP_LINEAR: + count = fluid_rvoice_dsp_interpolate_linear(&voice->dsp, dsp_buf, is_looping); + break; + + case FLUID_INTERP_4THORDER: + default: + count = fluid_rvoice_dsp_interpolate_4th_order(&voice->dsp, dsp_buf, is_looping); + break; + + case FLUID_INTERP_7THORDER: + count = fluid_rvoice_dsp_interpolate_7th_order(&voice->dsp, dsp_buf, is_looping); + break; + } + + fluid_check_fpe("voice_write interpolation"); + + if(count == 0) + { + return count; + } + + /*************** resonant filter ******************/ + + fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate, + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc + + modenv_val * voice->envlfo.modenv_to_fc); + + fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count); + + /* additional custom filter - only uses the fixed modulator, no lfos... */ + fluid_iir_filter_calc(&voice->resonant_custom_filter, voice->dsp.output_rate, 0); + fluid_iir_filter_apply(&voice->resonant_custom_filter, dsp_buf, count); + + return count; +} + +/** + * Initialize buffers up to (and including) bufnum + */ +static int +fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t *buffers, unsigned int bufnum) +{ + unsigned int i; + + if(bufnum < buffers->count) + { + return FLUID_OK; + } + + if(bufnum >= FLUID_RVOICE_MAX_BUFS) + { + return FLUID_FAILED; + } + + for(i = buffers->count; i <= bufnum; i++) + { + buffers->bufs[i].target_amp = 0.0f; + buffers->bufs[i].current_amp = 0.0f; + } + + buffers->count = bufnum + 1; + return FLUID_OK; +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp) +{ + fluid_rvoice_buffers_t *buffers = obj; + unsigned int bufnum = param[0].i; + fluid_real_t value = param[1].real; + + if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) + { + return; + } + + buffers->bufs[bufnum].target_amp = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping) +{ + fluid_rvoice_buffers_t *buffers = obj; + unsigned int bufnum = param[0].i; + int mapping = param[1].i; + + if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) + { + return; + } + + buffers->bufs[bufnum].mapping = mapping; +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset) +{ + fluid_rvoice_t *voice = obj; + + voice->dsp.has_looped = 0; + voice->envlfo.ticks = 0; + voice->envlfo.noteoff_ticks = 0; + voice->dsp.amp = 0.0f; /* The last value of the volume envelope, used to + calculate the volume increment during + processing */ + + /* legato initialization */ + voice->dsp.pitchoffset = 0.0; /* portamento initialization */ + voice->dsp.pitchinc = 0.0; + + /* mod env initialization*/ + fluid_adsr_env_reset(&voice->envlfo.modenv); + + /* vol env initialization */ + fluid_adsr_env_reset(&voice->envlfo.volenv); + + /* Fixme: Retrieve from any other existing + voice on this channel to keep LFOs in + unison? */ + fluid_lfo_reset(&voice->envlfo.viblfo); + fluid_lfo_reset(&voice->envlfo.modlfo); + + /* Clear sample history in filter */ + fluid_iir_filter_reset(&voice->resonant_filter); + fluid_iir_filter_reset(&voice->resonant_custom_filter); + + /* Force setting of the phase at the first DSP loop run + * This cannot be done earlier, because it depends on modulators. + [DH] Is that comment really true? */ + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff) +{ + fluid_rvoice_t *rvoice = obj; + unsigned int min_ticks = param[0].i; + + fluid_rvoice_noteoff_LOCAL(rvoice, min_ticks); +} + +static void +fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks) +{ + if(min_ticks > voice->envlfo.ticks) + { + /* Delay noteoff */ + voice->envlfo.noteoff_ticks = min_ticks; + return; + } + + voice->envlfo.noteoff_ticks = 0; + + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK) + { + /* A voice is turned off during the attack section of the volume + * envelope. The attack section ramps up linearly with + * amplitude. The other sections use logarithmic scaling. Calculate new + * volenv_val to achieve equivalent amplitude during the release phase + * for seamless volume transition. + */ + if(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0) + { + fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol; + fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * fluid_cb2amp(lfo); + fluid_real_t env_value = - (((-200.f / FLUID_M_LN10) * FLUID_LOGF(amp) - lfo) / FLUID_PEAK_ATTENUATION - 1); + fluid_clip(env_value, 0.0f, 1.0f); + fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); + } + } + + if(fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK) + { + /* A voice is turned off during the attack section of the modulation + * envelope. The attack section use convex scaling with pitch and filter + * frequency cutoff (see fluid_rvoice_write(): modenv_val = fluid_convex(127 * modenv.val) + * The other sections use linear scaling: modenv_val = modenv.val + * + * Calculate new modenv.val to achieve equivalent modenv_val during the release phase + * for seamless pitch and filter frequency cutoff transition. + */ + if(fluid_adsr_env_get_val(&voice->envlfo.modenv) > 0) + { + fluid_real_t env_value = fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv)); + fluid_clip(env_value, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.modenv, env_value); + } + } + + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE); + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE); +} + +/** + * skips to Attack section + * + * Updates vol and attack data + * Correction on volume val to achieve equivalent amplitude at noteOn legato + * + * @param voice the synthesis voice to be updated +*/ +static FLUID_INLINE void fluid_rvoice_local_retrigger_attack(fluid_rvoice_t *voice) +{ + /* skips to Attack section */ + /* Once in Attack section, current count must be reset, to be sure + that the section will be not be prematurely finished. */ + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVATTACK); + { + /* Correction on volume val to achieve equivalent amplitude at noteOn legato */ + fluid_env_data_t *env_data; + fluid_real_t peak = fluid_cb2amp(voice->dsp.attenuation); + fluid_real_t prev_peak = fluid_cb2amp(voice->dsp.prev_attenuation); + voice->envlfo.volenv.val = (voice->envlfo.volenv.val * prev_peak) / peak; + /* Correction on slope direction for Attack section */ + env_data = &voice->envlfo.volenv.data[FLUID_VOICE_ENVATTACK]; + + if(voice->envlfo.volenv.val <= 1.0f) + { + /* slope attack for legato note needs to be positive from val up to 1 */ + env_data->increment = 1.0f / env_data->count; + env_data->min = -1.0f; + env_data->max = 1.0f; + } + else + { + /* slope attack for legato note needs to be negative: from val down to 1 */ + env_data->increment = -voice->envlfo.volenv.val / env_data->count; + env_data->min = 1.0f; + env_data->max = voice->envlfo.volenv.val; + } + } +} + +/** + * Used by legato Mode : multi_retrigger + * see fluid_synth_noteon_mono_legato_multi_retrigger() + * @param voice the synthesis voice to be updated +*/ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack) +{ + fluid_rvoice_t *voice = obj; + int section; /* volume or modulation section */ + + /*------------------------------------------------------------------------- + Section skip for volume envelope + --------------------------------------------------------------------------*/ + section = fluid_adsr_env_get_section(&voice->envlfo.volenv); + if(section >= FLUID_VOICE_ENVHOLD) + { + /* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new + volenv_val to achieve equivalent amplitude during the attack phase + for seamless volume transition. */ + fluid_real_t amp_cb, env_value; + amp_cb = FLUID_PEAK_ATTENUATION * + (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)); + env_value = fluid_cb2amp(amp_cb); /* a bit of optimization */ + fluid_clip(env_value, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); + /* next, skips to Attack section */ + } + + /* skips to Attack section from any section */ + /* Update vol and attack data */ + fluid_rvoice_local_retrigger_attack(voice); + + /*------------------------------------------------------------------------- + Section skip for modulation envelope + --------------------------------------------------------------------------*/ + section = fluid_adsr_env_get_section(&voice->envlfo.modenv); + if(section >= FLUID_VOICE_ENVHOLD) + { + /* DECAY, SUSTAIN,RELEASE section use linear scaling. + Since v 2.1 , as recommended by soundfont 2.01/2.4 spec, ATTACK section + uses convex shape (see fluid_rvoice_write() - fluid_convex()). + Calculate new modenv value (new_value) for seamless attack transition. + Here we need the inverse of fluid_convex() function defined as: + new_value = pow(10, (1 - current_val) . FLUID_PEAK_ATTENUATION / -200 . 2.0) + For performance reason we use fluid_cb2amp(Val) = pow(10, val/-200) with + val = (1 - current_val) . FLUID_PEAK_ATTENUATION / 2.0 + */ + fluid_real_t new_value; /* new modenv value */ + new_value = fluid_cb2amp((1.0f - fluid_adsr_env_get_val(&voice->envlfo.modenv)) + * FLUID_PEAK_ATTENUATION / 2.0); + fluid_clip(new_value, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.modenv, new_value); + } + /* Skips from any section to ATTACK section */ + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK); +} + +/** + * sets the portamento dsp parameters: dsp.pitchoffset, dsp.pitchinc + * @param voice rvoice to set portamento. + * @param countinc increment count number. + * @param pitchoffset pitch offset to apply to voice dsp.pitch. + * + * Notes: + * 1) To get continuous portamento between consecutive noteOn (n1,n2,n3...), + * pitchoffset is accumulated in current dsp pitchoffset. + * 2) And to get constant portamento duration, dsp pitch increment is updated. +*/ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento) +{ + fluid_rvoice_t *voice = obj; + unsigned int countinc = param[0].i; + fluid_real_t pitchoffset = param[1].real; + + if(countinc) + { + voice->dsp.pitchoffset += pitchoffset; + voice->dsp.pitchinc = - voice->dsp.pitchoffset / countinc; + } + + /* Then during the voice processing (in fluid_rvoice_write()), + dsp.pitchoffset will be incremented by dsp pitchinc. */ +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.output_rate = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method) +{ + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.interp_method = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.root_pitch_hz = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.pitch = value; +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.prev_attenuation = voice->dsp.attenuation; + voice->dsp.attenuation = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.min_attenuation_cB = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.viblfo_to_pitch = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modlfo_to_pitch = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modlfo_to_vol = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modlfo_to_fc = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modenv_to_fc = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modenv_to_pitch = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.synth_gain = value; + + /* For a looped sample, this value will be overwritten as soon as the + * loop parameters are initialized (they may depend on modulators). + * This value can be kept, it is a worst-case estimate. + */ + voice->dsp.amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / value; + voice->dsp.amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start) +{ + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.start = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end) +{ + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.end = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart) +{ + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.loopstart = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend) +{ + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.loopend = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode) +{ + fluid_rvoice_t *voice = obj; + enum fluid_loop value = param[0].i; + + voice->dsp.samplemode = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample) +{ + fluid_rvoice_t *voice = obj; + fluid_sample_t *value = param[0].ptr; + + voice->dsp.sample = value; + + if(value) + { + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; + } +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff) +{ + fluid_rvoice_t *voice = obj; + + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED); + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED); +} diff --git a/libs/fluidsynth/src/rvoice/fluid_rvoice.h b/libs/fluidsynth/src/rvoice/fluid_rvoice.h new file mode 100644 index 00000000000..610afd72529 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_rvoice.h @@ -0,0 +1,231 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_RVOICE_H +#define _FLUID_RVOICE_H + +#include "fluidsynth_priv.h" +#include "fluid_iir_filter.h" +#include "fluid_adsr_env.h" +#include "fluid_lfo.h" +#include "fluid_phase.h" +#include "fluid_sfont.h" + +typedef struct _fluid_rvoice_envlfo_t fluid_rvoice_envlfo_t; +typedef struct _fluid_rvoice_dsp_t fluid_rvoice_dsp_t; +typedef struct _fluid_rvoice_buffers_t fluid_rvoice_buffers_t; +typedef struct _fluid_rvoice_t fluid_rvoice_t; + +/* Smallest amplitude that can be perceived (full scale is +/- 0.5) + * 16 bits => 96+4=100 dB dynamic range => 0.00001 + * 24 bits => 144-4 = 140 dB dynamic range => 1.e-7 + * 1.e-7 * 2 == 2.e-7 :) + */ +#define FLUID_NOISE_FLOOR ((fluid_real_t)2.e-7) + +enum fluid_loop +{ + FLUID_UNLOOPED = 0, + FLUID_LOOP_DURING_RELEASE = 1, + FLUID_NOTUSED = 2, + FLUID_LOOP_UNTIL_RELEASE = 3 +}; + +/* + * rvoice ticks-based parameters + * These parameters must be updated even if the voice is currently quiet. + */ +struct _fluid_rvoice_envlfo_t +{ + /* Note-off minimum length */ + unsigned int ticks; + unsigned int noteoff_ticks; + + /* vol env */ + fluid_adsr_env_t volenv; + + /* mod env */ + fluid_adsr_env_t modenv; + fluid_real_t modenv_to_fc; + fluid_real_t modenv_to_pitch; + + /* mod lfo */ + fluid_lfo_t modlfo; + fluid_real_t modlfo_to_fc; + fluid_real_t modlfo_to_pitch; + fluid_real_t modlfo_to_vol; + + /* vib lfo */ + fluid_lfo_t viblfo; + fluid_real_t viblfo_to_pitch; +}; + +/* + * rvoice parameters needed for dsp interpolation + */ +struct _fluid_rvoice_dsp_t +{ + /* interpolation method, as in fluid_interp in fluidsynth.h */ + enum fluid_interp interp_method; + enum fluid_loop samplemode; + + /* Flag that is set as soon as the first loop is completed. */ + char has_looped; + + /* Flag that initiates, that sample-related parameters have to be checked. */ + char check_sample_sanity_flag; + + fluid_sample_t *sample; + + /* sample and loop start and end points (offset in sample memory). */ + int start; + int end; + int loopstart; + int loopend; /* Note: first point following the loop (superimposed on loopstart) */ + + /* Stuff needed for portamento calculations */ + fluid_real_t pitchoffset; /* the portamento range in midicents */ + fluid_real_t pitchinc; /* the portamento increment in midicents */ + + /* Stuff needed for phase calculations */ + + fluid_real_t pitch; /* the pitch in midicents */ + fluid_real_t root_pitch_hz; + fluid_real_t output_rate; + + /* Stuff needed for amplitude calculations */ + + fluid_real_t attenuation; /* the attenuation in centibels */ + fluid_real_t prev_attenuation; /* the previous attenuation in centibels + used by fluid_rvoice_multi_retrigger_attack() */ + fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation + * during the lifetime of the voice */ + fluid_real_t amplitude_that_reaches_noise_floor_nonloop; + fluid_real_t amplitude_that_reaches_noise_floor_loop; + fluid_real_t synth_gain; /* master gain */ + + /* Dynamic input to the interpolator below */ + + fluid_real_t amp; /* current linear amplitude */ + fluid_real_t amp_incr; /* amplitude increment value for the next FLUID_BUFSIZE samples */ + + fluid_phase_t phase; /* the phase (current sample offset) of the sample wave */ + fluid_real_t phase_incr; /* the phase increment for the next FLUID_BUFSIZE samples */ +}; + +/* Currently left, right, reverb, chorus. To be changed if we + ever add surround positioning, or stereo reverb/chorus */ +#define FLUID_RVOICE_MAX_BUFS (4) + +/* + * rvoice mixer-related parameters + */ +struct _fluid_rvoice_buffers_t +{ + unsigned int count; /* Number of records in "bufs" */ + struct + { + /* the actual, linearly interpolated amplitude with which the dsp sample should be mixed into the buf */ + fluid_real_t current_amp; + + /* the desired amplitude [...] mixed into the buf (directly set by e.g. rapidly changing PAN events) */ + fluid_real_t target_amp; + + /* Mapping to mixdown buffer index */ + int mapping; + } bufs[FLUID_RVOICE_MAX_BUFS]; +}; + + +/* + * Hard real-time parameters needed to synthesize a voice + */ +struct _fluid_rvoice_t +{ + fluid_rvoice_envlfo_t envlfo; + fluid_rvoice_dsp_t dsp; + fluid_iir_filter_t resonant_filter; /* IIR resonant dsp filter */ + fluid_iir_filter_t resonant_custom_filter; /* optional custom/general-purpose IIR resonant filter */ + fluid_rvoice_buffers_t buffers; +}; + + +int fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample); + +/* defined in fluid_rvoice_dsp.c */ +void fluid_rvoice_dsp_config(void); +int fluid_rvoice_dsp_interpolate_none(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); +int fluid_rvoice_dsp_interpolate_linear(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); +int fluid_rvoice_dsp_interpolate_4th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); +int fluid_rvoice_dsp_interpolate_7th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); + + +/* + * Combines the most significant 16 bit part of a sample with a potentially present + * least sig. 8 bit part in order to create a 24 bit sample. + */ +static FLUID_INLINE int32_t +fluid_rvoice_get_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx) +{ + /* cast sample to unsigned type, so we can safely shift and bitwise or + * without relying on undefined behaviour (should never happen anyway ofc...) */ + uint32_t msb = (uint32_t)dsp_msb[idx]; + uint8_t lsb = 0U; + + /* most soundfonts have 16 bit samples, assume that it's unlikely we + * experience 24 bit samples here */ + if(FLUID_UNLIKELY(dsp_lsb != NULL)) + { + lsb = (uint8_t)dsp_lsb[idx]; + } + + return (int32_t)((msb << 8) | lsb); +} + +#endif diff --git a/libs/fluidsynth/src/rvoice/fluid_rvoice_dsp.c b/libs/fluidsynth/src/rvoice/fluid_rvoice_dsp.c new file mode 100644 index 00000000000..b43a0f19077 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_rvoice_dsp.c @@ -0,0 +1,636 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sys.h" +#include "fluid_phase.h" +#include "fluid_rvoice.h" +#include "fluid_rvoice_dsp_tables.inc.h" + +/* Purpose: + * + * Interpolates audio data (obtains values between the samples of the original + * waveform data). + * + * Variables loaded from the voice structure (assigned in fluid_rvoice_write()): + * - dsp_data: Pointer to the original waveform data + * - dsp_phase: The position in the original waveform data. + * This has an integer and a fractional part (between samples). + * - dsp_phase_incr: For each output sample, the position in the original + * waveform advances by dsp_phase_incr. This also has an integer + * part and a fractional part. + * If a sample is played at root pitch (no pitch change), + * dsp_phase_incr is integer=1 and fractional=0. + * - dsp_amp: The current amplitude envelope value. + * - dsp_amp_incr: The changing rate of the amplitude envelope. + * + * A couple of variables are used internally, their results are discarded: + * - dsp_i: Index through the output buffer + * - dsp_buf: Output buffer of floating point values (FLUID_BUFSIZE in length) + */ + +/* Interpolation (find a value between two samples of the original waveform) */ + +static FLUID_INLINE fluid_real_t +fluid_rvoice_get_float_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx) +{ + int32_t sample = fluid_rvoice_get_sample(dsp_msb, dsp_lsb, idx); + return (fluid_real_t)sample; +} + +/* No interpolation. Just take the sample, which is closest to + * the playback pointer. Questionable quality, but very + * efficient. */ +int +fluid_rvoice_dsp_interpolate_none(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int end_index; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + end_index = looping ? voice->loopend - 1 : voice->end; + + while(1) + { + dsp_phase_index = fluid_phase_index_round(dsp_phase); /* round to nearest point */ + + /* interpolate sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + dsp_buf[dsp_i] = dsp_amp * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index_round(dsp_phase); /* round to nearest point */ + dsp_amp += dsp_amp_incr; + } + + /* break out if not looping (buffer may not be full) */ + if(!looping) + { + break; + } + + /* go back to loop start */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + voice->has_looped = 1; + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + } + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +} + +/* Straight line interpolation. + * Returns number of samples processed (usually FLUID_BUFSIZE but could be + * smaller if end of sample occurs). + */ +int +fluid_rvoice_dsp_interpolate_linear(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int end_index; + fluid_real_t point; + const fluid_real_t *FLUID_RESTRICT coeffs; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + /* last index before 2nd interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 1; + + /* 2nd interpolation point to use at end of loop or sample */ + if(looping) + { + point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); /* loop start */ + } + else + { + point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end); /* duplicate end for samples no longer looping */ + } + + while(1) + { + dsp_phase_index = fluid_phase_index(dsp_phase); + + /* interpolate the sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[1] * point); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; /* increment amplitude */ + } + + if(!looping) + { + break; /* break out if not looping (end of sample) */ + } + + /* go back to loop start (if past */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + voice->has_looped = 1; + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index--; /* set end back to second to last sample point */ + } + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +} + +/* 4th order (cubic) interpolation. + * Returns number of samples processed (usually FLUID_BUFSIZE but could be + * smaller if end of sample occurs). + */ +int +fluid_rvoice_dsp_interpolate_4th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int start_index, end_index; + fluid_real_t start_point, end_point1, end_point2; + const fluid_real_t *FLUID_RESTRICT coeffs; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + /* last index before 4th interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 2; + + if(voice->has_looped) /* set start_index and start point if looped or not */ + { + start_index = voice->loopstart; + start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); /* last point in loop (wrap around) */ + } + else + { + start_index = voice->start; + start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->start); /* just duplicate the point */ + } + + /* get points off the end (loop start if looping, duplicate point if end) */ + if(looping) + { + end_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); + end_point2 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1); + } + else + { + end_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end); + end_point2 = end_point1; + } + + while(1) + { + dsp_phase_index = fluid_phase_index(dsp_phase); + + /* interpolate first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * start_point + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* interpolate the sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index++; /* we're now interpolating the 2nd to last point */ + + /* interpolate within 2nd to last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[3] * end_point1); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within the last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * end_point1 + + coeffs[3] * end_point2); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + if(!looping) + { + break; /* break out if not looping (end of sample) */ + } + + /* go back to loop start */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + + if(!voice->has_looped) + { + voice->has_looped = 1; + start_index = voice->loopstart; + start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); + } + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index -= 2; /* set end back to third to last sample point */ + } + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +} + +/* 7th order interpolation. + * Returns number of samples processed (usually FLUID_BUFSIZE but could be + * smaller if end of sample occurs). + */ +int +fluid_rvoice_dsp_interpolate_7th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int start_index, end_index; + fluid_real_t start_points[3], end_points[3]; + const fluid_real_t *FLUID_RESTRICT coeffs; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + /* add 1/2 sample to dsp_phase since 7th order interpolation is centered on + * the 4th sample point */ + fluid_phase_incr(dsp_phase, (fluid_phase_t)0x80000000); + + /* last index before 7th interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 3; + + if(voice->has_looped) /* set start_index and start point if looped or not */ + { + start_index = voice->loopstart; + start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); + start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2); + start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 3); + } + else + { + start_index = voice->start; + start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->start); /* just duplicate the start point */ + start_points[1] = start_points[0]; + start_points[2] = start_points[0]; + } + + /* get the 3 points off the end (loop start if looping, duplicate point if end) */ + if(looping) + { + end_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); + end_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1); + end_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 2); + } + else + { + end_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end); + end_points[1] = end_points[0]; + end_points[2] = end_points[0]; + } + + while(1) + { + dsp_phase_index = fluid_phase_index(dsp_phase); + + /* interpolate first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * start_points[2] + + coeffs[1] * start_points[1] + + coeffs[2] * start_points[0] + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index++; + + /* interpolate 2nd to first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * start_points[1] + + coeffs[1] * start_points[0] + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index++; + + /* interpolate 3rd to first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * start_points[0] + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index -= 2; /* set back to original start index */ + + + /* interpolate the sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index++; /* we're now interpolating the 3rd to last point */ + + /* interpolate within 3rd to last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * end_points[0]); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the 2nd to last point */ + + /* interpolate within 2nd to last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * end_points[0] + + coeffs[6] * end_points[1]); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * end_points[0] + + coeffs[5] * end_points[1] + + coeffs[6] * end_points[2]); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + if(!looping) + { + break; /* break out if not looping (end of sample) */ + } + + /* go back to loop start */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + + if(!voice->has_looped) + { + voice->has_looped = 1; + start_index = voice->loopstart; + start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); + start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2); + start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 3); + } + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index -= 3; /* set end back to 4th to last sample point */ + } + + /* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on + * the 4th sample point (correct back to real value) */ + fluid_phase_decr(dsp_phase, (fluid_phase_t)0x80000000); + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +} diff --git a/libs/fluidsynth/src/rvoice/fluid_rvoice_event.c b/libs/fluidsynth/src/rvoice/fluid_rvoice_event.c new file mode 100644 index 00000000000..e60115f3617 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_rvoice_event.c @@ -0,0 +1,202 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_rvoice_event.h" +#include "fluid_rvoice.h" +#include "fluid_rvoice_mixer.h" +#include "fluid_iir_filter.h" +#include "fluid_lfo.h" +#include "fluid_adsr_env.h" + +static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t *handler, const fluid_rvoice_event_t *src_event); + +static FLUID_INLINE void +fluid_rvoice_event_dispatch(fluid_rvoice_event_t *event) +{ + event->method(event->object, event->param); +} + + +/** + * In order to be able to push more than one event atomically, + * use push for all events, then use flush to commit them to the + * queue. If threadsafe is false, all events are processed immediately. */ +int +fluid_rvoice_eventhandler_push_int_real(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, int intparam, + fluid_real_t realparam) +{ + fluid_rvoice_event_t local_event; + + local_event.method = method; + local_event.object = object; + local_event.param[0].i = intparam; + local_event.param[1].real = realparam; + + return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); +} + +int +fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t *handler, fluid_rvoice_function_t method, void *object, fluid_rvoice_param_t param[MAX_EVENT_PARAMS]) +{ + fluid_rvoice_event_t local_event; + + local_event.method = method; + local_event.object = object; + FLUID_MEMCPY(&local_event.param, param, sizeof(*param) * MAX_EVENT_PARAMS); + + return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); +} + +int +fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, void *ptr) +{ + fluid_rvoice_event_t local_event; + + local_event.method = method; + local_event.object = object; + local_event.param[0].ptr = ptr; + + return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); +} + +static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t *handler, const fluid_rvoice_event_t *src_event) +{ + fluid_rvoice_event_t *event; + int old_queue_stored = fluid_atomic_int_add(&handler->queue_stored, 1); + + event = fluid_ringbuffer_get_inptr(handler->queue, old_queue_stored); + + if(event == NULL) + { + fluid_atomic_int_add(&handler->queue_stored, -1); + FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing synth.polyphony!"); + return FLUID_FAILED; // Buffer full... + } + + FLUID_MEMCPY(event, src_event, sizeof(*event)); + + return FLUID_OK; +} + + +void +fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *eventhandler, fluid_rvoice_t *rvoice) +{ + fluid_rvoice_t **vptr = fluid_ringbuffer_get_inptr(eventhandler->finished_voices, 0); + + if(vptr == NULL) + { + return; // Buffer full + } + + *vptr = rvoice; + fluid_ringbuffer_next_inptr(eventhandler->finished_voices, 1); +} + +fluid_rvoice_eventhandler_t * +new_fluid_rvoice_eventhandler(int queuesize, + int finished_voices_size, int bufs, int fx_bufs, int fx_units, + fluid_real_t sample_rate_max, fluid_real_t sample_rate, + int extra_threads, int prio) +{ + fluid_rvoice_eventhandler_t *eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t); + + if(eventhandler == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + eventhandler->mixer = NULL; + eventhandler->queue = NULL; + eventhandler->finished_voices = NULL; + + fluid_atomic_int_set(&eventhandler->queue_stored, 0); + + eventhandler->finished_voices = new_fluid_ringbuffer(finished_voices_size, + sizeof(fluid_rvoice_t *)); + + if(eventhandler->finished_voices == NULL) + { + goto error_recovery; + } + + eventhandler->queue = new_fluid_ringbuffer(queuesize, sizeof(fluid_rvoice_event_t)); + + if(eventhandler->queue == NULL) + { + goto error_recovery; + } + + eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, fx_units, + sample_rate_max, sample_rate, eventhandler, extra_threads, prio); + + if(eventhandler->mixer == NULL) + { + goto error_recovery; + } + + return eventhandler; + +error_recovery: + delete_fluid_rvoice_eventhandler(eventhandler); + return NULL; +} + +int +fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t *handler) +{ + return fluid_ringbuffer_get_count(handler->queue); +} + + +/** + * Call fluid_rvoice_event_dispatch for all events in queue + * @return number of events dispatched + */ +int +fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t *handler) +{ + fluid_rvoice_event_t *event; + int result = 0; + + while(NULL != (event = fluid_ringbuffer_get_outptr(handler->queue))) + { + fluid_rvoice_event_dispatch(event); + result++; + fluid_ringbuffer_next_outptr(handler->queue); + } + + return result; +} + + +void +delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t *handler) +{ + fluid_return_if_fail(handler != NULL); + + delete_fluid_rvoice_mixer(handler->mixer); + delete_fluid_ringbuffer(handler->queue); + delete_fluid_ringbuffer(handler->finished_voices); + FLUID_FREE(handler); +} diff --git a/libs/fluidsynth/src/rvoice/fluid_rvoice_event.h b/libs/fluidsynth/src/rvoice/fluid_rvoice_event.h new file mode 100644 index 00000000000..225e9069e13 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_rvoice_event.h @@ -0,0 +1,114 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_RVOICE_EVENT_H +#define _FLUID_RVOICE_EVENT_H + +#include "fluidsynth_priv.h" +#include "fluid_rvoice_mixer.h" +#include "fluid_ringbuffer.h" + +typedef struct _fluid_rvoice_event_t fluid_rvoice_event_t; + +struct _fluid_rvoice_event_t +{ + fluid_rvoice_function_t method; + void *object; + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; +}; + +/* + * Bridge between the renderer thread and the midi state thread. + * fluid_rvoice_eventhandler_fetch_all() can be called in parallel + * with fluid_rvoice_eventhandler_push/flush() + */ +struct _fluid_rvoice_eventhandler_t +{ + fluid_ringbuffer_t *queue; /**< List of fluid_rvoice_event_t */ + fluid_atomic_int_t queue_stored; /**< Extras pushed but not flushed */ + fluid_ringbuffer_t *finished_voices; /**< return queue from handler, list of fluid_rvoice_t* */ + fluid_rvoice_mixer_t *mixer; +}; + +fluid_rvoice_eventhandler_t *new_fluid_rvoice_eventhandler( + int queuesize, int finished_voices_size, int bufs, + int fx_bufs, int fx_units, fluid_real_t sample_rate_max, fluid_real_t sample_rate, int, int); + +void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t *); + +int fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t *); +int fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t *); +void fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *eventhandler, + fluid_rvoice_t *rvoice); + +static FLUID_INLINE void +fluid_rvoice_eventhandler_flush(fluid_rvoice_eventhandler_t *handler) +{ + int queue_stored = fluid_atomic_int_get(&handler->queue_stored); + + if(queue_stored > 0) + { + fluid_atomic_int_set(&handler->queue_stored, 0); + fluid_ringbuffer_next_inptr(handler->queue, queue_stored); + } +} + +/** + * @return next finished voice, or NULL if nothing in queue + */ +static FLUID_INLINE fluid_rvoice_t * +fluid_rvoice_eventhandler_get_finished_voice(fluid_rvoice_eventhandler_t *handler) +{ + void *result = fluid_ringbuffer_get_outptr(handler->finished_voices); + + if(result == NULL) + { + return NULL; + } + + result = * (fluid_rvoice_t **) result; + fluid_ringbuffer_next_outptr(handler->finished_voices); + return result; +} + + +int fluid_rvoice_eventhandler_push_int_real(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, int intparam, + fluid_real_t realparam); + +int fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, void *ptr); + +int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]); + +static FLUID_INLINE void +fluid_rvoice_eventhandler_add_rvoice(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_t *rvoice) +{ + fluid_rvoice_eventhandler_push_ptr(handler, fluid_rvoice_mixer_add_voice, + handler->mixer, rvoice); +} + + + +#endif diff --git a/libs/fluidsynth/src/rvoice/fluid_rvoice_mixer.c b/libs/fluidsynth/src/rvoice/fluid_rvoice_mixer.c new file mode 100644 index 00000000000..490c7fb360d --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_rvoice_mixer.c @@ -0,0 +1,1727 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_rvoice_mixer.h" +#include "fluid_rvoice.h" +#include "fluid_sys.h" +#include "fluid_rev.h" +#include "fluid_chorus.h" +#include "fluid_ladspa.h" +#include "fluid_synth.h" + + +// If less than x voices, the thread overhead is larger than the gain, +// so don't activate the thread(s). +#define VOICES_PER_THREAD 8 + +typedef struct _fluid_mixer_buffers_t fluid_mixer_buffers_t; + +struct _fluid_mixer_buffers_t +{ + fluid_rvoice_mixer_t *mixer; /**< Owner of object */ +#if ENABLE_MIXER_THREADS + fluid_thread_t *thread; /**< Thread object */ + fluid_atomic_int_t ready; /**< Atomic: buffers are ready for mixing */ +#endif + + fluid_rvoice_t **finished_voices; /* List of voices who have finished */ + int finished_voice_count; + + fluid_real_t *local_buf; + + int buf_count; + int fx_buf_count; + + /** buffer to store the left part of a stereo channel to. + * Specifically a two dimensional array, containing \c buf_count sample buffers + * (i.e. for each synth.audio-groups), of which each contains + * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples) + * @note Each sample buffer is aligned to the FLUID_DEFAULT_ALIGNMENT + * boundary provided that this pointer points to an aligned buffer. + * So make sure to access the sample buffer by first aligning this + * pointer using fluid_align_ptr() + */ + fluid_real_t *left_buf; + + /** dito, but for right part of a stereo channel */ + fluid_real_t *right_buf; + + /** buffer to store the left part of a stereo effects channel to. + * Specifically a two dimensional array, containing \c fx_buf_count buffers + * (i.e. for each synth.effects-channels), of which each buffer contains + * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples) + */ + fluid_real_t *fx_left_buf; + fluid_real_t *fx_right_buf; +}; + +typedef struct _fluid_mixer_fx_t fluid_mixer_fx_t; + +struct _fluid_mixer_fx_t +{ + fluid_revmodel_t *reverb; /**< Reverb unit */ + /* reverb shadow parameters here will be returned if queried */ + double reverb_param[FLUID_REVERB_PARAM_LAST]; + int reverb_on; /* reverb on/off */ + + fluid_chorus_t *chorus; /**< Chorus unit */ + /* chorus shadow parameters here will be returned if queried */ + double chorus_param[FLUID_CHORUS_PARAM_LAST]; + int chorus_on; /* chorus on/off */ +}; + +struct _fluid_rvoice_mixer_t +{ + fluid_mixer_fx_t *fx; + + fluid_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */ + fluid_rvoice_eventhandler_t *eventhandler; + + fluid_rvoice_t **rvoices; /**< Read-only: Voices array, sorted so that all nulls are last */ + int polyphony; /**< Read-only: Length of voices array */ + int active_voices; /**< Read-only: Number of non-null voices */ + int current_blockcount; /**< Read-only: how many blocks to process this time */ + int fx_units; + int with_reverb; /**< Should the synth use the built-in reverb unit? */ + int with_chorus; /**< Should the synth use the built-in chorus unit? */ + int mix_fx_to_out; /**< Should the effects be mixed in with the primary output? */ + +#ifdef LADSPA + fluid_ladspa_fx_t *ladspa_fx; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */ +#endif + +#if ENABLE_MIXER_THREADS +// int sleeping_threads; /**< Atomic: number of threads currently asleep */ +// int active_threads; /**< Atomic: number of threads in the thread loop */ + fluid_atomic_int_t threads_should_terminate; /**< Atomic: Set to TRUE when threads should terminate */ + fluid_atomic_int_t current_rvoice; /**< Atomic: for the threads to know next voice to */ + fluid_cond_t *wakeup_threads; /**< Signalled when the threads should wake up */ + fluid_cond_mutex_t *wakeup_threads_m; /**< wakeup_threads mutex companion */ + fluid_cond_t *thread_ready; /**< Signalled from thread, when the thread has a buffer ready for mixing */ + fluid_cond_mutex_t *thread_ready_m; /**< thread_ready mutex companion */ + + int thread_count; /**< Number of extra mixer threads for multi-core rendering */ + fluid_mixer_buffers_t *threads; /**< Array of mixer threads (thread_count in length) */ +#endif +}; + +#if ENABLE_MIXER_THREADS +static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer); +static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level); +#endif + +static FLUID_INLINE void +fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcount) +{ + // Making those variables const causes gcc to fail with "variable is predetermined ‘shared’ for ‘shared’". + // Not explicitly marking them shared makes it fail for clang and MSVC... + /*const*/ int fx_channels_per_unit = mixer->buffers.fx_buf_count / mixer->fx_units; + /*const*/ int dry_count = mixer->buffers.buf_count; /* dry buffers count */ + /*const*/ int mix_fx_to_out = mixer->mix_fx_to_out; /* get mix_fx_to_out mode */ + + void (*reverb_process_func)(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); + void (*chorus_process_func)(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); + + fluid_real_t *out_rev_l, *out_rev_r, *out_ch_l, *out_ch_r; + + // all dry unprocessed mono input is stored in the left channel + fluid_real_t *in_rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *in_ch = in_rev; + + fluid_profile_ref_var(prof_ref); + +#ifdef LADSPA + + /* Run the signal through the LADSPA Fx unit. The buffers have already been + * set up in fluid_rvoice_mixer_set_ladspa. */ + if(mixer->ladspa_fx) + { + fluid_ladspa_run(mixer->ladspa_fx, current_blockcount, FLUID_BUFSIZE); + fluid_check_fpe("LADSPA"); + } + +#endif + + if(mix_fx_to_out) + { + // mix effects to first stereo channel + out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); + out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); + + reverb_process_func = fluid_revmodel_processmix; + chorus_process_func = fluid_chorus_processmix; + } + else + { + // replace effects into respective stereo effects channel + out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + + reverb_process_func = fluid_revmodel_processreplace; + chorus_process_func = fluid_chorus_processreplace; + } + + if(mixer->with_reverb || mixer->with_chorus) + { +#if ENABLE_MIXER_THREADS && !defined(WITH_PROFILING) + int fx_mixer_threads = mixer->fx_units; + fluid_clip(fx_mixer_threads, 1, mixer->thread_count + 1); + #pragma omp parallel default(none) shared(mixer, reverb_process_func, chorus_process_func, dry_count, current_blockcount, mix_fx_to_out, fx_channels_per_unit) firstprivate(in_rev, in_ch, out_rev_l, out_rev_r, out_ch_l, out_ch_r) num_threads(fx_mixer_threads) +#endif + { + int i, f; + int buf_idx; /* buffer index */ + int samp_idx; /* sample index in buffer */ + int dry_idx = 0; /* dry buffer index */ + int sample_count; /* sample count to process */ + if(mixer->with_reverb) + { +#if ENABLE_MIXER_THREADS && !defined(WITH_PROFILING) + #pragma omp for schedule(static) +#endif + for(f = 0; f < mixer->fx_units; f++) + { + if(!mixer->fx[f].reverb_on) + { + continue; /* this reverb unit is disabled */ + } + + buf_idx = f * fx_channels_per_unit + SYNTH_REVERB_CHANNEL; + samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + sample_count = current_blockcount * FLUID_BUFSIZE; + + /* in mix mode, map fx out_rev at index f to a dry buffer at index dry_idx */ + if(mix_fx_to_out) + { + /* dry buffer mapping, should be done more flexible in the future */ + dry_idx = (f % dry_count) * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + } + + for(i = 0; i < sample_count; i += FLUID_BUFSIZE, samp_idx += FLUID_BUFSIZE) + { + reverb_process_func(mixer->fx[f].reverb, + &in_rev[samp_idx], + mix_fx_to_out ? &out_rev_l[dry_idx + i] : &out_rev_l[samp_idx], + mix_fx_to_out ? &out_rev_r[dry_idx + i] : &out_rev_r[samp_idx]); + } + } // implicit omp barrier - required, because out_rev_l aliases with out_ch_l + + fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref, 0, + current_blockcount * FLUID_BUFSIZE); + } + + if(mixer->with_chorus) + { +#if ENABLE_MIXER_THREADS && !defined(WITH_PROFILING) + #pragma omp for schedule(static) +#endif + for(f = 0; f < mixer->fx_units; f++) + { + if(!mixer->fx[f].chorus_on) + { + continue; /* this chorus unit is disabled */ + } + + buf_idx = f * fx_channels_per_unit + SYNTH_CHORUS_CHANNEL; + samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + sample_count = current_blockcount * FLUID_BUFSIZE; + + /* in mix mode, map fx out_ch at index f to a dry buffer at index dry_idx */ + if(mix_fx_to_out) + { + /* dry buffer mapping, should be done more flexible in the future */ + dry_idx = (f % dry_count) * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + } + + for(i = 0; i < sample_count; i += FLUID_BUFSIZE, samp_idx += FLUID_BUFSIZE) + { + chorus_process_func(mixer->fx[f].chorus, + &in_ch [samp_idx], + mix_fx_to_out ? &out_ch_l[dry_idx + i] : &out_ch_l[samp_idx], + mix_fx_to_out ? &out_ch_r[dry_idx + i] : &out_ch_r[samp_idx]); + } + } + + fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref, 0, + current_blockcount * FLUID_BUFSIZE); + } + } + } +} + +/** + * Glue to get fluid_rvoice_buffers_mix what it wants + * Note: Make sure outbufs has 2 * (buf_count + fx_buf_count) elements before calling + */ +static FLUID_INLINE int +fluid_mixer_buffers_prepare(fluid_mixer_buffers_t *buffers, fluid_real_t **outbufs) +{ + fluid_real_t *base_ptr; + int i; + const int fx_channels_per_unit = buffers->fx_buf_count / buffers->mixer->fx_units; + const int offset = buffers->buf_count * 2; + int with_reverb = buffers->mixer->with_reverb; + int with_chorus = buffers->mixer->with_chorus; + + /* Set up the reverb and chorus buffers only when the effect is enabled or + * when LADSPA is active. Nonexisting buffers are detected in the DSP loop. + * Not sending the effect signals saves some time in that case. */ +#ifdef LADSPA + int with_ladspa = (buffers->mixer->ladspa_fx != NULL); + with_reverb = (with_reverb | with_ladspa); + with_chorus = (with_chorus | with_ladspa); +#endif + + // all the dry, non-processed mono audio for effects is to be stored in the left buffers + base_ptr = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < buffers->mixer->fx_units; i++) + { + int fx_idx = i * fx_channels_per_unit; + + outbufs[offset + fx_idx + SYNTH_REVERB_CHANNEL] = + (with_reverb) + ? &base_ptr[(fx_idx + SYNTH_REVERB_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] + : NULL; + + outbufs[offset + fx_idx + SYNTH_CHORUS_CHANNEL] = + (with_chorus) + ? &base_ptr[(fx_idx + SYNTH_CHORUS_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] + : NULL; + } + + /* The output associated with a MIDI channel is wrapped around + * using the number of audio groups as modulo divider. This is + * typically the number of output channels on the 'sound card', + * as long as the LADSPA Fx unit is not used. In case of LADSPA + * unit, think of it as subgroups on a mixer. + * + * For example: Assume that the number of groups is set to 2. + * Then MIDI channel 1, 3, 5, 7 etc. go to output 1, channels 2, + * 4, 6, 8 etc to output 2. Or assume 3 groups: Then MIDI + * channels 1, 4, 7, 10 etc go to output 1; 2, 5, 8, 11 etc to + * output 2, 3, 6, 9, 12 etc to output 3. + */ + base_ptr = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < buffers->buf_count; i++) + { + outbufs[i * 2] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + } + + base_ptr = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < buffers->buf_count; i++) + { + outbufs[i * 2 + 1] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + } + + return offset + buffers->fx_buf_count; +} + + +static FLUID_INLINE void +fluid_finish_rvoice(fluid_mixer_buffers_t *buffers, fluid_rvoice_t *rvoice) +{ + if(buffers->finished_voice_count < buffers->mixer->polyphony) + { + buffers->finished_voices[buffers->finished_voice_count++] = rvoice; + } + else + { + FLUID_LOG(FLUID_ERR, "Exceeded finished voices array, try increasing polyphony"); + } +} + +static void +fluid_mixer_buffer_process_finished_voices(fluid_mixer_buffers_t *buffers) +{ + int i, j; + + for(i = 0; i < buffers->finished_voice_count; i++) + { + fluid_rvoice_t *v = buffers->finished_voices[i]; + int av = buffers->mixer->active_voices; + + for(j = 0; j < av; j++) + { + if(v == buffers->mixer->rvoices[j]) + { + av--; + + /* Pack the array */ + if(j < av) + { + buffers->mixer->rvoices[j] = buffers->mixer->rvoices[av]; + } + } + } + + buffers->mixer->active_voices = av; + + fluid_rvoice_eventhandler_finished_voice_callback(buffers->mixer->eventhandler, v); + } + + buffers->finished_voice_count = 0; +} + +static FLUID_INLINE void fluid_rvoice_mixer_process_finished_voices(fluid_rvoice_mixer_t *mixer) +{ +#if ENABLE_MIXER_THREADS + int i; + + for(i = 0; i < mixer->thread_count; i++) + { + fluid_mixer_buffer_process_finished_voices(&mixer->threads[i]); + } + +#endif + fluid_mixer_buffer_process_finished_voices(&mixer->buffers); +} + + +static FLUID_INLINE fluid_real_t * +get_dest_buf(fluid_rvoice_buffers_t *buffers, int index, + fluid_real_t **dest_bufs, int dest_bufcount) +{ + int j = buffers->bufs[index].mapping; + + if(j >= dest_bufcount || j < 0) + { + return NULL; + } + + return dest_bufs[j]; +} + +/** + * Mix samples down from internal dsp_buf to output buffers + * + * @param buffers Destination buffer(s) + * @param dsp_buf Mono sample source + * @param start_block starting sample in dsp_buf + * @param sample_count number of samples to mix following \c start_block + * @param dest_bufs Array of buffers to mixdown to + * @param dest_bufcount Length of dest_bufs (i.e count of buffers) + */ +static void +fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers, + const fluid_real_t *FLUID_RESTRICT dsp_buf, + int start_block, int sample_count, + fluid_real_t **dest_bufs, int dest_bufcount) +{ + /* buffers count to mixdown to */ + int bufcount = buffers->count; + int i, dsp_i; + + /* if there is nothing to mix, return immediately */ + if(sample_count <= 0 || dest_bufcount <= 0) + { + return; + } + + FLUID_ASSERT((uintptr_t)dsp_buf % FLUID_DEFAULT_ALIGNMENT == 0); + FLUID_ASSERT((uintptr_t)(&dsp_buf[start_block * FLUID_BUFSIZE]) % FLUID_DEFAULT_ALIGNMENT == 0); + + /* mixdown for each buffer */ + for(i = 0; i < bufcount; i++) + { + fluid_real_t *FLUID_RESTRICT buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount); + fluid_real_t target_amp = buffers->bufs[i].target_amp; + fluid_real_t current_amp = buffers->bufs[i].current_amp; + fluid_real_t amp_incr; + + if(buf == NULL || (current_amp == 0.0f && target_amp == 0.0f)) + { + continue; + } + + amp_incr = (target_amp - current_amp) / FLUID_BUFSIZE; + + FLUID_ASSERT((uintptr_t)buf % FLUID_DEFAULT_ALIGNMENT == 0); + + /* Mixdown sample_count samples in the current buffer buf + * + * For the first FLUID_BUFSIZE samples, we linearly interpolate the buffers amplitude to + * avoid clicks/pops when rapidly changing the channels panning (issue 768). + * + * We could have squashed this into one single loop by using an if clause within the loop body. + * But it seems like having two separate loops is easier for compilers to understand, and therefore + * auto-vectorizing the loops. + */ + if(sample_count < FLUID_BUFSIZE) + { + // scalar loop variant, the voice will have finished afterwards + for(dsp_i = 0; dsp_i < sample_count; dsp_i++) + { + buf[start_block * FLUID_BUFSIZE + dsp_i] += current_amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; + current_amp += amp_incr; + } + } + else + { + // here goes the vectorizable loop + #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT) + for(dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i++) + { + // We cannot simply increment current_amp by amp_incr during every iteration, as this would create a dependency and prevent vectorization. + buf[start_block * FLUID_BUFSIZE + dsp_i] += (current_amp + amp_incr * dsp_i) * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; + } + + // we have reached the target_amp + if(target_amp > 0) + { + /* Note, that this loop could be unrolled by FLUID_BUFSIZE elements */ + #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT) + for(dsp_i = FLUID_BUFSIZE; dsp_i < sample_count; dsp_i++) + { + // Index by blocks (not by samples) to let the compiler know that we always start accessing + // buf and dsp_buf at the FLUID_BUFSIZE*sizeof(fluid_real_t) byte boundary and never somewhere + // in between. + // A good compiler should understand: Aha, so I don't need to add a peel loop when vectorizing + // this loop. Great. + buf[start_block * FLUID_BUFSIZE + dsp_i] += target_amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; + } + } + } + + buffers->bufs[i].current_amp = target_amp; + } +} + +/** + * Synthesize one voice and add to buffer. + * NOTE: If return value is less than blockcount*FLUID_BUFSIZE, that means + * voice has been finished, removed and possibly replaced with another voice. + */ +static FLUID_INLINE void +fluid_mixer_buffers_render_one(fluid_mixer_buffers_t *buffers, + fluid_rvoice_t *rvoice, fluid_real_t **dest_bufs, + unsigned int dest_bufcount, fluid_real_t *src_buf, int blockcount) +{ + int i, total_samples = 0, last_block_mixed = 0; + + for(i = 0; i < blockcount; i++) + { + /* render one block in src_buf */ + int s = fluid_rvoice_write(rvoice, &src_buf[FLUID_BUFSIZE * i]); + + if(s == -1) + { + /* the voice is silent, mix back all the previously rendered sound */ + fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed, + total_samples - (last_block_mixed * FLUID_BUFSIZE), + dest_bufs, dest_bufcount); + + last_block_mixed = i + 1; /* future block start index to mix from */ + total_samples += FLUID_BUFSIZE; /* accumulate samples count rendered */ + } + else + { + /* the voice wasn't quiet. Some samples have been rendered [0..FLUID_BUFSIZE] */ + total_samples += s; + + if(s < FLUID_BUFSIZE) + { + /* voice has finished */ + break; + } + } + } + + /* Now mix the remaining blocks from last_block_mixed to total_sample */ + fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed, + total_samples - (last_block_mixed * FLUID_BUFSIZE), + dest_bufs, dest_bufcount); + + if(total_samples < blockcount * FLUID_BUFSIZE) + { + /* voice has finished */ + fluid_finish_rvoice(buffers, rvoice); + } +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice) +{ + int i; + fluid_rvoice_mixer_t *mixer = obj; + fluid_rvoice_t *voice = param[0].ptr; + + if(mixer->active_voices < mixer->polyphony) + { + mixer->rvoices[mixer->active_voices++] = voice; + return; // success + } + + /* See if any voices just finished, if so, take its place. + This can happen in voice overflow conditions. */ + for(i = 0; i < mixer->active_voices; i++) + { + if(mixer->rvoices[i] == voice) + { + FLUID_LOG(FLUID_ERR, "Internal error: Trying to replace an existing rvoice in fluid_rvoice_mixer_add_voice?!"); + return; + } + + if(mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED) + { + fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]); + mixer->rvoices[i] = voice; + return; // success + } + } + + /* This should never happen */ + FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice"); +} + +static int +fluid_mixer_buffers_update_polyphony(fluid_mixer_buffers_t *buffers, int value) +{ + void *newptr; + + if(buffers->finished_voice_count > value) + { + return FLUID_FAILED; + } + + newptr = FLUID_REALLOC(buffers->finished_voices, value * sizeof(fluid_rvoice_t *)); + + if(newptr == NULL && value > 0) + { + return FLUID_FAILED; + } + + buffers->finished_voices = newptr; + return FLUID_OK; +} + +/** + * Update polyphony - max number of voices (NOTE: not hard real-time capable) + * @return FLUID_OK or FLUID_FAILED + */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony) +{ + void *newptr; + fluid_rvoice_mixer_t *handler = obj; + int value = param[0].i; + + if(handler->active_voices > value) + { + return /*FLUID_FAILED*/; + } + + newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t *)); + + if(newptr == NULL) + { + return /*FLUID_FAILED*/; + } + + handler->rvoices = newptr; + + if(fluid_mixer_buffers_update_polyphony(&handler->buffers, value) + == FLUID_FAILED) + { + return /*FLUID_FAILED*/; + } + +#if ENABLE_MIXER_THREADS + { + int i; + + for(i = 0; i < handler->thread_count; i++) + { + if(fluid_mixer_buffers_update_polyphony(&handler->threads[i], value) + == FLUID_FAILED) + { + return /*FLUID_FAILED*/; + } + } + } +#endif + + handler->polyphony = value; + /*return FLUID_OK*/; +} + + +static void +fluid_render_loop_singlethread(fluid_rvoice_mixer_t *mixer, int blockcount) +{ + int i; + FLUID_DECLARE_VLA(fluid_real_t *, bufs, + mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2); + int bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs); + + fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT); + + fluid_profile_ref_var(prof_ref); + + for(i = 0; i < mixer->active_voices; i++) + { + fluid_mixer_buffers_render_one(&mixer->buffers, mixer->rvoices[i], bufs, + bufcount, local_buf, blockcount); + fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1, + blockcount * FLUID_BUFSIZE); + } +} + +static FLUID_INLINE void +fluid_mixer_buffers_zero(fluid_mixer_buffers_t *buffers, int current_blockcount) +{ + int i, size = current_blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t); + + /* TODO: Optimize by only zero out the buffers we actually use later on. */ + int buf_count = buffers->buf_count, fx_buf_count = buffers->fx_buf_count; + + fluid_real_t *FLUID_RESTRICT buf_l = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *FLUID_RESTRICT buf_r = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < buf_count; i++) + { + FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + } + + buf_l = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + buf_r = fluid_align_ptr(buffers->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < fx_buf_count; i++) + { + FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + } +} + +static int +fluid_mixer_buffers_init(fluid_mixer_buffers_t *buffers, fluid_rvoice_mixer_t *mixer) +{ + static const int samplecount = FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT; + + buffers->mixer = mixer; + buffers->buf_count = mixer->buffers.buf_count; + buffers->fx_buf_count = mixer->buffers.fx_buf_count; + + /* Local mono voice buf */ + buffers->local_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, samplecount, FLUID_DEFAULT_ALIGNMENT); + + /* Left and right audio buffers */ + + buffers->left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); + buffers->right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); + + if((buffers->local_buf == NULL) || (buffers->left_buf == NULL) || (buffers->right_buf == NULL)) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return 0; + } + + /* Effects audio buffers */ + + buffers->fx_left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); + buffers->fx_right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); + + if((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL)) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return 0; + } + + buffers->finished_voices = NULL; + + if(fluid_mixer_buffers_update_polyphony(buffers, mixer->polyphony) + == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return 0; + } + + return 1; +} + +/** + * Note: Not hard real-time capable (calls malloc) + */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate) +{ + fluid_rvoice_mixer_t *mixer = obj; + fluid_real_t samplerate = param[1].real; // because fluid_synth_update_mixer() puts real into arg2 + + int i; + + for(i = 0; i < mixer->fx_units; i++) + { + if(mixer->fx[i].chorus) + { + fluid_chorus_samplerate_change(mixer->fx[i].chorus, samplerate); + } + + if(mixer->fx[i].reverb) + { + fluid_revmodel_samplerate_change(mixer->fx[i].reverb, samplerate); + + /* + fluid_revmodel_samplerate_change() shouldn't fail if the reverb was created + with sample_rate_max set to the maximum sample rate indicated in the settings. + If this condition isn't respected, the reverb will continue to work but with + lost of quality. + */ + } + } + +#if LADSPA + + if(mixer->ladspa_fx != NULL) + { + fluid_ladspa_set_sample_rate(mixer->ladspa_fx, samplerate); + } + +#endif +} + + +/** + * @param buf_count number of primary stereo buffers + * @param fx_buf_count number of stereo effect buffers + */ +fluid_rvoice_mixer_t * +new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, + fluid_real_t sample_rate_max, + fluid_real_t sample_rate, + fluid_rvoice_eventhandler_t *evthandler, + int extra_threads, int prio) +{ + int i; + fluid_rvoice_mixer_t *mixer = FLUID_NEW(fluid_rvoice_mixer_t); + + if(mixer == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(mixer, 0, sizeof(fluid_rvoice_mixer_t)); + mixer->eventhandler = evthandler; + mixer->fx_units = fx_units; + mixer->buffers.buf_count = buf_count; + mixer->buffers.fx_buf_count = fx_buf_count * fx_units; + + /* allocate the reverb module */ + mixer->fx = FLUID_ARRAY(fluid_mixer_fx_t, fx_units); + + if(mixer->fx == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + FLUID_MEMSET(mixer->fx, 0, fx_units * sizeof(*mixer->fx)); + + for(i = 0; i < fx_units; i++) + { + /* create reverb and chorus units */ + mixer->fx[i].reverb = new_fluid_revmodel(sample_rate_max, sample_rate); + mixer->fx[i].chorus = new_fluid_chorus(sample_rate); + + if(mixer->fx[i].reverb == NULL || mixer->fx[i].chorus == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + } + + if(!fluid_mixer_buffers_init(&mixer->buffers, mixer)) + { + goto error_recovery; + } + +#if ENABLE_MIXER_THREADS + mixer->thread_ready = new_fluid_cond(); + mixer->wakeup_threads = new_fluid_cond(); + mixer->thread_ready_m = new_fluid_cond_mutex(); + mixer->wakeup_threads_m = new_fluid_cond_mutex(); + + if(!mixer->thread_ready || !mixer->wakeup_threads || + !mixer->thread_ready_m || !mixer->wakeup_threads_m) + { + goto error_recovery; + } + + if(fluid_rvoice_mixer_set_threads(mixer, extra_threads, prio) != FLUID_OK) + { + goto error_recovery; + } + +#endif + + return mixer; + +error_recovery: + delete_fluid_rvoice_mixer(mixer); + return NULL; +} + +static void +fluid_mixer_buffers_free(fluid_mixer_buffers_t *buffers) +{ + FLUID_FREE(buffers->finished_voices); + + /* free all the sample buffers */ + FLUID_FREE(buffers->local_buf); + FLUID_FREE(buffers->left_buf); + FLUID_FREE(buffers->right_buf); + FLUID_FREE(buffers->fx_left_buf); + FLUID_FREE(buffers->fx_right_buf); +} + +void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer) +{ + int i; + + fluid_return_if_fail(mixer != NULL); + +#if ENABLE_MIXER_THREADS + delete_rvoice_mixer_threads(mixer); + + if(mixer->thread_ready) + { + delete_fluid_cond(mixer->thread_ready); + } + + if(mixer->wakeup_threads) + { + delete_fluid_cond(mixer->wakeup_threads); + } + + if(mixer->thread_ready_m) + { + delete_fluid_cond_mutex(mixer->thread_ready_m); + } + + if(mixer->wakeup_threads_m) + { + delete_fluid_cond_mutex(mixer->wakeup_threads_m); + } + +#endif + fluid_mixer_buffers_free(&mixer->buffers); + + + for(i = 0; i < mixer->fx_units; i++) + { + if(mixer->fx[i].reverb) + { + delete_fluid_revmodel(mixer->fx[i].reverb); + } + + if(mixer->fx[i].chorus) + { + delete_fluid_chorus(mixer->fx[i].chorus); + } + } + + FLUID_FREE(mixer->fx); + FLUID_FREE(mixer->rvoices); + FLUID_FREE(mixer); +} + +#ifdef LADSPA +/** + * Set a LADSPS fx instance to be used by the mixer and assign the mixer buffers + * as LADSPA host buffers with sensible names */ +void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer, + fluid_ladspa_fx_t *ladspa_fx, int audio_groups) +{ + mixer->ladspa_fx = ladspa_fx; + + if(ladspa_fx == NULL) + { + return; + } + else + { + fluid_real_t *main_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *main_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); + + fluid_real_t *rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *chor = rev; + + rev = &rev[SYNTH_REVERB_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + chor = &chor[SYNTH_CHORUS_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + + fluid_ladspa_add_host_ports(ladspa_fx, "Main:L", audio_groups, + main_l, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + + fluid_ladspa_add_host_ports(ladspa_fx, "Main:R", audio_groups, + main_r, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + + fluid_ladspa_add_host_ports(ladspa_fx, "Reverb:Send", 1, + rev, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + + fluid_ladspa_add_host_ports(ladspa_fx, "Chorus:Send", 1, + chor, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + } +} +#endif + +/** + * set one or more reverb shadow parameters for one fx group. + * These parameters will be returned if queried. + * (see fluid_rvoice_mixer_reverb_get_param()) + * + * @param mixer that contains all fx units. + * @param fx_group index of the fx group to which parameters must be set. + * must be in the range [-1..mixer->fx_units[. If -1 the changes are applied to + * all fx units. + * @param set Flags indicating which parameters should be set (#fluid_revmodel_set_t) + * @param values table of parameters values. + */ +void +fluid_rvoice_mixer_set_reverb_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]) +{ + fluid_mixer_fx_t *fx = mixer->fx; + int nr_units = mixer->fx_units; + + if(fx_group >= 0) /* apply parameters to this fx group only */ + { + nr_units = fx_group + 1; + } + else /* apply parameters to all fx groups */ + { + fx_group = 0; + } + + for(; fx_group < nr_units; fx_group++) + { + int param; + + for(param = 0; param < FLUID_REVERB_PARAM_LAST; param++) + { + if(set & FLUID_REVPARAM_TO_SETFLAG(param)) + { + fx[fx_group].reverb_param[param] = values[param]; + } + } + } +} + +/** + * get one reverb shadow parameter for one fx group. + * (see fluid_rvoice_mixer_set_reverb_full()) + * + * @param mixer that contains all fx group units. + * @param fx_group index of the fx group to get parameter from. + * must be in the range [0..mixer->fx_units[. + * @param enum indicating the parameter to get. + * FLUID_REVERB_ROOMSIZE, reverb room size value. + * FLUID_REVERB_DAMP, reverb damping value. + * FLUID_REVERB_WIDTH, reverb width value. + * FLUID_REVERB_LEVEL, reverb level value. + * @return value. + */ +double +fluid_rvoice_mixer_reverb_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param) +{ + return mixer->fx[fx_group].reverb_param[param]; +} + +/** + * set one or more chorus shadow parameters for one fx group. + * These parameters will be returned if queried. + * (see fluid_rvoice_mixer_chorus_get_param()) + * + * @param mixer that contains all fx units. + * @param fx_group index of the fx group to which parameters must be set. + * must be in the range [-1..mixer->fx_units[. If -1 the changes are applied + * to all fx group. + * Keep in mind, that the needed CPU time is proportional to 'nr'. + * @param set Flags indicating which parameters to set (#fluid_chorus_set_t) + * @param values table of pararameters. + */ +void +fluid_rvoice_mixer_set_chorus_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]) +{ + fluid_mixer_fx_t *fx = mixer->fx; + int nr_units = mixer->fx_units; + + if(fx_group >= 0) /* apply parameters to this group fx only */ + { + nr_units = fx_group + 1; + } + else /* apply parameters to all fx units*/ + { + fx_group = 0; + } + + for(; fx_group < nr_units; fx_group++) + { + int param; + + for(param = 0; param < FLUID_CHORUS_PARAM_LAST; param++) + { + if(set & FLUID_CHORPARAM_TO_SETFLAG(param)) + { + fx[fx_group].chorus_param[param] = values[param]; + } + } + } +} + +/** + * get one chorus shadow parameter for one fx group. + * (see fluid_rvoice_mixer_set_chorus_full()) + * + * @param mixer that contains all fx groups units. + * @param fx_group index of the fx group to get parameter from. + * must be in the range [0..mixer->fx_units[. + * @param get Flags indicating which parameter to get (#fluid_chorus_set_t) + * @return the parameter value (0.0 is returned if error) + */ +double +fluid_rvoice_mixer_chorus_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param) +{ + return mixer->fx[fx_group].chorus_param[param]; +} + +/* @deprecated: use fluid_rvoice_mixer_reverb_enable instead */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled) +{ + fluid_rvoice_mixer_t *mixer = obj; + int on = param[0].i; + + mixer->with_reverb = on; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reverb_enable) +{ + fluid_rvoice_mixer_t *mixer = obj; + int fx_group = param[0].i; /* reverb fx group index */ + int on = param[1].i; /* on/off */ + + int nr_units = mixer->fx_units; + + /* does on/off must be applied only to fx group at index fx_group ? */ + if(fx_group >= 0) + { + mixer->fx[fx_group].reverb_on = on; + } + /* on/off must be applied to all fx groups */ + else + { + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + mixer->fx[fx_group].reverb_on = on; + } + } + + /* set with_reverb if at least one reverb unit is on */ + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + on = mixer->fx[fx_group].reverb_on; + + if(on) + { + break; + } + } + + mixer->with_reverb = on; +} + +/* @deprecated: use fluid_rvoice_mixer_chorus_enable instead */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled) +{ + fluid_rvoice_mixer_t *mixer = obj; + int on = param[0].i; + mixer->with_chorus = on; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_chorus_enable) +{ + fluid_rvoice_mixer_t *mixer = obj; + int fx_group = param[0].i; /* chorus fx group index */ + int on = param[1].i; /* on/off */ + + int nr_units = mixer->fx_units; + + /* does on/off must be applied only to fx group at index fx_group ? */ + if(fx_group >= 0) + { + mixer->fx[fx_group].chorus_on = on; + } + /* on/off must be applied to all fx groups */ + else + { + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + mixer->fx[fx_group].chorus_on = on; + } + } + + /* set with_chorus if at least one chorus unit is on */ + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + on = mixer->fx[fx_group].chorus_on; + + if(on) + { + break; + } + } + + mixer->with_chorus = on; +} + +void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on) +{ + mixer->mix_fx_to_out = on; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params) +{ + fluid_rvoice_mixer_t *mixer = obj; + int i = param[0].i; + int set = param[1].i; + int nr = param[2].i; + fluid_real_t level = param[3].real; + fluid_real_t speed = param[4].real; + fluid_real_t depth_ms = param[5].real; + int type = param[6].i; + + int nr_units = mixer->fx_units; + + /* does parameters must be applied only to fx group i ? */ + if(i >= 0) + { + nr_units = i + 1; + } + else + { + i = 0; /* parameters must be applied to all fx groups */ + } + + while(i < nr_units) + { + fluid_chorus_set(mixer->fx[i++].chorus, set, nr, level, speed, depth_ms, type); + } +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params) +{ + fluid_rvoice_mixer_t *mixer = obj; + int i = param[0].i; /* fx group index */ + int set = param[1].i; + fluid_real_t roomsize = param[2].real; + fluid_real_t damping = param[3].real; + fluid_real_t width = param[4].real; + fluid_real_t level = param[5].real; + + int nr_units = mixer->fx_units; + + /* does parameters change should be applied only to fx group i ? */ + if(i >= 0) + { + nr_units = i + 1; /* parameters change must be applied to fx groups i */ + } + else + { + i = 0; /* parameters change must be applied to all fx groups */ + } + + while(i < nr_units) + { + fluid_revmodel_set(mixer->fx[i++].reverb, set, roomsize, damping, width, level); + } +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb) +{ + fluid_rvoice_mixer_t *mixer = obj; + int i; + + for(i = 0; i < mixer->fx_units; i++) + { + fluid_revmodel_reset(mixer->fx[i].reverb); + } +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus) +{ + fluid_rvoice_mixer_t *mixer = obj; + int i; + + for(i = 0; i < mixer->fx_units; i++) + { + fluid_chorus_reset(mixer->fx[i].chorus); + } +} + +int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **left, fluid_real_t **right) +{ + *left = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); + *right = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); + return mixer->buffers.buf_count; +} + +int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **fx_left, fluid_real_t **fx_right) +{ + *fx_left = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + *fx_right = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + return mixer->buffers.fx_buf_count; +} + +int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer) +{ + return FLUID_MIXER_MAX_BUFFERS_DEFAULT; +} + +#if WITH_PROFILING +int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer) +{ + return mixer->active_voices; +} +#endif + +#if ENABLE_MIXER_THREADS + +static FLUID_INLINE fluid_rvoice_t * +fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t *mixer) +{ + int i = fluid_atomic_int_exchange_and_add(&mixer->current_rvoice, 1); + + if(i >= mixer->active_voices) + { + return NULL; + } + + return mixer->rvoices[i]; +} + +#define THREAD_BUF_PROCESSING 0 +#define THREAD_BUF_VALID 1 +#define THREAD_BUF_NODATA 2 +#define THREAD_BUF_TERMINATE 3 + +/* Core thread function (processes voices in parallel to primary synthesis thread) */ +static fluid_thread_return_t +fluid_mixer_thread_func(void *data) +{ + fluid_mixer_buffers_t *buffers = data; + fluid_rvoice_mixer_t *mixer = buffers->mixer; + int hasValidData = 0; + FLUID_DECLARE_VLA(fluid_real_t *, bufs, buffers->buf_count * 2 + buffers->fx_buf_count * 2); + int bufcount = 0; + int current_blockcount = 0; + fluid_real_t *local_buf = fluid_align_ptr(buffers->local_buf, FLUID_DEFAULT_ALIGNMENT); + + while(!fluid_atomic_int_get(&mixer->threads_should_terminate)) + { + fluid_rvoice_t *rvoice = fluid_mixer_get_mt_rvoice(mixer); + + if(rvoice == NULL) + { + // if no voices: signal rendered buffers, sleep + fluid_atomic_int_set(&buffers->ready, hasValidData ? THREAD_BUF_VALID : THREAD_BUF_NODATA); + fluid_cond_mutex_lock(mixer->thread_ready_m); + fluid_cond_signal(mixer->thread_ready); + fluid_cond_mutex_unlock(mixer->thread_ready_m); + + fluid_cond_mutex_lock(mixer->wakeup_threads_m); + + while(1) + { + int j = fluid_atomic_int_get(&buffers->ready); + + if(j == THREAD_BUF_PROCESSING || j == THREAD_BUF_TERMINATE) + { + break; + } + + fluid_cond_wait(mixer->wakeup_threads, mixer->wakeup_threads_m); + } + + fluid_cond_mutex_unlock(mixer->wakeup_threads_m); + + hasValidData = 0; + } + else + { + // else: if buffer is not zeroed, zero buffers + if(!hasValidData) + { + // blockcount may have changed, since thread was put to sleep + current_blockcount = mixer->current_blockcount; + fluid_mixer_buffers_zero(buffers, current_blockcount); + bufcount = fluid_mixer_buffers_prepare(buffers, bufs); + hasValidData = 1; + } + + // then render voice to buffers + fluid_mixer_buffers_render_one(buffers, rvoice, bufs, bufcount, local_buf, current_blockcount); + } + } + + return FLUID_THREAD_RETURN_VALUE; +} + +static void +fluid_mixer_buffers_mix(fluid_mixer_buffers_t *dst, fluid_mixer_buffers_t *src, int current_blockcount) +{ + int i, j; + int scount = current_blockcount * FLUID_BUFSIZE; + int minbuf; + fluid_real_t *FLUID_RESTRICT base_src; + fluid_real_t *FLUID_RESTRICT base_dst; + + minbuf = dst->buf_count; + + if(minbuf > src->buf_count) + { + minbuf = src->buf_count; + } + + base_src = fluid_align_ptr(src->left_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } + } + + base_src = fluid_align_ptr(src->right_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } + } + + minbuf = dst->fx_buf_count; + + if(minbuf > src->fx_buf_count) + { + minbuf = src->fx_buf_count; + } + + base_src = fluid_align_ptr(src->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } + } + + base_src = fluid_align_ptr(src->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } + } +} + + +/** + * Go through all threads and see if someone is finished for mixing + */ +static int +fluid_mixer_mix_in(fluid_rvoice_mixer_t *mixer, int extra_threads, int current_blockcount) +{ + int i, result, hasmixed; + + do + { + hasmixed = 0; + result = 0; + + for(i = 0; i < extra_threads; i++) + { + int j = fluid_atomic_int_get(&mixer->threads[i].ready); + + switch(j) + { + case THREAD_BUF_PROCESSING: + result = 1; + break; + + case THREAD_BUF_VALID: + fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_NODATA); + fluid_mixer_buffers_mix(&mixer->buffers, &mixer->threads[i], current_blockcount); + hasmixed = 1; + break; + } + } + } + while(hasmixed); + + return result; +} + +static void +fluid_render_loop_multithread(fluid_rvoice_mixer_t *mixer, int current_blockcount) +{ + int i, bufcount; + fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT); + + FLUID_DECLARE_VLA(fluid_real_t *, bufs, + mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2); + // How many threads should we start this time? + int extra_threads = mixer->active_voices / VOICES_PER_THREAD; + + if(extra_threads > mixer->thread_count) + { + extra_threads = mixer->thread_count; + } + + if(extra_threads == 0) + { + // No extra threads? No thread overhead! + fluid_render_loop_singlethread(mixer, current_blockcount); + return; + } + + bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs); + + // Prepare voice list + fluid_cond_mutex_lock(mixer->wakeup_threads_m); + fluid_atomic_int_set(&mixer->current_rvoice, 0); + + for(i = 0; i < extra_threads; i++) + { + fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_PROCESSING); + } + + // Signal threads to wake up + fluid_cond_broadcast(mixer->wakeup_threads); + fluid_cond_mutex_unlock(mixer->wakeup_threads_m); + + // If thread is finished, mix it in + while(fluid_mixer_mix_in(mixer, extra_threads, current_blockcount)) + { + // Otherwise get a voice and render it + fluid_rvoice_t *rvoice = fluid_mixer_get_mt_rvoice(mixer); + + if(rvoice != NULL) + { + fluid_profile_ref_var(prof_ref); + fluid_mixer_buffers_render_one(&mixer->buffers, rvoice, bufs, bufcount, local_buf, current_blockcount); + fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1, + current_blockcount * FLUID_BUFSIZE); + //test++; + } + else + { + // If no voices, wait for mixes. Make sure one is still processing to avoid deadlock + int is_processing = 0; + //waits++; + fluid_cond_mutex_lock(mixer->thread_ready_m); + + for(i = 0; i < extra_threads; i++) + { + if(fluid_atomic_int_get(&mixer->threads[i].ready) == + THREAD_BUF_PROCESSING) + { + is_processing = 1; + } + } + + if(is_processing) + { + fluid_cond_wait(mixer->thread_ready, mixer->thread_ready_m); + } + + fluid_cond_mutex_unlock(mixer->thread_ready_m); + } + } + + //FLUID_LOG(FLUID_DBG, "Blockcount: %d, mixed %d of %d voices myself, waits = %d", + // current_blockcount, test, mixer->active_voices, waits); +} + +static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer) +{ + int i; + + // if no threads have been created yet (e.g. because a previous error prevented creation of threads + // mutexes and condition variables), skip terminating threads + if(mixer->thread_count != 0) + { + fluid_atomic_int_set(&mixer->threads_should_terminate, 1); + // Signal threads to wake up + fluid_cond_mutex_lock(mixer->wakeup_threads_m); + + for(i = 0; i < mixer->thread_count; i++) + { + fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE); + } + + fluid_cond_broadcast(mixer->wakeup_threads); + fluid_cond_mutex_unlock(mixer->wakeup_threads_m); + + for(i = 0; i < mixer->thread_count; i++) + { + if(mixer->threads[i].thread) + { + fluid_thread_join(mixer->threads[i].thread); + delete_fluid_thread(mixer->threads[i].thread); + } + + fluid_mixer_buffers_free(&mixer->threads[i]); + } + } + + FLUID_FREE(mixer->threads); + mixer->thread_count = 0; + mixer->threads = NULL; +} + +/** + * Update amount of extra mixer threads. + * @param thread_count Number of extra mixer threads for multi-core rendering + * @param prio_level real-time prio level for the extra mixer threads + */ +static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level) +{ + char name[16]; + int i; + + // Kill all existing threads first + if(mixer->thread_count) + { + delete_rvoice_mixer_threads(mixer); + } + + if(thread_count == 0) + { + return FLUID_OK; + } + + // Now prepare the new threads + fluid_atomic_int_set(&mixer->threads_should_terminate, 0); + mixer->threads = FLUID_ARRAY(fluid_mixer_buffers_t, thread_count); + + if(mixer->threads == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + FLUID_MEMSET(mixer->threads, 0, thread_count * sizeof(fluid_mixer_buffers_t)); + mixer->thread_count = thread_count; + + for(i = 0; i < thread_count; i++) + { + fluid_mixer_buffers_t *b = &mixer->threads[i]; + + if(!fluid_mixer_buffers_init(b, mixer)) + { + return FLUID_FAILED; + } + + fluid_atomic_int_set(&b->ready, THREAD_BUF_NODATA); + FLUID_SNPRINTF(name, sizeof(name), "mixer%d", i); + b->thread = new_fluid_thread(name, fluid_mixer_thread_func, b, prio_level, 0); + + if(!b->thread) + { + return FLUID_FAILED; + } + } + + return FLUID_OK; +} +#endif + +/** + * Synthesize audio into buffers + * @param blockcount number of blocks to render, each having FLUID_BUFSIZE samples + * @return number of blocks rendered + */ +int +fluid_rvoice_mixer_render(fluid_rvoice_mixer_t *mixer, int blockcount) +{ + fluid_profile_ref_var(prof_ref); + + mixer->current_blockcount = blockcount; + + // Zero buffers + fluid_mixer_buffers_zero(&mixer->buffers, blockcount); + fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref, mixer->active_voices, + blockcount * FLUID_BUFSIZE); + +#if ENABLE_MIXER_THREADS + + if(mixer->thread_count > 0) + { + fluid_render_loop_multithread(mixer, blockcount); + } + else +#endif + { + fluid_render_loop_singlethread(mixer, blockcount); + } + + fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref, mixer->active_voices, + blockcount * FLUID_BUFSIZE); + + + // Process reverb & chorus + fluid_rvoice_mixer_process_fx(mixer, blockcount); + + // Call the callback and pack active voice array + fluid_rvoice_mixer_process_finished_voices(mixer); + + return blockcount; +} diff --git a/libs/fluidsynth/src/rvoice/fluid_rvoice_mixer.h b/libs/fluidsynth/src/rvoice/fluid_rvoice_mixer.h new file mode 100644 index 00000000000..afedc16a789 --- /dev/null +++ b/libs/fluidsynth/src/rvoice/fluid_rvoice_mixer.h @@ -0,0 +1,87 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_RVOICE_MIXER_H +#define _FLUID_RVOICE_MIXER_H + +#include "fluidsynth_priv.h" +#include "fluid_rvoice.h" +#include "fluid_ladspa.h" + +typedef struct _fluid_rvoice_mixer_t fluid_rvoice_mixer_t; + +int fluid_rvoice_mixer_render(fluid_rvoice_mixer_t *mixer, int blockcount); +int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **left, fluid_real_t **right); +int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **fx_left, fluid_real_t **fx_right); +int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer); +#if WITH_PROFILING +int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer); +#endif +fluid_rvoice_mixer_t *new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, + fluid_real_t sample_rate_max, fluid_real_t sample_rate, + fluid_rvoice_eventhandler_t *, int, int); + +void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *); + +void +fluid_rvoice_mixer_set_reverb_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]); + +double +fluid_rvoice_mixer_reverb_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param); +void +fluid_rvoice_mixer_set_chorus_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]); +double +fluid_rvoice_mixer_chorus_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param); + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony); + +/* @deprecated */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled); +/* @deprecated */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reverb_enable); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_chorus_enable); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus); + + + +void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on); +#ifdef LADSPA +void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer, + fluid_ladspa_fx_t *ladspa_fx, int audio_groups); +#endif + +#endif diff --git a/libs/fluidsynth/src/sfloader/fluid_defsfont.c b/libs/fluidsynth/src/sfloader/fluid_defsfont.c new file mode 100644 index 00000000000..c1a5917940c --- /dev/null +++ b/libs/fluidsynth/src/sfloader/fluid_defsfont.c @@ -0,0 +1,2372 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont file loading code borrowed from Smurf SoundFont Editor + * Copyright (C) 1999-2001 Josh Green + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#include "fluid_defsfont.h" +#include "fluid_sfont.h" +#include "fluid_sys.h" +#include "fluid_synth.h" +#include "fluid_samplecache.h" +#include "fluid_chan.h" + +/* EMU8k/10k hardware applies this factor to initial attenuation generator values set at preset and + * instrument level in a soundfont. We apply this factor when loading the generator values to stay + * compatible as most existing soundfonts expect exactly this (strange, non-standard) behaviour. */ +#define EMU_ATTENUATION_FACTOR (0.4f) + +/* Dynamic sample loading functions */ +static int pin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); +static int unpin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); +static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); +static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); +static void unload_sample(fluid_sample_t *sample); +static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan); +static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason); +static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone); +static fluid_inst_t *find_inst_by_idx(fluid_defsfont_t *defsfont, int idx); + + +/*************************************************************** + * + * SFONT LOADER + */ + +/** + * Creates a default soundfont2 loader that can be used with fluid_synth_add_sfloader(). + * By default every synth instance has an initial default soundfont loader instance. + * Calling this function is usually only necessary to load a soundfont from memory, by providing custom callback functions via fluid_sfloader_set_callbacks(). + * + * @param settings A settings instance obtained by new_fluid_settings() + * @return A default soundfont2 loader struct + */ +fluid_sfloader_t *new_fluid_defsfloader(fluid_settings_t *settings) +{ + fluid_sfloader_t *loader; + fluid_return_val_if_fail(settings != NULL, NULL); + + loader = new_fluid_sfloader(fluid_defsfloader_load, delete_fluid_sfloader); + + if(loader == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + fluid_sfloader_set_data(loader, settings); + + return loader; +} + +fluid_sfont_t *fluid_defsfloader_load(fluid_sfloader_t *loader, const char *filename) +{ + fluid_defsfont_t *defsfont; + fluid_sfont_t *sfont; + + defsfont = new_fluid_defsfont(fluid_sfloader_get_data(loader)); + + if(defsfont == NULL) + { + return NULL; + } + + sfont = new_fluid_sfont(fluid_defsfont_sfont_get_name, + fluid_defsfont_sfont_get_preset, + fluid_defsfont_sfont_iteration_start, + fluid_defsfont_sfont_iteration_next, + fluid_defsfont_sfont_delete); + + if(sfont == NULL) + { + delete_fluid_defsfont(defsfont); + return NULL; + } + + fluid_sfont_set_data(sfont, defsfont); + + defsfont->sfont = sfont; + + if(fluid_defsfont_load(defsfont, &loader->file_callbacks, filename) == FLUID_FAILED) + { + fluid_defsfont_sfont_delete(sfont); + return NULL; + } + + return sfont; +} + + + +/*************************************************************** + * + * PUBLIC INTERFACE + */ + +int fluid_defsfont_sfont_delete(fluid_sfont_t *sfont) +{ + if(delete_fluid_defsfont(fluid_sfont_get_data(sfont)) != FLUID_OK) + { + return -1; + } + + delete_fluid_sfont(sfont); + return 0; +} + +const char *fluid_defsfont_sfont_get_name(fluid_sfont_t *sfont) +{ + return fluid_defsfont_get_name(fluid_sfont_get_data(sfont)); +} + +fluid_preset_t * +fluid_defsfont_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum) +{ + return fluid_defsfont_get_preset(fluid_sfont_get_data(sfont), bank, prenum); +} + +void fluid_defsfont_sfont_iteration_start(fluid_sfont_t *sfont) +{ + fluid_defsfont_iteration_start(fluid_sfont_get_data(sfont)); +} + +fluid_preset_t *fluid_defsfont_sfont_iteration_next(fluid_sfont_t *sfont) +{ + return fluid_defsfont_iteration_next(fluid_sfont_get_data(sfont)); +} + +void fluid_defpreset_preset_delete(fluid_preset_t *preset) +{ + fluid_defsfont_t *defsfont; + fluid_defpreset_t *defpreset; + + defsfont = fluid_sfont_get_data(preset->sfont); + defpreset = fluid_preset_get_data(preset); + + if(defsfont) + { + defsfont->preset = fluid_list_remove(defsfont->preset, defpreset); + } + + delete_fluid_defpreset(defpreset); + delete_fluid_preset(preset); +} + +const char *fluid_defpreset_preset_get_name(fluid_preset_t *preset) +{ + return fluid_defpreset_get_name(fluid_preset_get_data(preset)); +} + +int fluid_defpreset_preset_get_banknum(fluid_preset_t *preset) +{ + return fluid_defpreset_get_banknum(fluid_preset_get_data(preset)); +} + +int fluid_defpreset_preset_get_num(fluid_preset_t *preset) +{ + return fluid_defpreset_get_num(fluid_preset_get_data(preset)); +} + +int fluid_defpreset_preset_noteon(fluid_preset_t *preset, fluid_synth_t *synth, + int chan, int key, int vel) +{ + return fluid_defpreset_noteon(fluid_preset_get_data(preset), synth, chan, key, vel); +} + + +/*************************************************************** + * + * SFONT + */ + +/* + * new_fluid_defsfont + */ +fluid_defsfont_t *new_fluid_defsfont(fluid_settings_t *settings) +{ + fluid_defsfont_t *defsfont; + + defsfont = FLUID_NEW(fluid_defsfont_t); + + if(defsfont == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(defsfont, 0, sizeof(*defsfont)); + + fluid_settings_getint(settings, "synth.lock-memory", &defsfont->mlock); + fluid_settings_getint(settings, "synth.dynamic-sample-loading", &defsfont->dynamic_samples); + + return defsfont; +} + +/* + * delete_fluid_defsfont + */ +int delete_fluid_defsfont(fluid_defsfont_t *defsfont) +{ + fluid_list_t *list; + fluid_preset_t *preset; + fluid_sample_t *sample; + + fluid_return_val_if_fail(defsfont != NULL, FLUID_OK); + + /* If we use dynamic sample loading, make sure we unpin any + * pinned presets before removing this soundfont */ + if(defsfont->dynamic_samples) + { + for(list = defsfont->preset; list; list = fluid_list_next(list)) + { + preset = (fluid_preset_t *)fluid_list_get(list); + unpin_preset_samples(defsfont, preset); + } + } + + /* Check that no samples are currently used */ + for(list = defsfont->sample; list; list = fluid_list_next(list)) + { + sample = (fluid_sample_t *) fluid_list_get(list); + + if(sample->refcount != 0) + { + return FLUID_FAILED; + } + } + + if(defsfont->filename != NULL) + { + FLUID_FREE(defsfont->filename); + } + + for(list = defsfont->sample; list; list = fluid_list_next(list)) + { + sample = (fluid_sample_t *) fluid_list_get(list); + + /* If the sample data pointer is different to the sampledata chunk of + * the soundfont, then the sample has been loaded individually (SF3) + * and needs to be unloaded explicitly. This is safe even if using + * dynamic sample loading, as the sample_unload mechanism sets + * sample->data to NULL after unload. */ + if ((sample->data != NULL) && (sample->data != defsfont->sampledata)) + { + fluid_samplecache_unload(sample->data); + } + delete_fluid_sample(sample); + } + + if(defsfont->sample) + { + delete_fluid_list(defsfont->sample); + } + + if(defsfont->sampledata != NULL) + { + fluid_samplecache_unload(defsfont->sampledata); + } + + for(list = defsfont->preset; list; list = fluid_list_next(list)) + { + preset = (fluid_preset_t *)fluid_list_get(list); + fluid_defpreset_preset_delete(preset); + } + + delete_fluid_list(defsfont->preset); + + for(list = defsfont->inst; list; list = fluid_list_next(list)) + { + delete_fluid_inst(fluid_list_get(list)); + } + + delete_fluid_list(defsfont->inst); + + FLUID_FREE(defsfont); + return FLUID_OK; +} + +/* + * fluid_defsfont_get_name + */ +const char *fluid_defsfont_get_name(fluid_defsfont_t *defsfont) +{ + return defsfont->filename; +} + +/* Load sample data for a single sample from the Soundfont file. + * Returns FLUID_OK on error, otherwise FLUID_FAILED + */ +int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample) +{ + int num_samples; + unsigned int source_end = sample->source_end; + + /* For uncompressed samples we want to include the 46 zero sample word area following each sample + * in the Soundfont. Otherwise samples with loopend > end, which we have decided not to correct, would + * be corrected after all in fluid_sample_sanitize_loop */ + if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) + { + source_end += 46; /* Length of zero sample word after each sample, according to SF specs */ + + /* Safeguard against Soundfonts that are not quite valid and don't include 46 sample words after the + * last sample */ + if(source_end >= (defsfont->samplesize / sizeof(short))) + { + source_end = defsfont->samplesize / sizeof(short); + } + } + + num_samples = fluid_samplecache_load( + sfdata, sample->source_start, source_end, sample->sampletype, + defsfont->mlock, &sample->data, &sample->data24); + + if(num_samples < 0) + { + return FLUID_FAILED; + } + + if(num_samples == 0) + { + sample->start = sample->end = 0; + sample->loopstart = sample->loopend = 0; + return FLUID_OK; + } + + /* Ogg Vorbis samples already have loop pointers relative to the individual decompressed sample, + * but SF2 samples are relative to sample chunk start, so they need to be adjusted */ + if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) + { + sample->loopstart = sample->source_loopstart - sample->source_start; + sample->loopend = sample->source_loopend - sample->source_start; + } + + /* As we've just loaded an individual sample into it's own buffer, we need to adjust the start + * and end pointers */ + sample->start = 0; + sample->end = num_samples - 1; + + return FLUID_OK; +} + +/* Loads the sample data for all samples from the Soundfont file. For SF2 files, it loads the data in + * one large block. For SF3 files, each compressed sample gets loaded individually. + * Returns FLUID_OK on success, otherwise FLUID_FAILED + */ +int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata) +{ + fluid_list_t *list; + fluid_sample_t *sample; + int sf3_file = (sfdata->version.major == 3); + int sample_parsing_result = FLUID_OK; + int invalid_loops_were_sanitized = FALSE; + + /* For SF2 files, we load the sample data in one large block */ + if(!sf3_file) + { + int read_samples; + int num_samples = sfdata->samplesize / sizeof(short); + + read_samples = fluid_samplecache_load(sfdata, 0, num_samples - 1, 0, defsfont->mlock, + &defsfont->sampledata, &defsfont->sample24data); + + if(read_samples != num_samples) + { + FLUID_LOG(FLUID_ERR, "Attempted to read %d words of sample data, but got %d instead", + num_samples, read_samples); + return FLUID_FAILED; + } + } + + #pragma omp parallel + #pragma omp single + for(list = defsfont->sample; list; list = fluid_list_next(list)) + { + sample = fluid_list_get(list); + + if(sf3_file) + { + /* SF3 samples get loaded individually, as most (or all) of them are in Ogg Vorbis format + * anyway */ + #pragma omp task firstprivate(sample,sfdata,defsfont) shared(sample_parsing_result, invalid_loops_were_sanitized) default(none) + { + if(fluid_defsfont_load_sampledata(defsfont, sfdata, sample) == FLUID_FAILED) + { + #pragma omp critical + { + FLUID_LOG(FLUID_ERR, "Failed to load sample '%s'", sample->name); + sample_parsing_result = FLUID_FAILED; + } + } + else + { + int modified = fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short)); + if(modified) + { + #pragma omp critical + { + invalid_loops_were_sanitized = TRUE; + } + } + fluid_voice_optimize_sample(sample); + } + } + } + else + { + #pragma omp task firstprivate(sample, defsfont) shared(invalid_loops_were_sanitized) default(none) + { + int modified; + /* Data pointers of SF2 samples point to large sample data block loaded above */ + sample->data = defsfont->sampledata; + sample->data24 = defsfont->sample24data; + modified = fluid_sample_sanitize_loop(sample, defsfont->samplesize); + if(modified) + { + #pragma omp critical + { + invalid_loops_were_sanitized = TRUE; + } + } + fluid_voice_optimize_sample(sample); + } + } + } + + if(invalid_loops_were_sanitized) + { + FLUID_LOG(FLUID_WARN, + "Some invalid sample loops were sanitized! If you experience audible glitches, " + "start fluidsynth in verbose mode for detailed information."); + } + + return sample_parsing_result; +} + +/* + * fluid_defsfont_load + */ +int fluid_defsfont_load(fluid_defsfont_t *defsfont, const fluid_file_callbacks_t *fcbs, const char *file) +{ + SFData *sfdata; + fluid_list_t *p; + SFPreset *sfpreset; + SFSample *sfsample; + fluid_sample_t *sample; + fluid_defpreset_t *defpreset = NULL; + + defsfont->filename = FLUID_STRDUP(file); + + if(defsfont->filename == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + defsfont->fcbs = fcbs; + + /* The actual loading is done in the sfont and sffile files */ + sfdata = fluid_sffile_open(file, fcbs); + + if(sfdata == NULL) + { + /* error message already printed */ + return FLUID_FAILED; + } + + if(fluid_sffile_parse_presets(sfdata) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Couldn't parse presets from soundfont file"); + goto err_exit; + } + + /* Keep track of the position and size of the sample data because + it's loaded separately (and might be unoaded/reloaded in future) */ + defsfont->samplepos = sfdata->samplepos; + defsfont->samplesize = sfdata->samplesize; + defsfont->sample24pos = sfdata->sample24pos; + defsfont->sample24size = sfdata->sample24size; + + /* Create all samples from sample headers */ + p = sfdata->sample; + + while(p != NULL) + { + sfsample = (SFSample *)fluid_list_get(p); + + sample = new_fluid_sample(); + + if(sample == NULL) + { + goto err_exit; + } + + if(fluid_sample_import_sfont(sample, sfsample, defsfont) == FLUID_OK) + { + fluid_defsfont_add_sample(defsfont, sample); + } + else + { + delete_fluid_sample(sample); + sample = NULL; + } + + /* Store reference to FluidSynth sample in SFSample for later IZone fixups */ + sfsample->fluid_sample = sample; + + p = fluid_list_next(p); + } + + /* If dynamic sample loading is disabled, load all samples in the Soundfont */ + if(!defsfont->dynamic_samples) + { + if(fluid_defsfont_load_all_sampledata(defsfont, sfdata) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Unable to load all sample data"); + goto err_exit; + } + } + + /* Load all the presets */ + p = sfdata->preset; + + while(p != NULL) + { + sfpreset = (SFPreset *)fluid_list_get(p); + defpreset = new_fluid_defpreset(); + + if(defpreset == NULL) + { + goto err_exit; + } + + if(fluid_defpreset_import_sfont(defpreset, sfpreset, defsfont, sfdata) != FLUID_OK) + { + goto err_exit; + } + + if(fluid_defsfont_add_preset(defsfont, defpreset) == FLUID_FAILED) + { + goto err_exit; + } + + p = fluid_list_next(p); + } + + fluid_sffile_close(sfdata); + + return FLUID_OK; + +err_exit: + fluid_sffile_close(sfdata); + delete_fluid_defpreset(defpreset); + return FLUID_FAILED; +} + +/* fluid_defsfont_add_sample + * + * Add a sample to the SoundFont + */ +int fluid_defsfont_add_sample(fluid_defsfont_t *defsfont, fluid_sample_t *sample) +{ + defsfont->sample = fluid_list_prepend(defsfont->sample, sample); + return FLUID_OK; +} + +/* fluid_defsfont_add_preset + * + * Add a preset to the SoundFont + */ +int fluid_defsfont_add_preset(fluid_defsfont_t *defsfont, fluid_defpreset_t *defpreset) +{ + fluid_preset_t *preset; + + preset = new_fluid_preset(defsfont->sfont, + fluid_defpreset_preset_get_name, + fluid_defpreset_preset_get_banknum, + fluid_defpreset_preset_get_num, + fluid_defpreset_preset_noteon, + fluid_defpreset_preset_delete); + + if(preset == NULL) + { + return FLUID_FAILED; + } + + if(defsfont->dynamic_samples) + { + preset->notify = dynamic_samples_preset_notify; + } + + fluid_preset_set_data(preset, defpreset); + + defsfont->preset = fluid_list_append(defsfont->preset, preset); + + return FLUID_OK; +} + +/* + * fluid_defsfont_get_preset + */ +fluid_preset_t *fluid_defsfont_get_preset(fluid_defsfont_t *defsfont, int bank, int num) +{ + fluid_preset_t *preset; + fluid_list_t *list; + + for(list = defsfont->preset; list != NULL; list = fluid_list_next(list)) + { + preset = (fluid_preset_t *)fluid_list_get(list); + + if((fluid_preset_get_banknum(preset) == bank) && (fluid_preset_get_num(preset) == num)) + { + return preset; + } + } + + return NULL; +} + +/* + * fluid_defsfont_iteration_start + */ +void fluid_defsfont_iteration_start(fluid_defsfont_t *defsfont) +{ + defsfont->preset_iter_cur = defsfont->preset; +} + +/* + * fluid_defsfont_iteration_next + */ +fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t *defsfont) +{ + fluid_preset_t *preset = (fluid_preset_t *)fluid_list_get(defsfont->preset_iter_cur); + + defsfont->preset_iter_cur = fluid_list_next(defsfont->preset_iter_cur); + + return preset; +} + +/*************************************************************** + * + * PRESET + */ + +/* + * new_fluid_defpreset + */ +fluid_defpreset_t * +new_fluid_defpreset(void) +{ + fluid_defpreset_t *defpreset = FLUID_NEW(fluid_defpreset_t); + + if(defpreset == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + defpreset->next = NULL; + defpreset->name[0] = 0; + defpreset->bank = 0; + defpreset->num = 0; + defpreset->global_zone = NULL; + defpreset->zone = NULL; + defpreset->pinned = FALSE; + return defpreset; +} + +/* + * delete_fluid_defpreset + */ +void +delete_fluid_defpreset(fluid_defpreset_t *defpreset) +{ + fluid_preset_zone_t *zone; + + fluid_return_if_fail(defpreset != NULL); + + delete_fluid_preset_zone(defpreset->global_zone); + defpreset->global_zone = NULL; + + zone = defpreset->zone; + + while(zone != NULL) + { + defpreset->zone = zone->next; + delete_fluid_preset_zone(zone); + zone = defpreset->zone; + } + + FLUID_FREE(defpreset); +} + +int +fluid_defpreset_get_banknum(fluid_defpreset_t *defpreset) +{ + return defpreset->bank; +} + +int +fluid_defpreset_get_num(fluid_defpreset_t *defpreset) +{ + return defpreset->num; +} + +const char * +fluid_defpreset_get_name(fluid_defpreset_t *defpreset) +{ + return defpreset->name; +} + +/* + * fluid_defpreset_next + */ +fluid_defpreset_t * +fluid_defpreset_next(fluid_defpreset_t *defpreset) +{ + return defpreset->next; +} + +/* + * Adds global and local modulators list to the voice. This is done in 2 steps: + * - Step 1: Local modulators replace identical global modulators. + * - Step 2: global + local modulators are added to the voice using mode. + * + * Instrument zone list (local/global) must be added using FLUID_VOICE_OVERWRITE. + * Preset zone list (local/global) must be added using FLUID_VOICE_ADD. + * + * @param voice voice instance. + * @param global_mod global list of modulators. + * @param local_mod local list of modulators. + * @param mode Determines how to handle an existing identical modulator. + * #FLUID_VOICE_ADD to add (offset) the modulator amounts, + * #FLUID_VOICE_OVERWRITE to replace the modulator, +*/ +static void +fluid_defpreset_noteon_add_mod_to_voice(fluid_voice_t *voice, + fluid_mod_t *global_mod, fluid_mod_t *local_mod, + int mode) +{ + fluid_mod_t *mod; + /* list for 'sorting' global/local modulators */ + fluid_mod_t *mod_list[FLUID_NUM_MOD]; + int mod_list_count, i; + + /* identity_limit_count is the modulator upper limit number to handle with + * existing identical modulators. + * When identity_limit_count is below the actual number of modulators, this + * will restrict identity check to this upper limit, + * This is useful when we know by advance that there is no duplicate with + * modulators at index above this limit. This avoid wasting cpu cycles at + * noteon. + */ + int identity_limit_count; + + /* Step 1: Local modulators replace identical global modulators. */ + + /* local (instrument zone/preset zone), modulators: Put them all into a list. */ + mod_list_count = 0; + + while(local_mod) + { + /* As modulators number in local_mod list was limited to FLUID_NUM_MOD at + soundfont loading time (fluid_limit_mod_list()), here we don't need + to check if mod_list is full. + */ + mod_list[mod_list_count++] = local_mod; + local_mod = local_mod->next; + } + + /* global (instrument zone/preset zone), modulators. + * Replace modulators with the same definition in the global list: + * (Instrument zone: SF 2.01 page 69, 'bullet' 8) + * (Preset zone: SF 2.01 page 69, second-last bullet). + * + * mod_list contains local modulators. Now we know that there + * is no global modulator identical to another global modulator (this has + * been checked at soundfont loading time). So global modulators + * are only checked against local modulators number. + */ + + /* Restrict identity check to the number of local modulators */ + identity_limit_count = mod_list_count; + + while(global_mod) + { + /* 'Identical' global modulators are ignored. + * SF2.01 section 9.5.1 + * page 69, 'bullet' 3 defines 'identical'. */ + + for(i = 0; i < identity_limit_count; i++) + { + if(fluid_mod_test_identity(global_mod, mod_list[i])) + { + break; + } + } + + /* Finally add the new modulator to the list. */ + if(i >= identity_limit_count) + { + /* Although local_mod and global_mod lists was limited to + FLUID_NUM_MOD at soundfont loading time, it is possible that + local + global modulators exceeds FLUID_NUM_MOD. + So, checks if mod_list_count reaches the limit. + */ + if(mod_list_count >= FLUID_NUM_MOD) + { + /* mod_list is full, we silently forget this modulator and + next global modulators. When mod_list will be added to the + voice, a warning will be displayed if the voice list is full. + (see fluid_voice_add_mod_local()). + */ + break; + } + + mod_list[mod_list_count++] = global_mod; + } + + global_mod = global_mod->next; + } + + /* Step 2: global + local modulators are added to the voice using mode. */ + + /* + * mod_list contains local and global modulators, we know that: + * - there is no global modulator identical to another global modulator, + * - there is no local modulator identical to another local modulator, + * So these local/global modulators are only checked against + * actual number of voice modulators. + */ + + /* Restrict identity check to the actual number of voice modulators */ + /* Actual number of voice modulators : defaults + [instruments] */ + identity_limit_count = voice->mod_count; + + for(i = 0; i < mod_list_count; i++) + { + + mod = mod_list[i]; + /* in mode FLUID_VOICE_OVERWRITE disabled instruments modulators CANNOT be skipped. */ + /* in mode FLUID_VOICE_ADD disabled preset modulators can be skipped. */ + + if((mode == FLUID_VOICE_OVERWRITE) || (mod->amount != 0)) + { + /* Instrument modulators -supersede- existing (default) modulators. + SF 2.01 page 69, 'bullet' 6 */ + + /* Preset modulators -add- to existing instrument modulators. + SF2.01 page 70 first bullet on page */ + fluid_voice_add_mod_local(voice, mod, mode, identity_limit_count); + } + } +} + +/* + * fluid_defpreset_noteon + */ +int +fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int chan, int key, int vel) +{ + fluid_preset_zone_t *preset_zone, *global_preset_zone; + fluid_inst_t *inst; + fluid_inst_zone_t *inst_zone, *global_inst_zone; + fluid_voice_zone_t *voice_zone; + fluid_list_t *list; + fluid_voice_t *voice; + int tuned_key; + int i; + + /* For detuned channels it might be better to use another key for Soundfont sample selection + * giving better approximations for the pitch than the original key. + * Example: play key 60 on 6370 Hz => use tuned key 64 for sample selection + * + * This feature is only enabled for melodic channels. + * For drum channels we always select Soundfont samples by key numbers. + */ + + if(synth->channel[chan]->channel_type == CHANNEL_TYPE_MELODIC) + { + tuned_key = (int)(fluid_channel_get_key_pitch(synth->channel[chan], key) / 100.0f + 0.5f); + } + else + { + tuned_key = key; + } + + global_preset_zone = fluid_defpreset_get_global_zone(defpreset); + + /* run thru all the zones of this preset */ + preset_zone = fluid_defpreset_get_zone(defpreset); + + while(preset_zone != NULL) + { + + /* check if the note falls into the key and velocity range of this + preset */ + if(fluid_zone_inside_range(&preset_zone->range, tuned_key, vel)) + { + + inst = fluid_preset_zone_get_inst(preset_zone); + global_inst_zone = fluid_inst_get_global_zone(inst); + + /* run thru all the zones of this instrument that could start a voice */ + for(list = preset_zone->voice_zone; list != NULL; list = fluid_list_next(list)) + { + voice_zone = fluid_list_get(list); + + /* check if the instrument zone is ignored and the note falls into + the key and velocity range of this instrument zone. + An instrument zone must be ignored when its voice is already running + played by a legato passage (see fluid_synth_noteon_monopoly_legato()) */ + if(fluid_zone_inside_range(&voice_zone->range, tuned_key, vel)) + { + + inst_zone = voice_zone->inst_zone; + + /* this is a good zone. allocate a new synthesis process and initialize it */ + voice = fluid_synth_alloc_voice_LOCAL(synth, inst_zone->sample, chan, key, vel, &voice_zone->range); + + if(voice == NULL) + { + return FLUID_FAILED; + } + + + /* Instrument level, generators */ + + for(i = 0; i < GEN_LAST; i++) + { + + /* SF 2.01 section 9.4 'bullet' 4: + * + * A generator in a local instrument zone supersedes a + * global instrument zone generator. Both cases supersede + * the default generator -> voice_gen_set */ + + if(inst_zone->gen[i].flags) + { + fluid_voice_gen_set(voice, i, inst_zone->gen[i].val); + + } + else if((global_inst_zone != NULL) && (global_inst_zone->gen[i].flags)) + { + fluid_voice_gen_set(voice, i, global_inst_zone->gen[i].val); + + } + else + { + /* The generator has not been defined in this instrument. + * Do nothing, leave it at the default. + */ + } + + } /* for all generators */ + + /* Adds instrument zone modulators (global and local) to the voice.*/ + fluid_defpreset_noteon_add_mod_to_voice(voice, + /* global instrument modulators */ + global_inst_zone ? global_inst_zone->mod : NULL, + inst_zone->mod, /* local instrument modulators */ + FLUID_VOICE_OVERWRITE); /* mode */ + + /* Preset level, generators */ + + for(i = 0; i < GEN_LAST; i++) + { + + /* SF 2.01 section 8.5 page 58: If some generators are + encountered at preset level, they should be ignored. + However this check is not necessary when the soundfont + loader has ignored invalid preset generators. + Actually load_pgen()has ignored these invalid preset + generators: + GEN_STARTADDROFS, GEN_ENDADDROFS, + GEN_STARTLOOPADDROFS, GEN_ENDLOOPADDROFS, + GEN_STARTADDRCOARSEOFS,GEN_ENDADDRCOARSEOFS, + GEN_STARTLOOPADDRCOARSEOFS, + GEN_KEYNUM, GEN_VELOCITY, + GEN_ENDLOOPADDRCOARSEOFS, + GEN_SAMPLEMODE, GEN_EXCLUSIVECLASS,GEN_OVERRIDEROOTKEY + */ + + /* SF 2.01 section 9.4 'bullet' 9: A generator in a + * local preset zone supersedes a global preset zone + * generator. The effect is -added- to the destination + * summing node -> voice_gen_incr */ + + if(preset_zone->gen[i].flags) + { + fluid_voice_gen_incr(voice, i, preset_zone->gen[i].val); + } + else if((global_preset_zone != NULL) && global_preset_zone->gen[i].flags) + { + fluid_voice_gen_incr(voice, i, global_preset_zone->gen[i].val); + } + else + { + /* The generator has not been defined in this preset + * Do nothing, leave it unchanged. + */ + } + } /* for all generators */ + + /* Adds preset zone modulators (global and local) to the voice.*/ + fluid_defpreset_noteon_add_mod_to_voice(voice, + /* global preset modulators */ + global_preset_zone ? global_preset_zone->mod : NULL, + preset_zone->mod, /* local preset modulators */ + FLUID_VOICE_ADD); /* mode */ + + /* add the synthesis process to the synthesis loop. */ + fluid_synth_start_voice(synth, voice); + + /* Store the ID of the first voice that was created by this noteon event. + * Exclusive class may only terminate older voices. + * That avoids killing voices, which have just been created. + * (a noteon event can create several voice processes with the same exclusive + * class - for example when using stereo samples) + */ + } + } + } + + preset_zone = fluid_preset_zone_next(preset_zone); + } + + return FLUID_OK; +} + +/* + * fluid_defpreset_set_global_zone + */ +int +fluid_defpreset_set_global_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone) +{ + defpreset->global_zone = zone; + return FLUID_OK; +} + +/* + * fluid_defpreset_import_sfont + */ +int +fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, + SFPreset *sfpreset, + fluid_defsfont_t *defsfont, + SFData *sfdata) +{ + fluid_list_t *p; + SFZone *sfzone; + fluid_preset_zone_t *zone; + int count; + char zone_name[256]; + + if(FLUID_STRLEN(sfpreset->name) > 0) + { + FLUID_STRCPY(defpreset->name, sfpreset->name); + } + else + { + FLUID_SNPRINTF(defpreset->name, sizeof(defpreset->name), "Bank%d,Pre%d", sfpreset->bank, sfpreset->prenum); + } + + defpreset->bank = sfpreset->bank; + defpreset->num = sfpreset->prenum; + p = sfpreset->zone; + count = 0; + + while(p != NULL) + { + sfzone = (SFZone *)fluid_list_get(p); + FLUID_SNPRINTF(zone_name, sizeof(zone_name), "pz:%s/%d", defpreset->name, count); + zone = new_fluid_preset_zone(zone_name); + + if(zone == NULL) + { + return FLUID_FAILED; + } + + if(fluid_preset_zone_import_sfont(zone, defpreset->global_zone, sfzone, defsfont, sfdata) != FLUID_OK) + { + delete_fluid_preset_zone(zone); + return FLUID_FAILED; + } + + if((count == 0) && (fluid_preset_zone_get_inst(zone) == NULL)) + { + fluid_defpreset_set_global_zone(defpreset, zone); + } + else if(fluid_defpreset_add_zone(defpreset, zone) != FLUID_OK) + { + delete_fluid_preset_zone(zone); + return FLUID_FAILED; + } + + p = fluid_list_next(p); + count++; + } + + return FLUID_OK; +} + +/* + * fluid_defpreset_add_zone + */ +int +fluid_defpreset_add_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone) +{ + if(defpreset->zone == NULL) + { + zone->next = NULL; + defpreset->zone = zone; + } + else + { + zone->next = defpreset->zone; + defpreset->zone = zone; + } + + return FLUID_OK; +} + +/* + * fluid_defpreset_get_zone + */ +fluid_preset_zone_t * +fluid_defpreset_get_zone(fluid_defpreset_t *defpreset) +{ + return defpreset->zone; +} + +/* + * fluid_defpreset_get_global_zone + */ +fluid_preset_zone_t * +fluid_defpreset_get_global_zone(fluid_defpreset_t *defpreset) +{ + return defpreset->global_zone; +} + +/*************************************************************** + * + * PRESET_ZONE + */ + +/* + * fluid_preset_zone_next + */ +fluid_preset_zone_t * +fluid_preset_zone_next(fluid_preset_zone_t *zone) +{ + return zone->next; +} + +/* + * new_fluid_preset_zone + */ +fluid_preset_zone_t * +new_fluid_preset_zone(char *name) +{ + fluid_preset_zone_t *zone = NULL; + zone = FLUID_NEW(fluid_preset_zone_t); + + if(zone == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + zone->next = NULL; + zone->voice_zone = NULL; + zone->name = FLUID_STRDUP(name); + + if(zone->name == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(zone); + return NULL; + } + + zone->inst = NULL; + zone->range.keylo = 0; + zone->range.keyhi = 128; + zone->range.vello = 0; + zone->range.velhi = 128; + zone->range.ignore = FALSE; + + /* Flag all generators as unused (default, they will be set when they are found + * in the sound font). + * This also sets the generator values to default, but that is of no concern here.*/ + fluid_gen_init(&zone->gen[0], NULL); + zone->mod = NULL; /* list of modulators */ + return zone; +} + +/* + * delete list of modulators. + */ +void delete_fluid_list_mod(fluid_mod_t *mod) +{ + fluid_mod_t *tmp; + + while(mod) /* delete the modulators */ + { + tmp = mod; + mod = mod->next; + delete_fluid_mod(tmp); + } +} + +/* + * delete_fluid_preset_zone + */ +void +delete_fluid_preset_zone(fluid_preset_zone_t *zone) +{ + fluid_list_t *list; + + fluid_return_if_fail(zone != NULL); + + delete_fluid_list_mod(zone->mod); + + for(list = zone->voice_zone; list != NULL; list = fluid_list_next(list)) + { + FLUID_FREE(fluid_list_get(list)); + } + + delete_fluid_list(zone->voice_zone); + + FLUID_FREE(zone->name); + FLUID_FREE(zone); +} + +static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone) +{ + fluid_inst_zone_t *inst_zone; + fluid_sample_t *sample; + fluid_voice_zone_t *voice_zone; + fluid_zone_range_t *irange; + fluid_zone_range_t *prange = &preset_zone->range; + + fluid_return_val_if_fail(preset_zone->inst != NULL, FLUID_FAILED); + + inst_zone = fluid_inst_get_zone(preset_zone->inst); + + while(inst_zone != NULL) + { + + /* We only create voice ranges for zones that could actually start a voice, + * i.e. that have a sample and don't point to ROM */ + sample = fluid_inst_zone_get_sample(inst_zone); + + if((sample == NULL) || fluid_sample_in_rom(sample)) + { + inst_zone = fluid_inst_zone_next(inst_zone); + continue; + } + + voice_zone = FLUID_NEW(fluid_voice_zone_t); + + if(voice_zone == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + voice_zone->inst_zone = inst_zone; + + irange = &inst_zone->range; + + voice_zone->range.keylo = (prange->keylo > irange->keylo) ? prange->keylo : irange->keylo; + voice_zone->range.keyhi = (prange->keyhi < irange->keyhi) ? prange->keyhi : irange->keyhi; + voice_zone->range.vello = (prange->vello > irange->vello) ? prange->vello : irange->vello; + voice_zone->range.velhi = (prange->velhi < irange->velhi) ? prange->velhi : irange->velhi; + voice_zone->range.ignore = FALSE; + + preset_zone->voice_zone = fluid_list_append(preset_zone->voice_zone, voice_zone); + + inst_zone = fluid_inst_zone_next(inst_zone); + } + + return FLUID_OK; +} + +/** + * Checks if modulator mod is identical to another modulator in the list + * (specs SF 2.0X 7.4, 7.8). + * @param mod, modulator list. + * @param name, if not NULL, pointer on a string displayed as warning. + * @return TRUE if mod is identical to another modulator, FALSE otherwise. + */ +static int +fluid_zone_is_mod_identical(fluid_mod_t *mod, char *name) +{ + fluid_mod_t *next = mod->next; + + while(next) + { + /* is mod identical to next ? */ + if(fluid_mod_test_identity(mod, next)) + { + if(name) + { + FLUID_LOG(FLUID_WARN, "Ignoring identical modulator %s", name); + } + + return TRUE; + } + + next = next->next; + } + + return FALSE; +} + +/** + * Limits the number of modulators in a modulator list. + * This is appropriate to internal synthesizer modulators tables + * which have a fixed size (FLUID_NUM_MOD). + * + * @param zone_name, zone name + * @param list_mod, address of pointer on modulator list. + */ +static void fluid_limit_mod_list(char *zone_name, fluid_mod_t **list_mod) +{ + int mod_idx = 0; /* modulator index */ + fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */ + fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */ + + while(mod) + { + if((mod_idx + 1) > FLUID_NUM_MOD) + { + /* truncation of list_mod */ + if(mod_idx) + { + prev_mod->next = NULL; + } + else + { + *list_mod = NULL; + } + + delete_fluid_list_mod(mod); + FLUID_LOG(FLUID_WARN, "%s, modulators count limited to %d", zone_name, + FLUID_NUM_MOD); + break; + } + + mod_idx++; + prev_mod = mod; + mod = mod->next; + } +} + +/** + * Checks and remove invalid modulators from a zone modulators list. + * - checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1). + * - checks identical modulators in the list (specs SF 2.01 7.4, 7.8). + * @param zone_name, zone name. + * @param list_mod, address of pointer on modulators list. + */ +static void +fluid_zone_check_mod(char *zone_name, fluid_mod_t **list_mod) +{ + fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */ + fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */ + int mod_idx = 0; /* modulator index */ + + while(mod) + { + char zone_mod_name[256]; + fluid_mod_t *next = mod->next; + + /* prepare modulator name: zonename/#modulator */ + FLUID_SNPRINTF(zone_mod_name, sizeof(zone_mod_name), "%s/mod%d", zone_name, mod_idx); + + /* has mod invalid sources ? */ + if(!fluid_mod_check_sources(mod, zone_mod_name) + /* or is mod identical to any following modulator ? */ + || fluid_zone_is_mod_identical(mod, zone_mod_name)) + { + /* the modulator is useless so we remove it */ + if(prev_mod) + { + prev_mod->next = next; + } + else + { + *list_mod = next; + } + + delete_fluid_mod(mod); /* freeing */ + } + else + { + prev_mod = mod; + } + + mod = next; + mod_idx++; + } + + /* limits the size of modulators list */ + fluid_limit_mod_list(zone_name, list_mod); +} + +/* + * fluid_zone_gen_import_sfont + * Imports generators from sfzone to gen and range. + * @param gen, pointer on destination generators table. + * @param range, pointer on destination range generators. + * @param sfzone, pointer on soundfont zone generators. + */ +static void +fluid_zone_gen_import_sfont(fluid_gen_t *gen, fluid_zone_range_t *range, fluid_zone_range_t *global_range, SFZone *sfzone) +{ + fluid_list_t *r; + SFGen *sfgen; + + if(global_range != NULL) + { + // All zones are initialized with the default range of 0-127. However, local zones should be superseded by + // the range of their global zone in case that local zone lacks a GEN_KEYRANGE or GEN_VELRANGE + // (see issue #1250). + range->keylo = global_range->keylo; + range->keyhi = global_range->keyhi; + range->vello = global_range->vello; + range->velhi = global_range->velhi; + } + + for(r = sfzone->gen; r != NULL;) + { + sfgen = (SFGen *)fluid_list_get(r); + + switch(sfgen->id) + { + case GEN_KEYRANGE: + range->keylo = sfgen->amount.range.lo; + range->keyhi = sfgen->amount.range.hi; + break; + + case GEN_VELRANGE: + range->vello = sfgen->amount.range.lo; + range->velhi = sfgen->amount.range.hi; + break; + + case GEN_ATTENUATION: + /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at + * preset and instrument level */ + gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR; + gen[sfgen->id].flags = GEN_SET; + break; + + case GEN_INSTRUMENT: + case GEN_SAMPLEID: + gen[sfgen->id].val = (fluid_real_t) sfgen->amount.uword; + gen[sfgen->id].flags = GEN_SET; + break; + + default: + gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; + gen[sfgen->id].flags = GEN_SET; + break; + } + + r = fluid_list_next(r); + } +} + +/* + * fluid_zone_mod_source_import_sfont + * Imports source information from sf_source to src and flags. + * @param src, pointer on destination modulator source. + * @param flags, pointer on destination modulator flags. + * @param sf_source, soundfont modulator source. + * @return return TRUE if success, FALSE if source type is unknown. + */ +static int +fluid_zone_mod_source_import_sfont(unsigned char *src, unsigned char *flags, unsigned short sf_source) +{ + int type; + unsigned char flags_dest; /* destination flags */ + + /* sources */ + *src = sf_source & 127; /* index of source, seven-bit value, SF2.01 section 8.2, page 50 */ + + /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ + flags_dest = 0; + + if(sf_source & (1 << 7)) + { + flags_dest |= FLUID_MOD_CC; + } + else + { + flags_dest |= FLUID_MOD_GC; + } + + /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ + if(sf_source & (1 << 8)) + { + flags_dest |= FLUID_MOD_NEGATIVE; + } + else + { + flags_dest |= FLUID_MOD_POSITIVE; + } + + /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ + if(sf_source & (1 << 9)) + { + flags_dest |= FLUID_MOD_BIPOLAR; + } + else + { + flags_dest |= FLUID_MOD_UNIPOLAR; + } + + /* modulator source types: SF2.01 section 8.2.1 page 52 */ + type = sf_source >> 10; + type &= 63; /* type is a 6-bit value */ + + if(type == 0) + { + flags_dest |= FLUID_MOD_LINEAR; + } + else if(type == 1) + { + flags_dest |= FLUID_MOD_CONCAVE; + } + else if(type == 2) + { + flags_dest |= FLUID_MOD_CONVEX; + } + else if(type == 3) + { + flags_dest |= FLUID_MOD_SWITCH; + } + else + { + *flags = flags_dest; + /* This shouldn't happen - unknown type! */ + return FALSE; + } + + *flags = flags_dest; + return TRUE; +} + +/* + * fluid_zone_mod_import_sfont + * Imports modulators from sfzone to modulators list mod. + * @param zone_name, zone name. + * @param mod, address of pointer on modulators list to return. + * @param sfzone, pointer on soundfont zone. + * @return FLUID_OK if success, FLUID_FAILED otherwise. + */ +static int +fluid_zone_mod_import_sfont(char *zone_name, fluid_mod_t **mod, SFZone *sfzone) +{ + fluid_list_t *r; + int count; + + /* Import the modulators (only SF2.1 and higher) */ + for(count = 0, r = sfzone->mod; r != NULL; count++) + { + + SFMod *mod_src = (SFMod *)fluid_list_get(r); + fluid_mod_t *mod_dest = new_fluid_mod(); + + if(mod_dest == NULL) + { + return FLUID_FAILED; + } + + mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ + + /* *** Amount *** */ + mod_dest->amount = mod_src->amount; + + /* *** Source *** */ + if(!fluid_zone_mod_source_import_sfont(&mod_dest->src1, &mod_dest->flags1, mod_src->src)) + { + /* This shouldn't happen - unknown type! + * Deactivate the modulator by setting the amount to 0. */ + mod_dest->amount = 0; + } + + /* Note: When primary source input (src1) is set to General Controller 'No Controller', + output will be forced to 0.0 at synthesis time (see fluid_mod_get_value()). + That means that the minimum value of the modulator will be always 0.0. + We need to force amount value to 0 to ensure a correct evaluation of the minimum + value later (see fluid_voice_get_lower_boundary_for_attenuation()). + */ + if(((mod_dest->flags1 & FLUID_MOD_CC) == FLUID_MOD_GC) && + (mod_dest->src1 == FLUID_MOD_NONE)) + { + mod_dest->amount = 0; + } + + /* *** Dest *** */ + mod_dest->dest = mod_src->dest; /* index of controlled generator */ + + /* *** Amount source *** */ + if(!fluid_zone_mod_source_import_sfont(&mod_dest->src2, &mod_dest->flags2, mod_src->amtsrc)) + { + /* This shouldn't happen - unknown type! + * Deactivate the modulator by setting the amount to 0. */ + mod_dest->amount = 0; + } + /* Note: When secondary source input (src2) is set to General Controller 'No Controller', + output will be forced to +1.0 at synthesis time (see fluid_mod_get_value()). + That means that this source will behave unipolar only. We need to force the + unipolar flag to ensure to ensure a correct evaluation of the minimum + value later (see fluid_voice_get_lower_boundary_for_attenuation()). + */ + if(((mod_dest->flags2 & FLUID_MOD_CC) == FLUID_MOD_GC) && + (mod_dest->src2 == FLUID_MOD_NONE)) + { + mod_dest->flags2 &= ~FLUID_MOD_BIPOLAR; + } + + /* *** Transform *** */ + /* SF2.01 only uses the 'linear' transform (0). + * Deactivate the modulator by setting the amount to 0 in any other case. + */ + if(mod_src->trans != 0) + { + mod_dest->amount = 0; + } + + /* Store the new modulator in the zone The order of modulators + * will make a difference, at least in an instrument context: The + * second modulator overwrites the first one, if they only differ + * in amount. */ + if(count == 0) + { + *mod = mod_dest; + } + else + { + fluid_mod_t *last_mod = *mod; + + /* Find the end of the list */ + while(last_mod->next != NULL) + { + last_mod = last_mod->next; + } + + last_mod->next = mod_dest; + } + + r = fluid_list_next(r); + } /* foreach modulator */ + + /* checks and removes invalid modulators in modulators list*/ + fluid_zone_check_mod(zone_name, mod); + return FLUID_OK; +} + +/* + * fluid_preset_zone_import_sfont + */ +int +fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, fluid_preset_zone_t *global_zone, SFZone *sfzone, fluid_defsfont_t *defsfont, SFData *sfdata) +{ + /* import the generators */ + fluid_zone_gen_import_sfont(zone->gen, &zone->range, global_zone ? &global_zone->range : NULL, sfzone); + + if(zone->gen[GEN_INSTRUMENT].flags == GEN_SET) + { + int inst_idx = (int) zone->gen[GEN_INSTRUMENT].val; + + zone->inst = find_inst_by_idx(defsfont, inst_idx); + + if(zone->inst == NULL) + { + zone->inst = fluid_inst_import_sfont(inst_idx, defsfont, sfdata); + } + + if(zone->inst == NULL) + { + + FLUID_LOG(FLUID_ERR, "Preset zone %s: Invalid instrument reference", + zone->name); + return FLUID_FAILED; + } + + if(fluid_preset_zone_create_voice_zones(zone) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + /* We don't need this generator anymore */ + zone->gen[GEN_INSTRUMENT].flags = GEN_UNUSED; + } + + /* Import the modulators (only SF2.1 and higher) */ + return fluid_zone_mod_import_sfont(zone->name, &zone->mod, sfzone); +} + +/* + * fluid_preset_zone_get_inst + */ +fluid_inst_t * +fluid_preset_zone_get_inst(fluid_preset_zone_t *zone) +{ + return zone->inst; +} + + +/*************************************************************** + * + * INST + */ + +/* + * new_fluid_inst + */ +fluid_inst_t * +new_fluid_inst() +{ + fluid_inst_t *inst = FLUID_NEW(fluid_inst_t); + + if(inst == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + inst->name[0] = 0; + inst->global_zone = NULL; + inst->zone = NULL; + return inst; +} + +/* + * delete_fluid_inst + */ +void +delete_fluid_inst(fluid_inst_t *inst) +{ + fluid_inst_zone_t *zone; + + fluid_return_if_fail(inst != NULL); + + delete_fluid_inst_zone(inst->global_zone); + inst->global_zone = NULL; + + zone = inst->zone; + + while(zone != NULL) + { + inst->zone = zone->next; + delete_fluid_inst_zone(zone); + zone = inst->zone; + } + + FLUID_FREE(inst); +} + +/* + * fluid_inst_set_global_zone + */ +int +fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone) +{ + inst->global_zone = zone; + return FLUID_OK; +} + +/* + * fluid_inst_import_sfont + */ +fluid_inst_t * +fluid_inst_import_sfont(int inst_idx, fluid_defsfont_t *defsfont, SFData *sfdata) +{ + fluid_list_t *p; + fluid_list_t *inst_list; + fluid_inst_t *inst; + SFZone *sfzone; + SFInst *sfinst; + fluid_inst_zone_t *inst_zone; + char zone_name[256]; + int count; + + for (inst_list = sfdata->inst; inst_list; inst_list = fluid_list_next(inst_list)) + { + sfinst = fluid_list_get(inst_list); + if (sfinst->idx == inst_idx) + { + break; + } + } + if (inst_list == NULL) + { + return NULL; + } + + inst = (fluid_inst_t *) new_fluid_inst(); + + if(inst == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + inst->source_idx = sfinst->idx; + + p = sfinst->zone; + + if(FLUID_STRLEN(sfinst->name) > 0) + { + FLUID_STRCPY(inst->name, sfinst->name); + } + else + { + FLUID_STRCPY(inst->name, ""); + } + + count = 0; + + while(p != NULL) + { + + sfzone = (SFZone *)fluid_list_get(p); + /* instrument zone name */ + FLUID_SNPRINTF(zone_name, sizeof(zone_name), "iz:%s/%d", inst->name, count); + + inst_zone = new_fluid_inst_zone(zone_name); + if(inst_zone == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error; + } + + if(fluid_inst_zone_import_sfont(inst_zone, inst->global_zone, sfzone, defsfont, sfdata) != FLUID_OK) + { + FLUID_LOG(FLUID_ERR, "fluid_inst_zone_import_sfont() failed for instrument %s", inst->name); + delete_fluid_inst_zone(inst_zone); + goto error; + } + + if((count == 0) && (fluid_inst_zone_get_sample(inst_zone) == NULL)) + { + fluid_inst_set_global_zone(inst, inst_zone); + + } + else if(fluid_inst_add_zone(inst, inst_zone) != FLUID_OK) + { + FLUID_LOG(FLUID_ERR, "fluid_inst_add_zone() failed for instrument %s", inst->name); + delete_fluid_inst_zone(inst_zone); + goto error; + } + + p = fluid_list_next(p); + count++; + } + + defsfont->inst = fluid_list_append(defsfont->inst, inst); + return inst; + +error: + delete_fluid_inst(inst); + return NULL; +} + +/* + * fluid_inst_add_zone + */ +int +fluid_inst_add_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone) +{ + if(inst->zone == NULL) + { + zone->next = NULL; + inst->zone = zone; + } + else + { + zone->next = inst->zone; + inst->zone = zone; + } + + return FLUID_OK; +} + +/* + * fluid_inst_get_zone + */ +fluid_inst_zone_t * +fluid_inst_get_zone(fluid_inst_t *inst) +{ + return inst->zone; +} + +/* + * fluid_inst_get_global_zone + */ +fluid_inst_zone_t * +fluid_inst_get_global_zone(fluid_inst_t *inst) +{ + return inst->global_zone; +} + +/*************************************************************** + * + * INST_ZONE + */ + +/* + * new_fluid_inst_zone + */ +fluid_inst_zone_t * +new_fluid_inst_zone(char *name) +{ + fluid_inst_zone_t *zone = NULL; + zone = FLUID_NEW(fluid_inst_zone_t); + + if(zone == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + zone->next = NULL; + zone->name = FLUID_STRDUP(name); + + if(zone->name == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(zone); + return NULL; + } + + zone->sample = NULL; + zone->range.keylo = 0; + zone->range.keyhi = 128; + zone->range.vello = 0; + zone->range.velhi = 128; + zone->range.ignore = FALSE; + /* Flag the generators as unused. + * This also sets the generator values to default, but they will be overwritten anyway, if used.*/ + fluid_gen_init(&zone->gen[0], NULL); + zone->mod = NULL; /* list of modulators */ + return zone; +} + +/* + * delete_fluid_inst_zone + */ +void +delete_fluid_inst_zone(fluid_inst_zone_t *zone) +{ + fluid_return_if_fail(zone != NULL); + + delete_fluid_list_mod(zone->mod); + + FLUID_FREE(zone->name); + FLUID_FREE(zone); +} + +/* + * fluid_inst_zone_next + */ +fluid_inst_zone_t * +fluid_inst_zone_next(fluid_inst_zone_t *zone) +{ + return zone->next; +} + +/* + * fluid_inst_zone_import_sfont + */ +int +fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, fluid_inst_zone_t *global_inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont, SFData *sfdata) +{ + /* import the generators */ + fluid_zone_gen_import_sfont(inst_zone->gen, &inst_zone->range, global_inst_zone ? &global_inst_zone->range : NULL, sfzone); + + /* FIXME */ + /* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */ + /* FLUID_LOG(FLUID_DBG, "ExclusiveClass=%d\n", (int) zone->gen[GEN_EXCLUSIVECLASS].val); */ + /* } */ + + if (inst_zone->gen[GEN_SAMPLEID].flags == GEN_SET) + { + fluid_list_t *list; + SFSample *sfsample; + int sample_idx = (int) inst_zone->gen[GEN_SAMPLEID].val; + + /* find the SFSample by index */ + for(list = sfdata->sample; list; list = fluid_list_next(list)) + { + sfsample = fluid_list_get(list); + if (sfsample->idx == sample_idx) + { + break; + } + } + if (list == NULL) + { + FLUID_LOG(FLUID_ERR, "Instrument zone '%s': Invalid sample reference", + inst_zone->name); + return FLUID_FAILED; + } + + inst_zone->sample = sfsample->fluid_sample; + + /* we don't need this generator anymore, mark it as unused */ + inst_zone->gen[GEN_SAMPLEID].flags = GEN_UNUSED; + } + + /* Import the modulators (only SF2.1 and higher) */ + return fluid_zone_mod_import_sfont(inst_zone->name, &inst_zone->mod, sfzone); +} + +/* + * fluid_inst_zone_get_sample + */ +fluid_sample_t * +fluid_inst_zone_get_sample(fluid_inst_zone_t *zone) +{ + return zone->sample; +} + + +int +fluid_zone_inside_range(fluid_zone_range_t *range, int key, int vel) +{ + /* ignoreInstrumentZone is set in mono legato playing */ + int ignore_zone = range->ignore; + + /* Reset the 'ignore' request */ + range->ignore = FALSE; + + return !ignore_zone && ((range->keylo <= key) && + (range->keyhi >= key) && + (range->vello <= vel) && + (range->velhi >= vel)); +} + +/*************************************************************** + * + * SAMPLE + */ + +/* + * fluid_sample_in_rom + */ +int +fluid_sample_in_rom(fluid_sample_t *sample) +{ + return (sample->sampletype & FLUID_SAMPLETYPE_ROM); +} + + +/* + * fluid_sample_import_sfont + */ +int +fluid_sample_import_sfont(fluid_sample_t *sample, SFSample *sfsample, fluid_defsfont_t *defsfont) +{ + FLUID_STRCPY(sample->name, sfsample->name); + + sample->source_start = sfsample->start; + sample->source_end = (sfsample->end > 0) ? sfsample->end - 1 : 0; /* marks last sample, contrary to SF spec. */ + sample->source_loopstart = sfsample->loopstart; + sample->source_loopend = sfsample->loopend; + + sample->start = sample->source_start; + sample->end = sample->source_end; + sample->loopstart = sample->source_loopstart; + sample->loopend = sample->source_loopend; + sample->samplerate = sfsample->samplerate; + sample->origpitch = sfsample->origpitch; + sample->pitchadj = sfsample->pitchadj; + sample->sampletype = sfsample->sampletype; + + if(defsfont->dynamic_samples) + { + sample->notify = dynamic_samples_sample_notify; + } + + if(fluid_sample_validate(sample, defsfont->samplesize) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + return FLUID_OK; +} + +/* Called if a sample is no longer used by a voice. Used by dynamic sample loading + * to unload a sample that is not used by any loaded presets anymore but couldn't + * be unloaded straight away because it was still in use by a voice. */ +static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason) +{ + if(reason == FLUID_SAMPLE_DONE && sample->preset_count == 0) + { + unload_sample(sample); + } + + return FLUID_OK; +} + +/* Called if a preset has been selected for or unselected from a channel. Used by + * dynamic sample loading to load and unload samples on demand. */ +static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan) +{ + fluid_defsfont_t *defsfont; + + if(reason == FLUID_PRESET_SELECTED) + { + FLUID_LOG(FLUID_DBG, "Selected preset '%s' on channel %d", fluid_preset_get_name(preset), chan); + defsfont = fluid_sfont_get_data(preset->sfont); + return load_preset_samples(defsfont, preset); + } + + if(reason == FLUID_PRESET_UNSELECTED) + { + FLUID_LOG(FLUID_DBG, "Deselected preset '%s' from channel %d", fluid_preset_get_name(preset), chan); + defsfont = fluid_sfont_get_data(preset->sfont); + return unload_preset_samples(defsfont, preset); + } + + if(reason == FLUID_PRESET_PIN) + { + defsfont = fluid_sfont_get_data(preset->sfont); + return pin_preset_samples(defsfont, preset); + } + + if(reason == FLUID_PRESET_UNPIN) + { + defsfont = fluid_sfont_get_data(preset->sfont); + return unpin_preset_samples(defsfont, preset); + } + + return FLUID_OK; +} + + +static int pin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) +{ + fluid_defpreset_t *defpreset; + + defpreset = fluid_preset_get_data(preset); + if (defpreset->pinned) + { + return FLUID_OK; + } + + FLUID_LOG(FLUID_DBG, "Pinning preset '%s'", fluid_preset_get_name(preset)); + + if(load_preset_samples(defsfont, preset) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + defpreset->pinned = TRUE; + + return FLUID_OK; +} + + +static int unpin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) +{ + fluid_defpreset_t *defpreset; + + defpreset = fluid_preset_get_data(preset); + if (!defpreset->pinned) + { + return FLUID_OK; + } + + FLUID_LOG(FLUID_DBG, "Unpinning preset '%s'", fluid_preset_get_name(preset)); + + if(unload_preset_samples(defsfont, preset) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + defpreset->pinned = FALSE; + + return FLUID_OK; +} + + +/* Walk through all samples used by the passed in preset and make sure that the + * sample data is loaded for each sample. Used by dynamic sample loading. */ +static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) +{ + fluid_defpreset_t *defpreset; + fluid_preset_zone_t *preset_zone; + fluid_inst_t *inst; + fluid_inst_zone_t *inst_zone; + fluid_sample_t *sample; + SFData *sffile = NULL; + + defpreset = fluid_preset_get_data(preset); + preset_zone = fluid_defpreset_get_zone(defpreset); + + while(preset_zone != NULL) + { + inst = fluid_preset_zone_get_inst(preset_zone); + inst_zone = fluid_inst_get_zone(inst); + + while(inst_zone != NULL) + { + sample = fluid_inst_zone_get_sample(inst_zone); + + if((sample != NULL) && (sample->start != sample->end)) + { + sample->preset_count++; + + /* If this is the first time this sample has been selected, + * load the sampledata */ + if(sample->preset_count == 1) + { + /* Make sure we have an open Soundfont file. Do this here + * to avoid having to open the file if no loading is necessary + * for a preset */ + if(sffile == NULL) + { + sffile = fluid_sffile_open(defsfont->filename, defsfont->fcbs); + + if(sffile == NULL) + { + FLUID_LOG(FLUID_ERR, "Unable to open Soundfont file"); + return FLUID_FAILED; + } + } + + if(fluid_defsfont_load_sampledata(defsfont, sffile, sample) == FLUID_OK) + { + fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short)); + fluid_voice_optimize_sample(sample); + } + else + { + FLUID_LOG(FLUID_ERR, "Unable to load sample '%s', disabling", sample->name); + sample->start = sample->end = 0; + } + } + } + + inst_zone = fluid_inst_zone_next(inst_zone); + } + + preset_zone = fluid_preset_zone_next(preset_zone); + } + + if(sffile != NULL) + { + fluid_sffile_close(sffile); + } + + return FLUID_OK; +} + +/* Walk through all samples used by the passed in preset and unload the sample data + * of each sample that is not used by any selected preset anymore. Used by dynamic + * sample loading. */ +static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) +{ + fluid_defpreset_t *defpreset; + fluid_preset_zone_t *preset_zone; + fluid_inst_t *inst; + fluid_inst_zone_t *inst_zone; + fluid_sample_t *sample; + + defpreset = fluid_preset_get_data(preset); + preset_zone = fluid_defpreset_get_zone(defpreset); + + while(preset_zone != NULL) + { + inst = fluid_preset_zone_get_inst(preset_zone); + inst_zone = fluid_inst_get_zone(inst); + + while(inst_zone != NULL) + { + sample = fluid_inst_zone_get_sample(inst_zone); + + if((sample != NULL) && (sample->preset_count > 0)) + { + sample->preset_count--; + + /* If the sample is not used by any preset or used by a + * sounding voice, unload it from the sample cache. If it's + * still in use by a voice, dynamic_samples_sample_notify will + * take care of unloading the sample as soon as the voice is + * finished with it (but only on the next API call). */ + if(sample->preset_count == 0 && sample->refcount == 0) + { + unload_sample(sample); + } + } + + inst_zone = fluid_inst_zone_next(inst_zone); + } + + preset_zone = fluid_preset_zone_next(preset_zone); + } + + return FLUID_OK; +} + +/* Unload an unused sample from the samplecache */ +static void unload_sample(fluid_sample_t *sample) +{ + fluid_return_if_fail(sample != NULL); + fluid_return_if_fail(sample->data != NULL); + fluid_return_if_fail(sample->preset_count == 0); + fluid_return_if_fail(sample->refcount == 0); + + FLUID_LOG(FLUID_DBG, "Unloading sample '%s'", sample->name); + + if(fluid_samplecache_unload(sample->data) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Unable to unload sample '%s'", sample->name); + } + else + { + sample->data = NULL; + sample->data24 = NULL; + } +} + +static fluid_inst_t *find_inst_by_idx(fluid_defsfont_t *defsfont, int idx) +{ + fluid_list_t *list; + fluid_inst_t *inst; + + for(list = defsfont->inst; list != NULL; list = fluid_list_next(list)) + { + inst = fluid_list_get(list); + + if(inst->source_idx == idx) + { + return inst; + } + } + + return NULL; +} diff --git a/libs/fluidsynth/src/sfloader/fluid_defsfont.h b/libs/fluidsynth/src/sfloader/fluid_defsfont.h new file mode 100644 index 00000000000..d67068955d7 --- /dev/null +++ b/libs/fluidsynth/src/sfloader/fluid_defsfont.h @@ -0,0 +1,232 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont loading code borrowed from Smurf SoundFont Editor by Josh Green + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_DEFSFONT_H +#define _FLUID_DEFSFONT_H + + +#include "fluidsynth.h" +#include "fluidsynth_priv.h" +#include "fluid_sffile.h" +#include "fluid_list.h" +#include "fluid_mod.h" +#include "fluid_gen.h" + + + +/*-----------------------------------sfont.h----------------------------*/ + +#define SF_SAMPMODES_LOOP 1 +#define SF_SAMPMODES_UNROLL 2 + +#define SF_MIN_SAMPLERATE 400 +#define SF_MAX_SAMPLERATE 50000 + +#define SF_MIN_SAMPLE_LENGTH 32 + +/*************************************************************** + * + * FORWARD DECLARATIONS + */ +typedef struct _fluid_defsfont_t fluid_defsfont_t; +typedef struct _fluid_defpreset_t fluid_defpreset_t; +typedef struct _fluid_preset_zone_t fluid_preset_zone_t; +typedef struct _fluid_inst_t fluid_inst_t; +typedef struct _fluid_inst_zone_t fluid_inst_zone_t; /**< Soundfont Instrument Zone */ +typedef struct _fluid_voice_zone_t fluid_voice_zone_t; + +/* defines the velocity and key range for a zone */ +struct _fluid_zone_range_t +{ + int keylo; + int keyhi; + int vello; + int velhi; + unsigned char ignore; /* set to TRUE for legato playing to ignore this range zone */ +}; + +/* Stored on a preset zone to keep track of the inst zones that could start a voice + * and their combined preset zone/instument zone ranges */ +struct _fluid_voice_zone_t +{ + fluid_inst_zone_t *inst_zone; + fluid_zone_range_t range; +}; + +/* + + Public interface + + */ + +fluid_sfont_t *fluid_defsfloader_load(fluid_sfloader_t *loader, const char *filename); + + +int fluid_defsfont_sfont_delete(fluid_sfont_t *sfont); +const char *fluid_defsfont_sfont_get_name(fluid_sfont_t *sfont); +fluid_preset_t *fluid_defsfont_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum); +void fluid_defsfont_sfont_iteration_start(fluid_sfont_t *sfont); +fluid_preset_t *fluid_defsfont_sfont_iteration_next(fluid_sfont_t *sfont); + + +void fluid_defpreset_preset_delete(fluid_preset_t *preset); +const char *fluid_defpreset_preset_get_name(fluid_preset_t *preset); +int fluid_defpreset_preset_get_banknum(fluid_preset_t *preset); +int fluid_defpreset_preset_get_num(fluid_preset_t *preset); +int fluid_defpreset_preset_noteon(fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel); + +int fluid_zone_inside_range(fluid_zone_range_t *zone_range, int key, int vel); + +/* + * fluid_defsfont_t + */ +struct _fluid_defsfont_t +{ + const fluid_file_callbacks_t *fcbs; /* the file callbacks used to load this Soundfont */ + char *filename; /* the filename of this soundfont */ + unsigned int samplepos; /* the position in the file at which the sample data starts */ + unsigned int samplesize; /* the size of the sample data in bytes */ + short *sampledata; /* the sample data, loaded in ram */ + + unsigned int sample24pos; /* position within sffd of the sm24 chunk, set to zero if no 24 bit sample support */ + unsigned int sample24size; /* length within sffd of the sm24 chunk */ + char *sample24data; /* if not NULL, the least significant byte of the 24bit sample data, loaded in ram */ + + fluid_sfont_t *sfont; /* pointer to parent sfont */ + fluid_list_t *sample; /* the samples in this soundfont */ + fluid_list_t *preset; /* the presets of this soundfont */ + fluid_list_t *inst; /* the instruments of this soundfont */ + int mlock; /* Should we try memlock (avoid swapping)? */ + int dynamic_samples; /* Enables dynamic sample loading if set */ + + fluid_list_t *preset_iter_cur; /* the current preset in the iteration */ +}; + + +fluid_defsfont_t *new_fluid_defsfont(fluid_settings_t *settings); +int delete_fluid_defsfont(fluid_defsfont_t *defsfont); +int fluid_defsfont_load(fluid_defsfont_t *defsfont, const fluid_file_callbacks_t *file_callbacks, const char *file); +const char *fluid_defsfont_get_name(fluid_defsfont_t *defsfont); +fluid_preset_t *fluid_defsfont_get_preset(fluid_defsfont_t *defsfont, int bank, int prenum); +void fluid_defsfont_iteration_start(fluid_defsfont_t *defsfont); +fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t *defsfont); +int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample); +int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata); + +int fluid_defsfont_add_sample(fluid_defsfont_t *defsfont, fluid_sample_t *sample); +int fluid_defsfont_add_preset(fluid_defsfont_t *defsfont, fluid_defpreset_t *defpreset); + + +/* + * fluid_preset_t + */ +struct _fluid_defpreset_t +{ + fluid_defpreset_t *next; + char name[21]; /* the name of the preset */ + unsigned int bank; /* the bank number */ + unsigned int num; /* the preset number */ + fluid_preset_zone_t *global_zone; /* the global zone of the preset */ + fluid_preset_zone_t *zone; /* the chained list of preset zones */ + int pinned; /* preset samples pinned to sample cache? */ +}; + +fluid_defpreset_t *new_fluid_defpreset(void); +void delete_fluid_defpreset(fluid_defpreset_t *defpreset); +fluid_defpreset_t *fluid_defpreset_next(fluid_defpreset_t *defpreset); +int fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, SFPreset *sfpreset, fluid_defsfont_t *defsfont, SFData *sfdata); +int fluid_defpreset_set_global_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone); +int fluid_defpreset_add_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone); +fluid_preset_zone_t *fluid_defpreset_get_zone(fluid_defpreset_t *defpreset); +fluid_preset_zone_t *fluid_defpreset_get_global_zone(fluid_defpreset_t *defpreset); +int fluid_defpreset_get_banknum(fluid_defpreset_t *defpreset); +int fluid_defpreset_get_num(fluid_defpreset_t *defpreset); +const char *fluid_defpreset_get_name(fluid_defpreset_t *defpreset); +int fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int chan, int key, int vel); + +/* + * fluid_preset_zone + */ +struct _fluid_preset_zone_t +{ + fluid_preset_zone_t *next; + char *name; + fluid_inst_t *inst; + fluid_list_t *voice_zone; + fluid_zone_range_t range; + fluid_gen_t gen[GEN_LAST]; + fluid_mod_t *mod; /* List of modulators */ +}; + +fluid_preset_zone_t *new_fluid_preset_zone(char *name); +void delete_fluid_list_mod(fluid_mod_t *mod); +void delete_fluid_preset_zone(fluid_preset_zone_t *zone); +fluid_preset_zone_t *fluid_preset_zone_next(fluid_preset_zone_t *zone); +int fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, fluid_preset_zone_t *global_zone, SFZone *sfzone, fluid_defsfont_t *defssfont, SFData *sfdata); +fluid_inst_t *fluid_preset_zone_get_inst(fluid_preset_zone_t *zone); + +/* + * fluid_inst_t + */ +struct _fluid_inst_t +{ + char name[21]; + int source_idx; /* Index of instrument in source Soundfont */ + fluid_inst_zone_t *global_zone; + fluid_inst_zone_t *zone; +}; + +fluid_inst_t *new_fluid_inst(void); +fluid_inst_t *fluid_inst_import_sfont(int inst_idx, fluid_defsfont_t *defsfont, SFData *sfdata); +void delete_fluid_inst(fluid_inst_t *inst); +int fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone); +int fluid_inst_add_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone); +fluid_inst_zone_t *fluid_inst_get_zone(fluid_inst_t *inst); +fluid_inst_zone_t *fluid_inst_get_global_zone(fluid_inst_t *inst); + +/* + * fluid_inst_zone_t + */ +struct _fluid_inst_zone_t +{ + fluid_inst_zone_t *next; + char *name; + fluid_sample_t *sample; + fluid_zone_range_t range; + fluid_gen_t gen[GEN_LAST]; + fluid_mod_t *mod; /* List of modulators */ +}; + + +fluid_inst_zone_t *new_fluid_inst_zone(char *name); +void delete_fluid_inst_zone(fluid_inst_zone_t *zone); +fluid_inst_zone_t *fluid_inst_zone_next(fluid_inst_zone_t *zone); +int fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, fluid_inst_zone_t *global_inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont, SFData *sfdata); +fluid_sample_t *fluid_inst_zone_get_sample(fluid_inst_zone_t *zone); + + +int fluid_sample_import_sfont(fluid_sample_t *sample, SFSample *sfsample, fluid_defsfont_t *defsfont); +int fluid_sample_in_rom(fluid_sample_t *sample); + + +#endif /* _FLUID_SFONT_H */ diff --git a/libs/fluidsynth/src/sfloader/fluid_instpatch.h b/libs/fluidsynth/src/sfloader/fluid_instpatch.h new file mode 100644 index 00000000000..c1838750ab7 --- /dev/null +++ b/libs/fluidsynth/src/sfloader/fluid_instpatch.h @@ -0,0 +1,14 @@ + +#ifndef _FLUID_INSTPATCH_H +#define _FLUID_INSTPATCH_H + +#include "fluid_sfont.h" +#include "fluid_settings.h" + +void fluid_instpatch_init(void); +void fluid_instpatch_deinit(void); +fluid_sfloader_t *new_fluid_instpatch_loader(fluid_settings_t *settings); + +int fluid_instpatch_supports_multi_init(void); + +#endif // _FLUID_INSTPATCH_H diff --git a/libs/fluidsynth/src/sfloader/fluid_samplecache.c b/libs/fluidsynth/src/sfloader/fluid_samplecache.c new file mode 100644 index 00000000000..64e9e9e7091 --- /dev/null +++ b/libs/fluidsynth/src/sfloader/fluid_samplecache.c @@ -0,0 +1,313 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont file loading code borrowed from Smurf SoundFont Editor + * Copyright (C) 1999-2001 Josh Green + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* CACHED SAMPLE DATA LOADER + * + * This is a wrapper around fluid_sffile_read_sample_data that attempts to cache the read + * data across all FluidSynth instances in a global (process-wide) list. + */ + +#include "fluid_samplecache.h" +#include "fluid_sys.h" +#include "fluid_list.h" + + +typedef struct _fluid_samplecache_entry_t fluid_samplecache_entry_t; + +struct _fluid_samplecache_entry_t +{ + /* The following members all form the cache key */ + char *filename; + time_t modification_time; + unsigned int sf_samplepos; + unsigned int sf_samplesize; + unsigned int sf_sample24pos; + unsigned int sf_sample24size; + unsigned int sample_start; + unsigned int sample_end; + int sample_type; + /* End of cache key members */ + + short *sample_data; + char *sample_data24; + int sample_count; + + int num_references; + int mlocked; +}; + +static fluid_list_t *samplecache_list = NULL; +static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT; + +static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, + unsigned int sample_end, int sample_type, time_t mtime); +static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, + unsigned int sample_end, int sample_type, time_t mtime); +static void delete_samplecache_entry(fluid_samplecache_entry_t *entry); + +static int fluid_get_file_modification_time(char *filename, time_t *modification_time); + + +/* PUBLIC INTERFACE */ + +int fluid_samplecache_load(SFData *sf, + unsigned int sample_start, unsigned int sample_end, int sample_type, + int try_mlock, short **sample_data, char **sample_data24) +{ + fluid_samplecache_entry_t *entry; + int ret; + time_t mtime; + + fluid_mutex_lock(samplecache_mutex); + + if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED) + { + mtime = 0; + } + + entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime); + + if(entry == NULL) + { + fluid_mutex_unlock(samplecache_mutex); + entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime); + + if(entry == NULL) + { + ret = -1; + goto unlock_exit; + } + + fluid_mutex_lock(samplecache_mutex); + samplecache_list = fluid_list_prepend(samplecache_list, entry); + } + fluid_mutex_unlock(samplecache_mutex); + + if(try_mlock && !entry->mlocked) + { + /* Lock the memory to disable paging. It's okay if this fails. It + * probably means that the user doesn't have the required permission. */ + if(fluid_mlock(entry->sample_data, entry->sample_count * sizeof(short)) == 0) + { + if(entry->sample_data24 != NULL) + { + entry->mlocked = (fluid_mlock(entry->sample_data24, entry->sample_count) == 0); + } + else + { + entry->mlocked = TRUE; + } + + if(!entry->mlocked) + { + fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short)); + FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible."); + } + } + } + + entry->num_references++; + *sample_data = entry->sample_data; + *sample_data24 = entry->sample_data24; + ret = entry->sample_count; + +unlock_exit: + return ret; +} + +int fluid_samplecache_unload(const short *sample_data) +{ + fluid_list_t *entry_list; + fluid_samplecache_entry_t *entry; + int ret; + + fluid_mutex_lock(samplecache_mutex); + + entry_list = samplecache_list; + + while(entry_list) + { + entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list); + + if(sample_data == entry->sample_data) + { + entry->num_references--; + + if(entry->num_references == 0) + { + if(entry->mlocked) + { + fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short)); + + if(entry->sample_data24 != NULL) + { + fluid_munlock(entry->sample_data24, entry->sample_count); + } + } + + samplecache_list = fluid_list_remove(samplecache_list, entry); + delete_samplecache_entry(entry); + } + + ret = FLUID_OK; + goto unlock_exit; + } + + entry_list = fluid_list_next(entry_list); + } + + FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache."); + ret = FLUID_FAILED; + +unlock_exit: + fluid_mutex_unlock(samplecache_mutex); + return ret; +} + + +/* Private functions */ +static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, + unsigned int sample_start, + unsigned int sample_end, + int sample_type, + time_t mtime) +{ + fluid_samplecache_entry_t *entry; + + entry = FLUID_NEW(fluid_samplecache_entry_t); + + if(entry == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(entry, 0, sizeof(*entry)); + + entry->filename = FLUID_STRDUP(sf->fname); + + if(entry->filename == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + entry->sf_samplepos = sf->samplepos; + entry->sf_samplesize = sf->samplesize; + entry->sf_sample24pos = sf->sample24pos; + entry->sf_sample24size = sf->sample24size; + entry->sample_start = sample_start; + entry->sample_end = sample_end; + entry->sample_type = sample_type; + entry->modification_time = mtime; + + entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type, + &entry->sample_data, &entry->sample_data24); + + if(entry->sample_count < 0) + { + goto error_exit; + } + + return entry; + +error_exit: + delete_samplecache_entry(entry); + return NULL; +} + +static void delete_samplecache_entry(fluid_samplecache_entry_t *entry) +{ + fluid_return_if_fail(entry != NULL); + + FLUID_FREE(entry->filename); + FLUID_FREE(entry->sample_data); + FLUID_FREE(entry->sample_data24); + FLUID_FREE(entry); +} + +static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, + unsigned int sample_start, + unsigned int sample_end, + int sample_type, + time_t mtime) +{ + fluid_list_t *entry_list; + fluid_samplecache_entry_t *entry; + + entry_list = samplecache_list; + + while(entry_list) + { + entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list); + + if((FLUID_STRCMP(sf->fname, entry->filename) == 0) && + (mtime == entry->modification_time) && + (sf->samplepos == entry->sf_samplepos) && + (sf->samplesize == entry->sf_samplesize) && + (sf->sample24pos == entry->sf_sample24pos) && + (sf->sample24size == entry->sf_sample24size) && + (sample_start == entry->sample_start) && + (sample_end == entry->sample_end) && + (sample_type == entry->sample_type)) + { + return entry; + } + + entry_list = fluid_list_next(entry_list); + } + + return NULL; +} + +static int fluid_get_file_modification_time(char *filename, time_t *modification_time) +{ + fluid_stat_buf_t buf; + + if(fluid_stat(filename, &buf)) + { + return FLUID_FAILED; + } + + *modification_time = buf.st_mtime; + return FLUID_OK; +} + + +/* Only used for tests */ +int fluid_samplecache_count_entries(void) +{ + fluid_list_t *entry; + int count = 0; + + fluid_mutex_lock(samplecache_mutex); + + for(entry = samplecache_list; entry != NULL; entry = fluid_list_next(entry)) + { + count++; + } + + fluid_mutex_unlock(samplecache_mutex); + + return count; +} diff --git a/libs/fluidsynth/src/sfloader/fluid_samplecache.h b/libs/fluidsynth/src/sfloader/fluid_samplecache.h new file mode 100644 index 00000000000..de6206ba7de --- /dev/null +++ b/libs/fluidsynth/src/sfloader/fluid_samplecache.h @@ -0,0 +1,37 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_SAMPLECACHE_H +#define _FLUID_SAMPLECACHE_H + +#include "fluid_sfont.h" +#include "fluid_sffile.h" + +int fluid_samplecache_load(SFData *sf, + unsigned int sample_start, unsigned int sample_end, int sample_type, + int try_mlock, short **data, char **data24); + +int fluid_samplecache_unload(const short *sample_data); + +/* Only used for tests */ +int fluid_samplecache_count_entries(void); + +#endif /* _FLUID_SAMPLECACHE_H */ diff --git a/libs/fluidsynth/src/sfloader/fluid_sffile.c b/libs/fluidsynth/src/sfloader/fluid_sffile.c new file mode 100644 index 00000000000..96d06ceae6c --- /dev/null +++ b/libs/fluidsynth/src/sfloader/fluid_sffile.c @@ -0,0 +1,2527 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont file loading code borrowed from Smurf SoundFont Editor + * Copyright (C) 1999-2001 Josh Green + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#include "fluid_sffile.h" +#include "fluid_sfont.h" +#include "fluid_sys.h" + +#if LIBSNDFILE_SUPPORT +#include +#endif + +#if LIBINSTPATCH_SUPPORT +#include +#endif + +/*=================================sfload.c======================== + Borrowed from Smurf SoundFont Editor by Josh Green + =================================================================*/ + +/* FOURCC definitions */ +#define RIFF_FCC FLUID_FOURCC('R','I','F','F') +#define LIST_FCC FLUID_FOURCC('L','I','S','T') +#define SFBK_FCC FLUID_FOURCC('s','f','b','k') +#define INFO_FCC FLUID_FOURCC('I','N','F','O') +#define SDTA_FCC FLUID_FOURCC('s','d','t','a') +#define PDTA_FCC FLUID_FOURCC('p','d','t','a') /* info/sample/preset */ + +#define IFIL_FCC FLUID_FOURCC('i','f','i','l') +#define ISNG_FCC FLUID_FOURCC('i','s','n','g') +#define INAM_FCC FLUID_FOURCC('I','N','A','M') +#define IROM_FCC FLUID_FOURCC('i','r','o','m') /* info ids (1st byte of info strings) */ +#define IVER_FCC FLUID_FOURCC('i','v','e','r') +#define ICRD_FCC FLUID_FOURCC('I','C','R','D') +#define IENG_FCC FLUID_FOURCC('I','E','N','G') +#define IPRD_FCC FLUID_FOURCC('I','P','R','D') /* more info ids */ +#define ICOP_FCC FLUID_FOURCC('I','C','O','P') +#define ICMT_FCC FLUID_FOURCC('I','C','M','T') +#define ISFT_FCC FLUID_FOURCC('I','S','F','T') /* and yet more info ids */ + +#define SNAM_FCC FLUID_FOURCC('s','n','a','m') +#define SMPL_FCC FLUID_FOURCC('s','m','p','l') /* sample ids */ +#define PHDR_FCC FLUID_FOURCC('p','h','d','r') +#define PBAG_FCC FLUID_FOURCC('p','b','a','g') +#define PMOD_FCC FLUID_FOURCC('p','m','o','d') +#define PGEN_FCC FLUID_FOURCC('p','g','e','n') /* preset ids */ +#define IHDR_FCC FLUID_FOURCC('i','n','s','t') +#define IBAG_FCC FLUID_FOURCC('i','b','a','g') +#define IMOD_FCC FLUID_FOURCC('i','m','o','d') +#define IGEN_FCC FLUID_FOURCC('i','g','e','n') /* instrument ids */ +#define SHDR_FCC FLUID_FOURCC('s','h','d','r') /* sample info */ +#define SM24_FCC FLUID_FOURCC('s','m','2','4') + +/* Set when the FCC code is unknown */ +#define UNKN_ID FLUID_N_ELEMENTS(idlist) + +/* + * This declares a uint32_t array containing the SF2 chunk identifiers. + */ +static const uint32_t idlist[] = +{ + RIFF_FCC, + LIST_FCC, + SFBK_FCC, + INFO_FCC, + SDTA_FCC, + PDTA_FCC, + + IFIL_FCC, + ISNG_FCC, + INAM_FCC, + IROM_FCC, + IVER_FCC, + ICRD_FCC, + IENG_FCC, + IPRD_FCC, + ICOP_FCC, + ICMT_FCC, + ISFT_FCC, + + SNAM_FCC, + SMPL_FCC, + PHDR_FCC, + PBAG_FCC, + PMOD_FCC, + PGEN_FCC, + IHDR_FCC, + IBAG_FCC, + IMOD_FCC, + IGEN_FCC, + SHDR_FCC, + SM24_FCC +}; + +static const unsigned short invalid_inst_gen[] = +{ + GEN_UNUSED1, + GEN_UNUSED2, + GEN_UNUSED3, + GEN_UNUSED4, + GEN_RESERVED1, + GEN_RESERVED2, + GEN_RESERVED3, + GEN_INSTRUMENT, +}; + +static const unsigned short invalid_preset_gen[] = +{ + GEN_STARTADDROFS, + GEN_ENDADDROFS, + GEN_STARTLOOPADDROFS, + GEN_ENDLOOPADDROFS, + GEN_STARTADDRCOARSEOFS, + GEN_ENDADDRCOARSEOFS, + GEN_STARTLOOPADDRCOARSEOFS, + GEN_KEYNUM, + GEN_VELOCITY, + GEN_ENDLOOPADDRCOARSEOFS, + GEN_SAMPLEMODE, + GEN_EXCLUSIVECLASS, + GEN_OVERRIDEROOTKEY, + GEN_SAMPLEID, +}; + + +/* sfont file chunk sizes */ +#define SF_PHDR_SIZE (38) +#define SF_BAG_SIZE (4) +#define SF_MOD_SIZE (10) +#define SF_GEN_SIZE (4) +#define SF_IHDR_SIZE (22) +#define SF_SHDR_SIZE (46) + + +#define READCHUNK(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(var, 8, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + ((SFChunk *)(var))->size = FLUID_LE32TOH(((SFChunk *)(var))->size); \ + } while (0) + +#define READD(sf, var) \ + do \ + { \ + uint32_t _temp; \ + if (sf->fcbs->fread(&_temp, 4, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + var = FLUID_LE32TOH(_temp); \ + } while (0) + +#define READW(sf, var) \ + do \ + { \ + uint16_t _temp; \ + if (sf->fcbs->fread(&_temp, 2, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + var = FLUID_LE16TOH(_temp); \ + } while (0) + +#define READID(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(var, 4, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +#define READSTR(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(var, 20, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + (*var)[20] = '\0'; \ + } while (0) + +#define READB(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(&var, 1, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +#define FSKIP(sf, size) \ + do \ + { \ + if (sf->fcbs->fseek(sf->sffd, size, SEEK_CUR) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +#define FSKIPW(sf) \ + do \ + { \ + if (sf->fcbs->fseek(sf->sffd, 2, SEEK_CUR) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +/* removes and advances a fluid_list_t pointer */ +#define SLADVREM(list, item) \ + do \ + { \ + fluid_list_t *_temp = item; \ + item = fluid_list_next(item); \ + list = fluid_list_remove_link(list, _temp); \ + delete1_fluid_list(_temp); \ + } while (0) + + +static int load_header(SFData *sf); +static int load_body(SFData *sf); +static int process_info(SFData *sf, int size); +static int process_sdta(SFData *sf, unsigned int size); +static int process_pdta(SFData *sf, int size); +static int load_phdr(SFData *sf, unsigned int size); +static int load_pbag(SFData *sf, int size); +static int load_pmod(SFData *sf, int size); +static int load_ihdr(SFData *sf, unsigned int size); +static int load_ibag(SFData *sf, int size); +static int load_imod(SFData *sf, int size); +static int load_shdr(SFData *sf, unsigned int size); + +static int chunkid(uint32_t id); +static int read_listchunk(SFData *sf, SFChunk *chunk); +static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size); +static int preset_compare_func(const void *a, const void *b); +static fluid_list_t *find_gen_by_id(int gen, fluid_list_t *genlist); +static int valid_inst_genid(unsigned short genid); +static int valid_preset_genid(unsigned short genid); + +static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data); +static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24); + +/** + * Check if a file is a SoundFont file. + * + * @param filename Path to the file to check + * @return TRUE if it could be a SF2, SF3 or DLS file, FALSE otherwise + * + * If fluidsynth was built with DLS support, this function will also identify DLS files. + * + * @note This function only checks whether header(s) in the RIFF chunk are present. + * A call to fluid_synth_sfload() might still fail. + */ +int fluid_is_soundfont(const char *filename) +{ + FILE *fp; + uint32_t fcc; + int retcode = FALSE; + const char* err_msg; + + do + { + if((fp = fluid_file_open(filename, &err_msg)) == NULL) + { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): fopen() failed: '%s'", err_msg); + return retcode; + } + + if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) + { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): failed to read RIFF chunk id."); + break; + } + + if(fcc != RIFF_FCC) + { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): expected RIFF chunk id '0x%04X' but got '0x%04X'.", (unsigned int) RIFF_FCC, (unsigned int)fcc); + break; + } + + if(FLUID_FSEEK(fp, 4, SEEK_CUR)) + { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): cannot seek +4 bytes."); + break; + } + + if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) + { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): failed to read SFBK chunk id."); + break; + } + + retcode = (fcc == SFBK_FCC); + if(retcode) + { + break; // seems to be SF2, stop here + } +#ifdef LIBINSTPATCH_SUPPORT + else + { + IpatchFileHandle *fhandle = ipatch_file_identify_open(filename, NULL); + if(fhandle != NULL) + { + retcode = (ipatch_file_identify(fhandle->file, NULL) == IPATCH_TYPE_DLS_FILE); + ipatch_file_close(fhandle); + } + } +#endif + } + while(0); + + FLUID_FCLOSE(fp); + + return retcode; +} + +/* + * Open a SoundFont file and parse it's contents into a SFData structure. + * + * @param fname filename + * @param fcbs file callback structure + * @return the partially parsed SoundFont as SFData structure or NULL on error + */ +SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs) +{ + SFData *sf; + fluid_long_long_t fsize = 0; + + if(!(sf = FLUID_NEW(SFData))) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(sf, 0, sizeof(SFData)); + + fluid_rec_mutex_init(sf->mtx); + sf->fcbs = fcbs; + + if((sf->sffd = fcbs->fopen(fname)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Unable to open file '%s'", fname); + goto error_exit; + } + + sf->fname = FLUID_STRDUP(fname); + + if(sf->fname == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + /* get size of file by seeking to end */ + if(fcbs->fseek(sf->sffd, 0L, SEEK_END) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Seek to end of file failed"); + goto error_exit; + } + + if((fsize = fcbs->ftell(sf->sffd)) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Get end of file position failed"); + goto error_exit; + } + + sf->filesize = fsize; + + if(fcbs->fseek(sf->sffd, 0, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Rewind to start of file failed"); + goto error_exit; + } + + if(!load_header(sf)) + { + goto error_exit; + } + + return sf; + +error_exit: + fluid_sffile_close(sf); + return NULL; +} + +/* + * Parse all preset information from the soundfont + * + * @return FLUID_OK on success, otherwise FLUID_FAILED + */ +int fluid_sffile_parse_presets(SFData *sf) +{ + if(!load_body(sf)) + { + return FLUID_FAILED; + } + + return FLUID_OK; +} + +/* Load sample data from the soundfont file + * + * This function will always return the sample data in WAV format. If the sample_type specifies an + * Ogg Vorbis compressed sample, it will be decompressed automatically before returning. + * + * @param sf SFData instance + * @param sample_start index of first sample point in Soundfont sample chunk + * @param sample_end index of last sample point in Soundfont sample chunk + * @param sample_type type of the sample in Soundfont + * @param data pointer to sample data pointer, will point to loaded sample data on success + * @param data24 pointer to 24-bit sample data pointer if 24-bit data present, will point to loaded + * 24-bit sample data on success or NULL if no 24-bit data is present in file + * + * @return The number of sample words in returned buffers or -1 on failure + */ +int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end, + int sample_type, short **data, char **data24) +{ + int num_samples; + + if(sample_type & FLUID_SAMPLETYPE_OGG_VORBIS) + { + num_samples = fluid_sffile_read_vorbis(sf, sample_start, sample_end, data); + } + else + { + num_samples = fluid_sffile_read_wav(sf, sample_start, sample_end, data, data24); + } + + return num_samples; +} + +/* + * Close a SoundFont file and free the SFData structure. + * + * @param sf pointer to SFData structure + * @param fcbs file callback structure + */ +void fluid_sffile_close(SFData *sf) +{ + fluid_list_t *entry; + SFPreset *preset; + SFInst *inst; + + fluid_rec_mutex_destroy(sf->mtx); + if(sf->sffd) + { + sf->fcbs->fclose(sf->sffd); + } + + FLUID_FREE(sf->fname); + + entry = sf->info; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->info); + + entry = sf->preset; + + while(entry) + { + preset = (SFPreset *)fluid_list_get(entry); + delete_preset(preset); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->preset); + + entry = sf->inst; + + while(entry) + { + inst = (SFInst *)fluid_list_get(entry); + delete_inst(inst); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->inst); + + entry = sf->sample; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->sample); + + FLUID_FREE(sf); +} + + +/* + * Private functions + */ + +/* sound font file load functions */ +static int chunkid(uint32_t id) +{ + unsigned int i; + + for(i = 0; i < FLUID_N_ELEMENTS(idlist); i++) + { + if(idlist[i] == id) + { + break; + } + } + + /* Return chunk id or UNKN_ID if not found */ + return i; +} + +static int load_header(SFData *sf) +{ + SFChunk chunk; + + READCHUNK(sf, &chunk); /* load RIFF chunk */ + + if(chunk.id != RIFF_FCC) + { + /* error if not RIFF */ + FLUID_LOG(FLUID_ERR, "Not a RIFF file"); + return FALSE; + } + + READID(sf, &chunk.id); /* load file ID */ + + if(chunk.id != SFBK_FCC) + { + /* error if not SFBK_ID */ + FLUID_LOG(FLUID_ERR, "Not a SoundFont file"); + return FALSE; + } + + if(chunk.size != sf->filesize - 8) + { + FLUID_LOG(FLUID_ERR, "SoundFont file size mismatch"); + return FALSE; + } + + /* Process INFO block */ + if(!read_listchunk(sf, &chunk)) + { + return FALSE; + } + + if(chunk.id != INFO_FCC) + { + FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting INFO chunk"); + return FALSE; + } + + if(!process_info(sf, chunk.size)) + { + return FALSE; + } + + /* Process sample chunk */ + if(!read_listchunk(sf, &chunk)) + { + return FALSE; + } + + if(chunk.id != SDTA_FCC) + { + FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting SAMPLE chunk"); + return FALSE; + } + + if(!process_sdta(sf, chunk.size)) + { + return FALSE; + } + + /* process HYDRA chunk */ + if(!read_listchunk(sf, &chunk)) + { + return FALSE; + } + + if(chunk.id != PDTA_FCC) + { + FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk"); + return FALSE; + } + + sf->hydrapos = sf->fcbs->ftell(sf->sffd); + sf->hydrasize = chunk.size; + + return TRUE; +} + +static int load_body(SFData *sf) +{ + if(sf->fcbs->fseek(sf->sffd, sf->hydrapos, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to seek to HYDRA position"); + return FALSE; + } + + if(!process_pdta(sf, sf->hydrasize)) + { + return FALSE; + } + + /* sort preset list by bank, preset # */ + sf->preset = fluid_list_sort(sf->preset, preset_compare_func); + + return TRUE; +} + +static int read_listchunk(SFData *sf, SFChunk *chunk) +{ + READCHUNK(sf, chunk); /* read list chunk */ + + if(chunk->id != LIST_FCC) /* error if ! list chunk */ + { + FLUID_LOG(FLUID_ERR, "Invalid chunk id in level 0 parse"); + return FALSE; + } + + READID(sf, &chunk->id); /* read id string */ + chunk->size -= 4; + return TRUE; +} + +static int process_info(SFData *sf, int size) +{ + SFChunk chunk; + union + { + char *chr; + uint32_t *fcc; + } item; + unsigned short ver; + + while(size > 0) + { + READCHUNK(sf, &chunk); + size -= 8; + + if(chunk.id == IFIL_FCC) + { + /* sound font version chunk? */ + if(chunk.size != 4) + { + FLUID_LOG(FLUID_ERR, "Sound font version info chunk has invalid size"); + return FALSE; + } + + READW(sf, ver); + sf->version.major = ver; + READW(sf, ver); + sf->version.minor = ver; + + if(sf->version.major < 2) + { + FLUID_LOG(FLUID_ERR, "Sound font version is %d.%d which is not" + " supported, convert to version 2.0x", + sf->version.major, sf->version.minor); + return FALSE; + } + + if(sf->version.major == 3) + { +#if !LIBSNDFILE_SUPPORT + FLUID_LOG(FLUID_WARN, + "Sound font version is %d.%d but fluidsynth was compiled without" + " support for (v3.x)", + sf->version.major, sf->version.minor); + return FALSE; +#endif + } + else if(sf->version.major > 2) + { + FLUID_LOG(FLUID_WARN, + "Sound font version is %d.%d which is newer than" + " what this version of fluidsynth was designed for (v2.0x)", + sf->version.major, sf->version.minor); + return FALSE; + } + } + else if(chunk.id == IVER_FCC) + { + /* ROM version chunk? */ + if(chunk.size != 4) + { + FLUID_LOG(FLUID_ERR, "ROM version info chunk has invalid size"); + return FALSE; + } + + READW(sf, ver); + sf->romver.major = ver; + READW(sf, ver); + sf->romver.minor = ver; + } + else if(chunkid(chunk.id) != UNKN_ID) + { + if((chunk.id != ICMT_FCC && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2)) + { + FLUID_LOG(FLUID_ERR, "INFO sub chunk %.4s has invalid chunk size of %d bytes", + (char*)&chunk.id, chunk.size); + return FALSE; + } + + /* alloc for chunk fcc and da chunk */ + if(!(item.fcc = FLUID_MALLOC(chunk.size + sizeof(uint32_t) + 1))) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + /* attach to INFO list, fluid_sffile_close will cleanup if FAIL occurs */ + sf->info = fluid_list_append(sf->info, item.fcc); + + /* save chunk fcc and update pointer to data value */ + *item.fcc++ = chunk.id; + + if(sf->fcbs->fread(item.chr, chunk.size, sf->sffd) == FLUID_FAILED) + { + return FALSE; + } + + /* force terminate info item */ + item.chr[chunk.size] = '\0'; + } + else + { + FLUID_LOG(FLUID_ERR, "Invalid chunk id in INFO chunk"); + return FALSE; + } + + size -= chunk.size; + } + + if(size < 0) + { + FLUID_LOG(FLUID_ERR, "INFO chunk size mismatch"); + return FALSE; + } + + return TRUE; +} + +static int process_sdta(SFData *sf, unsigned int size) +{ + SFChunk chunk; + + if(size == 0) + { + return TRUE; /* no sample data? */ + } + + /* read sub chunk */ + READCHUNK(sf, &chunk); + size -= 8; + + if(chunk.id != SMPL_FCC) + { + FLUID_LOG(FLUID_ERR, "Expected SMPL chunk found invalid id instead"); + return FALSE; + } + + /* SDTA chunk may also contain sm24 chunk for 24 bit samples + * (not yet supported), only an error if SMPL chunk size is + * greater than SDTA. */ + if(chunk.size > size) + { + FLUID_LOG(FLUID_ERR, "SDTA chunk size mismatch"); + return FALSE; + } + + /* sample data follows */ + sf->samplepos = sf->fcbs->ftell(sf->sffd); + + /* used to check validity of sample headers */ + sf->samplesize = chunk.size; + + FSKIP(sf, chunk.size); + size -= chunk.size; + + if(sf->version.major >= 2 && sf->version.minor >= 4) + { + /* any chance to find another chunk here? */ + if(size > 8) + { + /* read sub chunk */ + READCHUNK(sf, &chunk); + size -= 8; + + if(chunk.id == SM24_FCC) + { + int sm24size, sdtahalfsize; + + FLUID_LOG(FLUID_DBG, "Found SM24 chunk"); + + if(chunk.size > size) + { + FLUID_LOG(FLUID_WARN, "SM24 exceeds SDTA chunk, ignoring SM24"); + goto ret; // no error + } + + sdtahalfsize = sf->samplesize / 2; + /* + 1 byte in the case that half the size of smpl chunk is an odd value */ + sdtahalfsize += sdtahalfsize % 2; + sm24size = chunk.size; + + if(sdtahalfsize != sm24size) + { + FLUID_LOG(FLUID_WARN, "SM24 not equal to half the size of SMPL chunk (0x%X != " + "0x%X), ignoring SM24", + sm24size, sdtahalfsize); + goto ret; // no error + } + + /* sample data24 follows */ + sf->sample24pos = sf->fcbs->ftell(sf->sffd); + sf->sample24size = sm24size; + } + } + } + +ret: + FSKIP(sf, size); + + return TRUE; +} + +static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size) +{ + READCHUNK(sf, chunk); + *size -= 8; + + if(chunk->id != expid) + { + FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", (char*)&expid); + return FALSE; + } + + if(chunk->size % reclen) /* valid chunk size? */ + { + FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", (char*)&expid, reclen); + return FALSE; + } + + if((*size -= chunk->size) < 0) + { + FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", (char*)&expid); + return FALSE; + } + + return TRUE; +} + +static int process_pdta(SFData *sf, int size) +{ + SFChunk chunk; + + if(!pdtahelper(sf, PHDR_FCC, SF_PHDR_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_phdr(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, PBAG_FCC, SF_BAG_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_pbag(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, PMOD_FCC, SF_MOD_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_pmod(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, PGEN_FCC, SF_GEN_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_pgen(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IHDR_FCC, SF_IHDR_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_ihdr(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IBAG_FCC, SF_BAG_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_ibag(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IMOD_FCC, SF_MOD_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_imod(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IGEN_FCC, SF_GEN_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_igen(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, SHDR_FCC, SF_SHDR_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_shdr(sf, chunk.size)) + { + return FALSE; + } + + return TRUE; +} + +/* preset header loader */ +static int load_phdr(SFData *sf, unsigned int size) +{ + unsigned int i; + int i2; + SFPreset *preset, *prev_preset = NULL; + unsigned short pbag_idx, prev_pbag_idx = 0; + + if(size % SF_PHDR_SIZE || size == 0) + { + FLUID_LOG(FLUID_ERR, "Preset header chunk size is invalid"); + return FALSE; + } + + i = size / SF_PHDR_SIZE - 1; + + if(i == 0) + { + /* at least one preset + term record */ + FLUID_LOG(FLUID_WARN, "File contains no presets"); + FSKIP(sf, SF_PHDR_SIZE); + return TRUE; + } + + for(; i > 0; i--) + { + /* load all preset headers */ + if((preset = FLUID_NEW(SFPreset)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + sf->preset = fluid_list_append(sf->preset, preset); + preset->zone = NULL; /* In case of failure, fluid_sffile_close can cleanup */ + READSTR(sf, &preset->name); /* possible read failure ^ */ + READW(sf, preset->prenum); + READW(sf, preset->bank); + READW(sf, pbag_idx); + FSKIP(sf, 4); /* library ignored */ + FSKIP(sf, 4); /* genre ignored */ + FSKIP(sf, 4); /* morphology ignored */ + + if(prev_preset) + { + /* not first preset? */ + if(pbag_idx < prev_pbag_idx) + { + FLUID_LOG(FLUID_ERR, "Preset header indices not monotonic"); + return FALSE; + } + + i2 = pbag_idx - prev_pbag_idx; + + while(i2--) + { + prev_preset->zone = fluid_list_prepend(prev_preset->zone, NULL); + } + } + else if(pbag_idx > 0) /* 1st preset, warn if ofs >0 */ + { + FLUID_LOG(FLUID_WARN, "%d preset zones not referenced, discarding", pbag_idx); + } + + prev_preset = preset; /* update preset ptr */ + prev_pbag_idx = pbag_idx; + } + + FSKIP(sf, 24); + READW(sf, pbag_idx); /* Read terminal generator index */ + FSKIP(sf, 12); + + if(pbag_idx < prev_pbag_idx) + { + FLUID_LOG(FLUID_ERR, "Preset header indices not monotonic"); + return FALSE; + } + + i2 = pbag_idx - prev_pbag_idx; + + while(i2--) + { + prev_preset->zone = fluid_list_prepend(prev_preset->zone, NULL); + } + + return TRUE; +} + +/* preset bag loader */ +static int load_pbag(SFData *sf, int size) +{ + fluid_list_t *preset_list; + fluid_list_t *zone_list; + SFZone *z, *pz = NULL; + unsigned short genndx, modndx; + unsigned short pgenndx = 0, pmodndx = 0; + unsigned short i; + + if(size % SF_BAG_SIZE || size == 0) /* size is multiple of SF_BAG_SIZE? */ + { + FLUID_LOG(FLUID_ERR, "Preset bag chunk size is invalid"); + return FALSE; + } + + preset_list = sf->preset; + + /* traverse through presets */ + while(preset_list) + { + zone_list = ((SFPreset *)(preset_list->data))->zone; + + /* traverse preset's zones */ + while(zone_list) + { + if((size -= SF_BAG_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset bag chunk size mismatch"); + return FALSE; + } + + if((z = FLUID_NEW(SFZone)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + zone_list->data = z; + z->gen = NULL; /* Init gen and mod before possible failure, */ + z->mod = NULL; /* to ensure proper cleanup (fluid_sffile_close) */ + READW(sf, genndx); /* possible read failure ^ */ + READW(sf, modndx); + + if(pz) + { + /* if not first zone */ + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag modulator indices not monotonic"); + return FALSE; + } + + i = genndx - pgenndx; + + while(i--) + { + pz->gen = fluid_list_prepend(pz->gen, NULL); + } + + i = modndx - pmodndx; + + while(i--) + { + pz->mod = fluid_list_prepend(pz->mod, NULL); + } + } + + pz = z; /* update previous zone ptr */ + pgenndx = genndx; /* update previous zone gen index */ + pmodndx = modndx; /* update previous zone mod index */ + zone_list = fluid_list_next(zone_list); + } + + preset_list = fluid_list_next(preset_list); + } + + size -= SF_BAG_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Preset bag chunk size mismatch"); + return FALSE; + } + + READW(sf, genndx); + READW(sf, modndx); + + if(!pz) + { + if(genndx > 0) + { + FLUID_LOG(FLUID_WARN, "No preset generators and terminal index not 0"); + } + + if(modndx > 0) + { + FLUID_LOG(FLUID_WARN, "No preset modulators and terminal index not 0"); + } + + return TRUE; + } + + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag modulator indices not monotonic"); + return FALSE; + } + + i = genndx - pgenndx; + + while(i--) + { + pz->gen = fluid_list_prepend(pz->gen, NULL); + } + + i = modndx - pmodndx; + + while(i--) + { + pz->mod = fluid_list_prepend(pz->mod, NULL); + } + + return TRUE; +} + +/* preset modulator loader */ +static int load_pmod(SFData *sf, int size) +{ + fluid_list_t *preset_list; + fluid_list_t *zone_list; + fluid_list_t *mod_list; + SFMod *m; + + preset_list = sf->preset; + + while(preset_list) + { + /* traverse through all presets */ + zone_list = ((SFPreset *)(preset_list->data))->zone; + + while(zone_list) + { + /* traverse this preset's zones */ + mod_list = ((SFZone *)(zone_list->data))->mod; + + while(mod_list) + { + /* load zone's modulators */ + if((size -= SF_MOD_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset modulator chunk size mismatch"); + return FALSE; + } + + if((m = FLUID_NEW(SFMod)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + mod_list->data = m; + READW(sf, m->src); + READW(sf, m->dest); + READW(sf, m->amount); + READW(sf, m->amtsrc); + READW(sf, m->trans); + mod_list = fluid_list_next(mod_list); + } + + zone_list = fluid_list_next(zone_list); + } + + preset_list = fluid_list_next(preset_list); + } + + /* + If there isn't even a terminal record + Hmmm, the specs say there should be one, but.. + */ + if(size == 0) + { + return TRUE; + } + + size -= SF_MOD_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Preset modulator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_MOD_SIZE); /* terminal mod */ + + return TRUE; +} + +/* ------------------------------------------------------------------- + * preset generator loader + * generator (per preset) loading rules: + * Zones with no generators or modulators shall be annihilated + * Global zone must be 1st zone, discard additional ones (instrumentless zones) + * + * generator (per zone) loading rules (in order of decreasing precedence): + * KeyRange is 1st in list (if exists), else discard + * if a VelRange exists only preceded by a KeyRange, else discard + * if a generator follows an instrument discard it + * if a duplicate generator exists replace previous one + * ------------------------------------------------------------------- */ +int load_pgen(SFData *sf, int size) +{ + fluid_list_t *dup; + fluid_list_t *preset_list; + fluid_list_t *zone_list; + fluid_list_t *gen_list; + SFZone *zone; + SFGen *g; + SFPreset *preset; + SFGenAmount genval; + unsigned short genid; + int level, skip, drop, discarded; + + preset_list = sf->preset; + + while(preset_list) + { + preset = fluid_list_get(preset_list); + + /* traverse through all presets */ + discarded = FALSE; + zone_list = preset->zone; + + /* traverse preset's zones */ + while(zone_list) + { + zone = fluid_list_get(zone_list); + level = 0; + gen_list = zone->gen; + + while(gen_list) + { + /* load zone's generators */ + dup = NULL; + skip = FALSE; + drop = FALSE; + + if((size -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); + return FALSE; + } + + READW(sf, genid); + + if(genid == GEN_KEYRANGE) + { + /* nothing precedes */ + if(level == 0) + { + level = 1; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == GEN_VELRANGE) + { + /* only KeyRange precedes */ + if(level <= 1) + { + level = 2; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == GEN_INSTRUMENT) + { + /* inst is last gen */ + level = 3; + READW(sf, genval.uword); + } + else + { + level = 2; + + if(valid_preset_genid(genid)) + { + /* generator valid? */ + READW(sf, genval.sword); + dup = find_gen_by_id(genid, zone->gen); + } + else + { + skip = TRUE; + } + } + + if(!skip) + { + if(!dup) + { + /* if gen ! dup alloc new */ + if((g = FLUID_NEW(SFGen)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + gen_list->data = g; + g->id = genid; + } + else + { + g = (SFGen *)(dup->data); /* ptr to orig gen */ + drop = TRUE; + } + + g->amount = genval; + } + else + { + /* Skip this generator */ + discarded = TRUE; + drop = TRUE; + FSKIPW(sf); + } + + if(!drop) + { + gen_list = fluid_list_next(gen_list); /* next gen */ + } + else + { + SLADVREM(zone->gen, gen_list); /* drop place holder */ + } + + /* GEN_INSTRUMENT should be the last generator */ + if (level == 3) + { + break; + } + + } /* generator loop */ + + /* Anything below level 3 means it's a global zone. The global zone + * should always be the first zone in the list, so discard any + * other global zones we encounter */ + if(level < 3 && (zone_list != preset->zone)) + { + /* advance to next zone before deleting the current list element */ + zone_list = fluid_list_next(zone_list); + + FLUID_LOG(FLUID_WARN, "Preset '%s': Discarding invalid global zone", + preset->name); + preset->zone = fluid_list_remove(preset->zone, zone); + delete_zone(zone); + + /* we have already advanced the zone_list pointer, so continue with next zone */ + continue; + } + + /* All remaining generators are invalid and should be discarded + * (because they come after an instrument generator) */ + while(gen_list) + { + discarded = TRUE; + + if((size -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); + SLADVREM(zone->gen, gen_list); + } + + zone_list = fluid_list_next(zone_list); + } + + if(discarded) + { + FLUID_LOG(FLUID_WARN, + "Preset '%s': Some invalid generators were discarded", + preset->name); + } + + preset_list = fluid_list_next(preset_list); + } + + /* in case there isn't a terminal record */ + if(size == 0) + { + return TRUE; + } + + size -= SF_GEN_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); /* terminal gen */ + + return TRUE; +} + +/* instrument header loader */ +static int load_ihdr(SFData *sf, unsigned int size) +{ + unsigned int i; + int i2; + SFInst *inst, *prev_inst = NULL; /* ptr to current & previous instrument */ + unsigned short zndx, pzndx = 0; + + if(size % SF_IHDR_SIZE || size == 0) /* chunk size is valid? */ + { + FLUID_LOG(FLUID_ERR, "Instrument header has invalid size"); + return FALSE; + } + + size = size / SF_IHDR_SIZE - 1; + + if(size == 0) + { + /* at least one preset + term record */ + FLUID_LOG(FLUID_WARN, "File contains no instruments"); + FSKIP(sf, SF_IHDR_SIZE); + return TRUE; + } + + for(i = 0; i < size; i++) + { + /* load all instrument headers */ + if((inst = FLUID_NEW(SFInst)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + sf->inst = fluid_list_append(sf->inst, inst); + inst->zone = NULL; /* For proper cleanup if fail (fluid_sffile_close) */ + inst->idx = i; + READSTR(sf, &inst->name); /* Possible read failure ^ */ + READW(sf, zndx); + + if(prev_inst) + { + /* not first instrument? */ + if(zndx < pzndx) + { + FLUID_LOG(FLUID_ERR, "Instrument header indices not monotonic"); + return FALSE; + } + + i2 = zndx - pzndx; + + while(i2--) + { + prev_inst->zone = fluid_list_prepend(prev_inst->zone, NULL); + } + } + else if(zndx > 0) /* 1st inst, warn if ofs >0 */ + { + FLUID_LOG(FLUID_WARN, "%d instrument zones not referenced, discarding", zndx); + } + + pzndx = zndx; + prev_inst = inst; /* update instrument ptr */ + } + + FSKIP(sf, 20); + READW(sf, zndx); + + if(zndx < pzndx) + { + FLUID_LOG(FLUID_ERR, "Instrument header indices not monotonic"); + return FALSE; + } + + i2 = zndx - pzndx; + + while(i2--) + { + prev_inst->zone = fluid_list_prepend(prev_inst->zone, NULL); + } + + return TRUE; +} + +/* instrument bag loader */ +static int load_ibag(SFData *sf, int size) +{ + fluid_list_t *inst_list; + fluid_list_t *zone_list; + SFZone *z, *pz = NULL; + unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0; + int i; + + if(size % SF_BAG_SIZE || size == 0) /* size is multiple of SF_BAG_SIZE? */ + { + FLUID_LOG(FLUID_ERR, "Instrument bag chunk size is invalid"); + return FALSE; + } + + inst_list = sf->inst; + + while(inst_list) + { + /* traverse through inst */ + zone_list = ((SFInst *)(inst_list->data))->zone; + + while(zone_list) + { + /* load this inst's zones */ + if((size -= SF_BAG_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Instrument bag chunk size mismatch"); + return FALSE; + } + + if((z = FLUID_NEW(SFZone)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + zone_list->data = z; + z->gen = NULL; /* In case of failure, */ + z->mod = NULL; /* fluid_sffile_close can clean up */ + READW(sf, genndx); /* READW = possible read failure */ + READW(sf, modndx); + + if(pz) + { + /* if not first zone */ + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Instrument generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator indices not monotonic"); + return FALSE; + } + + i = genndx - pgenndx; + + while(i--) + { + pz->gen = fluid_list_prepend(pz->gen, NULL); + } + + i = modndx - pmodndx; + + while(i--) + { + pz->mod = fluid_list_prepend(pz->mod, NULL); + } + } + + pz = z; /* update previous zone ptr */ + pgenndx = genndx; + pmodndx = modndx; + zone_list = fluid_list_next(zone_list); + } + + inst_list = fluid_list_next(inst_list); + } + + size -= SF_BAG_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Instrument chunk size mismatch"); + return FALSE; + } + + READW(sf, genndx); + READW(sf, modndx); + + if(!pz) + { + /* in case that all are no zoners */ + if(genndx > 0) + { + FLUID_LOG(FLUID_WARN, "No instrument generators and terminal index not 0"); + } + + if(modndx > 0) + { + FLUID_LOG(FLUID_WARN, "No instrument modulators and terminal index not 0"); + } + + return TRUE; + } + + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Instrument generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator indices not monotonic"); + return FALSE; + } + + i = genndx - pgenndx; + + while(i--) + { + pz->gen = fluid_list_prepend(pz->gen, NULL); + } + + i = modndx - pmodndx; + + while(i--) + { + pz->mod = fluid_list_prepend(pz->mod, NULL); + } + + return TRUE; +} + +/* instrument modulator loader */ +static int load_imod(SFData *sf, int size) +{ + fluid_list_t *inst_list; + fluid_list_t *zone_list; + fluid_list_t *mod_list; + SFMod *m; + + inst_list = sf->inst; + + while(inst_list) + { + /* traverse through all inst */ + zone_list = ((SFInst *)(inst_list->data))->zone; + + while(zone_list) + { + /* traverse this inst's zones */ + mod_list = ((SFZone *)(zone_list->data))->mod; + + while(mod_list) + { + /* load zone's modulators */ + if((size -= SF_MOD_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator chunk size mismatch"); + return FALSE; + } + + if((m = FLUID_NEW(SFMod)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + mod_list->data = m; + READW(sf, m->src); + READW(sf, m->dest); + READW(sf, m->amount); + READW(sf, m->amtsrc); + READW(sf, m->trans); + mod_list = fluid_list_next(mod_list); + } + + zone_list = fluid_list_next(zone_list); + } + + inst_list = fluid_list_next(inst_list); + } + + /* + If there isn't even a terminal record + Hmmm, the specs say there should be one, but.. + */ + if(size == 0) + { + return TRUE; + } + + size -= SF_MOD_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_MOD_SIZE); /* terminal mod */ + + return TRUE; +} + +/* load instrument generators (see load_pgen for loading rules) */ +int load_igen(SFData *sf, int size) +{ + fluid_list_t *dup; + fluid_list_t *inst_list; + fluid_list_t *zone_list; + fluid_list_t *gen_list; + SFZone *zone; + SFGen *g; + SFInst *inst; + SFGenAmount genval; + unsigned short genid; + int level, skip, drop, discarded; + + inst_list = sf->inst; + + /* traverse through all instruments */ + while(inst_list) + { + inst = fluid_list_get(inst_list); + + discarded = FALSE; + zone_list = inst->zone; + + /* traverse this instrument's zones */ + while(zone_list) + { + zone = fluid_list_get(zone_list); + + level = 0; + gen_list = zone->gen; + + while(gen_list) + { + /* load zone's generators */ + dup = NULL; + skip = FALSE; + drop = FALSE; + + if((size -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "IGEN chunk size mismatch"); + return FALSE; + } + + READW(sf, genid); + + if(genid == GEN_KEYRANGE) + { + /* nothing precedes */ + if(level == 0) + { + level = 1; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == GEN_VELRANGE) + { + /* only KeyRange precedes */ + if(level <= 1) + { + level = 2; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == GEN_SAMPLEID) + { + /* sample is last gen */ + level = 3; + READW(sf, genval.uword); + } + else + { + level = 2; + + if(valid_inst_genid(genid)) + { + /* gen valid? */ + READW(sf, genval.sword); + dup = find_gen_by_id(genid, zone->gen); + } + else + { + skip = TRUE; + } + } + + if(!skip) + { + if(!dup) + { + /* if gen ! dup alloc new */ + if((g = FLUID_NEW(SFGen)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + gen_list->data = g; + g->id = genid; + } + else + { + g = (SFGen *)(dup->data); + drop = TRUE; + } + + g->amount = genval; + } + else + { + /* skip this generator */ + discarded = TRUE; + drop = TRUE; + FSKIPW(sf); + } + + if(!drop) + { + gen_list = fluid_list_next(gen_list); /* next gen */ + } + else + { + SLADVREM(zone->gen, gen_list); + } + + /* GEN_SAMPLEID should be last generator */ + if (level == 3) + { + break; + } + + } /* generator loop */ + + /* Anything below level 3 means it's a global zone. The global zone + * should always be the first zone in the list, so discard any + * other global zones we encounter */ + if(level < 3 && (zone_list != inst->zone)) + { + /* advance to next zone before deleting the current list element */ + zone_list = fluid_list_next(zone_list); + + FLUID_LOG(FLUID_WARN, "Instrument '%s': Discarding invalid global zone", + inst->name); + inst->zone = fluid_list_remove(inst->zone, zone); + delete_zone(zone); + + /* we have already advanced the zone_list pointer, so continue with next zone */ + continue; + } + + /* All remaining generators must be invalid and should be discarded + * (because they come after a sampleid generator) */ + while(gen_list) + { + discarded = TRUE; + + if((size -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Instrument generator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); + SLADVREM(zone->gen, gen_list); + } + + zone_list = fluid_list_next(zone_list); /* next zone */ + } + + if(discarded) + { + FLUID_LOG(FLUID_WARN, + "Instrument '%s': Some invalid generators were discarded", + inst->name); + } + + inst_list = fluid_list_next(inst_list); + } + + /* for those non-terminal record cases, grr! */ + if(size == 0) + { + return TRUE; + } + + size -= SF_GEN_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "IGEN chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); /* terminal gen */ + + return TRUE; +} + +/* sample header loader */ +static int load_shdr(SFData *sf, unsigned int size) +{ + unsigned int i; + SFSample *p; + + if(size % SF_SHDR_SIZE || size == 0) /* size is multiple of SHDR size? */ + { + FLUID_LOG(FLUID_ERR, "Sample header has invalid size"); + return FALSE; + } + + size = size / SF_SHDR_SIZE - 1; + + if(size == 0) + { + /* at least one sample + term record? */ + FLUID_LOG(FLUID_WARN, "File contains no samples"); + FSKIP(sf, SF_SHDR_SIZE); + return TRUE; + } + + /* load all sample headers */ + for(i = 0; i < size; i++) + { + if((p = FLUID_NEW(SFSample)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + p->idx = i; + + sf->sample = fluid_list_prepend(sf->sample, p); + READSTR(sf, &p->name); + READD(sf, p->start); + READD(sf, p->end); + READD(sf, p->loopstart); + READD(sf, p->loopend); + READD(sf, p->samplerate); + READB(sf, p->origpitch); + READB(sf, p->pitchadj); + FSKIPW(sf); /* skip sample link */ + READW(sf, p->sampletype); + } + + FSKIP(sf, SF_SHDR_SIZE); /* skip terminal shdr */ + + return TRUE; +} + +void delete_preset(SFPreset *preset) +{ + fluid_list_t *entry; + SFZone *zone; + + if(!preset) + { + return; + } + + entry = preset->zone; + + while(entry) + { + zone = (SFZone *)fluid_list_get(entry); + delete_zone(zone); + entry = fluid_list_next(entry); + } + + delete_fluid_list(preset->zone); + + FLUID_FREE(preset); +} + +void delete_inst(SFInst *inst) +{ + fluid_list_t *entry; + SFZone *zone; + + if(!inst) + { + return; + } + + entry = inst->zone; + + while(entry) + { + zone = (SFZone *)fluid_list_get(entry); + delete_zone(zone); + entry = fluid_list_next(entry); + } + + delete_fluid_list(inst->zone); + + FLUID_FREE(inst); +} + + +/* Free all elements of a zone (Preset or Instrument) */ +void delete_zone(SFZone *zone) +{ + fluid_list_t *entry; + + if(!zone) + { + return; + } + + entry = zone->gen; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(zone->gen); + + entry = zone->mod; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(zone->mod); + + FLUID_FREE(zone); +} + +/* preset sort function, first by bank, then by preset # */ +static int preset_compare_func(const void *a, const void *b) +{ + int aval, bval; + + aval = (int)(((const SFPreset *)a)->bank) << 16 | ((const SFPreset *)a)->prenum; + bval = (int)(((const SFPreset *)b)->bank) << 16 | ((const SFPreset *)b)->prenum; + + return (aval - bval); +} + +/* Find a generator by its id in the passed in list. + * + * @return pointer to SFGen if found, otherwise NULL + */ +static fluid_list_t *find_gen_by_id(int gen, fluid_list_t *genlist) +{ + /* is generator in gen list? */ + fluid_list_t *p; + + p = genlist; + + while(p) + { + if(p->data == NULL) + { + return NULL; + } + + if(gen == ((SFGen *)p->data)->id) + { + break; + } + + p = fluid_list_next(p); + } + + return p; +} + +/* check validity of instrument generator */ +static int valid_inst_genid(unsigned short genid) +{ + size_t i; + + /* OVERRIDEROOTKEY is the last official generator, everything + * following it are generators internal to FluidSynth and will + * never appear in a SoundFont file. */ + if(genid > GEN_OVERRIDEROOTKEY) + { + return FALSE; + } + + for(i = 0; i < FLUID_N_ELEMENTS(invalid_inst_gen); i++) + { + if (invalid_inst_gen[i] == genid) + { + return FALSE; + } + } + + return TRUE; +} + +/* check validity of preset generator */ +static int valid_preset_genid(unsigned short genid) +{ + size_t i; + + if(!valid_inst_genid(genid)) + { + return FALSE; + } + + for(i = 0; i < FLUID_N_ELEMENTS(invalid_preset_gen); i++) + { + if (invalid_preset_gen[i] == genid) + { + return FALSE; + } + } + + return TRUE; +} + + +static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24) +{ + short *loaded_data = NULL; + char *loaded_data24 = NULL; + unsigned int num_samples; + + fluid_return_val_if_fail((end + 1) > start , -1); + + num_samples = (end + 1) - start; + + if((start * sizeof(short) > sf->samplesize) || (end * sizeof(short) > sf->samplesize)) + { + FLUID_LOG(FLUID_ERR, "Sample offsets exceed sample data chunk"); + goto error_exit; + } + + /* Load 16-bit sample data */ + if(sf->fcbs->fseek(sf->sffd, sf->samplepos + (start * sizeof(short)), SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to seek to sample position"); + goto error_exit; + } + + loaded_data = FLUID_ARRAY(short, num_samples); + + if(loaded_data == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + if(sf->fcbs->fread(loaded_data, num_samples * sizeof(short), sf->sffd) == FLUID_FAILED) + { +#if FLUID_VERSION_CHECK(FLUIDSYNTH_VERSION_MAJOR, FLUIDSYNTH_VERSION_MINOR, FLUIDSYNTH_VERSION_MICRO) < FLUID_VERSION_CHECK(2,2,0) + if((int)(num_samples * sizeof(short)) < 0) + { + FLUID_LOG(FLUID_INFO, + "This SoundFont seems to be bigger than 2GB, which is not supported in this version of fluidsynth. " + "You need to use at least fluidsynth 2.2.0"); + } +#endif + FLUID_LOG(FLUID_ERR, "Failed to read sample data"); + goto error_exit; + } + + /* If this machine is big endian, byte swap the 16 bit samples */ + if(FLUID_IS_BIG_ENDIAN) + { + unsigned int i; + + for(i = 0; i < num_samples; i++) + { + loaded_data[i] = FLUID_LE16TOH(loaded_data[i]); + } + } + + *data = loaded_data; + + /* Optionally load additional 8 bit sample data for 24-bit support. Any failures while loading + * the 24-bit sample data will be logged as errors but won't prevent the sample reading to + * fail, as sound output is still possible with the 16-bit sample data. */ + if(sf->sample24pos) + { + if((start > sf->sample24size) || (end > sf->sample24size)) + { + FLUID_LOG(FLUID_ERR, "Sample offsets exceed 24-bit sample data chunk"); + goto error24_exit; + } + + if(sf->fcbs->fseek(sf->sffd, sf->sample24pos + start, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to seek position for 24-bit sample data in data file"); + goto error24_exit; + } + + loaded_data24 = FLUID_ARRAY(char, num_samples); + + if(loaded_data24 == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory reading 24-bit sample data"); + goto error24_exit; + } + + if(sf->fcbs->fread(loaded_data24, num_samples, sf->sffd) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to read 24-bit sample data"); + goto error24_exit; + } + } + + *data24 = loaded_data24; + + return num_samples; + +error24_exit: + FLUID_LOG(FLUID_WARN, "Ignoring 24-bit sample data, sound quality might suffer"); + FLUID_FREE(loaded_data24); + *data24 = NULL; + return num_samples; + +error_exit: + FLUID_FREE(loaded_data); + FLUID_FREE(loaded_data24); + return -1; +} + + +/* Ogg Vorbis loading and decompression */ +#if LIBSNDFILE_SUPPORT + +/* Virtual file access routines to allow loading individually compressed + * samples from the Soundfont sample data chunk using the file callbacks + * passing in during opening of the file */ +typedef struct _sfvio_data_t +{ + SFData *sffile; + sf_count_t start; /* start byte offset of compressed data */ + sf_count_t end; /* end byte offset of compressed data */ + sf_count_t offset; /* current virtual file offset from start byte offset */ + +} sfvio_data_t; + +static sf_count_t sfvio_get_filelen(void *user_data) +{ + sfvio_data_t *data = user_data; + + return (data->end + 1) - data->start; +} + +static sf_count_t sfvio_seek(sf_count_t offset, int whence, void *user_data) +{ + sfvio_data_t *data = user_data; + SFData *sf = data->sffile; + sf_count_t new_offset; + + switch(whence) + { + case SEEK_SET: + new_offset = offset; + break; + + case SEEK_CUR: + new_offset = data->offset + offset; + break; + + case SEEK_END: + new_offset = sfvio_get_filelen(user_data) + offset; + break; + + default: + goto fail; /* proper error handling not possible?? */ + } + + new_offset += data->start; + fluid_rec_mutex_lock(sf->mtx); + if (data->start <= new_offset && new_offset <= data->end && + sf->fcbs->fseek(sf->sffd, new_offset, SEEK_SET) != FLUID_FAILED) + { + data->offset = new_offset - data->start; + } + fluid_rec_mutex_unlock(sf->mtx); + +fail: + return data->offset; +} + +static sf_count_t sfvio_read(void *ptr, sf_count_t count, void *user_data) +{ + sfvio_data_t *data = user_data; + SFData *sf = data->sffile; + sf_count_t remain; + + remain = sfvio_get_filelen(user_data) - data->offset; + + if(count > remain) + { + count = remain; + } + + if(count == 0) + { + return count; + } + + fluid_rec_mutex_lock(sf->mtx); + if (sf->fcbs->fseek(sf->sffd, data->start + data->offset, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "This should never happen: fseek failed in sfvoid_read()"); + count = 0; + } + else + { + if (sf->fcbs->fread(ptr, count, sf->sffd) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to read compressed sample data"); + count = 0; + } + } + fluid_rec_mutex_unlock(sf->mtx); + + data->offset += count; + + return count; +} + +static sf_count_t sfvio_tell(void *user_data) +{ + sfvio_data_t *data = user_data; + + return data->offset; +} + +/** + * Read Ogg Vorbis compressed data from the Soundfont and decompress it, returning the number of samples + * in the decompressed WAV. Only 16-bit mono samples are supported. + * + * Note that this function takes byte indices for start and end source data. The sample headers in SF3 + * files use byte indices, so those pointers can be passed directly to this function. + * + * This function uses a virtual file structure in order to read the Ogg Vorbis + * data from arbitrary locations in the source file. + */ +static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data) +{ + SNDFILE *sndfile; + SF_INFO sfinfo; + SF_VIRTUAL_IO sfvio = + { + sfvio_get_filelen, + sfvio_seek, + sfvio_read, + NULL, + sfvio_tell + }; + sfvio_data_t sfdata; + short *wav_data = NULL; + + if((start_byte > sf->samplesize) || (end_byte > sf->samplesize)) + { + FLUID_LOG(FLUID_ERR, "Ogg Vorbis data offsets exceed sample data chunk"); + return -1; + } + + // Initialize file position indicator and SF_INFO structure + sfdata.sffile = sf; + sfdata.start = sf->samplepos + start_byte; + sfdata.end = sf->samplepos + end_byte; + sfdata.offset = -1; + + /* Seek to sfdata.start, the beginning of Ogg Vorbis data in Soundfont */ + sfvio_seek(0, SEEK_SET, &sfdata); + if (sfdata.offset != 0) + { + FLUID_LOG(FLUID_ERR, "Failed to seek to compressed sample position"); + return -1; + } + + FLUID_MEMSET(&sfinfo, 0, sizeof(sfinfo)); + + // Open sample as a virtual file + sndfile = sf_open_virtual(&sfvio, SFM_READ, &sfinfo, &sfdata); + + if(!sndfile) + { + FLUID_LOG(FLUID_ERR, "sf_open_virtual(): %s", sf_strerror(sndfile)); + return -1; + } + + // Empty sample + if(sfinfo.frames <= 0 || sfinfo.channels <= 0) + { + FLUID_LOG(FLUID_DBG, "Empty decompressed sample"); + *data = NULL; + sf_close(sndfile); + return 0; + } + + // Mono sample + if(sfinfo.channels != 1) + { + FLUID_LOG(FLUID_DBG, "Unsupported channel count %d in ogg sample", sfinfo.channels); + goto error_exit; + } + + if((sfinfo.format & SF_FORMAT_OGG) == 0) + { + FLUID_LOG(FLUID_WARN, "OGG sample is not OGG compressed, this is not officially supported"); + } + + wav_data = FLUID_ARRAY(short, sfinfo.frames * sfinfo.channels); + + if(!wav_data) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + /* Automatically decompresses the Ogg Vorbis data to 16-bit PCM */ + if(sf_readf_short(sndfile, wav_data, sfinfo.frames) < sfinfo.frames) + { + FLUID_LOG(FLUID_DBG, "Decompression failed!"); + FLUID_LOG(FLUID_ERR, "sf_readf_short(): %s", sf_strerror(sndfile)); + goto error_exit; + } + + sf_close(sndfile); + + *data = wav_data; + + return sfinfo.frames; + +error_exit: + FLUID_FREE(wav_data); + sf_close(sndfile); + return -1; +} +#else +static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data) +{ + return -1; +} +#endif diff --git a/libs/fluidsynth/src/sfloader/fluid_sffile.h b/libs/fluidsynth/src/sfloader/fluid_sffile.h new file mode 100644 index 00000000000..5275c6252bc --- /dev/null +++ b/libs/fluidsynth/src/sfloader/fluid_sffile.h @@ -0,0 +1,194 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont loading code borrowed from Smurf SoundFont Editor by Josh Green + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_SFFILE_H +#define _FLUID_SFFILE_H + + +#include "fluid_gen.h" +#include "fluid_list.h" +#include "fluid_mod.h" +#include "fluidsynth.h" +#include "fluid_sys.h" + + +/* Sound Font structure defines */ + +/* Forward declarations */ +typedef union _SFGenAmount SFGenAmount; +typedef struct _SFVersion SFVersion; +typedef struct _SFMod SFMod; +typedef struct _SFGen SFGen; +typedef struct _SFZone SFZone; +typedef struct _SFSample SFSample; +typedef struct _SFInst SFInst; +typedef struct _SFPreset SFPreset; +typedef struct _SFData SFData; +typedef struct _SFChunk SFChunk; + + +struct _SFVersion +{ + /* version structure */ + unsigned short major; + unsigned short minor; +}; + +struct _SFMod +{ + /* Modulator structure */ + unsigned short src; /* source modulator */ + unsigned short dest; /* destination generator */ + signed short amount; /* signed, degree of modulation */ + unsigned short amtsrc; /* second source controls amnt of first */ + unsigned short trans; /* transform applied to source */ +}; + +union _SFGenAmount /* Generator amount structure */ +{ + signed short sword; /* signed 16 bit value */ + unsigned short uword; /* unsigned 16 bit value */ + struct + { + unsigned char lo; /* low value for ranges */ + unsigned char hi; /* high value for ranges */ + } range; +}; + +struct _SFGen +{ + /* Generator structure */ + unsigned short id; /* generator ID */ + SFGenAmount amount; /* generator value */ +}; + +struct _SFZone +{ + /* Sample/instrument zone structure */ + fluid_list_t *gen; /* list of generators */ + fluid_list_t *mod; /* list of modulators */ +}; + +struct _SFSample +{ + /* Sample structure */ + char name[21]; /* Name of sample */ + int idx; /* Index of this instrument in the Soundfont */ + unsigned int start; /* Offset in sample area to start of sample */ + unsigned int end; /* Offset from start to end of sample, + this is the last point of the + sample, the SF spec has this as the + 1st point after, corrected on + load/save */ + unsigned int loopstart; /* Offset from start to start of loop */ + unsigned int loopend; /* Offset from start to end of loop, + marks the first point after loop, + whose sample value is ideally + equivalent to loopstart */ + unsigned int samplerate; /* Sample rate recorded at */ + unsigned char origpitch; /* root midi key number */ + signed char pitchadj; /* pitch correction in cents */ + unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */ + fluid_sample_t *fluid_sample; /* Imported sample (fixed up in fluid_defsfont_load) */ +}; + +struct _SFInst +{ + /* Instrument structure */ + char name[21]; /* Name of instrument */ + int idx; /* Index of this instrument in the Soundfont */ + fluid_list_t *zone; /* list of instrument zones */ +}; + +struct _SFPreset +{ + /* Preset structure */ + char name[21]; /* preset name */ + unsigned short prenum; /* preset number */ + unsigned short bank; /* bank number */ + fluid_list_t *zone; /* list of preset zones */ +}; + +/* NOTE: sffd is also used to determine if sound font is new (NULL) */ +struct _SFData +{ + /* Sound font data structure */ + SFVersion version; /* sound font version */ + SFVersion romver; /* ROM version */ + + unsigned int filesize; + + unsigned int samplepos; /* position within sffd of the sample chunk */ + unsigned int samplesize; /* length within sffd of the sample chunk */ + + unsigned int sample24pos; /* position within sffd of the sm24 chunk, set to zero if no 24 bit + sample support */ + unsigned int sample24size; /* length within sffd of the sm24 chunk */ + + unsigned int hydrapos; + unsigned int hydrasize; + + char *fname; /* file name */ + FILE *sffd; /* loaded sfont file descriptor */ + const fluid_file_callbacks_t *fcbs; /* file callbacks used to read this file */ + + fluid_rec_mutex_t mtx; /* this mutex can be used to synchronize calls to fcbs when using multiple threads (e.g. SF3 loading) */ + + fluid_list_t *info; /* linked list of info strings (1st byte is ID) */ + fluid_list_t *preset; /* linked list of preset info */ + fluid_list_t *inst; /* linked list of instrument info */ + fluid_list_t *sample; /* linked list of sample info */ +}; + +/* functions */ + + +/*-----------------------------------sffile.h----------------------------*/ +/* + File structures and routines (used to be in sffile.h) +*/ + +/* sfont file data structures */ +struct _SFChunk +{ + /* RIFF file chunk structure */ + unsigned int id; /* chunk id */ + unsigned int size; /* size of the following chunk */ +}; + +/* Public functions */ +SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs); +void fluid_sffile_close(SFData *sf); +int fluid_sffile_parse_presets(SFData *sf); +int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end, + int sample_type, short **data, char **data24); + + +/* extern only for unit test purposes */ +int load_igen(SFData *sf, int size); +int load_pgen(SFData *sf, int size); +void delete_preset(SFPreset *preset); +void delete_inst(SFInst *inst); +void delete_zone(SFZone *zone); + +#endif /* _FLUID_SFFILE_H */ diff --git a/libs/fluidsynth/src/sfloader/fluid_sfont.c b/libs/fluidsynth/src/sfloader/fluid_sfont.c new file mode 100644 index 00000000000..00423c00356 --- /dev/null +++ b/libs/fluidsynth/src/sfloader/fluid_sfont.c @@ -0,0 +1,850 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sfont.h" +#include "fluid_sys.h" + + +void *default_fopen(const char *path) +{ + const char* msg; + FILE* handle = fluid_file_open(path, &msg); + + if(handle == NULL) + { + FLUID_LOG(FLUID_ERR, "fluid_sfloader_load(): Failed to open '%s': %s", path, msg); + } + + return handle; +} + +int default_fclose(void *handle) +{ + return FLUID_FCLOSE((FILE *)handle) == 0 ? FLUID_OK : FLUID_FAILED; +} + +fluid_long_long_t default_ftell(void *handle) +{ + return FLUID_FTELL((FILE *)handle); +} + +#ifdef _WIN32 +#define FLUID_PRIi64 "I64d" +#else +#define FLUID_PRIi64 "lld" +#endif + +int safe_fread(void *buf, fluid_long_long_t count, void *fd) +{ + if(FLUID_FREAD(buf, (size_t)count, 1, (FILE *)fd) != 1) + { + if(feof((FILE *)fd)) + { + FLUID_LOG(FLUID_ERR, "EOF while attempting to read %" FLUID_PRIi64 " bytes", count); + } + else + { + FLUID_LOG(FLUID_ERR, "File read failed"); + } + + return FLUID_FAILED; + } + + return FLUID_OK; +} + +int safe_fseek(void *fd, fluid_long_long_t ofs, int whence) +{ + if(FLUID_FSEEK((FILE *)fd, ofs, whence) != 0) + { + FLUID_LOG(FLUID_ERR, "File seek failed with offset = %" FLUID_PRIi64 " and whence = %d", ofs, whence); + return FLUID_FAILED; + } + + return FLUID_OK; +} + +#undef FLUID_PRIi64 + +/** + * Creates a new SoundFont loader. + * + * @param load Pointer to a function that provides a #fluid_sfont_t (see #fluid_sfloader_load_t). + * @param free Pointer to a function that destroys this instance (see #fluid_sfloader_free_t). + * Unless any private data needs to be freed it is sufficient to set this to delete_fluid_sfloader(). + * + * @return the SoundFont loader instance on success, NULL otherwise. + */ +fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free) +{ + fluid_sfloader_t *loader; + + fluid_return_val_if_fail(load != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + loader = FLUID_NEW(fluid_sfloader_t); + + if(loader == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(loader, 0, sizeof(*loader)); + + loader->load = load; + loader->free = free; + fluid_sfloader_set_callbacks(loader, + default_fopen, + safe_fread, + safe_fseek, + default_ftell, + default_fclose); + + return loader; +} + +/** + * Frees a SoundFont loader created with new_fluid_sfloader(). + * + * @param loader The SoundFont loader instance to free. + */ +void delete_fluid_sfloader(fluid_sfloader_t *loader) +{ + fluid_return_if_fail(loader != NULL); + + FLUID_FREE(loader); +} + +/** + * Specify private data to be used by #fluid_sfloader_load_t. + * + * @param loader The SoundFont loader instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data) +{ + fluid_return_val_if_fail(loader != NULL, FLUID_FAILED); + + loader->data = data; + return FLUID_OK; +} + +/** + * Obtain private data previously set with fluid_sfloader_set_data(). + * + * @param loader The SoundFont loader instance. + * @return The private data or NULL if none explicitly set before. + */ +void *fluid_sfloader_get_data(fluid_sfloader_t *loader) +{ + fluid_return_val_if_fail(loader != NULL, NULL); + + return loader->data; +} + +/** + * Set custom callbacks to be used upon soundfont loading. + * + * @param loader The SoundFont loader instance. + * @param open A function implementing #fluid_sfloader_callback_open_t. + * @param read A function implementing #fluid_sfloader_callback_read_t. + * @param seek A function implementing #fluid_sfloader_callback_seek_t. + * @param tell A function implementing #fluid_sfloader_callback_tell_t. + * @param close A function implementing #fluid_sfloader_callback_close_t. + * @return #FLUID_OK if the callbacks have been successfully set, #FLUID_FAILED otherwise. + * + * Useful for loading a soundfont from memory, see \a doc/fluidsynth_sfload_mem.c as an example. + * + */ +int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader, + fluid_sfloader_callback_open_t open, + fluid_sfloader_callback_read_t read, + fluid_sfloader_callback_seek_t seek, + fluid_sfloader_callback_tell_t tell, + fluid_sfloader_callback_close_t close) +{ + fluid_file_callbacks_t *cb; + + fluid_return_val_if_fail(loader != NULL, FLUID_FAILED); + fluid_return_val_if_fail(open != NULL, FLUID_FAILED); + fluid_return_val_if_fail(read != NULL, FLUID_FAILED); + fluid_return_val_if_fail(seek != NULL, FLUID_FAILED); + fluid_return_val_if_fail(tell != NULL, FLUID_FAILED); + fluid_return_val_if_fail(close != NULL, FLUID_FAILED); + + cb = &loader->file_callbacks; + + cb->fopen = open; + cb->fread = read; + cb->fseek = seek; + cb->ftell = tell; + cb->fclose = close; + + // NOTE: if we ever make the instpatch loader public, this may return FLUID_FAILED + return FLUID_OK; +} + +/** + * Creates a new virtual SoundFont instance structure. + * + * @param get_name A function implementing #fluid_sfont_get_name_t. + * @param get_preset A function implementing #fluid_sfont_get_preset_t. + * @param iter_start A function implementing #fluid_sfont_iteration_start_t, or NULL if preset iteration not needed. + * @param iter_next A function implementing #fluid_sfont_iteration_next_t, or NULL if preset iteration not needed. + * @param free A function implementing #fluid_sfont_free_t. + * @return The soundfont instance on success or NULL otherwise. + */ +fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name, + fluid_sfont_get_preset_t get_preset, + fluid_sfont_iteration_start_t iter_start, + fluid_sfont_iteration_next_t iter_next, + fluid_sfont_free_t free) +{ + fluid_sfont_t *sfont; + + fluid_return_val_if_fail(get_name != NULL, NULL); + fluid_return_val_if_fail(get_preset != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + sfont = FLUID_NEW(fluid_sfont_t); + + if(sfont == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(sfont, 0, sizeof(*sfont)); + + sfont->get_name = get_name; + sfont->get_preset = get_preset; + sfont->iteration_start = iter_start; + sfont->iteration_next = iter_next; + sfont->free = free; + + return sfont; +} + +/** + * Set private data to use with a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data) +{ + fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); + + sfont->data = data; + return FLUID_OK; +} + +/** + * Retrieve the private data of a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @return The private data or NULL if none explicitly set before. + */ +void *fluid_sfont_get_data(fluid_sfont_t *sfont) +{ + fluid_return_val_if_fail(sfont != NULL, NULL); + + return sfont->data; +} + +/** + * Retrieve the unique ID of a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @return The SoundFont ID. + */ +int fluid_sfont_get_id(fluid_sfont_t *sfont) +{ + return sfont->id; +} + +/** + * Retrieve the name of a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @return The name of the SoundFont. + */ +const char *fluid_sfont_get_name(fluid_sfont_t *sfont) +{ + return sfont->get_name(sfont); +} + +/** + * Retrieve the preset assigned the a SoundFont instance for the given bank and preset number. + * + * @param sfont The SoundFont instance. + * @param bank bank number of the preset + * @param prenum program number of the preset + * @return The preset instance or NULL if none found. + */ +fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum) +{ + return sfont->get_preset(sfont, bank, prenum); +} + + +/** + * Starts / re-starts virtual preset iteration in a SoundFont. + * + * @param sfont Virtual SoundFont instance + */ +void fluid_sfont_iteration_start(fluid_sfont_t *sfont) +{ + fluid_return_if_fail(sfont != NULL); + fluid_return_if_fail(sfont->iteration_start != NULL); + + sfont->iteration_start(sfont); +} + +/** + * Virtual SoundFont preset iteration function. + * + * Returns preset information to the caller and advances the + * internal iteration state to the next preset for subsequent calls. + * @param sfont The SoundFont instance. + * @return NULL when no more presets are available, otherwise the a pointer to the current preset + */ +fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont) +{ + fluid_return_val_if_fail(sfont != NULL, NULL); + fluid_return_val_if_fail(sfont->iteration_next != NULL, NULL); + + return sfont->iteration_next(sfont); +} + +/** + * Destroys a SoundFont instance created with new_fluid_sfont(). + * + * @param sfont The SoundFont instance to destroy. + * @return Always returns 0. + * + * Implements #fluid_sfont_free_t. + * + */ +int delete_fluid_sfont(fluid_sfont_t *sfont) +{ + fluid_return_val_if_fail(sfont != NULL, 0); + + FLUID_FREE(sfont); + return 0; +} + +/** + * Create a virtual SoundFont preset instance. + * + * @param parent_sfont The SoundFont instance this preset shall belong to + * @param get_name A function implementing #fluid_preset_get_name_t + * @param get_bank A function implementing #fluid_preset_get_banknum_t + * @param get_num A function implementing #fluid_preset_get_num_t + * @param noteon A function implementing #fluid_preset_noteon_t + * @param free A function implementing #fluid_preset_free_t + * @return The preset instance on success, NULL otherwise. + */ +fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont, + fluid_preset_get_name_t get_name, + fluid_preset_get_banknum_t get_bank, + fluid_preset_get_num_t get_num, + fluid_preset_noteon_t noteon, + fluid_preset_free_t free) +{ + fluid_preset_t *preset; + + fluid_return_val_if_fail(parent_sfont != NULL, NULL); + fluid_return_val_if_fail(get_name != NULL, NULL); + fluid_return_val_if_fail(get_bank != NULL, NULL); + fluid_return_val_if_fail(get_num != NULL, NULL); + fluid_return_val_if_fail(noteon != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + preset = FLUID_NEW(fluid_preset_t); + + if(preset == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(preset, 0, sizeof(*preset)); + + preset->sfont = parent_sfont; + preset->get_name = get_name; + preset->get_banknum = get_bank; + preset->get_num = get_num; + preset->noteon = noteon; + preset->free = free; + + return preset; +} + +/** + * Set private data to use with a SoundFont preset instance. + * + * @param preset The SoundFont preset instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_preset_set_data(fluid_preset_t *preset, void *data) +{ + fluid_return_val_if_fail(preset != NULL, FLUID_FAILED); + + preset->data = data; + return FLUID_OK; +} + +/** + * Retrieve the private data of a SoundFont preset instance. + * + * @param preset The SoundFont preset instance. + * @return The private data or NULL if none explicitly set before. + */ +void *fluid_preset_get_data(fluid_preset_t *preset) +{ + fluid_return_val_if_fail(preset != NULL, NULL); + + return preset->data; +} + +/** + * Retrieves the presets name by executing the \p get_name function + * provided on its creation. + * + * @param preset The SoundFont preset instance. + * @return Pointer to a NULL-terminated string containing the presets name. + */ +const char *fluid_preset_get_name(fluid_preset_t *preset) +{ + return preset->get_name(preset); +} + +/** + * Retrieves the presets bank number by executing the \p get_bank function + * provided on its creation. + * + * @param preset The SoundFont preset instance. + * @return The bank number of \p preset. + */ +int fluid_preset_get_banknum(fluid_preset_t *preset) +{ + return preset->get_banknum(preset); +} + +/** + * Retrieves the presets (instrument) number by executing the \p get_num function + * provided on its creation. + * + * @param preset The SoundFont preset instance. + * @return The number of \p preset. + */ +int fluid_preset_get_num(fluid_preset_t *preset) +{ + return preset->get_num(preset); +} + +/** + * Retrieves the presets parent SoundFont instance. + * + * @param preset The SoundFont preset instance. + * @return The parent SoundFont of \p preset. + */ +fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset) +{ + return preset->sfont; +} + +/** + * Destroys a SoundFont preset instance created with new_fluid_preset(). + * + * @param preset The SoundFont preset instance to destroy. + * + * Implements #fluid_preset_free_t. + * + */ +void delete_fluid_preset(fluid_preset_t *preset) +{ + fluid_return_if_fail(preset != NULL); + + FLUID_FREE(preset); +} + +/** + * Create a new sample instance. + * + * @return The sample on success, NULL otherwise. + */ +fluid_sample_t * +new_fluid_sample() +{ + fluid_sample_t *sample = NULL; + + sample = FLUID_NEW(fluid_sample_t); + + if(sample == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(sample, 0, sizeof(*sample)); + + return sample; +} + +/** + * Destroy a sample instance previously created with new_fluid_sample(). + * + * @param sample The sample to destroy. + */ +void +delete_fluid_sample(fluid_sample_t *sample) +{ + fluid_return_if_fail(sample != NULL); + + if(sample->auto_free) + { + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + } + + FLUID_FREE(sample); +} + +/** + * Returns the size of the fluid_sample_t structure. + * + * @return Size of fluid_sample_t in bytes + * + * Useful in low latency scenarios e.g. to allocate a pool of samples. + * + * @note It is recommend to zero initialize the memory before using the object. + * + * @warning Do NOT allocate samples on the stack and assign them to a voice! + */ +size_t fluid_sample_sizeof() +{ + return sizeof(fluid_sample_t); +} + +/** + * Set the name of a SoundFont sample. + * + * @param sample SoundFont sample + * @param name Name to assign to sample (20 chars in length + zero terminator) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_sample_set_name(fluid_sample_t *sample, const char *name) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + + FLUID_STRNCPY(sample->name, name, sizeof(sample->name)); + return FLUID_OK; +} + +/** + * Assign sample data to a SoundFont sample. + * + * @param sample SoundFont sample + * @param data Buffer containing 16 bit (mono-)audio sample data + * @param data24 If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples + * @param nbframes Number of samples in \a data + * @param sample_rate Sampling rate of the sample data + * @param copy_data TRUE to copy the sample data (and automatically free it upon delete_fluid_sample()), FALSE to use it directly (and not free it) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note If \a copy_data is FALSE, data should have 8 unused frames at start + * and 8 unused frames at the end and \a nbframes should be >=48 + */ +int +fluid_sample_set_sound_data(fluid_sample_t *sample, + short *data, + char *data24, + unsigned int nbframes, + unsigned int sample_rate, + short copy_data + ) +{ + /* the number of samples before the start and after the end */ +#define SAMPLE_LOOP_MARGIN 8U + + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(data != NULL, FLUID_FAILED); + fluid_return_val_if_fail(nbframes != 0, FLUID_FAILED); + + /* in case we already have some data */ + if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free) + { + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + } + + sample->data = NULL; + sample->data24 = NULL; + + if(copy_data) + { + unsigned int storedNbFrames; + + /* nbframes should be >= 48 (SoundFont specs) */ + storedNbFrames = nbframes; + + if(storedNbFrames < 48) + { + storedNbFrames = 48; + } + + storedNbFrames += 2 * SAMPLE_LOOP_MARGIN; + + sample->data = FLUID_ARRAY(short, storedNbFrames); + + if(sample->data == NULL) + { + goto error_rec; + } + + FLUID_MEMSET(sample->data, 0, storedNbFrames * sizeof(short)); + FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes * sizeof(short)); + + if(data24 != NULL) + { + sample->data24 = FLUID_ARRAY(char, storedNbFrames); + + if(sample->data24 == NULL) + { + goto error_rec; + } + + FLUID_MEMSET(sample->data24, 0, storedNbFrames); + FLUID_MEMCPY(sample->data24 + SAMPLE_LOOP_MARGIN, data24, nbframes * sizeof(char)); + } + + /* pointers */ + /* all from the start of data */ + sample->start = SAMPLE_LOOP_MARGIN; + sample->end = SAMPLE_LOOP_MARGIN + nbframes - 1; + } + else + { + /* we cannot assure the SAMPLE_LOOP_MARGIN */ + sample->data = data; + sample->data24 = data24; + sample->start = 0; + sample->end = nbframes - 1; + } + + sample->samplerate = sample_rate; + sample->sampletype = FLUID_SAMPLETYPE_MONO; + sample->auto_free = copy_data; + + return FLUID_OK; + +error_rec: + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + sample->data = NULL; + sample->data24 = NULL; + return FLUID_FAILED; + +#undef SAMPLE_LOOP_MARGIN +} + +/** + * Set the loop of a sample. + * + * @param sample SoundFont sample + * @param loop_start Start sample index of the loop. + * @param loop_end End index of the loop (must be a valid sample as it marks the last sample to be played). + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + + sample->loopstart = loop_start; + sample->loopend = loop_end; + + return FLUID_OK; +} + +/** + * Set the pitch of a sample. + * + * @param sample SoundFont sample + * @param root_key Root MIDI note of sample (0-127) + * @param fine_tune Fine tune in cents + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(0 <= root_key && root_key <= 127, FLUID_FAILED); + + sample->origpitch = root_key; + sample->pitchadj = fine_tune; + + return FLUID_OK; +} + + +/** + * Validate parameters of a sample + * + */ +int fluid_sample_validate(fluid_sample_t *sample, unsigned int buffer_size) +{ +#define EXCLUSIVE_FLAGS (FLUID_SAMPLETYPE_MONO | FLUID_SAMPLETYPE_RIGHT | FLUID_SAMPLETYPE_LEFT) + static const unsigned int supported_flags = EXCLUSIVE_FLAGS | FLUID_SAMPLETYPE_LINKED | FLUID_SAMPLETYPE_OGG_VORBIS | FLUID_SAMPLETYPE_ROM; + + /* ROM samples are unusable for us by definition */ + if(sample->sampletype & FLUID_SAMPLETYPE_ROM) + { + FLUID_LOG(FLUID_WARN, "Sample '%s': ROM sample ignored", sample->name); + return FLUID_FAILED; + } + + if(sample->sampletype & ~supported_flags) + { + FLUID_LOG(FLUID_WARN, "Sample '%s' has unknown flags, possibly using an unsupported compression; sample ignored", sample->name); + return FLUID_FAILED; + } + + if((sample->sampletype & EXCLUSIVE_FLAGS) & ((sample->sampletype & EXCLUSIVE_FLAGS) - 1)) + { + FLUID_LOG(FLUID_INFO, "Sample '%s' should be either mono or left or right; using it anyway", sample->name); + } + + if((sample->sampletype & FLUID_SAMPLETYPE_LINKED) && (sample->sampletype & EXCLUSIVE_FLAGS)) + { + FLUID_LOG(FLUID_INFO, "Linked sample '%s' should not be mono, left or right at the same time; using it anyway", sample->name); + } + + if((sample->sampletype & EXCLUSIVE_FLAGS) == 0) + { + FLUID_LOG(FLUID_INFO, "Sample '%s' has no flags set, assuming mono", sample->name); + sample->sampletype = FLUID_SAMPLETYPE_MONO; + } + + /* Ogg vorbis compressed samples in the SF3 format use byte indices for + * sample start and end pointers before decompression. Standard SF2 samples + * use sample word indices for all pointers, so use half the buffer_size + * for validation. */ + if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) + { + if(buffer_size % 2) + { + FLUID_LOG(FLUID_WARN, "Sample '%s': invalid buffer size", sample->name); + return FLUID_FAILED; + } + + buffer_size /= 2; + } + + if((sample->end > buffer_size) || (sample->start >= sample->end)) + { + FLUID_LOG(FLUID_WARN, "Sample '%s': invalid start/end file positions", sample->name); + return FLUID_FAILED; + } + + return FLUID_OK; +#undef EXCLUSIVE_FLAGS +} + +/* Check the sample loop pointers and optionally convert them to something + * usable in case they are broken. Return a boolean indicating if the pointers + * have been modified, so the user can be notified of possible audio glitches. + */ +int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size) +{ + int modified = FALSE; + unsigned int max_end = buffer_size / 2; + /* In fluid_sample_t the sample end pointer points to the last sample, not + * to the data word after the last sample. FIXME: why? */ + unsigned int sample_end = sample->end + 1; + + if(sample->loopstart == sample->loopend) + { + /* Some SoundFonts disable loops by setting loopstart = loopend. While + * technically invalid, we decided to accept those samples anyway. + * Before fluidsynth 2.2.5 we've set those indices to zero, as this + * change was believed to be inaudible. This turned out to be an + * incorrect assumption, as the loop points may still be modified by + * loop offset modulators afterwards. + */ + if(sample->loopstart != sample->start) + { + // Many soundfonts set loopstart == loopend == sample->start to disabled to loop. + // Only report cases where it's not equal to the sample->start, to avoid spam. + FLUID_LOG(FLUID_DBG, "Sample '%s': zero length loop detected: loopstart == loopend == '%d', sample start '%d', using it anyway", + sample->name, sample->loopstart, sample->start); + } + } + else if(sample->loopstart > sample->loopend) + { + unsigned int tmp; + + /* If loop start and end are reversed, try to swap them around and + * continue validation */ + FLUID_LOG(FLUID_DBG, "Sample '%s': reversed loop pointers '%d' - '%d', trying to fix", + sample->name, sample->loopstart, sample->loopend); + tmp = sample->loopstart; + sample->loopstart = sample->loopend; + sample->loopend = tmp; + modified = TRUE; + } + + /* The SoundFont 2.4 spec defines the loopstart index as the first sample + * point of the loop while loopend is the first point AFTER the last sample + * of the loop. However we cannot be sure whether any of loopend or end is + * correct. Hours of thinking through this have concluded that it would be + * best practice to mangle with loops as little as necessary by only making + * sure the pointers are within sample->start to max_end. Incorrect + * soundfont shall preferably fail loudly. */ + if((sample->loopstart < sample->start) || (sample->loopstart > max_end)) + { + FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop start '%d', setting to sample start '%d'", + sample->name, sample->loopstart, sample->start); + sample->loopstart = sample->start; + modified = TRUE; + } + + if((sample->loopend < sample->start) || (sample->loopend > max_end)) + { + FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop end '%d', setting to sample end '%d'", + sample->name, sample->loopend, sample_end); + sample->loopend = sample_end; + modified = TRUE; + } + + if((sample->loopstart > sample_end) || (sample->loopend > sample_end)) + { + FLUID_LOG(FLUID_DBG, "Sample '%s': loop range '%d - %d' after sample end '%d', using it anyway", + sample->name, sample->loopstart, sample->loopend, sample_end); + } + + return modified; +} diff --git a/libs/fluidsynth/src/sfloader/fluid_sfont.h b/libs/fluidsynth/src/sfloader/fluid_sfont.h new file mode 100644 index 00000000000..9a42c02eb19 --- /dev/null +++ b/libs/fluidsynth/src/sfloader/fluid_sfont.h @@ -0,0 +1,189 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _PRIV_FLUID_SFONT_H +#define _PRIV_FLUID_SFONT_H + +#include "fluidsynth.h" + +int fluid_sample_validate(fluid_sample_t *sample, unsigned int max_end); +int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int max_end); + +/* + * Utility macros to access soundfonts, presets, and samples + */ + +#define fluid_sfloader_delete(_loader) { if ((_loader) && (_loader)->free) (*(_loader)->free)(_loader); } +#define fluid_sfloader_load(_loader, _filename) (*(_loader)->load)(_loader, _filename) + + +#define fluid_sfont_delete_internal(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0) + + +#define fluid_preset_delete_internal(_preset) \ + { if ((_preset) && (_preset)->free) { (*(_preset)->free)(_preset); }} + +#define fluid_preset_noteon(_preset,_synth,_ch,_key,_vel) \ + (*(_preset)->noteon)(_preset,_synth,_ch,_key,_vel) + +#define fluid_preset_notify(_preset,_reason,_chan) \ + ( ((_preset) && (_preset)->notify) ? (*(_preset)->notify)(_preset,_reason,_chan) : FLUID_OK ) + + +#define fluid_sample_incr_ref(_sample) { (_sample)->refcount++; } + +#define fluid_sample_decr_ref(_sample) \ + (_sample)->refcount--; \ + if (((_sample)->refcount == 0) && ((_sample)->notify)) \ + (*(_sample)->notify)(_sample, FLUID_SAMPLE_DONE); + + + +/** + * File callback structure to enable custom soundfont loading (e.g. from memory). + */ +struct _fluid_file_callbacks_t +{ + fluid_sfloader_callback_open_t fopen; + fluid_sfloader_callback_read_t fread; + fluid_sfloader_callback_seek_t fseek; + fluid_sfloader_callback_close_t fclose; + fluid_sfloader_callback_tell_t ftell; +}; + +/** + * SoundFont loader structure. + */ +struct _fluid_sfloader_t +{ + void *data; /**< User defined data pointer used by _fluid_sfloader_t::load() */ + + /** Callback structure specifying file operations used during soundfont loading to allow custom loading, such as from memory */ + fluid_file_callbacks_t file_callbacks; + + fluid_sfloader_free_t free; + + fluid_sfloader_load_t load; +}; + +/** + * Virtual SoundFont instance structure. + */ +struct _fluid_sfont_t +{ + void *data; /**< User defined data */ + int id; /**< SoundFont ID */ + int refcount; /**< SoundFont reference count (1 if no presets referencing it) */ + int bankofs; /**< Bank offset */ + + fluid_sfont_free_t free; + + fluid_sfont_get_name_t get_name; + + fluid_sfont_get_preset_t get_preset; + + fluid_sfont_iteration_start_t iteration_start; + + fluid_sfont_iteration_next_t iteration_next; +}; + +/** + * Virtual SoundFont preset. + */ +struct _fluid_preset_t +{ + void *data; /**< User supplied data */ + fluid_sfont_t *sfont; /**< Parent virtual SoundFont */ + + fluid_preset_free_t free; + + fluid_preset_get_name_t get_name; + + fluid_preset_get_banknum_t get_banknum; + + fluid_preset_get_num_t get_num; + + fluid_preset_noteon_t noteon; + + /** + * Virtual SoundFont preset notify method. + * @param preset Virtual SoundFont preset + * @param reason #FLUID_PRESET_SELECTED or #FLUID_PRESET_UNSELECTED + * @param chan MIDI channel number + * @return Should return #FLUID_OK + * + * Implement this optional method if the preset needs to be notified about + * preset select and unselect events. + * + * This method may be called from within synthesis context and therefore + * should be as efficient as possible and not perform any operations considered + * bad for realtime audio output (memory allocations and other OS calls). + */ + int (*notify)(fluid_preset_t *preset, int reason, int chan); +}; + +/** + * Virtual SoundFont sample. + */ +struct _fluid_sample_t +{ + char name[21]; /**< Sample name */ + + /* The following four sample pointers store the original pointers from the Soundfont + * file. They are never changed after loading and are used to re-create the + * actual sample pointers after a sample has been unloaded and loaded again. The + * actual sample pointers get modified during loading for SF3 (compressed) samples + * and individually loaded SF2 samples. */ + unsigned int source_start; + unsigned int source_end; + unsigned int source_loopstart; + unsigned int source_loopend; + + unsigned int start; /**< Start index */ + unsigned int end; /**< End index, index of last valid sample point (contrary to SF spec) */ + unsigned int loopstart; /**< Loop start index */ + unsigned int loopend; /**< Loop end index, first point following the loop (superimposed on loopstart) */ + + unsigned int samplerate; /**< Sample rate */ + int origpitch; /**< Original pitch (MIDI note number, 0-127) */ + int pitchadj; /**< Fine pitch adjustment (+/- 99 cents) */ + int sampletype; /**< Specifies the type of this sample as indicated by the #fluid_sample_type enum */ + int auto_free; /**< TRUE if _fluid_sample_t::data and _fluid_sample_t::data24 should be freed upon sample destruction */ + short *data; /**< Pointer to the sample's 16 bit PCM data */ + char *data24; /**< If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples */ + + int amplitude_that_reaches_noise_floor_is_valid; /**< Indicates if \a amplitude_that_reaches_noise_floor is valid (TRUE), set to FALSE initially to calculate. */ + double amplitude_that_reaches_noise_floor; /**< The amplitude at which the sample's loop will be below the noise floor. For voice off optimization, calculated automatically. */ + + unsigned int refcount; /**< Count of voices using this sample */ + int preset_count; /**< Count of selected presets using this sample (used for dynamic sample loading) */ + + /** + * Implement this function to receive notification when sample is no longer used. + * @param sample Virtual SoundFont sample + * @param reason #FLUID_SAMPLE_DONE only currently + * @return Should return #FLUID_OK + */ + int (*notify)(fluid_sample_t *sample, int reason); +}; + + +#endif /* _PRIV_FLUID_SFONT_H */ diff --git a/libs/fluidsynth/src/synth/fluid_chan.c b/libs/fluidsynth/src/synth/fluid_chan.c new file mode 100644 index 00000000000..0f2eecb44e0 --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_chan.c @@ -0,0 +1,732 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_chan.h" +#include "fluid_mod.h" +#include "fluid_synth.h" +#include "fluid_sfont.h" + +/* Field shift amounts for sfont_bank_prog bit field integer */ +#define PROG_SHIFTVAL 0 +#define BANK_SHIFTVAL 8 +#define SFONT_SHIFTVAL 22 + +/* Field mask values for sfont_bank_prog bit field integer */ +#define PROG_MASKVAL 0x000000FF /* Bit 7 is used to indicate unset state */ +#define BANK_MASKVAL 0x003FFF00 +#define BANKLSB_MASKVAL 0x00007F00 +#define BANKMSB_MASKVAL 0x003F8000 +#define SFONT_MASKVAL 0xFFC00000 + + +static void fluid_channel_init(fluid_channel_t *chan); + + +fluid_channel_t * +new_fluid_channel(fluid_synth_t *synth, int num) +{ + fluid_channel_t *chan; + + chan = FLUID_NEW(fluid_channel_t); + + if(chan == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + chan->synth = synth; + chan->channum = num; + chan->preset = NULL; + chan->tuning = NULL; + + fluid_channel_init(chan); + fluid_channel_init_ctrl(chan, 0); + + return chan; +} + +static void +fluid_channel_init(fluid_channel_t *chan) +{ + fluid_preset_t *newpreset; + int i, prognum, banknum; + + chan->sostenuto_orderid = 0; + /*--- Init poly/mono modes variables --------------------------------------*/ + chan->mode = 0; + chan->mode_val = 0; + + /* monophonic list initialization */ + for(i = 0; i < FLUID_CHANNEL_SIZE_MONOLIST; i++) + { + chan->monolist[i].next = i + 1; + } + + chan->monolist[FLUID_CHANNEL_SIZE_MONOLIST - 1].next = 0; /* ending element chained to the 1st */ + chan->i_last = chan->n_notes = 0; /* clears the list */ + chan->i_first = chan->monolist[chan->i_last].next; /* first note index in the list */ + fluid_channel_clear_prev_note(chan); /* Mark previous note invalid */ + /*---*/ + chan->key_mono_sustained = INVALID_NOTE; /* No previous mono note sustained */ + chan->legatomode = FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER; /* Default mode */ + chan->portamentomode = FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY; /* Default mode */ + /*--- End of poly/mono initialization --------------------------------------*/ + + chan->channel_type = (chan->channum == 9) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; + prognum = 0; + banknum = (chan->channel_type == CHANNEL_TYPE_DRUM) ? DRUM_INST_BANK : 0; + + chan->sfont_bank_prog = 0 << SFONT_SHIFTVAL | banknum << BANK_SHIFTVAL + | prognum << PROG_SHIFTVAL; + + newpreset = fluid_synth_find_preset(chan->synth, banknum, prognum); + fluid_channel_set_preset(chan, newpreset); + + chan->interp_method = FLUID_INTERP_DEFAULT; + chan->tuning_bank = 0; + chan->tuning_prog = 0; + chan->nrpn_select = 0; + chan->nrpn_active = 0; + + if(chan->tuning) + { + fluid_tuning_unref(chan->tuning, 1); + chan->tuning = NULL; + } +} + +/* + @param is_all_ctrl_off if nonzero, only resets some controllers, according to + https://www.midi.org/techspecs/rp15.php +*/ +void +fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off) +{ + int i; + + chan->channel_pressure = 0; + chan->pitch_bend = 0x2000; /* Range is 0x4000, pitch bend wheel starts in centered position */ + + for(i = 0; i < GEN_LAST; i++) + { + chan->gen[i] = 0.0f; + } + + if(is_all_ctrl_off) + { + for(i = 0; i < ALL_SOUND_OFF; i++) + { + if(i >= EFFECTS_DEPTH1 && i <= EFFECTS_DEPTH5) + { + continue; + } + + if(i >= SOUND_CTRL1 && i <= SOUND_CTRL10) + { + continue; + } + + if(i == BANK_SELECT_MSB || i == BANK_SELECT_LSB || i == VOLUME_MSB || + i == VOLUME_LSB || i == PAN_MSB || i == PAN_LSB || + i == BALANCE_MSB || i == BALANCE_LSB + ) + { + continue; + } + + fluid_channel_set_cc(chan, i, 0); + } + } + else + { + for(i = 0; i < 128; i++) + { + fluid_channel_set_cc(chan, i, 0); + } + + chan->previous_cc_breath = 0;/* Reset previous breath */ + } + /* Unconditionally clear PTC receive (issue #1050) */ + fluid_channel_clear_portamento(chan); + + /* Reset polyphonic key pressure on all voices */ + for(i = 0; i < 128; i++) + { + fluid_channel_set_key_pressure(chan, i, 0); + } + + /* Set RPN controllers to NULL state */ + fluid_channel_set_cc(chan, RPN_LSB, 127); + fluid_channel_set_cc(chan, RPN_MSB, 127); + + /* Set NRPN controllers to NULL state */ + fluid_channel_set_cc(chan, NRPN_LSB, 127); + fluid_channel_set_cc(chan, NRPN_MSB, 127); + + /* Expression (MSB & LSB) */ + fluid_channel_set_cc(chan, EXPRESSION_MSB, 127); + fluid_channel_set_cc(chan, EXPRESSION_LSB, 127); + + if(!is_all_ctrl_off) + { + + chan->pitch_wheel_sensitivity = 2; /* two semi-tones */ + + /* Just like panning, a value of 64 indicates no change for sound ctrls */ + for(i = SOUND_CTRL1; i <= SOUND_CTRL10; i++) + { + fluid_channel_set_cc(chan, i, 64); + } + + /* Volume / initial attenuation (MSB & LSB) */ + fluid_channel_set_cc(chan, VOLUME_MSB, 100); + fluid_channel_set_cc(chan, VOLUME_LSB, 0); + + /* Pan (MSB & LSB) */ + fluid_channel_set_cc(chan, PAN_MSB, 64); + fluid_channel_set_cc(chan, PAN_LSB, 0); + + /* Balance (MSB & LSB) */ + fluid_channel_set_cc(chan, BALANCE_MSB, 64); + fluid_channel_set_cc(chan, BALANCE_LSB, 0); + + /* Reverb */ + /* fluid_channel_set_cc (chan, EFFECTS_DEPTH1, 40); */ + /* Note: although XG standard specifies the default amount of reverb to + be 40, most people preferred having it at zero. + See https://lists.gnu.org/archive/html/fluid-dev/2009-07/msg00016.html */ + } +} + +/* Only called by delete_fluid_synth(), so no need to queue a preset free event */ +void +delete_fluid_channel(fluid_channel_t *chan) +{ + fluid_return_if_fail(chan != NULL); + + FLUID_FREE(chan); +} + +void +fluid_channel_reset(fluid_channel_t *chan) +{ + fluid_channel_init(chan); + fluid_channel_init_ctrl(chan, 0); +} + +/* Should only be called from synthesis context */ +int +fluid_channel_set_preset(fluid_channel_t *chan, fluid_preset_t *preset) +{ + fluid_sfont_t *sfont; + + if(chan->preset == preset) + { + return FLUID_OK; + } + + if(chan->preset) + { + sfont = chan->preset->sfont; + sfont->refcount--; + } + + fluid_preset_notify(chan->preset, FLUID_PRESET_UNSELECTED, chan->channum); + + chan->preset = preset; + + if(preset) + { + sfont = preset->sfont; + sfont->refcount++; + } + + fluid_preset_notify(preset, FLUID_PRESET_SELECTED, chan->channum); + + return FLUID_OK; +} + +/* Set SoundFont ID, MIDI bank and/or program. Use -1 to use current value. */ +void +fluid_channel_set_sfont_bank_prog(fluid_channel_t *chan, int sfontnum, + int banknum, int prognum) +{ + int oldval, newval, oldmask; + + newval = ((sfontnum != -1) ? sfontnum << SFONT_SHIFTVAL : 0) + | ((banknum != -1) ? banknum << BANK_SHIFTVAL : 0) + | ((prognum != -1) ? prognum << PROG_SHIFTVAL : 0); + + oldmask = ((sfontnum != -1) ? 0 : SFONT_MASKVAL) + | ((banknum != -1) ? 0 : BANK_MASKVAL) + | ((prognum != -1) ? 0 : PROG_MASKVAL); + + oldval = chan->sfont_bank_prog; + newval = (newval & ~oldmask) | (oldval & oldmask); + chan->sfont_bank_prog = newval; +} + +/* Set bank LSB 7 bits */ +void +fluid_channel_set_bank_lsb(fluid_channel_t *chan, int banklsb) +{ + int oldval, newval, style; + + style = chan->synth->bank_select; + + if(style == FLUID_BANK_STYLE_GM || + style == FLUID_BANK_STYLE_GS) + { + return; /* ignored */ + } + + oldval = chan->sfont_bank_prog; + + if(style == FLUID_BANK_STYLE_XG) + { + newval = (oldval & ~BANK_MASKVAL) | (banklsb << BANK_SHIFTVAL); + } + else /* style == FLUID_BANK_STYLE_MMA */ + { + newval = (oldval & ~BANKLSB_MASKVAL) | (banklsb << BANK_SHIFTVAL); + } + + chan->sfont_bank_prog = newval; +} + +/* Set bank MSB 7 bits */ +void +fluid_channel_set_bank_msb(fluid_channel_t *chan, int bankmsb) +{ + int oldval, newval, style; + + style = chan->synth->bank_select; + + if(style == FLUID_BANK_STYLE_XG) + { + /* XG bank, do drum-channel auto-switch */ + /* The number "120" was based on several keyboards having drums at 120 - 127, + reference: https://lists.nongnu.org/archive/html/fluid-dev/2011-02/msg00003.html */ + chan->channel_type = (120 <= bankmsb) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; + return; + } + + if(style == FLUID_BANK_STYLE_GM || + chan->channel_type == CHANNEL_TYPE_DRUM) + { + return; /* ignored */ + } + + oldval = chan->sfont_bank_prog; + + if(style == FLUID_BANK_STYLE_GS) + { + newval = (oldval & ~BANK_MASKVAL) | (bankmsb << BANK_SHIFTVAL); + } + else /* style == FLUID_BANK_STYLE_MMA */ + { + newval = (oldval & ~BANKMSB_MASKVAL) | (bankmsb << (BANK_SHIFTVAL + 7)); + } + + chan->sfont_bank_prog = newval; + +} + +/* Get SoundFont ID, MIDI bank and/or program. Use NULL to ignore a value. */ +void +fluid_channel_get_sfont_bank_prog(fluid_channel_t *chan, int *sfont, + int *bank, int *prog) +{ + int sfont_bank_prog; + + sfont_bank_prog = chan->sfont_bank_prog; + + if(sfont) + { + *sfont = (sfont_bank_prog & SFONT_MASKVAL) >> SFONT_SHIFTVAL; + } + + if(bank) + { + *bank = (sfont_bank_prog & BANK_MASKVAL) >> BANK_SHIFTVAL; + } + + if(prog) + { + *prog = (sfont_bank_prog & PROG_MASKVAL) >> PROG_SHIFTVAL; + } +} + +/** + * Compute the pitch for a key after applying Fluidsynth's tuning functionality + * and channel coarse/fine tunings. + * @param chan fluid_channel_t + * @param key MIDI note number (0-127) + * @return the pitch of the key + */ +fluid_real_t fluid_channel_get_key_pitch(fluid_channel_t *chan, int key) +{ + if(chan->tuning) + { + return fluid_tuning_get_pitch(chan->tuning, key) + + 100.0f * fluid_channel_get_gen(chan, GEN_COARSETUNE) + + fluid_channel_get_gen(chan, GEN_FINETUNE); + } + else + { + return key * 100.0f; + } +} + +/** + * Updates legato/ staccato playing state + * The function is called: + * - on noteon before adding a note into the monolist. + * - on noteoff after removing a note out of the monolist. + * @param chan fluid_channel_t. +*/ +static void +fluid_channel_update_legato_staccato_state(fluid_channel_t *chan) +{ + /* Updates legato/ staccato playing state */ + if(chan->n_notes) + { + chan->mode |= FLUID_CHANNEL_LEGATO_PLAYING; /* Legato state */ + } + else + { + chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */ + } +} + +/** + * Adds a note into the monophonic list. The function is part of the legato + * detector. fluid_channel_add_monolist() is intended to be called by + * fluid_synth_noteon_mono_LOCAL(). + * + * When a note is added at noteOn each element is use in the forward direction + * and indexed by i_last variable. + * + * @param chan fluid_channel_t. + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127, 0=noteoff). + * @param onenote. When 1 the function adds the note but the monophonic list + * keeps only one note (used on noteOn poly). + * Note: i_last index keeps a trace of the most recent note added. + * prev_note keeps a trace of the note prior i_last note. + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing state. + * + * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). +*/ +void +fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key, + unsigned char vel, unsigned char onenote) +{ + unsigned char i_last = chan->i_last; + /* Updates legato/ staccato playing state */ + fluid_channel_update_legato_staccato_state(chan); + + if(chan->n_notes) + { + /* keeps trace of the note prior last note */ + chan->prev_note = chan->monolist[i_last].note; + } + + /* moves i_last forward before writing new note */ + i_last = chan->monolist[i_last].next; + chan->i_last = i_last; /* now ilast indexes the last note */ + chan->monolist[i_last].note = key; /* we save note and velocity */ + chan->monolist[i_last].vel = vel; + + if(onenote) + { + /* clears monolist before one note addition */ + chan->i_first = i_last; + chan->n_notes = 0; + } + + if(chan->n_notes < FLUID_CHANNEL_SIZE_MONOLIST) + { + chan->n_notes++; /* updates n_notes */ + } + else + { + /* The end of buffer is reach. So circular motion for i_first */ + /* i_first index is moved forward */ + chan->i_first = chan->monolist[i_last].next; + } +} + +/** + * Searching a note in the monophonic list. The function is part of the legato + * detector. fluid_channel_search_monolist() is intended to be called by + * fluid_synth_noteoff_mono_LOCAL(). + * + * The search starts from the first note in the list indexed by i_first + + * @param chan fluid_channel_t. + * @param key MIDI note number (0-127) to search. + * @param i_prev pointer on returned index of the note prior the note to search. + * @return index of the note if find, FLUID_FAILED otherwise. + * + */ +int +fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_prev) +{ + short n = chan->n_notes; /* number of notes in monophonic list */ + short j, i = chan->i_first; /* searching starts from i_first included */ + + for(j = 0 ; j < n ; j++) + { + if(chan->monolist[i].note == key) + { + if(i == chan->i_first) + { + /* tracking index of the previous note (i_prev) */ + for(j = chan->i_last ; n < FLUID_CHANNEL_SIZE_MONOLIST; n++) + { + j = chan->monolist[j].next; + } + + * i_prev = j; /* returns index of the previous note */ + } + + return i; /* returns index of the note to search */ + } + + * i_prev = i; /* tracking index of the previous note (i_prev) */ + i = chan->monolist[i].next; /* next element */ + } + + return FLUID_FAILED; /* not found */ +} + +/** + * removes a note from the monophonic list. The function is part of + * the legato detector. + * fluid_channel_remove_monolist() is intended to be called by + * fluid_synth_noteoff_mono_LOCAL(). + * + * When a note is removed at noteOff the element concerned is fast unlinked + * and relinked after the i_last element. + * + * @param chan fluid_channel_t. + * @param + * i, index of the note to remove. If i is invalid or the list is + * empty, the function do nothing and returns FLUID_FAILED. + * @param + * On input, i_prev is a pointer on index of the note previous i. + * On output i_prev is a pointer on index of the note previous i if i is the last note + * in the list,FLUID_FAILED otherwise. When the returned index is valid it means + * a legato detection on noteoff. + * + * Note: the following variables in Channel keeps trace of the situation. + * - i_last index keeps a trace of the most recent note played even if + * the list is empty. + * - prev_note keeps a trace of the note removed if it is i_last. + * - FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing state. + * + * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). + */ +void +fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev) +{ + unsigned char i_last = chan->i_last; + + /* checks if index is valid */ + if(i < 0 || i >= FLUID_CHANNEL_SIZE_MONOLIST || !chan->n_notes) + { + * i_prev = FLUID_FAILED; + } + + /* The element is about to be removed and inserted between i_last and next */ + /* Note: when i is egal to i_last or egal to i_first, removing/inserting + isn't necessary */ + if(i == i_last) + { + /* Removing/Inserting isn't necessary */ + /* keeps trace of the note prior last note */ + chan->prev_note = chan->monolist[i_last].note; + /* moves i_last backward to the previous */ + chan->i_last = *i_prev; /* i_last index is moved backward */ + } + else + { + /* i is before i_last */ + if(i == chan->i_first) + { + /* Removing/inserting isn't necessary */ + /* i_first index is moved forward to the next element*/ + chan->i_first = chan->monolist[i].next; + } + else + { + /* i is between i_first and i_last */ + /* Unlinks element i and inserts after i_last */ + chan->monolist[* i_prev].next = chan->monolist[i].next; /* unlinks i */ + /*inserts i after i_last */ + chan->monolist[i].next = chan->monolist[i_last].next; + chan->monolist[i_last].next = i; + } + + * i_prev = FLUID_FAILED; + } + + chan->n_notes--; /* updates the number of note in the list */ + /* Updates legato/ staccato playing state */ + fluid_channel_update_legato_staccato_state(chan); +} + +/** + * On noteOff on a polyphonic channel,the monophonic list is fully flushed. + * + * @param chan fluid_channel_t. + * Note: i_last index keeps a trace of the most recent note played even if + * the list is empty. + * prev_note keeps a trace of the note i_last . + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing. + */ +void fluid_channel_clear_monolist(fluid_channel_t *chan) +{ + /* keeps trace off the most recent note played */ + chan->prev_note = chan->monolist[chan->i_last].note; + + /* flushes the monolist */ + chan->i_first = chan->monolist[chan->i_last].next; + chan->n_notes = 0; + /* Update legato/ sataccato playing state */ + chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */ +} + +/** + * On noteOn on a polyphonic channel,adds the note into the monophonic list + * keeping only this note. + * @param + * chan fluid_channel_t. + * key, vel, note and velocity added in the monolist + * Note: i_last index keeps a trace of the most recent note inserted. + * prev_note keeps a trace of the note prior i_last note. + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing. + */ +void fluid_channel_set_onenote_monolist(fluid_channel_t *chan, unsigned char key, + unsigned char vel) +{ + fluid_channel_add_monolist(chan, key, vel, 1); +} + +/** + * The function changes the state (Valid/Invalid) of the previous note played in + * a staccato manner (fluid_channel_prev_note()). + * When potamento mode 'each note' or 'staccato only' is selected, on next + * noteOn a portamento will be started from the most recent note played + * staccato. + * It will be possible that it isn't appropriate. To give the musician the + * possibility to choose a portamento from this note , prev_note will be forced + * to invalid state on noteOff if portamento pedal is Off. + * + * The function is intended to be called when the following event occurs: + * - On noteOff (in poly or mono mode), to mark prev_note invalid. + * - On Portamento Off(in poly or mono mode), to mark prev_note invalid. + * @param chan fluid_channel_t. + */ +void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan) +{ + /* checks if the playing is staccato */ + if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + { + + /* checks if portamento pedal is off */ + if(! fluid_channel_portamento(chan)) + { + /* forces prev_note invalid */ + fluid_channel_clear_prev_note(chan); + } + } + + /* else prev_note still remains valid for next fromkey portamento */ +} + +/** + * The function handles poly/mono commutation on legato pedal On/Off. + * @param chan fluid_channel_t. + * @param value, value of the CC legato. + */ +void fluid_channel_cc_legato(fluid_channel_t *chan, int value) +{ + /* Special handling of the monophonic list */ + if(!(chan->mode & FLUID_CHANNEL_POLY_OFF) && chan->n_notes) /* The monophonic list have notes */ + { + if(value < 64) /* legato is released */ + { + /* returns from monophonic to polyphonic with notes in the monophonic list */ + + /* The monophonic list is flushed keeping last note only + Note: i_last index keeps a trace of the most recent note played. + prev_note keeps a trace of the note i_last. + FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing. + */ + chan->i_first = chan->i_last; + chan->n_notes = 1; + } + else /* legato is depressed */ + { + /* Inters in monophonic from polyphonic with note in monophonic list */ + /* Stops the running note to remain coherent with Breath Sync mode */ + if((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && !fluid_channel_breath_msb(chan)) + { + fluid_synth_noteoff_monopoly(chan->synth, chan->channum, + fluid_channel_last_note(chan), 1); + } + } + } +} + +/** + * The function handles CC Breath On/Off detection. When a channel is in + * Breath Sync mode and in monophonic playing, the breath controller allows + * to trigger noteon/noteoff note when the musician starts to breath (noteon) and + * stops to breath (noteoff). + * @param chan fluid_channel_t. + * @param value, value of the CC Breath.. + */ +void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value) +{ + if((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && fluid_channel_is_playing_mono(chan) && + (chan->n_notes)) + { + /* The monophonic list isn't empty */ + if((value > 0) && (chan->previous_cc_breath == 0)) + { + /* CC Breath On detection */ + fluid_synth_noteon_mono_staccato(chan->synth, chan->channum, + fluid_channel_last_note(chan), + fluid_channel_last_vel(chan)); + } + else if((value == 0) && (chan->previous_cc_breath > 0)) + { + /* CC Breath Off detection */ + fluid_synth_noteoff_monopoly(chan->synth, chan->channum, + fluid_channel_last_note(chan), 1); + } + } + + chan->previous_cc_breath = value; +} diff --git a/libs/fluidsynth/src/synth/fluid_chan.h b/libs/fluidsynth/src/synth/fluid_chan.h new file mode 100644 index 00000000000..96eb02e37f5 --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_chan.h @@ -0,0 +1,276 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_CHAN_H +#define _FLUID_CHAN_H + +#include "fluidsynth_priv.h" +#include "fluid_midi.h" +#include "fluid_tuning.h" + +/* The mononophonic list is part of the legato detector for monophonic mode */ +/* see fluid_synth_monopoly.c about a description of the legato detector device */ +/* Size of the monophonic list + - 1 is the minimum. it allows playing legato passage of any number + of notes on noteon only. + - Size above 1 allows playing legato on noteon but also on noteOff. + This allows the musician to play fast trills. + This feature is particularly usful when the MIDI input device is a keyboard. + Choosing a size of 10 is sufficient (because most musicians have only 10 + fingers when playing a monophonic instrument). +*/ +#define FLUID_CHANNEL_SIZE_MONOLIST 10 + +/* + + The monophonic list + +------------------------------------------------+ + | +----+ +----+ +----+ +----+ | + | |note| |note| |note| |note| | + +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+ + +----+ +----+ +----+ +----+ + /|\ /|\ + | | + i_first i_last + + The monophonic list is a circular buffer of FLUID_CHANNEL_SIZE_MONOLIST elements. + Each element is linked forward at initialisation time. + - when a note is added at noteOn (see fluid_channel_add_monolist()) each + element is use in the forward direction and indexed by i_last variable. + - when a note is removed at noteOff (see fluid_channel_remove_monolist()), + the element concerned is fast unlinked and relinked after the i_last element. + + The most recent note added is indexed by i_last. + The most ancient note added is the first note indexed by i_first. i_first is + moving in the forward direction in a circular manner. + +*/ +struct mononote +{ + unsigned char next; /* next note */ + unsigned char note; /* note */ + unsigned char vel; /* velocity */ +}; + +/* + * fluid_channel_t + * + * Mutual exclusion notes (as of 1.1.2): + * None - everything should have been synchronized by the synth. + */ +struct _fluid_channel_t +{ + fluid_synth_t *synth; /**< Parent synthesizer instance */ + int channum; /**< MIDI channel number */ + + /* Poly Mono variables see macro access description */ + int mode; /**< Poly Mono mode */ + int mode_val; /**< number of channel in basic channel group */ + + /* monophonic list - legato detector */ + unsigned char i_first; /**< First note index */ + unsigned char i_last; /**< most recent note index since the most recent add */ + unsigned char prev_note; /**< previous note of the most recent add/remove */ + unsigned char n_notes; /**< actual number of notes in the list */ + struct mononote monolist[FLUID_CHANNEL_SIZE_MONOLIST]; /**< monophonic list */ + + unsigned char key_mono_sustained; /**< previous sustained monophonic note */ + unsigned char previous_cc_breath; /**< Previous Breath */ + enum fluid_channel_legato_mode legatomode; /**< legato mode */ + enum fluid_channel_portamento_mode portamentomode; /**< portamento mode */ + /*- End of Poly/mono variables description */ + + unsigned char cc[128]; /**< MIDI controller values from [0;127] */ + unsigned char key_pressure[128]; /**< MIDI polyphonic key pressure from [0;127] */ + + /* Drum channel flag, CHANNEL_TYPE_MELODIC, or CHANNEL_TYPE_DRUM. */ + enum fluid_midi_channel_type channel_type; + enum fluid_interp interp_method; /**< Interpolation method (enum fluid_interp) */ + + unsigned char channel_pressure; /**< MIDI channel pressure from [0;127] */ + unsigned char pitch_wheel_sensitivity; /**< Current pitch wheel sensitivity */ + short pitch_bend; /**< Current pitch bend value */ + /* Sostenuto order id gives the order of SostenutoOn event. + * This value is useful to known when the sostenuto pedal is depressed + * (before or after a key note). We need to compare SostenutoOrderId with voice id. + */ + unsigned int sostenuto_orderid; + + int tuning_bank; /**< Current tuning bank number */ + int tuning_prog; /**< Current tuning program number */ + fluid_tuning_t *tuning; /**< Micro tuning */ + + fluid_preset_t *preset; /**< Selected preset */ + int sfont_bank_prog; /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ + + /* NRPN system */ + enum fluid_gen_type nrpn_select; /* Generator ID of SoundFont NRPN message */ + char nrpn_active; /* 1 if data entry CCs are for NRPN, 0 if RPN */ + + /* The values of the generators, set by NRPN messages, or by + * fluid_synth_set_gen(), are cached in the channel so they can be + * applied to future notes. They are copied to a voice's generators + * in fluid_voice_init(), which calls fluid_gen_init(). */ + fluid_real_t gen[GEN_LAST]; +}; + +fluid_channel_t *new_fluid_channel(fluid_synth_t *synth, int num); +void fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off); +void delete_fluid_channel(fluid_channel_t *chan); +void fluid_channel_reset(fluid_channel_t *chan); +int fluid_channel_set_preset(fluid_channel_t *chan, fluid_preset_t *preset); +void fluid_channel_set_sfont_bank_prog(fluid_channel_t *chan, int sfont, + int bank, int prog); +void fluid_channel_set_bank_lsb(fluid_channel_t *chan, int banklsb); +void fluid_channel_set_bank_msb(fluid_channel_t *chan, int bankmsb); +void fluid_channel_get_sfont_bank_prog(fluid_channel_t *chan, int *sfont, + int *bank, int *prog); +fluid_real_t fluid_channel_get_key_pitch(fluid_channel_t *chan, int key); + +#define fluid_channel_get_preset(chan) ((chan)->preset) +#define fluid_channel_set_cc(chan, num, val) \ + ((chan)->cc[num] = (val)) +#define fluid_channel_get_cc(chan, num) \ + ((chan)->cc[num]) +#define fluid_channel_get_key_pressure(chan, key) \ + ((chan)->key_pressure[key]) +#define fluid_channel_set_key_pressure(chan, key, val) \ + ((chan)->key_pressure[key] = (val)) +#define fluid_channel_get_channel_pressure(chan) \ + ((chan)->channel_pressure) +#define fluid_channel_set_channel_pressure(chan, val) \ + ((chan)->channel_pressure = (val)) +#define fluid_channel_get_pitch_bend(chan) \ + ((chan)->pitch_bend) +#define fluid_channel_set_pitch_bend(chan, val) \ + ((chan)->pitch_bend = (val)) +#define fluid_channel_get_pitch_wheel_sensitivity(chan) \ + ((chan)->pitch_wheel_sensitivity) +#define fluid_channel_set_pitch_wheel_sensitivity(chan, val) \ + ((chan)->pitch_wheel_sensitivity = (val)) +#define fluid_channel_get_num(chan) ((chan)->channum) +#define fluid_channel_set_interp_method(chan, new_method) \ + ((chan)->interp_method = (new_method)) +#define fluid_channel_get_interp_method(chan) \ + ((chan)->interp_method); +#define fluid_channel_set_tuning(_c, _t) { (_c)->tuning = _t; } +#define fluid_channel_has_tuning(_c) ((_c)->tuning != NULL) +#define fluid_channel_get_tuning(_c) ((_c)->tuning) +#define fluid_channel_get_tuning_bank(chan) \ + ((chan)->tuning_bank) +#define fluid_channel_set_tuning_bank(chan, bank) \ + ((chan)->tuning_bank = (bank)) +#define fluid_channel_get_tuning_prog(chan) \ + ((chan)->tuning_prog) +#define fluid_channel_set_tuning_prog(chan, prog) \ + ((chan)->tuning_prog = (prog)) +#define fluid_channel_portamentotime(_c) \ + ((_c)->cc[PORTAMENTO_TIME_MSB] * 128 + (_c)->cc[PORTAMENTO_TIME_LSB]) +#define fluid_channel_portamento(_c) ((_c)->cc[PORTAMENTO_SWITCH] >= 64) +#define fluid_channel_breath_msb(_c) ((_c)->cc[BREATH_MSB] > 0) +#define fluid_channel_clear_portamento(_c) ((_c)->cc[PORTAMENTO_CTRL] = INVALID_NOTE) +#define fluid_channel_legato(_c) ((_c)->cc[LEGATO_SWITCH] >= 64) +#define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64) +#define fluid_channel_sostenuto(_c) ((_c)->cc[SOSTENUTO_SWITCH] >= 64) +#define fluid_channel_set_gen(_c, _n, _v) { (_c)->gen[_n] = _v; } +#define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n]) +#define fluid_channel_get_min_note_length_ticks(chan) \ + ((chan)->synth->min_note_length_ticks) + +/* Macros interface to poly/mono mode variables */ +#define MASK_BASICCHANINFOS (FLUID_CHANNEL_MODE_MASK|FLUID_CHANNEL_BASIC|FLUID_CHANNEL_ENABLED) +/* Set the basic channel infos for a MIDI basic channel */ +#define fluid_channel_set_basic_channel_info(chan,Infos) \ + (chan->mode = (chan->mode & ~MASK_BASICCHANINFOS) | (Infos & MASK_BASICCHANINFOS)) +/* Reset the basic channel infos for a MIDI basic channel */ +#define fluid_channel_reset_basic_channel_info(chan) (chan->mode &= ~MASK_BASICCHANINFOS) + +/* Macros interface to breath variables */ +#define FLUID_CHANNEL_BREATH_MASK (FLUID_CHANNEL_BREATH_POLY|FLUID_CHANNEL_BREATH_MONO|FLUID_CHANNEL_BREATH_SYNC) +/* Set the breath infos for a MIDI channel */ +#define fluid_channel_set_breath_info(chan,BreathInfos) \ +(chan->mode = (chan->mode & ~FLUID_CHANNEL_BREATH_MASK) | (BreathInfos & FLUID_CHANNEL_BREATH_MASK)) +/* Get the breath infos for a MIDI channel */ +#define fluid_channel_get_breath_info(chan) (chan->mode & FLUID_CHANNEL_BREATH_MASK) + +/* Returns true when channel is mono or legato is on */ +#define fluid_channel_is_playing_mono(chan) ((chan->mode & FLUID_CHANNEL_POLY_OFF) ||\ + fluid_channel_legato(chan)) + +/* Macros interface to monophonic list variables */ +#define INVALID_NOTE (255) +/* Returns true when a note is a valid note */ +#define fluid_channel_is_valid_note(n) (n != INVALID_NOTE) +/* Marks prev_note as invalid. */ +#define fluid_channel_clear_prev_note(chan) (chan->prev_note = INVALID_NOTE) + +/* Returns the most recent note from i_last entry of the monophonic list */ +#define fluid_channel_last_note(chan) (chan->monolist[chan->i_last].note) + +/* Returns the most recent velocity from i_last entry of the monophonic list */ +#define fluid_channel_last_vel(chan) (chan->monolist[chan->i_last].vel) + +/* + prev_note is used to determine fromkey_portamento as well as + fromkey_legato (see fluid_synth_get_fromkey_portamento_legato()). + + prev_note is updated on noteOn/noteOff mono by the legato detector as this: + - On noteOn mono, before adding a new note into the monolist,the most + recent note in the list (i.e at i_last position) is kept in prev_note. + - Similarly, on noteOff mono , before removing a note out of the monolist, + the most recent note (i.e those at i_last position) is kept in prev_note. +*/ +#define fluid_channel_prev_note(chan) (chan->prev_note) + +/* Interface to poly/mono mode variables */ +enum fluid_channel_mode_flags_internal +{ + FLUID_CHANNEL_BASIC = 0x04, /**< if flag set the corresponding midi channel is a basic channel */ + FLUID_CHANNEL_ENABLED = 0x08, /**< if flag set the corresponding midi channel is enabled, else disabled, i.e. channel ignores any MIDI messages */ + + /* + FLUID_CHANNEL_LEGATO_PLAYING bit of channel mode keeps trace of the legato /staccato + state playing. + FLUID_CHANNEL_LEGATO_PLAYING bit is updated on noteOn/noteOff mono by the legato detector: + - On noteOn, before inserting a new note into the monolist. + - On noteOff, after removing a note out of the monolist. + + - On noteOn, this state is used by fluid_synth_noteon_mono_LOCAL() + to play the current note legato or staccato. + - On noteOff, this state is used by fluid_synth_noteoff_mono_LOCAL() + to play the current noteOff legato with the most recent note. + */ + /* bit7, 1: means legato playing , 0: means staccato playing */ + FLUID_CHANNEL_LEGATO_PLAYING = 0x80 +}; + +/* End of interface to monophonic list variables */ + +void fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel, unsigned char onenote); +int fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_prev); +void fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev); +void fluid_channel_clear_monolist(fluid_channel_t *chan); +void fluid_channel_set_onenote_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel); +void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan); +void fluid_channel_cc_legato(fluid_channel_t *chan, int value); +void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value); + + +#endif /* _FLUID_CHAN_H */ diff --git a/libs/fluidsynth/src/synth/fluid_event.c b/libs/fluidsynth/src/synth/fluid_event.c new file mode 100644 index 00000000000..48b781bd7bc --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_event.c @@ -0,0 +1,863 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/* + 2002 : API design by Peter Hanappe and Antoine Schmitt + August 2002 : Implementation by Antoine Schmitt as@gratin.org + as part of the infiniteCD author project + http://www.infiniteCD.org/ + Oct4.2002 : AS : corrected bug in heap allocation, that caused a crash during sequencer free. +*/ + + +#include "fluid_event.h" +#include "fluidsynth_priv.h" +#include "fluid_midi.h" + +/*************************************************************** + * + * SEQUENCER EVENTS + */ + +/* Event alloc/free */ + +void +fluid_event_clear(fluid_event_t *evt) +{ + FLUID_MEMSET(evt, 0, sizeof(fluid_event_t)); + + // by default, no type + evt->dest = -1; + evt->src = -1; + evt->type = -1; + evt->id = -1; +} + +/** + * Create a new sequencer event structure. + * @return New sequencer event structure or NULL if out of memory + */ +fluid_event_t * +new_fluid_event() +{ + fluid_event_t *evt; + + evt = FLUID_NEW(fluid_event_t); + + if(evt == NULL) + { + FLUID_LOG(FLUID_PANIC, "event: Out of memory\n"); + return NULL; + } + + fluid_event_clear(evt); + + return(evt); +} + +/** + * Delete a sequencer event structure. + * @param evt Sequencer event structure created by new_fluid_event(). + */ +void +delete_fluid_event(fluid_event_t *evt) +{ + fluid_return_if_fail(evt != NULL); + + FLUID_FREE(evt); +} + +/** + * Set the time field of a sequencer event. + * @internal + * @param evt Sequencer event structure + * @param time Time value to assign + */ +void +fluid_event_set_time(fluid_event_t *evt, unsigned int time) +{ + evt->time = time; +} + +void +fluid_event_set_id(fluid_event_t *evt, fluid_note_id_t id) +{ + evt->id = id; +} + +/** + * Set source of a sequencer event. \c src must be a unique sequencer ID or -1 if not set. + * @param evt Sequencer event structure + * @param src Unique sequencer ID + */ +void +fluid_event_set_source(fluid_event_t *evt, fluid_seq_id_t src) +{ + evt->src = src; +} + +/** + * Set destination of this sequencer event, i.e. the sequencer client this event will be sent to. \c dest must be a unique sequencer ID. + * @param evt Sequencer event structure + * @param dest The destination unique sequencer ID + */ +void +fluid_event_set_dest(fluid_event_t *evt, fluid_seq_id_t dest) +{ + evt->dest = dest; +} + +/** + * Set a sequencer event to be a timer event. + * @param evt Sequencer event structure + * @param data User supplied data pointer + */ +void +fluid_event_timer(fluid_event_t *evt, void *data) +{ + evt->type = FLUID_SEQ_TIMER; + evt->data = data; +} + +/** + * Set a sequencer event to be a note on event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param key MIDI note number (0-127) + * @param vel MIDI velocity value (0-127) + * @note Since fluidsynth 2.2.2, this function will give you a #FLUID_SEQ_NOTEOFF when + * called with @p vel being zero. + */ +void +fluid_event_noteon(fluid_event_t *evt, int channel, short key, short vel) +{ + if(vel == 0) + { + fluid_event_noteoff(evt, channel, key); + return; + } + + evt->type = FLUID_SEQ_NOTEON; + evt->channel = channel; + evt->key = key; + evt->vel = vel; +} + +/** + * Set a sequencer event to be a note off event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param key MIDI note number (0-127) + */ +void +fluid_event_noteoff(fluid_event_t *evt, int channel, short key) +{ + evt->type = FLUID_SEQ_NOTEOFF; + evt->channel = channel; + evt->key = key; +} + +/** + * Set a sequencer event to be a note duration event. + * + * Before fluidsynth 2.2.0, this event type was naively implemented when used in conjunction with fluid_sequencer_register_fluidsynth(), + * because it simply enqueued a fluid_event_noteon() and fluid_event_noteoff(). + * A handling for overlapping notes was not implemented. Starting with 2.2.0, this changes: If a fluid_event_note() is already playing, + * while another fluid_event_note() arrives on the same @c channel and @c key, the earlier event will be canceled. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param key MIDI note number (0-127) + * @param vel MIDI velocity value (1-127) + * @param duration Duration of note in the time scale used by the sequencer + * + * @note The application should decide whether to use only Notes with duration, or separate NoteOn and NoteOff events. + * @warning Calling this function with @p vel or @p duration being zero results in undefined behavior! + */ +void +fluid_event_note(fluid_event_t *evt, int channel, short key, short vel, unsigned int duration) +{ + evt->type = FLUID_SEQ_NOTE; + evt->channel = channel; + evt->key = key; + evt->vel = vel; + evt->duration = duration; +} + +/** + * Set a sequencer event to be an all sounds off event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + */ +void +fluid_event_all_sounds_off(fluid_event_t *evt, int channel) +{ + evt->type = FLUID_SEQ_ALLSOUNDSOFF; + evt->channel = channel; +} + +/** + * Set a sequencer event to be a all notes off event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + */ +void +fluid_event_all_notes_off(fluid_event_t *evt, int channel) +{ + evt->type = FLUID_SEQ_ALLNOTESOFF; + evt->channel = channel; +} + +/** + * Set a sequencer event to be a bank select event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param bank_num MIDI bank number (0-16383) + */ +void +fluid_event_bank_select(fluid_event_t *evt, int channel, short bank_num) +{ + evt->type = FLUID_SEQ_BANKSELECT; + evt->channel = channel; + evt->control = bank_num; +} + +/** + * Set a sequencer event to be a program change event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val MIDI program number (0-127) + */ +void +fluid_event_program_change(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_PROGRAMCHANGE; + evt->channel = channel; + evt->value = val; +} + +/** + * Set a sequencer event to be a program select event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param sfont_id SoundFont ID number + * @param bank_num MIDI bank number (0-16383) + * @param preset_num MIDI preset number (0-127) + */ +void +fluid_event_program_select(fluid_event_t *evt, int channel, + unsigned int sfont_id, short bank_num, short preset_num) +{ + evt->type = FLUID_SEQ_PROGRAMSELECT; + evt->channel = channel; + evt->duration = sfont_id; + evt->value = preset_num; + evt->control = bank_num; +} + +/** + * Set a sequencer event to be a pitch bend event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param pitch MIDI pitch bend value (0-16383, 8192 = no bend) + */ +void +fluid_event_pitch_bend(fluid_event_t *evt, int channel, int pitch) +{ + evt->type = FLUID_SEQ_PITCHBEND; + evt->channel = channel; + + if(pitch < 0) + { + pitch = 0; + } + + if(pitch > 16383) + { + pitch = 16383; + } + + evt->pitch = pitch; +} + +/** + * Set a sequencer event to be a pitch wheel sensitivity event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param value MIDI pitch wheel sensitivity value in semitones + */ +void +fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, int value) +{ + evt->type = FLUID_SEQ_PITCHWHEELSENS; + evt->channel = channel; + evt->value = value; +} + +/** + * Set a sequencer event to be a modulation event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val MIDI modulation value (0-127) + */ +void +fluid_event_modulation(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_MODULATION; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a MIDI sustain event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val MIDI sustain value (0-127) + */ +void +fluid_event_sustain(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_SUSTAIN; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a MIDI control change event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param control MIDI control number (0-127) + * @param val MIDI control value (0-127) + */ +void +fluid_event_control_change(fluid_event_t *evt, int channel, short control, int val) +{ + evt->type = FLUID_SEQ_CONTROLCHANGE; + evt->channel = channel; + evt->control = control; + evt->value = val; +} + +/** + * Set a sequencer event to be a stereo pan event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val MIDI panning value (0-127, 0=left, 64 = middle, 127 = right) + */ +void +fluid_event_pan(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_PAN; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a volume event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val Volume value (0-127) + */ +void +fluid_event_volume(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_VOLUME; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a reverb send event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val Reverb amount (0-127) + */ +void +fluid_event_reverb_send(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_REVERBSEND; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a chorus send event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val Chorus amount (0-127) + */ +void +fluid_event_chorus_send(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_CHORUSSEND; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + + +/** + * Set a sequencer event to be an unregistering event. + * @param evt Sequencer event structure + * @since 1.1.0 + */ +void +fluid_event_unregistering(fluid_event_t *evt) +{ + evt->type = FLUID_SEQ_UNREGISTERING; +} + +/** + * Set a sequencer event to be a scale change event. + * Useful for scheduling tempo changes. + * @param evt Sequencer event structure + * @param new_scale The new time scale to apply to the sequencer, see fluid_sequencer_set_time_scale() + * @since 2.2.0 + */ +void +fluid_event_scale(fluid_event_t *evt, double new_scale) +{ + evt->type = FLUID_SEQ_SCALE; + evt->scale = new_scale; +} + +/** + * Set a sequencer event to be a channel-wide aftertouch event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val Aftertouch amount (0-127) + * @since 1.1.0 + */ +void +fluid_event_channel_pressure(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_CHANNELPRESSURE; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a polyphonic aftertouch event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param key MIDI note number (0-127) + * @param val Aftertouch amount (0-127) + * @since 2.0.0 + */ +void +fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, int val) +{ + evt->type = FLUID_SEQ_KEYPRESSURE; + evt->channel = channel; + + if(key < 0) + { + key = 0; + } + + if(key > 127) + { + key = 127; + } + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->key = key; + evt->value = val; +} + +/** + * Set a sequencer event to be a midi system reset event. + * @param evt Sequencer event structure + * @since 1.1.0 + */ +void +fluid_event_system_reset(fluid_event_t *evt) +{ + evt->type = FLUID_SEQ_SYSTEMRESET; +} + +/** + * Transforms an incoming MIDI event (from a MIDI driver or MIDI router) to a + * sequencer event. + * + * @param evt Sequencer event structure + * @param event MIDI event + * @return #FLUID_OK or #FLUID_FAILED + * + * @note This function copies the fields of the MIDI event into the provided + * sequencer event. Calling applications must create the sequencer event and set + * additional fields such as the source and destination of the sequencer event. + * + * @code{.cpp} + * // ... get MIDI event, e.g. using player_callback() + * + * // Send MIDI event to sequencer to play + * fluid_event_t *evt = new_fluid_event(); + * fluid_event_set_source(evt, -1); + * fluid_event_set_dest(evt, seqid); + * fluid_event_from_midi_event(evt, event); + * fluid_sequencer_send_at(sequencer, evt, 50, 0); // relative time + * delete_fluid_event(evt); + * @endcode + * + * @since 2.2.7 + */ +int fluid_event_from_midi_event(fluid_event_t *evt, const fluid_midi_event_t *event) +{ + int chan; + fluid_return_val_if_fail(event != NULL, FLUID_FAILED); + + chan = fluid_midi_event_get_channel(event); + + switch (fluid_midi_event_get_type(event)) + { + case NOTE_OFF: + fluid_event_noteoff(evt, chan, (short)fluid_midi_event_get_key(event)); + break; + + case NOTE_ON: + fluid_event_noteon(evt, + fluid_midi_event_get_channel(event), + (short)fluid_midi_event_get_key(event), + (short)fluid_midi_event_get_velocity(event)); + break; + + case CONTROL_CHANGE: + fluid_event_control_change(evt, + chan, + (short)fluid_midi_event_get_control(event), + (short)fluid_midi_event_get_value(event)); + break; + + case PROGRAM_CHANGE: + fluid_event_program_change(evt, chan, (short)fluid_midi_event_get_program(event)); + break; + + case PITCH_BEND: + fluid_event_pitch_bend(evt, chan, fluid_midi_event_get_pitch(event)); + break; + + case CHANNEL_PRESSURE: + fluid_event_channel_pressure(evt, chan, (short)fluid_midi_event_get_program(event)); + break; + + case KEY_PRESSURE: + fluid_event_key_pressure(evt, + chan, + (short)fluid_midi_event_get_key(event), + (short)fluid_midi_event_get_value(event)); + break; + + case MIDI_SYSTEM_RESET: + fluid_event_system_reset(evt); + break; + + default: /* Not yet implemented */ + return FLUID_FAILED; + } + + return FLUID_OK; +} + +/* + * Accessing event data + */ + +/** + * Get the event type (#fluid_seq_event_type) field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Event type (#fluid_seq_event_type). + */ +int fluid_event_get_type(fluid_event_t *evt) +{ + return evt->type; +} + +/** + * @internal + * Get the time field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Time value + */ +unsigned int fluid_event_get_time(fluid_event_t *evt) +{ + return evt->time; +} + +/** + * @internal + * Get the time field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Time value + */ +fluid_note_id_t fluid_event_get_id(fluid_event_t *evt) +{ + return evt->id; +} + +/** + * Get the source sequencer client from a sequencer event structure. + * @param evt Sequencer event structure + * @return source field of the sequencer event + */ +fluid_seq_id_t fluid_event_get_source(fluid_event_t *evt) +{ + return evt->src; +} + +/** + * Get the dest sequencer client from a sequencer event structure. + * @param evt Sequencer event structure + * @return dest field of the sequencer event + */ +fluid_seq_id_t fluid_event_get_dest(fluid_event_t *evt) +{ + return evt->dest; +} + +/** + * Get the MIDI channel field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI zero-based channel number + */ +int fluid_event_get_channel(fluid_event_t *evt) +{ + return evt->channel; +} + +/** + * Get the MIDI note field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI note number (0-127) + */ +short fluid_event_get_key(fluid_event_t *evt) +{ + return evt->key; +} + +/** + * Get the MIDI velocity field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI velocity value (0-127) + */ +short fluid_event_get_velocity(fluid_event_t *evt) + +{ + return evt->vel; +} + +/** + * Get the MIDI control number field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI control number (0-127) + */ +short fluid_event_get_control(fluid_event_t *evt) +{ + return evt->control; +} + +/** + * Get the value field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Value field of event. + * + * The Value field is used by the following event types: + * #FLUID_SEQ_PROGRAMCHANGE, #FLUID_SEQ_PROGRAMSELECT (preset_num), + * #FLUID_SEQ_PITCHWHEELSENS, #FLUID_SEQ_MODULATION, #FLUID_SEQ_SUSTAIN, + * #FLUID_SEQ_CONTROLCHANGE, #FLUID_SEQ_PAN, #FLUID_SEQ_VOLUME, + * #FLUID_SEQ_REVERBSEND, #FLUID_SEQ_CHORUSSEND. + */ +int fluid_event_get_value(fluid_event_t *evt) +{ + return evt->value; +} + +/** + * Get the data field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Data field of event. + * + * Used by the #FLUID_SEQ_TIMER event type. + */ +void *fluid_event_get_data(fluid_event_t *evt) +{ + return evt->data; +} + +/** + * Get the duration field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Note duration value in the time scale used by the sequencer (by default milliseconds) + * + * Used by the #FLUID_SEQ_NOTE event type. + */ +unsigned int fluid_event_get_duration(fluid_event_t *evt) +{ + return evt->duration; +} + +/** + * Get the MIDI bank field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI bank number (0-16383) + * + * Used by the #FLUID_SEQ_BANKSELECT and #FLUID_SEQ_PROGRAMSELECT + * event types. + */ +short fluid_event_get_bank(fluid_event_t *evt) +{ + return evt->control; +} + +/** + * Get the pitch field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI pitch bend pitch value (0-16383, 8192 = no bend) + * + * Used by the #FLUID_SEQ_PITCHBEND event type. + */ +int fluid_event_get_pitch(fluid_event_t *evt) +{ + return evt->pitch; +} + +/** + * Get the MIDI program field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI program number (0-127) + * + * Used by the #FLUID_SEQ_PROGRAMCHANGE and #FLUID_SEQ_PROGRAMSELECT + * event types. + */ +int +fluid_event_get_program(fluid_event_t *evt) +{ + return evt->value; +} + +/** + * Get the SoundFont ID field from a sequencer event structure. + * @param evt Sequencer event structure + * @return SoundFont identifier value. + * + * Used by the #FLUID_SEQ_PROGRAMSELECT event type. + */ +unsigned int +fluid_event_get_sfont_id(fluid_event_t *evt) +{ + return evt->duration; +} + +/** + * Gets time scale field from a sequencer event structure. + * @param evt Sequencer event structure + * @return SoundFont identifier value. + * + * Used by the #FLUID_SEQ_SCALE event type. + */ +double fluid_event_get_scale(fluid_event_t *evt) +{ + return evt->scale; +} diff --git a/libs/fluidsynth/src/synth/fluid_event.h b/libs/fluidsynth/src/synth/fluid_event.h new file mode 100644 index 00000000000..4a0cca5d93d --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_event.h @@ -0,0 +1,65 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_EVENT_PRIV_H +#define _FLUID_EVENT_PRIV_H + +#include "fluidsynth.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int fluid_note_id_t; + +/* Private data for event */ +/* ?? should be optimized in size, using unions */ +struct _fluid_event_t +{ + unsigned int time; + int type; + fluid_seq_id_t src; + fluid_seq_id_t dest; + int channel; + short key; + short vel; + short control; + int value; + fluid_note_id_t id; + int pitch; + unsigned int duration; + double scale; + void *data; +}; + +unsigned int fluid_event_get_time(fluid_event_t *evt); +void fluid_event_set_time(fluid_event_t *evt, unsigned int time); + +fluid_note_id_t fluid_event_get_id(fluid_event_t *evt); +void fluid_event_set_id(fluid_event_t *evt, fluid_note_id_t id); + +void fluid_event_clear(fluid_event_t *evt); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUID_EVENT_PRIV_H */ diff --git a/libs/fluidsynth/src/synth/fluid_gen.c b/libs/fluidsynth/src/synth/fluid_gen.c new file mode 100644 index 00000000000..2ce2b0f74b5 --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_gen.c @@ -0,0 +1,133 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#include "fluid_gen.h" +#include "fluid_chan.h" + + +#define _GEN(_name) GEN_ ## _name, #_name + + +/* See SFSpec21 $8.1.3 */ +static const fluid_gen_info_t fluid_gen_info[] = +{ + /* number/name init nrpn-scale min max def */ + { _GEN(STARTADDROFS), 1, 1, 0.0f, 1e10f, 0.0f }, + { _GEN(ENDADDROFS), 1, 1, -1e10f, 0.0f, 0.0f }, + { _GEN(STARTLOOPADDROFS), 1, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(ENDLOOPADDROFS), 1, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(STARTADDRCOARSEOFS), 0, 1, 0.0f, 1e10f, 0.0f }, + { _GEN(MODLFOTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(VIBLFOTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(MODENVTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(FILTERFC), 1, 2, 1500.0f, 13500.0f, 13500.0f }, + { _GEN(FILTERQ), 1, 1, 0.0f, 960.0f, 0.0f }, + { _GEN(MODLFOTOFILTERFC), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(MODENVTOFILTERFC), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(ENDADDRCOARSEOFS), 0, 1, -1e10f, 0.0f, 0.0f }, + { _GEN(MODLFOTOVOL), 1, 1, -960.0f, 960.0f, 0.0f }, + { _GEN(UNUSED1), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(CHORUSSEND), 1, 1, 0.0f, 1000.0f, 0.0f }, + { _GEN(REVERBSEND), 1, 1, 0.0f, 1000.0f, 0.0f }, + { _GEN(PAN), 1, 1, -500.0f, 500.0f, 0.0f }, + { _GEN(UNUSED2), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(UNUSED3), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(UNUSED4), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(MODLFODELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(MODLFOFREQ), 1, 4, -16000.0f, 4500.0f, 0.0f }, + { _GEN(VIBLFODELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(VIBLFOFREQ), 1, 4, -16000.0f, 4500.0f, 0.0f }, + { _GEN(MODENVDELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(MODENVATTACK), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(MODENVHOLD), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(MODENVDECAY), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(MODENVSUSTAIN), 0, 1, 0.0f, 1000.0f, 0.0f }, + { _GEN(MODENVRELEASE), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(KEYTOMODENVHOLD), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(KEYTOMODENVDECAY), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(VOLENVDELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(VOLENVATTACK), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(VOLENVHOLD), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(VOLENVDECAY), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(VOLENVSUSTAIN), 0, 1, 0.0f, 1440.0f, 0.0f }, + { _GEN(VOLENVRELEASE), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(KEYTOVOLENVHOLD), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(KEYTOVOLENVDECAY), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(INSTRUMENT), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(RESERVED1), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(KEYRANGE), 0, 0, 0.0f, 127.0f, 0.0f }, + { _GEN(VELRANGE), 0, 0, 0.0f, 127.0f, 0.0f }, + { _GEN(STARTLOOPADDRCOARSEOFS), 0, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(KEYNUM), 1, 0, 0.0f, 127.0f, -1.0f }, + { _GEN(VELOCITY), 1, 1, 0.0f, 127.0f, -1.0f }, + { _GEN(ATTENUATION), 1, 1, 0.0f, 1440.0f, 0.0f }, + { _GEN(RESERVED2), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(ENDLOOPADDRCOARSEOFS), 0, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(COARSETUNE), 0, 1, -120.0f, 120.0f, 0.0f }, + { _GEN(FINETUNE), 0, 1, -99.0f, 99.0f, 0.0f }, + { _GEN(SAMPLEID), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(SAMPLEMODE), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(RESERVED3), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(SCALETUNE), 0, 1, 0.0f, 1200.0f, 100.0f }, + { _GEN(EXCLUSIVECLASS), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(OVERRIDEROOTKEY), 1, 0, 0.0f, 127.0f, -1.0f }, + { _GEN(PITCH), 1, 0, 0.0f, 127.0f, 0.0f }, + { _GEN(CUSTOM_BALANCE), 1, 0, -960.0f, 960.0f, 0.0f }, + { _GEN(CUSTOM_FILTERFC), 1, 2, 0.0f, 22050.0f, 0.0f }, + { _GEN(CUSTOM_FILTERQ), 1, 1, 0.0f, 960.0f, 0.0f } +}; + +/* fluid_gen_init + * + * Set an array of generators to their initial value + */ +void +fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel) +{ + int i; + + for(i = 0; i < GEN_LAST; i++) + { + gen[i].flags = GEN_UNUSED; + gen[i].mod = 0.0; + gen[i].nrpn = (channel == NULL) ? 0.0 : fluid_channel_get_gen(channel, i); + gen[i].val = fluid_gen_info[i].def; + } +} + +fluid_real_t fluid_gen_scale(int gen, float value) +{ + return (fluid_gen_info[gen].min + + value * (fluid_gen_info[gen].max - fluid_gen_info[gen].min)); +} + +fluid_real_t fluid_gen_scale_nrpn(int gen, int data) +{ + data = data - 8192; + fluid_clip(data, -8192, 8192); + return (fluid_real_t)(data * fluid_gen_info[gen].nrpn_scale); +} + + +const char *fluid_gen_name(int gen) +{ + return fluid_gen_info[gen].name; +} diff --git a/libs/fluidsynth/src/synth/fluid_gen.h b/libs/fluidsynth/src/synth/fluid_gen.h new file mode 100644 index 00000000000..b87e8d8a8c6 --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_gen.h @@ -0,0 +1,67 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_GEN_H +#define _FLUID_GEN_H + +#include "fluidsynth_priv.h" + +typedef struct _fluid_gen_info_t +{ + char num; /* Generator number */ + char *name; + char init; /* Does the generator need to be initialized (not used) */ + char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */ + float min; /* The minimum value */ + float max; /* The maximum value */ + float def; /* The default value (cfr. fluid_gen_init()) */ +} fluid_gen_info_t; + +/* + * SoundFont generator structure. + */ +typedef struct _fluid_gen_t +{ + unsigned char flags; /**< Is the generator set or not (#fluid_gen_flags) */ + double val; /**< The nominal value */ + double mod; /**< Change by modulators */ + double nrpn; /**< Change by NRPN messages */ +} fluid_gen_t; + +/* + * Enum value for 'flags' field of #fluid_gen_t (not really flags). + */ +enum fluid_gen_flags +{ + GEN_UNUSED, /**< Generator value is not set */ + GEN_SET, /**< Generator value is set */ +}; + +#define fluid_gen_set_mod(_gen, _val) { (_gen)->mod = (double) (_val); } +#define fluid_gen_set_nrpn(_gen, _val) { (_gen)->nrpn = (double) (_val); } + +fluid_real_t fluid_gen_scale(int gen, float value); +fluid_real_t fluid_gen_scale_nrpn(int gen, int nrpn); +void fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel); +const char *fluid_gen_name(int gen); + + +#endif /* _FLUID_GEN_H */ diff --git a/libs/fluidsynth/src/synth/fluid_mod.c b/libs/fluidsynth/src/synth/fluid_mod.c new file mode 100644 index 00000000000..88e3ba3532b --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_mod.c @@ -0,0 +1,880 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_mod.h" +#include "fluid_chan.h" +#include "fluid_voice.h" + +/** + * Clone the modulators destination, sources, flags and amount. + * + * @param mod the modulator to store the copy to + * @param src the source modulator to retrieve the information from + * + * @note The \c next member of \c mod will be left unchanged. + */ +void +fluid_mod_clone(fluid_mod_t *mod, const fluid_mod_t *src) +{ + mod->dest = src->dest; + mod->src1 = src->src1; + mod->flags1 = src->flags1; + mod->src2 = src->src2; + mod->flags2 = src->flags2; + mod->amount = src->amount; +} + +/** + * Set a modulator's primary source controller and flags. + * + * @param mod The modulator instance + * @param src Modulator source (#fluid_mod_src or a MIDI controller number) + * @param flags Flags determining mapping function and whether the source + * controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller + * (#FLUID_MOD_CC), see #fluid_mod_flags. + */ +void +fluid_mod_set_source1(fluid_mod_t *mod, int src, int flags) +{ + mod->src1 = src; + mod->flags1 = flags; +} + +/** + * Set a modulator's secondary source controller and flags. + * + * @param mod The modulator instance + * @param src Modulator source (#fluid_mod_src or a MIDI controller number) + * @param flags Flags determining mapping function and whether the source + * controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller + * (#FLUID_MOD_CC), see #fluid_mod_flags. + */ +void +fluid_mod_set_source2(fluid_mod_t *mod, int src, int flags) +{ + mod->src2 = src; + mod->flags2 = flags; +} + +/** + * Set the destination effect of a modulator. + * + * @param mod The modulator instance + * @param dest Destination generator (#fluid_gen_type) + */ +void +fluid_mod_set_dest(fluid_mod_t *mod, int dest) +{ + mod->dest = dest; +} + +/** + * Set the scale amount of a modulator. + * + * @param mod The modulator instance + * @param amount Scale amount to assign + */ +void +fluid_mod_set_amount(fluid_mod_t *mod, double amount) +{ + mod->amount = (double) amount; +} + +/** + * Get the primary source value from a modulator. + * + * @param mod The modulator instance + * @return The primary source value (#fluid_mod_src or a MIDI CC controller value). + */ +int +fluid_mod_get_source1(const fluid_mod_t *mod) +{ + return mod->src1; +} + +/** + * Get primary source flags from a modulator. + * + * @param mod The modulator instance + * @return The primary source flags (#fluid_mod_flags). + */ +int +fluid_mod_get_flags1(const fluid_mod_t *mod) +{ + return mod->flags1; +} + +/** + * Get the secondary source value from a modulator. + * + * @param mod The modulator instance + * @return The secondary source value (#fluid_mod_src or a MIDI CC controller value). + */ +int +fluid_mod_get_source2(const fluid_mod_t *mod) +{ + return mod->src2; +} + +/** + * Get secondary source flags from a modulator. + * + * @param mod The modulator instance + * @return The secondary source flags (#fluid_mod_flags). + */ +int +fluid_mod_get_flags2(const fluid_mod_t *mod) +{ + return mod->flags2; +} + +/** + * Get destination effect from a modulator. + * + * @param mod The modulator instance + * @return Destination generator (#fluid_gen_type) + */ +int +fluid_mod_get_dest(const fluid_mod_t *mod) +{ + return mod->dest; +} + +/** + * Get the scale amount from a modulator. + * + * @param mod The modulator instance + * @return Scale amount + */ +double +fluid_mod_get_amount(const fluid_mod_t *mod) +{ + return (double) mod->amount; +} + +/* + * retrieves the initial value from the given source of the modulator + */ +static fluid_real_t +fluid_mod_get_source_value(const unsigned char mod_src, + const unsigned char mod_flags, + fluid_real_t *range, + const fluid_voice_t *voice + ) +{ + const fluid_channel_t *chan = voice->channel; + fluid_real_t val; + + if(mod_flags & FLUID_MOD_CC) + { + val = fluid_channel_get_cc(chan, mod_src); + + if(mod_src == PORTAMENTO_CTRL) + { + // an invalid portamento fromkey should be treated as 0 when it's actually used for moulating + if(!fluid_channel_is_valid_note(val)) + { + val = 0; + } + } + } + else + { + switch(mod_src) + { + case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */ + val = *range; + break; + + case FLUID_MOD_VELOCITY: + val = fluid_voice_get_actual_velocity(voice); + break; + + case FLUID_MOD_KEY: + val = fluid_voice_get_actual_key(voice); + break; + + case FLUID_MOD_KEYPRESSURE: + val = fluid_channel_get_key_pressure(chan, voice->key); + break; + + case FLUID_MOD_CHANNELPRESSURE: + val = fluid_channel_get_channel_pressure(chan); + break; + + case FLUID_MOD_PITCHWHEEL: + val = fluid_channel_get_pitch_bend(chan); + *range = 0x4000; + break; + + case FLUID_MOD_PITCHWHEELSENS: + val = fluid_channel_get_pitch_wheel_sensitivity(chan); + break; + + default: + FLUID_LOG(FLUID_ERR, "Unknown modulator source '%d', disabling modulator.", mod_src); + val = 0.0; + } + } + + return val; +} + +/** + * transforms the initial value retrieved by \c fluid_mod_get_source_value into [0.0;1.0] + */ +static fluid_real_t +fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, const fluid_real_t range) +{ + /* normalized value, i.e. usually in the range [0;1] */ + const fluid_real_t val_norm = val / range; + + /* we could also only switch case the lower nibble of mod_flags, however + * this would keep us from adding further mod types in the future + * + * instead just remove the flag(s) we already took care of + */ + mod_flags &= ~FLUID_MOD_CC; + + switch(mod_flags/* & 0x0f*/) + { + case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =0 */ + val = val_norm; + break; + + case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =1 */ + val = 1.0f - val_norm; + break; + + case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =2 */ + val = -1.0f + 2.0f * val_norm; + break; + + case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =3 */ + val = 1.0f - 2.0f * val_norm; + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =4 */ + val = fluid_concave(127 * (val_norm)); + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =5 */ + val = fluid_concave(127 * (1.0f - val_norm)); + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =6 */ + val = (val_norm > 0.5f) ? fluid_concave(127 * 2 * (val_norm - 0.5f)) + : -fluid_concave(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =7 */ + val = (val_norm > 0.5f) ? -fluid_concave(127 * 2 * (val_norm - 0.5f)) + : fluid_concave(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =8 */ + val = fluid_convex(127 * (val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =9 */ + val = fluid_convex(127 * (1.0f - val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =10 */ + val = (val_norm > 0.5f) ? fluid_convex(127 * 2 * (val_norm - 0.5f)) + : -fluid_convex(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =11 */ + val = (val_norm > 0.5f) ? -fluid_convex(127 * 2 * (val_norm - 0.5f)) + : fluid_convex(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =12 */ + val = (val_norm >= 0.5f) ? 1.0f : 0.0f; + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =13 */ + val = (val_norm >= 0.5f) ? 0.0f : 1.0f; + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =14 */ + val = (val_norm >= 0.5f) ? 1.0f : -1.0f; + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =15 */ + val = (val_norm >= 0.5f) ? -1.0f : 1.0f; + break; + + /* + * MIDI CCs only have a resolution of 7 bits. The closer val_norm gets to 1, + * the less will be the resulting change of the sinus. When using this sin() + * for scaling the cutoff frequency, there will be no audible difference between + * MIDI CCs 118 to 127. To avoid this waste of CCs multiply with 0.87 + * (at least for unipolar) which makes sin() never get to 1.0 but to 0.98 which + * is close enough. + */ + case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* custom sin(x) */ + val = FLUID_SIN((FLUID_M_PI / 2.0f * 0.87f) * val_norm); + break; + + case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* custom */ + val = FLUID_SIN((FLUID_M_PI / 2.0f * 0.87f) * (1.0f - val_norm)); + break; + + case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* custom */ + val = (val_norm > 0.5f) ? FLUID_SIN(FLUID_M_PI * (val_norm - 0.5f)) + : -FLUID_SIN(FLUID_M_PI * (0.5f - val_norm)); + break; + + case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* custom */ + val = (val_norm > 0.5f) ? -FLUID_SIN(FLUID_M_PI * (val_norm - 0.5f)) + : FLUID_SIN(FLUID_M_PI * (0.5f - val_norm)); + break; + + default: + FLUID_LOG(FLUID_ERR, "Unknown modulator type '%d', disabling modulator.", mod_flags); + val = 0.0f; + break; + } + + return val; +} + +/* + * fluid_mod_get_value. + * Computes and return modulator output following SF2.01 + * (See SoundFont Modulator Controller Model Chapter 9.5). + * + * Output = Transform(Amount * Map(primary source input) * Map(secondary source input)) + * + * Notes: + * 1)fluid_mod_get_value, ignores the Transform operator. The result is: + * + * Output = Amount * Map(primary source input) * Map(secondary source input) + * + * 2)When primary source input (src1) is set to General Controller 'No Controller', + * output is forced to 0. + * + * 3)When secondary source input (src2) is set to General Controller 'No Controller', + * output is forced to +1.0 + */ +fluid_real_t +fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice) +{ + extern fluid_mod_t default_vel2filter_mod; + + fluid_real_t v1 = 0.0, v2 = 1.0; + /* The wording of the default modulators refers to a range of 127/128. + * And the table in section 9.5.3 suggests, that this mapping should be applied + * to all unipolar and bipolar mappings respectively. + * + * Thinking about this further, this is actually pretty clever, as this is properly + * addresses MIDI Recommended Practice (RP-036) Default Pan Formula + * "Since MIDI controller values range from 0 to 127, the exact center + * of the range, 63.5, cannot be represented." + * + * When changing the overall range to 127/128 however, the "middle pan" value of 64 + * can be correctly represented. + */ + fluid_real_t range1 = 128.0, range2 = 128.0; + + /* 'special treatment' for default controller + * + * Reference: SF2.01 section 8.4.2 + * + * The GM default controller 'vel-to-filter cut off' is not clearly + * defined: If implemented according to the specs, the filter + * frequency jumps between vel=63 and vel=64. To maintain + * compatibility with existing sound fonts, the implementation is + * 'hardcoded', it is impossible to implement using only one + * modulator otherwise. + * + * I assume here, that the 'intention' of the paragraph is one + * octave (1200 cents) filter frequency shift between vel=127 and + * vel=64. 'amount' is (-2400), at least as long as the controller + * is set to default. + * + * Further, the 'appearance' of the modulator (source enumerator, + * destination enumerator, flags etc) is different from that + * described in section 8.4.2, but it matches the definition used in + * several SF2.1 sound fonts (where it is used only to turn it off). + * */ + if(fluid_mod_test_identity(mod, &default_vel2filter_mod)) + { +// S. Christian Collins' mod, to stop forcing velocity based filtering + /* + if (voice->vel < 64){ + return (fluid_real_t) mod->amount / 2.0; + } else { + return (fluid_real_t) mod->amount * (127 - voice->vel) / 127; + } + */ + return 0; // (fluid_real_t) mod->amount / 2.0; + } + +// end S. Christian Collins' mod + + /* get the initial value of the first source */ + if(mod->src1 > 0) + { + v1 = fluid_mod_get_source_value(mod->src1, mod->flags1, &range1, voice); + + /* transform the input value */ + v1 = fluid_mod_transform_source_value(v1, mod->flags1, range1); + } + /* When primary source input (src1) is set to General Controller 'No Controller', + output is forced to 0.0 + */ + else + { + return 0.0; + } + + /* no need to go further */ + if(v1 == 0.0f) + { + return 0.0f; + } + + /* get the second input source */ + if(mod->src2 > 0) + { + v2 = fluid_mod_get_source_value(mod->src2, mod->flags2, &range2, voice); + + /* transform the second input value */ + v2 = fluid_mod_transform_source_value(v2, mod->flags2, range2); + } + /* When secondary source input (src2) is set to General Controller 'No Controller', + output is forced to +1.0 + */ + else + { + v2 = 1.0f; + } + + /* it's as simple as that: */ + return (fluid_real_t) mod->amount * v1 * v2; +} + +/** + * Create a new uninitialized modulator structure. + * + * @return New allocated modulator or NULL if out of memory + */ +fluid_mod_t * +new_fluid_mod() +{ + fluid_mod_t *mod = FLUID_NEW(fluid_mod_t); + + if(mod == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + return mod; +} + +/** + * Free a modulator structure. + * + * @param mod Modulator to free + */ +void +delete_fluid_mod(fluid_mod_t *mod) +{ + FLUID_FREE(mod); +} + +/** + * Returns the size of the fluid_mod_t structure. + * + * @return Size of fluid_mod_t in bytes + * + * Useful in low latency scenarios e.g. to allocate a modulator on the stack. + */ +size_t fluid_mod_sizeof() +{ + return sizeof(fluid_mod_t); +} + +/** + * Checks if modulator with source 1 other than CC is FLUID_MOD_NONE. + * + * @param mod, modulator. + * @return TRUE if modulator source 1 other than cc is FLUID_MOD_NONE, FALSE otherwise. + */ +static int +fluid_mod_is_src1_none(const fluid_mod_t *mod) +{ + return(((mod->flags1 & FLUID_MOD_CC) == 0) && (mod->src1 == FLUID_MOD_NONE)); +} + +/** + * Checks if modulators source other than CC source is invalid. + * + * @param mod, modulator. + * @param src1_select, source input selection to check. + * 1 to check src1 source. + * 0 to check src2 source. + * @return FALSE if selected modulator source other than cc is invalid, TRUE otherwise. + * + * (specs SF 2.01 7.4, 7.8, 8.2.1) + */ +static int +fluid_mod_check_non_cc_source(const fluid_mod_t *mod, unsigned char src1_select) +{ + unsigned char flags, src; + + if(src1_select) + { + flags = mod->flags1; + src = mod->src1; + } + else + { + flags = mod->flags2; + src = mod->src2; + } + + return(((flags & FLUID_MOD_CC) != 0) /* src is a CC */ + /* SF2.01 section 8.2.1: Constant value */ + || ((src == FLUID_MOD_NONE) + || (src == FLUID_MOD_VELOCITY) /* Note-on velocity */ + || (src == FLUID_MOD_KEY) /* Note-on key number */ + || (src == FLUID_MOD_KEYPRESSURE) /* Poly pressure */ + || (src == FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */ + || (src == FLUID_MOD_PITCHWHEEL) /* Pitch wheel */ + || (src == FLUID_MOD_PITCHWHEELSENS) /* Pitch wheel sensitivity */ + )); +} + +/** + * Checks if modulator CC source is invalid (specs SF 2.01 7.4, 7.8, 8.2.1). + * + * @param mod, modulator. + * @src1_select, source input selection: + * 1 to check src1 source or + * 0 to check src2 source. + * @return FALSE if selected modulator's source CC is invalid, TRUE otherwise. + */ +static int +fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select) +{ + unsigned char flags, src; + + if(src1_select) + { + flags = mod->flags1; + src = mod->src1; + } + else + { + flags = mod->flags2; + src = mod->src2; + } + + return(((flags & FLUID_MOD_CC) == 0) /* src is non CC */ + || ((src != BANK_SELECT_MSB) + && (src != BANK_SELECT_LSB) + && (src != DATA_ENTRY_MSB) + && (src != DATA_ENTRY_LSB) + /* is src not NRPN_LSB, NRPN_MSB, RPN_LSB, RPN_MSB */ + && ((src < NRPN_LSB) || (RPN_MSB < src)) + /* is src not ALL_SOUND_OFF, ALL_CTRL_OFF, LOCAL_CONTROL, ALL_NOTES_OFF ? */ + /* is src not OMNI_OFF, OMNI_ON, POLY_OFF, POLY_ON ? */ + && (src < ALL_SOUND_OFF) + /* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1) + However, as long fluidsynth will use only CC 7 bits resolution, + it is safe to ignore these SF recommendations on CC receive. + See explanations in fluid_synth_cc_LOCAL() */ + /* uncomment next line to forbid CC lsb */ + /* && ((src < 32) || (63 < src)) */ + )); +} + +/** + * Checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1) + * + * @param mod, modulator. + * @param name,if not NULL, pointer on a string displayed as a warning. + * @return TRUE if modulator sources src1, src2 are valid, FALSE otherwise. + */ +int fluid_mod_check_sources(const fluid_mod_t *mod, char *name) +{ + static const char invalid_non_cc_src[] = + "Invalid modulator, using non-CC source %s.src%d=%d"; + static const char invalid_cc_src[] = + "Invalid modulator, using CC source %s.src%d=%d"; + static const char src1_is_none[] = + "Modulator with source 1 none %s.src1=%d"; + + /* checks valid non cc sources */ + if(!fluid_mod_check_non_cc_source(mod, 1)) /* check src1 */ + { + if(name) + { + FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 1, mod->src1); + } + + return FALSE; + } + + /* + When src1 is non CC source FLUID_MOD_NONE, the modulator is valid but + the output of this modulator will be forced to 0 at synthesis time. + Also this modulator cannot be used to overwrite a default modulator (as + there is no default modulator with src1 source equal to FLUID_MOD_NONE). + Consequently it is useful to return FALSE to indicate this modulator + being useless. It will be removed later with others invalid modulators. + */ + if(fluid_mod_is_src1_none(mod)) + { + if(name) + { + FLUID_LOG(FLUID_WARN, src1_is_none, name, mod->src1); + } + + return FALSE; + } + + if(!fluid_mod_check_non_cc_source(mod, 0)) /* check src2 */ + { + if(name) + { + FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 2, mod->src2); + } + + return FALSE; + } + + /* checks valid cc sources */ + if(!fluid_mod_check_cc_source(mod, 1)) /* check src1 */ + { + if(name) + { + FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 1, mod->src1); + } + + return FALSE; + } + + if(!fluid_mod_check_cc_source(mod, 0)) /* check src2 */ + { + if(name) + { + FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 2, mod->src2); + } + + return FALSE; + } + + return TRUE; +} + +/** + * Checks if two modulators are identical in sources, flags and destination. + * + * @param mod1 First modulator + * @param mod2 Second modulator + * @return TRUE if identical, FALSE otherwise + * + * SF2.01 section 9.5.1 page 69, 'bullet' 3 defines 'identical'. + */ +int +fluid_mod_test_identity(const fluid_mod_t *mod1, const fluid_mod_t *mod2) +{ + return mod1->dest == mod2->dest + && mod1->src1 == mod2->src1 + && mod1->src2 == mod2->src2 + && mod1->flags1 == mod2->flags1 + && mod1->flags2 == mod2->flags2; +} + +/** + * Check if the modulator has the given source. + * + * @param mod The modulator instance + * @param cc Boolean value indicating if ctrl is a CC controller or not + * @param ctrl The source to check for (if \c cc == FALSE : a value of type #fluid_mod_src, else the value of the MIDI CC to check for) + * + * @return TRUE if the modulator has the given source, FALSE otherwise. + */ +int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl) +{ + return + ( + ( + ((mod->src1 == ctrl) && ((mod->flags1 & FLUID_MOD_CC) != 0) && (cc != 0)) + || ((mod->src1 == ctrl) && ((mod->flags1 & FLUID_MOD_CC) == 0) && (cc == 0)) + ) + || + ( + ((mod->src2 == ctrl) && ((mod->flags2 & FLUID_MOD_CC) != 0) && (cc != 0)) + || ((mod->src2 == ctrl) && ((mod->flags2 & FLUID_MOD_CC) == 0) && (cc == 0)) + ) + ); +} + +/** + * Check if the modulator has the given destination. + * + * @param mod The modulator instance + * @param gen The destination generator of type #fluid_gen_type to check for + * @return TRUE if the modulator has the given destination, FALSE otherwise. + */ +int fluid_mod_has_dest(const fluid_mod_t *mod, int gen) +{ + return mod->dest == gen; +} + + +/* debug function: Prints the contents of a modulator */ +#ifdef DEBUG +void fluid_dump_modulator(fluid_mod_t *mod) +{ + int src1 = mod->src1; + int dest = mod->dest; + int src2 = mod->src2; + int flags1 = mod->flags1; + int flags2 = mod->flags2; + fluid_real_t amount = (fluid_real_t)mod->amount; + + printf("Src: "); + + if(flags1 & FLUID_MOD_CC) + { + printf("MIDI CC=%i", src1); + } + else + { + switch(src1) + { + case FLUID_MOD_NONE: + printf("None"); + break; + + case FLUID_MOD_VELOCITY: + printf("note-on velocity"); + break; + + case FLUID_MOD_KEY: + printf("Key nr"); + break; + + case FLUID_MOD_KEYPRESSURE: + printf("Poly pressure"); + break; + + case FLUID_MOD_CHANNELPRESSURE: + printf("Chan pressure"); + break; + + case FLUID_MOD_PITCHWHEEL: + printf("Pitch Wheel"); + break; + + case FLUID_MOD_PITCHWHEELSENS: + printf("Pitch Wheel sens"); + break; + + default: + printf("(unknown: %i)", src1); + }; /* switch src1 */ + }; /* if not CC */ + + if(flags1 & FLUID_MOD_NEGATIVE) + { + printf("- "); + } + else + { + printf("+ "); + }; + + if(flags1 & FLUID_MOD_BIPOLAR) + { + printf("bip "); + } + else + { + printf("unip "); + }; + + printf("-> "); + + switch(dest) + { + case GEN_FILTERQ: + printf("Q"); + break; + + case GEN_FILTERFC: + printf("fc"); + break; + + case GEN_CUSTOM_FILTERQ: + printf("custom-Q"); + break; + + case GEN_CUSTOM_FILTERFC: + printf("custom-fc"); + break; + + case GEN_VIBLFOTOPITCH: + printf("VibLFO-to-pitch"); + break; + + case GEN_MODENVTOPITCH: + printf("ModEnv-to-pitch"); + break; + + case GEN_MODLFOTOPITCH: + printf("ModLFO-to-pitch"); + break; + + case GEN_CHORUSSEND: + printf("Chorus send"); + break; + + case GEN_REVERBSEND: + printf("Reverb send"); + break; + + case GEN_PAN: + printf("pan"); + break; + + case GEN_CUSTOM_BALANCE: + printf("balance"); + break; + + case GEN_ATTENUATION: + printf("att"); + break; + + default: + printf("dest %i", dest); + }; /* switch dest */ + + printf(", amount %f flags %i src2 %i flags2 %i\n", amount, flags1, src2, flags2); +}; +#endif diff --git a/libs/fluidsynth/src/synth/fluid_mod.h b/libs/fluidsynth/src/synth/fluid_mod.h new file mode 100644 index 00000000000..3e7661741f9 --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_mod.h @@ -0,0 +1,54 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_MOD_H +#define _FLUID_MOD_H + +#include "fluidsynth_priv.h" +#include "fluid_conv.h" + +/* + * Modulator structure. See SoundFont 2.04 PDF section 8.2. + */ +struct _fluid_mod_t +{ + unsigned char dest; /**< Destination generator to control */ + unsigned char src1; /**< Source controller 1 */ + unsigned char flags1; /**< Source controller 1 flags */ + unsigned char src2; /**< Source controller 2 */ + unsigned char flags2; /**< Source controller 2 flags */ + double amount; /**< Multiplier amount */ + /* The 'next' field allows to link modulators into a list. It is + * not used in fluid_voice.c, there each voice allocates memory for a + * fixed number of modulators. Since there may be a huge number of + * different zones, this is more efficient. + */ + fluid_mod_t *next; +}; + +fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice); +int fluid_mod_check_sources(const fluid_mod_t *mod, char *name); + +#ifdef DEBUG +void fluid_dump_modulator(fluid_mod_t *mod); +#endif + + +#endif /* _FLUID_MOD_H */ diff --git a/libs/fluidsynth/src/synth/fluid_synth.c b/libs/fluidsynth/src/synth/fluid_synth.c new file mode 100644 index 00000000000..3b83e3d8b73 --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_synth.c @@ -0,0 +1,8445 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_synth.h" +#include "fluid_sys.h" +#include "fluid_chan.h" +#include "fluid_tuning.h" +#include "fluid_settings.h" +#include "fluid_sfont.h" +#include "fluid_defsfont.h" +#include "fluid_instpatch.h" + +#ifdef TRAP_ON_FPE +#define _GNU_SOURCE +#include + +/* seems to not be declared in fenv.h */ +extern int feenableexcept(int excepts); +#endif + +#define FLUID_API_RETURN(return_value) \ + do { fluid_synth_api_exit(synth); \ + return return_value; } while (0) + +#define FLUID_API_RETURN_IF_CHAN_DISABLED(return_value) \ + do { if (FLUID_LIKELY(synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED)) \ + {} \ + else \ + { FLUID_API_RETURN(return_value); } \ + } while (0) + +#define FLUID_API_ENTRY_CHAN(fail_value) \ + fluid_return_val_if_fail (synth != NULL, fail_value); \ + fluid_return_val_if_fail (chan >= 0, fail_value); \ + fluid_synth_api_enter(synth); \ + if (chan >= synth->midi_channels) { \ + FLUID_API_RETURN(fail_value); \ + } \ + +static void fluid_synth_init(void); +static void fluid_synth_api_enter(fluid_synth_t *synth); +static void fluid_synth_api_exit(fluid_synth_t *synth); + +static int fluid_synth_noteon_LOCAL(fluid_synth_t *synth, int chan, int key, + int vel); +static int fluid_synth_noteoff_LOCAL(fluid_synth_t *synth, int chan, int key); +static int fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num); +static int fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, + int len, char *response, + int *response_len, int avail_response, + int *handled, int dryrun); +static int fluid_synth_sysex_gs_dt1(fluid_synth_t *synth, const char *data, + int len, char *response, + int *response_len, int avail_response, + int *handled, int dryrun); +static int fluid_synth_sysex_xg(fluid_synth_t *synth, const char *data, + int len, char *response, + int *response_len, int avail_response, + int *handled, int dryrun); +int fluid_synth_all_notes_off_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_system_reset_LOCAL(fluid_synth_t *synth); +static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t *synth, int chan, + int is_cc, int ctrl); +static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t *synth, int channum); +static int fluid_synth_update_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key); +static int fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_set_preset(fluid_synth_t *synth, int chan, + fluid_preset_t *preset); +static int fluid_synth_reverb_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value); +static int fluid_synth_chorus_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value); + +static fluid_preset_t * +fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum, + int banknum, int prognum); +static fluid_preset_t * +fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname, + int banknum, int prognum); + +static void fluid_synth_update_presets(fluid_synth_t *synth); +static void fluid_synth_update_gain_LOCAL(fluid_synth_t *synth); +static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony); +static void init_dither(void); +static FLUID_INLINE int16_t round_clip_to_i16(float x); +static int fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount); + +static fluid_voice_t *fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth); +static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth, + fluid_voice_t *new_voice); +static int fluid_synth_sfunload_callback(void *data, unsigned int msec); +static fluid_tuning_t *fluid_synth_get_tuning(fluid_synth_t *synth, + int bank, int prog); +static int fluid_synth_replace_tuning_LOCK(fluid_synth_t *synth, + fluid_tuning_t *tuning, + int bank, int prog, int apply); +static void fluid_synth_replace_tuning_LOCAL(fluid_synth_t *synth, + fluid_tuning_t *old_tuning, + fluid_tuning_t *new_tuning, + int apply, int unref_new); +static void fluid_synth_update_voice_tuning_LOCAL(fluid_synth_t *synth, + fluid_channel_t *channel); +static int fluid_synth_set_tuning_LOCAL(fluid_synth_t *synth, int chan, + fluid_tuning_t *tuning, int apply); +static void fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, + int param, float value); +static void fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id); + + +static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels); + + +/* Callback handlers for real-time settings */ +static void fluid_synth_handle_gain(void *data, const char *name, double value); +static void fluid_synth_handle_polyphony(void *data, const char *name, int value); +static void fluid_synth_handle_device_id(void *data, const char *name, int value); +static void fluid_synth_handle_overflow(void *data, const char *name, double value); +static void fluid_synth_handle_important_channels(void *data, const char *name, + const char *value); +static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value); +static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value); + + +static void fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan); +static int fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val); +static void fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val); + +/*************************************************************** + * + * GLOBAL + */ + +/* has the synth module been initialized? */ +/* fluid_atomic_int_t may be anything, so init with {0} to catch most cases */ +static fluid_atomic_int_t fluid_synth_initialized = {0}; + +/* default modulators + * SF2.01 page 52 ff: + * + * There is a set of predefined default modulators. They have to be + * explicitly overridden by the sound font in order to turn them off. + */ + +static fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */ +/*not static */ fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */ +static fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */ +static fluid_mod_t default_mod2viblfo_mod; /* SF2.01 section 8.4.4 */ +static fluid_mod_t default_att_mod; /* SF2.01 section 8.4.5 */ +static fluid_mod_t default_pan_mod; /* SF2.01 section 8.4.6 */ +static fluid_mod_t default_expr_mod; /* SF2.01 section 8.4.7 */ +static fluid_mod_t default_reverb_mod; /* SF2.01 section 8.4.8 */ +static fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */ +static fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */ +static fluid_mod_t custom_balance_mod; /* Non-standard modulator */ + + +/* custom_breath2att_modulator is not a default modulator specified in SF +it is intended to replace default_vel2att_mod on demand using +API fluid_set_breath_mode() or command shell setbreathmode. +*/ +static fluid_mod_t custom_breath2att_mod; + +/* reverb presets */ +static const fluid_revmodel_presets_t revmodel_preset[] = +{ + /* name */ /* roomsize */ /* damp */ /* width */ /* level */ + { "Test 1", 0.2f, 0.0f, 0.5f, 0.9f }, + { "Test 2", 0.4f, 0.2f, 0.5f, 0.8f }, + { "Test 3", 0.6f, 0.4f, 0.5f, 0.7f }, + { "Test 4", 0.8f, 0.7f, 0.5f, 0.6f }, + { "Test 5", 0.8f, 1.0f, 0.5f, 0.5f }, +}; + + +/*************************************************************** + * + * INITIALIZATION & UTILITIES + */ + +void fluid_synth_settings(fluid_settings_t *settings) +{ + fluid_settings_register_int(settings, "synth.verbose", 0, 0, 1, FLUID_HINT_TOGGLED); + + fluid_settings_register_int(settings, "synth.reverb.active", 1, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_num(settings, "synth.reverb.room-size", FLUID_REVERB_DEFAULT_ROOMSIZE, 0.0f, 1.0f, 0); + fluid_settings_register_num(settings, "synth.reverb.damp", FLUID_REVERB_DEFAULT_DAMP, 0.0f, 1.0f, 0); + fluid_settings_register_num(settings, "synth.reverb.width", FLUID_REVERB_DEFAULT_WIDTH, 0.0f, 100.0f, 0); + fluid_settings_register_num(settings, "synth.reverb.level", FLUID_REVERB_DEFAULT_LEVEL, 0.0f, 1.0f, 0); + + fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_int(settings, "synth.chorus.nr", FLUID_CHORUS_DEFAULT_N, 0, 99, 0); + fluid_settings_register_num(settings, "synth.chorus.level", FLUID_CHORUS_DEFAULT_LEVEL, 0.0f, 10.0f, 0); + fluid_settings_register_num(settings, "synth.chorus.speed", FLUID_CHORUS_DEFAULT_SPEED, 0.1f, 5.0f, 0); + fluid_settings_register_num(settings, "synth.chorus.depth", FLUID_CHORUS_DEFAULT_DEPTH, 0.0f, 256.0f, 0); + + fluid_settings_register_int(settings, "synth.ladspa.active", 0, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_int(settings, "synth.lock-memory", 1, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_str(settings, "midi.portname", "", 0); + +#ifdef DEFAULT_SOUNDFONT + fluid_settings_register_str(settings, "synth.default-soundfont", DEFAULT_SOUNDFONT, 0); +#endif + + fluid_settings_register_int(settings, "synth.polyphony", 256, 1, 65535, 0); + fluid_settings_register_int(settings, "synth.midi-channels", 16, 16, 256, 0); + fluid_settings_register_num(settings, "synth.gain", 0.2f, 0.0f, 10.0f, 0); + fluid_settings_register_int(settings, "synth.audio-channels", 1, 1, 128, 0); + fluid_settings_register_int(settings, "synth.audio-groups", 1, 1, 128, 0); + fluid_settings_register_int(settings, "synth.effects-channels", 2, 2, 2, 0); + fluid_settings_register_int(settings, "synth.effects-groups", 1, 1, 128, 0); + fluid_settings_register_num(settings, "synth.sample-rate", 44100.0f, 8000.0f, 96000.0f, 0); + fluid_settings_register_int(settings, "synth.device-id", 0, 0, 127, 0); +#ifdef ENABLE_MIXER_THREADS + fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0); +#else + fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 1, 0); +#endif + + fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0); + + fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED); + + fluid_settings_register_num(settings, "synth.overflow.percussion", 4000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.sustained", -1000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.released", -2000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.age", 1000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.volume", 500, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.important", 5000, -50000, 50000, 0); + fluid_settings_register_str(settings, "synth.overflow.important-channels", "", 0); + + fluid_settings_register_str(settings, "synth.midi-bank-select", "gs", 0); + fluid_settings_add_option(settings, "synth.midi-bank-select", "gm"); + fluid_settings_add_option(settings, "synth.midi-bank-select", "gs"); + fluid_settings_add_option(settings, "synth.midi-bank-select", "xg"); + fluid_settings_add_option(settings, "synth.midi-bank-select", "mma"); + + fluid_settings_register_int(settings, "synth.dynamic-sample-loading", 0, 0, 1, FLUID_HINT_TOGGLED); +} + +/** + * Get FluidSynth runtime version. + * @param major Location to store major number + * @param minor Location to store minor number + * @param micro Location to store micro number + */ +void fluid_version(int *major, int *minor, int *micro) +{ + *major = FLUIDSYNTH_VERSION_MAJOR; + *minor = FLUIDSYNTH_VERSION_MINOR; + *micro = FLUIDSYNTH_VERSION_MICRO; +} + +/** + * Get FluidSynth runtime version as a string. + * @return FluidSynth version string, which is internal and should not be + * modified or freed. + */ +char * +fluid_version_str(void) +{ + return FLUIDSYNTH_VERSION; +} + +/* + * void fluid_synth_init + * + * Does all the initialization for this module. + */ +static void +fluid_synth_init(void) +{ +#ifdef TRAP_ON_FPE + #if !defined(__GLIBC__) && defined(__linux__) + #warning "Trap on FPE is only supported when using glibc!" + #else + /* Turn on floating point exception traps */ + feenableexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_INVALID); + #endif +#endif + + init_dither(); + + /* custom_breath2att_mod is not a default modulator specified in SF2.01. + it is intended to replace default_vel2att_mod on demand using + API fluid_set_breath_mode() or command shell setbreathmode. + */ + fluid_mod_set_source1(&custom_breath2att_mod, /* The modulator we are programming here */ + BREATH_MSB, /* Source. breath MSB corresponds to 2. */ + FLUID_MOD_CC /* MIDI continuous controller */ + | FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */ + | FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */ + | FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */ + ); + fluid_mod_set_source2(&custom_breath2att_mod, 0, 0); /* No 2nd source */ + fluid_mod_set_dest(&custom_breath2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&custom_breath2att_mod, FLUID_PEAK_ATTENUATION); /* Modulation amount: 960 */ + + /* SF2.01 page 53 section 8.4.1: MIDI Note-On Velocity to Initial Attenuation */ + fluid_mod_set_source1(&default_vel2att_mod, /* The modulator we are programming here */ + FLUID_MOD_VELOCITY, /* Source. VELOCITY corresponds to 'index=2'. */ + FLUID_MOD_GC /* Not a MIDI continuous controller */ + | FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */ + | FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */ + | FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */ + ); + fluid_mod_set_source2(&default_vel2att_mod, 0, 0); /* No 2nd source */ + fluid_mod_set_dest(&default_vel2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&default_vel2att_mod, FLUID_PEAK_ATTENUATION); /* Modulation amount: 960 */ + + + + /* SF2.01 page 53 section 8.4.2: MIDI Note-On Velocity to Filter Cutoff + * Have to make a design decision here. The specs don't make any sense this way or another. + * One sound font, 'Kingston Piano', which has been praised for its quality, tries to + * override this modulator with an amount of 0 and positive polarity (instead of what + * the specs say, D=1) for the secondary source. + * So if we change the polarity to 'positive', one of the best free sound fonts works... + */ + fluid_mod_set_source1(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_NEGATIVE /* D=1 */ + ); + fluid_mod_set_source2(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_SWITCH /* type=3 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + // do not remove | FLUID_MOD_NEGATIVE /* D=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_dest(&default_vel2filter_mod, GEN_FILTERFC); /* Target: Initial filter cutoff */ + fluid_mod_set_amount(&default_vel2filter_mod, -2400); + + + + /* SF2.01 page 53 section 8.4.3: MIDI Channel pressure to Vibrato LFO pitch depth */ + fluid_mod_set_source1(&default_at2viblfo_mod, FLUID_MOD_CHANNELPRESSURE, /* Index=13 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_at2viblfo_mod, 0, 0); /* no second source */ + fluid_mod_set_dest(&default_at2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */ + fluid_mod_set_amount(&default_at2viblfo_mod, 50); + + + + /* SF2.01 page 53 section 8.4.4: Mod wheel (Controller 1) to Vibrato LFO pitch depth */ + fluid_mod_set_source1(&default_mod2viblfo_mod, MODULATION_MSB, /* Index=1 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_mod2viblfo_mod, 0, 0); /* no second source */ + fluid_mod_set_dest(&default_mod2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */ + fluid_mod_set_amount(&default_mod2viblfo_mod, 50); + + + + /* SF2.01 page 55 section 8.4.5: MIDI continuous controller 7 to initial attenuation*/ + fluid_mod_set_source1(&default_att_mod, VOLUME_MSB, /* index=7 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_CONCAVE /* type=1 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_NEGATIVE /* D=1 */ + ); + fluid_mod_set_source2(&default_att_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&default_att_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ + + + + /* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */ + fluid_mod_set_source1(&default_pan_mod, PAN_MSB, /* index=10 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_BIPOLAR /* P=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_pan_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_pan_mod, GEN_PAN); /* Target: pan */ + /* Amount: 500. The SF specs $8.4.6, p. 55 says: "Amount = 1000 + tenths of a percent". The center value (64) corresponds to 50%, + so it follows that amount = 50% x 1000/% = 500. */ + fluid_mod_set_amount(&default_pan_mod, 500.0); + + + /* SF2.01 page 55 section 8.4.7: MIDI continuous controller 11 to initial attenuation*/ + fluid_mod_set_source1(&default_expr_mod, EXPRESSION_MSB, /* index=11 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_CONCAVE /* type=1 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_NEGATIVE /* D=1 */ + ); + fluid_mod_set_source2(&default_expr_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_expr_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&default_expr_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ + + + + /* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */ + fluid_mod_set_source1(&default_reverb_mod, EFFECTS_DEPTH1, /* index=91 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_reverb_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_reverb_mod, GEN_REVERBSEND); /* Target: Reverb send */ + fluid_mod_set_amount(&default_reverb_mod, 200); /* Amount: 200 ('tenths of a percent') */ + + + + /* SF2.01 page 55 section 8.4.9: MIDI continuous controller 93 to Chorus send */ + fluid_mod_set_source1(&default_chorus_mod, EFFECTS_DEPTH3, /* index=93 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_chorus_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_chorus_mod, GEN_CHORUSSEND); /* Target: Chorus */ + fluid_mod_set_amount(&default_chorus_mod, 200); /* Amount: 200 ('tenths of a percent') */ + + + + /* SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch ... */ + /* Initial Pitch is not a "standard" generator, because it isn't mentioned in the + list of generators in the SF2 specifications. That's why destination Initial Pitch + is replaced here by fine tune generator. + */ + fluid_mod_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */ + FLUID_MOD_GC /* CC =0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_BIPOLAR /* P=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEELSENS, /* Index = 16 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + /* Also see the comment in gen.h about GEN_PITCH */ + fluid_mod_set_dest(&default_pitch_bend_mod, GEN_FINETUNE); /* Destination: Fine Tune */ + fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */ + + + /* Non-standard MIDI continuous controller 8 to channel stereo balance */ + fluid_mod_set_source1(&custom_balance_mod, BALANCE_MSB, /* Index=8 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_CONCAVE /* type=1 */ + | FLUID_MOD_BIPOLAR /* P=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&custom_balance_mod, 0, 0); + fluid_mod_set_dest(&custom_balance_mod, GEN_CUSTOM_BALANCE); /* Destination: stereo balance */ + /* Amount: 96 dB of attenuation (on the opposite channel) */ + fluid_mod_set_amount(&custom_balance_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ + +#if defined(LIBINSTPATCH_SUPPORT) + /* defer libinstpatch init to fluid_instpatch.c to avoid #include "libinstpatch.h" */ + if(!fluid_instpatch_supports_multi_init()) + { + fluid_instpatch_init(); + } +#endif +} + +static FLUID_INLINE unsigned int fluid_synth_get_ticks(fluid_synth_t *synth) +{ + return fluid_atomic_int_get(&synth->ticks_since_start); +} + +static FLUID_INLINE void fluid_synth_add_ticks(fluid_synth_t *synth, int val) +{ + fluid_atomic_int_add(&synth->ticks_since_start, val); +} + + +/*************************************************************** + * FLUID SAMPLE TIMERS + * Timers that use written audio data as timing reference + */ +struct _fluid_sample_timer_t +{ + fluid_sample_timer_t *next; /* Single linked list of timers */ + unsigned long starttick; + fluid_timer_callback_t callback; + void *data; + int isfinished; +}; + +/* + * fluid_sample_timer_process - called when synth->ticks is updated + */ +static void fluid_sample_timer_process(fluid_synth_t *synth) +{ + fluid_sample_timer_t *st; + long msec; + int cont; + unsigned int ticks = fluid_synth_get_ticks(synth); + + for(st = synth->sample_timers; st; st = st->next) + { + if(st->isfinished) + { + continue; + } + + msec = (long)(1000.0 * ((double)(ticks - st->starttick)) / synth->sample_rate); + cont = (*st->callback)(st->data, msec); + + if(cont == 0) + { + st->isfinished = 1; + } + } +} + +fluid_sample_timer_t *new_fluid_sample_timer(fluid_synth_t *synth, fluid_timer_callback_t callback, void *data) +{ + fluid_sample_timer_t *result = FLUID_NEW(fluid_sample_timer_t); + + if(result == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + fluid_sample_timer_reset(synth, result); + result->data = data; + result->callback = callback; + result->next = synth->sample_timers; + synth->sample_timers = result; + return result; +} + +void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer) +{ + fluid_sample_timer_t **ptr; + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(timer != NULL); + + ptr = &synth->sample_timers; + + while(*ptr) + { + if(*ptr == timer) + { + *ptr = timer->next; + FLUID_FREE(timer); + return; + } + + ptr = &((*ptr)->next); + } +} + +void fluid_sample_timer_reset(fluid_synth_t *synth, fluid_sample_timer_t *timer) +{ + timer->starttick = fluid_synth_get_ticks(synth); + timer->isfinished = 0; +} + +/*************************************************************** + * + * FLUID SYNTH + */ + +static FLUID_INLINE void +fluid_synth_update_mixer(fluid_synth_t *synth, fluid_rvoice_function_t method, int intparam, + fluid_real_t realparam) +{ + fluid_return_if_fail(synth != NULL && synth->eventhandler != NULL); + fluid_return_if_fail(synth->eventhandler->mixer != NULL); + fluid_rvoice_eventhandler_push_int_real(synth->eventhandler, method, + synth->eventhandler->mixer, + intparam, realparam); +} + +static FLUID_INLINE unsigned int fluid_synth_get_min_note_length_LOCAL(fluid_synth_t *synth) +{ + int i; + fluid_settings_getint(synth->settings, "synth.min-note-length", &i); + return (unsigned int)(i * synth->sample_rate / 1000.0f); +} + +/** + * Create new FluidSynth instance. + * @param settings Configuration parameters to use (used directly). + * @return New FluidSynth instance or NULL on error + * + * @note The @p settings parameter is used directly, but the synth does not take ownership of it. + * Hence, the caller is responsible for freeing it, when no longer needed. + * Further note that you may modify FluidSettings of the + * @p settings instance. However, only those FluidSettings marked as 'realtime' will + * affect the synth immediately. See the \ref fluidsettings for more details. + * + * @warning The @p settings object should only be used by a single synth at a time. I.e. creating + * multiple synth instances with a single @p settings object causes undefined behavior. Once the + * "single synth" has been deleted, you may use the @p settings object again for another synth. + */ +fluid_synth_t * +new_fluid_synth(fluid_settings_t *settings) +{ + fluid_synth_t *synth; + fluid_sfloader_t *loader; + char *important_channels; + int i, prio_level = 0; + int with_ladspa = 0; + double sample_rate_min, sample_rate_max; + + /* initialize all the conversion tables and other stuff */ + if(fluid_atomic_int_compare_and_exchange(&fluid_synth_initialized, 0, 1)) + { + fluid_synth_init(); + } + + /* allocate a new synthesizer object */ + synth = FLUID_NEW(fluid_synth_t); + + if(synth == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t)); + +#if defined(LIBINSTPATCH_SUPPORT) + if(fluid_instpatch_supports_multi_init()) + { + fluid_instpatch_init(); + } +#endif + + fluid_rec_mutex_init(synth->mutex); + fluid_settings_getint(settings, "synth.threadsafe-api", &synth->use_mutex); + synth->public_api_count = 0; + + synth->settings = settings; + + fluid_settings_getint(settings, "synth.reverb.active", &synth->with_reverb); + fluid_settings_getint(settings, "synth.chorus.active", &synth->with_chorus); + fluid_settings_getint(settings, "synth.verbose", &synth->verbose); + + fluid_settings_getint(settings, "synth.polyphony", &synth->polyphony); + fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate); + fluid_settings_getnum_range(settings, "synth.sample-rate", &sample_rate_min, &sample_rate_max); + fluid_settings_getint(settings, "synth.midi-channels", &synth->midi_channels); + fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels); + fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups); + fluid_settings_getint(settings, "synth.effects-channels", &synth->effects_channels); + fluid_settings_getint(settings, "synth.effects-groups", &synth->effects_groups); + fluid_settings_getnum_float(settings, "synth.gain", &synth->gain); + fluid_settings_getint(settings, "synth.device-id", &synth->device_id); + fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores); + + fluid_settings_getnum_float(settings, "synth.overflow.percussion", &synth->overflow.percussion); + fluid_settings_getnum_float(settings, "synth.overflow.released", &synth->overflow.released); + fluid_settings_getnum_float(settings, "synth.overflow.sustained", &synth->overflow.sustained); + fluid_settings_getnum_float(settings, "synth.overflow.volume", &synth->overflow.volume); + fluid_settings_getnum_float(settings, "synth.overflow.age", &synth->overflow.age); + fluid_settings_getnum_float(settings, "synth.overflow.important", &synth->overflow.important); + + /* register the callbacks */ + fluid_settings_callback_num(settings, "synth.gain", + fluid_synth_handle_gain, synth); + fluid_settings_callback_int(settings, "synth.polyphony", + fluid_synth_handle_polyphony, synth); + fluid_settings_callback_int(settings, "synth.device-id", + fluid_synth_handle_device_id, synth); + fluid_settings_callback_num(settings, "synth.overflow.percussion", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.sustained", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.released", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.age", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.volume", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.important", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_str(settings, "synth.overflow.important-channels", + fluid_synth_handle_important_channels, synth); + fluid_settings_callback_num(settings, "synth.reverb.room-size", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.reverb.damp", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.reverb.width", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.reverb.level", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_int(settings, "synth.reverb.active", + fluid_synth_handle_reverb_chorus_int, synth); + fluid_settings_callback_int(settings, "synth.chorus.active", + fluid_synth_handle_reverb_chorus_int, synth); + fluid_settings_callback_int(settings, "synth.chorus.nr", + fluid_synth_handle_reverb_chorus_int, synth); + fluid_settings_callback_num(settings, "synth.chorus.level", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.chorus.depth", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.chorus.speed", + fluid_synth_handle_reverb_chorus_num, synth); + + /* do some basic sanity checking on the settings */ + + if(synth->midi_channels % 16 != 0) + { + int n = synth->midi_channels / 16; + synth->midi_channels = (n + 1) * 16; + fluid_settings_setint(settings, "synth.midi-channels", synth->midi_channels); + FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is not a multiple of 16. " + "I'll increase the number of channels to the next multiple."); + } + + if(synth->audio_channels < 1) + { + FLUID_LOG(FLUID_WARN, "Requested number of audio channels is smaller than 1. " + "Changing this setting to 1."); + synth->audio_channels = 1; + } + else if(synth->audio_channels > 128) + { + FLUID_LOG(FLUID_WARN, "Requested number of audio channels is too big (%d). " + "Limiting this setting to 128.", synth->audio_channels); + synth->audio_channels = 128; + } + + if(synth->audio_groups < 1) + { + FLUID_LOG(FLUID_WARN, "Requested number of audio groups is smaller than 1. " + "Changing this setting to 1."); + synth->audio_groups = 1; + } + else if(synth->audio_groups > 128) + { + FLUID_LOG(FLUID_WARN, "Requested number of audio groups is too big (%d). " + "Limiting this setting to 128.", synth->audio_groups); + synth->audio_groups = 128; + } + + if(synth->effects_channels < 2) + { + FLUID_LOG(FLUID_WARN, "Invalid number of effects channels (%d)." + "Setting effects channels to 2.", synth->effects_channels); + synth->effects_channels = 2; + } + + /* + number of buffers rendered by the mixer is determined by synth->audio_groups. + audio from MIDI channel is rendered, mapped and mixed in these buffers. + + Typically synth->audio_channels is only used by audio driver and should be set + to the same value that synth->audio_groups. In some situation using LADSPA, + it is best to diminish audio-channels so that the driver will be able to pass + the audio to audio devices in the case these devices have a limited number of + audio channels. + + audio-channels must not be greater then audio-groups, otherwise these + audio output above audio-groups will not be rendered by the mixeur. + */ + if(synth->audio_channels > synth->audio_groups) + { + synth->audio_channels = synth->audio_groups; + fluid_settings_setint(settings, "synth.audio-channels", synth->audio_channels); + FLUID_LOG(FLUID_WARN, "Requested audio-channels to high. " + "Limiting this setting to audio-groups."); + } + + if(fluid_settings_dupstr(settings, "synth.overflow.important-channels", + &important_channels) == FLUID_OK) + { + if(fluid_synth_set_important_channels(synth, important_channels) != FLUID_OK) + { + FLUID_LOG(FLUID_WARN, "Failed to set overflow important channels"); + } + + FLUID_FREE(important_channels); + } + + /* as soon as the synth is created it starts playing. */ + synth->state = FLUID_SYNTH_PLAYING; + + synth->fromkey_portamento = INVALID_NOTE; /* disable portamento */ + + fluid_atomic_int_set(&synth->ticks_since_start, 0); + synth->tuning = NULL; + fluid_private_init(synth->tuning_iter); + + /* Initialize multi-core variables if multiple cores enabled */ + if(synth->cores > 1) + { + fluid_settings_getint(synth->settings, "audio.realtime-prio", &prio_level); + } + + /* Allocate event queue for rvoice mixer */ + /* In an overflow situation, a new voice takes about 50 spaces in the queue! */ + synth->eventhandler = new_fluid_rvoice_eventhandler(synth->polyphony * 64, + synth->polyphony, synth->audio_groups, + synth->effects_channels, synth->effects_groups, + (fluid_real_t)sample_rate_max, synth->sample_rate, + synth->cores - 1, prio_level); + + if(synth->eventhandler == NULL) + { + goto error_recovery; + } + + /* Setup the list of default modulators. + * Needs to happen after eventhandler has been set up, as fluid_synth_enter_api is called in the process */ + synth->default_mod = NULL; + fluid_synth_add_default_mod(synth, &default_vel2att_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_vel2filter_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_at2viblfo_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_mod2viblfo_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_att_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_pan_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_expr_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_reverb_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_chorus_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_pitch_bend_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &custom_balance_mod, FLUID_SYNTH_ADD); + + /* Create and initialize the Fx unit.*/ + fluid_settings_getint(settings, "synth.ladspa.active", &with_ladspa); + + if(with_ladspa) + { +#ifdef LADSPA + synth->ladspa_fx = new_fluid_ladspa_fx(synth->sample_rate, + FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE); + + if(synth->ladspa_fx == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + fluid_rvoice_mixer_set_ladspa(synth->eventhandler->mixer, synth->ladspa_fx, + synth->audio_groups); +#else /* LADSPA */ + FLUID_LOG(FLUID_WARN, "FluidSynth has not been compiled with LADSPA support"); +#endif /* LADSPA */ + } + + /* allocate and add the dls sfont loader */ +#ifdef LIBINSTPATCH_SUPPORT + loader = new_fluid_instpatch_loader(settings); + + if(loader == NULL) + { + FLUID_LOG(FLUID_WARN, "Failed to create the instpatch SoundFont loader"); + } + else + { + fluid_synth_add_sfloader(synth, loader); + } +#endif + + /* allocate and add the default sfont loader */ + loader = new_fluid_defsfloader(settings); + + if(loader == NULL) + { + FLUID_LOG(FLUID_WARN, "Failed to create the default SoundFont loader"); + } + else + { + fluid_synth_add_sfloader(synth, loader); + } + + /* allocate all channel objects */ + synth->channel = FLUID_ARRAY(fluid_channel_t *, synth->midi_channels); + + if(synth->channel == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + FLUID_MEMSET(synth->channel, 0, synth->midi_channels * sizeof(*synth->channel)); + for(i = 0; i < synth->midi_channels; i++) + { + synth->channel[i] = new_fluid_channel(synth, i); + + if(synth->channel[i] == NULL) + { + goto error_recovery; + } + } + + /* allocate all synthesis processes */ + synth->nvoice = synth->polyphony; + synth->voice = FLUID_ARRAY(fluid_voice_t *, synth->nvoice); + + if(synth->voice == NULL) + { + goto error_recovery; + } + + FLUID_MEMSET(synth->voice, 0, synth->nvoice * sizeof(*synth->voice)); + for(i = 0; i < synth->nvoice; i++) + { + synth->voice[i] = new_fluid_voice(synth->eventhandler, synth->sample_rate); + + if(synth->voice[i] == NULL) + { + goto error_recovery; + } + } + + /* sets a default basic channel */ + /* Sets one basic channel: basic channel 0, mode 0 (Omni On - Poly) */ + /* (i.e all channels are polyphonic) */ + /* Must be called after channel objects allocation */ + fluid_synth_set_basic_channel_LOCAL(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, + synth->midi_channels); + + synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth); + + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, + synth->polyphony, 0.0f); + fluid_synth_reverb_on(synth, -1, synth->with_reverb); + fluid_synth_chorus_on(synth, -1, synth->with_chorus); + + synth->cur = FLUID_BUFSIZE; + synth->curmax = 0; + synth->dither_index = 0; + + { + double values[FLUID_REVERB_PARAM_LAST]; + + fluid_settings_getnum(settings, "synth.reverb.room-size", &values[FLUID_REVERB_ROOMSIZE]); + fluid_settings_getnum(settings, "synth.reverb.damp", &values[FLUID_REVERB_DAMP]); + fluid_settings_getnum(settings, "synth.reverb.width", &values[FLUID_REVERB_WIDTH]); + fluid_settings_getnum(settings, "synth.reverb.level", &values[FLUID_REVERB_LEVEL]); + + fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); + } + + { + double values[FLUID_CHORUS_PARAM_LAST]; + + fluid_settings_getint(settings, "synth.chorus.nr", &i); + values[FLUID_CHORUS_NR] = (double)i; + fluid_settings_getnum(settings, "synth.chorus.level", &values[FLUID_CHORUS_LEVEL]); + fluid_settings_getnum(settings, "synth.chorus.speed", &values[FLUID_CHORUS_SPEED]); + fluid_settings_getnum(settings, "synth.chorus.depth", &values[FLUID_CHORUS_DEPTH]); + values[FLUID_CHORUS_TYPE] = (double)FLUID_CHORUS_DEFAULT_TYPE; + + fluid_synth_set_chorus_full(synth, -1, FLUID_CHORUS_SET_ALL, values); + } + + + synth->bank_select = FLUID_BANK_STYLE_GS; + + if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gm")) + { + synth->bank_select = FLUID_BANK_STYLE_GM; + } + else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gs")) + { + synth->bank_select = FLUID_BANK_STYLE_GS; + } + else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "xg")) + { + synth->bank_select = FLUID_BANK_STYLE_XG; + } + else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "mma")) + { + synth->bank_select = FLUID_BANK_STYLE_MMA; + } + + fluid_synth_process_event_queue(synth); + + /* FIXME */ + synth->start = fluid_curtime(); + + return synth; + +error_recovery: + delete_fluid_synth(synth); + return NULL; +} + + +/** + * Delete a FluidSynth instance. + * @param synth FluidSynth instance to delete + * + * @note Other users of a synthesizer instance, such as audio and MIDI drivers, + * should be deleted prior to freeing the FluidSynth instance. + */ +void +delete_fluid_synth(fluid_synth_t *synth) +{ + int i, k; + fluid_list_t *list; + fluid_sfont_t *sfont; + fluid_sfloader_t *loader; + + fluid_return_if_fail(synth != NULL); + + fluid_profiling_print(); + + /* unregister all real-time settings callback, to avoid a use-after-free when changing those settings after + * this synth has been deleted*/ + + fluid_settings_callback_num(synth->settings, "synth.gain", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.polyphony", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.device-id", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.percussion", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.sustained", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.released", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.age", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.volume", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.important", + NULL, NULL); + fluid_settings_callback_str(synth->settings, "synth.overflow.important-channels", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.room-size", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.damp", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.width", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.level", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.reverb.active", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.chorus.active", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.chorus.nr", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.level", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.depth", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.speed", + NULL, NULL); + + /* turn off all voices, needed to unload SoundFont data */ + if(synth->voice != NULL) + { + for(i = 0; i < synth->nvoice; i++) + { + fluid_voice_t *voice = synth->voice[i]; + + if(!voice) + { + continue; + } + + /* WARNING: A this point we must ensure that the reference counter + of any soundfont sample owned by any rvoice belonging to the voice + are correctly decremented. This is the contrary part to + to fluid_voice_init() where the sample's reference counter is + incremented. + */ + fluid_voice_unlock_rvoice(voice); + fluid_voice_overflow_rvoice_finished(voice); + + if(fluid_voice_is_playing(voice)) + { + fluid_voice_off(voice); + /* If we only use fluid_voice_off(voice) it will trigger a delayed + * fluid_voice_stop(voice) via fluid_synth_check_finished_voices(). + * But here, we are deleting the fluid_synth_t instance so + * fluid_voice_stop() will be never triggered resulting in + * SoundFont data never unloaded (i.e a serious memory leak). + * So, fluid_voice_stop() must be explicitly called to insure + * unloading SoundFont data + */ + fluid_voice_stop(voice); + } + } + } + + /* also unset all presets for clean SoundFont unload */ + if(synth->channel != NULL) + { + for(i = 0; i < synth->midi_channels; i++) + { + if(synth->channel[i] != NULL) + { + fluid_channel_set_preset(synth->channel[i], NULL); + } + } + } + + delete_fluid_rvoice_eventhandler(synth->eventhandler); + + /* delete all the SoundFonts */ + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + fluid_sfont_delete_internal(sfont); + } + + delete_fluid_list(synth->sfont); + + /* delete all the SoundFont loaders */ + + for(list = synth->loaders; list; list = fluid_list_next(list)) + { + loader = (fluid_sfloader_t *) fluid_list_get(list); + fluid_sfloader_delete(loader); + } + + delete_fluid_list(synth->loaders); + + /* wait for and delete all the lazy sfont unloading timers */ + + for(list = synth->fonts_to_be_unloaded; list; list = fluid_list_next(list)) + { + fluid_timer_t* timer = fluid_list_get(list); + // explicitly join to wait for the unload really to happen + fluid_timer_join(timer); + // delete_fluid_timer alone would stop the timer, even if it had not unloaded the soundfont yet + delete_fluid_timer(timer); + } + + delete_fluid_list(synth->fonts_to_be_unloaded); + + if(synth->channel != NULL) + { + for(i = 0; i < synth->midi_channels; i++) + { + delete_fluid_channel(synth->channel[i]); + } + + FLUID_FREE(synth->channel); + } + + if(synth->voice != NULL) + { + for(i = 0; i < synth->nvoice; i++) + { + delete_fluid_voice(synth->voice[i]); + } + + FLUID_FREE(synth->voice); + } + + + /* free the tunings, if any */ + if(synth->tuning != NULL) + { + for(i = 0; i < 128; i++) + { + if(synth->tuning[i] != NULL) + { + for(k = 0; k < 128; k++) + { + delete_fluid_tuning(synth->tuning[i][k]); + } + + FLUID_FREE(synth->tuning[i]); + } + } + + FLUID_FREE(synth->tuning); + } + + fluid_private_free(synth->tuning_iter); + +#ifdef LADSPA + /* Release the LADSPA effects unit */ + delete_fluid_ladspa_fx(synth->ladspa_fx); +#endif + + /* delete all default modulators */ + delete_fluid_list_mod(synth->default_mod); + + FLUID_FREE(synth->overflow.important_channels); + + fluid_rec_mutex_destroy(synth->mutex); + + FLUID_FREE(synth); + +#if defined(LIBINSTPATCH_SUPPORT) + if(fluid_instpatch_supports_multi_init()) + { + fluid_instpatch_deinit(); + } +#endif +} + +/** + * Get a textual representation of the last error + * @param synth FluidSynth instance + * @return Pointer to string of last error message. Valid until the same + * calling thread calls another FluidSynth function which fails. String is + * internal and should not be modified or freed. + * @deprecated This function is not thread-safe and does not work with multiple synths. + * It has been deprecated. It may return "" in a future release and will eventually be removed. + */ +const char * +fluid_synth_error(fluid_synth_t *synth) +{ + return ""; +} + +/** + * Send a note-on event to a FluidSynth object. + * + * This function will take care of proper legato playing. If a note on channel @p chan is + * already playing at the given key @p key, it will be released (even if it is sustained). + * In other words, overlapping notes are not allowed. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number (0-127) + * @param vel MIDI velocity (0-127, 0=noteoff) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_noteon(fluid_synth_t *synth, int chan, int key, int vel) +{ + int result; + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail(vel >= 0 && vel <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + result = fluid_synth_noteon_LOCAL(synth, chan, key, vel); + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of fluid_synth_noteon */ +static int +fluid_synth_noteon_LOCAL(fluid_synth_t *synth, int chan, int key, int vel) +{ + fluid_channel_t *channel ; + + /* notes with velocity zero go to noteoff */ + if(vel == 0) + { + return fluid_synth_noteoff_LOCAL(synth, chan, key); + } + + channel = synth->channel[chan]; + + /* makes sure this channel has a preset */ + if(channel->preset == NULL) + { + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d\t%s", + chan, key, vel, 0, + fluid_synth_get_ticks(synth) / 44100.0f, + (fluid_curtime() - synth->start) / 1000.0f, + 0.0f, 0, "channel has no preset"); + } + + return FLUID_FAILED; + } + + if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */ + { + /* play the noteOn in monophonic */ + return fluid_synth_noteon_mono_LOCAL(synth, chan, key, vel); + } + else + { + /* channel is poly and legato CC is Off) */ + + /* plays the noteOn in polyphonic */ + /* Sets the note at first position in monophonic list */ + /* In the case where the musician intends to inter the channel in monophonic + (by depressing the CC legato on), the next noteOn mono could be played legato + with the previous note poly (if the musician choose this). + */ + fluid_channel_set_onenote_monolist(channel, (unsigned char) key, + (unsigned char) vel); + + /* If there is another voice process on the same channel and key, + advance it to the release phase. */ + fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key); + + /* a noteon poly is passed to fluid_synth_noteon_monopoly_legato(). + This allows an opportunity to get this note played legato with a previous + note if a CC PTC have been received before this noteon. This behavior is + a MIDI specification (see FluidPolymono-0004.pdf chapter 4.3-a ,3.4.11 + for details). + */ + return fluid_synth_noteon_monopoly_legato(synth, chan, INVALID_NOTE, key, vel); + } +} + +/** + * Sends a note-off event to a FluidSynth object. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise (may just mean that no + * voices matched the note off event) + */ +int +fluid_synth_noteoff(fluid_synth_t *synth, int chan, int key) +{ + int result; + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + result = fluid_synth_noteoff_LOCAL(synth, chan, key); + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of fluid_synth_noteoff */ +static int +fluid_synth_noteoff_LOCAL(fluid_synth_t *synth, int chan, int key) +{ + int status; + fluid_channel_t *channel = synth->channel[chan]; + + if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */ + { + /* play the noteOff in monophonic */ + status = fluid_synth_noteoff_mono_LOCAL(synth, chan, key); + } + else + { + /* channel is poly and legato CC is Off) */ + /* removes the note from the monophonic list */ + if(channel->n_notes && key == fluid_channel_last_note(channel)) + { + fluid_channel_clear_monolist(channel); + } + + status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); + } + + /* Changes the state (Valid/Invalid) of the most recent note played in a + staccato manner */ + fluid_channel_invalid_prev_note_staccato(channel); + return status; +} + +/* Damps voices on a channel (turn notes off), if they're sustained by + sustain pedal */ +static int +fluid_synth_damp_voices_by_sustain_LOCAL(fluid_synth_t *synth, int chan) +{ + fluid_channel_t *channel = synth->channel[chan]; + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sustained(voice)) + { + if(voice->key == channel->key_mono_sustained) + { + /* key_mono_sustained is a possible mono note sustainted + (by sustain or sostenuto pedal). It must be marked released + (INVALID_NOTE) here because it is released only by sustain pedal */ + channel->key_mono_sustained = INVALID_NOTE; + } + + fluid_voice_release(voice); + } + } + + return FLUID_OK; +} + +/* Damps voices on a channel (turn notes off), if they're sustained by + sostenuto pedal */ +static int +fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t *synth, int chan) +{ + fluid_channel_t *channel = synth->channel[chan]; + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sostenuto(voice)) + { + if(voice->key == channel->key_mono_sustained) + { + /* key_mono_sustained is a possible mono note sustainted + (by sustain or sostenuto pedal). It must be marked released + (INVALID_NOTE) here because it is released only by sostenuto pedal */ + channel->key_mono_sustained = INVALID_NOTE; + } + + fluid_voice_release(voice); + } + } + + return FLUID_OK; +} + +/** + * Adds the specified modulator \c mod as default modulator to the synth. \c mod will + * take effect for any subsequently created voice. + * @param synth FluidSynth instance + * @param mod Modulator info (values copied, passed in object can be freed immediately afterwards) + * @param mode Determines how to handle an existing identical modulator (#fluid_synth_add_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note Not realtime safe (due to internal memory allocation) and therefore should not be called + * from synthesis context at the risk of stalling audio output. + */ +int +fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode) +{ + fluid_mod_t *default_mod; + fluid_mod_t *last_mod = NULL; + fluid_mod_t *new_mod; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(mod != NULL, FLUID_FAILED); + fluid_return_val_if_fail((mode == FLUID_SYNTH_ADD) || (mode == FLUID_SYNTH_OVERWRITE) , FLUID_FAILED); + + /* Checks if modulators sources are valid */ + if(!fluid_mod_check_sources(mod, "api fluid_synth_add_default_mod mod")) + { + return FLUID_FAILED; + } + + fluid_synth_api_enter(synth); + + default_mod = synth->default_mod; + + while(default_mod != NULL) + { + if(fluid_mod_test_identity(default_mod, mod)) + { + if(mode == FLUID_SYNTH_ADD) + { + default_mod->amount += mod->amount; + } + else // mode == FLUID_SYNTH_OVERWRITE + { + default_mod->amount = mod->amount; + } + + FLUID_API_RETURN(FLUID_OK); + } + + last_mod = default_mod; + default_mod = default_mod->next; + } + + /* Add a new modulator (no existing modulator to add / overwrite). */ + new_mod = new_fluid_mod(); + + if(new_mod == NULL) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + fluid_mod_clone(new_mod, mod); + new_mod->next = NULL; + + if(last_mod == NULL) + { + synth->default_mod = new_mod; + } + else + { + last_mod->next = new_mod; + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Removes the specified modulator \c mod from the synth's default modulator list. + * fluid_mod_test_identity() will be used to test modulator matching. + * @param synth synth instance + * @param mod The modulator to remove + * @return #FLUID_OK if a matching modulator was found and successfully removed, #FLUID_FAILED otherwise + * + * @note Not realtime safe (due to internal memory freeing) and therefore should not be called + * from synthesis context at the risk of stalling audio output. + */ +int +fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod) +{ + fluid_mod_t *default_mod; + fluid_mod_t *last_mod; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(mod != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + last_mod = default_mod = synth->default_mod; + + while(default_mod != NULL) + { + if(fluid_mod_test_identity(default_mod, mod)) + { + if(synth->default_mod == default_mod) + { + synth->default_mod = default_mod->next; + } + else + { + last_mod->next = default_mod->next; + } + + delete_fluid_mod(default_mod); + FLUID_API_RETURN(FLUID_OK); + } + + last_mod = default_mod; + default_mod = default_mod->next; + } + + FLUID_API_RETURN(FLUID_FAILED); +} + + +/** + * Send a MIDI controller event on a MIDI channel. + * + * Most CCs are 7-bits wide in FluidSynth. There are a few exceptions which may be 14-bits wide as are documented here: + * https://github.com/FluidSynth/fluidsynth/wiki/FluidFeatures#midi-control-change-implementation-chart + * + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param num MIDI controller number (0-127) + * @param val MIDI controller value (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @note This function supports MIDI Global Controllers which will be sent to + * all channels of the basic channel if this basic channel is in mode OmniOff/Mono. + * This is accomplished by sending the CC one MIDI channel below the basic + * channel of the receiver. + * Examples: let a synthesizer with 16 MIDI channels: + * - Let a basic channel 7 in mode 3 (Omni Off, Mono). If MIDI channel 6 is disabled it + * could be used as CC global for all channels belonging to basic channel 7. + * - Let a basic channel 0 in mode 3. If MIDI channel 15 is disabled it could be used + * as CC global for all channels belonging to basic channel 0. + * @warning Contrary to the MIDI Standard, this function does not clear LSB controllers, + * when MSB controllers are received. + */ +int +fluid_synth_cc(fluid_synth_t *synth, int chan, int num, int val) +{ + int result = FLUID_FAILED; + fluid_channel_t *channel; + fluid_return_val_if_fail(num >= 0 && num <= 127, FLUID_FAILED); + fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + channel = synth->channel[chan]; + + if(channel->mode & FLUID_CHANNEL_ENABLED) + { + /* chan is enabled */ + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); + } + + fluid_channel_set_cc(channel, num, val); + result = fluid_synth_cc_LOCAL(synth, chan, num); + } + else /* chan is disabled so it is a candidate for global channel */ + { + /* looks for next basic channel */ + int n_chan = synth->midi_channels; /* MIDI Channels number */ + int basicchan ; + + if(chan < n_chan - 1) + { + basicchan = chan + 1; /* next channel */ + } + else + { + basicchan = 0; /* wrap to 0 */ + } + + channel = synth->channel[basicchan]; + + /* Channel must be a basicchan in mode OMNIOFF_MONO */ + if((channel->mode & FLUID_CHANNEL_BASIC) && + ((channel->mode & FLUID_CHANNEL_MODE_MASK) == FLUID_CHANNEL_MODE_OMNIOFF_MONO)) + { + /* sends cc to all channels in this basic channel */ + int i, nbr = channel->mode_val; + + for(i = basicchan; i < basicchan + nbr; i++) + { + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", i, num, val); + } + + fluid_channel_set_cc(synth->channel[i], num, val); + result = fluid_synth_cc_LOCAL(synth, i, num); + } + } + /* The channel chan is not a valid 'global channel' */ + else + { + result = FLUID_FAILED; + } + } + + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of MIDI CC set function. + Most of CC are allowed to modulate but not all. A comment describes if CC num + isn't allowed to modulate. + Following explanations should help to understand both MIDI specifications and + Soundfont specifications in regard to MIDI specs. + + MIDI specs: + CC LSB (32 to 63) are LSB contributions to CC MSB (0 to 31). + It's up to the synthesizer to decide to take LSB values into account or not. + Actually Fluidsynth doesn't use CC LSB value inside fluid_voice_update_param() + (once fluid_voice_modulate() has been triggered). This is because actually + fluidsynth needs only 7 bits resolution (and not 14 bits) from these CCs. + So fluidsynth is using only 7 bit MSB (except for portamento time). + In regard to MIDI specs Fluidsynth behaves correctly. + + Soundfont specs 2.01 - 8.2.1: + To deal correctly with MIDI CC (regardless if any synth will use CC MSB alone (7 bit) + or both CCs MSB,LSB (14 bits) during synthesis), SF specs recommend not making use of + CC LSB (i.e only CC MSB) in modulator sources to trigger modulation (i.e modulators + with CC LSB connected to sources inputs should be ignored). + These specifics are particularly suited for synths that use 14 bits CCs. In this case, + the MIDI transmitter sends CC LSB first followed by CC MSB. The MIDI synth receives + both CC LSB and CC MSB but only CC MSB will trigger the modulation. + This will produce correct synthesis parameters update from a correct 14 bits CC. + If in SF specs, modulator sources with CC LSB had been accepted, both CC LSB and + CC MSB will triggers 2 modulations. This leads to incorrect synthesis parameters + update followed by correct synthesis parameters update. + + However, as long as fluidsynth will use only CC 7 bits resolution, it is safe to ignore + these SF recommendations on CC receive. +*/ +static int +fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) +{ + fluid_channel_t *chan = synth->channel[channum]; + int nrpn_select; + int value; + + value = fluid_channel_get_cc(chan, num); + + switch(num) + { + case LOCAL_CONTROL: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + break; + + /* CC omnioff, omnion, mono, poly */ + /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + case POLY_OFF: + case POLY_ON: + case OMNI_OFF: + case OMNI_ON: + + /* allowed only if channum is a basic channel */ + if(chan->mode & FLUID_CHANNEL_BASIC) + { + /* Construction of new_mode from current channel mode and this CC mode */ + int new_mode = chan->mode & FLUID_CHANNEL_MODE_MASK; + + switch(num) + { + case POLY_OFF: + new_mode |= FLUID_CHANNEL_POLY_OFF; + break; + + case POLY_ON: + new_mode &= ~FLUID_CHANNEL_POLY_OFF; + break; + + case OMNI_OFF: + new_mode |= FLUID_CHANNEL_OMNI_OFF; + break; + + case OMNI_ON: + new_mode &= ~FLUID_CHANNEL_OMNI_OFF; + break; + + default: /* should never happen */ + return FLUID_FAILED; + } + + /* MIDI specs: if value is 0 it means all channels from channum to next + basic channel minus 1 (if any) or to MIDI channel count minus 1. + However, if value is > 0 (e.g. 4), the group of channels will be be + limited to 4. + value is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY as this mode + implies a group of only one channel. + */ + /* Checks value range and changes this existing basic channel group */ + value = fluid_synth_check_next_basic_channel(synth, channum, new_mode, value); + + if(value != FLUID_FAILED) + { + /* reset the current basic channel before changing it */ + fluid_synth_reset_basic_channel_LOCAL(synth, channum, chan->mode_val); + fluid_synth_set_basic_channel_LOCAL(synth, channum, new_mode, value); + break; /* FLUID_OK */ + } + } + + return FLUID_FAILED; + + case LEGATO_SWITCH: /* not allowed to modulate */ + /* handles Poly/mono commutation on Legato pedal On/Off.*/ + fluid_channel_cc_legato(chan, value); + break; + + case PORTAMENTO_SWITCH: /* not allowed to modulate */ + /* Special handling of the monophonic list */ + /* Invalids the most recent note played in a staccato manner */ + fluid_channel_invalid_prev_note_staccato(chan); + break; + + case SUSTAIN_SWITCH: /* not allowed to modulate */ + + /* Release voices if Sustain switch is released */ + if(value < 64) /* Sustain is released */ + { + fluid_synth_damp_voices_by_sustain_LOCAL(synth, channum); + } + + break; + + case SOSTENUTO_SWITCH: /* not allowed to modulate */ + + /* Release voices if Sostetuno switch is released */ + if(value < 64) /* Sostenuto is released */ + { + fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum); + } + else /* Sostenuto is depressed */ + /* Update sostenuto order id when pedaling on Sostenuto */ + { + chan->sostenuto_orderid = synth->noteid; /* future voice id value */ + } + + break; + + case BANK_SELECT_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_channel_set_bank_msb(chan, value & 0x7F); + break; + + case BANK_SELECT_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_channel_set_bank_lsb(chan, value & 0x7F); + break; + + case ALL_NOTES_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_synth_all_notes_off_LOCAL(synth, channum); + break; + + case ALL_SOUND_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_synth_all_sounds_off_LOCAL(synth, channum); + break; + + case ALL_CTRL_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_channel_init_ctrl(chan, 1); + // the hold pedals have been reset, we maybe need to release voices + fluid_synth_damp_voices_by_sustain_LOCAL(synth, channum); + fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum); + fluid_synth_modulate_voices_all_LOCAL(synth, channum); + break; + + case DATA_ENTRY_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + break; + + case DATA_ENTRY_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + { + int data = (value << 7) + fluid_channel_get_cc(chan, DATA_ENTRY_LSB); + + if(chan->nrpn_active) /* NRPN is active? */ + { + /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ + if((fluid_channel_get_cc(chan, NRPN_MSB) == 120) + && (fluid_channel_get_cc(chan, NRPN_LSB) < 100)) + { + nrpn_select = chan->nrpn_select; + + if(nrpn_select < GEN_LAST) + { + float val = fluid_gen_scale_nrpn(nrpn_select, data); + fluid_synth_set_gen_LOCAL(synth, channum, nrpn_select, val); + } + + chan->nrpn_select = 0; /* Reset to 0 */ + } + } + else if(fluid_channel_get_cc(chan, RPN_MSB) == 0) /* RPN is active: MSB = 0? */ + { + switch(fluid_channel_get_cc(chan, RPN_LSB)) + { + case RPN_PITCH_BEND_RANGE: /* Set bend range in semitones */ + fluid_channel_set_pitch_wheel_sensitivity(synth->channel[channum], value); + fluid_synth_update_pitch_wheel_sens_LOCAL(synth, channum); /* Update bend range */ + /* FIXME - Handle LSB? (Fine bend range in cents) */ + break; + + case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over +/-1 semitone (+/- 100 cents, 8192 = center) */ + fluid_synth_set_gen_LOCAL(synth, channum, GEN_FINETUNE, + (float)(data - 8192) * (100.0f / 8192.0f)); + break; + + case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */ + fluid_synth_set_gen_LOCAL(synth, channum, GEN_COARSETUNE, + value - 64); + break; + + case RPN_TUNING_PROGRAM_CHANGE: + fluid_channel_set_tuning_prog(chan, value); + fluid_synth_activate_tuning(synth, channum, + fluid_channel_get_tuning_bank(chan), + value, TRUE); + break; + + case RPN_TUNING_BANK_SELECT: + fluid_channel_set_tuning_bank(chan, value); + break; + + case RPN_MODULATION_DEPTH_RANGE: + break; + } + } + + break; + } + + case NRPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_channel_set_cc(chan, NRPN_LSB, 0); + chan->nrpn_select = 0; + chan->nrpn_active = 1; + break; + + case NRPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + + /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ + if(fluid_channel_get_cc(chan, NRPN_MSB) == 120) + { + if(value == 100) + { + chan->nrpn_select += 100; + } + else if(value == 101) + { + chan->nrpn_select += 1000; + } + else if(value == 102) + { + chan->nrpn_select += 10000; + } + else if(value < 100) + { + chan->nrpn_select += value; + } + } + + chan->nrpn_active = 1; + break; + + case RPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + case RPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + chan->nrpn_active = 0; + break; + + case BREATH_MSB: + /* handles CC Breath On/Off noteOn/noteOff mode */ + fluid_channel_cc_breath_note_on_off(chan, value); + + /* fall-through */ + default: + /* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1) */ + /* However, as long fluidsynth will use only CC 7 bits resolution, it + is safe to ignore these SF recommendations on CC receive. See + explanations above */ + /* if (! (32 <= num && num <= 63)) */ + { + return fluid_synth_modulate_voices_LOCAL(synth, channum, 1, num); + } + } + + return FLUID_OK; +} + +/** + * Get current MIDI controller value on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param num MIDI controller number (0-127) + * @param pval Location to store MIDI controller value (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_cc(fluid_synth_t *synth, int chan, int num, int *pval) +{ + fluid_return_val_if_fail(num >= 0 && num < 128, FLUID_FAILED); + fluid_return_val_if_fail(pval != NULL, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + *pval = fluid_channel_get_cc(synth->channel[chan], num); + FLUID_API_RETURN(FLUID_OK); +} + +/* + * Handler for synth.device-id setting. + */ +static void +fluid_synth_handle_device_id(void *data, const char *name, int value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + fluid_synth_api_enter(synth); + synth->device_id = value; + fluid_synth_api_exit(synth); +} + +/** + * Process a MIDI SYSEX (system exclusive) message. + * @param synth FluidSynth instance + * @param data Buffer containing SYSEX data (not including 0xF0 and 0xF7) + * @param len Length of data in buffer + * @param response Buffer to store response to or NULL to ignore + * @param response_len IN/OUT parameter, in: size of response buffer, out: + * amount of data written to response buffer (if #FLUID_FAILED is returned and + * this value is non-zero, it indicates the response buffer is too small) + * @param handled Optional location to store boolean value if message was + * recognized and handled or not (set to TRUE if it was handled) + * @param dryrun TRUE to just do a dry run but not actually execute the SYSEX + * command (useful for checking if a SYSEX message would be handled) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + * @note When Fluidsynth receives an XG System Mode ON message, it compares the @p synth 's deviceID + * directly with the deviceID of the SysEx message. This is contrary to the XG spec (page 42), which + * requires to only compare the lower nibble. However, following the XG spec seems to break drum channels + * for a lot of MIDI files out there and therefore we've decided for this customization. If you rely on + * XG System Mode ON messages, make sure to set the setting \ref settings_synth_device-id to match the + * deviceID provided in the SysEx message (in most cases, this will be deviceID=16). + * + * @code + * SYSEX format (0xF0 and 0xF7 bytes shall not be passed to this function): + * Non-realtime: 0xF0 0x7E [BODY] 0xF7 + * Realtime: 0xF0 0x7F [BODY] 0xF7 + * Tuning messages: 0xF0 0x7E/0x7F 0x08 [BODY] 0xF7 + * GS DT1 messages: 0xF0 0x41 0x42 0x12 [ADDRESS (3 bytes)] [DATA] 0xF7 + * @endcode + */ +int +fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int *handled, int dryrun) +{ + int avail_response = 0; + + if(handled) + { + *handled = FALSE; + } + + if(response_len) + { + avail_response = *response_len; + *response_len = 0; + } + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(data != NULL, FLUID_FAILED); + fluid_return_val_if_fail(len > 0, FLUID_FAILED); + fluid_return_val_if_fail(!response || response_len, FLUID_FAILED); + + if(len < 4) + { + return FLUID_OK; + } + + /* MIDI tuning SYSEX message? */ + if((data[0] == MIDI_SYSEX_UNIV_NON_REALTIME || data[0] == MIDI_SYSEX_UNIV_REALTIME) + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_MIDI_TUNING_ID) + { + int result; + fluid_synth_api_enter(synth); + result = fluid_synth_sysex_midi_tuning(synth, data, len, response, + response_len, avail_response, + handled, dryrun); + + FLUID_API_RETURN(result); + } + + /* GM or GM2 system on */ + if(data[0] == MIDI_SYSEX_UNIV_NON_REALTIME + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_GM_ID) + { + if(handled) + { + *handled = TRUE; + } + if(!dryrun && (data[3] == MIDI_SYSEX_GM_ON + || data[3] == MIDI_SYSEX_GM2_ON)) + { + int result; + synth->bank_select = FLUID_BANK_STYLE_GM; + fluid_synth_api_enter(synth); + result = fluid_synth_system_reset_LOCAL(synth); + FLUID_API_RETURN(result); + } + return FLUID_OK; + } + + /* GS DT1 message */ + if(data[0] == MIDI_SYSEX_MANUF_ROLAND + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_GS_ID + && data[3] == MIDI_SYSEX_GS_DT1) + { + int result; + fluid_synth_api_enter(synth); + result = fluid_synth_sysex_gs_dt1(synth, data, len, response, + response_len, avail_response, + handled, dryrun); + FLUID_API_RETURN(result); + } + + /* XG message */ + if(data[0] == MIDI_SYSEX_MANUF_YAMAHA + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL || synth->device_id == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_XG_ID) + { + int result; + fluid_synth_api_enter(synth); + result = fluid_synth_sysex_xg(synth, data, len, response, + response_len, avail_response, + handled, dryrun); + FLUID_API_RETURN(result); + } + + return FLUID_OK; +} + +/* Handler for MIDI tuning SYSEX messages */ +static int +fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int avail_response, + int *handled, int dryrun) +{ + int realtime, msgid; + int bank = 0, prog, channels; + double tunedata[128]; + int keys[128]; + char name[17]={0}; + int note, frac, frac2; + uint8_t chksum; + int i, count, index; + const char *dataptr; + char *resptr; + + realtime = data[0] == MIDI_SYSEX_UNIV_REALTIME; + msgid = data[3]; + + switch(msgid) + { + case MIDI_SYSEX_TUNING_BULK_DUMP_REQ: + case MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK: + if(data[3] == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) + { + if(len != 5 || data[4] & 0x80 || !response) + { + return FLUID_OK; + } + + *response_len = 406; + prog = data[4]; + } + else + { + if(len != 6 || data[4] & 0x80 || data[5] & 0x80 || !response) + { + return FLUID_OK; + } + + *response_len = 407; + bank = data[4]; + prog = data[5]; + } + + if(dryrun) + { + if(handled) + { + *handled = TRUE; + } + + return FLUID_OK; + } + + if(avail_response < *response_len) + { + return FLUID_FAILED; + } + + /* Get tuning data, return if tuning not found */ + if(fluid_synth_tuning_dump(synth, bank, prog, name, 17, tunedata) == FLUID_FAILED) + { + *response_len = 0; + return FLUID_OK; + } + + resptr = response; + + *resptr++ = MIDI_SYSEX_UNIV_NON_REALTIME; + *resptr++ = synth->device_id; + *resptr++ = MIDI_SYSEX_MIDI_TUNING_ID; + *resptr++ = MIDI_SYSEX_TUNING_BULK_DUMP; + + if(msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK) + { + *resptr++ = bank; + } + + *resptr++ = prog; + /* copy 16 ASCII characters (potentially not null terminated) to the sysex buffer */ + FLUID_MEMCPY(resptr, name, 16); + resptr += 16; + + for(i = 0; i < 128; i++) + { + note = tunedata[i] / 100.0; + fluid_clip(note, 0, 127); + + frac = ((tunedata[i] - note * 100.0) * 16384.0 + 50.0) / 100.0; + fluid_clip(frac, 0, 16383); + + *resptr++ = note; + *resptr++ = frac >> 7; + *resptr++ = frac & 0x7F; + } + + if(msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) + { + /* NOTE: Checksum is not as straight forward as the bank based messages */ + chksum = MIDI_SYSEX_UNIV_NON_REALTIME ^ MIDI_SYSEX_MIDI_TUNING_ID + ^ MIDI_SYSEX_TUNING_BULK_DUMP ^ prog; + + for(i = 21; i < 128 * 3 + 21; i++) + { + chksum ^= response[i]; + } + } + else + { + for(i = 1, chksum = 0; i < 406; i++) + { + chksum ^= response[i]; + } + } + + *resptr++ = chksum & 0x7F; + + if(handled) + { + *handled = TRUE; + } + + break; + + case MIDI_SYSEX_TUNING_NOTE_TUNE: + case MIDI_SYSEX_TUNING_NOTE_TUNE_BANK: + dataptr = data + 4; + + if(msgid == MIDI_SYSEX_TUNING_NOTE_TUNE) + { + if(len < 10 || data[4] & 0x80 || data[5] & 0x80 || len != data[5] * 4 + 6) + { + return FLUID_OK; + } + } + else + { + if(len < 11 || data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80 + || len != data[6] * 4 + 7) + { + return FLUID_OK; + } + + bank = *dataptr++; + } + + if(dryrun) + { + if(handled) + { + *handled = TRUE; + } + + return FLUID_OK; + } + + prog = *dataptr++; + count = *dataptr++; + + for(i = 0, index = 0; i < count; i++) + { + note = *dataptr++; + + if(note & 0x80) + { + return FLUID_OK; + } + + keys[index] = note; + + note = *dataptr++; + frac = *dataptr++; + frac2 = *dataptr++; + + if(note & 0x80 || frac & 0x80 || frac2 & 0x80) + { + return FLUID_OK; + } + + frac = frac << 7 | frac2; + + /* No change pitch value? Doesn't really make sense to send that, but.. */ + if(note == 0x7F && frac == 16383) + { + continue; + } + + tunedata[index] = note * 100.0 + (frac * 100.0 / 16384.0); + index++; + } + + if(index > 0) + { + if(fluid_synth_tune_notes(synth, bank, prog, index, keys, tunedata, + realtime) == FLUID_FAILED) + { + return FLUID_FAILED; + } + } + + if(handled) + { + *handled = TRUE; + } + + break; + + case MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE: + case MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE: + if((msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE && len != 19) + || (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE && len != 31)) + { + return FLUID_OK; + } + + if(data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80) + { + return FLUID_OK; + } + + if(dryrun) + { + if(handled) + { + *handled = TRUE; + } + + return FLUID_OK; + } + + channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6]; + + if(msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE) + { + for(i = 0; i < 12; i++) + { + frac = data[i + 7]; + + if(frac & 0x80) + { + return FLUID_OK; + } + + tunedata[i] = (int)frac - 64; + } + } + else + { + for(i = 0; i < 12; i++) + { + frac = data[i * 2 + 7]; + frac2 = data[i * 2 + 8]; + + if(frac & 0x80 || frac2 & 0x80) + { + return FLUID_OK; + } + + tunedata[i] = (((int)frac << 7 | (int)frac2) - 8192) * (200.0 / 16384.0); + } + } + + if(fluid_synth_activate_octave_tuning(synth, 0, 0, "SYSEX", + tunedata, realtime) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + if(channels) + { + for(i = 0; i < 16; i++) + { + if(channels & (1 << i)) + { + fluid_synth_activate_tuning(synth, i, 0, 0, realtime); + } + } + } + + if(handled) + { + *handled = TRUE; + } + + break; + } + + return FLUID_OK; +} + +/* Handler for GS DT1 messages */ +static int +fluid_synth_sysex_gs_dt1(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int avail_response, + int *handled, int dryrun) +{ + int addr; + int len_data; + int checksum = 0, i; + + if(len < 9) // at least one byte of data should be transmitted + { + FLUID_LOG(FLUID_INFO, "SysEx DT1: message too short, dropping it."); + return FLUID_FAILED; + } + len_data = len - 8; + addr = (data[4] << 16) | (data[5] << 8) | data[6]; + + for (i = 4; i < len - 1; ++i) + { + checksum += data[i]; + } + checksum = 0x80 - (checksum & 0x7F); + if (checksum != data[len - 1]) + { + FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping message on addr 0x%x due to incorrect checksum 0x%x. Correct checksum: 0x%x", addr, (int)data[len - 1], checksum); + return FLUID_FAILED; + } + + if (addr == 0x40007F) // Mode set + { + if (len_data > 1 || (data[7] != 0 && data[7] != 0x7f)) + { + FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping invalid mode set message"); + return FLUID_FAILED; + } + if (handled) + { + *handled = TRUE; + } + if (!dryrun) + { + if (data[7] == 0) + { + synth->bank_select = FLUID_BANK_STYLE_GS; + } + else + { + synth->bank_select = FLUID_BANK_STYLE_GM; + } + return fluid_synth_system_reset_LOCAL(synth); + } + return FLUID_OK; + } + + if (synth->bank_select != FLUID_BANK_STYLE_GS) + { + return FLUID_OK; // Silently ignore all other messages + } + + if ((addr & 0xFFF0FF) == 0x401015) // Use for rhythm part + { + if (len_data > 1 || data[7] > 0x02) + { + FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping invalid rhythm part message"); + return FLUID_FAILED; + } + if (handled) + { + *handled = TRUE; + } + if (!dryrun) + { + int chan = (addr >> 8) & 0x0F; + //See the Patch Part parameters section in SC-88Pro/8850 owner's manual + chan = chan >= 0x0a ? chan : (chan == 0 ? 9 : chan - 1); + synth->channel[chan]->channel_type = + data[7] == 0x00 ? CHANNEL_TYPE_MELODIC : CHANNEL_TYPE_DRUM; + + FLUID_LOG(FLUID_DBG, "SysEx DT1: setting MIDI channel %d to type %d", chan, (int)synth->channel[chan]->channel_type); + //Roland synths seem to "remember" the last instrument a channel + //used in the selected mode. This behavior is not replicated here. + fluid_synth_program_change(synth, chan, 0); + } + return FLUID_OK; + } + + //silently ignore + return FLUID_OK; +} + +/* Handler for XG messages */ +static int +fluid_synth_sysex_xg(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int avail_response, + int *handled, int dryrun) +{ + int addr; + int len_data; + + if(len < 7) // at least one byte of data should be transmitted + { + return FLUID_FAILED; + } + len_data = len - 6; + addr = (data[3] << 16) | (data[4] << 8) | data[5]; + + if (addr == 0x00007E // Reset + || addr == 0x00007F) // Reset to factory + { + if (len_data > 1 || data[6] != 0) + { + return FLUID_FAILED; + } + if (handled) + { + *handled = TRUE; + } + if (!dryrun) + { + synth->bank_select = FLUID_BANK_STYLE_XG; + return fluid_synth_system_reset_LOCAL(synth); + } + return FLUID_OK; + } + + /* No other messages handled yet + if (synth->bank_select != FLUID_BANK_STYLE_XG) + { + return FLUID_OK; // Silently ignore all other messages + }*/ + + //silently ignore + return FLUID_OK; +} + +/** + * Turn off all voices that are playing on the given MIDI channel, by putting them into release phase. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.4 + */ +int +fluid_synth_all_notes_off(fluid_synth_t *synth, int chan) +{ + int result; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(chan >= -1, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(chan >= synth->midi_channels) + { + result = FLUID_FAILED; + } + else + { + /* Allowed (even for channel disabled) as chan = -1 selects all channels */ + result = fluid_synth_all_notes_off_LOCAL(synth, chan); + } + + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of all notes off, (chan=-1 selects all channels) */ +//static int +int +fluid_synth_all_notes_off_LOCAL(fluid_synth_t *synth, int chan) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice)))) + { + fluid_voice_noteoff(voice); + } + } + + return FLUID_OK; +} + +/** + * Immediately stop all voices on the given MIDI channel (skips release phase). + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.4 + */ +int +fluid_synth_all_sounds_off(fluid_synth_t *synth, int chan) +{ + int result; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(chan >= -1, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(chan >= synth->midi_channels) + { + result = FLUID_FAILED; + } + else + { + /* Allowed (even for channel disabled) as chan = -1 selects all channels */ + result = fluid_synth_all_sounds_off_LOCAL(synth, chan); + } + + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of all sounds off, (chan=-1 selects all channels) */ +static int +fluid_synth_all_sounds_off_LOCAL(fluid_synth_t *synth, int chan) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice)))) + { + fluid_voice_off(voice); + } + } + + return FLUID_OK; +} + +/** + * Reset reverb engine + * @param synth FluidSynth instance + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_reset_reverb(fluid_synth_t *synth) +{ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f); + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Reset chorus engine + * @param synth FluidSynth instance + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_reset_chorus(fluid_synth_t *synth) +{ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f); + FLUID_API_RETURN(FLUID_OK); +} + + +/** + * Send MIDI system reset command (big red 'panic' button), turns off notes, resets + * controllers and restores initial basic channel configuration. + * @param synth FluidSynth instance + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_system_reset(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + result = fluid_synth_system_reset_LOCAL(synth); + FLUID_API_RETURN(result); +} + +/* Local variant of the system reset command */ +static int +fluid_synth_system_reset_LOCAL(fluid_synth_t *synth) +{ + int i; + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "=== systemreset ==="); + } + + fluid_synth_all_sounds_off_LOCAL(synth, -1); + + for(i = 0; i < synth->midi_channels; i++) + { + fluid_channel_reset(synth->channel[i]); + } + + /* Basic channel 0, Mode Omni On Poly */ + fluid_synth_set_basic_channel(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, + synth->midi_channels); + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f); + + return FLUID_OK; +} + +/** + * Update voices on a MIDI channel after a MIDI control change. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param is_cc Boolean value indicating if ctrl is a CC controller or not + * @param ctrl MIDI controller value + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +static int +fluid_synth_modulate_voices_LOCAL(fluid_synth_t *synth, int chan, int is_cc, int ctrl) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_get_channel(voice) == chan) + { + fluid_voice_modulate(voice, is_cc, ctrl); + } + } + + return FLUID_OK; +} + +/** + * Update voices on a MIDI channel after all MIDI controllers have been changed. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +static int +fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t *synth, int chan) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_get_channel(voice) == chan) + { + fluid_voice_modulate_all(voice); + } + } + + return FLUID_OK; +} + +/** + * Set the MIDI channel pressure controller value. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param val MIDI channel pressure value (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_channel_pressure(fluid_synth_t *synth, int chan, int val) +{ + int result; + fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val); + } + + fluid_channel_set_channel_pressure(synth->channel[chan], val); + result = fluid_synth_update_channel_pressure_LOCAL(synth, chan); + + FLUID_API_RETURN(result); +} + +/* Updates channel pressure from within synthesis thread */ +static int +fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t *synth, int chan) +{ + return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_CHANNELPRESSURE); +} + +/** + * Set the MIDI polyphonic key pressure controller value. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI key number (0-127) + * @param val MIDI key pressure value (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 2.0.0 + */ +int +fluid_synth_key_pressure(fluid_synth_t *synth, int chan, int key, int val) +{ + int result; + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "keypressure\t%d\t%d\t%d", chan, key, val); + } + + fluid_channel_set_key_pressure(synth->channel[chan], key, val); + result = fluid_synth_update_key_pressure_LOCAL(synth, chan, key); + + FLUID_API_RETURN(result); +} + +/* Updates key pressure from within synthesis thread */ +static int +fluid_synth_update_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key) +{ + fluid_voice_t *voice; + int i; + int result = FLUID_OK; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(voice->chan == chan && voice->key == key) + { + result = fluid_voice_modulate(voice, 0, FLUID_MOD_KEYPRESSURE); + + if(result != FLUID_OK) + { + return result; + } + } + } + + return result; +} + +/** + * Set the MIDI pitch bend controller value on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param val MIDI pitch bend value (0-16383 with 8192 being center) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_pitch_bend(fluid_synth_t *synth, int chan, int val) +{ + int result; + fluid_return_val_if_fail(val >= 0 && val <= 16383, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); + } + + fluid_channel_set_pitch_bend(synth->channel[chan], val); + result = fluid_synth_update_pitch_bend_LOCAL(synth, chan); + + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of pitch bend */ +static int +fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t *synth, int chan) +{ + return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEEL); +} + +/** + * Get the MIDI pitch bend controller value on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param ppitch_bend Location to store MIDI pitch bend value (0-16383 with + * 8192 being center) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_pitch_bend(fluid_synth_t *synth, int chan, int *ppitch_bend) +{ + int result; + fluid_return_val_if_fail(ppitch_bend != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + *ppitch_bend = fluid_channel_get_pitch_bend(synth->channel[chan]); + result = FLUID_OK; + + FLUID_API_RETURN(result); +} + +/** + * Set MIDI pitch wheel sensitivity on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param val Pitch wheel sensitivity value in semitones + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_pitch_wheel_sens(fluid_synth_t *synth, int chan, int val) +{ + int result; + fluid_return_val_if_fail(val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); + } + + fluid_channel_set_pitch_wheel_sensitivity(synth->channel[chan], val); + result = fluid_synth_update_pitch_wheel_sens_LOCAL(synth, chan); + + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of set pitch wheel sensitivity */ +static int +fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t *synth, int chan) +{ + return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEELSENS); +} + +/** + * Get MIDI pitch wheel sensitivity on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param pval Location to store pitch wheel sensitivity value in semitones + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since Sometime AFTER v1.0 API freeze. + */ +int +fluid_synth_get_pitch_wheel_sens(fluid_synth_t *synth, int chan, int *pval) +{ + int result; + fluid_return_val_if_fail(pval != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + *pval = fluid_channel_get_pitch_wheel_sensitivity(synth->channel[chan]); + result = FLUID_OK; + + FLUID_API_RETURN(result); +} + +/** + * Assign a preset to a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param preset Preset to assign to channel or NULL to clear (ownership is taken over) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +static int +fluid_synth_set_preset(fluid_synth_t *synth, int chan, fluid_preset_t *preset) +{ + fluid_channel_t *channel; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + + channel = synth->channel[chan]; + + return fluid_channel_set_preset(channel, preset); +} + +/* Get a preset by SoundFont, bank and program numbers. + * Returns preset pointer or NULL. + */ +static fluid_preset_t * +fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum, + int banknum, int prognum) +{ + fluid_sfont_t *sfont; + fluid_list_t *list; + + /* 128 indicates an "unset" operation" */ + if(prognum == FLUID_UNSET_PROGRAM) + { + return NULL; + } + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == sfontnum) + { + return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); + } + } + + return NULL; +} + +/* Get a preset by SoundFont name, bank and program. + * Returns preset pointer or NULL. + */ +static fluid_preset_t * +fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname, + int banknum, int prognum) +{ + fluid_sfont_t *sfont; + fluid_list_t *list; + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(FLUID_STRCMP(fluid_sfont_get_name(sfont), sfontname) == 0) + { + return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); + } + } + + return NULL; +} + +/* Find a preset by bank and program numbers. + * Returns preset pointer or NULL. + */ +fluid_preset_t * +fluid_synth_find_preset(fluid_synth_t *synth, int banknum, + int prognum) +{ + fluid_preset_t *preset; + fluid_sfont_t *sfont; + fluid_list_t *list; + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + preset = fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); + + if(preset) + { + return preset; + } + } + + return NULL; +} + +/** + * Send a program change event on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param prognum MIDI program number (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +/* As of 1.1.1 prognum can be set to 128 to unset the preset. Not documented + * since fluid_synth_unset_program() should be used instead. */ +int +fluid_synth_program_change(fluid_synth_t *synth, int chan, int prognum) +{ + fluid_preset_t *preset = NULL; + fluid_channel_t *channel; + int subst_bank, subst_prog, banknum = 0, result = FLUID_FAILED; + + fluid_return_val_if_fail(prognum >= 0 && prognum <= 128, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + channel = synth->channel[chan]; + + if(channel->channel_type == CHANNEL_TYPE_DRUM) + { + banknum = DRUM_INST_BANK; + } + else + { + fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL); + } + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); + } + + /* I think this is a hack for MIDI files that do bank changes in GM mode. + * Proper way to handle this would probably be to ignore bank changes when in + * GM mode. - JG + * This is now possible by setting synth.midi-bank-select=gm, but let the hack + * stay for the time being. - DH + */ + if(prognum != FLUID_UNSET_PROGRAM) + { + subst_bank = banknum; + subst_prog = prognum; + + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + + /* Fallback to another preset if not found */ + if(!preset) + { + /* Percussion: Fallback to preset 0 in percussion bank */ + if(channel->channel_type == CHANNEL_TYPE_DRUM) + { + subst_prog = 0; + subst_bank = DRUM_INST_BANK; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + } + /* Melodic instrument */ + else + { + /* Fallback first to bank 0:prognum */ + subst_bank = 0; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + + /* Fallback to first preset in bank 0 (usually piano...) */ + if(!preset) + { + subst_prog = 0; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + } + } + + if(preset) + { + FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]", + chan, banknum, prognum, subst_bank, subst_prog); + } + else + { + FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]", chan, banknum, prognum); + } + } + } + + /* Assign the SoundFont ID and program number to the channel */ + fluid_channel_set_sfont_bank_prog(channel, preset ? fluid_sfont_get_id(preset->sfont) : 0, + -1, prognum); + result = fluid_synth_set_preset(synth, chan, preset); + + FLUID_API_RETURN(result); +} + +/** + * Set instrument bank number on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param bank MIDI bank number + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @note This function does not change the instrument currently assigned to \c chan, + * as it is usually called prior to fluid_synth_program_change(). If you still want + * instrument changes to take effect immediately, call fluid_synth_program_reset() + * after having set up the bank configuration. + * + */ +int +fluid_synth_bank_select(fluid_synth_t *synth, int chan, int bank) +{ + int result; + fluid_return_val_if_fail(bank <= 16383, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + fluid_channel_set_sfont_bank_prog(synth->channel[chan], -1, bank, -1); + result = FLUID_OK; + + FLUID_API_RETURN(result); +} + +/** + * Set SoundFont ID on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_id ID of a loaded SoundFont + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @note This function does not change the instrument currently assigned to \c chan, + * as it is usually called prior to fluid_synth_bank_select() or fluid_synth_program_change(). + * If you still want instrument changes to take effect immediately, call fluid_synth_program_reset() + * after having selected the soundfont. + */ +int +fluid_synth_sfont_select(fluid_synth_t *synth, int chan, int sfont_id) +{ + int result; + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1); + result = FLUID_OK; + + FLUID_API_RETURN(result); +} + +/** + * Set the preset of a MIDI channel to an unassigned state. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.1 + * + * @note Channel retains its SoundFont ID and bank numbers, while the program + * number is set to an "unset" state. MIDI program changes may re-assign a + * preset if one matches. + */ +int +fluid_synth_unset_program(fluid_synth_t *synth, int chan) +{ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + FLUID_API_RETURN(fluid_synth_program_change(synth, chan, FLUID_UNSET_PROGRAM)); +} + +/** + * Get current SoundFont ID, bank number and program number for a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_id Location to store SoundFont ID + * @param bank_num Location to store MIDI bank number + * @param preset_num Location to store MIDI program number + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_program(fluid_synth_t *synth, int chan, int *sfont_id, + int *bank_num, int *preset_num) +{ + int result; + fluid_channel_t *channel; + + fluid_return_val_if_fail(sfont_id != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank_num != NULL, FLUID_FAILED); + fluid_return_val_if_fail(preset_num != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + channel = synth->channel[chan]; + fluid_channel_get_sfont_bank_prog(channel, sfont_id, bank_num, preset_num); + + /* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */ + if(*preset_num == FLUID_UNSET_PROGRAM) + { + *preset_num = 0; + } + + result = FLUID_OK; + + FLUID_API_RETURN(result); +} + +/** + * Select an instrument on a MIDI channel by SoundFont ID, bank and program numbers. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_id ID of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id, + int bank_num, int preset_num) +{ + fluid_preset_t *preset = NULL; + fluid_channel_t *channel; + int result; + fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); + fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + channel = synth->channel[chan]; + + preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num); + + if(preset == NULL) + { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %d", + bank_num, preset_num, sfont_id); + FLUID_API_RETURN(FLUID_FAILED); + } + + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog(channel, sfont_id, bank_num, preset_num); + result = fluid_synth_set_preset(synth, chan, preset); + + FLUID_API_RETURN(result); +} + +/** + * Pins all samples of the given preset. + * + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return #FLUID_OK if the preset was found, pinned and loaded + * into memory successfully. #FLUID_FAILED otherwise. Note that #FLUID_OK + * is returned, even if synth.dynamic-sample-loading is disabled or + * the preset doesn't support dynamic-sample-loading. + * + * This function will attempt to pin all samples of the given preset and + * load them into memory, if they are currently unloaded. "To pin" in this + * context means preventing them from being unloaded by an upcoming channel + * prog change. + * + * @note This function is only useful if \ref settings_synth_dynamic-sample-loading is enabled. + * By default, dynamic-sample-loading is disabled and all samples are kept in memory. + * Furthermore, this is only useful for presets which support dynamic-sample-loading (currently, + * only preset loaded with the default soundfont loader do). + * + * @since 2.2.0 + */ +int +fluid_synth_pin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num) +{ + int ret; + fluid_preset_t *preset; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); + fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num); + + if(preset == NULL) + { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %d", + bank_num, preset_num, sfont_id); + FLUID_API_RETURN(FLUID_FAILED); + } + + ret = fluid_preset_notify(preset, FLUID_PRESET_PIN, -1); // channel unused for pinning messages + + FLUID_API_RETURN(ret); +} + +/** + * Unpin all samples of the given preset. + * + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return #FLUID_OK if preset was found, #FLUID_FAILED otherwise + * + * This function undoes the effect of fluid_synth_pin_preset(). If the preset is + * not currently used, its samples will be unloaded. + * + * @note Only useful for presets loaded with the default soundfont loader and + * only if \ref settings_synth_dynamic-sample-loading is enabled. + * + * @since 2.2.0 + */ +int +fluid_synth_unpin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num) +{ + int ret; + fluid_preset_t *preset; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); + fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num); + + if(preset == NULL) + { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %d", + bank_num, preset_num, sfont_id); + FLUID_API_RETURN(FLUID_FAILED); + } + + ret = fluid_preset_notify(preset, FLUID_PRESET_UNPIN, -1); // channel unused for pinning messages + + FLUID_API_RETURN(ret); +} + +/** + * Select an instrument on a MIDI channel by SoundFont name, bank and program numbers. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_name Name of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + */ +int +fluid_synth_program_select_by_sfont_name(fluid_synth_t *synth, int chan, + const char *sfont_name, int bank_num, + int preset_num) +{ + fluid_preset_t *preset = NULL; + fluid_channel_t *channel; + int result; + fluid_return_val_if_fail(sfont_name != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + channel = synth->channel[chan]; + + preset = fluid_synth_get_preset_by_sfont_name(synth, sfont_name, bank_num, + preset_num); + + if(preset == NULL) + { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %s", + bank_num, preset_num, sfont_name); + FLUID_API_RETURN(FLUID_FAILED); + } + + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog(channel, fluid_sfont_get_id(preset->sfont), + bank_num, preset_num); + result = fluid_synth_set_preset(synth, chan, preset); + + FLUID_API_RETURN(result); +} + +/* + * This function assures that every MIDI channel has a valid preset + * (NULL is okay). This function is called after a SoundFont is + * unloaded or reloaded. + */ +static void +fluid_synth_update_presets(fluid_synth_t *synth) +{ + fluid_channel_t *channel; + fluid_preset_t *preset; + int sfont, bank, prog; + int chan; + + for(chan = 0; chan < synth->midi_channels; chan++) + { + channel = synth->channel[chan]; + fluid_channel_get_sfont_bank_prog(channel, &sfont, &bank, &prog); + preset = fluid_synth_get_preset(synth, sfont, bank, prog); + fluid_synth_set_preset(synth, chan, preset); + } +} + +static void +fluid_synth_set_sample_rate_LOCAL(fluid_synth_t *synth, float sample_rate) +{ + int i; + fluid_clip(sample_rate, 8000.0f, 96000.0f); + synth->sample_rate = sample_rate; + + synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth); + + for(i = 0; i < synth->polyphony; i++) + { + fluid_voice_set_output_rate(synth->voice[i], sample_rate); + } +} + +/** + * Set up an event to change the sample-rate of the synth during the next rendering call. + * @warning This function is broken-by-design! Don't use it! Instead, specify the sample-rate when creating the synth. + * @deprecated As of fluidsynth 2.1.0 this function has been deprecated. + * Changing the sample-rate is generally not considered to be a real-time use-case, as it always produces some audible artifact ("click", "pop") on the dry sound and effects (because LFOs for chorus and reverb need to be reinitialized). + * The sample-rate change may also require memory allocation deep down in the effect units. + * However, this memory allocation may fail and there is no way for the caller to know that, because the actual change of the sample-rate is executed during rendering. + * This function cannot (must not) do the sample-rate change itself, otherwise the synth needs to be locked down, causing rendering to block. + * Esp. do not use this function if this @p synth instance is used by an audio driver, because the audio driver cannot be notified by this sample-rate change. + * Long story short: don't use it. + * @code{.cpp} + fluid_synth_t* synth; // assume initialized + // [...] + // sample-rate change needed? Delete the audio driver, if any. + delete_fluid_audio_driver(adriver); + // then delete the synth + delete_fluid_synth(synth); + // update the sample-rate + fluid_settings_setnum(settings, "synth.sample-rate", 22050.0); + // and re-create objects + synth = new_fluid_synth(settings); + adriver = new_fluid_audio_driver(settings, synth); + * @endcode + * @param synth FluidSynth instance + * @param sample_rate New sample-rate (Hz) + * @since 1.1.2 + */ +void +fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate) +{ + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + fluid_synth_set_sample_rate_LOCAL(synth, sample_rate); + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_samplerate, + 0, synth->sample_rate); + fluid_synth_api_exit(synth); +} + +// internal sample rate change function for the jack driver +// executes immediately, therefore, make sure no rendering call is running! +void +fluid_synth_set_sample_rate_immediately(fluid_synth_t *synth, float sample_rate) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + fluid_synth_set_sample_rate_LOCAL(synth, sample_rate); + + param[0].i = 0; + param[1].real = synth->sample_rate; + fluid_rvoice_mixer_set_samplerate(synth->eventhandler->mixer, param); + + fluid_synth_api_exit(synth); +} + + +/* Handler for synth.gain setting. */ +static void +fluid_synth_handle_gain(void *data, const char *name, double value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_synth_set_gain(synth, (float) value); +} + +/** + * Set synth output gain value. + * @param synth FluidSynth instance + * @param gain Gain value (function clamps value to the range 0.0 to 10.0) + */ +void +fluid_synth_set_gain(fluid_synth_t *synth, float gain) +{ + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + fluid_clip(gain, 0.0f, 10.0f); + + synth->gain = gain; + fluid_synth_update_gain_LOCAL(synth); + fluid_synth_api_exit(synth); +} + +/* Called by synthesis thread to update the gain in all voices */ +static void +fluid_synth_update_gain_LOCAL(fluid_synth_t *synth) +{ + fluid_voice_t *voice; + float gain; + int i; + + gain = synth->gain; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice)) + { + fluid_voice_set_gain(voice, gain); + } + } +} + +/** + * Get synth output gain value. + * @param synth FluidSynth instance + * @return Synth gain value (0.0 to 10.0) + */ +float +fluid_synth_get_gain(fluid_synth_t *synth) +{ + float result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); + + result = synth->gain; + FLUID_API_RETURN(result); +} + +/* + * Handler for synth.polyphony setting. + */ +static void +fluid_synth_handle_polyphony(void *data, const char *name, int value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_synth_set_polyphony(synth, value); +} + +/** + * Set synthesizer polyphony (max number of voices). + * @param synth FluidSynth instance + * @param polyphony Polyphony to assign + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.0.6 + */ +int +fluid_synth_set_polyphony(fluid_synth_t *synth, int polyphony) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(polyphony >= 1 && polyphony <= 65535, FLUID_FAILED); + fluid_synth_api_enter(synth); + + result = fluid_synth_update_polyphony_LOCAL(synth, polyphony); + + FLUID_API_RETURN(result); +} + +/* Called by synthesis thread to update the polyphony value */ +static int +fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony) +{ + fluid_voice_t *voice; + int i; + + if(new_polyphony > synth->nvoice) + { + /* Create more voices */ + fluid_voice_t **new_voices = FLUID_REALLOC(synth->voice, + sizeof(fluid_voice_t *) * new_polyphony); + + if(new_voices == NULL) + { + return FLUID_FAILED; + } + + synth->voice = new_voices; + + for(i = synth->nvoice; i < new_polyphony; i++) + { + synth->voice[i] = new_fluid_voice(synth->eventhandler, synth->sample_rate); + + if(synth->voice[i] == NULL) + { + return FLUID_FAILED; + } + + fluid_voice_set_custom_filter(synth->voice[i], synth->custom_filter_type, synth->custom_filter_flags); + } + + synth->nvoice = new_polyphony; + } + + synth->polyphony = new_polyphony; + + /* turn off any voices above the new limit */ + for(i = synth->polyphony; i < synth->nvoice; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice)) + { + fluid_voice_off(voice); + } + } + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, + synth->polyphony, 0.0f); + + return FLUID_OK; +} + +/** + * Get current synthesizer polyphony (max number of voices). + * @param synth FluidSynth instance + * @return Synth polyphony value. + * @since 1.0.6 + */ +int +fluid_synth_get_polyphony(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + result = synth->polyphony; + FLUID_API_RETURN(result); +} + +/** + * @brief Get current number of active voices. + * + * I.e. the no. of voices that have been + * started and have not yet finished. Unless called from synthesis context, + * this number does not necessarily have to be equal to the number of voices + * currently processed by the DSP loop, see below. + * @param synth FluidSynth instance + * @return Number of currently active voices. + * @since 1.1.0 + * + * @note To generate accurate continuous statistics of the voice count, caller + * should ensure this function is called synchronously with the audio synthesis + * process. This can be done in the new_fluid_audio_driver2() audio callback + * function for example. Otherwise every call to this function may return different + * voice counts as it may change after any (concurrent) call to fluid_synth_write_*() made by + * e.g. an audio driver or the applications audio rendering thread. + */ +int +fluid_synth_get_active_voice_count(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + result = synth->active_voice_count; + FLUID_API_RETURN(result); +} + +/** + * Get the internal synthesis buffer size value. + * @param synth FluidSynth instance + * @return Internal buffer size in audio frames. + * + * Audio is synthesized at this number of frames at a time. Defaults to 64 frames. I.e. the synth can only react to notes, + * control changes, and other audio affecting events after having processed 64 audio frames. + */ +int +fluid_synth_get_internal_bufsize(fluid_synth_t *synth) +{ + return FLUID_BUFSIZE; +} + +/** + * Resend a bank select and a program change for every channel and assign corresponding instruments. + * @param synth FluidSynth instance + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * This function is called mainly after a SoundFont has been loaded, + * unloaded or reloaded. + */ +int +fluid_synth_program_reset(fluid_synth_t *synth) +{ + int i, prog; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + /* try to set the correct presets */ + for(i = 0; i < synth->midi_channels; i++) + { + fluid_channel_get_sfont_bank_prog(synth->channel[i], NULL, NULL, &prog); + fluid_synth_program_change(synth, i, prog); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Synthesize a block of floating point audio to separate audio buffers (multi-channel rendering). + * + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param left Array of float buffers to store left channel of planar audio (as many as \c synth.audio-channels buffers, each of \c len in size) + * @param right Array of float buffers to store right channel of planar audio (size: dito) + * @param fx_left Since 1.1.7: If not \c NULL, array of float buffers to store left effect channels (as many as \c synth.effects-channels buffers, each of \c len in size) + * @param fx_right Since 1.1.7: If not \c NULL, array of float buffers to store right effect channels (size: dito) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * First effect channel used by reverb, second for chorus. + * + * @note Should only be called from synthesis thread. + * + * @deprecated fluid_synth_nwrite_float() is deprecated and will be removed in a future release. + * It may continue to work or it may return #FLUID_FAILED in the future. Consider using the more + * powerful and flexible fluid_synth_process(). + * + * Usage example: + * @code{.cpp} + const int FramesToRender = 64; + int channels; + // retrieve number of stereo audio channels + fluid_settings_getint(settings, "synth.audio-channels", &channels); + + // we need twice as many (mono-)buffers + channels *= 2; + + // fluid_synth_nwrite_float renders planar audio, e.g. if synth.audio-channels==16: + // each midi channel gets rendered to its own stereo buffer, rather than having + // one buffer and interleaved PCM + float** mix_buf = new float*[channels]; + for(int i = 0; i < channels; i++) + { + mix_buf[i] = new float[FramesToRender]; + } + + // retrieve number of (stereo) effect channels (internally hardcoded to reverb (first chan) + // and chrous (second chan)) + fluid_settings_getint(settings, "synth.effects-channels", &channels); + channels *= 2; + + float** fx_buf = new float*[channels]; + for(int i = 0; i < channels; i++) + { + fx_buf[i] = new float[FramesToRender]; + } + + float** mix_buf_l = mix_buf; + float** mix_buf_r = &mix_buf[channels/2]; + + float** fx_buf_l = fx_buf; + float** fx_buf_r = &fx_buf[channels/2]; + + fluid_synth_nwrite_float(synth, FramesToRender, mix_buf_l, mix_buf_r, fx_buf_l, fx_buf_r) + * @endcode + */ +int +fluid_synth_nwrite_float(fluid_synth_t *synth, int len, + float **left, float **right, + float **fx_left, float **fx_right) +{ + fluid_real_t *left_in, *fx_left_in; + fluid_real_t *right_in, *fx_right_in; + double time = fluid_utime(); + int i, num, available, count; +#ifdef WITH_FLOAT + int bytes; +#endif + float cpu_load; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(left != NULL, FLUID_FAILED); + fluid_return_val_if_fail(right != NULL, FLUID_FAILED); + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below + + /* First, take what's still available in the buffer */ + count = 0; + num = synth->cur; + + if(synth->cur < FLUID_BUFSIZE) + { + available = FLUID_BUFSIZE - synth->cur; + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); + + num = (available > len) ? len : available; +#ifdef WITH_FLOAT + bytes = num * sizeof(float); +#endif + + for(i = 0; i < synth->audio_channels; i++) + { +#ifdef WITH_FLOAT + FLUID_MEMCPY(left[i], &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); + FLUID_MEMCPY(right[i], &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); +#else //WITH_FLOAT + int j; + + for(j = 0; j < num; j++) + { + left[i][j] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + right[i][j] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + } + +#endif //WITH_FLOAT + } + + for(i = 0; i < synth->effects_channels; i++) + { +#ifdef WITH_FLOAT + + if(fx_left != NULL) + { + FLUID_MEMCPY(fx_left[i], &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); + } + + if(fx_right != NULL) + { + FLUID_MEMCPY(fx_right[i], &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); + } + +#else //WITH_FLOAT + int j; + + if(fx_left != NULL) + { + for(j = 0; j < num; j++) + { + fx_left[i][j] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + } + } + + if(fx_right != NULL) + { + for(j = 0; j < num; j++) + { + fx_right[i][j] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + } + } + +#endif //WITH_FLOAT + } + + count += num; + num += synth->cur; /* if we're now done, num becomes the new synth->cur below */ + } + + /* Then, run one_block() and copy till we have 'len' samples */ + while(count < len) + { + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 0); + fluid_synth_render_blocks(synth, 1); // TODO: + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); + + num = (FLUID_BUFSIZE > len - count) ? len - count : FLUID_BUFSIZE; +#ifdef WITH_FLOAT + bytes = num * sizeof(float); +#endif + + for(i = 0; i < synth->audio_channels; i++) + { +#ifdef WITH_FLOAT + FLUID_MEMCPY(left[i] + count, &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); + FLUID_MEMCPY(right[i] + count, &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); +#else //WITH_FLOAT + int j; + + for(j = 0; j < num; j++) + { + left[i][j + count] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + right[i][j + count] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + } + +#endif //WITH_FLOAT + } + + for(i = 0; i < synth->effects_channels; i++) + { +#ifdef WITH_FLOAT + + if(fx_left != NULL) + { + FLUID_MEMCPY(fx_left[i] + count, &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); + } + + if(fx_right != NULL) + { + FLUID_MEMCPY(fx_right[i] + count, &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); + } + +#else //WITH_FLOAT + int j; + + if(fx_left != NULL) + { + for(j = 0; j < num; j++) + { + fx_left[i][j + count] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + } + } + + if(fx_right != NULL) + { + for(j = 0; j < num; j++) + { + fx_right[i][j + count] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + } + } + +#endif //WITH_FLOAT + } + + count += num; + } + + synth->cur = num; + + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); + + return FLUID_OK; +} + +/** + * mixes the samples of \p in to \p out + * + * @param out the output sample buffer to mix to + * @param ooff sample offset in \p out + * @param in the rvoice_mixer input sample buffer to mix from + * @param ioff sample offset in \p in + * @param buf_idx the sample buffer index of \p in to mix from + * @param num number of samples to mix + */ +static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out, + int ooff, + const fluid_real_t *FLUID_RESTRICT in, + int ioff, + int buf_idx, + int num) +{ + if(out != NULL) + { + int j; + + for(j = 0; j < num; j++) + { + out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff]; + } + } +} + +/** + * Synthesize floating point audio to stereo audio channels + * (implements the default interface #fluid_audio_func_t). + * + * @param synth FluidSynth instance + * + * @param len Count of audio frames to synthesize and store in every single buffer provided by \p out and \p fx. + * Zero value is permitted, the function does nothing and return FLUID_OK. + * + * @param nfx Count of arrays in \c fx. Must be a multiple of 2 (because of stereo) + * and in the range 0 <= nfx/2 <= (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups()). + * Note that zero value is valid and allows to skip mixing effects in all fx output buffers. + * + * @param fx Array of buffers to store effects audio to. Buffers may + * alias with buffers of \c out. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer. + * + * @param nout Count of arrays in \c out. Must be a multiple of 2 + * (because of stereo) and in the range 0 <= nout/2 <= fluid_synth_count_audio_channels(). + * Note that zero value is valid and allows to skip mixing dry audio in all out output buffers. + * + * @param out Array of buffers to store (dry) audio to. Buffers may + * alias with buffers of \c fx. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer. + * + * @return #FLUID_OK on success, + * #FLUID_FAILED otherwise, + * - fx == NULL while nfx > 0, or out == NULL while nout > 0. + * - \c nfx or \c nout not multiple of 2. + * - len < 0. + * - \c nfx or \c nout exceed the range explained above. + * + * Synthesize and mix audio to a given number of planar audio buffers. + * Therefore pass nout = N*2 float buffers to \p out in order to render + * the synthesized audio to \p N stereo channels. Each float buffer must be + * able to hold \p len elements. + * + * \p out contains an array of planar buffers for normal, dry, stereo + * audio (alternating left and right). Like: +@code{.cpp} +out[0] = left_buffer_audio_channel_0 +out[1] = right_buffer_audio_channel_0 +out[2] = left_buffer_audio_channel_1 +out[3] = right_buffer_audio_channel_1 +... +out[ (i * 2 + 0) % nout ] = left_buffer_audio_channel_i +out[ (i * 2 + 1) % nout ] = right_buffer_audio_channel_i +@endcode + * + * for zero-based channel index \p i. + * The buffer layout of \p fx used for storing effects + * like reverb and chorus looks similar: +@code{.cpp} +fx[0] = left_buffer_channel_of_reverb_unit_0 +fx[1] = right_buffer_channel_of_reverb_unit_0 +fx[2] = left_buffer_channel_of_chorus_unit_0 +fx[3] = right_buffer_channel_of_chorus_unit_0 +fx[4] = left_buffer_channel_of_reverb_unit_1 +fx[5] = right_buffer_channel_of_reverb_unit_1 +fx[6] = left_buffer_channel_of_chorus_unit_1 +fx[7] = right_buffer_channel_of_chorus_unit_1 +fx[8] = left_buffer_channel_of_reverb_unit_2 +... +fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 0) % nfx ] = left_buffer_for_effect_channel_j_of_unit_k +fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 1) % nfx ] = right_buffer_for_effect_channel_j_of_unit_k +@endcode + * where 0 <= k < fluid_synth_count_effects_groups() is a zero-based index denoting the effects unit and + * 0 <= j < fluid_synth_count_effects_channels() is a zero-based index denoting the effect channel within + * unit \p k. + * + * Any playing voice is assigned to audio channels based on the MIDI channel it's playing on: Let \p chan be the + * zero-based MIDI channel index an arbitrary voice is playing on. To determine the audio channel and effects unit it is + * going to be rendered to use: + * + * i = chan % fluid_synth_count_audio_groups() + * + * k = chan % fluid_synth_count_effects_groups() + * + * @parblock + * @note The owner of the sample buffers must zero them out before calling this + * function, because any synthesized audio is mixed (i.e. added) to the buffers. + * E.g. if fluid_synth_process() is called from a custom audio driver process function + * (see new_fluid_audio_driver2()), the audio driver takes care of zeroing the buffers. + * @endparblock + * + * @parblock + * @note No matter how many buffers you pass in, fluid_synth_process() + * will always render all audio channels to the + * buffers in \c out and all effects channels to the + * buffers in \c fx, provided that nout > 0 and nfx > 0 respectively. If + * nout/2 < fluid_synth_count_audio_channels() it will wrap around. Same + * is true for effects audio if nfx/2 < (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups()). + * See usage examples below. + * @endparblock + * + * @parblock + * @note Should only be called from synthesis thread. + * @endparblock + */ +int +fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[], + int nout, float *out[]) +{ + return fluid_synth_process_LOCAL(synth, len, nfx, fx, nout, out, fluid_synth_render_blocks); +} + +/* declared public (instead of static) for testing purpose */ +int +fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], + int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int)) +{ + fluid_real_t *left_in, *fx_left_in; + fluid_real_t *right_in, *fx_right_in; + int nfxchan, nfxunits, naudchan; + + double time = fluid_utime(); + int i, f, num, count, buffered_blocks; + + float cpu_load; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + /* fx NULL while nfx > 0 is invalid */ + fluid_return_val_if_fail((fx != NULL) || (nfx == 0), FLUID_FAILED); + /* nfx must be multiple of 2. Note that 0 value is valid and + allows to skip mixing in fx output buffers + */ + fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED); + + /* out NULL while nout > 0 is invalid */ + fluid_return_val_if_fail((out != NULL) || (nout == 0), FLUID_FAILED); + /* nout must be multiple of 2. Note that 0 value is valid and + allows to skip mixing in out output buffers + */ + fluid_return_val_if_fail(nout % 2 == 0, FLUID_FAILED); + + /* check len value. Note that 0 value is valid, the function does nothing and returns FLUID_OK. + */ + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below + + nfxchan = synth->effects_channels; + nfxunits = synth->effects_groups; + naudchan = synth->audio_channels; + + fluid_return_val_if_fail(0 <= nfx / 2 && nfx / 2 <= nfxchan * nfxunits, FLUID_FAILED); + fluid_return_val_if_fail(0 <= nout / 2 && nout / 2 <= naudchan, FLUID_FAILED); + + /* get internal mixer audio dry buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + /* get internal mixer audio effect buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); + + /* Conversely to fluid_synth_write_float(),fluid_synth_write_s16() (which handle only one + stereo output) we don't want rendered audio effect mixed in internal audio dry buffers. + FALSE instructs the mixer that internal audio effects will be mixed in respective internal + audio effects buffers. + */ + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, FALSE); + + + /* First, take what's still available in the buffer */ + count = 0; + /* synth->cur indicates if available samples are still in internal mixer buffer */ + num = synth->cur; + + buffered_blocks = (synth->cur + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + if(synth->cur < buffered_blocks * FLUID_BUFSIZE) + { + /* yes, available sample are in internal mixer buffer */ + int available = (buffered_blocks * FLUID_BUFSIZE) - synth->cur; + num = (available > len) ? len : available; + + /* mixing dry samples (or skip if requested by the caller) */ + if(nout != 0) + { + for(i = 0; i < naudchan; i++) + { + /* mix num left samples from input mixer buffer (left_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ + float *out_buf = out[(i * 2) % nout]; + fluid_synth_mix_single_buffer(out_buf, 0, left_in, synth->cur, i, num); + + /* mix num right samples from input mixer buffer (right_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ + out_buf = out[(i * 2 + 1) % nout]; + fluid_synth_mix_single_buffer(out_buf, 0, right_in, synth->cur, i, num); + } + } + + /* mixing effects samples (or skip if requested by the caller) */ + if(nfx != 0) + { + // loop over all effects units + for(f = 0; f < nfxunits; f++) + { + // write out all effects (i.e. reverb and chorus) + for(i = 0; i < nfxchan; i++) + { + int buf_idx = f * nfxchan + i; + + /* mix num left samples from input mixer buffer (fx_left_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ + float *out_buf = fx[(buf_idx * 2) % nfx]; + fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num); + + /* mix num right samples from input mixer buffer (fx_right_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ + out_buf = fx[(buf_idx * 2 + 1) % nfx]; + fluid_synth_mix_single_buffer(out_buf, 0, fx_right_in, synth->cur, buf_idx, num); + } + } + } + + count += num; + num += synth->cur; /* if we're now done, num becomes the new synth->cur below */ + } + + /* Then, render blocks and copy till we have 'len' samples */ + while(count < len) + { + /* always render full bloc multiple of FLUID_BUFSIZE */ + int blocksleft = (len - count + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + /* render audio (dry and effect) to respective internal dry and effect buffers */ + int blockcount = block_render_func(synth, blocksleft); + + num = (blockcount * FLUID_BUFSIZE > len - count) ? len - count : blockcount * FLUID_BUFSIZE; + + /* mixing dry samples (or skip if requested by the caller) */ + if(nout != 0) + { + for(i = 0; i < naudchan; i++) + { + /* mix num left samples from input mixer buffer (left_in) at input offset + 0 to output buffer (out_buf) at offset count */ + float *out_buf = out[(i * 2) % nout]; + fluid_synth_mix_single_buffer(out_buf, count, left_in, 0, i, num); + + /* mix num right samples from input mixer buffer (right_in) at input offset + 0 to output buffer (out_buf) at offset count */ + out_buf = out[(i * 2 + 1) % nout]; + fluid_synth_mix_single_buffer(out_buf, count, right_in, 0, i, num); + } + } + + /* mixing effects samples (or skip if requested by the caller) */ + if(nfx != 0) + { + // loop over all effects units + for(f = 0; f < nfxunits; f++) + { + // write out all effects (i.e. reverb and chorus) + for(i = 0; i < nfxchan; i++) + { + int buf_idx = f * nfxchan + i; + + /* mix num left samples from input mixer buffer (fx_left_in) at input offset + 0 to output buffer (out_buf) at offset count */ + float *out_buf = fx[(buf_idx * 2) % nfx]; + fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num); + + /* mix num right samples from input mixer buffer (fx_right_in) at input offset + 0 to output buffer (out_buf) at offset count */ + out_buf = fx[(buf_idx * 2 + 1) % nfx]; + fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num); + } + } + } + + count += num; + } + + synth->cur = num; + + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); + + return FLUID_OK; +} + + +/** + * Synthesize a block of floating point audio samples to audio buffers. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param lout Array of floats to store left channel of audio + * @param loff Offset index in 'lout' for first sample + * @param lincr Increment between samples stored to 'lout' + * @param rout Array of floats to store right channel of audio + * @param roff Offset index in 'rout' for first sample + * @param rincr Increment between samples stored to 'rout' + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1, + * lincr = 2, rincr = 2). + * + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. + */ +int +fluid_synth_write_float(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr) +{ + void *channels_out[2] = {lout, rout}; + int channels_off[2] = {loff, roff }; + int channels_incr[2] = {lincr, rincr }; + + return fluid_synth_write_float_channels(synth, len, 2, channels_out, + channels_off, channels_incr); +} + +/** + * Synthesize a block of float audio samples channels to audio buffers. + * The function is convenient for audio driver to render multiple stereo + * channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels). + * + * @param synth FluidSynth instance. + * @param len Count of audio frames to synthesize. + * @param channels_count Count of channels in a frame. + * must be multiple of 2 and channel_count/2 must not exceed the number + * of internal mixer buffers (synth->audio_groups) + * @param channels_out Array of channels_count pointers on 16 bit words to + * store sample channels. Modified on return. + * @param channels_off Array of channels_count offset index to add to respective pointer + * in channels_out for first sample. + * @param channels_incr Array of channels_count increment between consecutive + * samples channels. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + * + * Useful for storing: + * - interleaved channels in a unique buffer. + * - non interleaved channels in an unique buffer (or in distinct buffers). + * + * Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn) + * in a unique buffer: + * { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4,... + * sn:c1, sn:c2, sn:c3, sn:c4 }. + * + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. + */ +int +fluid_synth_write_float_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]) +{ + return fluid_synth_write_float_channels_LOCAL(synth, len, channels_count, + channels_out, channels_off, channels_incr, + fluid_synth_render_blocks); +} + +int +fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[], + int (*block_render_func)(fluid_synth_t *, int)) +{ + float **chan_out = (float **)channels_out; + int n, cur, size; + + /* pointers on first input mixer buffer */ + fluid_real_t *left_in; + fluid_real_t *right_in; + int bufs_in_count; /* number of stereo input buffers */ + int i; + + /* start average cpu load probe */ + double time = fluid_utime(); + float cpu_load; + + /* start profiling duration probe (if profiling is enabled) */ + fluid_profile_ref_var(prof_ref); + + /* check parameters */ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below + + /* check for valid channel_count: must be multiple of 2 and + channel_count/2 must not exceed the number of internal mixer buffers + (synth->audio_groups) + */ + fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */ + && channels_count >= 2, FLUID_FAILED); + + bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */ + fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED); + + fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED); + + /* initialize output channels buffers on first sample position */ + i = channels_count; + do + { + i--; + chan_out[i] += channels_off[i]; + } + while(i); + + /* Conversely to fluid_synth_process(), + we want rendered audio effect mixed in internal audio dry buffers. + TRUE instructs the mixer that internal audio effects will be mixed in internal + audio dry buffers. + */ + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE); + + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + + size = len; + + /* synth->cur indicates if available samples are still in internal mixer buffer */ + cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */ + + do + { + /* fill up the buffers as needed */ + if(cur >= synth->curmax) + { + /* render audio (dry and effect) to internal dry buffers */ + /* always render full blocs multiple of FLUID_BUFSIZE */ + int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + synth->curmax = FLUID_BUFSIZE * block_render_func(synth, blocksleft); + + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + cur = 0; + } + + /* calculate amount of available samples */ + n = synth->curmax - cur; + + /* keep track of emitted samples */ + if(n > size) + { + n = size; + } + + size -= n; + + /* update pointers to current position */ + left_in += cur + n; + right_in += cur + n; + + /* set final cursor position */ + cur += n; + + /* reverse index */ + n = 0 - n; + + do + { + i = bufs_in_count; + do + { + /* input sample index in stereo buffer i */ + int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n; + int c = i << 1; /* channel index c to write */ + + /* write left input sample to channel sample */ + *chan_out[c] = (float) left_in[in_idx]; + + /* write right input sample to next channel sample */ + *chan_out[c+1] = (float) right_in[in_idx]; + + /* advance output pointers */ + chan_out[c] += channels_incr[c]; + chan_out[c+1] += channels_incr[c+1]; + } + while(i); + } + while(++n < 0); + } + while(size); + + synth->cur = cur; /* save current sample position. It will be used on next call */ + + /* save average cpu load, use by API for real time cpu load meter */ + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); + + /* stop duration probe and save performance measurement (if profiling is enabled) */ + fluid_profile_write(FLUID_PROF_WRITE, prof_ref, + fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), + len); + return FLUID_OK; +} + +/* for testing purpose */ +int +fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr, + int (*block_render_func)(fluid_synth_t *, int) + ) +{ + void *channels_out[2] = {lout, rout}; + int channels_off[2] = {loff, roff }; + int channels_incr[2] = {lincr, rincr }; + + return fluid_synth_write_float_channels_LOCAL(synth, len, 2, channels_out, + channels_off, channels_incr, + block_render_func); +} + + +#define DITHER_SIZE 48000 +#define DITHER_CHANNELS 2 + +static float rand_table[DITHER_CHANNELS][DITHER_SIZE]; + +/* Init dither table */ +static void +init_dither(void) +{ + float d, dp; + int c, i; + + for(c = 0; c < DITHER_CHANNELS; c++) + { + dp = 0; + + for(i = 0; i < DITHER_SIZE - 1; i++) + { + d = rand() / (float)RAND_MAX - 0.5f; + rand_table[c][i] = d - dp; + dp = d; + } + + rand_table[c][DITHER_SIZE - 1] = 0 - dp; + } +} + +/* A portable replacement for roundf(), seems it may actually be faster too! */ +static FLUID_INLINE int16_t +round_clip_to_i16(float x) +{ + long i; + + if(x >= 0.0f) + { + i = (long)(x + 0.5f); + + if(FLUID_UNLIKELY(i > 32767)) + { + i = 32767; + } + } + else + { + i = (long)(x - 0.5f); + + if(FLUID_UNLIKELY(i < -32768)) + { + i = -32768; + } + } + + return (int16_t)i; +} + +/** + * Synthesize a block of 16 bit audio samples to audio buffers. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param lout Array of 16 bit words to store left channel of audio + * @param loff Offset index in 'lout' for first sample + * @param lincr Increment between samples stored to 'lout' + * @param rout Array of 16 bit words to store right channel of audio + * @param roff Offset index in 'rout' for first sample + * @param rincr Increment between samples stored to 'rout' + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1, + * lincr = 2, rincr = 2). + * + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. + * @note Dithering is performed when converting from internal floating point to + * 16 bit audio. + */ +int +fluid_synth_write_s16(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr) +{ + void *channels_out[2] = {lout, rout}; + int channels_off[2] = {loff, roff }; + int channels_incr[2] = {lincr, rincr }; + + return fluid_synth_write_s16_channels(synth, len, 2, channels_out, + channels_off, channels_incr); +} + +/** + * Synthesize a block of 16 bit audio samples channels to audio buffers. + * The function is convenient for audio driver to render multiple stereo + * channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels). + * + * @param synth FluidSynth instance. + * @param len Count of audio frames to synthesize. + * @param channels_count Count of channels in a frame. + * must be multiple of 2 and channel_count/2 must not exceed the number + * of internal mixer buffers (synth->audio_groups) + * @param channels_out Array of channels_count pointers on 16 bit words to + * store sample channels. Modified on return. + * @param channels_off Array of channels_count offset index to add to respective pointer + * in channels_out for first sample. + * @param channels_incr Array of channels_count increment between consecutive + * samples channels. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + * + * Useful for storing: + * - interleaved channels in a unique buffer. + * - non interleaved channels in an unique buffer (or in distinct buffers). + * + * Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn) + * in a unique buffer: + * { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4, .... + * sn:c1, sn:c2, sn:c3, sn:c4 }. + * + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. + * @note Dithering is performed when converting from internal floating point to + * 16 bit audio. + */ +int +fluid_synth_write_s16_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]) +{ + int16_t **chan_out = (int16_t **)channels_out; + int di, n, cur, size; + + /* pointers on first input mixer buffer */ + fluid_real_t *left_in; + fluid_real_t *right_in; + int bufs_in_count; /* number of stereo input buffers */ + int i; + + /* start average cpu load probe */ + double time = fluid_utime(); + float cpu_load; + + /* start profiling duration probe (if profiling is enabled) */ + fluid_profile_ref_var(prof_ref); + + /* check parameters */ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below + + /* check for valid channel_count: must be multiple of 2 and + channel_count/2 must not exceed the number of internal mixer buffers + (synth->audio_groups) + */ + fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */ + && channels_count >= 2, FLUID_FAILED); + + bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */ + fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED); + + fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED); + + /* initialize output channels buffers on first sample position */ + i = channels_count; + do + { + i--; + chan_out[i] += channels_off[i]; + } + while(i); + + /* Conversely to fluid_synth_process(), + we want rendered audio effect mixed in internal audio dry buffers. + TRUE instructs the mixer that internal audio effects will be mixed in internal + audio dry buffers. + */ + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE); + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + + size = len; + /* synth->cur indicates if available samples are still in internal mixer buffer */ + cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */ + di = synth->dither_index; + + do + { + /* fill up the buffers as needed */ + if(cur >= synth->curmax) + { + /* render audio (dry and effect) to internal dry buffers */ + /* always render full blocs multiple of FLUID_BUFSIZE */ + int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); + + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + cur = 0; + } + + /* calculate amount of available samples */ + n = synth->curmax - cur; + + /* keep track of emitted samples */ + if(n > size) + { + n = size; + } + + size -= n; + + /* update pointers to current position */ + left_in += cur + n; + right_in += cur + n; + + /* set final cursor position */ + cur += n; + + /* reverse index */ + n = 0 - n; + + do + { + i = bufs_in_count; + do + { + /* input sample index in stereo buffer i */ + int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n; + int c = i << 1; /* channel index c to write */ + + /* write left input sample to channel sample */ + *chan_out[c] = round_clip_to_i16(left_in[in_idx] * 32766.0f + + rand_table[0][di]); + + /* write right input sample to next channel sample */ + *chan_out[c+1] = round_clip_to_i16(right_in[in_idx] * 32766.0f + + rand_table[1][di]); + /* advance output pointers */ + chan_out[c] += channels_incr[c]; + chan_out[c+1] += channels_incr[c+1]; + } + while(i); + + if(++di >= DITHER_SIZE) + { + di = 0; + } + } + while(++n < 0); + } + while(size); + + synth->cur = cur; /* save current sample position. It will be used on next call */ + synth->dither_index = di; /* keep dither buffer continuous */ + + /* save average cpu load, used by API for real time cpu load meter */ + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); + + /* stop duration probe and save performance measurement (if profiling is enabled) */ + fluid_profile_write(FLUID_PROF_WRITE, prof_ref, + fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), + len); + return FLUID_OK; +} + +/** + * Converts stereo floating point sample data to signed 16 bit data with dithering. + * @param dither_index Pointer to an integer which should be initialized to 0 + * before the first call and passed unmodified to additional calls which are + * part of the same synthesis output. + * @param len Length in frames to convert + * @param lin Buffer of left audio samples to convert from + * @param rin Buffer of right audio samples to convert from + * @param lout Array of 16 bit words to store left channel of audio + * @param loff Offset index in 'lout' for first sample + * @param lincr Increment between samples stored to 'lout' + * @param rout Array of 16 bit words to store right channel of audio + * @param roff Offset index in 'rout' for first sample + * @param rincr Increment between samples stored to 'rout' + * + * @note Currently private to libfluidsynth. + */ +void +fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const float *rin, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr) +{ + int i, j, k; + int16_t *left_out = lout; + int16_t *right_out = rout; + int di = *dither_index; + fluid_profile_ref_var(prof_ref); + + for(i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr) + { + left_out[j] = round_clip_to_i16(lin[i] * 32766.0f + rand_table[0][di]); + right_out[k] = round_clip_to_i16(rin[i] * 32766.0f + rand_table[1][di]); + + if(++di >= DITHER_SIZE) + { + di = 0; + } + } + + *dither_index = di; /* keep dither buffer continuous */ + + fluid_profile(FLUID_PROF_WRITE, prof_ref, 0, len); +} + +static void +fluid_synth_check_finished_voices(fluid_synth_t *synth) +{ + int j; + fluid_rvoice_t *fv; + + while(NULL != (fv = fluid_rvoice_eventhandler_get_finished_voice(synth->eventhandler))) + { + for(j = 0; j < synth->polyphony; j++) + { + if(synth->voice[j]->rvoice == fv) + { + fluid_voice_unlock_rvoice(synth->voice[j]); + fluid_voice_stop(synth->voice[j]); + break; + } + else if(synth->voice[j]->overflow_rvoice == fv) + { + /* Unlock the overflow_rvoice of the voice. + Decrement the reference count of the sample owned by this + rvoice. + */ + fluid_voice_overflow_rvoice_finished(synth->voice[j]); + + /* Decrement synth active voice count. Must not be incorporated + in fluid_voice_overflow_rvoice_finished() because + fluid_voice_overflow_rvoice_finished() is called also + at synth destruction and in this case the variable should be + accessed via voice->channel->synth->active_voice_count. + And for certain voices which are not playing, the field + voice->channel is NULL. + */ + synth->active_voice_count--; + break; + } + } + } +} + +/** + * Process all waiting events in the rvoice queue. + * Make sure no (other) rendering is running in parallel when + * you call this function! + */ +void fluid_synth_process_event_queue(fluid_synth_t *synth) +{ + fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler); +} + + +/** + * Process blocks (FLUID_BUFSIZE) of audio. + * Must be called from renderer thread only! + * @return number of blocks rendered. Might (often) return less than requested + */ +static int +fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount) +{ + int i, maxblocks; + fluid_profile_ref_var(prof_ref); + + /* Assign ID of synthesis thread */ +// synth->synth_thread_id = fluid_thread_get_id (); + + fluid_check_fpe("??? Just starting up ???"); + + fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler); + + /* do not render more blocks than we can store internally */ + maxblocks = fluid_rvoice_mixer_get_bufcount(synth->eventhandler->mixer); + + if(blockcount > maxblocks) + { + blockcount = maxblocks; + } + + for(i = 0; i < blockcount; i++) + { + fluid_sample_timer_process(synth); + fluid_synth_add_ticks(synth, FLUID_BUFSIZE); + + /* If events have been queued waiting for fluid_rvoice_eventhandler_dispatch_all() + * (should only happen with parallel render) stop processing and go for rendering + */ + if(fluid_rvoice_eventhandler_dispatch_count(synth->eventhandler)) + { + // Something has happened, we can't process more + blockcount = i + 1; + break; + } + } + + fluid_check_fpe("fluid_sample_timer_process"); + + blockcount = fluid_rvoice_mixer_render(synth->eventhandler->mixer, blockcount); + + /* Testcase, that provokes a denormal floating point error */ +#if 0 + { + float num = 1; + + while(num != 0) + { + num *= 0.5; + }; + }; +#endif + fluid_check_fpe("??? Remainder of synth_one_block ???"); + fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref, + fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), + blockcount * FLUID_BUFSIZE); + return blockcount; +} + +/* + * Handler for synth.reverb.* and synth.chorus.* double settings. + */ +static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + if(FLUID_STRCMP(name, "synth.reverb.room-size") == 0) + { + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, value); + } + else if(FLUID_STRCMP(name, "synth.reverb.damp") == 0) + { + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, value); + } + else if(FLUID_STRCMP(name, "synth.reverb.width") == 0) + { + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, value); + } + else if(FLUID_STRCMP(name, "synth.reverb.level") == 0) + { + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.depth") == 0) + { + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.speed") == 0) + { + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.level") == 0) + { + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, value); + } +} + +/* + * Handler for synth.reverb.* and synth.chorus.* integer settings. + */ +static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + if(FLUID_STRCMP(name, "synth.reverb.active") == 0) + { + fluid_synth_reverb_on(synth, -1, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.active") == 0) + { + fluid_synth_chorus_on(synth, -1, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.nr") == 0) + { + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, (double)value); + } +} + +/* + * Handler for synth.overflow.* settings. + */ +static void fluid_synth_handle_overflow(void *data, const char *name, double value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + fluid_synth_api_enter(synth); + + if(FLUID_STRCMP(name, "synth.overflow.percussion") == 0) + { + synth->overflow.percussion = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.released") == 0) + { + synth->overflow.released = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.sustained") == 0) + { + synth->overflow.sustained = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.volume") == 0) + { + synth->overflow.volume = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.age") == 0) + { + synth->overflow.age = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.important") == 0) + { + synth->overflow.important = value; + } + + fluid_synth_api_exit(synth); +} + +/* Selects a voice for killing. */ +static fluid_voice_t * +fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth) +{ + int i; + float best_prio = OVERFLOW_PRIO_CANNOT_KILL - 1; + float this_voice_prio; + fluid_voice_t *voice; + int best_voice_index = -1; + unsigned int ticks = fluid_synth_get_ticks(synth); + + for(i = 0; i < synth->polyphony; i++) + { + + voice = synth->voice[i]; + + /* safeguard against an available voice. */ + if(_AVAILABLE(voice)) + { + return voice; + } + + this_voice_prio = fluid_voice_get_overflow_prio(voice, &synth->overflow, + ticks); + + /* check if this voice has less priority than the previous candidate. */ + if(this_voice_prio < best_prio) + { + best_voice_index = i; + best_prio = this_voice_prio; + } + } + + if(best_voice_index < 0) + { + return NULL; + } + + voice = synth->voice[best_voice_index]; + FLUID_LOG(FLUID_DBG, "Killing voice %d, index %d, chan %d, key %d ", + fluid_voice_get_id(voice), best_voice_index, fluid_voice_get_channel(voice), fluid_voice_get_key(voice)); + fluid_voice_off(voice); + + return voice; +} + + +/** + * Allocate a synthesis voice. + * @param synth FluidSynth instance + * @param sample Sample to assign to the voice + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number for the voice (0-127) + * @param vel MIDI velocity for the voice (0-127) + * @return Allocated synthesis voice or NULL on error + * + * This function is called by a SoundFont's preset in response to a noteon event. + * The returned voice comes with default modulators and generators. + * A single noteon event may create any number of voices, when the preset is layered. + * + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon method. + */ +fluid_voice_t * +fluid_synth_alloc_voice(fluid_synth_t *synth, fluid_sample_t *sample, + int chan, int key, int vel) +{ + fluid_return_val_if_fail(sample != NULL, NULL); + fluid_return_val_if_fail(sample->data != NULL, NULL); + FLUID_API_ENTRY_CHAN(NULL); + FLUID_API_RETURN(fluid_synth_alloc_voice_LOCAL(synth, sample, chan, key, vel, NULL)); + +} + +fluid_voice_t * +fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range) +{ + int i, k; + fluid_voice_t *voice = NULL; + fluid_channel_t *channel = NULL; + unsigned int ticks; + + /* check if there's an available synthesis process */ + for(i = 0; i < synth->polyphony; i++) + { + if(_AVAILABLE(synth->voice[i])) + { + voice = synth->voice[i]; + break; + } + } + + /* No success yet? Then stop a running voice. */ + if(voice == NULL) + { + FLUID_LOG(FLUID_DBG, "Polyphony exceeded, trying to kill a voice"); + voice = fluid_synth_free_voice_by_kill_LOCAL(synth); + } + + if(voice == NULL) + { + FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key); + return NULL; + } + + ticks = fluid_synth_get_ticks(synth); + + if(synth->verbose) + { + k = 0; + + for(i = 0; i < synth->polyphony; i++) + { + if(!_AVAILABLE(synth->voice[i])) + { + k++; + } + } + + FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d", + chan, key, vel, synth->storeid, + (float) ticks / 44100.0f, + (fluid_curtime() - synth->start) / 1000.0f, + 0.0f, + k); + } + + channel = synth->channel[chan]; + + if(fluid_voice_init(voice, sample, zone_range, channel, key, vel, + synth->storeid, ticks, synth->gain) != FLUID_OK) + { + FLUID_LOG(FLUID_WARN, "Failed to initialize voice"); + return NULL; + } + + /* add the default modulators to the synthesis process. */ + /* custom_breath2att_modulator is not a default modulator specified in SF + it is intended to replace default_vel2att_mod for this channel on demand using + API fluid_synth_set_breath_mode() or shell command setbreathmode for this channel. + */ + { + int mono = fluid_channel_is_playing_mono(channel); + fluid_mod_t *default_mod = synth->default_mod; + + while(default_mod != NULL) + { + if( + /* See if default_mod is the velocity_to_attenuation modulator */ + fluid_mod_test_identity(default_mod, &default_vel2att_mod) && + // See if a replacement by custom_breath2att_modulator has been demanded + // for this channel + ((!mono && (channel->mode & FLUID_CHANNEL_BREATH_POLY)) || + (mono && (channel->mode & FLUID_CHANNEL_BREATH_MONO))) + ) + { + // Replacement of default_vel2att modulator by custom_breath2att_modulator + fluid_voice_add_mod_local(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT, 0); + } + else + { + fluid_voice_add_mod_local(voice, default_mod, FLUID_VOICE_DEFAULT, 0); + } + + // Next default modulator to add to the voice + default_mod = default_mod->next; + } + } + + return voice; +} + +/* Kill all voices on a given channel, which have the same exclusive class + * generator as new_voice. + */ +static void +fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth, + fluid_voice_t *new_voice) +{ + int excl_class = fluid_voice_gen_value(new_voice, GEN_EXCLUSIVECLASS); + int i; + + /* Excl. class 0: No exclusive class */ + if(excl_class == 0) + { + return; + } + + /* Kill all notes on the same channel with the same exclusive class */ + for(i = 0; i < synth->polyphony; i++) + { + fluid_voice_t *existing_voice = synth->voice[i]; + + /* If voice is playing, on the same channel, has same exclusive + * class and is not part of the same noteon event (voice group), then kill it */ + + if(fluid_voice_is_playing(existing_voice) + && fluid_voice_get_channel(existing_voice) == fluid_voice_get_channel(new_voice) + && fluid_voice_gen_value(existing_voice, GEN_EXCLUSIVECLASS) == excl_class + && fluid_voice_get_id(existing_voice) != fluid_voice_get_id(new_voice)) + { + fluid_voice_kill_excl(existing_voice); + } + } +} + +/** + * Activate a voice previously allocated with fluid_synth_alloc_voice(). + * @param synth FluidSynth instance + * @param voice Voice to activate + * + * This function is called by a SoundFont's preset in response to a noteon + * event. Exclusive classes are processed here. + * + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon method. + */ +void +fluid_synth_start_voice(fluid_synth_t *synth, fluid_voice_t *voice) +{ + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(voice != NULL); +// fluid_return_if_fail (fluid_synth_is_synth_thread (synth)); + fluid_synth_api_enter(synth); + + /* Find the exclusive class of this voice. If set, kill all voices + * that match the exclusive class and are younger than the first + * voice process created by this noteon event. */ + fluid_synth_kill_by_exclusive_class_LOCAL(synth, voice); + + fluid_voice_start(voice); /* Start the new voice */ + fluid_voice_lock_rvoice(voice); + fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice); + fluid_synth_api_exit(synth); +} + +/** + * Add a SoundFont loader to the synth. This function takes ownership of \c loader + * and frees it automatically upon \c synth destruction. + * @param synth FluidSynth instance + * @param loader Loader API structure + * + * SoundFont loaders are used to add custom instrument loading to FluidSynth. + * The caller supplied functions for loading files, allocating presets, + * retrieving information on them and synthesizing note-on events. Using this + * method even non SoundFont instruments can be synthesized, although limited + * to the SoundFont synthesis model. + * + * @note Should only be called before any SoundFont files are loaded. + */ +void +fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader) +{ + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(loader != NULL); + fluid_synth_api_enter(synth); + + /* Test if sfont is already loaded */ + if(synth->sfont == NULL) + { + synth->loaders = fluid_list_prepend(synth->loaders, loader); + } + + fluid_synth_api_exit(synth); +} + +/** + * Load a SoundFont file (filename is interpreted by SoundFont loaders). + * The newly loaded SoundFont will be put on top of the SoundFont + * stack. Presets are searched starting from the SoundFont on the + * top of the stack, working the way down the stack until a preset is found. + * + * @param synth FluidSynth instance + * @param filename File to load + * @param reset_presets TRUE to re-assign presets for all MIDI channels (equivalent to calling fluid_synth_program_reset()) + * @return SoundFont ID on success, #FLUID_FAILED on error + * + * @note Since FluidSynth 2.2.0 @c filename is treated as an UTF8 encoded string on Windows. FluidSynth will convert it + * to wide-char internally and then pass it to _wfopen(). Before FluidSynth 2.2.0, @c filename was treated as ANSI string + * on Windows. All other platforms directly pass it to fopen() without any conversion (usually, UTF8 is accepted). + */ +int +fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets) +{ + fluid_sfont_t *sfont; + fluid_list_t *list; + fluid_sfloader_t *loader; + int sfont_id; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(filename != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + sfont_id = synth->sfont_id; + + if(++sfont_id != FLUID_FAILED) + { + /* MT NOTE: Loaders list should not change. */ + + for(list = synth->loaders; list; list = fluid_list_next(list)) + { + loader = (fluid_sfloader_t *) fluid_list_get(list); + + sfont = fluid_sfloader_load(loader, filename); + + if(sfont != NULL) + { + sfont->refcount++; + synth->sfont_id = sfont->id = sfont_id; + + synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */ + + /* reset the presets for all channels if requested */ + if(reset_presets) + { + fluid_synth_program_reset(synth); + } + + FLUID_API_RETURN(sfont_id); + } + } + } + + FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); + FLUID_API_RETURN(FLUID_FAILED); +} + +/** + * Schedule a SoundFont for unloading. + * + * If the SoundFont isn't used anymore by any playing voices, it will be unloaded immediately. + * + * If any samples of the given SoundFont are still required by active voices, + * the SoundFont will be unloaded in a lazy manner, once those voices have finished synthesizing. + * If you call delete_fluid_synth(), all voices will be destroyed and the SoundFont + * will be unloaded in any case. + * Once this function returned, fluid_synth_sfcount() and similar functions will behave as if + * the SoundFont has already been unloaded, even though the lazy-unloading is still pending. + * + * @note This lazy-unloading mechanism was broken between FluidSynth 1.1.4 and 2.1.5 . As a + * consequence, SoundFonts scheduled for lazy-unloading may be never freed under certain + * conditions. Calling delete_fluid_synth() does not recover this situation either. + * + * @param synth FluidSynth instance + * @param id ID of SoundFont to unload + * @param reset_presets TRUE to re-assign presets for all MIDI channels + * @return #FLUID_OK if the given @p id was found, #FLUID_FAILED otherwise. + */ +int +fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets) +{ + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + /* remove the SoundFont from the list */ + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == id) + { + synth->sfont = fluid_list_remove(synth->sfont, sfont); + break; + } + } + + if(!list) + { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); + FLUID_API_RETURN(FLUID_FAILED); + } + + /* reset the presets for all channels (SoundFont will be freed when there are no more references) */ + if(reset_presets) + { + fluid_synth_program_reset(synth); + } + else + { + fluid_synth_update_presets(synth); + } + + /* -- Remove synth->sfont list's reference to SoundFont */ + fluid_synth_sfont_unref(synth, sfont); + + FLUID_API_RETURN(FLUID_OK); +} + +/* Unref a SoundFont and destroy if no more references */ +void +fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont) +{ + fluid_return_if_fail(sfont != NULL); /* Shouldn't happen, programming error if so */ + + sfont->refcount--; /* -- Remove the sfont list's reference */ + + if(sfont->refcount == 0) /* No more references? - Attempt delete */ + { + if(fluid_sfont_delete_internal(sfont) == 0) /* SoundFont loader can block SoundFont unload */ + { + FLUID_LOG(FLUID_DBG, "Unloaded SoundFont"); + } /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */ + else + { + fluid_timer_t* timer = new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, TRUE, FALSE, FALSE); + synth->fonts_to_be_unloaded = fluid_list_prepend(synth->fonts_to_be_unloaded, timer); + } + } +} + +/* Callback to continually attempt to unload a SoundFont, + * only if a SoundFont loader blocked the unload operation */ +static int +fluid_synth_sfunload_callback(void *data, unsigned int msec) +{ + fluid_sfont_t *sfont = data; + + if(fluid_sfont_delete_internal(sfont) == 0) + { + FLUID_LOG(FLUID_DBG, "Unloaded SoundFont"); + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * Reload a SoundFont. The SoundFont retains its ID and index on the SoundFont stack. + * @param synth FluidSynth instance + * @param id ID of SoundFont to reload + * @return SoundFont ID on success, #FLUID_FAILED on error + */ +int +fluid_synth_sfreload(fluid_synth_t *synth, int id) +{ + char *filename = NULL; + fluid_sfont_t *sfont; + fluid_sfloader_t *loader; + fluid_list_t *list; + int index, ret = FLUID_FAILED; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + /* Search for SoundFont and get its index */ + for(list = synth->sfont, index = 0; list; list = fluid_list_next(list), index++) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == id) + { + break; + } + } + + if(!list) + { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); + goto exit; + } + + /* keep a copy of the SoundFont's filename */ + filename = FLUID_STRDUP(fluid_sfont_get_name(sfont)); + + if(filename == NULL || fluid_synth_sfunload(synth, id, FALSE) != FLUID_OK) + { + goto exit; + } + + /* MT Note: SoundFont loader list will not change */ + + for(list = synth->loaders; list; list = fluid_list_next(list)) + { + loader = (fluid_sfloader_t *) fluid_list_get(list); + + sfont = fluid_sfloader_load(loader, filename); + + if(sfont != NULL) + { + sfont->id = id; + sfont->refcount++; + + synth->sfont = fluid_list_insert_at(synth->sfont, index, sfont); /* insert the sfont at the same index */ + + /* reset the presets for all channels */ + fluid_synth_update_presets(synth); + ret = id; + goto exit; + } + } + + FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); + +exit: + FLUID_FREE(filename); + FLUID_API_RETURN(ret); +} + +/** + * Add a SoundFont. The SoundFont will be added to the top of the SoundFont stack and ownership is transferred to @p synth. + * @param synth FluidSynth instance + * @param sfont SoundFont to add + * @return New assigned SoundFont ID or #FLUID_FAILED on error + */ +int +fluid_synth_add_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont) +{ + int sfont_id; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + sfont_id = synth->sfont_id; + + if(++sfont_id != FLUID_FAILED) + { + synth->sfont_id = sfont->id = sfont_id; + synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */ + + /* reset the presets for all channels */ + fluid_synth_program_reset(synth); + } + + FLUID_API_RETURN(sfont_id); +} + +/** + * Remove a SoundFont from the SoundFont stack without deleting it. + * @param synth FluidSynth instance + * @param sfont SoundFont to remove + * @return #FLUID_OK if \c sfont successfully removed, #FLUID_FAILED otherwise + * + * SoundFont is not freed and is left as the responsibility of the caller. + * + * @note The SoundFont should only be freed after there are no presets + * referencing it. This can only be ensured by the SoundFont loader and + * therefore this function should not normally be used. + */ +int +fluid_synth_remove_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont) +{ + fluid_sfont_t *sfont_tmp; + fluid_list_t *list; + int ret = FLUID_FAILED; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + /* remove the SoundFont from the list */ + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont_tmp = fluid_list_get(list); + + if(sfont_tmp == sfont) + { + synth->sfont = fluid_list_remove(synth->sfont, sfont_tmp); + ret = FLUID_OK; + break; + } + } + + /* reset the presets for all channels */ + fluid_synth_program_reset(synth); + + FLUID_API_RETURN(ret); +} + +/** + * Count number of loaded SoundFont files. + * @param synth FluidSynth instance + * @return Count of loaded SoundFont files. + */ +int +fluid_synth_sfcount(fluid_synth_t *synth) +{ + int count; + + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + count = fluid_list_size(synth->sfont); + FLUID_API_RETURN(count); +} + +/** + * Get SoundFont by index. + * @param synth FluidSynth instance + * @param num SoundFont index on the stack (starting from 0 for top of stack). + * @return SoundFont instance or NULL if invalid index + * + * @note Caller should be certain that SoundFont is not deleted (unloaded) for + * the duration of use of the returned pointer. + */ +fluid_sfont_t * +fluid_synth_get_sfont(fluid_synth_t *synth, unsigned int num) +{ + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, NULL); + fluid_synth_api_enter(synth); + list = fluid_list_nth(synth->sfont, num); + + if(list) + { + sfont = fluid_list_get(list); + } + + FLUID_API_RETURN(sfont); +} + +/** + * Get SoundFont by ID. + * @param synth FluidSynth instance + * @param id SoundFont ID + * @return SoundFont instance or NULL if invalid ID + * + * @note Caller should be certain that SoundFont is not deleted (unloaded) for + * the duration of use of the returned pointer. + */ +fluid_sfont_t * +fluid_synth_get_sfont_by_id(fluid_synth_t *synth, int id) +{ + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, NULL); + fluid_synth_api_enter(synth); + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == id) + { + break; + } + } + + FLUID_API_RETURN(list ? sfont : NULL); +} + +/** + * Get SoundFont by name. + * @param synth FluidSynth instance + * @param name Name of SoundFont + * @return SoundFont instance or NULL if invalid name + * @since 1.1.0 + * + * @note Caller should be certain that SoundFont is not deleted (unloaded) for + * the duration of use of the returned pointer. + */ +fluid_sfont_t * +fluid_synth_get_sfont_by_name(fluid_synth_t *synth, const char *name) +{ + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, NULL); + fluid_return_val_if_fail(name != NULL, NULL); + fluid_synth_api_enter(synth); + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0) + { + break; + } + } + + FLUID_API_RETURN(list ? sfont : NULL); +} + +/** + * Get active preset on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @return Preset or NULL if no preset active on \c chan + * + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon methods. Not thread safe otherwise. + */ +fluid_preset_t * +fluid_synth_get_channel_preset(fluid_synth_t *synth, int chan) +{ + fluid_preset_t *result; + fluid_channel_t *channel; + FLUID_API_ENTRY_CHAN(NULL); + + channel = synth->channel[chan]; + result = channel->preset; + fluid_synth_api_exit(synth); + return result; +} + +/** + * Get list of currently playing voices. + * @param synth FluidSynth instance + * @param buf Array to store voices to (NULL terminated if not filled completely) + * @param bufsize Count of indexes in buf + * @param id Voice ID to search for or < 0 to return list of all playing voices + * + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon methods. Voices are only guaranteed to remain + * unchanged until next synthesis process iteration. + */ +void +fluid_synth_get_voicelist(fluid_synth_t *synth, fluid_voice_t *buf[], int bufsize, + int id) +{ + int count = 0; + int i; + + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(buf != NULL); + fluid_synth_api_enter(synth); + + for(i = 0; i < synth->polyphony && count < bufsize; i++) + { + fluid_voice_t *voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice) && (id < 0 || (int)voice->id == id)) + { + buf[count++] = voice; + } + } + + if(count < bufsize) + { + buf[count] = NULL; + } + + fluid_synth_api_exit(synth); +} + +/** + * Enable or disable reverb effect. + * @param synth FluidSynth instance + * @param on TRUE to enable chorus, FALSE to disable + * @deprecated Use fluid_synth_reverb_on() instead. + */ +void +fluid_synth_set_reverb_on(fluid_synth_t *synth, int on) +{ + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + synth->with_reverb = (on != 0); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_reverb_enabled, + on != 0, 0.0f); + fluid_synth_api_exit(synth); +} + +/** + * Enable or disable reverb on one fx group unit. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param on TRUE to enable reverb, FALSE to disable + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_reverb_on(fluid_synth_t *synth, int fx_group, int on) +{ + int ret; + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if(fx_group < 0 ) + { + synth->with_reverb = (on != 0); + } + + param[0].i = fx_group; + param[1].i = on; + ret = fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_reverb_enable, + synth->eventhandler->mixer, + param); + + FLUID_API_RETURN(ret); +} + +/** + * Activate a reverb preset. + * @param synth FluidSynth instance + * @param num Reverb preset number + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note Currently private to libfluidsynth. + */ +int +fluid_synth_set_reverb_preset(fluid_synth_t *synth, unsigned int num) +{ + double values[FLUID_REVERB_PARAM_LAST]; + + fluid_return_val_if_fail( + num < FLUID_N_ELEMENTS(revmodel_preset), + FLUID_FAILED + ); + + values[FLUID_REVERB_ROOMSIZE] = revmodel_preset[num].roomsize; + values[FLUID_REVERB_DAMP] = revmodel_preset[num].damp; + values[FLUID_REVERB_WIDTH] = revmodel_preset[num].width; + values[FLUID_REVERB_LEVEL] = revmodel_preset[num].level; + fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); + return FLUID_OK; +} + +/** + * Set reverb parameters to all groups. + * + * @param synth FluidSynth instance + * @param roomsize Reverb room size value (0.0-1.0) + * @param damping Reverb damping value (0.0-1.0) + * @param width Reverb width value (0.0-100.0) + * @param level Reverb level value (0.0-1.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use the individual reverb setter functions in new code instead. + */ +int +fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, double damping, + double width, double level) +{ + double values[FLUID_REVERB_PARAM_LAST]; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + values[FLUID_REVERB_ROOMSIZE] = roomsize; + values[FLUID_REVERB_DAMP] = damping; + values[FLUID_REVERB_WIDTH] = width; + values[FLUID_REVERB_LEVEL] = level; + return fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); +} + +/** + * Set reverb roomsize of all groups. + * + * @param synth FluidSynth instance + * @param roomsize Reverb room size value (0.0-1.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_roomsize() in new code instead. + */ +int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize) +{ + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, roomsize); +} + +/** + * Set reverb damping of all groups. + * + * @param synth FluidSynth instance + * @param damping Reverb damping value (0.0-1.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_damp() in new code instead. + */ +int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping) +{ + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, damping); +} + +/** + * Set reverb width of all groups. + * + * @param synth FluidSynth instance + * @param width Reverb width value (0.0-100.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_width() in new code instead. + */ +int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width) +{ + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, width); +} + +/** + * Set reverb level of all groups. + * + * @param synth FluidSynth instance + * @param level Reverb level value (0.0-1.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_level() in new code instead. + */ +int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level) +{ + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, level); +} + +/** + * Set reverb roomsize to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param roomsize roomsize value to set. Must be in the range indicated by + * synth.reverb.room-size setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, + double roomsize) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize); +} + +/** + * Set reverb damp to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param damping damping value to set. Must be in the range indicated by + * synth.reverb.damp setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_set_reverb_group_damp(fluid_synth_t *synth, int fx_group, + double damping) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_DAMP, damping); +} + +/** + * Set reverb width to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param width width value to set. Must be in the range indicated by + * synth.reverb.width setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_set_reverb_group_width(fluid_synth_t *synth, int fx_group, + double width) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_WIDTH, width); +} + +/** + * Set reverb level to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param level output level to set. Must be in the range indicated by + * synth.reverb.level setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_set_reverb_group_level(fluid_synth_t *synth, int fx_group, + double level) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_LEVEL, level); +} + +/** + * Set one reverb parameter to one fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param enum indicating the parameter to set (#fluid_reverb_param). + * FLUID_REVERB_ROOMSIZE, roomsize Reverb room size value (0.0-1.0) + * FLUID_REVERB_DAMP, reverb damping value (0.0-1.0) + * FLUID_REVERB_WIDTH, reverb width value (0.0-100.0) + * FLUID_REVERB_LEVEL, reverb level value (0.0-1.0) + * @param value, parameter value + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_reverb_set_param(fluid_synth_t *synth, int fx_group, + int param, double value) +{ + int ret; + double values[FLUID_REVERB_PARAM_LAST] = {0.0}; + static const char *name[FLUID_REVERB_PARAM_LAST] = + { + "synth.reverb.room-size", "synth.reverb.damp", + "synth.reverb.width", "synth.reverb.level" + }; + + double min; /* minimum value */ + double max; /* maximum value */ + + /* check parameters */ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* check if reverb value is in max min range */ + fluid_settings_getnum_range(synth->settings, name[param], &min, &max); + if(value < min || value > max) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* set the value */ + values[param] = value; + ret = fluid_synth_set_reverb_full(synth, fx_group, FLUID_REVPARAM_TO_SETFLAG(param), values); + FLUID_API_RETURN(ret); +} + +int +fluid_synth_set_reverb_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + /* if non of the flags is set, fail */ + fluid_return_val_if_fail(set & FLUID_REVMODEL_SET_ALL, FLUID_FAILED); + + /* fx group shadow values are set here so that they will be returned if queried */ + fluid_rvoice_mixer_set_reverb_full(synth->eventhandler->mixer, fx_group, set, + values); + + /* Synth shadow values are set here so that they will be returned if queried */ + if (fx_group < 0) + { + int i; + for(i = 0; i < FLUID_REVERB_PARAM_LAST; i++) + { + if(set & FLUID_REVPARAM_TO_SETFLAG(i)) + { + synth->reverb_param[i] = values[i]; + } + } + } + + param[0].i = fx_group; + param[1].i = set; + param[2].real = values[FLUID_REVERB_ROOMSIZE]; + param[3].real = values[FLUID_REVERB_DAMP]; + param[4].real = values[FLUID_REVERB_WIDTH]; + param[5].real = values[FLUID_REVERB_LEVEL]; + /* finally enqueue an rvoice event to the mixer to actual update reverb */ + return fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_set_reverb_params, + synth->eventhandler->mixer, + param); +} + +/** + * Get reverb room size of all fx groups. + * @param synth FluidSynth instance + * @return Reverb room size (0.0-1.2) + * @deprecated Use fluid_synth_get_reverb_group_roomsize() in new code instead. + */ +double +fluid_synth_get_reverb_roomsize(fluid_synth_t *synth) +{ + double roomsize = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_ROOMSIZE, &roomsize); + return roomsize; +} + +/** + * Get reverb damping of all fx groups. + * @param synth FluidSynth instance + * @return Reverb damping value (0.0-1.0) + * @deprecated Use fluid_synth_get_reverb_group_damp() in new code instead. + */ +double +fluid_synth_get_reverb_damp(fluid_synth_t *synth) +{ + double damp = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_DAMP, &damp); + return damp; +} + +/** + * Get reverb level of all fx groups. + * @param synth FluidSynth instance + * @return Reverb level value (0.0-1.0) + * @deprecated Use fluid_synth_get_reverb_group_level() in new code instead. + */ +double +fluid_synth_get_reverb_level(fluid_synth_t *synth) +{ + double level = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_LEVEL, &level); + return level; +} + +/** + * Get reverb width of all fx groups. + * @param synth FluidSynth instance + * @return Reverb width value (0.0-100.0) + * @deprecated Use fluid_synth_get_reverb_group_width() in new code instead. + */ +double +fluid_synth_get_reverb_width(fluid_synth_t *synth) +{ + double width = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_WIDTH, &width); + return width; +} + +/** + * get reverb roomsize of one or all groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param roomsize valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_get_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, + double *roomsize) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize); +} + +/** + * get reverb damp of one or all groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param damping valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_get_reverb_group_damp(fluid_synth_t *synth, int fx_group, + double *damping) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_DAMP, damping); +} + +/** + * get reverb width of one or all groups + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param width valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_get_reverb_group_width(fluid_synth_t *synth, int fx_group, + double *width) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_WIDTH, width); +} + +/** + * get reverb level of one or all groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param level valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_get_reverb_group_level(fluid_synth_t *synth, int fx_group, + double *level) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_LEVEL, level); +} + + +/** + * Get one reverb parameter value of one fx groups. + * @param synth FluidSynth instance + * @param fx_group index of the fx group to get parameter value from. + * Must be in the range -1 to synth->effects_groups-1. If -1 get the + * parameter common to all fx groups. + * @param enum indicating the parameter to get (#fluid_reverb_param). + * FLUID_REVERB_ROOMSIZE, reverb room size value. + * FLUID_REVERB_DAMP, reverb damping value. + * FLUID_REVERB_WIDTH, reverb width value. + * FLUID_REVERB_LEVEL, reverb level value. + * @param value pointer on the value to return. + * @return FLUID_OK if success, FLUID_FAILED otherwise. + */ +static int fluid_synth_reverb_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value) +{ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED); + fluid_return_val_if_fail(value != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if (fx_group < 0) + { + /* return reverb param common to all fx groups */ + *value = synth->reverb_param[param]; + } + else + { + /* return reverb param of fx group at index fx_group */ + *value = fluid_rvoice_mixer_reverb_get_param(synth->eventhandler->mixer, + fx_group, param); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Enable or disable all chorus groups. + * @param synth FluidSynth instance + * @param on TRUE to enable chorus, FALSE to disable + * @deprecated Use fluid_synth_chorus_on() in new code instead. + */ +void +fluid_synth_set_chorus_on(fluid_synth_t *synth, int on) +{ + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + synth->with_chorus = (on != 0); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_chorus_enabled, + on != 0, 0.0f); + fluid_synth_api_exit(synth); +} + +/** + * Enable or disable chorus on one or all groups. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param on TRUE to enable chorus, FALSE to disable + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_chorus_on(fluid_synth_t *synth, int fx_group, int on) +{ + int ret; + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if(fx_group < 0 ) + { + synth->with_chorus = (on != 0); + } + + param[0].i = fx_group; + param[1].i = on; + ret = fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_chorus_enable, + synth->eventhandler->mixer, + param); + + FLUID_API_RETURN(ret); +} + +/** + * Set chorus parameters to all fx groups. + * Keep in mind, that the needed CPU time is proportional to 'nr'. + * @param synth FluidSynth instance + * @param nr Chorus voice count (0-99, CPU time consumption proportional to + * this value) + * @param level Chorus level (0.0-10.0) + * @param speed Chorus speed in Hz (0.1-5.0) + * @param depth_ms Chorus depth (max value depends on synth sample-rate, + * 0.0-21.0 is safe for sample-rate values up to 96KHz) + * @param type Chorus waveform type (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use the individual chorus setter functions in new code instead. + * + * Keep in mind, that the needed CPU time is proportional to 'nr'. + */ +int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level, + double speed, double depth_ms, int type) +{ + double values[FLUID_CHORUS_PARAM_LAST]; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + values[FLUID_CHORUS_NR] = nr; + values[FLUID_CHORUS_LEVEL] = level; + values[FLUID_CHORUS_SPEED] = speed; + values[FLUID_CHORUS_DEPTH] = depth_ms; + values[FLUID_CHORUS_TYPE] = type; + return fluid_synth_set_chorus_full(synth, -1, FLUID_CHORUS_SET_ALL, values); +} + +/** + * Set the chorus voice count of all groups. + * + * @param synth FluidSynth instance + * @param nr Chorus voice count (0-99, CPU time consumption proportional to + * this value) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_nr() in new code instead. + */ +int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr) +{ + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, nr); +} + +/** + * Set the chorus level of all groups. + * + * @param synth FluidSynth instance + * @param level Chorus level (0.0-10.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_level() in new code instead. + */ +int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level) +{ + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, level); +} + +/** + * Set the chorus speed of all groups. + * + * @param synth FluidSynth instance + * @param speed Chorus speed in Hz (0.1-5.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_speed() in new code instead. + */ +int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed) +{ + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, speed); +} + +/** + * Set the chorus depth of all groups. + * + * @param synth FluidSynth instance + * @param depth_ms Chorus depth (max value depends on synth sample-rate, + * 0.0-21.0 is safe for sample-rate values up to 96KHz) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_depth() in new code instead. + */ +int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms) +{ + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, depth_ms); +} + +/** + * Set the chorus type of all groups. + * + * @param synth FluidSynth instance + * @param type Chorus waveform type (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_type() in new code instead. + */ +int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type) +{ + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_TYPE, type); +} + +/** + * Set chorus voice count nr to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param nr Voice count to set. Must be in the range indicated by \setting{synth_chorus_nr} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_nr(fluid_synth_t *synth, int fx_group, int nr) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_NR, (double)nr); +} + +/** + * Set chorus output level to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param level Output level to set. Must be in the range indicated by \setting{synth_chorus_level} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_level(fluid_synth_t *synth, int fx_group, double level) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_LEVEL, level); +} + +/** + * Set chorus lfo speed to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param speed Lfo speed to set. Must be in the range indicated by \setting{synth_chorus_speed} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_speed(fluid_synth_t *synth, int fx_group, double speed) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_SPEED, speed); +} + +/** + * Set chorus lfo depth to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param depth_ms lfo depth to set. Must be in the range indicated by \setting{synth_chorus_depth} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_depth(fluid_synth_t *synth, int fx_group, double depth_ms) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms); +} + +/** + * Set chorus lfo waveform type to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param type Lfo waveform type to set. (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_type(fluid_synth_t *synth, int fx_group, int type) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_TYPE, (double)type); +} + +/** + * Set one chorus parameter to one fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param enum indicating the parameter to set (#fluid_chorus_param). + * FLUID_CHORUS_NR, chorus voice count (0-99, CPU time consumption proportional to + * this value). + * FLUID_CHORUS_LEVEL, chorus level (0.0-10.0). + * FLUID_CHORUS_SPEED, chorus speed in Hz (0.1-5.0). + * FLUID_CHORUS_DEPTH, chorus depth (max value depends on synth sample-rate, + * 0.0-21.0 is safe for sample-rate values up to 96KHz). + * FLUID_CHORUS_TYPE, chorus waveform type (#fluid_chorus_mod) + * @param value, parameter value + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_chorus_set_param(fluid_synth_t *synth, int fx_group, int param, + double value) +{ + int ret; + double values[FLUID_CHORUS_PARAM_LAST] = {0.0}; + + /* setting name (except lfo waveform type) */ + static const char *name[FLUID_CHORUS_PARAM_LAST-1] = + { + "synth.chorus.nr", "synth.chorus.level", + "synth.chorus.speed", "synth.chorus.depth" + }; + + /* check parameters */ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* check if chorus value is in max min range */ + if(param == FLUID_CHORUS_TYPE || param == FLUID_CHORUS_NR) /* integer value */ + { + int min = FLUID_CHORUS_MOD_SINE; + int max = FLUID_CHORUS_MOD_TRIANGLE; + if(param == FLUID_CHORUS_NR) + { + fluid_settings_getint_range(synth->settings, name[param], &min, &max); + } + if((int)value < min || (int)value > max) + { + FLUID_API_RETURN(FLUID_FAILED); + } + } + else /* float value */ + { + double min; + double max; + fluid_settings_getnum_range(synth->settings, name[param], &min, &max); + if(value < min || value > max) + { + FLUID_API_RETURN(FLUID_FAILED); + } + } + + /* set the value */ + values[param] = value; + ret = fluid_synth_set_chorus_full(synth, fx_group, + FLUID_CHORPARAM_TO_SETFLAG(param), values); + FLUID_API_RETURN(ret); +} + +int +fluid_synth_set_chorus_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + /* if non of the flags is set, fail */ + fluid_return_val_if_fail(set & FLUID_CHORUS_SET_ALL, FLUID_FAILED); + + /* fx group shadow values are set here so that they will be returned if queried */ + fluid_rvoice_mixer_set_chorus_full(synth->eventhandler->mixer, fx_group, + set, values); + + /* Synth shadow values are set here so that they will be returned if queried */ + if (fx_group < 0) + { + int i; + for(i = 0; i < FLUID_CHORUS_PARAM_LAST; i++) + { + if(set & FLUID_CHORPARAM_TO_SETFLAG(i)) + { + synth->chorus_param[i] = values[i]; + } + } + } + + param[0].i = fx_group; + param[1].i = set; + param[2].i = (int)values[FLUID_CHORUS_NR]; + param[3].real = values[FLUID_CHORUS_LEVEL]; + param[4].real = values[FLUID_CHORUS_SPEED]; + param[5].real = values[FLUID_CHORUS_DEPTH]; + param[6].i = (int)values[FLUID_CHORUS_TYPE]; + return fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_set_chorus_params, + synth->eventhandler->mixer, + param); +} + +/** + * Get chorus voice number (delay line count) value of all fx groups. + * @param synth FluidSynth instance + * @return Chorus voice count + * @deprecated Use fluid_synth_get_chorus_group_nr() in new code instead. + */ +int +fluid_synth_get_chorus_nr(fluid_synth_t *synth) +{ + double nr = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_NR, &nr); + return (int)nr; +} + +/** + * Get chorus level of all fx groups. + * @param synth FluidSynth instance + * @return Chorus level value + * @deprecated Use fluid_synth_get_chorus_group_level() in new code instead. + */ +double +fluid_synth_get_chorus_level(fluid_synth_t *synth) +{ + double level = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_LEVEL, &level); + return level; +} + +/** + * Get chorus speed in Hz of all fx groups. + * @param synth FluidSynth instance + * @return Chorus speed in Hz + * @deprecated Use fluid_synth_get_chorus_group_speed() in new code instead. + */ +double +fluid_synth_get_chorus_speed(fluid_synth_t *synth) +{ + double speed = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_SPEED, &speed); + return speed; +} + +/** + * Get chorus depth of all fx groups. + * @param synth FluidSynth instance + * @return Chorus depth + * @deprecated Use fluid_synth_get_chorus_group_depth() in new code instead. + */ +double +fluid_synth_get_chorus_depth(fluid_synth_t *synth) +{ + double depth = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_DEPTH, &depth); + return depth; +} + +/** + * Get chorus waveform type of all fx groups. + * @param synth FluidSynth instance + * @return Chorus waveform type (#fluid_chorus_mod) + * @deprecated Use fluid_synth_get_chorus_group_type() in new code instead. + */ +int +fluid_synth_get_chorus_type(fluid_synth_t *synth) +{ + double type = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_TYPE, &type); + return (int)type; +} + +/** + * Get chorus count nr of one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group from which to fetch the chorus voice count. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param nr valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_get_chorus_group_nr(fluid_synth_t *synth, int fx_group, int *nr) +{ + double num_nr = 0.0; + int status; + status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_NR, &num_nr); + *nr = (int)num_nr; + return status; +} + +/** + * Get chorus output level of one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group from which chorus level to fetch. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param level valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_get_chorus_group_level(fluid_synth_t *synth, int fx_group, double *level) +{ + return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_LEVEL, level); +} + +/** + * Get chorus waveform lfo speed of one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group from which lfo speed to fetch. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param speed valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_get_chorus_group_speed(fluid_synth_t *synth, int fx_group, double *speed) +{ + return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_SPEED, speed); +} + +/** + * Get chorus lfo depth of one or all fx groups. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group from which lfo depth to fetch. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param depth_ms valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_chorus_group_depth(fluid_synth_t *synth, int fx_group, double *depth_ms) +{ + return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms); +} + +/** + * Get chorus waveform type of one or all fx groups. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group from which to fetch the waveform type. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param type valid pointer on waveform type to return (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_chorus_group_type(fluid_synth_t *synth, int fx_group, int *type) +{ + double num_type = 0.0; + int status; + status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_TYPE, &num_type); + *type = (int)num_type; + return status; +} + +/** + * Get chorus parameter value of one or all fx groups. + * @param synth FluidSynth instance + * @param fx_group index of the fx group + * @param enum indicating the parameter to get. + * FLUID_CHORUS_NR, chorus voice count. + * FLUID_CHORUS_LEVEL, chorus level. + * FLUID_CHORUS_SPEED, chorus speed. + * FLUID_CHORUS_DEPTH, chorus depth. + * FLUID_CHORUS_TYPE, chorus waveform type. + * @param value pointer on the value to return. + * @return FLUID_OK if success, FLUID_FAILED otherwise. + */ +static int fluid_synth_chorus_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value) +{ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED); + fluid_return_val_if_fail(value != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if (fx_group < 0) + { + /* return chorus param common to all fx groups */ + *value = synth->chorus_param[param]; + } + else + { + /* return chorus param of fx group at index group */ + *value = fluid_rvoice_mixer_chorus_get_param(synth->eventhandler->mixer, + fx_group, param); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/* + * If the same note is hit twice on the same channel, then the older + * voice process is advanced to the release stage. Using a mechanical + * MIDI controller, the only way this can happen is when the sustain + * pedal is held. In this case the behaviour implemented here is + * natural for many instruments. Note: One noteon event can trigger + * several voice processes, for example a stereo sample. Don't + * release those... + */ +void +fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan, + int key) +{ + int i; + fluid_voice_t *voice; + + /* storeid is a parameter for fluid_voice_init() */ + synth->storeid = synth->noteid++; + + /* for "monophonic playing" key is the previous sustained note + if it exists (0 to 127) or INVALID_NOTE otherwise */ + if(key == INVALID_NOTE) + { + return; + } + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice) + && (fluid_voice_get_channel(voice) == chan) + && (fluid_voice_get_key(voice) == key) + && (fluid_voice_get_id(voice) != synth->noteid)) + { + /* Id of voices that was sustained by sostenuto */ + if(fluid_voice_is_sostenuto(voice)) + { + synth->storeid = fluid_voice_get_id(voice); + } + + /* Force the voice into release stage except if pedaling + (sostenuto or sustain) is active */ + fluid_voice_noteoff(voice); + } + } +} + +/** + * Set synthesis interpolation method on one or all MIDI channels. + * @param synth FluidSynth instance + * @param chan MIDI channel to set interpolation method on or -1 for all channels + * @param interp_method Interpolation method (#fluid_interp) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method) +{ + int i; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(chan < -1 || chan >= synth->midi_channels) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if(synth->channel[0] == NULL) + { + FLUID_LOG(FLUID_ERR, "Channels don't exist (yet)!"); + FLUID_API_RETURN(FLUID_FAILED); + } + + for(i = 0; i < synth->midi_channels; i++) + { + if(chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan) + { + fluid_channel_set_interp_method(synth->channel[i], interp_method); + } + } + + FLUID_API_RETURN(FLUID_OK); +}; + +/** + * Get the total count of MIDI channels. + * @param synth FluidSynth instance + * @return Count of MIDI channels + */ +int +fluid_synth_count_midi_channels(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + result = synth->midi_channels; + FLUID_API_RETURN(result); +} + +/** + * Get the total count of audio channels. + * @param synth FluidSynth instance + * @return Count of audio channel stereo pairs (1 = 2 channels, 2 = 4, etc) + */ +int +fluid_synth_count_audio_channels(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + result = synth->audio_channels; + FLUID_API_RETURN(result); +} + +/** + * Get the total number of allocated audio channels. Usually identical to the + * number of audio channels. Can be employed by LADSPA effects subsystem. + * + * @param synth FluidSynth instance + * @return Count of audio group stereo pairs (1 = 2 channels, 2 = 4, etc) + */ +int +fluid_synth_count_audio_groups(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + result = synth->audio_groups; + FLUID_API_RETURN(result); +} + +/** + * Get the total number of allocated effects channels. + * @param synth FluidSynth instance + * @return Count of allocated effects channels + */ +int +fluid_synth_count_effects_channels(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + result = synth->effects_channels; + FLUID_API_RETURN(result); +} + +/** + * Get the total number of allocated effects units. + * + * This is the same number as initially provided by the setting \setting{synth_effects-groups}. + * @param synth FluidSynth instance + * @return Count of allocated effects units + */ +int +fluid_synth_count_effects_groups(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + result = synth->effects_groups; + FLUID_API_RETURN(result); +} + +/** + * Get the synth CPU load value. + * @param synth FluidSynth instance + * @return Estimated CPU load value in percent (0-100) + */ +double +fluid_synth_get_cpu_load(fluid_synth_t *synth) +{ + fluid_return_val_if_fail(synth != NULL, 0); + return fluid_atomic_float_get(&synth->cpu_load); +} + +/* Get tuning for a given bank:program */ +static fluid_tuning_t * +fluid_synth_get_tuning(fluid_synth_t *synth, int bank, int prog) +{ + + if((synth->tuning == NULL) || + (synth->tuning[bank] == NULL) || + (synth->tuning[bank][prog] == NULL)) + { + return NULL; + } + + return synth->tuning[bank][prog]; +} + +/* Replace tuning on a given bank:program (need not already exist). + * Synth mutex should already be locked by caller. */ +static int +fluid_synth_replace_tuning_LOCK(fluid_synth_t *synth, fluid_tuning_t *tuning, + int bank, int prog, int apply) +{ + fluid_tuning_t *old_tuning; + + if(synth->tuning == NULL) + { + synth->tuning = FLUID_ARRAY(fluid_tuning_t **, 128); + + if(synth->tuning == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + FLUID_MEMSET(synth->tuning, 0, 128 * sizeof(fluid_tuning_t **)); + } + + if(synth->tuning[bank] == NULL) + { + synth->tuning[bank] = FLUID_ARRAY(fluid_tuning_t *, 128); + + if(synth->tuning[bank] == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t *)); + } + + old_tuning = synth->tuning[bank][prog]; + synth->tuning[bank][prog] = tuning; + + if(old_tuning) + { + if(!fluid_tuning_unref(old_tuning, 1)) /* -- unref old tuning */ + { + /* Replace old tuning if present */ + fluid_synth_replace_tuning_LOCAL(synth, old_tuning, tuning, apply, FALSE); + } + } + + return FLUID_OK; +} + +/* Replace a tuning with a new one in all MIDI channels. new_tuning can be + * NULL, in which case channels are reset to default equal tempered scale. */ +static void +fluid_synth_replace_tuning_LOCAL(fluid_synth_t *synth, fluid_tuning_t *old_tuning, + fluid_tuning_t *new_tuning, int apply, int unref_new) +{ + fluid_channel_t *channel; + int old_tuning_unref = 0; + int i; + + for(i = 0; i < synth->midi_channels; i++) + { + channel = synth->channel[i]; + + if(fluid_channel_get_tuning(channel) == old_tuning) + { + old_tuning_unref++; + + if(new_tuning) + { + fluid_tuning_ref(new_tuning); /* ++ ref new tuning for channel */ + } + + fluid_channel_set_tuning(channel, new_tuning); + + if(apply) + { + fluid_synth_update_voice_tuning_LOCAL(synth, channel); + } + } + } + + /* Send unref old tuning event if any unrefs */ + if(old_tuning && old_tuning_unref) + { + fluid_tuning_unref(old_tuning, old_tuning_unref); + } + + if(!unref_new || !new_tuning) + { + return; + } + + fluid_tuning_unref(new_tuning, 1); +} + +/* Update voice tunings in realtime */ +static void +fluid_synth_update_voice_tuning_LOCAL(fluid_synth_t *synth, fluid_channel_t *channel) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_on(voice) && (voice->channel == channel)) + { + fluid_voice_calculate_gen_pitch(voice); + fluid_voice_update_param(voice, GEN_PITCH); + } + } +} + +/** + * Set the tuning of the entire MIDI note scale. + * @param synth FluidSynth instance + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program + * @param name Label name for this tuning + * @param pitch Array of pitch values (length of 128, each value is number of + * cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc). + * Pass NULL to create a equal tempered (normal) scale. + * @param apply TRUE to apply new tuning in realtime to existing notes which + * are using the replaced tuning (if any), FALSE otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + */ +int +fluid_synth_activate_key_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply) +{ + fluid_tuning_t *tuning; + int retval = FLUID_OK; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + tuning = new_fluid_tuning(name, bank, prog); + + if(tuning) + { + if(pitch) + { + fluid_tuning_set_all(tuning, pitch); + } + + retval = fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, apply); + + if(retval == FLUID_FAILED) + { + fluid_tuning_unref(tuning, 1); + } + } + else + { + retval = FLUID_FAILED; + } + + FLUID_API_RETURN(retval); +} + +/** + * Activate an octave tuning on every octave in the MIDI note scale. + * @param synth FluidSynth instance + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program + * @param name Label name for this tuning + * @param pitch Array of pitch values (length of 12 for each note of an octave + * starting at note C, values are number of offset cents to add to the normal + * tuning amount) + * @param apply TRUE to apply new tuning in realtime to existing notes which + * are using the replaced tuning (if any), FALSE otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + */ +int +fluid_synth_activate_octave_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply) +{ + fluid_tuning_t *tuning; + int retval = FLUID_OK; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(pitch != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + tuning = new_fluid_tuning(name, bank, prog); + + if(tuning) + { + fluid_tuning_set_octave(tuning, pitch); + retval = fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, apply); + + if(retval == FLUID_FAILED) + { + fluid_tuning_unref(tuning, 1); + } + } + else + { + retval = FLUID_FAILED; + } + + FLUID_API_RETURN(retval); +} + +/** + * Set tuning values for one or more MIDI notes for an existing tuning. + * @param synth FluidSynth instance + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program + * @param len Number of MIDI notes to assign + * @param key Array of MIDI key numbers (length of 'len', values 0-127) + * @param pitch Array of pitch values (length of 'len', values are number of + * cents from MIDI note 0) + * @param apply TRUE to apply tuning change in realtime to existing notes using + * the specified tuning, FALSE otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note Prior to version 1.1.0 it was an error to specify a tuning that didn't + * already exist. Starting with 1.1.0, the default equal tempered scale will be + * used as a basis, if no tuning exists for the given bank and prog. + */ +int +fluid_synth_tune_notes(fluid_synth_t *synth, int bank, int prog, + int len, const int *key, const double *pitch, int apply) +{ + fluid_tuning_t *old_tuning, *new_tuning; + int retval = FLUID_OK; + int i; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail(len > 0, FLUID_FAILED); + fluid_return_val_if_fail(key != NULL, FLUID_FAILED); + fluid_return_val_if_fail(pitch != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + old_tuning = fluid_synth_get_tuning(synth, bank, prog); + + if(old_tuning) + { + new_tuning = fluid_tuning_duplicate(old_tuning); + } + else + { + new_tuning = new_fluid_tuning("Unnamed", bank, prog); + } + + if(new_tuning) + { + for(i = 0; i < len; i++) + { + fluid_tuning_set_pitch(new_tuning, key[i], pitch[i]); + } + + retval = fluid_synth_replace_tuning_LOCK(synth, new_tuning, bank, prog, apply); + + if(retval == FLUID_FAILED) + { + fluid_tuning_unref(new_tuning, 1); + } + } + else + { + retval = FLUID_FAILED; + } + + FLUID_API_RETURN(retval); +} + +/** + * Activate a tuning scale on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program + * @param apply TRUE to apply tuning change to active notes, FALSE otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + * + * @note A default equal tempered scale will be created, if no tuning exists + * on the given bank and prog. + */ +int +fluid_synth_activate_tuning(fluid_synth_t *synth, int chan, int bank, int prog, + int apply) +{ + fluid_tuning_t *tuning; + int retval = FLUID_OK; + + //fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + //fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + tuning = fluid_synth_get_tuning(synth, bank, prog); + + /* If no tuning exists, create a new default tuning. We do this, so that + * it can be replaced later, if any changes are made. */ + if(!tuning) + { + tuning = new_fluid_tuning("Unnamed", bank, prog); + + if(tuning) + { + fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, FALSE); + } + } + + if(tuning) + { + fluid_tuning_ref(tuning); /* ++ ref for outside of lock */ + } + + if(!tuning) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + fluid_tuning_ref(tuning); /* ++ ref new tuning for following function */ + retval = fluid_synth_set_tuning_LOCAL(synth, chan, tuning, apply); + + fluid_tuning_unref(tuning, 1); /* -- unref for outside of lock */ + + FLUID_API_RETURN(retval); +} + +/* Local synthesis thread set tuning function (takes over tuning reference) */ +static int +fluid_synth_set_tuning_LOCAL(fluid_synth_t *synth, int chan, + fluid_tuning_t *tuning, int apply) +{ + fluid_tuning_t *old_tuning; + fluid_channel_t *channel; + + channel = synth->channel[chan]; + + old_tuning = fluid_channel_get_tuning(channel); + fluid_channel_set_tuning(channel, tuning); /* !! Takes over callers reference */ + + if(apply) + { + fluid_synth_update_voice_tuning_LOCAL(synth, channel); + } + + /* Send unref old tuning event */ + if(old_tuning) + { + fluid_tuning_unref(old_tuning, 1); + } + + + return FLUID_OK; +} + +/** + * Clear tuning scale on a MIDI channel (use default equal tempered scale). + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param apply TRUE to apply tuning change to active notes, FALSE otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + */ +int +fluid_synth_deactivate_tuning(fluid_synth_t *synth, int chan, int apply) +{ + int retval = FLUID_OK; + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + retval = fluid_synth_set_tuning_LOCAL(synth, chan, NULL, apply); + + FLUID_API_RETURN(retval); +} + +/** + * Start tuning iteration. + * @param synth FluidSynth instance + */ +void +fluid_synth_tuning_iteration_start(fluid_synth_t *synth) +{ + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + fluid_private_set(synth->tuning_iter, FLUID_INT_TO_POINTER(0)); + fluid_synth_api_exit(synth); +} + +/** + * Advance to next tuning. + * @param synth FluidSynth instance + * @param bank Location to store MIDI bank number of next tuning scale + * @param prog Location to store MIDI program number of next tuning scale + * @return 1 if tuning iteration advanced, 0 if no more tunings + */ +int +fluid_synth_tuning_iteration_next(fluid_synth_t *synth, int *bank, int *prog) +{ + void *pval; + int b = 0, p = 0; + + fluid_return_val_if_fail(synth != NULL, 0); + fluid_return_val_if_fail(bank != NULL, 0); + fluid_return_val_if_fail(prog != NULL, 0); + fluid_synth_api_enter(synth); + + /* Current tuning iteration stored as: bank << 8 | program */ + pval = fluid_private_get(synth->tuning_iter); + p = FLUID_POINTER_TO_INT(pval); + b = (p >> 8) & 0xFF; + p &= 0xFF; + + if(!synth->tuning) + { + FLUID_API_RETURN(0); + } + + for(; b < 128; b++, p = 0) + { + if(synth->tuning[b] == NULL) + { + continue; + } + + for(; p < 128; p++) + { + if(synth->tuning[b][p] == NULL) + { + continue; + } + + *bank = b; + *prog = p; + + if(p < 127) + { + fluid_private_set(synth->tuning_iter, + FLUID_INT_TO_POINTER(b << 8 | (p + 1))); + } + else + { + fluid_private_set(synth->tuning_iter, FLUID_INT_TO_POINTER((b + 1) << 8)); + } + + FLUID_API_RETURN(1); + } + } + + FLUID_API_RETURN(0); +} + +/** + * Get the entire note tuning for a given MIDI bank and program. + * @param synth FluidSynth instance + * @param bank MIDI bank number of tuning + * @param prog MIDI program number of tuning + * @param name Location to store tuning name or NULL to ignore + * @param len Maximum number of chars to store to 'name' (including NULL byte) + * @param pitch Array to store tuning scale to or NULL to ignore (len of 128) + * @return #FLUID_OK if matching tuning was found, #FLUID_FAILED otherwise + */ +int +fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int prog, + char *name, int len, double *pitch) +{ + fluid_tuning_t *tuning; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + tuning = fluid_synth_get_tuning(synth, bank, prog); + + if(tuning) + { + if(name) + { + FLUID_SNPRINTF(name, len - 1, "%s", fluid_tuning_get_name(tuning)); + name[len - 1] = 0; /* make sure the string is null terminated */ + } + + if(pitch) + { + FLUID_MEMCPY(pitch, fluid_tuning_get_all(tuning), 128 * sizeof(double)); + } + } + + FLUID_API_RETURN(tuning ? FLUID_OK : FLUID_FAILED); +} + +/** + * Get settings assigned to a synth. + * @param synth FluidSynth instance + * @return FluidSynth settings which are assigned to the synth + */ +fluid_settings_t * +fluid_synth_get_settings(fluid_synth_t *synth) +{ + fluid_return_val_if_fail(synth != NULL, NULL); + + return synth->settings; +} + +/** + * Apply an offset to a SoundFont generator on a MIDI channel. + * + * This function allows to set an offset for the specified destination generator in real-time. + * The offset will be applied immediately to all voices that are currently and subsequently playing + * on the given MIDI channel. This functionality works equivalent to using NRPN MIDI messages to + * manipulate synthesis parameters. See SoundFont spec, paragraph 8.1.3, for details on SoundFont + * generator parameters and valid ranges, as well as paragraph 9.6 for details on NRPN messages. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param SoundFont generator ID (#fluid_gen_type) + * @param value Offset value (in native units of the generator) to assign to the MIDI channel + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_gen(fluid_synth_t *synth, int chan, int param, float value) +{ + fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + fluid_synth_set_gen_LOCAL(synth, chan, param, value); + + FLUID_API_RETURN(FLUID_OK); +} + +/* Synthesis thread local set gen function */ +static void +fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, int param, float value) +{ + fluid_voice_t *voice; + int i; + + fluid_channel_set_gen(synth->channel[chan], param, value); + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_get_channel(voice) == chan) + { + fluid_voice_set_param(voice, param, value); + } + } +} + +/** + * Retrieve the generator NRPN offset assigned to a MIDI channel. + * + * The value returned is in native units of the generator. By default, the offset is zero. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param SoundFont generator ID (#fluid_gen_type) + * @return Current NRPN generator offset value assigned to the MIDI channel + */ +float +fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param) +{ + float result; + fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + result = fluid_channel_get_gen(synth->channel[chan], param); + FLUID_API_RETURN(result); +} + +/** + * Handle MIDI event from MIDI router, used as a callback function. + * @param data FluidSynth instance + * @param event MIDI event to handle + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_handle_midi_event(void *data, fluid_midi_event_t *event) +{ + fluid_synth_t *synth = (fluid_synth_t *) data; + int type = fluid_midi_event_get_type(event); + int chan = fluid_midi_event_get_channel(event); + + switch(type) + { + case NOTE_ON: + return fluid_synth_noteon(synth, chan, + fluid_midi_event_get_key(event), + fluid_midi_event_get_velocity(event)); + + case NOTE_OFF: + return fluid_synth_noteoff(synth, chan, fluid_midi_event_get_key(event)); + + case CONTROL_CHANGE: + return fluid_synth_cc(synth, chan, + fluid_midi_event_get_control(event), + fluid_midi_event_get_value(event)); + + case PROGRAM_CHANGE: + return fluid_synth_program_change(synth, chan, fluid_midi_event_get_program(event)); + + case CHANNEL_PRESSURE: + return fluid_synth_channel_pressure(synth, chan, fluid_midi_event_get_program(event)); + + case KEY_PRESSURE: + return fluid_synth_key_pressure(synth, chan, + fluid_midi_event_get_key(event), + fluid_midi_event_get_value(event)); + + case PITCH_BEND: + return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event)); + + case MIDI_SYSTEM_RESET: + return fluid_synth_system_reset(synth); + + case MIDI_SYSEX: + return fluid_synth_sysex(synth, event->paramptr, event->param1, NULL, NULL, NULL, FALSE); + + case MIDI_TEXT: + case MIDI_LYRIC: + case MIDI_SET_TEMPO: + return FLUID_OK; + } + + return FLUID_FAILED; +} + +/** + * Create and start voices using an arbitrary preset and a MIDI note on event. + * + * Using this function is only supported when the setting @c synth.dynamic-sample-loading is false! + * @param synth FluidSynth instance + * @param id Voice group ID to use (can be used with fluid_synth_stop()). + * @param preset Preset to synthesize + * @param audio_chan Unused currently, set to 0 + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number (0-127) + * @param vel MIDI velocity number (1-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon method. + */ +int +fluid_synth_start(fluid_synth_t *synth, unsigned int id, fluid_preset_t *preset, + int audio_chan, int chan, int key, int vel) +{ + int result, dynamic_samples; + fluid_return_val_if_fail(preset != NULL, FLUID_FAILED); + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail(vel >= 1 && vel <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + fluid_settings_getint(fluid_synth_get_settings(synth), "synth.dynamic-sample-loading", &dynamic_samples); + if(dynamic_samples) + { + // The preset might not be currently used, thus its sample data may not be loaded. + // This guard is to avoid a NULL deref in rvoice_write(). + FLUID_LOG(FLUID_ERR, "Calling fluid_synth_start() while synth.dynamic-sample-loading is enabled is not supported."); + // Although we would be able to select the preset (and load it's samples) we have no way to + // unselect the preset again in fluid_synth_stop(). Also dynamic sample loading was intended + // to be used only when presets have been selected on a MIDI channel. + // Note that even if the preset is currently selected on a channel, it could be unselected at + // any time. And we would end up with a NULL sample->data again, because we are not referencing + // the preset here. Thus failure is our only option. + result = FLUID_FAILED; + } + else + { + synth->storeid = id; + result = fluid_preset_noteon(preset, synth, chan, key, vel); + } + + FLUID_API_RETURN(result); +} + +/** + * Stop notes for a given note event voice ID. + * @param synth FluidSynth instance + * @param id Voice note event ID + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note In FluidSynth versions prior to 1.1.0 #FLUID_FAILED would be returned + * if no matching voice note event ID was found. Versions after 1.1.0 only + * return #FLUID_FAILED if an error occurs. + */ +int +fluid_synth_stop(fluid_synth_t *synth, unsigned int id) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + fluid_synth_stop_LOCAL(synth, id); + result = FLUID_OK; + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of fluid_synth_stop */ +static void +fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_on(voice) && (fluid_voice_get_id(voice) == id)) + { + fluid_voice_noteoff(voice); + } + } +} + +/** + * Offset the bank numbers of a loaded SoundFont, i.e.\ subtract + * \c offset from any bank number when assigning instruments. + * + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @param offset Bank offset value to apply to all instruments + * @return #FLUID_OK if the offset was set successfully, #FLUID_FAILED otherwise + */ +int +fluid_synth_set_bank_offset(fluid_synth_t *synth, int sfont_id, int offset) +{ + fluid_sfont_t *sfont; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == sfont_id) + { + sfont->bankofs = offset; + break; + } + } + + if(!list) + { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id); + FLUID_API_RETURN(FLUID_FAILED); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Get bank offset of a loaded SoundFont. + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @return SoundFont bank offset value + */ +int +fluid_synth_get_bank_offset(fluid_synth_t *synth, int sfont_id) +{ + fluid_sfont_t *sfont; + fluid_list_t *list; + int offset = 0; + + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == sfont_id) + { + offset = sfont->bankofs; + break; + } + } + + if(!list) + { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id); + FLUID_API_RETURN(0); + } + + FLUID_API_RETURN(offset); +} + +void +fluid_synth_api_enter(fluid_synth_t *synth) +{ + if(synth->use_mutex) + { + fluid_rec_mutex_lock(synth->mutex); + } + + if(!synth->public_api_count) + { + fluid_synth_check_finished_voices(synth); + } + + synth->public_api_count++; +} + +void fluid_synth_api_exit(fluid_synth_t *synth) +{ + synth->public_api_count--; + + if(!synth->public_api_count) + { + fluid_rvoice_eventhandler_flush(synth->eventhandler); + } + + if(synth->use_mutex) + { + fluid_rec_mutex_unlock(synth->mutex); + } + +} + +/** + * Set midi channel type + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param type MIDI channel type (#fluid_midi_channel_type) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.4 + */ +int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type) +{ + fluid_return_val_if_fail((type >= CHANNEL_TYPE_MELODIC) && (type <= CHANNEL_TYPE_DRUM), FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + synth->channel[chan]->channel_type = type; + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Return the LADSPA effects instance used by FluidSynth + * + * @param synth FluidSynth instance + * @return pointer to LADSPA fx or NULL if compiled without LADSPA support or LADSPA is not active + */ +fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth) +{ + fluid_return_val_if_fail(synth != NULL, NULL); + + return synth->ladspa_fx; +} + +/** + * Configure a general-purpose IIR biquad filter. + * + * @param synth FluidSynth instance + * @param type Type of the IIR filter to use (see #fluid_iir_filter_type) + * @param flags Additional flags to customize this filter or zero to stay with the default (see #fluid_iir_filter_flags) + * @return #FLUID_OK if the settings have been successfully applied, otherwise #FLUID_FAILED + * + * This is an optional, additional filter that operates independently from the default low-pass filter required by the Soundfont2 standard. + * By default this filter is off (#FLUID_IIR_DISABLED). + */ +int fluid_synth_set_custom_filter(fluid_synth_t *synth, int type, int flags) +{ + int i; + fluid_voice_t *voice; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(type >= FLUID_IIR_DISABLED && type < FLUID_IIR_LAST, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + synth->custom_filter_type = type; + synth->custom_filter_flags = flags; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + fluid_voice_set_custom_filter(voice, type, flags); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Set the important channels for voice overflow priority calculation. + * + * @param synth FluidSynth instance + * @param channels comma-separated list of channel numbers + * @return #FLUID_OK on success, otherwise #FLUID_FAILED + */ +static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels) +{ + int i; + int retval = FLUID_FAILED; + int *values = NULL; + int num_values; + fluid_overflow_prio_t *scores; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + scores = &synth->overflow; + + if(scores->num_important_channels < synth->midi_channels) + { + scores->important_channels = FLUID_REALLOC(scores->important_channels, + sizeof(*scores->important_channels) * synth->midi_channels); + + if(scores->important_channels == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto exit; + } + + scores->num_important_channels = synth->midi_channels; + } + + FLUID_MEMSET(scores->important_channels, FALSE, + sizeof(*scores->important_channels) * scores->num_important_channels); + + if(channels != NULL) + { + values = FLUID_ARRAY(int, synth->midi_channels); + + if(values == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto exit; + } + + /* Every channel given in the comma-separated list of channel numbers + * is set to TRUE, i.e. flagging it as "important". Channel numbers are + * 1-based. */ + num_values = fluid_settings_split_csv(channels, values, synth->midi_channels); + + for(i = 0; i < num_values; i++) + { + if(values[i] > 0 && values[i] <= synth->midi_channels) + { + scores->important_channels[values[i] - 1] = TRUE; + } + } + } + + retval = FLUID_OK; + +exit: + FLUID_FREE(values); + return retval; +} + +/* + * Handler for synth.overflow.important-channels setting. + */ +static void fluid_synth_handle_important_channels(void *data, const char *name, + const char *value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + + fluid_synth_api_enter(synth); + fluid_synth_set_important_channels(synth, value); + fluid_synth_api_exit(synth); +} + + +/* API legato mode *********************************************************/ + +/** + * Sets the legato mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a legatomode is invalid. + */ +int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(legatomode >= 0, FLUID_FAILED); + fluid_return_val_if_fail(legatomode < FLUID_CHANNEL_LEGATO_MODE_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + synth->channel[chan]->legatomode = legatomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Gets the legato mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a legatomode is NULL. + */ +int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(legatomode != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * legatomode = synth->channel[chan]->legatomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/* API portamento mode *********************************************************/ + +/** + * Sets the portamento mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param portamentomode The portamento mode as indicated by #fluid_channel_portamento_mode. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a portamentomode is invalid. + */ +int fluid_synth_set_portamento_mode(fluid_synth_t *synth, int chan, + int portamentomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(portamentomode >= 0, FLUID_FAILED); + fluid_return_val_if_fail(portamentomode < FLUID_CHANNEL_PORTAMENTO_MODE_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + synth->channel[chan]->portamentomode = portamentomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Gets the portamento mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param portamentomode Pointer to the portamento mode as indicated by #fluid_channel_portamento_mode. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a portamentomode is NULL. + */ +int fluid_synth_get_portamento_mode(fluid_synth_t *synth, int chan, + int *portamentomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(portamentomode != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * portamentomode = synth->channel[chan]->portamentomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/* API breath mode *********************************************************/ + +/** + * Sets the breath mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param breathmode The breath mode as indicated by #fluid_channel_breath_flags. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + */ +int fluid_synth_set_breath_mode(fluid_synth_t *synth, int chan, int breathmode) +{ + /* checks parameters first */ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + fluid_channel_set_breath_info(synth->channel[chan], breathmode); + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Gets the breath mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param breathmode Pointer to the returned breath mode as indicated by #fluid_channel_breath_flags. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a breathmode is NULL. + */ +int fluid_synth_get_breath_mode(fluid_synth_t *synth, int chan, int *breathmode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(breathmode != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * breathmode = fluid_channel_get_breath_info(synth->channel[chan]); + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** API Poly/mono mode ******************************************************/ + +/* + * Resets a basic channel group of MIDI channels. + * @param synth the synth instance. + * @param chan the beginning channel of the group. + * @param nbr_chan the number of channel in the group. +*/ +static void +fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan) +{ + int i; + + for(i = chan; i < chan + nbr_chan; i++) + { + fluid_channel_reset_basic_channel_info(synth->channel[i]); + synth->channel[i]->mode_val = 0; + } +} + +/** + * Disables and unassigns all channels from a basic channel group. + * + * @param synth The synth instance. + * @param chan The basic channel of the group to reset or -1 to reset all channels. + * @note By default (i.e. on creation after new_fluid_synth() and after fluid_synth_system_reset()) + * a synth instance has one basic channel at channel 0 in mode #FLUID_CHANNEL_MODE_OMNION_POLY. + * All other channels belong to this basic channel group. Make sure to call this function before + * setting any custom basic channel setup. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a chan isn't a basic channel. + */ +int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan) +{ + int nbr_chan; + + /* checks parameters first */ + if(chan < 0) + { + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + /* The range is all MIDI channels from 0 to MIDI channel count -1 */ + chan = 0; /* beginning chan */ + nbr_chan = synth->midi_channels; /* MIDI Channels number */ + } + else + { + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* checks if chan is a basic channel */ + if(!(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC)) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* The range is all MIDI channels in the group from chan */ + nbr_chan = synth->channel[chan]->mode_val; /* nbr of channels in the group */ + } + + /* resets the range of MIDI channels */ + fluid_synth_reset_basic_channel_LOCAL(synth, chan, nbr_chan); + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Checks if a new basic channel group overlaps the next basic channel group. + * + * On success the function returns the possible number of channel for this + * new basic channel group. + * The function fails if the new group overlaps the next basic channel group. + * + * @param see fluid_synth_set_basic_channel. + * @return + * - On success, the effective number of channels for this new basic channel group, + * #FLUID_FAILED otherwise. + * - #FLUID_FAILED + * - \a val has a number of channels overlapping next basic channel group or been + * above MIDI channel count. + */ +static int +fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val) +{ + int i, n_chan = synth->midi_channels; /* MIDI Channels count */ + int real_val = val; /* real number of channels in the group */ + + /* adjusts val range */ + if(mode == FLUID_CHANNEL_MODE_OMNIOFF_POLY) + { + real_val = 1; /* mode poly omnioff implies a group of only one channel.*/ + } + else if(val == 0) + { + /* mode poly omnion (0), mono omnion (1), mono omni off (3) */ + /* value 0 means all possible channels from basicchan to MIDI channel count -1.*/ + real_val = n_chan - basicchan; + } + /* checks if val range is above MIDI channel count */ + else if(basicchan + val > n_chan) + { + return FLUID_FAILED; + } + + /* checks if this basic channel group overlaps next basic channel group */ + for(i = basicchan + 1; i < basicchan + real_val; i++) + { + if(synth->channel[i]->mode & FLUID_CHANNEL_BASIC) + { + /* A value of 0 for val means all possible channels from basicchan to + to the next basic channel -1 (if any). + When i reaches the next basic channel group, real_val will be + limited if it is possible */ + if(val == 0) + { + /* limitation of real_val */ + real_val = i - basicchan; + break; + } + + /* overlap with the next basic channel group */ + return FLUID_FAILED; + } + } + + return real_val; +} + +/** + * Sets a new basic channel group only. The function doesn't allow to change an + * existing basic channel. + * + * The function fails if any channel overlaps any existing basic channel group. + * To make room if necessary, basic channel groups can be cleared using + * fluid_synth_reset_basic_channel(). + * + * @param synth the synth instance. + * @param chan the basic Channel number (0 to MIDI channel count-1). + * @param mode the MIDI mode to use for chan (see #fluid_basic_channel_modes). + * @param val number of channels in the group. + * @note \a val is only relevant for mode #FLUID_CHANNEL_MODE_OMNION_POLY, + * #FLUID_CHANNEL_MODE_OMNION_MONO and #FLUID_CHANNEL_MODE_OMNIOFF_MONO. A value + * of 0 means all possible channels from \a chan to to next basic channel minus 1 (if any) + * or to MIDI channel count minus 1. Val is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY + * as this mode implies a group of only one channel. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a mode is invalid. + * - \a val has a number of channels overlapping another basic channel group or been + * above MIDI channel count. + * - When the function fails, any existing basic channels aren't modified. + */ +int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val) +{ + /* check parameters */ + fluid_return_val_if_fail(mode >= 0, FLUID_FAILED); + fluid_return_val_if_fail(mode < FLUID_CHANNEL_MODE_LAST, FLUID_FAILED); + fluid_return_val_if_fail(val >= 0, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /**/ + if(val > 0 && chan + val > synth->midi_channels) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* Checks if there is an overlap with the next basic channel */ + val = fluid_synth_check_next_basic_channel(synth, chan, mode, val); + + if(val == FLUID_FAILED || synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) + { + /* overlap with the next or previous channel group */ + FLUID_LOG(FLUID_INFO, "basic channel %d overlaps another group", chan); + FLUID_API_RETURN(FLUID_FAILED); + } + + /* sets a new basic channel group */ + fluid_synth_set_basic_channel_LOCAL(synth, chan, mode, val); + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/* + * Local version of fluid_synth_set_basic_channel(), called internally: + * - by fluid_synth_set_basic_channel() to set a new basic channel group. + * - during creation new_fluid_synth() or on CC reset to set a default basic channel group. + * - on CC ominoff, CC omnion, CC poly , CC mono to change an existing basic channel group. + * + * @param see fluid_synth_set_basic_channel() +*/ +static void +fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val) +{ + int i; + + /* sets the basic channel group */ + for(i = basicchan; i < basicchan + val; i++) + { + int new_mode = mode; /* OMNI_OFF/ON, MONO/POLY ,others bits are zero */ + int new_val; + /* MIDI specs: when mode is changed, channel must receive ALL_NOTES_OFF */ + fluid_synth_all_notes_off_LOCAL(synth, i); + + if(i == basicchan) + { + new_mode |= FLUID_CHANNEL_BASIC; /* First channel in the group */ + new_val = val; /* number of channels in the group */ + } + else + { + new_val = 0; /* val is 0 for other channel than basic channel */ + } + + /* Channel is enabled */ + new_mode |= FLUID_CHANNEL_ENABLED; + /* Now new_mode is OMNI OFF/ON,MONO/POLY, BASIC_CHANNEL or not and enabled */ + fluid_channel_set_basic_channel_info(synth->channel[i], new_mode); + synth->channel[i]->mode_val = new_val; + } +} + +/** + * Searches a previous basic channel starting from chan. + * + * @param synth the synth instance. + * @param chan starting index of the search (including chan). + * @return index of the basic channel if found , FLUID_FAILED otherwise. + */ +static int fluid_synth_get_previous_basic_channel(fluid_synth_t *synth, int chan) +{ + for(; chan >= 0; chan--) + { + /* searches previous basic channel */ + if(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC) + { + /* chan is the previous basic channel */ + return chan; + } + } + + return FLUID_FAILED; +} + +/** + * Returns poly mono mode information of any MIDI channel. + * + * @param synth the synth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param basic_chan_out Buffer to store the basic channel \a chan belongs to or #FLUID_FAILED if \a chan is disabled. + * @param mode_out Buffer to store the mode of \a chan (see #fluid_basic_channel_modes) or #FLUID_FAILED if \a chan is disabled. + * @param val_out Buffer to store the total number of channels in this basic channel group or #FLUID_FAILED if \a chan is disabled. + * @note If any of \a basic_chan_out, \a mode_out, \a val_out pointer is NULL + * the corresponding information isn't returned. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + */ +int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan, + int *basic_chan_out, + int *mode_out, + int *val_out) +{ + int basic_chan = FLUID_FAILED; + int mode = FLUID_FAILED; + int val = FLUID_FAILED; + + /* checks parameters first */ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + if((synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) && + /* chan is enabled , we search the basic channel chan belongs to */ + (basic_chan = fluid_synth_get_previous_basic_channel(synth, chan)) != FLUID_FAILED) + { + mode = synth->channel[chan]->mode & FLUID_CHANNEL_MODE_MASK; + val = synth->channel[basic_chan]->mode_val; + } + + /* returns the information if they are requested */ + if(basic_chan_out) + { + * basic_chan_out = basic_chan; + } + + if(mode_out) + { + * mode_out = mode; + } + + if(val_out) + { + * val_out = val; + } + + FLUID_API_RETURN(FLUID_OK); +} diff --git a/libs/fluidsynth/src/synth/fluid_synth.h b/libs/fluidsynth/src/synth/fluid_synth.h new file mode 100644 index 00000000000..cb838e92462 --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_synth.h @@ -0,0 +1,263 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_SYNTH_H +#define _FLUID_SYNTH_H + + +/*************************************************************** + * + * INCLUDES + */ + +#include "fluid_sys.h" +#include "fluid_list.h" +#include "fluid_rev.h" +#include "fluid_voice.h" +#include "fluid_chorus.h" +#include "fluid_ladspa.h" +#include "fluid_midi_router.h" +#include "fluid_rvoice_event.h" + +/*************************************************************** + * + * DEFINES + */ +#define FLUID_NUM_PROGRAMS 128 +#define DRUM_INST_BANK 128 + +#define FLUID_UNSET_PROGRAM 128 /* Program number used to unset a preset */ + +#define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f /**< Default reverb room size */ +#define FLUID_REVERB_DEFAULT_DAMP 0.0f /**< Default reverb damping */ +#define FLUID_REVERB_DEFAULT_WIDTH 0.5f /**< Default reverb width */ +#define FLUID_REVERB_DEFAULT_LEVEL 0.9f /**< Default reverb level */ + +#define FLUID_CHORUS_DEFAULT_N 3 /**< Default chorus voice count */ +#define FLUID_CHORUS_DEFAULT_LEVEL 2.0f /**< Default chorus level */ +#define FLUID_CHORUS_DEFAULT_SPEED 0.3f /**< Default chorus speed */ +#define FLUID_CHORUS_DEFAULT_DEPTH 8.0f /**< Default chorus depth */ +#define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE /**< Default chorus waveform type */ + +/*************************************************************** + * + * ENUM + */ + +/** + * Bank Select MIDI message styles. Default style is GS. + */ +enum fluid_midi_bank_select +{ + FLUID_BANK_STYLE_GM, /**< GM style, bank = 0 always (CC0/MSB and CC32/LSB ignored) */ + FLUID_BANK_STYLE_GS, /**< GS style, bank = CC0/MSB (CC32/LSB ignored) */ + FLUID_BANK_STYLE_XG, /**< XG style, bank = CC32/LSB (CC0/MSB ignored) */ + FLUID_BANK_STYLE_MMA /**< MMA style bank = 128*MSB+LSB */ +}; + +enum fluid_synth_status +{ + FLUID_SYNTH_CLEAN, + FLUID_SYNTH_PLAYING, + FLUID_SYNTH_QUIET, + FLUID_SYNTH_STOPPED +}; + +#define SYNTH_REVERB_CHANNEL 0 +#define SYNTH_CHORUS_CHANNEL 1 + +/* + * fluid_synth_t + * + * Mutual exclusion notes (as of 1.1.2): + * + * All variables are considered belongning to the "public API" thread, + * which processes all MIDI, except for: + * + * ticks_since_start - atomic, set by rendering thread only + * cpu_load - atomic, set by rendering thread only + * cur, curmax, dither_index - used by rendering thread only + * ladspa_fx - same instance copied in rendering thread. Synchronising handled internally. + * + */ + +struct _fluid_synth_t +{ + fluid_rec_mutex_t mutex; /**< Lock for public API */ + int use_mutex; /**< Use mutex for all public API functions? */ + int public_api_count; /**< How many times the mutex is currently locked */ + + fluid_settings_t *settings; /**< the synthesizer settings */ + int device_id; /**< Device ID used for SYSEX messages */ + int polyphony; /**< Maximum polyphony */ + int with_reverb; /**< Should the synth use the built-in reverb unit? */ + int with_chorus; /**< Should the synth use the built-in chorus unit? */ + int verbose; /**< Turn verbose mode on? */ + double sample_rate; /**< The sample rate */ + int midi_channels; /**< the number of MIDI channels (>= 16) */ + int bank_select; /**< the style of Bank Select MIDI messages */ + int audio_channels; /**< the number of audio channels (1 channel=left+right) */ + int audio_groups; /**< the number of (stereo) 'sub'groups from the synth. + Typically equal to audio_channels. */ + int effects_channels; /**< the number of effects channels (>= 2) */ + int effects_groups; /**< the number of effects units (>= 1) */ + int state; /**< the synthesizer state */ + fluid_atomic_uint_t ticks_since_start; /**< the number of audio samples since the start */ + unsigned int start; /**< the start in msec, as returned by system clock */ + fluid_overflow_prio_t overflow; /**< parameters for overflow priority (aka voice-stealing) */ + + fluid_list_t *loaders; /**< the SoundFont loaders */ + fluid_list_t *sfont; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */ + int sfont_id; /**< Incrementing ID assigned to each loaded SoundFont */ + fluid_list_t *fonts_to_be_unloaded; /**< list of timers that try to unload a soundfont */ + + float gain; /**< master gain */ + fluid_channel_t **channel; /**< the channels */ + int nvoice; /**< the length of the synthesis process array (max polyphony allowed) */ + fluid_voice_t **voice; /**< the synthesis voices */ + int active_voice_count; /**< count of active voices */ + unsigned int noteid; /**< the id is incremented for every new note. it's used for noteoff's */ + unsigned int storeid; + int fromkey_portamento; /**< fromkey portamento */ + fluid_rvoice_eventhandler_t *eventhandler; + + /**< Shadow of reverb parameter: roomsize, damping, width, level */ + double reverb_param[FLUID_REVERB_PARAM_LAST]; + + /**< Shadow of chorus parameter: chorus number, level, speed, depth, type */ + double chorus_param[FLUID_CHORUS_PARAM_LAST]; + + int cur; /**< the current sample in the audio buffers to be output */ + int curmax; /**< current amount of samples present in the audio buffers */ + int dither_index; /**< current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */ + + fluid_atomic_float_t cpu_load; /**< CPU load in percent (CPU time required / audio synthesized time * 100) */ + + fluid_tuning_t ***tuning; /**< 128 banks of 128 programs for the tunings */ + fluid_private_t tuning_iter; /**< Tuning iterators per each thread */ + + fluid_sample_timer_t *sample_timers; /**< List of timers triggered before a block is processed */ + unsigned int min_note_length_ticks; /**< If note-offs are triggered just after a note-on, they will be delayed */ + + int cores; /**< Number of CPU cores (1 by default) */ + + fluid_mod_t *default_mod; /**< the (dynamic) list of default modulators */ + + fluid_ladspa_fx_t *ladspa_fx; /**< Effects unit for LADSPA support */ + enum fluid_iir_filter_type custom_filter_type; /**< filter type of the user-defined filter currently used for all voices */ + enum fluid_iir_filter_flags custom_filter_flags; /**< filter type of the user-defined filter currently used for all voices */ +}; + +/** + * Type definition of the synthesizer's audio callback function. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param out1 Array to store left channel of audio to + * @param loff Offset index in 'out1' for first sample + * @param lincr Increment between samples stored to 'out1' + * @param out2 Array to store right channel of audio to + * @param roff Offset index in 'out2' for first sample + * @param rincr Increment between samples stored to 'out2' + */ +typedef int (*fluid_audio_callback_t)(fluid_synth_t *synth, int len, + void *out1, int loff, int lincr, + void *out2, int roff, int rincr); + +typedef int (*fluid_audio_channels_callback_t)(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]); + +int +fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[], + int (*block_render_func)(fluid_synth_t *, int)); + +int +fluid_synth_write_s16_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]); +int +fluid_synth_write_float_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]); + +fluid_preset_t *fluid_synth_find_preset(fluid_synth_t *synth, + int banknum, + int prognum); +void fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont); + +void fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const float *rin, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr); + +int fluid_synth_reset_reverb(fluid_synth_t *synth); +int fluid_synth_set_reverb_preset(fluid_synth_t *synth, unsigned int num); +int fluid_synth_reverb_set_param(fluid_synth_t *synth, int fx_group, + int param, + double value); +int fluid_synth_set_reverb_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]); + +int fluid_synth_reset_chorus(fluid_synth_t *synth); +int fluid_synth_chorus_set_param(fluid_synth_t *synth, int fx_group, + int param, double value); +int fluid_synth_set_chorus_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]); + +fluid_sample_timer_t *new_fluid_sample_timer(fluid_synth_t *synth, fluid_timer_callback_t callback, void *data); +void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer); +void fluid_sample_timer_reset(fluid_synth_t *synth, fluid_sample_timer_t *timer); + +void fluid_synth_process_event_queue(fluid_synth_t *synth); + +int +fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], + int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int)); +int +fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr, + int (*block_render_func)(fluid_synth_t *, int)); +/* + * misc + */ +void fluid_synth_settings(fluid_settings_t *settings); +void fluid_synth_set_sample_rate_immediately(fluid_synth_t *synth, float sample_rate); + + +/* extern declared in fluid_synth_monopoly.c */ + +int fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int vel); +int fluid_synth_noteon_mono_LOCAL(fluid_synth_t *synth, int chan, int key, int vel); +int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t *synth, int chan, int key); +int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, int fromkey, int tokey, int vel); +int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, char Mono); + +fluid_voice_t * +fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range); + +void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan, int key); +#endif /* _FLUID_SYNTH_H */ diff --git a/libs/fluidsynth/src/synth/fluid_synth_monopoly.c b/libs/fluidsynth/src/synth/fluid_synth_monopoly.c new file mode 100644 index 00000000000..d1de13196ec --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_synth_monopoly.c @@ -0,0 +1,722 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_synth.h" +#include "fluid_chan.h" +#include "fluid_defsfont.h" + + +/****************************************************************************** + The legato detector is composed as this, + variables: + - monolist: monophonic list variable. + - prev_note: to store the most recent note before adding on noteon or before + removing on noteoff. + - FLUID_CHANNEL_LEGATO_PLAYING: legato/staccato state bit that informs on + legato or staccato playing. + functions: + - fluid_channel_add_monolist(), for inserting a new note. + - fluid_channel_search_monolist(), for searching the position of a note + into the list. + - fluid_channel_remove_monolist(), for removing a note from the list. + + The monophonic list + +------------------------------------------------+ + | +----+ +----+ +----+ +----+ | + | |note| |note| |note| |note| | + +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+ + +----+ +----+ +----+ +----+ + /|\ /|\ + | | + i_first i_last + + The list allows an easy automatic detection of a legato passage when it is + played on a MIDI keyboard input device. + It is useful also when the input device is an ewi (electronic wind instrument) + or evi (electronic valve instrument) and these instruments are unable to send + MIDI CC legato on/off. + + The list memorizes the notes in playing order. + - (a) On noteOn n2, if a previous note n1 exists, there is a legato + detection with n1 (with or without portamento from n1 to n2 See note below). + - (b) On noteOff of the running note n2, if a previous note n1 exists, + there is a legato detection from n2 to n1, allowing fast trills playing + (with or without portamento from n2 to n1. See note below). + + Notes in the list are inserted to the end of the list that works like a + circular buffer.The features are: + + 1) It is always possible to play an infinite legato passage in + direct order (n1_On,n2_On,n3_On,....). + + 2) Playing legato in the reverse order (n10_Off, n9_Off,,...) helps in + fast trills playing as the list memorizes 10 most recent notes. + + 3) Playing an infinite lagato passage in ascendant or descendant order, + without playing trills is always possible using the usual way like this: + First we begin with an ascendant passage, + n1On, (n2On,n1Off), (n3On,n2Off) , (n4On,n3Off), then + we continue with a descendant passage + (n3On,n4off), (n2On,n3off), (n1On,n2off), n1Off...and so on + + Each MIDI channel have a legato detector. + + Note: + Portamento is a feature independent of the legato detector. So + portamento isn't part of the lagato detector. However portamento + (when enabled) is triggered at noteOn (like legato). Like in legato + situation it is usual to have a portamento from a note 'fromkey' to another + note 'tokey'. Portamento fromkey note choice is determined at noteOn by + fluid_synth_get_fromkey_portamento_legato() (see below). + + More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). +******************************************************************************/ + + +/***************************************************************************** + Portamento related functions in Poly or Mono mode +******************************************************************************/ + +/** + * fluid_synth_get_fromkey_portamento_legato returns two information: + * - fromkey note for portamento. + * - fromkey note for legato. + * +-----> fromkey_portamento + * ______|________ + * portamento modes >------->| | + * | get_fromkey | + * Porta.on/off >------------------------->|_______________| + * (PTC) | + * +-----> fromkey_legato + * + * The functions is intended to be call on noteOn mono + * see fluid_synth_noteon_mono_staccato(), fluid_synth_noteon_monopoly_legato() + * ------- + * 1)The function determines if a portamento must occur on next noteOn. + * The value returned is 'fromkey portamento' which is the pitchstart key + * of a portamento, as function of PTC or (default_fromkey, prev_note) both + * if Portamento On. By order of precedence the result is: + * 1.1) PTC have precedence over Portamento On. + * If CC PTC has been received, its value supersedes and any + * portamento pedal On, default_fromkey,prev_note or portamento mode. + * 1.2) Otherwise ,when Portamento On the function takes the following value: + * - default_fromkey if valid + * - otherwise prev_note(prev_note is the note prior the most recent + * note played). + * Then portamento mode is applied to validate the value chosen. + * Where portamento mode is: + * - each note, a portamento occurs on each note. + * - legato only, portamento only on notes played legato. + * - staccato only, portamento only on notes played staccato. + * 1.3) Otherwise, portamento is off,INVALID_NOTE is returned (portamento is disabled). + * ------ + * 2)The function determines if a legato playing must occur on next noteOn. + * 'fromkey legato note' is returned as a function of default_fromkey, PTC, + * current mono/poly mode,actual 'staccato/legato' playing state and prev_note. + * By order of precedence the result is: + * 2.1) If valid, default_fromkey have precedence over any others values. + * 2.2) Otherwise if CC PTC has been received its value is returned. + * 2.3) Otherwise fromkey legato is determined from the mono/poly mode, + * the actual 'staccato/legato' playing state (FLUID_CHANNEL_LEGATO_PLAYING) and prev_note + * as this: + * - in (poly/Mono) staccato , INVALID_NOTE is returned. + * - in poly legato , actually we don't want playing legato. So + * INVALID_NOTE is returned. + * - in mono legato , prev_note is returned. + * + * On input + * @param chan fluid_channel_t. + * @param defaultFromkey, the default 'fromkey portamento' note or 'fromkey legato' + * note (see description above). + * + * @return + * 1)'fromkey portamento' is returned in fluid_synth_t.fromkey_portamento. + * If valid,it means that portamento is enabled . + * + * 2)The 'fromkey legato' note is returned. + * + * Notes about usage: + * The function is intended to be called when the following event occurs: + * - On noteOn (Poly or Mono) after insertion in the monophonic list. + * - On noteOff(mono legato playing). In this case, default_fromkey must be valid. + * + * Typical calling usage: + * - In poly, default_fromkey must be INVALID_NOTE. + * - In mono staccato playing,default_fromkey must be INVALID_NOTE. + * - In mono legato playing,default_fromkey must be valid. + */ +static char fluid_synth_get_fromkey_portamento_legato(fluid_channel_t *chan, + int default_fromkey) +{ + unsigned char ptc = fluid_channel_get_cc(chan, PORTAMENTO_CTRL); + + if(fluid_channel_is_valid_note(ptc)) + { + /* CC PTC has been received */ + fluid_channel_clear_portamento(chan); /* clears the CC PTC receive */ + chan->synth->fromkey_portamento = ptc;/* returns fromkey portamento */ + + /* returns fromkey legato */ + if(!fluid_channel_is_valid_note(default_fromkey)) + { + default_fromkey = ptc; + } + } + else + { + /* determines and returns fromkey portamento */ + unsigned char fromkey_portamento = INVALID_NOTE; + + if(fluid_channel_portamento(chan)) + { + /* Portamento when Portamento pedal is On */ + /* 'fromkey portamento'is determined from the portamento mode + and the most recent note played (prev_note)*/ + enum fluid_channel_portamento_mode portamentomode = chan->portamentomode; + + if(fluid_channel_is_valid_note(default_fromkey)) + { + fromkey_portamento = default_fromkey; /* on each note */ + } + else + { + fromkey_portamento = fluid_channel_prev_note(chan); /* on each note */ + } + + if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY) + { + /* Mode portamento:legato only */ + if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + { + fromkey_portamento = INVALID_NOTE; + } + } + else if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY) + { + /* Mode portamento:staccato only */ + if(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { + fromkey_portamento = INVALID_NOTE; + } + } + + /* else Mode portamento: on each note (staccato/legato) */ + } + + /* Returns fromkey portamento */ + chan->synth->fromkey_portamento = fromkey_portamento; + + /* Determines and returns fromkey legato */ + if(!fluid_channel_is_valid_note(default_fromkey)) + { + /* in staccato (poly/Mono) returns INVALID_NOTE */ + /* In mono mode legato playing returns the note prior most + recent note played */ + if(fluid_channel_is_playing_mono(chan) && (chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + { + default_fromkey = fluid_channel_prev_note(chan); /* note prior last note */ + } + + /* In poly mode legato playing, actually we don't want playing legato. + So returns INVALID_NOTE */ + } + } + + return default_fromkey; /* Returns legato fromkey */ +} + +/***************************************************************************** + noteon - noteoff functions in Mono mode +******************************************************************************/ +/* + * noteon - noteoff on a channel in "monophonic playing". + * + * A channel needs to be played monophonic if this channel has been set in + * monophonic mode by basic channel API.(see fluid_synth_polymono.c). + * A channel needs also to be played monophonic if it has been set in + * polyphonic mode and legato pedal is On during the playing. + * When a channel is in "monophonic playing" state, only one note at a time can be + * played in a staccato or legato manner (with or without portamento). + * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). + * _______________ + * ________________ | noteon | + * | legato detector| O-->| mono_staccato |--*-> preset_noteon + * noteon_mono ->| (add_monolist) |--O-- |_______________| | (with or without) + * LOCAL |________________| O /|\ | (portamento) + * /|\ set_onenote | | fromkey | + * | | | portamento| + * noteOn poly >---*------------------* | | + * | | | + * | _____ |________ | + * portamento modes >--- | ->| | | + * | | get_fromkey | | + * Porta.on/off >--------------------- | ->|_______________| | + * (PTC) | | | + * | fromkey | fromkey | + * | legato | portamento| + * | _____\|/_______ | + * *-->| noteon |--/ + * | | monopoly | + * | | legato |----> voices + * legato modes >------- | ->|_______________| triggering + * | (with or without) + * | (portamento) + * | + * | + * noteOff poly >---*----------------- | ---------+ + * | clear | | + * _\|/_____________ | | + * | legato detector | O | + * noteoff_mono->|(search_monolist)|-O-- _____\|/_______ + * LOCAL |(remove_monolist)| O-->| noteoff | + * |_________________| | monopoly |----> noteoff + * Sust.on/off >------------------------->|_______________| + * Sost.on/off +------------------------------------------------------------------------------*/ + +/** + * Plays a noteon event for a Synth instance in "monophonic playing" state. + * Please see the description above about "monophonic playing". + * _______________ + * ________________ | noteon | + * | legato detector| O-->| mono_staccato |--->preset_noteon + * noteon_mono ->| (add_monolist) |--O-- |_______________| + * LOCAL |________________| O + * | + * | + * | + * | + * | + * | + * | + * | + * | + * | _______________ + * | | noteon | + * +-->| monopoly | + * | legato |---> voices + * |_______________| triggering + * + * The function uses the legato detector (see above) to determine if the note must + * be played staccato or legato. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int fluid_synth_noteon_mono_LOCAL(fluid_synth_t *synth, int chan, + int key, int vel) +{ + fluid_channel_t *channel = synth->channel[chan]; + + /* Adds the note into the monophonic list */ + fluid_channel_add_monolist(channel, key, vel, 0); + + /* in Breath Sync mode, the noteon triggering is postponed + until the musician starts blowing in the breath controller */ + if(!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) || + fluid_channel_breath_msb(channel)) + { + /* legato/staccato playing detection */ + if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { + /* legato playing */ + /* legato from prev_note to key */ + /* the voices from prev_note key number are to be used to play key number */ + /* fromkey must be valid */ + return fluid_synth_noteon_monopoly_legato(synth, chan, + fluid_channel_prev_note(channel), key, vel); + } + else + { + /* staccato playing */ + return fluid_synth_noteon_mono_staccato(synth, chan, key, vel); + } + } + else + { + return FLUID_OK; + } +} + +/** + * Plays a noteoff event for a Synth instance in "monophonic playing" state. + * Please see the description above about "monophonic playing" + * + * _______________ + * | noteon | + * +-->| monopoly | + * | | legato |----> voices + * | |_______________| triggering + * | (with or without) + * | (portamento) + * | + * | + * | + * | + * | + * | + * _________________ | + * | legato detector | O + * noteoff_mono->|(search_monolist)|-O-- _______________ + * LOCAL |(remove_monolist)| O-->| noteoff | + * |_________________| | monopoly |----> noteoff + * |_______________| + * + * The function uses the legato detector (see above) to determine if the noteoff must + * be played staccato or legato. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t *synth, int chan, int key) +{ + int status; + int i, i_prev; + fluid_channel_t *channel = synth->channel[chan]; + /* searching the note in the monophonic list */ + i = fluid_channel_search_monolist(channel, key, &i_prev); + + if(i >= 0) + { + /* the note is in the monophonic list */ + /* Removes the note from the monophonic list */ + fluid_channel_remove_monolist(channel, i, &i_prev); + + /* in Breath Sync mode, the noteoff triggering is done + if the musician is blowing in the breath controller */ + if(!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) || + fluid_channel_breath_msb(channel)) + { + /* legato playing detection */ + if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { + /* the list contains others notes */ + if(i_prev >= 0) + { + /* legato playing detection on noteoff */ + /* legato from key to i_prev key */ + /* the voices from key number are to be used to + play i_prev key number. */ + status = fluid_synth_noteon_monopoly_legato(synth, chan, + key, channel->monolist[i_prev].note, + channel->monolist[i_prev].vel); + } + /* else the note doesn't need to be played off */ + else + { + status = FLUID_OK; + } + } + else + { + /* the monophonic list is empty */ + /* plays the monophonic note noteoff and eventually held + by sustain/sostenuto */ + status = fluid_synth_noteoff_monopoly(synth, chan, key, 1); + } + } + else + { + status = FLUID_OK; + } + } + else + { + /* the note is not found in the list so the note was + played On when the channel was in polyphonic playing */ + /* plays the noteoff as for polyphonic */ + status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); + } + + return status; +} + +/*---------------------------------------------------------------------------- + staccato playing +-----------------------------------------------------------------------------*/ +/** + * Plays noteon for a monophonic note in staccato manner. + * Please see the description above about "monophonic playing". + * _______________ + * | noteon | + * noteon_mono >------------------------>| mono_staccato |----> preset_noteon + * |_______________| (with or without) + * LOCAL /|\ (portamento) + * | fromkey + * | portamento + * | + * | + * ______|________ + * portamento modes >----->| | + * | get_fromkey | + * Porta.on/off >----------------------->|_______________| + * Portamento + * (PTC) + * + * We are in staccato situation (where no previous note have been depressed). + * Before the note been passed to fluid_preset_noteon(), the function must determine + * the from_key_portamento parameter used by fluid_preset_noteon(). + * + * from_key_portamento is returned by fluid_synth_get_fromkey_portamento_legato() function. + * fromkey_portamento is set to valid/invalid key value depending of the portamento + * modes (see portamento mode API) , CC portamento On/Off , and CC portamento control + * (PTC). + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int +fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int vel) +{ + fluid_channel_t *channel = synth->channel[chan]; + + /* Before playing a new note, if a previous monophonic note is currently + sustained it needs to be released */ + fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, channel->key_mono_sustained); + /* Get possible 'fromkey portamento' */ + fluid_synth_get_fromkey_portamento_legato(channel, INVALID_NOTE); + /* The note needs to be played by voices allocation */ + return fluid_preset_noteon(channel->preset, synth, chan, key, vel); +} + +/** + * Plays noteoff for a polyphonic or monophonic note + * Please see the description above about "monophonic playing". + * + * + * noteOff poly >---------------------------------+ + * | + * | + * | + * noteoff_mono _____\|/_______ + * LOCAL >------------------------->| noteoff | + * | monopoly |----> noteoff + * Sust.on/off >------------------------->|_______________| + * Sost.on/off + * + * The function has the same behaviour when the noteoff is poly of mono, except + * that for mono noteoff, if any pedal (sustain or sostenuto ) is depressed, the + * key is memorized. This is necessary when the next mono note will be played + * staccato, as any current mono note currently sustained will need to be released + * (see fluid_synth_noteon_mono_staccato()). + * Note also that for a monophonic legato passage, the function is called only when + * the last noteoff of the passage occurs. That means that if sustain or sostenuto + * is depressed, only the last note of a legato passage will be sustained. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param Mono, 1 noteoff on monophonic note. + * 0 noteoff on polyphonic note. + * @return FLUID_OK on success, FLUID_FAILED otherwise. + * + * Note: On return, on monophonic, possible sustained note is memorized in + * key_mono_sustained. Memorization is done here on noteOff. + */ +int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, + char Mono) +{ + int status = FLUID_FAILED; + fluid_voice_t *voice; + int i; + fluid_channel_t *channel = synth->channel[chan]; + + /* Key_sustained is prepared to return no note sustained (INVALID_NOTE) */ + if(Mono) + { + channel->key_mono_sustained = INVALID_NOTE; /* no mono note sustained */ + } + + /* noteoff for all voices with same chan and same key */ + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_on(voice) && + fluid_voice_get_channel(voice) == chan && + fluid_voice_get_key(voice) == key) + { + if(synth->verbose) + { + int used_voices = 0; + int k; + + for(k = 0; k < synth->polyphony; k++) + { + if(!_AVAILABLE(synth->voice[k])) + { + used_voices++; + } + } + + FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%d", + fluid_voice_get_channel(voice), fluid_voice_get_key(voice), 0, + fluid_voice_get_id(voice), + (fluid_curtime() - synth->start) / 1000.0f, + used_voices); + } /* if verbose */ + + fluid_voice_noteoff(voice); + + /* noteoff on monophonic note */ + /* Key memorization if the note is sustained */ + if(Mono && + (fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice))) + { + channel->key_mono_sustained = key; + } + + status = FLUID_OK; + } /* if voice on */ + } /* for all voices */ + + return status; +} + +/*---------------------------------------------------------------------------- + legato playing +-----------------------------------------------------------------------------*/ +/** + * Plays noteon for a monophonic note played legato. + * Please see the description above about "monophonic playing". + * + * + * _______________ + * portamento modes >----->| | + * | get_fromkey | + * Porta.on/off >----------------------->|_______________| + * Portamento | + * (PTC) | +-->preset_noteon + * fromkey | fromkey | (with or without) + * legato | portamento| (portamento) + * _____\|/_______ | + * | noteon |--+ + * noteon_mono >------------------------>| monopoly | + * LOCAL | legato |----->voices + * |_______________| triggering + * /|\ (with or without) + * | (portamento) + * legato modes >-----------------+ + * + * We are in legato situation (where a previous note has been depressed). + * The function must determine the from_key_portamento and from_key_legato parameters + * used respectively by fluid_preset_noteon() function and voices triggering functions. + * + * from_key_portamento and from_key_legato are returned by + * fluid_synth_get_fromkey_portamento_legato() function. + * fromkey_portamento is set to valid/invalid key value depending of the portamento + * modes (see portamento mode API), CC portamento On/Off, and CC portamento control + * (PTC). + * Then, depending of the legato modes (see legato mode API), the function will call + * the appropriate triggering functions. + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param fromkey MIDI note number (0-127). + * @param tokey MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + * + * Note: The voices with key 'fromkey' are to be used to play key 'tokey'. + * The function is able to play legato through Preset Zone(s) (PZ) and + * Instrument Zone(s) (IZ) as far as possible. + * When key tokey is outside the current Instrument Zone, Preset Zone, + * current 'fromkey' voices are released. If necessary new voices + * are restarted when tokey enters inside new Instrument(s) Zones,Preset Zone(s). + * More information in FluidPolyMono-0004.pdf chapter 4.7 (Appendices). + */ +int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, + int fromkey, int tokey, int vel) +{ + fluid_channel_t *channel = synth->channel[chan]; + enum fluid_channel_legato_mode legatomode = channel->legatomode; + fluid_voice_t *voice; + int i ; + /* Gets possible 'fromkey portamento' and possible 'fromkey legato' note */ + fromkey = fluid_synth_get_fromkey_portamento_legato(channel, fromkey); + + if(fluid_channel_is_valid_note(fromkey)) + { + for(i = 0; i < synth->polyphony; i++) + { + /* searching fromkey voices: only those who don't have 'note off' */ + voice = synth->voice[i]; + + if(fluid_voice_is_on(voice) && + fluid_voice_get_channel(voice) == chan && + fluid_voice_get_key(voice) == fromkey) + { + fluid_zone_range_t *zone_range = voice->zone_range; + + /* Ignores voice when there is no instrument zone (i.e no zone_range). Otherwise + checks if tokey is inside the range of the running voice */ + if(zone_range && fluid_zone_inside_range(zone_range, tokey, vel)) + { + switch(legatomode) + { + case FLUID_CHANNEL_LEGATO_MODE_RETRIGGER: /* mode 0 */ + fluid_voice_release(voice); /* normal release */ + break; + + case FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER: /* mode 1 */ + /* Skip in attack section */ + fluid_voice_update_multi_retrigger_attack(voice, tokey, vel); + + /* Starts portamento if enabled */ + if(fluid_channel_is_valid_note(synth->fromkey_portamento)) + { + /* Sends portamento parameters to the voice dsp */ + fluid_voice_update_portamento(voice, + synth->fromkey_portamento, + tokey); + } + + /* The voice is now used to play tokey in legato manner */ + /* Marks this Instrument Zone to be ignored during next + fluid_preset_noteon() */ + zone_range->ignore = TRUE; + break; + + default: /* Invalid mode: this should never happen */ + FLUID_LOG(FLUID_WARN, "Failed to execute legato mode: %d", + legatomode); + return FLUID_FAILED; + } + } + else + { + /* tokey note is outside the voice range, so the voice is released */ + fluid_voice_release(voice); + } + } + } + } + + /* May be,tokey will enter in new others Insrument Zone(s),Preset Zone(s), in + this case it needs to be played by voices allocation */ + return fluid_preset_noteon(channel->preset, synth, chan, tokey, vel); +} diff --git a/libs/fluidsynth/src/synth/fluid_tuning.c b/libs/fluidsynth/src/synth/fluid_tuning.c new file mode 100644 index 00000000000..0df248b7bfb --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_tuning.c @@ -0,0 +1,190 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#include "fluid_tuning.h" +#include "fluid_sys.h" + + +fluid_tuning_t *new_fluid_tuning(const char *name, int bank, int prog) +{ + fluid_tuning_t *tuning; + int i; + + tuning = FLUID_NEW(fluid_tuning_t); + + if(tuning == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(tuning, 0, sizeof(fluid_tuning_t)); + + if(fluid_tuning_set_name(tuning, name) != FLUID_OK) + { + delete_fluid_tuning(tuning); + return NULL; + } + + tuning->bank = bank; + tuning->prog = prog; + + for(i = 0; i < 128; i++) + { + tuning->pitch[i] = i * 100.0; + } + + fluid_atomic_int_set(&tuning->refcount, 1); /* Start with a refcount of 1 */ + + return tuning; +} + +/* Duplicate a tuning */ +fluid_tuning_t * +fluid_tuning_duplicate(fluid_tuning_t *tuning) +{ + fluid_tuning_t *new_tuning; + int i; + + new_tuning = FLUID_NEW(fluid_tuning_t); + + if(!new_tuning) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(new_tuning, 0, sizeof(fluid_tuning_t)); + + if(fluid_tuning_set_name(new_tuning, tuning->name) != FLUID_OK) + { + delete_fluid_tuning(new_tuning); + return NULL; + } + + new_tuning->bank = tuning->bank; + new_tuning->prog = tuning->prog; + + for(i = 0; i < 128; i++) + { + new_tuning->pitch[i] = tuning->pitch[i]; + } + + fluid_atomic_int_set(&new_tuning->refcount, 1); /* Start with a refcount of 1 */ + + return new_tuning; +} + +void +delete_fluid_tuning(fluid_tuning_t *tuning) +{ + fluid_return_if_fail(tuning != NULL); + + FLUID_FREE(tuning->name); + FLUID_FREE(tuning); +} + +/* Add a reference to a tuning object */ +void +fluid_tuning_ref(fluid_tuning_t *tuning) +{ + fluid_return_if_fail(tuning != NULL); + + fluid_atomic_int_inc(&tuning->refcount); +} + +/* Unref a tuning object, when it reaches 0 it is deleted, returns TRUE if deleted */ +int +fluid_tuning_unref(fluid_tuning_t *tuning, int count) +{ + fluid_return_val_if_fail(tuning != NULL, FALSE); + + /* Add and compare are separate, but that is OK, since refcount will only + * reach 0 when there are no references and therefore no possibility of + * another thread adding a reference in between */ + fluid_atomic_int_add(&tuning->refcount, -count); + + /* Delete when refcount reaches 0 */ + if(!fluid_atomic_int_get(&tuning->refcount)) + { + delete_fluid_tuning(tuning); + return TRUE; + } + else + { + return FALSE; + } +} + +int fluid_tuning_set_name(fluid_tuning_t *tuning, const char *name) +{ + if(tuning->name != NULL) + { + FLUID_FREE(tuning->name); + tuning->name = NULL; + } + + if(name != NULL) + { + tuning->name = FLUID_STRDUP(name); + + if(tuning->name == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + } + + return FLUID_OK; +} + +char *fluid_tuning_get_name(fluid_tuning_t *tuning) +{ + return tuning->name; +} + +void fluid_tuning_set_octave(fluid_tuning_t *tuning, const double *pitch_deriv) +{ + int i; + + for(i = 0; i < 128; i++) + { + tuning->pitch[i] = i * 100.0 + pitch_deriv[i % 12]; + } +} + +void fluid_tuning_set_all(fluid_tuning_t *tuning, const double *pitch) +{ + int i; + + for(i = 0; i < 128; i++) + { + tuning->pitch[i] = pitch[i]; + } +} + +void fluid_tuning_set_pitch(fluid_tuning_t *tuning, int key, double pitch) +{ + if((key >= 0) && (key < 128)) + { + tuning->pitch[key] = pitch; + } +} diff --git a/libs/fluidsynth/src/synth/fluid_tuning.h b/libs/fluidsynth/src/synth/fluid_tuning.h new file mode 100644 index 00000000000..542d2ced68f --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_tuning.h @@ -0,0 +1,69 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/* + + More information about micro tuning can be found at: + + https://www.midi.org/about-midi/tuning.htm + https://www.midi.org/about-midi/tuning-scale.htm + https://www.midi.org/about-midi/tuning_extens.htm + +*/ + +#ifndef _FLUID_TUNING_H +#define _FLUID_TUNING_H + +#include "fluidsynth_priv.h" + +struct _fluid_tuning_t +{ + char *name; + int bank; + int prog; + double pitch[128]; /* the pitch of every key, in cents */ + fluid_atomic_int_t refcount; /* Tuning reference count */ +}; + +fluid_tuning_t *new_fluid_tuning(const char *name, int bank, int prog); +void delete_fluid_tuning(fluid_tuning_t *tuning); +fluid_tuning_t *fluid_tuning_duplicate(fluid_tuning_t *tuning); +void fluid_tuning_ref(fluid_tuning_t *tuning); +int fluid_tuning_unref(fluid_tuning_t *tuning, int count); + +int fluid_tuning_set_name(fluid_tuning_t *tuning, const char *name); +char *fluid_tuning_get_name(fluid_tuning_t *tuning); + +#define fluid_tuning_get_bank(_t) ((_t)->bank) +#define fluid_tuning_get_prog(_t) ((_t)->prog) + +void fluid_tuning_set_pitch(fluid_tuning_t *tuning, int key, double pitch); +#define fluid_tuning_get_pitch(_t, _key) ((_t)->pitch[_key]) + +void fluid_tuning_set_octave(fluid_tuning_t *tuning, const double *pitch_deriv); + +void fluid_tuning_set_all(fluid_tuning_t *tuning, const double *pitch); +#define fluid_tuning_get_all(_t) (&(_t)->pitch[0]) + + + + +#endif /* _FLUID_TUNING_H */ diff --git a/libs/fluidsynth/src/synth/fluid_voice.c b/libs/fluidsynth/src/synth/fluid_voice.c new file mode 100644 index 00000000000..2361e78c18d --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_voice.c @@ -0,0 +1,2051 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sys.h" +#include "fluid_voice.h" +#include "fluid_mod.h" +#include "fluid_chan.h" +#include "fluid_conv.h" +#include "fluid_synth.h" +#include "fluid_sys.h" +#include "fluid_sfont.h" +#include "fluid_rvoice_event.h" +#include "fluid_defsfont.h" + +/* used for filter turn off optimization - if filter cutoff is above the + specified value and filter q is below the other value, turn filter off */ +#define FLUID_MAX_AUDIBLE_FILTER_FC 19000.0f +#define FLUID_MIN_AUDIBLE_FILTER_Q 1.2f + +/* min vol envelope release (to stop clicks) in SoundFont timecents */ +#define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */ + + +static const int32_t INT24_MAX = (1 << (16 + 8 - 1)); + +static int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t *voice); +static int calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base, + int gen_key2base, int is_decay); +static fluid_real_t +fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice); + +#define UPDATE_RVOICE0(proc) \ + do { \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, voice->rvoice, param); \ + } while (0) + +#define UPDATE_RVOICE_GENERIC_R1(proc, obj, rarg) \ + do { \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].real = rarg; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ + } while (0) + +#define UPDATE_RVOICE_GENERIC_I1(proc, obj, iarg) \ + do { \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].i = iarg; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ + } while (0) + +#define UPDATE_RVOICE_GENERIC_I2(proc, obj, iarg1, iarg2) \ + do { \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].i = iarg1; \ + param[1].i = iarg2; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ + } while (0) + +#define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \ + do { \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].i = iarg; \ + param[1].real = rarg; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ + } while (0) + + +#define UPDATE_RVOICE_R1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, voice->rvoice, arg1) +#define UPDATE_RVOICE_I1(proc, arg1) UPDATE_RVOICE_GENERIC_I1(proc, voice->rvoice, arg1) + +#define UPDATE_RVOICE_BUFFERS_AMP(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->buffers, iarg, rarg) +#define UPDATE_RVOICE_ENVLFO_R1(proc, envp, rarg) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->envlfo.envp, rarg) +#define UPDATE_RVOICE_ENVLFO_I1(proc, envp, iarg) UPDATE_RVOICE_GENERIC_I1(proc, &voice->rvoice->envlfo.envp, iarg) + +static FLUID_INLINE void +fluid_voice_update_volenv(fluid_voice_t *voice, + int enqueue, + fluid_adsr_env_section_t section, + unsigned int count, + fluid_real_t coeff, + fluid_real_t increment, + fluid_real_t min, + fluid_real_t max) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + param[0].i = section; + param[1].i = count; + param[2].real = coeff; + param[3].real = increment; + param[4].real = min; + param[5].real = max; + + if(enqueue) + { + fluid_rvoice_eventhandler_push(voice->eventhandler, + fluid_adsr_env_set_data, + &voice->rvoice->envlfo.volenv, + param); + } + else + { + fluid_adsr_env_set_data(&voice->rvoice->envlfo.volenv, param); + } +} + +static FLUID_INLINE void +fluid_voice_update_modenv(fluid_voice_t *voice, + int enqueue, + fluid_adsr_env_section_t section, + unsigned int count, + fluid_real_t coeff, + fluid_real_t increment, + fluid_real_t min, + fluid_real_t max) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + param[0].i = section; + param[1].i = count; + param[2].real = coeff; + param[3].real = increment; + param[4].real = min; + param[5].real = max; + + if(enqueue) + { + fluid_rvoice_eventhandler_push(voice->eventhandler, + fluid_adsr_env_set_data, + &voice->rvoice->envlfo.modenv, + param); + } + else + { + fluid_adsr_env_set_data(&voice->rvoice->envlfo.modenv, param); + } +} + +static FLUID_INLINE void fluid_voice_sample_unref(fluid_sample_t **sample) +{ + if(*sample != NULL) + { + fluid_sample_decr_ref(*sample); + *sample = NULL; + } +} + +/* + * Swaps the current rvoice with the current overflow_rvoice + */ +static void fluid_voice_swap_rvoice(fluid_voice_t *voice) +{ + fluid_rvoice_t *rtemp = voice->rvoice; + int ctemp = voice->can_access_rvoice; + voice->rvoice = voice->overflow_rvoice; + voice->can_access_rvoice = voice->can_access_overflow_rvoice; + voice->overflow_rvoice = rtemp; + voice->can_access_overflow_rvoice = ctemp; + voice->overflow_sample = voice->sample; +} + +static void fluid_voice_initialize_rvoice(fluid_voice_t *voice, fluid_real_t output_rate) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + FLUID_MEMSET(voice->rvoice, 0, sizeof(fluid_rvoice_t)); + + /* The 'sustain' and 'finished' segments of the volume / modulation + * envelope are constant. They are never affected by any modulator + * or generator. Therefore it is enough to initialize them once + * during the lifetime of the synth. + */ + fluid_voice_update_volenv(voice, FALSE, FLUID_VOICE_ENVSUSTAIN, + 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); + fluid_voice_update_volenv(voice, FALSE, FLUID_VOICE_ENVFINISHED, + 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); + fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVSUSTAIN, + 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); + fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVFINISHED, + 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); + + param[0].i = FLUID_IIR_LOWPASS; + param[1].i = 0; + fluid_iir_filter_init(&voice->rvoice->resonant_filter, param); + + param[0].i = FLUID_IIR_DISABLED; + fluid_iir_filter_init(&voice->rvoice->resonant_custom_filter, param); + + param[0].real = output_rate; + fluid_rvoice_set_output_rate(voice->rvoice, param); +} + +/* + * new_fluid_voice + */ +fluid_voice_t * +new_fluid_voice(fluid_rvoice_eventhandler_t *handler, fluid_real_t output_rate) +{ + fluid_voice_t *voice; + voice = FLUID_NEW(fluid_voice_t); + + if(voice == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + voice->can_access_rvoice = TRUE; + voice->can_access_overflow_rvoice = TRUE; + + voice->rvoice = FLUID_NEW(fluid_rvoice_t); + voice->overflow_rvoice = FLUID_NEW(fluid_rvoice_t); + + if(voice->rvoice == NULL || voice->overflow_rvoice == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + delete_fluid_voice(voice); + return NULL; + } + + voice->status = FLUID_VOICE_CLEAN; + voice->chan = NO_CHANNEL; + voice->key = 0; + voice->vel = 0; + voice->eventhandler = handler; + voice->channel = NULL; + voice->sample = NULL; + voice->overflow_sample = NULL; + voice->output_rate = output_rate; + + /* Initialize both the rvoice and overflow_rvoice */ + fluid_voice_initialize_rvoice(voice, output_rate); + fluid_voice_swap_rvoice(voice); + fluid_voice_initialize_rvoice(voice, output_rate); + + return voice; +} + +/* + * delete_fluid_voice + */ +void +delete_fluid_voice(fluid_voice_t *voice) +{ + fluid_return_if_fail(voice != NULL); + + if(!voice->can_access_rvoice || !voice->can_access_overflow_rvoice) + { + FLUID_LOG(FLUID_WARN, "Deleting voice %u which has locked rvoices!", voice->id); + } + + FLUID_FREE(voice->overflow_rvoice); + FLUID_FREE(voice->rvoice); + FLUID_FREE(voice); +} + +/* fluid_voice_init + * + * Initialize the synthesis process + * inst_zone, the Instrument Zone contains the sample, Keyrange,Velrange + * of the voice. + * When playing legato (n1,n2) in mono mode, n2 will use n1 voices + * as far as n2 still enters in Keyrange,Velrange of n1. + */ +int +fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample, + fluid_zone_range_t *inst_zone_range, + fluid_channel_t *channel, int key, int vel, unsigned int id, + unsigned int start_time, fluid_real_t gain) +{ + /* Note: The voice parameters will be initialized later, when the + * generators have been retrieved from the sound font. Here, only + * the 'working memory' of the voice (position in envelopes, history + * of IIR filters, position in sample etc) is initialized. */ + int i; + + if(!voice->can_access_rvoice) + { + if(voice->can_access_overflow_rvoice) + { + fluid_voice_swap_rvoice(voice); + } + else + { + FLUID_LOG(FLUID_ERR, "Internal error: Cannot access an rvoice in fluid_voice_init!"); + return FLUID_FAILED; + } + } + + /* We are now guaranteed to have access to the rvoice */ + + if(voice->sample) + { + fluid_voice_off(voice); + } + + voice->zone_range = inst_zone_range; /* Instrument zone range for legato */ + voice->id = id; + voice->chan = fluid_channel_get_num(channel); + voice->key = (unsigned char) key; + voice->vel = (unsigned char) vel; + voice->channel = channel; + voice->mod_count = 0; + voice->start_time = start_time; + voice->has_noteoff = 0; + UPDATE_RVOICE0(fluid_rvoice_reset); + + /* + We increment the reference count of the sample to indicate that this + sample is about to be owned by the rvoice. This will prevent the + unloading of the soundfont while this rvoice is playing. + */ + fluid_sample_incr_ref(sample); + fluid_rvoice_eventhandler_push_ptr(voice->eventhandler, fluid_rvoice_set_sample, voice->rvoice, sample); + voice->sample = sample; + + i = fluid_channel_get_interp_method(channel); + UPDATE_RVOICE_I1(fluid_rvoice_set_interp_method, i); + + /* Set all the generators to their default value, according to SF + * 2.01 section 8.1.3 (page 48). The value of NRPN messages are + * copied from the channel to the voice's generators. The sound font + * loader overwrites them. The generator values are later converted + * into voice parameters in + * fluid_voice_calculate_runtime_synthesis_parameters. */ + fluid_gen_init(&voice->gen[0], channel); + UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, _SAMPLEMODE(voice)); + + voice->synth_gain = gain; + + /* avoid division by zero later*/ + if(voice->synth_gain < 0.0000001f) + { + voice->synth_gain = 0.0000001f; + } + + UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain); + + /* Set up buffer mapping, should be done more flexible in the future. */ + i = 2 * channel->synth->audio_groups; + i += (voice->chan % channel->synth->effects_groups) * channel->synth->effects_channels; + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 2, i + SYNTH_REVERB_CHANNEL); + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 3, i + SYNTH_CHORUS_CHANNEL); + + i = 2 * (voice->chan % channel->synth->audio_groups); + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 0, i); + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 1, i + 1); + + return FLUID_OK; +} + + +/** + * Update sample rate. + * + * @note If the voice is active, it will be turned off. + */ +void +fluid_voice_set_output_rate(fluid_voice_t *voice, fluid_real_t value) +{ + if(fluid_voice_is_playing(voice)) + { + fluid_voice_off(voice); + } + + voice->output_rate = value; + UPDATE_RVOICE_GENERIC_R1(fluid_rvoice_set_output_rate, voice->rvoice, value); + UPDATE_RVOICE_GENERIC_R1(fluid_rvoice_set_output_rate, voice->overflow_rvoice, value); +} + + +/** + * Set the value of a generator. + * + * @param voice Voice instance + * @param i Generator ID (#fluid_gen_type) + * @param val Generator value + */ +void +fluid_voice_gen_set(fluid_voice_t *voice, int i, float val) +{ + voice->gen[i].val = val; + voice->gen[i].flags = GEN_SET; + + if(i == GEN_SAMPLEMODE) + { + UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, (int) val); + } +} + +/** + * Offset the value of a generator. + * + * @param voice Voice instance + * @param i Generator ID (#fluid_gen_type) + * @param val Value to add to the existing value + */ +void +fluid_voice_gen_incr(fluid_voice_t *voice, int i, float val) +{ + voice->gen[i].val += val; + voice->gen[i].flags = GEN_SET; +} + +/** + * Get the value of a generator. + * + * @param voice Voice instance + * @param gen Generator ID (#fluid_gen_type) + * @return Current generator value + */ +float +fluid_voice_gen_get(fluid_voice_t *voice, int gen) +{ + return voice->gen[gen].val; +} + +fluid_real_t fluid_voice_gen_value(const fluid_voice_t *voice, int num) +{ + return (fluid_real_t)(voice->gen[num].val + voice->gen[num].mod + voice->gen[num].nrpn); +} + +/* + * fluid_voice_start + */ +void fluid_voice_start(fluid_voice_t *voice) +{ + /* The maximum volume of the loop is calculated and cached once for each + * sample with its nominal loop settings. This happens, when the sample is used + * for the first time.*/ + + fluid_voice_calculate_runtime_synthesis_parameters(voice); + +#ifdef WITH_PROFILING + voice->ref = fluid_profile_ref(); +#endif + + voice->status = FLUID_VOICE_ON; + + /* Increment voice count */ + voice->channel->synth->active_voice_count++; +} + +/** + * Calculate the amplitude of a voice. + * + * @param gain The gain value in the range [0.0 ; 1.0] + * @return An amplitude used by rvoice_mixer's buffers + */ +static FLUID_INLINE fluid_real_t +fluid_voice_calculate_gain_amplitude(const fluid_voice_t *voice, fluid_real_t gain) +{ + /* we use 24bit samples in fluid_rvoice_dsp. in order to normalize float + * samples to [0.0;1.0] divide samples by the max. value of an int24 and + * amplify them with the gain */ + return gain * voice->synth_gain / (INT24_MAX * 1.0f); +} + +/* Useful to return the nominal pitch of a key */ +/* The nominal pitch is dependent of voice->root_pitch,tuning, and + GEN_SCALETUNE generator. + This is useful to set the value of GEN_PITCH generator on noteOn. + This is useful to get the beginning/ending pitch for portamento. +*/ +fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t *voice, int key) +{ + fluid_tuning_t *tuning; + fluid_real_t x, pitch; + + /* Now the nominal pitch of the key is returned. + * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a + * non-realtime parameter. So we don't allow modulation (as opposed + * to fluid_voice_gen_value(voice, GEN_SCALETUNE) When the scale tuning is varied, + * one key remains fixed. Here C3 (MIDI number 60) is used. + */ + if(fluid_channel_has_tuning(voice->channel)) + { + tuning = fluid_channel_get_tuning(voice->channel); + x = fluid_tuning_get_pitch(tuning, (int)(voice->root_pitch / 100.0f)); + pitch = voice->gen[GEN_SCALETUNE].val / 100.0f * + (fluid_tuning_get_pitch(tuning, key) - x) + x; + } + else + { + pitch = voice->gen[GEN_SCALETUNE].val + * (key - voice->root_pitch / 100.0f) + voice->root_pitch; + } + + return pitch; +} + +void +fluid_voice_calculate_gen_pitch(fluid_voice_t *voice) +{ + voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, fluid_voice_get_actual_key(voice)); +} + +/* + * fluid_voice_calculate_runtime_synthesis_parameters + * + * in this function we calculate the values of all the parameters. the + * parameters are converted to their most useful unit for the DSP + * algorithm, for example, number of samples instead of + * timecents. Some parameters keep their "perceptual" unit and + * conversion will be done in the DSP function. This is the case, for + * example, for the pitch since it is modulated by the controllers in + * cents. */ +static int +fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t *voice) +{ + int i; + unsigned int n; + + static int const list_of_generators_to_initialize[] = + { + GEN_STARTADDROFS, /* SF2.01 page 48 #0 */ + GEN_ENDADDROFS, /* #1 */ + GEN_STARTLOOPADDROFS, /* #2 */ + GEN_ENDLOOPADDROFS, /* #3 */ + /* GEN_STARTADDRCOARSEOFS see comment below [1] #4 */ + GEN_MODLFOTOPITCH, /* #5 */ + GEN_VIBLFOTOPITCH, /* #6 */ + GEN_MODENVTOPITCH, /* #7 */ + GEN_FILTERFC, /* #8 */ + GEN_FILTERQ, /* #9 */ + GEN_MODLFOTOFILTERFC, /* #10 */ + GEN_MODENVTOFILTERFC, /* #11 */ + /* GEN_ENDADDRCOARSEOFS [1] #12 */ + GEN_MODLFOTOVOL, /* #13 */ + /* not defined #14 */ + GEN_CHORUSSEND, /* #15 */ + GEN_REVERBSEND, /* #16 */ + GEN_PAN, /* #17 */ + /* not defined #18 */ + /* not defined #19 */ + /* not defined #20 */ + GEN_MODLFODELAY, /* #21 */ + GEN_MODLFOFREQ, /* #22 */ + GEN_VIBLFODELAY, /* #23 */ + GEN_VIBLFOFREQ, /* #24 */ + GEN_MODENVDELAY, /* #25 */ + GEN_MODENVATTACK, /* #26 */ + GEN_MODENVHOLD, /* #27 */ + GEN_MODENVDECAY, /* #28 */ + /* GEN_MODENVSUSTAIN [1] #29 */ + GEN_MODENVRELEASE, /* #30 */ + /* GEN_KEYTOMODENVHOLD [1] #31 */ + /* GEN_KEYTOMODENVDECAY [1] #32 */ + GEN_VOLENVDELAY, /* #33 */ + GEN_VOLENVATTACK, /* #34 */ + GEN_VOLENVHOLD, /* #35 */ + GEN_VOLENVDECAY, /* #36 */ + /* GEN_VOLENVSUSTAIN [1] #37 */ + GEN_VOLENVRELEASE, /* #38 */ + /* GEN_KEYTOVOLENVHOLD [1] #39 */ + /* GEN_KEYTOVOLENVDECAY [1] #40 */ + /* GEN_STARTLOOPADDRCOARSEOFS [1] #45 */ + GEN_KEYNUM, /* #46 */ + GEN_VELOCITY, /* #47 */ + GEN_ATTENUATION, /* #48 */ + /* GEN_ENDLOOPADDRCOARSEOFS [1] #50 */ + /* GEN_COARSETUNE [1] #51 */ + /* GEN_FINETUNE [1] #52 */ + GEN_OVERRIDEROOTKEY, /* #58 */ + GEN_PITCH, /* --- */ + GEN_CUSTOM_BALANCE, /* --- */ + GEN_CUSTOM_FILTERFC, /* --- */ + GEN_CUSTOM_FILTERQ /* --- */ + }; + + /* When the voice is made ready for the synthesis process, a lot of + * voice-internal parameters have to be calculated. + * + * At this point, the sound font has already set the -nominal- value + * for all generators (excluding GEN_PITCH). Most generators can be + * modulated - they include a nominal value and an offset (which + * changes with velocity, note number, channel parameters like + * aftertouch, mod wheel...) Now this offset will be calculated as + * follows: + * + * - Process each modulator once. + * - Calculate its output value. + * - Find the target generator. + * - Add the output value to the modulation value of the generator. + * + * Note: The generators have been initialized with + * fluid_gen_init(). + */ + + for(i = 0; i < voice->mod_count; i++) + { + fluid_mod_t *mod = &voice->mod[i]; + fluid_real_t modval = fluid_mod_get_value(mod, voice); + int dest_gen_index = mod->dest; + fluid_gen_t *dest_gen = &voice->gen[dest_gen_index]; + dest_gen->mod += modval; + /* fluid_dump_modulator(mod); */ + } + + /* Now the generators are initialized, nominal and modulation value. + * The voice parameters (which depend on generators) are calculated + * with fluid_voice_update_param. Processing the list of generator + * changes will calculate each voice parameter once. + * + * Note [1]: Some voice parameters depend on several generators. For + * example, the pitch depends on GEN_COARSETUNE, GEN_FINETUNE and + * GEN_PITCH. voice->pitch. Unnecessary recalculation is avoided + * by removing all but one generator from the list of voice + * parameters. Same with GEN_XXX and GEN_XXXCOARSE: the + * initialisation list contains only GEN_XXX. + */ + + /* Calculate the voice parameter(s) dependent on each generator. */ + for(n = 0; n < FLUID_N_ELEMENTS(list_of_generators_to_initialize); n++) + { + fluid_voice_update_param(voice, list_of_generators_to_initialize[n]); + } + + /* Start portamento if enabled */ + { + /* fromkey note comes from "GetFromKeyPortamentoLegato()" detector. + When fromkey is set to ValidNote , portamento is started */ + /* Return fromkey portamento */ + int fromkey = voice->channel->synth->fromkey_portamento; + + if(fluid_channel_is_valid_note(fromkey)) + { + /* Send portamento parameters to the voice dsp */ + fluid_voice_update_portamento(voice, fromkey, fluid_voice_get_actual_key(voice)); + } + } + + /* Make an estimate on how loud this voice can get at any time (attenuation). */ + UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB, + fluid_voice_get_lower_boundary_for_attenuation(voice)); + return FLUID_OK; +} + +/* + * calculate_hold_decay_buffers + */ +static int +calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base, + int gen_key2base, int is_decay) +{ + /* Purpose: + * + * Returns the number of DSP loops, that correspond to the hold + * (is_decay=0) or decay (is_decay=1) time. + * gen_base=GEN_VOLENVHOLD, GEN_VOLENVDECAY, GEN_MODENVHOLD, + * GEN_MODENVDECAY gen_key2base=GEN_KEYTOVOLENVHOLD, + * GEN_KEYTOVOLENVDECAY, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY + */ + + fluid_real_t keysteps; + fluid_real_t timecents; + fluid_real_t seconds; + int buffers; + + /* SF2.01 section 8.4.3 # 31, 32, 39, 40 + * GEN_KEYTOxxxENVxxx uses key 60 as 'origin'. + * The unit of the generator is timecents per key number. + * If KEYTOxxxENVxxx is 100, a key one octave over key 60 (72) + * will cause (60-72)*100=-1200 timecents of time variation. + * The time is cut in half. + */ + + keysteps = 60.0f - fluid_channel_get_key_pitch(voice->channel, fluid_voice_get_actual_key(voice)) / 100.0f; + timecents = fluid_voice_gen_value(voice, gen_base) + fluid_voice_gen_value(voice, gen_key2base) * keysteps; + + /* Range checking */ + if(is_decay) + { + /* SF 2.01 section 8.1.3 # 28, 36 */ + if(timecents > 8000.f) + { + timecents = 8000.f; + } + } + else + { + /* SF 2.01 section 8.1.3 # 27, 35 */ + if(timecents > 5000.f) + { + timecents = 5000.f; + } + + /* SF 2.01 section 8.1.2 # 27, 35: + * The most negative number indicates no hold time + */ + if(timecents <= -32768.f) + { + return 0; + } + } + + /* SF 2.01 section 8.1.3 # 27, 28, 35, 36 */ + if(timecents < -12000.f) + { + timecents = -12000.f; + } + + seconds = fluid_tc2sec(timecents); + /* Each DSP loop processes FLUID_BUFSIZE samples. */ + + /* round to next full number of buffers */ + buffers = (int)(((fluid_real_t)voice->output_rate * seconds) + / (fluid_real_t)FLUID_BUFSIZE + + 0.5f); + + return buffers; +} + +/* + * The value of a generator (gen) has changed. (The different + * generators are listed in fluidsynth.h, or in SF2.01 page 48-49) + * Now the dependent 'voice' parameters are calculated. + * + * fluid_voice_update_param can be called during the setup of the + * voice (to calculate the initial value for a voice parameter), or + * during its operation (a generator has been changed due to + * real-time parameter modifications like pitch-bend). + * + * Note: The generator holds three values: The base value .val, an + * offset caused by modulators .mod, and an offset caused by the + * NRPN system. fluid_voice_gen_value(voice, generator_enumerator) returns the sum + * of all three. + */ + +/** + * Update all the synthesis parameters which depend on generator \a gen. + * + * @param voice Voice instance + * @param gen Generator id (#fluid_gen_type) + * + * Calling this function is only necessary after changing a generator of an already playing voice. + */ +void +fluid_voice_update_param(fluid_voice_t *voice, int gen) +{ + unsigned int count, z; + fluid_real_t x = fluid_voice_gen_value(voice, gen); + + switch(gen) + { + + case GEN_PAN: + case GEN_CUSTOM_BALANCE: + /* range checking is done in the fluid_pan and fluid_balance functions */ + voice->pan = fluid_voice_gen_value(voice, GEN_PAN); + voice->balance = fluid_voice_gen_value(voice, GEN_CUSTOM_BALANCE); + + /* left amp */ + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0, + fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1))); + + /* right amp */ + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1, + fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0))); + break; + + case GEN_ATTENUATION: + voice->attenuation = x; + + /* Range: SF2.01 section 8.1.3 # 48 + * Motivation for range checking: + * OHPiano.SF2 sets initial attenuation to a whooping -96 dB */ + fluid_clip(voice->attenuation, 0.f, 1440.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_attenuation, voice->attenuation); + break; + + /* The pitch is calculated from three different generators. + * Read comment in fluidsynth.h about GEN_PITCH. + */ + case GEN_PITCH: + case GEN_COARSETUNE: + case GEN_FINETUNE: + /* The testing for allowed range is done in 'fluid_ct2hz' */ + voice->pitch = (fluid_voice_gen_value(voice, GEN_PITCH) + + 100.0f * fluid_voice_gen_value(voice, GEN_COARSETUNE) + + fluid_voice_gen_value(voice, GEN_FINETUNE)); + UPDATE_RVOICE_R1(fluid_rvoice_set_pitch, voice->pitch); + break; + + case GEN_REVERBSEND: + /* The generator unit is 'tenths of a percent'. */ + voice->reverb_send = x / 1000.0f; + fluid_clip(voice->reverb_send, 0.f, 1.f); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send)); + break; + + case GEN_CHORUSSEND: + /* The generator unit is 'tenths of a percent'. */ + voice->chorus_send = x / 1000.0f; + fluid_clip(voice->chorus_send, 0.f, 1.f); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send)); + break; + + case GEN_OVERRIDEROOTKEY: + + /* This is a non-realtime parameter. Therefore the .mod part of the generator + * can be neglected. + * NOTE: origpitch sets MIDI root note while pitchadj is a fine tuning amount + * which offsets the original rate. This means that the fine tuning is + * inverted with respect to the root note (so subtract it, not add). + */ + if(voice->sample != NULL) + { + if(voice->gen[GEN_OVERRIDEROOTKEY].val > -1) //FIXME: use flag instead of -1 + { + voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f + - voice->sample->pitchadj; + } + else + { + voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj; + } + + x = (fluid_ct2hz_real(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate)); + } + else + { + if(voice->gen[GEN_OVERRIDEROOTKEY].val > -1) //FIXME: use flag instead of -1 + { + voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f; + } + else + { + voice->root_pitch = 0; + } + + x = fluid_ct2hz_real(voice->root_pitch); + } + + /* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */ + fluid_voice_calculate_gen_pitch(voice); + UPDATE_RVOICE_R1(fluid_rvoice_set_root_pitch_hz, x); + + break; + + case GEN_FILTERFC: + /* The resonance frequency is converted from absolute cents to + * midicents .val and .mod are both used, this permits real-time + * modulation. The allowed range is tested in the 'fluid_ct2hz' + * function [PH,20021214] + */ + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_filter, x); + break; + + case GEN_FILTERQ: + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_filter, x); + break; + + /* same as the two above, only for the custom filter */ + case GEN_CUSTOM_FILTERFC: + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_custom_filter, x); + break; + + case GEN_CUSTOM_FILTERQ: + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_custom_filter, x); + break; + + case GEN_MODLFOTOPITCH: + fluid_clip(x, -12000.f, 12000.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x); + break; + + case GEN_MODLFOTOVOL: + fluid_clip(x, -960.f, 960.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x); + break; + + case GEN_MODLFOTOFILTERFC: + fluid_clip(x, -12000.f, 12000.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x); + break; + + case GEN_MODLFODELAY: + fluid_clip(x, -12000.0f, 5000.0f); + z = (unsigned int)(voice->output_rate * fluid_tc2sec_delay(x)); + UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, modlfo, z); + break; + + case GEN_MODLFOFREQ: + /* - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples + * - the delay into a sample delay + */ + fluid_clip(x, -16000.0f, 4500.0f); + x = (4.0f * FLUID_BUFSIZE * fluid_ct2hz_real(x) / voice->output_rate); + UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, modlfo, x); + break; + + case GEN_VIBLFOFREQ: + /* vib lfo + * + * - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples + * - the delay into a sample delay + */ + fluid_clip(x, -16000.0f, 4500.0f); + x = 4.0f * FLUID_BUFSIZE * fluid_ct2hz_real(x) / voice->output_rate; + UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, viblfo, x); + break; + + case GEN_VIBLFODELAY: + fluid_clip(x, -12000.0f, 5000.0f); + z = (unsigned int)(voice->output_rate * fluid_tc2sec_delay(x)); + UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, viblfo, z); + break; + + case GEN_VIBLFOTOPITCH: + fluid_clip(x, -12000.f, 12000.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_viblfo_to_pitch, x); + break; + + case GEN_KEYNUM: + /* GEN_KEYNUM: SF2.01 page 46, item 46 + * + * If this generator is active, it forces the key number to its + * value. Non-realtime controller. + * + * There is a flag, which should indicate, whether a generator is + * enabled or not. But here we rely on the default value of -1. + */ + + /* 2017-09-02: do not change the voice's key here, otherwise it will + * never be released on a noteoff event + */ +#if 0 + x = fluid_voice_gen_value(voice, GEN_KEYNUM); + + if(x >= 0) + { + voice->key = x; + } + +#endif + break; + + case GEN_VELOCITY: + /* GEN_VELOCITY: SF2.01 page 46, item 47 + * + * If this generator is active, it forces the velocity to its + * value. Non-realtime controller. + * + * There is a flag, which should indicate, whether a generator is + * enabled or not. But here we rely on the default value of -1. + */ + /* 2017-09-02: do not change the voice's velocity here, use + * fluid_voice_get_actual_velocity() to get the value of this generator + * if active. + */ +#if 0 + x = fluid_voice_gen_value(voice, GEN_VELOCITY); + + if(x > 0) + { + voice->vel = x; + } + +#endif + break; + + case GEN_MODENVTOPITCH: + fluid_clip(x, -12000.f, 12000.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x); + break; + + case GEN_MODENVTOFILTERFC: + /* Range: SF2.01 section 8.1.3 # 1 + * Motivation for range checking: + * Filter is reported to make funny noises now and then + */ + fluid_clip(x, -12000.f, 12000.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_fc, x); + break; + + + /* sample start and ends points + * + * Range checking is initiated via the + * voice->check_sample_sanity flag, + * because it is impossible to check here: + * During the voice setup, all modulators are processed, while + * the voice is inactive. Therefore, illegal settings may + * occur during the setup (for example: First move the loop + * end point ahead of the loop start point => invalid, then + * move the loop start point forward => valid again. + */ + case GEN_STARTADDROFS: /* SF2.01 section 8.1.3 # 0 */ + case GEN_STARTADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 4 */ + if(voice->sample != NULL) + { + fluid_real_t start_fine = fluid_voice_gen_value(voice, GEN_STARTADDROFS); + fluid_real_t start_coar = fluid_voice_gen_value(voice, GEN_STARTADDRCOARSEOFS); + + z = voice->sample->start + (int)start_fine + 32768 * (int)start_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_start, z); + } + + break; + + case GEN_ENDADDROFS: /* SF2.01 section 8.1.3 # 1 */ + case GEN_ENDADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 12 */ + if(voice->sample != NULL) + { + fluid_real_t end_fine = fluid_voice_gen_value(voice, GEN_ENDADDROFS); + fluid_real_t end_coar = fluid_voice_gen_value(voice, GEN_ENDADDRCOARSEOFS); + + z = voice->sample->end + (int)end_fine + 32768 * (int)end_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_end, z); + } + + break; + + case GEN_STARTLOOPADDROFS: /* SF2.01 section 8.1.3 # 2 */ + case GEN_STARTLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 45 */ + if(voice->sample != NULL) + { + fluid_real_t lstart_fine = fluid_voice_gen_value(voice, GEN_STARTLOOPADDROFS); + fluid_real_t lstart_coar = fluid_voice_gen_value(voice, GEN_STARTLOOPADDRCOARSEOFS); + + z = voice->sample->loopstart + (int)lstart_fine + 32768 * (int)lstart_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_loopstart, z); + } + + break; + + case GEN_ENDLOOPADDROFS: /* SF2.01 section 8.1.3 # 3 */ + case GEN_ENDLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 50 */ + if(voice->sample != NULL) + { + fluid_real_t lend_fine = fluid_voice_gen_value(voice, GEN_ENDLOOPADDROFS); + fluid_real_t lend_coar = fluid_voice_gen_value(voice, GEN_ENDLOOPADDRCOARSEOFS); + + z = voice->sample->loopend + (int)lend_fine + 32768 * (int)lend_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_loopend, z); + } + + break; + + /* Conversion functions differ in range limit */ +#define NUM_BUFFERS_DELAY(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_delay(_v) / FLUID_BUFSIZE) +#define NUM_BUFFERS_ATTACK(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_attack(_v) / FLUID_BUFSIZE) +#define NUM_BUFFERS_RELEASE(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_release(_v) / FLUID_BUFSIZE) + + /* volume envelope + * + * - delay and hold times are converted to absolute number of samples + * - sustain is converted to its absolute value + * - attack, decay and release are converted to their increment per sample + */ + case GEN_VOLENVDELAY: /* SF2.01 section 8.1.3 # 33 */ + fluid_clip(x, -12000.0f, 5000.0f); + count = NUM_BUFFERS_DELAY(x); + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDELAY, + count, 0.0f, 0.0f, -1.0f, 1.0f); + break; + + case GEN_VOLENVATTACK: /* SF2.01 section 8.1.3 # 34 */ + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_ATTACK(x); + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVATTACK, + count, 1.0f, 1.0f / count, -1.0f, 1.0f); + break; + + case GEN_VOLENVHOLD: /* SF2.01 section 8.1.3 # 35 */ + case GEN_KEYTOVOLENVHOLD: /* SF2.01 section 8.1.3 # 39 */ + count = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0); /* 0 means: hold */ + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVHOLD, + count, 1.0f, 0.0f, -1.0f, 2.0f); + break; + + case GEN_VOLENVDECAY: /* SF2.01 section 8.1.3 # 36 */ + case GEN_VOLENVSUSTAIN: /* SF2.01 section 8.1.3 # 37 */ + case GEN_KEYTOVOLENVDECAY: /* SF2.01 section 8.1.3 # 40 */ + x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_VOLENVSUSTAIN); + fluid_clip(x, 0.0f, 1.0f); + count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */ + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDECAY, + count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f); + break; + + case GEN_VOLENVRELEASE: /* SF2.01 section 8.1.3 # 38 */ + fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f); + count = 1 + NUM_BUFFERS_RELEASE(x); + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVRELEASE, + count, 1.0f, -1.0f / count, 0.0f, 1.0f); + break; + + /* Modulation envelope */ + case GEN_MODENVDELAY: /* SF2.01 section 8.1.3 # 25 */ + fluid_clip(x, -12000.0f, 5000.0f); + count = NUM_BUFFERS_DELAY(x); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVDELAY, + count, 0.0f, 0.0f, -1.0f, 1.0f); + break; + + case GEN_MODENVATTACK: /* SF2.01 section 8.1.3 # 26 */ + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_ATTACK(x); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVATTACK, + count, 1.0f, 1.0f / count, -1.0f, 1.0f); + break; + + case GEN_MODENVHOLD: /* SF2.01 section 8.1.3 # 27 */ + case GEN_KEYTOMODENVHOLD: /* SF2.01 section 8.1.3 # 31 */ + count = calculate_hold_decay_buffers(voice, GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0); /* 1 means: hold */ + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVHOLD, + count, 1.0f, 0.0f, -1.0f, 2.0f); + break; + + case GEN_MODENVDECAY: /* SF 2.01 section 8.1.3 # 28 */ + case GEN_MODENVSUSTAIN: /* SF 2.01 section 8.1.3 # 29 */ + case GEN_KEYTOMODENVDECAY: /* SF 2.01 section 8.1.3 # 32 */ + count = calculate_hold_decay_buffers(voice, GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1); /* 1 for decay */ + x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_MODENVSUSTAIN); + fluid_clip(x, 0.0f, 1.0f); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVDECAY, + count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f); + break; + + case GEN_MODENVRELEASE: /* SF 2.01 section 8.1.3 # 30 */ + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_RELEASE(x); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVRELEASE, + count, 1.0f, -1.0f / count, 0.0f, 2.0f); + + break; + + } /* switch gen */ +} + +/** + * Recalculate voice parameters for a given control. + * + * @param voice the synthesis voice + * @param cc flag to distinguish between a continuous control and a channel control (pitch bend, ...) + * @param ctrl the control number: + * when >=0, only modulators's destination having ctrl as source are updated. + * when -1, all modulators's destination are updated (regardless of ctrl). + * + * In this implementation, I want to make sure that all controllers + * are event based: the parameter values of the DSP algorithm should + * only be updates when a controller event arrived and not at every + * iteration of the audio cycle (which would probably be feasible if + * the synth was made in silicon). + * + * The update is done in two steps: + * + * - step 1: first, we look for all the modulators that have the changed + * controller as a source. This will yield a generator that will be changed + * because of the controller event. + * + * - step 2: For this generator, calculate its new value. This is the + * sum of its original value plus the values of all the attached modulators. + * The generator flag is set to indicate the parameters must be updated. + * This avoid the risk to call 'fluid_voice_update_param' several + * times for the same generator if several modulators have that generator as + * destination. So every changed generators are updated only once. + */ + + /* bit table for each generator being updated. The bits are packed in variables + Each variable have NBR_BIT_BY_VAR bits represented by NBR_BIT_BY_VAR_LN2. + The size of the table is the number of variables: SIZE_UPDATED_GEN_BIT. + + Note: In this implementation NBR_BIT_BY_VAR_LN2 is set to 5 (convenient for 32 bits cpu) + but this could be set to 6 for 64 bits cpu. + */ + +#define NBR_BIT_BY_VAR_LN2 5 /* for 32 bits variables */ +#define NBR_BIT_BY_VAR (1 << NBR_BIT_BY_VAR_LN2) +#define NBR_BIT_BY_VAR_ANDMASK (NBR_BIT_BY_VAR - 1) +#define SIZE_UPDATED_GEN_BIT ((GEN_LAST + NBR_BIT_BY_VAR_ANDMASK) / NBR_BIT_BY_VAR) + +#define is_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] & (1 << (gen & NBR_BIT_BY_VAR_ANDMASK))) +#define set_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] |= (1 << (gen & NBR_BIT_BY_VAR_ANDMASK))) + +int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl) +{ + int i, k; + fluid_mod_t *mod; + uint32_t gen; + fluid_real_t modval; + + /* Clears registered bits table of updated generators */ + uint32_t updated_gen_bit[SIZE_UPDATED_GEN_BIT] = {0}; + + /* printf("Chan=%d, CC=%d, Src=%d, Val=%d\n", voice->channel->channum, cc, ctrl, val); */ + + for(i = 0; i < voice->mod_count; i++) + { + mod = &voice->mod[i]; + + /* step 1: find all the modulators that have the changed controller + as input source. When ctrl is -1 all modulators destination + are updated */ + if(ctrl < 0 || fluid_mod_has_source(mod, cc, ctrl)) + { + gen = fluid_mod_get_dest(mod); + + /* Skip if this generator has already been updated */ + if(!is_gen_updated(updated_gen_bit, gen)) + { + modval = 0.0; + + /* step 2: for every attached modulator, calculate the modulation + * value for the generator gen */ + for(k = 0; k < voice->mod_count; k++) + { + if(fluid_mod_has_dest(&voice->mod[k], gen)) + { + modval += fluid_mod_get_value(&voice->mod[k], voice); + } + } + + fluid_gen_set_mod(&voice->gen[gen], modval); + + /* now recalculate the parameter values that are derived from the + generator */ + fluid_voice_update_param(voice, gen); + + /* set the bit that indicates this generator is updated */ + set_gen_updated(updated_gen_bit, gen); + } + } + } + + return FLUID_OK; +} + +/** + * Update all the modulators. + * + * This function is called after a ALL_CTRL_OFF MIDI message has been received (CC 121). + * All destinations of all modulators will be updated. + */ +int fluid_voice_modulate_all(fluid_voice_t *voice) +{ + return fluid_voice_modulate(voice, 0, -1); +} + +/* legato update functions --------------------------------------------------*/ + +/* Updates voice portamento parameters + * + * @voice voice the synthesis voice + * @fromkey the beginning pitch of portamento. + * @tokey the ending pitch of portamento. + * + * The function calculates pitch offset and increment, then these parameters + * are send to the dsp. +*/ +void fluid_voice_update_portamento(fluid_voice_t *voice, int fromkey, int tokey) + +{ + fluid_channel_t *channel = voice->channel; + + /* calculates pitch offset */ + fluid_real_t PitchBeg = fluid_voice_calculate_pitch(voice, fromkey); + fluid_real_t PitchEnd = fluid_voice_calculate_pitch(voice, tokey); + fluid_real_t pitchoffset = PitchBeg - PitchEnd; + + /* Calculates increment countinc */ + /* Increment is function of PortamentoTime (ms)*/ + unsigned int countinc = (unsigned int)(((fluid_real_t)voice->output_rate * + 0.001f * + (fluid_real_t)fluid_channel_portamentotime(channel)) / + (fluid_real_t)FLUID_BUFSIZE + 0.5f); + + /* Send portamento parameters to the voice dsp */ + UPDATE_RVOICE_GENERIC_IR(fluid_rvoice_set_portamento, voice->rvoice, countinc, pitchoffset); +} + +/*---------------------------------------------------------------*/ +/*legato mode 1: multi_retrigger + * + * Modulates all generators dependent of key,vel. + * Forces the voice envelopes in the attack section (legato mode 1). + * + * @voice voice the synthesis voice + * @tokey the new key to be applied to this voice. + * @vel the new velocity to be applied to this voice. + */ +void fluid_voice_update_multi_retrigger_attack(fluid_voice_t *voice, + int tokey, int vel) +{ + voice->key = tokey; /* new note */ + voice->vel = vel; /* new velocity */ + /* Updates generators dependent of velocity */ + /* Modulates GEN_ATTENUATION (and others ) before calling + fluid_rvoice_multi_retrigger_attack().*/ + fluid_voice_modulate(voice, FALSE, FLUID_MOD_VELOCITY); + + /* Updates generator dependent of voice->key */ + fluid_voice_update_param(voice, GEN_KEYTOMODENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOMODENVDECAY); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVDECAY); + + /* Updates pitch generator */ + fluid_voice_calculate_gen_pitch(voice); + fluid_voice_update_param(voice, GEN_PITCH); + + /* updates adsr generator */ + UPDATE_RVOICE0(fluid_rvoice_multi_retrigger_attack); +} +/** end of legato update functions */ + +/* + Force the voice into release stage. Useful anywhere a voice + needs to be damped even if pedals (sustain sostenuto) are depressed. + See fluid_synth_damp_voices_by_sustain_LOCAL(), + fluid_synth_damp_voices_by_sostenuto_LOCAL, + fluid_voice_noteoff(). +*/ +void +fluid_voice_release(fluid_voice_t *voice) +{ + unsigned int at_tick = fluid_channel_get_min_note_length_ticks(voice->channel); + UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick); + voice->has_noteoff = 1; // voice is marked as noteoff occurred +} + +/* + * fluid_voice_noteoff + * + * Sending a noteoff event will advance the envelopes to section 5 (release). + * The function is convenient for polyphonic or monophonic note + */ +void +fluid_voice_noteoff(fluid_voice_t *voice) +{ + fluid_channel_t *channel; + + fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref, 0, 0); + + channel = voice->channel; + + /* Sustain a note under Sostenuto pedal */ + if(fluid_channel_sostenuto(channel) && + channel->sostenuto_orderid > voice->id) + { + // Sostenuto depressed after note + voice->status = FLUID_VOICE_HELD_BY_SOSTENUTO; + } + /* Or sustain a note under Sustain pedal */ + else if(fluid_channel_sustained(channel)) + { + voice->status = FLUID_VOICE_SUSTAINED; + } + /* Or force the voice to release stage */ + else + { + fluid_voice_release(voice); + } +} + +/* + * fluid_voice_kill_excl + * + * Percussion sounds can be mutually exclusive: for example, a 'closed + * hihat' sound will terminate an 'open hihat' sound ringing at the + * same time. This behaviour is modeled using 'exclusive classes', + * turning on a voice with an exclusive class other than 0 will kill + * all other voices having that exclusive class within the same preset + * or channel. fluid_voice_kill_excl gets called, when 'voice' is to + * be killed for that reason. + */ + +int +fluid_voice_kill_excl(fluid_voice_t *voice) +{ + + unsigned int at_tick; + + if(!fluid_voice_is_playing(voice)) + { + return FLUID_OK; + } + + /* Turn off the exclusive class information for this voice, + so that it doesn't get killed twice + */ + fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0); + + /* Speed up the volume envelope */ + /* The value was found through listening tests with hi-hat samples. */ + fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, -200); + fluid_voice_update_param(voice, GEN_VOLENVRELEASE); + + /* Speed up the modulation envelope */ + fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200); + fluid_voice_update_param(voice, GEN_MODENVRELEASE); + + at_tick = fluid_channel_get_min_note_length_ticks(voice->channel); + UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick); + + + return FLUID_OK; +} + +/* + * Unlock the overflow rvoice of the voice. + * Decrement the reference count of the sample owned by this rvoice. + * + * Called by fluid_synth when the overflow rvoice has finished by itself. + * Must be called also explicitly at synth destruction to ensure that + * the soundfont be unloaded successfully. + */ +void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice) +{ + voice->can_access_overflow_rvoice = 1; + + /* Decrement the reference count of the sample to indicate + that this sample isn't owned by the rvoice anymore */ + fluid_voice_sample_unref(&voice->overflow_sample); +} + +/* + * fluid_voice_off + * + * Force the voice into finished stage. Useful anywhere a voice + * needs to be cancelled from MIDI API. + */ +void fluid_voice_off(fluid_voice_t *voice) +{ + UPDATE_RVOICE0(fluid_rvoice_voiceoff); /* request to finish the voice */ +} + +/* + * fluid_voice_stop + * + * Purpose: + * Turns off a voice, meaning that it is not processed anymore by the + * DSP loop, i.e. contrary part to fluid_voice_start(). + */ +void +fluid_voice_stop(fluid_voice_t *voice) +{ + fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref, 0, 0); + + voice->chan = NO_CHANNEL; + + /* Decrement the reference count of the sample, to indicate + that this sample isn't owned by the rvoice anymore. + */ + fluid_voice_sample_unref(&voice->sample); + + voice->status = FLUID_VOICE_OFF; + voice->has_noteoff = 1; + + /* Decrement voice count */ + voice->channel->synth->active_voice_count--; +} + +/** + * Adds a modulator to the voice if the modulator has valid sources. + * + * @param voice Voice instance. + * @param mod Modulator info (copied). + * @param mode Determines how to handle an existing identical modulator. + * #FLUID_VOICE_ADD to add (offset) the modulator amounts, + * #FLUID_VOICE_OVERWRITE to replace the modulator, + * #FLUID_VOICE_DEFAULT when adding a default modulator - no duplicate should + * exist so don't check. + */ +void +fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode) +{ + /* Ignore the modulator if its sources inputs are invalid */ + if(fluid_mod_check_sources(mod, "api fluid_voice_add_mod mod")) + { + fluid_voice_add_mod_local(voice, mod, mode, FLUID_NUM_MOD); + } +} + +/** + * Adds a modulator to the voice. + * local version of fluid_voice_add_mod function. Called at noteon time. + * @param voice, mod, mode, same as for fluid_voice_add_mod() (see above). + * @param check_limit_count is the modulator number limit to handle with existing + * identical modulator(i.e mode FLUID_VOICE_OVERWRITE, FLUID_VOICE_ADD). + * - When FLUID_NUM_MOD, all the voices modulators (since the previous call) + * are checked for identity. + * - When check_count_limit is below the actual number of voices modulators + * (voice->mod_count), this will restrict identity check to this number, + * This is useful when we know by advance that there is no duplicate with + * modulators at index above this limit. This avoid wasting cpu cycles at noteon. + */ +void +fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count) +{ + int i; + + /* check_limit_count cannot be above voice->mod_count */ + if(check_limit_count > voice->mod_count) + { + check_limit_count = voice->mod_count; + } + + if(mode == FLUID_VOICE_ADD) + { + + /* if identical modulator exists, add them */ + for(i = 0; i < check_limit_count; i++) + { + if(fluid_mod_test_identity(&voice->mod[i], mod)) + { + // printf("Adding modulator...\n"); + voice->mod[i].amount += mod->amount; + return; + } + } + + } + else if(mode == FLUID_VOICE_OVERWRITE) + { + + /* if identical modulator exists, replace it (only the amount has to be changed) */ + for(i = 0; i < check_limit_count; i++) + { + if(fluid_mod_test_identity(&voice->mod[i], mod)) + { + // printf("Replacing modulator...amount is %f\n",mod->amount); + voice->mod[i].amount = mod->amount; + return; + } + } + } + + /* Add a new modulator (No existing modulator to add / overwrite). + Also, default modulators (FLUID_VOICE_DEFAULT) are added without + checking, if the same modulator already exists. */ + if(voice->mod_count < FLUID_NUM_MOD) + { + fluid_mod_clone(&voice->mod[voice->mod_count++], mod); + } + else + { + FLUID_LOG(FLUID_WARN, "Voice %i has more modulators than supported, ignoring.", voice->id); + } +} + +/** + * Get the unique ID of the noteon-event. + * + * @param voice Voice instance + * @return Note on unique ID + * + * A SoundFont loader may store pointers to voices it has created for + * real-time control during the operation of a voice (for example: parameter + * changes in SoundFont editor). The synth uses a pool of voices internally which are + * 'recycled' and never deallocated. + * + * However, before modifying an existing voice, check + * - that its state is still 'playing' + * - that the ID is still the same + * + * Otherwise the voice has finished playing. + */ +unsigned int fluid_voice_get_id(const fluid_voice_t *voice) +{ + return voice->id; +} + +/** + * Check if a voice is producing sound. + * + * Like fluid_voice_is_on() this will return TRUE once a call to + * fluid_synth_start_voice() has been made. Contrary to fluid_voice_is_on(), + * this might also return TRUE after the voice received a noteoff event, as it may + * still be playing in release phase, or because it has been sustained or + * sostenuto'ed. + * + * @param voice Voice instance + * @return TRUE if playing, FALSE otherwise + */ +int fluid_voice_is_playing(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_ON) + || fluid_voice_is_sustained(voice) + || fluid_voice_is_sostenuto(voice); + +} + +/** + * Check if a voice is ON. + * + * A voice is in ON state as soon as a call to fluid_synth_start_voice() has been made + * (which is typically done in a fluid_preset_t's noteon function). + * A voice stays ON as long as it has not received a noteoff event. + * + * @param voice Voice instance + * @return TRUE if on, FALSE otherwise + * + * @since 1.1.7 + */ +int fluid_voice_is_on(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_ON && !voice->has_noteoff); +} + +/** + * Check if a voice keeps playing after it has received a noteoff due to being held by sustain. + * + * @param voice Voice instance + * @return TRUE if sustained, FALSE otherwise + * + * @since 1.1.7 + */ +int fluid_voice_is_sustained(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_SUSTAINED); +} + +/** + * Check if a voice keeps playing after it has received a noteoff due to being held by sostenuto. + * + * @param voice Voice instance + * @return TRUE if sostenuto, FALSE otherwise + * + * @since 1.1.7 + */ +int fluid_voice_is_sostenuto(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_HELD_BY_SOSTENUTO); +} + +/** + * Return the MIDI channel the voice is playing on. + * + * @param voice Voice instance + * @return The channel assigned to this voice + * + * @note The result of this function is only valid if the voice is playing. + * + * @since 1.1.7 + */ +int fluid_voice_get_channel(const fluid_voice_t *voice) +{ + return voice->chan; +} + +/** + * Return the effective MIDI key of the playing voice. + * + * @param voice Voice instance + * @return The MIDI key this voice is playing at + * + * If the voice was started from an instrument which uses a fixed key generator, it returns that. + * Otherwise returns the same value as \c fluid_voice_get_key. + * + * @note The result of this function is only valid if the voice is playing. + * + * @since 1.1.7 + */ +int fluid_voice_get_actual_key(const fluid_voice_t *voice) +{ + fluid_real_t x = fluid_voice_gen_value(voice, GEN_KEYNUM); + + if(x >= 0) + { + return (int)x; + } + else + { + return fluid_voice_get_key(voice); + } +} + +/** + * Return the MIDI key from the starting noteon event. + * + * @param voice Voice instance + * @return The MIDI key of the noteon event that originally turned on this voice + * + * @note The result of this function is only valid if the voice is playing. + * + * @since 1.1.7 + */ +int fluid_voice_get_key(const fluid_voice_t *voice) +{ + return voice->key; +} + +/** + * Return the effective MIDI velocity of the playing voice. + * + * @param voice Voice instance + * @return The MIDI velocity this voice is playing at + * + * If the voice was started from an instrument which uses a fixed velocity generator, it returns that. + * Otherwise it returns the same value as \c fluid_voice_get_velocity. + * + * @note The result of this function is only valid if the voice is playing. + * + * @since 1.1.7 + */ +int fluid_voice_get_actual_velocity(const fluid_voice_t *voice) +{ + fluid_real_t x = fluid_voice_gen_value(voice, GEN_VELOCITY); + + if(x > 0) + { + return (int)x; + } + else + { + return fluid_voice_get_velocity(voice); + } +} + +/** + * Return the MIDI velocity from the starting noteon event. + * + * @param voice Voice instance + * @return The MIDI velocity which originally turned on this voice + * + * @note The result of this function is only valid if the voice is playing. + * + * @since 1.1.7 + */ +int fluid_voice_get_velocity(const fluid_voice_t *voice) +{ + return voice->vel; +} + +/* + * fluid_voice_get_lower_boundary_for_attenuation + * + * Purpose: + * + * A lower boundary for the attenuation (as in 'the minimum + * attenuation of this voice, with volume pedals, modulators + * etc. resulting in minimum attenuation, cannot fall below x cB) is + * calculated. This has to be called during fluid_voice_start, after + * all modulators have been run on the voice once. Also, + * voice->attenuation has to be initialized. + * (see fluid_voice_calculate_runtime_synthesis_parameters()) + */ +static fluid_real_t +fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice) +{ + int i; + fluid_mod_t *mod; + fluid_real_t possible_att_reduction_cB = 0; + fluid_real_t lower_bound; + + for(i = 0; i < voice->mod_count; i++) + { + mod = &voice->mod[i]; + + /* Modulator has attenuation as target and can change over time? */ + if((mod->dest == GEN_ATTENUATION) + && ((mod->flags1 & FLUID_MOD_CC) + || (mod->flags2 & FLUID_MOD_CC) + || (mod->src1 == FLUID_MOD_CHANNELPRESSURE) + || (mod->src1 == FLUID_MOD_KEYPRESSURE) + || (mod->src1 == FLUID_MOD_PITCHWHEEL) + || (mod->src2 == FLUID_MOD_CHANNELPRESSURE) + || (mod->src2 == FLUID_MOD_KEYPRESSURE) + || (mod->src2 == FLUID_MOD_PITCHWHEEL))) + { + + fluid_real_t current_val = fluid_mod_get_value(mod, voice); + /* min_val is the possible minimum value for this modulator. + it depends of 3 things : + 1)the minimum values of src1,src2 (i.e -1 if mapping is bipolar + or 0 if mapping is unipolar). + 2)the sign of amount. + 3)absolute value of amount. + + When at least one source mapping is bipolar: + min_val is -|amount| regardless the sign of amount. + When both sources mapping are unipolar: + min_val is -|amount|, if amount is negative. + min_val is 0, if amount is positive + */ + fluid_real_t min_val = fabs(mod->amount); + + /* Can this modulator produce a negative contribution? */ + if((mod->flags1 & FLUID_MOD_BIPOLAR) + || (mod->flags2 & FLUID_MOD_BIPOLAR) + || (mod->amount < 0)) + { + min_val = -min_val; /* min_val = - |amount|*/ + } + else + { + /* No negative value possible. But still, the minimum contribution is 0. */ + min_val = 0; + } + + /* For example: + * - current_val=100 + * - min_val=-4000 + * - possible reduction contribution of this modulator = current_val - min_val = 4100 + */ + if(current_val > min_val) + { + possible_att_reduction_cB += (current_val - min_val); + } + } + } + + lower_bound = voice->attenuation - possible_att_reduction_cB; + + /* SF2.01 specs do not allow negative attenuation */ + if(lower_bound < 0) + { + lower_bound = 0; + } + + return lower_bound; +} + + + + +int fluid_voice_set_param(fluid_voice_t *voice, int gen, fluid_real_t nrpn_value) +{ + voice->gen[gen].nrpn = nrpn_value; + voice->gen[gen].flags = GEN_SET; + fluid_voice_update_param(voice, gen); + return FLUID_OK; +} + +int fluid_voice_set_gain(fluid_voice_t *voice, fluid_real_t gain) +{ + fluid_real_t left, right, reverb, chorus; + + /* avoid division by zero*/ + if(gain < 0.0000001f) + { + gain = 0.0000001f; + } + + voice->synth_gain = gain; + left = fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1)); + right = fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0)); + reverb = fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send); + chorus = fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send); + + UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0, left); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1, right); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, reverb); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, chorus); + + return FLUID_OK; +} + +/* - Scan the loop + * - determine the peak level + * - Calculate, what factor will make the loop inaudible + * - Store in sample + */ + +/** + * Calculate the peak volume of a sample for voice off optimization. + * + * @param s Sample to optimize + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * If the peak volume during the loop is known, then the voice can + * be released earlier during the release phase. Otherwise, the + * voice will operate (inaudibly), until the envelope is at the + * nominal turnoff point. So it's a good idea to call + * fluid_voice_optimize_sample() on each sample once. + */ +int +fluid_voice_optimize_sample(fluid_sample_t *s) +{ + int32_t peak_max = 0; + int32_t peak_min = 0; + int32_t peak; + fluid_real_t normalized_amplitude_during_loop; + double result; + unsigned int i; + + /* ignore disabled samples */ + if(s->start == s->end) + { + return (FLUID_OK); + } + + if(!s->amplitude_that_reaches_noise_floor_is_valid) /* Only once */ + { + /* Scan the loop */ + for(i = s->loopstart; i < s->loopend; i++) + { + int32_t val = fluid_rvoice_get_sample(s->data, s->data24, i); + + if(val > peak_max) + { + peak_max = val; + } + else if(val < peak_min) + { + peak_min = val; + } + } + + /* Determine the peak level */ + if(peak_max > -peak_min) + { + peak = peak_max; + } + else + { + peak = -peak_min; + } + + if(peak == 0) + { + /* Avoid division by zero */ + peak = 1; + } + + /* Calculate what factor will make the loop inaudible + * For example: Take a peak of 3277 (10 % of 32768). The + * normalized amplitude is 0.1 (10 % of 32768). An amplitude + * factor of 0.0001 (as opposed to the default 0.00001) will + * drop this sample to the noise floor. + */ + + /* 16 bits => 96+4=100 dB dynamic range => 0.00001 */ + normalized_amplitude_during_loop = ((fluid_real_t)peak) / (INT24_MAX * 1.0f); + result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop; + + /* Store in sample */ + s->amplitude_that_reaches_noise_floor = (double)result; + s->amplitude_that_reaches_noise_floor_is_valid = 1; +#if 0 + printf("Sample peak detection: factor %f\n", (double)result); +#endif + } + + return FLUID_OK; +} + +float +fluid_voice_get_overflow_prio(fluid_voice_t *voice, + fluid_overflow_prio_t *score, + unsigned int cur_time) +{ + float this_voice_prio = 0; + int channel; + + /* Are we already overflowing? */ + if(!voice->can_access_overflow_rvoice) + { + return OVERFLOW_PRIO_CANNOT_KILL; + } + + /* Is this voice on the drum channel? + * Then it is very important. + * Also skip the released and sustained scores. + */ + if(voice->channel->channel_type == CHANNEL_TYPE_DRUM) + { + this_voice_prio += score->percussion; + } + else if(voice->has_noteoff) + { + /* Noteoff has */ + this_voice_prio += score->released; + } + else if(fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice)) + { + /* This voice is still active, since the sustain pedal is held down. + * Consider it less important than non-sustained channels. + * This decision is somehow subjective. But usually the sustain pedal + * is used to play 'more-voices-than-fingers', so it shouldn't hurt + * if we kill one voice. + */ + this_voice_prio += score->sustained; + } + + /* We are not enthusiastic about releasing voices, which have just been started. + * Otherwise hitting a chord may result in killing notes belonging to that very same + * chord. So give newer voices a higher score. */ + if(score->age) + { + cur_time -= voice->start_time; + + if(cur_time < 1) + { + cur_time = 1; // Avoid div by zero + } + + this_voice_prio += (score->age * voice->output_rate) / cur_time; + } + + /* take a rough estimate of loudness into account. Louder voices are more important. */ + if(score->volume) + { + fluid_real_t a = voice->attenuation; + + if(voice->has_noteoff) + { + // FIXME: Should take into account where on the envelope we are...? + } + + if(a < 0.1f) + { + a = 0.1f; // Avoid div by zero + } + + this_voice_prio += score->volume / a; + } + + /* Check if this voice is on an important channel. If so, then add the + * score for important channels */ + channel = fluid_voice_get_channel(voice); + + if(channel < score->num_important_channels && score->important_channels[channel]) + { + this_voice_prio += score->important; + } + + return this_voice_prio; +} + + +void fluid_voice_set_custom_filter(fluid_voice_t *voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags) +{ + UPDATE_RVOICE_GENERIC_I2(fluid_iir_filter_init, &voice->rvoice->resonant_custom_filter, type, flags); +} diff --git a/libs/fluidsynth/src/synth/fluid_voice.h b/libs/fluidsynth/src/synth/fluid_voice.h new file mode 100644 index 00000000000..4ce6c2b7ab5 --- /dev/null +++ b/libs/fluidsynth/src/synth/fluid_voice.h @@ -0,0 +1,198 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_VOICE_H +#define _FLUID_VOICE_H + +#include "fluid_phase.h" +#include "fluid_gen.h" +#include "fluid_mod.h" +#include "fluid_iir_filter.h" +#include "fluid_adsr_env.h" +#include "fluid_lfo.h" +#include "fluid_rvoice.h" +#include "fluid_rvoice_event.h" + +#define NO_CHANNEL 0xff + +typedef struct _fluid_overflow_prio_t fluid_overflow_prio_t; + +struct _fluid_overflow_prio_t +{ + float percussion; /**< Is this voice on the drum channel? Then add this score */ + float released; /**< Is this voice in release stage? Then add this score (usually negative) */ + float sustained; /**< Is this voice sustained? Then add this score (usually negative) */ + float volume; /**< Multiply current (or future) volume (a value between 0 and 1) */ + float age; /**< This score will be divided by the number of seconds the voice has lasted */ + float important; /**< This score will be added to all important channels */ + char *important_channels; /**< "important" flags indexed by MIDI channel number */ + int num_important_channels; /**< Number of elements in the important_channels array */ +}; + +enum fluid_voice_status +{ + FLUID_VOICE_CLEAN, + FLUID_VOICE_ON, + FLUID_VOICE_SUSTAINED, /* Sustained by Sustain pedal */ + FLUID_VOICE_HELD_BY_SOSTENUTO, /* Sustained by Sostenuto pedal */ + FLUID_VOICE_OFF +}; + + +/* + * fluid_voice_t + */ +struct _fluid_voice_t +{ + unsigned int id; /* the id is incremented for every new noteon. + it's used for noteoff's */ + unsigned char status; + unsigned char chan; /* the channel number, quick access for channel messages */ + unsigned char key; /* the key of the noteon event, quick access for noteoff */ + unsigned char vel; /* the velocity of the noteon event */ + fluid_channel_t *channel; + fluid_rvoice_eventhandler_t *eventhandler; + fluid_zone_range_t *zone_range; /* instrument zone range*/ + fluid_sample_t *sample; /* Pointer to sample (dupe in rvoice) */ + fluid_sample_t *overflow_sample; /* Pointer to sample (dupe in overflow_rvoice) */ + + unsigned int start_time; + int mod_count; + fluid_mod_t mod[FLUID_NUM_MOD]; + fluid_gen_t gen[GEN_LAST]; + + /* basic parameters */ + fluid_real_t output_rate; /* the sample rate of the synthesizer (dupe in rvoice) */ + + /* basic parameters */ + fluid_real_t pitch; /* the pitch in midicents (dupe in rvoice) */ + fluid_real_t attenuation; /* the attenuation in centibels (dupe in rvoice) */ + fluid_real_t root_pitch; + + /* master gain (dupe in rvoice) */ + fluid_real_t synth_gain; + + /* pan */ + fluid_real_t pan; + + /* balance */ + fluid_real_t balance; + + /* reverb */ + fluid_real_t reverb_send; + + /* chorus */ + fluid_real_t chorus_send; + + /* rvoice control */ + fluid_rvoice_t *rvoice; + fluid_rvoice_t *overflow_rvoice; /* Used temporarily and only in overflow situations */ + char can_access_rvoice; /* False if rvoice is being rendered in separate thread */ + char can_access_overflow_rvoice; /* False if overflow_rvoice is being rendered in separate thread */ + char has_noteoff; /* Flag set when noteoff has been sent */ + +#ifdef WITH_PROFILING + /* for debugging */ + double ref; +#endif +}; + + +fluid_voice_t *new_fluid_voice(fluid_rvoice_eventhandler_t *handler, fluid_real_t output_rate); +void delete_fluid_voice(fluid_voice_t *voice); + +void fluid_voice_start(fluid_voice_t *voice); +void fluid_voice_calculate_gen_pitch(fluid_voice_t *voice); + +int fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample, + fluid_zone_range_t *inst_zone_range, + fluid_channel_t *channel, int key, int vel, + unsigned int id, unsigned int time, fluid_real_t gain); + +int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl); +int fluid_voice_modulate_all(fluid_voice_t *voice); + +/** Set the NRPN value of a generator. */ +int fluid_voice_set_param(fluid_voice_t *voice, int gen, fluid_real_t value); + + +/** Set the gain. */ +int fluid_voice_set_gain(fluid_voice_t *voice, fluid_real_t gain); + +void fluid_voice_set_output_rate(fluid_voice_t *voice, fluid_real_t value); + + +/** Update all the synthesis parameters, which depend on generator + 'gen'. This is only necessary after changing a generator of an + already operating voice. Most applications will not need this + function.*/ +void fluid_voice_update_param(fluid_voice_t *voice, int gen); + +/** legato modes */ +/* force in the attack section for legato mode multi_retrigger: 1 */ +void fluid_voice_update_multi_retrigger_attack(fluid_voice_t *voice, int tokey, int vel); +/* Update portamento parameter */ +void fluid_voice_update_portamento(fluid_voice_t *voice, int fromkey, int tokey); + + +void fluid_voice_release(fluid_voice_t *voice); +void fluid_voice_noteoff(fluid_voice_t *voice); +void fluid_voice_off(fluid_voice_t *voice); +void fluid_voice_stop(fluid_voice_t *voice); +void fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count); +void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice); + +int fluid_voice_kill_excl(fluid_voice_t *voice); +float fluid_voice_get_overflow_prio(fluid_voice_t *voice, + fluid_overflow_prio_t *score, + unsigned int cur_time); + +#define OVERFLOW_PRIO_CANNOT_KILL 999999. + +/** + * Locks the rvoice for rendering, so it can't be modified directly + */ +static FLUID_INLINE void +fluid_voice_lock_rvoice(fluid_voice_t *voice) +{ + voice->can_access_rvoice = 0; +} + +/** + * Unlocks the rvoice for rendering, so it can be modified directly + */ +static FLUID_INLINE void +fluid_voice_unlock_rvoice(fluid_voice_t *voice) +{ + voice->can_access_rvoice = 1; +} + +#define _AVAILABLE(voice) ((voice)->can_access_rvoice && \ + (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF))) +//#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL) +#define _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val) + + +fluid_real_t fluid_voice_gen_value(const fluid_voice_t *voice, int num); +void fluid_voice_set_custom_filter(fluid_voice_t *voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags); + + +#endif /* _FLUID_VOICE_H */ diff --git a/libs/fluidsynth/src/utils/fluid_conv.c b/libs/fluidsynth/src/utils/fluid_conv.c new file mode 100644 index 00000000000..4a459ae0a2c --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_conv.c @@ -0,0 +1,333 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_conv.h" +#include "fluid_sys.h" +#include "fluid_conv_tables.inc.h" + +/* + * Converts absolute cents to Hertz + * + * As per sfspec section 9.3: + * + * ABSOLUTE CENTS - An absolute logarithmic measure of frequency based on a + * reference of MIDI key number scaled by 100. + * A cent is 1/1200 of an octave [which is the twelve hundredth root of two], + * and value 6900 is 440 Hz (A-440). + * + * Implemented below basically is the following: + * 440 * 2^((cents-6900)/1200) + * = 440 * 2^((int)((cents-6900)/1200)) * 2^(((int)cents-6900)%1200)) + * = 2^((int)((cents-6900)/1200)) * (440 * 2^(((int)cents-6900)%1200))) + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * This second factor is stored in the lookup table. + * + * The first factor can be implemented with a fast shift when the exponent + * is always an int. This is the case when using 440/2^6 Hz rather than 440Hz + * reference. + */ +fluid_real_t +fluid_ct2hz_real(fluid_real_t cents) +{ + if(FLUID_UNLIKELY(cents < 0)) + { + return fluid_act2hz(cents); + } + else + { + unsigned int mult, fac, rem; + unsigned int icents = (unsigned int)cents; + icents += 300u; + + // don't use stdlib div() here, it turned out have poor performance + fac = icents / 1200u; + rem = icents % 1200u; + + // Think of "mult" as the factor that we multiply (440/2^6)Hz with, + // or in other words mult is the "first factor" of the above + // functions comment. + // + // Assuming sizeof(uint)==4 this will give us a maximum range of + // 32 * 1200cents - 300cents == 38100 cents == 29,527,900,160 Hz + // which is much more than ever needed. For bigger values, just + // safely wrap around (the & is just a replacement for the quick + // modulo operation % 32). + mult = 1u << (fac & (sizeof(mult)*8u - 1u)); + + // don't use ldexp() either (poor performance) + return mult * fluid_ct2hz_tab[rem]; + } +} + +/* + * fluid_ct2hz + */ +fluid_real_t +fluid_ct2hz(fluid_real_t cents) +{ + /* Filter fc limit: SF2.01 page 48 # 8 */ + if(cents >= 13500) + { + cents = 13500; /* 20 kHz */ + } + else if(cents < 1500) + { + cents = 1500; /* 20 Hz */ + } + + return fluid_ct2hz_real(cents); +} + +/* + * fluid_cb2amp + * + * in: a value between 0 and 1440, 0 is no attenuation + * out: a value between 1 and 0 + */ +fluid_real_t +fluid_cb2amp(fluid_real_t cb) +{ + /* + * cb: an attenuation in 'centibels' (1/10 dB) + * SF2.01 page 49 # 48 limits it to 144 dB. + * 96 dB is reasonable for 16 bit systems, 144 would make sense for 24 bit. + */ + + /* minimum attenuation: 0 dB */ + if(cb < 0) + { + return 1.0; + } + + if(cb >= FLUID_CB_AMP_SIZE) + { + return 0.0; + } + + return fluid_cb2amp_tab[(int) cb]; +} + +/* + * fluid_tc2sec_delay + */ +fluid_real_t +fluid_tc2sec_delay(fluid_real_t tc) +{ + /* SF2.01 section 8.1.2 items 21, 23, 25, 33 + * SF2.01 section 8.1.3 items 21, 23, 25, 33 + * + * The most negative number indicates a delay of 0. Range is limited + * from -12000 to 5000 */ + if(tc <= -32768.0f) + { + return (fluid_real_t) 0.0f; + }; + + if(tc < -12000.f) + { + tc = (fluid_real_t) -12000.0f; + } + + if(tc > 5000.0f) + { + tc = (fluid_real_t) 5000.0f; + } + + return FLUID_POW(2.f, tc / 1200.f); +} + +/* + * fluid_tc2sec_attack + */ +fluid_real_t +fluid_tc2sec_attack(fluid_real_t tc) +{ + /* SF2.01 section 8.1.2 items 26, 34 + * SF2.01 section 8.1.3 items 26, 34 + * The most negative number indicates a delay of 0 + * Range is limited from -12000 to 8000 */ + if(tc <= -32768.f) + { + return (fluid_real_t) 0.f; + }; + + if(tc < -12000.f) + { + tc = (fluid_real_t) -12000.f; + }; + + if(tc > 8000.f) + { + tc = (fluid_real_t) 8000.f; + }; + + return FLUID_POW(2.f, tc / 1200.f); +} + +/* + * fluid_tc2sec + */ +fluid_real_t +fluid_tc2sec(fluid_real_t tc) +{ + /* No range checking here! */ + return FLUID_POW(2.f, tc / 1200.f); +} + +/* + * fluid_tc2sec_release + */ +fluid_real_t +fluid_tc2sec_release(fluid_real_t tc) +{ + /* SF2.01 section 8.1.2 items 30, 38 + * SF2.01 section 8.1.3 items 30, 38 + * No 'most negative number' rule here! + * Range is limited from -12000 to 8000 */ + if(tc <= -32768.f) + { + return (fluid_real_t) 0.f; + }; + + if(tc < -12000.f) + { + tc = (fluid_real_t) -12000.f; + }; + + if(tc > 8000.f) + { + tc = (fluid_real_t) 8000.f; + }; + + return FLUID_POW(2.f, tc / 1200.f); +} + +/* + * fluid_act2hz + * + * Convert from absolute cents to Hertz + * + * The inverse operation, converting from Hertz to cents, was unused and implemented as + * +fluid_hz2ct(fluid_real_t f) +{ + return 6900.f + (1200.f / FLUID_M_LN2) * FLUID_LOGF(f / 440.0f)); +} + */ +double +fluid_act2hz(double c) +{ + // do not use FLUID_POW, otherwise the unit tests will fail when compiled in single precision + return 8.1757989156437073336828122976032719176391831357 * pow(2.f, c / 1200.f); +} + +/* + * fluid_pan + */ +fluid_real_t +fluid_pan(fluid_real_t c, int left) +{ + if(left) + { + c = -c; + } + + if(c <= -500.f) + { + return (fluid_real_t) 0.f; + } + else if(c >= 500.f) + { + return (fluid_real_t) 1.f; + } + else + { + return fluid_pan_tab[(int)(c) + 500]; + } +} + +/* + * Return the amount of attenuation based on the balance for the specified + * channel. If balance is negative (turned toward left channel, only the right + * channel is attenuated. If balance is positive, only the left channel is + * attenuated. + * + * @params balance left/right balance, range [-960;960] in absolute centibels + * @return amount of attenuation [0.0;1.0] + */ +fluid_real_t fluid_balance(fluid_real_t balance, int left) +{ + /* This is the most common case */ + if(balance == 0.f) + { + return 1.0f; + } + + if((left && balance < 0.f) || (!left && balance > 0.f)) + { + return 1.0f; + } + + if(balance < 0.f) + { + balance = -balance; + } + + return fluid_cb2amp(balance); +} + +/* + * fluid_concave + */ +fluid_real_t +fluid_concave(fluid_real_t val) +{ + int ival = (int)val; + if(val < 0.f) + { + return 0.f; + } + else if (ival >= FLUID_VEL_CB_SIZE - 1) + { + return fluid_concave_tab[FLUID_VEL_CB_SIZE - 1]; + } + + return fluid_concave_tab[ival] + (fluid_concave_tab[ival + 1] - fluid_concave_tab[ival]) * (val - ival); +} + +/* + * fluid_convex + */ +fluid_real_t +fluid_convex(fluid_real_t val) +{ + int ival = (int)val; + if(val < 0.f) + { + return 0.f; + } + else if (ival >= FLUID_VEL_CB_SIZE - 1) + { + return fluid_convex_tab[FLUID_VEL_CB_SIZE - 1]; + } + + // interpolation between convex steps: fixes bad sounds with modenv and filter cutoff + return fluid_convex_tab[ival] + (fluid_convex_tab[ival + 1] - fluid_convex_tab[ival]) * (val - ival); +} diff --git a/libs/fluidsynth/src/utils/fluid_conv.h b/libs/fluidsynth/src/utils/fluid_conv.h new file mode 100644 index 00000000000..985c02d0b38 --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_conv.h @@ -0,0 +1,40 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_CONV_H +#define _FLUID_CONV_H + +#include "fluidsynth_priv.h" +#include "utils/fluid_conv_tables.h" + +fluid_real_t fluid_ct2hz_real(fluid_real_t cents); +fluid_real_t fluid_ct2hz(fluid_real_t cents); +fluid_real_t fluid_cb2amp(fluid_real_t cb); +fluid_real_t fluid_tc2sec(fluid_real_t tc); +fluid_real_t fluid_tc2sec_delay(fluid_real_t tc); +fluid_real_t fluid_tc2sec_attack(fluid_real_t tc); +fluid_real_t fluid_tc2sec_release(fluid_real_t tc); +double fluid_act2hz(double c); +fluid_real_t fluid_pan(fluid_real_t c, int left); +fluid_real_t fluid_balance(fluid_real_t balance, int left); +fluid_real_t fluid_concave(fluid_real_t val); +fluid_real_t fluid_convex(fluid_real_t val); + +#endif /* _FLUID_CONV_H */ diff --git a/libs/fluidsynth/src/utils/fluid_conv_tables.h b/libs/fluidsynth/src/utils/fluid_conv_tables.h new file mode 100644 index 00000000000..8d1ae71540a --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_conv_tables.h @@ -0,0 +1,41 @@ + +#ifndef _FLUID_CONV_TABLES_H +#define _FLUID_CONV_TABLES_H + +/* + Attenuation range in centibels. + Attenuation range is the dynamic range of the volume envelope generator + from 0 to the end of attack segment. + fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation. + However the spec makes no distinction between 16 or 24 bit synths, so use + 96 dB here. + + Note about usefulness of 24 bits: + 1)Even fluidsynth is a 24 bit synth, this format is only relevant if + the sample format coming from the soundfont is 24 bits and the audio sample format + chosen by the application (audio.sample.format) is not 16 bits. + + 2)When the sample soundfont is 16 bits, the internal 24 bits number have + 16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of + this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db) + even if this sample is produced by the audio driver using an audio sample format + compatible for a 24 bit DAC. + + 3)When the audio sample format settings is 16 bits (audio.sample.format), the + audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB + even if the initial sample comes from a 24 bits soundfont. + + In both cases (2) or (3), the real dynamic range is only 96 dB. + + Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3): + - for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB). + - for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB). + */ +#define FLUID_PEAK_ATTENUATION 960.0f + +#define FLUID_CENTS_HZ_SIZE 1200 +#define FLUID_VEL_CB_SIZE 128 +#define FLUID_CB_AMP_SIZE 1441 +#define FLUID_PAN_SIZE 1002 + +#endif diff --git a/libs/fluidsynth/src/utils/fluid_hash.c b/libs/fluidsynth/src/utils/fluid_hash.c new file mode 100644 index 00000000000..7efd0deddad --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_hash.c @@ -0,0 +1,1407 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * 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 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + * + * Adapted for FluidSynth use by Josh Green + * September 8, 2009 from glib 2.18.4 + */ + +/* + * MT safe + */ + +#include "fluid_sys.h" +#include "fluid_hash.h" +#include "fluid_list.h" + + +#define HASH_TABLE_MIN_SIZE 11 +#define HASH_TABLE_MAX_SIZE 13845163 + + +typedef struct +{ + fluid_hashtable_t *hashtable; + fluid_hashnode_t *prev_node; + fluid_hashnode_t *node; + int position; + int pre_advanced; // Boolean + int version; +} RealIter; + + +/* Excerpt from glib gprimes.c */ + +static const unsigned int primes[] = +{ + 11, + 19, + 37, + 73, + 109, + 163, + 251, + 367, + 557, + 823, + 1237, + 1861, + 2777, + 4177, + 6247, + 9371, + 14057, + 21089, + 31627, + 47431, + 71143, + 106721, + 160073, + 240101, + 360163, + 540217, + 810343, + 1215497, + 1823231, + 2734867, + 4102283, + 6153409, + 9230113, + 13845163, +}; + +static const unsigned int nprimes = FLUID_N_ELEMENTS(primes); + +static unsigned int +spaced_primes_closest(unsigned int num) +{ + unsigned int i; + + for(i = 0; i < nprimes; i++) + { + if(primes[i] > num) + { + return primes[i]; + } + } + + return primes[nprimes - 1]; +} + +/* End excerpt from glib gprimes.c */ + + +/* + * @hashtable: our #fluid_hashtable_t + * @key: the key to lookup against + * @hash_return: optional key hash return location + * Return value: a pointer to the described #fluid_hashnode_t pointer + * + * Performs a lookup in the hash table. Virtually all hash operations + * will use this function internally. + * + * This function first computes the hash value of the key using the + * user's hash function. + * + * If an entry in the table matching @key is found then this function + * returns a pointer to the pointer to that entry in the table. In + * the case that the entry is at the head of a chain, this pointer + * will be an item in the nodes[] array. In the case that the entry + * is not at the head of a chain, this pointer will be the ->next + * pointer on the node that precedes it. + * + * In the case that no matching entry exists in the table, a pointer + * to a %NULL pointer will be returned. To insert a item, this %NULL + * pointer should be updated to point to the new #fluid_hashnode_t. + * + * If @hash_return is a pass-by-reference parameter. If it is + * non-%NULL then the computed hash value is returned. This is to + * save insertions from having to compute the hash record again for + * the new record. + */ +static FLUID_INLINE fluid_hashnode_t ** +fluid_hashtable_lookup_node(fluid_hashtable_t *hashtable, const void *key, + unsigned int *hash_return) +{ + fluid_hashnode_t **node_ptr, *node; + unsigned int hash_value; + + hash_value = (* hashtable->hash_func)(key); + node_ptr = &hashtable->nodes[hash_value % hashtable->size]; + + if(hash_return) + { + *hash_return = hash_value; + } + + /* Hash table lookup needs to be fast. + * We therefore remove the extra conditional of testing + * whether to call the key_equal_func or not from + * the inner loop. + * + * Additional optimisation: first check if our full hash + * values are equal so we can avoid calling the full-blown + * key equality function in most cases. + */ + if(hashtable->key_equal_func) + { + while((node = *node_ptr)) + { + if(node->key_hash == hash_value && + hashtable->key_equal_func(node->key, key)) + { + break; + } + + node_ptr = &(*node_ptr)->next; + } + } + else + { + while((node = *node_ptr)) + { + if(node->key == key) + { + break; + } + + node_ptr = &(*node_ptr)->next; + } + } + + return node_ptr; +} + +/* + * @hashtable: our #fluid_hashtable_t + * @node_ptr_ptr: a pointer to the return value from + * fluid_hashtable_lookup_node() + * @notify: %TRUE if the destroy notify handlers are to be called + * + * Removes a node from the hash table and updates the node count. The + * node is freed. No table resize is performed. + * + * If @notify is %TRUE then the destroy notify functions are called + * for the key and value of the hash node. + * + * @node_ptr_ptr is a pass-by-reference in/out parameter. When the + * function is called, it should point to the pointer to the node to + * remove. This level of indirection is required so that the pointer + * may be updated appropriately once the node has been removed. + * + * Before the function returns, the pointer at @node_ptr_ptr will be + * updated to point to the position in the table that contains the + * pointer to the "next" node in the chain. This makes this function + * convenient to use from functions that iterate over the entire + * table. If there is no further item in the chain then the + * #fluid_hashnode_t pointer will be %NULL (ie: **node_ptr_ptr == %NULL). + * + * Since the pointer in the table to the removed node is replaced with + * either a pointer to the next node or a %NULL pointer as + * appropriate, the pointer at the end of @node_ptr_ptr will never be + * modified at all. Stay tuned. :) + */ +static void +fluid_hashtable_remove_node(fluid_hashtable_t *hashtable, + fluid_hashnode_t ***node_ptr_ptr, int notify) +{ + fluid_hashnode_t **node_ptr, *node; + + node_ptr = *node_ptr_ptr; + node = *node_ptr; + + *node_ptr = node->next; + + if(notify && hashtable->key_destroy_func) + { + hashtable->key_destroy_func(node->key); + } + + if(notify && hashtable->value_destroy_func) + { + hashtable->value_destroy_func(node->value); + } + + FLUID_FREE(node); + + hashtable->nnodes--; +} + +/* + * fluid_hashtable_remove_all_nodes: + * @hashtable: our #fluid_hashtable_t + * @notify: %TRUE if the destroy notify handlers are to be called + * + * Removes all nodes from the table. Since this may be a precursor to + * freeing the table entirely, no resize is performed. + * + * If @notify is %TRUE then the destroy notify functions are called + * for the key and value of the hash node. + */ +static void +fluid_hashtable_remove_all_nodes(fluid_hashtable_t *hashtable, int notify) +{ + fluid_hashnode_t **node_ptr; + int i; + + for(i = 0; i < hashtable->size; i++) + { + for(node_ptr = &hashtable->nodes[i]; *node_ptr != NULL;) + { + fluid_hashtable_remove_node(hashtable, &node_ptr, notify); + } + } + + hashtable->nnodes = 0; +} + +/* + * fluid_hashtable_resize: + * @hashtable: our #fluid_hashtable_t + * + * Resizes the hash table to the optimal size based on the number of + * nodes currently held. If you call this function then a resize will + * occur, even if one does not need to occur. Use + * fluid_hashtable_maybe_resize() instead. + */ +static void +fluid_hashtable_resize(fluid_hashtable_t *hashtable) +{ + fluid_hashnode_t **new_nodes; + fluid_hashnode_t *node; + fluid_hashnode_t *next; + unsigned int hash_val; + int new_size; + int i; + + new_size = spaced_primes_closest(hashtable->nnodes); + new_size = (new_size < HASH_TABLE_MIN_SIZE) ? HASH_TABLE_MIN_SIZE : + ((new_size > HASH_TABLE_MAX_SIZE) ? HASH_TABLE_MAX_SIZE : new_size); + + new_nodes = FLUID_ARRAY(fluid_hashnode_t *, new_size); + + if(!new_nodes) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return; + } + + FLUID_MEMSET(new_nodes, 0, new_size * sizeof(fluid_hashnode_t *)); + + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = next) + { + next = node->next; + + hash_val = node->key_hash % new_size; + + node->next = new_nodes[hash_val]; + new_nodes[hash_val] = node; + } + } + + FLUID_FREE(hashtable->nodes); + hashtable->nodes = new_nodes; + hashtable->size = new_size; +} + +/* + * fluid_hashtable_maybe_resize: + * @hashtable: our #fluid_hashtable_t + * + * Resizes the hash table, if needed. + * + * Essentially, calls fluid_hashtable_resize() if the table has strayed + * too far from its ideal size for its number of nodes. + */ +static FLUID_INLINE void +fluid_hashtable_maybe_resize(fluid_hashtable_t *hashtable) +{ + int nnodes = hashtable->nnodes; + int size = hashtable->size; + + if((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) || + (3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE)) + { + fluid_hashtable_resize(hashtable); + } +} + +/** + * new_fluid_hashtable: + * @hash_func: a function to create a hash value from a key. + * Hash values are used to determine where keys are stored within the + * #fluid_hashtable_t data structure. The fluid_direct_hash(), fluid_int_hash() and + * fluid_str_hash() functions are provided for some common types of keys. + * If hash_func is %NULL, fluid_direct_hash() is used. + * @key_equal_func: a function to check two keys for equality. This is + * used when looking up keys in the #fluid_hashtable_t. The fluid_direct_equal(), + * fluid_int_equal() and fluid_str_equal() functions are provided for the most + * common types of keys. If @key_equal_func is %NULL, keys are compared + * directly in a similar fashion to fluid_direct_equal(), but without the + * overhead of a function call. + * + * Creates a new #fluid_hashtable_t with a reference count of 1. + * + * Return value: a new #fluid_hashtable_t. + **/ +fluid_hashtable_t * +new_fluid_hashtable(fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func) +{ + return new_fluid_hashtable_full(hash_func, key_equal_func, NULL, NULL); +} + + +/** + * new_fluid_hashtable_full: + * @hash_func: a function to create a hash value from a key. + * @key_equal_func: a function to check two keys for equality. + * @key_destroy_func: a function to free the memory allocated for the key + * used when removing the entry from the #fluid_hashtable_t or %NULL if you + * don't want to supply such a function. + * @value_destroy_func: a function to free the memory allocated for the + * value used when removing the entry from the #fluid_hashtable_t or %NULL if + * you don't want to supply such a function. + * + * Creates a new #fluid_hashtable_t like fluid_hashtable_new() with a reference count + * of 1 and allows to specify functions to free the memory allocated for the + * key and value that get called when removing the entry from the #fluid_hashtable_t. + * + * Return value: a new #fluid_hashtable_t. + **/ +fluid_hashtable_t * +new_fluid_hashtable_full(fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func, + fluid_destroy_notify_t key_destroy_func, + fluid_destroy_notify_t value_destroy_func) +{ + fluid_hashtable_t *hashtable; + + hashtable = FLUID_NEW(fluid_hashtable_t); + + if(!hashtable) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + hashtable->size = HASH_TABLE_MIN_SIZE; + hashtable->nnodes = 0; + hashtable->hash_func = hash_func ? hash_func : fluid_direct_hash; + hashtable->key_equal_func = key_equal_func; + fluid_atomic_int_set(&hashtable->ref_count, 1); + hashtable->key_destroy_func = key_destroy_func; + hashtable->value_destroy_func = value_destroy_func; + hashtable->nodes = FLUID_ARRAY(fluid_hashnode_t *, hashtable->size); + if(hashtable->nodes == NULL) + { + delete_fluid_hashtable(hashtable); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + FLUID_MEMSET(hashtable->nodes, 0, hashtable->size * sizeof(*hashtable->nodes)); + + return hashtable; +} + +/** + * fluid_hashtable_iter_init: + * @iter: an uninitialized #fluid_hashtable_iter_t. + * @hashtable: a #fluid_hashtable_t. + * + * Initializes a key/value pair iterator and associates it with + * @hashtable. Modifying the hash table after calling this function + * invalidates the returned iterator. + * |[ + * fluid_hashtable_iter_t iter; + * gpointer key, value; + * + * fluid_hashtable_iter_init (&iter, hashtable); + * while (fluid_hashtable_iter_next (&iter, &key, &value)) + * { + * /* do something with key and value */ + * } + * ]| + * + * Since: 2.16 + **/ +void +fluid_hashtable_iter_init(fluid_hashtable_iter_t *iter, + fluid_hashtable_t *hashtable) +{ + RealIter *ri = (RealIter *) iter; + + fluid_return_if_fail(iter != NULL); + fluid_return_if_fail(hashtable != NULL); + + ri->hashtable = hashtable; + ri->prev_node = NULL; + ri->node = NULL; + ri->position = -1; + ri->pre_advanced = FALSE; +} + +/** + * fluid_hashtable_iter_next: + * @iter: an initialized #fluid_hashtable_iter_t. + * @key: a location to store the key, or %NULL. + * @value: a location to store the value, or %NULL. + * + * Advances @iter and retrieves the key and/or value that are now + * pointed to as a result of this advancement. If %FALSE is returned, + * @key and @value are not set, and the iterator becomes invalid. + * + * Return value: %FALSE if the end of the #fluid_hashtable_t has been reached. + * + * Since: 2.16 + **/ +int +fluid_hashtable_iter_next(fluid_hashtable_iter_t *iter, void **key, + void **value) +{ + RealIter *ri = (RealIter *) iter; + + fluid_return_val_if_fail(iter != NULL, FALSE); + + if(ri->pre_advanced) + { + ri->pre_advanced = FALSE; + + if(ri->node == NULL) + { + return FALSE; + } + } + else + { + if(ri->node != NULL) + { + ri->prev_node = ri->node; + ri->node = ri->node->next; + } + + while(ri->node == NULL) + { + ri->position++; + + if(ri->position >= ri->hashtable->size) + { + return FALSE; + } + + ri->prev_node = NULL; + ri->node = ri->hashtable->nodes[ri->position]; + } + } + + if(key != NULL) + { + *key = ri->node->key; + } + + if(value != NULL) + { + *value = ri->node->value; + } + + return TRUE; +} + +/** + * fluid_hashtable_iter_get_hash_table: + * @iter: an initialized #fluid_hashtable_iter_t. + * + * Returns the #fluid_hashtable_t associated with @iter. + * + * Return value: the #fluid_hashtable_t associated with @iter. + * + * Since: 2.16 + **/ +fluid_hashtable_t * +fluid_hashtable_iter_get_hash_table(fluid_hashtable_iter_t *iter) +{ + fluid_return_val_if_fail(iter != NULL, NULL); + + return ((RealIter *) iter)->hashtable; +} + +static void +iter_remove_or_steal(RealIter *ri, int notify) +{ + fluid_hashnode_t *prev; + fluid_hashnode_t *node; + int position; + + fluid_return_if_fail(ri != NULL); + fluid_return_if_fail(ri->node != NULL); + + prev = ri->prev_node; + node = ri->node; + position = ri->position; + + /* pre-advance the iterator since we will remove the node */ + + ri->node = ri->node->next; + /* ri->prev_node is still the correct previous node */ + + while(ri->node == NULL) + { + ri->position++; + + if(ri->position >= ri->hashtable->size) + { + break; + } + + ri->prev_node = NULL; + ri->node = ri->hashtable->nodes[ri->position]; + } + + ri->pre_advanced = TRUE; + + /* remove the node */ + + if(prev != NULL) + { + prev->next = node->next; + } + else + { + ri->hashtable->nodes[position] = node->next; + } + + if(notify) + { + if(ri->hashtable->key_destroy_func) + { + ri->hashtable->key_destroy_func(node->key); + } + + if(ri->hashtable->value_destroy_func) + { + ri->hashtable->value_destroy_func(node->value); + } + } + + FLUID_FREE(node); + + ri->hashtable->nnodes--; +} + +/** + * fluid_hashtable_iter_remove(): + * @iter: an initialized #fluid_hashtable_iter_t. + * + * Removes the key/value pair currently pointed to by the iterator + * from its associated #fluid_hashtable_t. Can only be called after + * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more + * than once for the same key/value pair. + * + * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the + * key and value are freed using the supplied destroy functions, otherwise + * you have to make sure that any dynamically allocated values are freed + * yourself. + * + * Since: 2.16 + **/ +void +fluid_hashtable_iter_remove(fluid_hashtable_iter_t *iter) +{ + iter_remove_or_steal((RealIter *) iter, TRUE); +} + +/** + * fluid_hashtable_iter_steal(): + * @iter: an initialized #fluid_hashtable_iter_t. + * + * Removes the key/value pair currently pointed to by the iterator + * from its associated #fluid_hashtable_t, without calling the key and value + * destroy functions. Can only be called after + * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more + * than once for the same key/value pair. + * + * Since: 2.16 + **/ +void +fluid_hashtable_iter_steal(fluid_hashtable_iter_t *iter) +{ + iter_remove_or_steal((RealIter *) iter, FALSE); +} + + +/** + * fluid_hashtable_ref: + * @hashtable: a valid #fluid_hashtable_t. + * + * Atomically increments the reference count of @hashtable by one. + * This function is MT-safe and may be called from any thread. + * + * Return value: the passed in #fluid_hashtable_t. + * + * Since: 2.10 + **/ +fluid_hashtable_t * +fluid_hashtable_ref(fluid_hashtable_t *hashtable) +{ + fluid_return_val_if_fail(hashtable != NULL, NULL); + fluid_return_val_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0, hashtable); + + fluid_atomic_int_add(&hashtable->ref_count, 1); + return hashtable; +} + +/** + * fluid_hashtable_unref: + * @hashtable: a valid #fluid_hashtable_t. + * + * Atomically decrements the reference count of @hashtable by one. + * If the reference count drops to 0, all keys and values will be + * destroyed, and all memory allocated by the hash table is released. + * This function is MT-safe and may be called from any thread. + * + * Since: 2.10 + **/ +void +fluid_hashtable_unref(fluid_hashtable_t *hashtable) +{ + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0); + + if(fluid_atomic_int_exchange_and_add(&hashtable->ref_count, -1) - 1 == 0) + { + fluid_hashtable_remove_all_nodes(hashtable, TRUE); + FLUID_FREE(hashtable->nodes); + FLUID_FREE(hashtable); + } +} + +/** + * delete_fluid_hashtable: + * @hashtable: a #fluid_hashtable_t. + * + * Destroys all keys and values in the #fluid_hashtable_t and decrements its + * reference count by 1. If keys and/or values are dynamically allocated, + * you should either free them first or create the #fluid_hashtable_t with destroy + * notifiers using fluid_hashtable_new_full(). In the latter case the destroy + * functions you supplied will be called on all keys and values during the + * destruction phase. + **/ +void +delete_fluid_hashtable(fluid_hashtable_t *hashtable) +{ + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0); + + fluid_hashtable_remove_all(hashtable); + fluid_hashtable_unref(hashtable); +} + +/** + * fluid_hashtable_lookup: + * @hashtable: a #fluid_hashtable_t. + * @key: the key to look up. + * + * Looks up a key in a #fluid_hashtable_t. Note that this function cannot + * distinguish between a key that is not present and one which is present + * and has the value %NULL. If you need this distinction, use + * fluid_hashtable_lookup_extended(). + * + * Return value: the associated value, or %NULL if the key is not found. + **/ +void * +fluid_hashtable_lookup(fluid_hashtable_t *hashtable, const void *key) +{ + fluid_hashnode_t *node; + + fluid_return_val_if_fail(hashtable != NULL, NULL); + + node = *fluid_hashtable_lookup_node(hashtable, key, NULL); + + return node ? node->value : NULL; +} + +/** + * fluid_hashtable_lookup_extended: + * @hashtable: a #fluid_hashtable_t. + * @lookup_key: the key to look up. + * @orig_key: returns the original key. + * @value: returns the value associated with the key. + * + * Looks up a key in the #fluid_hashtable_t, returning the original key and the + * associated value and a #gboolean which is %TRUE if the key was found. This + * is useful if you need to free the memory allocated for the original key, + * for example before calling fluid_hashtable_remove(). + * + * Return value: %TRUE if the key was found in the #fluid_hashtable_t. + **/ +int +fluid_hashtable_lookup_extended(fluid_hashtable_t *hashtable, + const void *lookup_key, + void **orig_key, void **value) +{ + fluid_hashnode_t *node; + + fluid_return_val_if_fail(hashtable != NULL, FALSE); + + node = *fluid_hashtable_lookup_node(hashtable, lookup_key, NULL); + + if(node == NULL) + { + return FALSE; + } + + if(orig_key) + { + *orig_key = node->key; + } + + if(value) + { + *value = node->value; + } + + return TRUE; +} + +/* + * fluid_hashtable_insert_internal: + * @hashtable: our #fluid_hashtable_t + * @key: the key to insert + * @value: the value to insert + * @keep_new_key: if %TRUE and this key already exists in the table + * then call the destroy notify function on the old key. If %FALSE + * then call the destroy notify function on the new key. + * + * Implements the common logic for the fluid_hashtable_insert() and + * fluid_hashtable_replace() functions. + * + * Do a lookup of @key. If it is found, replace it with the new + * @value (and perhaps the new @key). If it is not found, create a + * new node. + */ +static void +fluid_hashtable_insert_internal(fluid_hashtable_t *hashtable, void *key, + void *value, int keep_new_key) +{ + fluid_hashnode_t **node_ptr, *node; + unsigned int key_hash; + + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0); + + node_ptr = fluid_hashtable_lookup_node(hashtable, key, &key_hash); + + if((node = *node_ptr)) + { + if(keep_new_key) + { + if(hashtable->key_destroy_func) + { + hashtable->key_destroy_func(node->key); + } + + node->key = key; + } + else + { + if(hashtable->key_destroy_func) + { + hashtable->key_destroy_func(key); + } + } + + if(hashtable->value_destroy_func) + { + hashtable->value_destroy_func(node->value); + } + + node->value = value; + } + else + { + node = FLUID_NEW(fluid_hashnode_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return; + } + + node->key = key; + node->value = value; + node->key_hash = key_hash; + node->next = NULL; + + *node_ptr = node; + hashtable->nnodes++; + fluid_hashtable_maybe_resize(hashtable); + } +} + +/** + * fluid_hashtable_insert: + * @hashtable: a #fluid_hashtable_t. + * @key: a key to insert. + * @value: the value to associate with the key. + * + * Inserts a new key and value into a #fluid_hashtable_t. + * + * If the key already exists in the #fluid_hashtable_t its current value is replaced + * with the new value. If you supplied a @value_destroy_func when creating the + * #fluid_hashtable_t, the old value is freed using that function. If you supplied + * a @key_destroy_func when creating the #fluid_hashtable_t, the passed key is freed + * using that function. + **/ +void +fluid_hashtable_insert(fluid_hashtable_t *hashtable, void *key, void *value) +{ + fluid_hashtable_insert_internal(hashtable, key, value, FALSE); +} + +/** + * fluid_hashtable_replace: + * @hashtable: a #fluid_hashtable_t. + * @key: a key to insert. + * @value: the value to associate with the key. + * + * Inserts a new key and value into a #fluid_hashtable_t similar to + * fluid_hashtable_insert(). The difference is that if the key already exists + * in the #fluid_hashtable_t, it gets replaced by the new key. If you supplied a + * @value_destroy_func when creating the #fluid_hashtable_t, the old value is freed + * using that function. If you supplied a @key_destroy_func when creating the + * #fluid_hashtable_t, the old key is freed using that function. + **/ +void +fluid_hashtable_replace(fluid_hashtable_t *hashtable, void *key, void *value) +{ + fluid_hashtable_insert_internal(hashtable, key, value, TRUE); +} + +/* + * fluid_hashtable_remove_internal: + * @hashtable: our #fluid_hashtable_t + * @key: the key to remove + * @notify: %TRUE if the destroy notify handlers are to be called + * Return value: %TRUE if a node was found and removed, else %FALSE + * + * Implements the common logic for the fluid_hashtable_remove() and + * fluid_hashtable_steal() functions. + * + * Do a lookup of @key and remove it if it is found, calling the + * destroy notify handlers only if @notify is %TRUE. + */ +static int +fluid_hashtable_remove_internal(fluid_hashtable_t *hashtable, const void *key, + int notify) +{ + fluid_hashnode_t **node_ptr; + + fluid_return_val_if_fail(hashtable != NULL, FALSE); + + node_ptr = fluid_hashtable_lookup_node(hashtable, key, NULL); + + if(*node_ptr == NULL) + { + return FALSE; + } + + fluid_hashtable_remove_node(hashtable, &node_ptr, notify); + fluid_hashtable_maybe_resize(hashtable); + + return TRUE; +} + +/** + * fluid_hashtable_remove: + * @hashtable: a #fluid_hashtable_t. + * @key: the key to remove. + * + * Removes a key and its associated value from a #fluid_hashtable_t. + * + * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the + * key and value are freed using the supplied destroy functions, otherwise + * you have to make sure that any dynamically allocated values are freed + * yourself. + * + * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. + **/ +int +fluid_hashtable_remove(fluid_hashtable_t *hashtable, const void *key) +{ + return fluid_hashtable_remove_internal(hashtable, key, TRUE); +} + +/** + * fluid_hashtable_steal: + * @hashtable: a #fluid_hashtable_t. + * @key: the key to remove. + * + * Removes a key and its associated value from a #fluid_hashtable_t without + * calling the key and value destroy functions. + * + * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. + **/ +int +fluid_hashtable_steal(fluid_hashtable_t *hashtable, const void *key) +{ + return fluid_hashtable_remove_internal(hashtable, key, FALSE); +} + +/** + * fluid_hashtable_remove_all: + * @hashtable: a #fluid_hashtable_t + * + * Removes all keys and their associated values from a #fluid_hashtable_t. + * + * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the keys + * and values are freed using the supplied destroy functions, otherwise you + * have to make sure that any dynamically allocated values are freed + * yourself. + * + * Since: 2.12 + **/ +void +fluid_hashtable_remove_all(fluid_hashtable_t *hashtable) +{ + fluid_return_if_fail(hashtable != NULL); + + fluid_hashtable_remove_all_nodes(hashtable, TRUE); + fluid_hashtable_maybe_resize(hashtable); +} + +/** + * fluid_hashtable_steal_all: + * @hashtable: a #fluid_hashtable_t. + * + * Removes all keys and their associated values from a #fluid_hashtable_t + * without calling the key and value destroy functions. + * + * Since: 2.12 + **/ +void +fluid_hashtable_steal_all(fluid_hashtable_t *hashtable) +{ + fluid_return_if_fail(hashtable != NULL); + + fluid_hashtable_remove_all_nodes(hashtable, FALSE); + fluid_hashtable_maybe_resize(hashtable); +} + +/* + * fluid_hashtable_foreach_remove_or_steal: + * @hashtable: our #fluid_hashtable_t + * @func: the user's callback function + * @user_data: data for @func + * @notify: %TRUE if the destroy notify handlers are to be called + * + * Implements the common logic for fluid_hashtable_foreach_remove() and + * fluid_hashtable_foreach_steal(). + * + * Iterates over every node in the table, calling @func with the key + * and value of the node (and @user_data). If @func returns %TRUE the + * node is removed from the table. + * + * If @notify is true then the destroy notify handlers will be called + * for each removed node. + */ +static unsigned int +fluid_hashtable_foreach_remove_or_steal(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data, + int notify) +{ + fluid_hashnode_t *node, **node_ptr; + unsigned int deleted = 0; + int i; + + for(i = 0; i < hashtable->size; i++) + { + for(node_ptr = &hashtable->nodes[i]; (node = *node_ptr) != NULL;) + { + if((* func)(node->key, node->value, user_data)) + { + fluid_hashtable_remove_node(hashtable, &node_ptr, notify); + deleted++; + } + else + { + node_ptr = &node->next; + } + } + } + + fluid_hashtable_maybe_resize(hashtable); + + return deleted; +} + +#if 0 +/** + * fluid_hashtable_foreach_remove: + * @hashtable: a #fluid_hashtable_t. + * @func: the function to call for each key/value pair. + * @user_data: user data to pass to the function. + * + * Calls the given function for each key/value pair in the #fluid_hashtable_t. + * If the function returns %TRUE, then the key/value pair is removed from the + * #fluid_hashtable_t. If you supplied key or value destroy functions when creating + * the #fluid_hashtable_t, they are used to free the memory allocated for the removed + * keys and values. + * + * See #fluid_hashtable_iter_t for an alternative way to loop over the + * key/value pairs in the hash table. + * + * Return value: the number of key/value pairs removed. + **/ +static unsigned int +fluid_hashtable_foreach_remove(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data) +{ + fluid_return_val_if_fail(hashtable != NULL, 0); + fluid_return_val_if_fail(func != NULL, 0); + + return fluid_hashtable_foreach_remove_or_steal(hashtable, func, user_data, TRUE); +} +#endif + +/** + * fluid_hashtable_foreach_steal: + * @hashtable: a #fluid_hashtable_t. + * @func: the function to call for each key/value pair. + * @user_data: user data to pass to the function. + * + * Calls the given function for each key/value pair in the #fluid_hashtable_t. + * If the function returns %TRUE, then the key/value pair is removed from the + * #fluid_hashtable_t, but no key or value destroy functions are called. + * + * See #fluid_hashtable_iter_t for an alternative way to loop over the + * key/value pairs in the hash table. + * + * Return value: the number of key/value pairs removed. + **/ +unsigned int +fluid_hashtable_foreach_steal(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data) +{ + fluid_return_val_if_fail(hashtable != NULL, 0); + fluid_return_val_if_fail(func != NULL, 0); + + return fluid_hashtable_foreach_remove_or_steal(hashtable, func, user_data, FALSE); +} + +/** + * fluid_hashtable_foreach: + * @hashtable: a #fluid_hashtable_t. + * @func: the function to call for each key/value pair. + * @user_data: user data to pass to the function. + * + * Calls the given function for each of the key/value pairs in the + * #fluid_hashtable_t. The function is passed the key and value of each + * pair, and the given @user_data parameter. The hash table may not + * be modified while iterating over it (you can't add/remove + * items). To remove all items matching a predicate, use + * fluid_hashtable_foreach_remove(). + * + * See fluid_hashtable_find() for performance caveats for linear + * order searches in contrast to fluid_hashtable_lookup(). + **/ +void +fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hr_func_t func, + void *user_data) +{ + fluid_hashnode_t *node; + int i; + + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(func != NULL); + + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + (* func)(node->key, node->value, user_data); + } + } +} + +/** + * fluid_hashtable_find: + * @hashtable: a #fluid_hashtable_t. + * @predicate: function to test the key/value pairs for a certain property. + * @user_data: user data to pass to the function. + * + * Calls the given function for key/value pairs in the #fluid_hashtable_t until + * @predicate returns %TRUE. The function is passed the key and value of + * each pair, and the given @user_data parameter. The hash table may not + * be modified while iterating over it (you can't add/remove items). + * + * Note, that hash tables are really only optimized for forward lookups, + * i.e. fluid_hashtable_lookup(). + * So code that frequently issues fluid_hashtable_find() or + * fluid_hashtable_foreach() (e.g. in the order of once per every entry in a + * hash table) should probably be reworked to use additional or different + * data structures for reverse lookups (keep in mind that an O(n) find/foreach + * operation issued for all n values in a hash table ends up needing O(n*n) + * operations). + * + * Return value: The value of the first key/value pair is returned, for which + * func evaluates to %TRUE. If no pair with the requested property is found, + * %NULL is returned. + * + * Since: 2.4 + **/ +void * +fluid_hashtable_find(fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, + void *user_data) +{ + fluid_hashnode_t *node; + int i; + + fluid_return_val_if_fail(hashtable != NULL, NULL); + fluid_return_val_if_fail(predicate != NULL, NULL); + + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + if(predicate(node->key, node->value, user_data)) + { + return node->value; + } + } + } + + return NULL; +} + +/** + * fluid_hashtable_size: + * @hashtable: a #fluid_hashtable_t. + * + * Returns the number of elements contained in the #fluid_hashtable_t. + * + * Return value: the number of key/value pairs in the #fluid_hashtable_t. + **/ +unsigned int +fluid_hashtable_size(fluid_hashtable_t *hashtable) +{ + fluid_return_val_if_fail(hashtable != NULL, 0); + + return hashtable->nnodes; +} + +/** + * fluid_hashtable_get_keys: + * @hashtable: a #fluid_hashtable_t + * + * Retrieves every key inside @hashtable. The returned data is valid + * until @hashtable is modified. + * + * Return value: a #GList containing all the keys inside the hash + * table. The content of the list is owned by the hash table and + * should not be modified or freed. Use delete_fluid_list() when done + * using the list. + * + * Since: 2.14 + */ +fluid_list_t * +fluid_hashtable_get_keys(fluid_hashtable_t *hashtable) +{ + fluid_hashnode_t *node; + int i; + fluid_list_t *retval; + + fluid_return_val_if_fail(hashtable != NULL, NULL); + + retval = NULL; + + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + retval = fluid_list_prepend(retval, node->key); + } + } + + return retval; +} + +/** + * fluid_hashtable_get_values: + * @hashtable: a #fluid_hashtable_t + * + * Retrieves every value inside @hashtable. The returned data is + * valid until @hashtable is modified. + * + * Return value: a #GList containing all the values inside the hash + * table. The content of the list is owned by the hash table and + * should not be modified or freed. Use delete_fluid_list() when done + * using the list. + * + * Since: 2.14 + */ +fluid_list_t * +fluid_hashtable_get_values(fluid_hashtable_t *hashtable) +{ + fluid_hashnode_t *node; + int i; + fluid_list_t *retval; + + fluid_return_val_if_fail(hashtable != NULL, NULL); + + retval = NULL; + + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + retval = fluid_list_prepend(retval, node->value); + } + } + + return retval; +} + + +/* Extracted from glib/gstring.c */ + + +/** + * fluid_str_equal: + * @v1: a key + * @v2: a key to compare with @v1 + * + * Compares two strings for byte-by-byte equality and returns %TRUE + * if they are equal. It can be passed to new_fluid_hashtable() as the + * @key_equal_func parameter, when using strings as keys in a #Ghashtable. + * + * Returns: %TRUE if the two keys match + */ +int +fluid_str_equal(const void *v1, const void *v2) +{ + const char *string1 = v1; + const char *string2 = v2; + + return FLUID_STRCMP(string1, string2) == 0; +} + +/** + * fluid_str_hash: + * @v: a string key + * + * Converts a string to a hash value. + * It can be passed to new_fluid_hashtable() as the @hash_func + * parameter, when using strings as keys in a #fluid_hashtable_t. + * + * Returns: a hash value corresponding to the key + */ +unsigned int +fluid_str_hash(const void *v) +{ + /* 31 bit hash function */ + const signed char *p = v; + uint32_t h = *p; + + if(h) + { + for(p += 1; *p != '\0'; p++) + { + h = (h << 5) - h + *p; + } + } + + return h; +} + + +/* Extracted from glib/gutils.c */ + + +/** + * fluid_direct_equal: + * @v1: a key. + * @v2: a key to compare with @v1. + * + * Compares two #gpointer arguments and returns %TRUE if they are equal. + * It can be passed to new_fluid_hashtable() as the @key_equal_func + * parameter, when using pointers as keys in a #fluid_hashtable_t. + * + * Returns: %TRUE if the two keys match. + */ +int +fluid_direct_equal(const void *v1, const void *v2) +{ + return v1 == v2; +} + +/** + * fluid_direct_hash: + * @v: a void * key + * + * Converts a gpointer to a hash value. + * It can be passed to g_hashtable_new() as the @hash_func parameter, + * when using pointers as keys in a #fluid_hashtable_t. + * + * Returns: a hash value corresponding to the key. + */ +unsigned int +fluid_direct_hash(const void *v) +{ + return FLUID_POINTER_TO_UINT(v); +} + +/** + * fluid_int_equal: + * @v1: a pointer to a int key. + * @v2: a pointer to a int key to compare with @v1. + * + * Compares the two #gint values being pointed to and returns + * %TRUE if they are equal. + * It can be passed to g_hashtable_new() as the @key_equal_func + * parameter, when using pointers to integers as keys in a #fluid_hashtable_t. + * + * Returns: %TRUE if the two keys match. + */ +int +fluid_int_equal(const void *v1, const void *v2) +{ + return *((const int *) v1) == *((const int *) v2); +} + +/** + * fluid_int_hash: + * @v: a pointer to a int key + * + * Converts a pointer to a #gint to a hash value. + * It can be passed to g_hashtable_new() as the @hash_func parameter, + * when using pointers to integers values as keys in a #fluid_hashtable_t. + * + * Returns: a hash value corresponding to the key. + */ +unsigned int +fluid_int_hash(const void *v) +{ + return *(const int *) v; +} diff --git a/libs/fluidsynth/src/utils/fluid_hash.h b/libs/fluidsynth/src/utils/fluid_hash.h new file mode 100644 index 00000000000..b801876833a --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_hash.h @@ -0,0 +1,130 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * 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 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/* + * Adapted for FluidSynth use by Josh Green + * September 8, 2009 from glib 2.18.4 + * + * - Self contained (no dependencies on glib) + * - changed names to fluid_hashtable_... + */ + +#ifndef _FLUID_HASH_H +#define _FLUID_HASH_H + +#include "fluidsynth_priv.h" +#include "fluid_list.h" +#include "fluid_sys.h" + +/* Extracted from gtypes.h */ +typedef void (*fluid_destroy_notify_t)(void *data); +typedef unsigned int (*fluid_hash_func_t)(const void *key); +typedef int (*fluid_equal_func_t)(const void *a, const void *b); +/* End gtypes.h extraction */ + +typedef int (*fluid_hr_func_t)(void *key, void *value, void *user_data); +typedef struct _fluid_hashtable_iter_t fluid_hashtable_iter_t; + +typedef struct _fluid_hashnode_t fluid_hashnode_t; + +struct _fluid_hashnode_t +{ + void *key; + void *value; + fluid_hashnode_t *next; + unsigned int key_hash; +}; + +struct _fluid_hashtable_t +{ + int size; + int nnodes; + fluid_hashnode_t **nodes; + fluid_hash_func_t hash_func; + fluid_equal_func_t key_equal_func; + fluid_atomic_int_t ref_count; + fluid_destroy_notify_t key_destroy_func; + fluid_destroy_notify_t value_destroy_func; + fluid_rec_mutex_t mutex; // Optionally used in other modules (fluid_settings.c for example) +}; + +struct _fluid_hashtable_iter_t +{ + /*< private >*/ + void *dummy1; + void *dummy2; + void *dummy3; + int dummy4; + int dummy5; // Bool + void *dummy6; +}; + +fluid_hashtable_t *new_fluid_hashtable(fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func); +fluid_hashtable_t *new_fluid_hashtable_full(fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func, + fluid_destroy_notify_t key_destroy_func, + fluid_destroy_notify_t value_destroy_func); +void delete_fluid_hashtable(fluid_hashtable_t *hashtable); + +void fluid_hashtable_iter_init(fluid_hashtable_iter_t *iter, fluid_hashtable_t *hashtable); +int fluid_hashtable_iter_next(fluid_hashtable_iter_t *iter, void **key, void **value); +fluid_hashtable_t *fluid_hashtable_iter_get_hash_table(fluid_hashtable_iter_t *iter); +void fluid_hashtable_iter_remove(fluid_hashtable_iter_t *iter); +void fluid_hashtable_iter_steal(fluid_hashtable_iter_t *iter); + +fluid_hashtable_t *fluid_hashtable_ref(fluid_hashtable_t *hashtable); +void fluid_hashtable_unref(fluid_hashtable_t *hashtable); + +void *fluid_hashtable_lookup(fluid_hashtable_t *hashtable, const void *key); +int fluid_hashtable_lookup_extended(fluid_hashtable_t *hashtable, const void *lookup_key, + void **orig_key, void **value); + +void fluid_hashtable_insert(fluid_hashtable_t *hashtable, void *key, void *value); +void fluid_hashtable_replace(fluid_hashtable_t *hashtable, void *key, void *value); + +int fluid_hashtable_remove(fluid_hashtable_t *hashtable, const void *key); +int fluid_hashtable_steal(fluid_hashtable_t *hashtable, const void *key); +void fluid_hashtable_remove_all(fluid_hashtable_t *hashtable); +void fluid_hashtable_steal_all(fluid_hashtable_t *hashtable); +unsigned int fluid_hashtable_foreach_steal(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data); +void fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hr_func_t func, + void *user_data); +void *fluid_hashtable_find(fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, + void *user_data); +unsigned int fluid_hashtable_size(fluid_hashtable_t *hashtable); +fluid_list_t *fluid_hashtable_get_keys(fluid_hashtable_t *hashtable); +fluid_list_t *fluid_hashtable_get_values(fluid_hashtable_t *hashtable); + +int fluid_str_equal(const void *v1, const void *v2); +unsigned int fluid_str_hash(const void *v); +int fluid_direct_equal(const void *v1, const void *v2); +unsigned int fluid_direct_hash(const void *v); +int fluid_int_equal(const void *v1, const void *v2); +unsigned int fluid_int_hash(const void *v); + +#endif /* _FLUID_HASH_H */ diff --git a/libs/fluidsynth/src/utils/fluid_list.c b/libs/fluidsynth/src/utils/fluid_list.c new file mode 100644 index 00000000000..c88e2aec097 --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_list.c @@ -0,0 +1,337 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * 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 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + */ + +/* + * Modified by the GLib Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + + + +#include "fluid_sys.h" +#include "fluid_list.h" + + +fluid_list_t * +new_fluid_list(void) +{ + fluid_list_t *list; + list = (fluid_list_t *) FLUID_MALLOC(sizeof(fluid_list_t)); + list->data = NULL; + list->next = NULL; + return list; +} + +void +delete_fluid_list(fluid_list_t *list) +{ + fluid_list_t *next; + fluid_return_if_fail(list != NULL); + + while(list) + { + next = list->next; + FLUID_FREE(list); + list = next; + } +} + +void +delete1_fluid_list(fluid_list_t *list) +{ + FLUID_FREE(list); +} + +fluid_list_t * +fluid_list_append(fluid_list_t *list, void *data) +{ + fluid_list_t *new_list; + fluid_list_t *last; + + new_list = new_fluid_list(); + new_list->data = data; + + if(list) + { + last = fluid_list_last(list); + /* g_assert (last != NULL); */ + last->next = new_list; + + return list; + } + else + { + return new_list; + } +} + +fluid_list_t * +fluid_list_prepend(fluid_list_t *list, void *data) +{ + fluid_list_t *new_list; + + new_list = new_fluid_list(); + new_list->data = data; + new_list->next = list; + + return new_list; +} + +fluid_list_t * +fluid_list_nth(fluid_list_t *list, int n) +{ + while((n-- > 0) && list) + { + list = list->next; + } + + return list; +} + +fluid_list_t * +fluid_list_remove(fluid_list_t *list, void *data) +{ + fluid_list_t *tmp; + fluid_list_t *prev; + + prev = NULL; + tmp = list; + + while(tmp) + { + if(tmp->data == data) + { + if(prev) + { + prev->next = tmp->next; + } + + if(list == tmp) + { + list = list->next; + } + + tmp->next = NULL; + delete_fluid_list(tmp); + + break; + } + + prev = tmp; + tmp = tmp->next; + } + + return list; +} + +fluid_list_t * +fluid_list_remove_link(fluid_list_t *list, fluid_list_t *link) +{ + fluid_list_t *tmp; + fluid_list_t *prev; + + prev = NULL; + tmp = list; + + while(tmp) + { + if(tmp == link) + { + if(prev) + { + prev->next = tmp->next; + } + + if(list == tmp) + { + list = list->next; + } + + tmp->next = NULL; + break; + } + + prev = tmp; + tmp = tmp->next; + } + + return list; +} + +static fluid_list_t * +fluid_list_sort_merge(fluid_list_t *l1, fluid_list_t *l2, fluid_compare_func_t compare_func) +{ + fluid_list_t list, *l; + + l = &list; + + while(l1 && l2) + { + if(compare_func(l1->data, l2->data) < 0) + { + l = l->next = l1; + l1 = l1->next; + } + else + { + l = l->next = l2; + l2 = l2->next; + } + } + + l->next = l1 ? l1 : l2; + + return list.next; +} + +fluid_list_t * +fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func) +{ + fluid_list_t *l1, *l2; + + if(!list) + { + return NULL; + } + + if(!list->next) + { + return list; + } + + l1 = list; + l2 = list->next; + + while((l2 = l2->next) != NULL) + { + if((l2 = l2->next) == NULL) + { + break; + } + + l1 = l1->next; + } + + l2 = l1->next; + l1->next = NULL; + + return fluid_list_sort_merge(fluid_list_sort(list, compare_func), + fluid_list_sort(l2, compare_func), + compare_func); +} + + +fluid_list_t * +fluid_list_last(fluid_list_t *list) +{ + if(list) + { + while(list->next) + { + list = list->next; + } + } + + return list; +} + +int +fluid_list_size(fluid_list_t *list) +{ + int n = 0; + + while(list) + { + n++; + list = list->next; + } + + return n; +} + +fluid_list_t *fluid_list_insert_at(fluid_list_t *list, int n, void *data) +{ + fluid_list_t *new_list; + fluid_list_t *cur; + fluid_list_t *prev = NULL; + + new_list = new_fluid_list(); + new_list->data = data; + + cur = list; + + while((n-- > 0) && cur) + { + prev = cur; + cur = cur->next; + } + + new_list->next = cur; + + if(prev) + { + prev->next = new_list; + return list; + } + else + { + return new_list; + } +} + +/* Compare function to sort strings alphabetically, + * for use with fluid_list_sort(). */ +int +fluid_list_str_compare_func(const void *a, const void *b) +{ + if(a && b) + { + return FLUID_STRCMP(a, b); + } + + if(!a && !b) + { + return 0; + } + + if(a) + { + return -1; + } + + return 1; +} + +int fluid_list_idx(fluid_list_t *list, void *data) +{ + int i = 0; + + while(list) + { + if (list->data == data) + { + return i; + } + list = list->next; + } + + return -1; +} diff --git a/libs/fluidsynth/src/utils/fluid_list.h b/libs/fluidsynth/src/utils/fluid_list.h new file mode 100644 index 00000000000..a290135cec0 --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_list.h @@ -0,0 +1,63 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * 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 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _FLUID_LIST_H +#define _FLUID_LIST_H + +#include "fluidsynth_priv.h" + +/* + * + * Lists + * + * A sound font loader has to pack the data from the .SF2 file into + * list structures of this type. + * + */ + +typedef struct _fluid_list_t fluid_list_t; + +typedef int (*fluid_compare_func_t)(const void *a, const void *b); + +struct _fluid_list_t +{ + void *data; + fluid_list_t *next; +}; + +fluid_list_t *new_fluid_list(void); +void delete_fluid_list(fluid_list_t *list); +void delete1_fluid_list(fluid_list_t *list); +fluid_list_t *fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func); +fluid_list_t *fluid_list_append(fluid_list_t *list, void *data); +fluid_list_t *fluid_list_prepend(fluid_list_t *list, void *data); +fluid_list_t *fluid_list_remove(fluid_list_t *list, void *data); +fluid_list_t *fluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink); +fluid_list_t *fluid_list_nth(fluid_list_t *list, int n); +fluid_list_t *fluid_list_last(fluid_list_t *list); +fluid_list_t *fluid_list_insert_at(fluid_list_t *list, int n, void *data); +int fluid_list_idx(fluid_list_t *list, void *data); +int fluid_list_size(fluid_list_t *list); + +#define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL) +#define fluid_list_get(slist) ((slist) ? ((slist)->data) : NULL) + +int fluid_list_str_compare_func(const void *a, const void *b); + +#endif /* _FLUID_LIST_H */ diff --git a/libs/fluidsynth/src/utils/fluid_ringbuffer.c b/libs/fluidsynth/src/utils/fluid_ringbuffer.c new file mode 100644 index 00000000000..e9fc4ddd358 --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_ringbuffer.c @@ -0,0 +1,90 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* + * Josh Green + * 2009-05-28 + */ + +#include "fluid_ringbuffer.h" +#include "fluid_sys.h" + + +/** + * Create a lock free queue with a fixed maximum count and size of elements. + * @param count Count of elements in queue (fixed max number of queued elements) + * @return New lock free queue or NULL if out of memory (error message logged) + * + * Lockless FIFO queues don't use any locking mechanisms and can therefore be + * advantageous in certain situations, such as passing data between a lower + * priority thread and a higher "real time" thread, without potential lock + * contention which could stall the high priority thread. Note that there may + * only be one producer thread and one consumer thread. + */ +fluid_ringbuffer_t * +new_fluid_ringbuffer(int count, size_t elementsize) +{ + fluid_ringbuffer_t *queue; + + fluid_return_val_if_fail(count > 0, NULL); + + queue = FLUID_NEW(fluid_ringbuffer_t); + + if(!queue) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + queue->array = FLUID_MALLOC(elementsize * count); + + if(!queue->array) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + delete_fluid_ringbuffer(queue); + return NULL; + } + + /* Clear array, in case dynamic pointer reclaiming is being done */ + FLUID_MEMSET(queue->array, 0, elementsize * count); + + queue->totalcount = count; + queue->elementsize = elementsize; + fluid_atomic_int_set(&queue->count, 0); + queue->in = 0; + queue->out = 0; + + return (queue); +} + +/** + * Free an event queue. + * @param queue Lockless queue instance + * + * Care must be taken when freeing a queue, to ensure that the consumer and + * producer threads will no longer access it. + */ +void +delete_fluid_ringbuffer(fluid_ringbuffer_t *queue) +{ + fluid_return_if_fail(queue != NULL); + FLUID_FREE(queue->array); + FLUID_FREE(queue); +} diff --git a/libs/fluidsynth/src/utils/fluid_ringbuffer.h b/libs/fluidsynth/src/utils/fluid_ringbuffer.h new file mode 100644 index 00000000000..6b0a2df37dc --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_ringbuffer.h @@ -0,0 +1,133 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_RINGBUFFER_H +#define _FLUID_RINGBUFFER_H + +#include "fluid_sys.h" + +/* + * Lockless event queue instance. + */ +struct _fluid_ringbuffer_t +{ + char *array; /**< Queue array of arbitrary size elements */ + int totalcount; /**< Total count of elements in array */ + fluid_atomic_int_t count; /**< Current count of elements */ + int in; /**< Index in queue to store next pushed element */ + int out; /**< Index in queue of next popped element */ + size_t elementsize; /**< Size of each element */ + void *userdata; +}; + +typedef struct _fluid_ringbuffer_t fluid_ringbuffer_t; + + +fluid_ringbuffer_t *new_fluid_ringbuffer(int count, size_t elementsize); +void delete_fluid_ringbuffer(fluid_ringbuffer_t *queue); + +/** + * Get pointer to next input array element in queue. + * @param queue Lockless queue instance + * @param offset Normally zero, or more if you need to push several items at once + * @return Pointer to array element in queue to store data to or NULL if queue is full + * + * This function along with fluid_ringbuffer_next_inptr() form a queue "push" + * operation and is split into 2 functions to avoid an element copy. Note that + * the returned array element pointer may contain the data of a previous element + * if the queue has wrapped around. This can be used to reclaim pointers to + * allocated memory, etc. + */ +static FLUID_INLINE void * +fluid_ringbuffer_get_inptr(fluid_ringbuffer_t *queue, int offset) +{ + return fluid_atomic_int_get(&queue->count) + offset >= queue->totalcount ? NULL + : queue->array + queue->elementsize * ((queue->in + offset) % queue->totalcount); +} + +/** + * Advance the input queue index to complete a "push" operation. + * @param queue Lockless queue instance + * @param count Normally one, or more if you need to push several items at once + * + * This function along with fluid_ringbuffer_get_inptr() form a queue "push" + * operation and is split into 2 functions to avoid element copy. + */ +static FLUID_INLINE void +fluid_ringbuffer_next_inptr(fluid_ringbuffer_t *queue, int count) +{ + fluid_atomic_int_add(&queue->count, count); + + queue->in += count; + + if(queue->in >= queue->totalcount) + { + queue->in -= queue->totalcount; + } +} + +/** + * Get amount of items currently in queue + * @param queue Lockless queue instance + * @return amount of items currently in queue + */ +static FLUID_INLINE int +fluid_ringbuffer_get_count(fluid_ringbuffer_t *queue) +{ + return fluid_atomic_int_get(&queue->count); +} + + +/** + * Get pointer to next output array element in queue. + * @param queue Lockless queue instance + * @return Pointer to array element data in the queue or NULL if empty, can only + * be used up until fluid_ringbuffer_next_outptr() is called. + * + * This function along with fluid_ringbuffer_next_outptr() form a queue "pop" + * operation and is split into 2 functions to avoid an element copy. + */ +static FLUID_INLINE void * +fluid_ringbuffer_get_outptr(fluid_ringbuffer_t *queue) +{ + return fluid_ringbuffer_get_count(queue) == 0 ? NULL + : queue->array + queue->elementsize * queue->out; +} + + +/** + * Advance the output queue index to complete a "pop" operation. + * @param queue Lockless queue instance + * + * This function along with fluid_ringbuffer_get_outptr() form a queue "pop" + * operation and is split into 2 functions to avoid an element copy. + */ +static FLUID_INLINE void +fluid_ringbuffer_next_outptr(fluid_ringbuffer_t *queue) +{ + fluid_atomic_int_add(&queue->count, -1); + + if(++queue->out == queue->totalcount) + { + queue->out = 0; + } +} + +#endif /* _FLUID_ringbuffer_H */ diff --git a/libs/fluidsynth/src/utils/fluid_settings.c b/libs/fluidsynth/src/utils/fluid_settings.c new file mode 100644 index 00000000000..c657a0a0665 --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_settings.c @@ -0,0 +1,2004 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sys.h" +#include "fluid_hash.h" +#include "fluid_synth.h" +#if 0 /* unused in Wine */ +#include "fluid_cmd.h" +#include "fluid_adriver.h" +#include "fluid_mdriver.h" +#endif /* unused in Wine */ +#include "fluid_settings.h" +#include "fluid_midi.h" + +/* maximum allowed components of a settings variable (separated by '.') */ +#define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */ +#define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */ + +static void fluid_settings_init(fluid_settings_t *settings); +static void fluid_settings_key_destroy_func(void *value); +static void fluid_settings_value_destroy_func(void *value); +static int fluid_settings_tokenize(const char *s, char *buf, char **ptr); + +/* Common structure to all settings nodes */ +typedef struct +{ + char *value; + char *def; + int hints; + fluid_list_t *options; + fluid_str_update_t update; + void *data; +} fluid_str_setting_t; + +typedef struct +{ + double value; + double def; + double min; + double max; + int hints; + fluid_num_update_t update; + void *data; +} fluid_num_setting_t; + +typedef struct +{ + int value; + int def; + int min; + int max; + int hints; + fluid_int_update_t update; + void *data; +} fluid_int_setting_t; + +typedef struct +{ + fluid_hashtable_t *hashtable; +} fluid_set_setting_t; + +typedef struct +{ + int type; /**< fluid_types_enum */ + + union + { + fluid_str_setting_t str; + fluid_num_setting_t num; + fluid_int_setting_t i; + fluid_set_setting_t set; + }; +} fluid_setting_node_t; + +static fluid_setting_node_t * +new_fluid_str_setting(const char *value, const char *def, int hints) +{ + fluid_setting_node_t *node; + fluid_str_setting_t *str; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_STR_TYPE; + + str = &node->str; + str->value = value ? FLUID_STRDUP(value) : NULL; + str->def = def ? FLUID_STRDUP(def) : NULL; + str->hints = hints; + str->options = NULL; + str->update = NULL; + str->data = NULL; + return node; +} + +static void +delete_fluid_str_setting(fluid_setting_node_t *node) +{ + fluid_return_if_fail(node != NULL); + + FLUID_ASSERT(node->type == FLUID_STR_TYPE); + + FLUID_FREE(node->str.value); + FLUID_FREE(node->str.def); + + if(node->str.options) + { + fluid_list_t *list = node->str.options; + + while(list) + { + FLUID_FREE(list->data); + list = fluid_list_next(list); + } + + delete_fluid_list(node->str.options); + } + + FLUID_FREE(node); +} + + +static fluid_setting_node_t * +new_fluid_num_setting(double min, double max, double def, int hints) +{ + fluid_setting_node_t *node; + fluid_num_setting_t *num; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_NUM_TYPE; + + num = &node->num; + num->value = def; + num->def = def; + num->min = min; + num->max = max; + num->hints = hints; + num->update = NULL; + num->data = NULL; + + return node; +} + +static void +delete_fluid_num_setting(fluid_setting_node_t *node) +{ + fluid_return_if_fail(node != NULL); + + FLUID_ASSERT(node->type == FLUID_NUM_TYPE); + FLUID_FREE(node); +} + +static fluid_setting_node_t * +new_fluid_int_setting(int min, int max, int def, int hints) +{ + fluid_setting_node_t *node; + fluid_int_setting_t *i; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_INT_TYPE; + + i = &node->i; + i->value = def; + i->def = def; + i->min = min; + i->max = max; + i->hints = hints; + i->update = NULL; + i->data = NULL; + return node; +} + +static void +delete_fluid_int_setting(fluid_setting_node_t *node) +{ + fluid_return_if_fail(node != NULL); + + FLUID_ASSERT(node->type == FLUID_INT_TYPE); + FLUID_FREE(node); +} + +static fluid_setting_node_t * +new_fluid_set_setting(void) +{ + fluid_setting_node_t *node; + fluid_set_setting_t *set; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_SET_TYPE; + set = &node->set; + + set->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, + fluid_settings_key_destroy_func, + fluid_settings_value_destroy_func); + + if(!set->hashtable) + { + FLUID_FREE(node); + return NULL; + } + + return node; +} + +static void +delete_fluid_set_setting(fluid_setting_node_t *node) +{ + fluid_return_if_fail(node != NULL); + + FLUID_ASSERT(node->type == FLUID_SET_TYPE); + delete_fluid_hashtable(node->set.hashtable); + FLUID_FREE(node); +} + +/** + * Create a new settings object + * + * @return the pointer to the settings object + */ +fluid_settings_t * +new_fluid_settings(void) +{ + fluid_settings_t *settings; + + settings = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, + fluid_settings_key_destroy_func, + fluid_settings_value_destroy_func); + + if(settings == NULL) + { + return NULL; + } + + fluid_rec_mutex_init(settings->mutex); + fluid_settings_init(settings); + return settings; +} + +/** + * Delete the provided settings object + * + * @param settings a settings object + */ +void +delete_fluid_settings(fluid_settings_t *settings) +{ + fluid_return_if_fail(settings != NULL); + + fluid_rec_mutex_destroy(settings->mutex); + delete_fluid_hashtable(settings); +} + +/* Settings hash key destroy function */ +static void +fluid_settings_key_destroy_func(void *value) +{ + FLUID_FREE(value); /* Free the string key value */ +} + +/* Settings hash value destroy function */ +static void +fluid_settings_value_destroy_func(void *value) +{ + fluid_setting_node_t *node = value; + + switch(node->type) + { + case FLUID_NUM_TYPE: + delete_fluid_num_setting(node); + break; + + case FLUID_INT_TYPE: + delete_fluid_int_setting(node); + break; + + case FLUID_STR_TYPE: + delete_fluid_str_setting(node); + break; + + case FLUID_SET_TYPE: + delete_fluid_set_setting(node); + break; + } +} + +void +fluid_settings_init(fluid_settings_t *settings) +{ + fluid_return_if_fail(settings != NULL); + + fluid_synth_settings(settings); +#if 0 /* unused in Wine */ + fluid_shell_settings(settings); + fluid_player_settings(settings); + fluid_file_renderer_settings(settings); + fluid_audio_driver_settings(settings); + fluid_midi_driver_settings(settings); +#endif /* unused in Wine */ +} + +static int +fluid_settings_tokenize(const char *s, char *buf, char **ptr) +{ + char *tokstr, *tok; + int n = 0; + + if(FLUID_STRLEN(s) > MAX_SETTINGS_LABEL) + { + FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars", + MAX_SETTINGS_LABEL); + return 0; + } + + FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */ + tokstr = buf; + + while((tok = fluid_strtok(&tokstr, "."))) + { + if(n >= MAX_SETTINGS_TOKENS) + { + FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d", + MAX_SETTINGS_TOKENS); + return 0; + } + else + { + ptr[n++] = tok; + } + } + + return n; +} + +/** + * Get a setting name, value and type + * + * @param settings a settings object + * @param name Settings name + * @param value Location to store setting node if found + * @return #FLUID_OK if the node exists, #FLUID_FAILED otherwise + */ +static int +fluid_settings_get(fluid_settings_t *settings, const char *name, + fluid_setting_node_t **value) +{ + fluid_hashtable_t *table = settings; + fluid_setting_node_t *node = NULL; + char *tokens[MAX_SETTINGS_TOKENS]; + char buf[MAX_SETTINGS_LABEL + 1]; + int ntokens; + int n; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if(table == NULL || ntokens <= 0) + { + return FLUID_FAILED; + } + + for(n = 0; n < ntokens; n++) + { + + node = fluid_hashtable_lookup(table, tokens[n]); + + if(!node) + { + return FLUID_FAILED; + } + + table = (node->type == FLUID_SET_TYPE) ? node->set.hashtable : NULL; + } + + if(value) + { + *value = node; + } + + return FLUID_OK; +} + +/** + * Set a setting name, value and type, replacing it if already exists + * + * @param settings a settings object + * @param name Settings name + * @param value Node instance to assign (used directly) + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise + */ +static int +fluid_settings_set(fluid_settings_t *settings, const char *name, fluid_setting_node_t *value) +{ + fluid_hashtable_t *table = settings; + fluid_setting_node_t *node; + char *tokens[MAX_SETTINGS_TOKENS]; + char buf[MAX_SETTINGS_LABEL + 1]; + int n, num; + char *dupname; + + num = fluid_settings_tokenize(name, buf, tokens); + + if(num == 0) + { + return FLUID_FAILED; + } + + num--; + + for(n = 0; n < num; n++) + { + + node = fluid_hashtable_lookup(table, tokens[n]); + + if(node) + { + + if(node->type == FLUID_SET_TYPE) + { + table = node->set.hashtable; + } + else + { + /* path ends prematurely */ + FLUID_LOG(FLUID_ERR, "'%s' is not a node. Name of the setting was '%s'", tokens[n], name); + return FLUID_FAILED; + } + + } + else + { + /* create a new node */ + fluid_setting_node_t *setnode; + + dupname = FLUID_STRDUP(tokens[n]); + setnode = new_fluid_set_setting(); + + if(!dupname || !setnode) + { + if(dupname) + { + FLUID_FREE(dupname); + } + else + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + + if(setnode) + { + delete_fluid_set_setting(setnode); + } + + return FLUID_FAILED; + } + + fluid_hashtable_insert(table, dupname, setnode); + table = setnode->set.hashtable; + } + } + + dupname = FLUID_STRDUP(tokens[num]); + + if(!dupname) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + fluid_hashtable_insert(table, dupname, value); + + return FLUID_OK; +} + +/** + * Registers a new string value for the specified setting. + * + * @param settings a settings object + * @param name the setting's name + * @param def the default value for the setting + * @param hints the hints for the setting + * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise + */ +int +fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) != FLUID_OK) + { + node = new_fluid_str_setting(def, def, hints); + retval = fluid_settings_set(settings, name, node); + + if(retval != FLUID_OK) + { + delete_fluid_str_setting(node); + } + } + else + { + /* if variable already exists, don't change its value. */ + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + FLUID_FREE(setting->def); + setting->def = def ? FLUID_STRDUP(def) : NULL; + setting->hints = hints; + retval = FLUID_OK; + } + else + { + FLUID_LOG(FLUID_ERR, "Failed to register string setting '%s' as it already exists with a different type", name); + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Registers a new float value for the specified setting. + * + * @param settings a settings object + * @param name the setting's name + * @param def the default value for the setting + * @param min the smallest allowed value for the setting + * @param max the largest allowed value for the setting + * @param hints the hints for the setting + * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise + */ +int +fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def, + double min, double max, int hints) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + + /* For now, all floating point settings are bounded below and above */ + hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) != FLUID_OK) + { + /* insert a new setting */ + node = new_fluid_num_setting(min, max, def, hints); + retval = fluid_settings_set(settings, name, node); + + if(retval != FLUID_OK) + { + delete_fluid_num_setting(node); + } + } + else + { + if(node->type == FLUID_NUM_TYPE) + { + /* update the existing setting but don't change its value */ + fluid_num_setting_t *setting = &node->num; + setting->min = min; + setting->max = max; + setting->def = def; + setting->hints = hints; + retval = FLUID_OK; + } + else + { + /* type mismatch */ + FLUID_LOG(FLUID_ERR, "Failed to register numeric setting '%s' as it already exists with a different type", name); + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Registers a new integer value for the specified setting. + * + * @param settings a settings object + * @param name the setting's name + * @param def the default value for the setting + * @param min the smallest allowed value for the setting + * @param max the largest allowed value for the setting + * @param hints the hints for the setting + * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise + */ +int +fluid_settings_register_int(fluid_settings_t *settings, const char *name, int def, + int min, int max, int hints) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + + /* For now, all integer settings are bounded below and above */ + hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) != FLUID_OK) + { + /* insert a new setting */ + node = new_fluid_int_setting(min, max, def, hints); + retval = fluid_settings_set(settings, name, node); + + if(retval != FLUID_OK) + { + delete_fluid_int_setting(node); + } + } + else + { + if(node->type == FLUID_INT_TYPE) + { + /* update the existing setting but don't change its value */ + fluid_int_setting_t *setting = &node->i; + setting->min = min; + setting->max = max; + setting->def = def; + setting->hints = hints; + retval = FLUID_OK; + } + else + { + /* type mismatch */ + FLUID_LOG(FLUID_ERR, "Failed to register int setting '%s' as it already exists with a different type", name); + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Registers a callback for the specified string setting. + * + * @param settings a settings object + * @param name the setting's name + * @param callback an update function for the setting + * @param data user supplied data passed to the update function + * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise + */ +int fluid_settings_callback_str(fluid_settings_t *settings, const char *name, + fluid_str_update_t callback, void *data) +{ + fluid_setting_node_t *node; + fluid_str_setting_t *setting; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || node->type != FLUID_STR_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; + } + + setting = &node->str; + setting->update = callback; + setting->data = data; + + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_OK; +} + +/** + * Registers a callback for the specified numeric setting. + * + * @param settings a settings object + * @param name the setting's name + * @param callback an update function for the setting + * @param data user supplied data passed to the update function + * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise + */ +int fluid_settings_callback_num(fluid_settings_t *settings, const char *name, + fluid_num_update_t callback, void *data) +{ + fluid_setting_node_t *node; + fluid_num_setting_t *setting; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || node->type != FLUID_NUM_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; + } + + setting = &node->num; + setting->update = callback; + setting->data = data; + + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_OK; +} + +/** + * Registers a callback for the specified int setting. + * + * @param settings a settings object + * @param name the setting's name + * @param callback an update function for the setting + * @param data user supplied data passed to the update function + * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise + */ +int fluid_settings_callback_int(fluid_settings_t *settings, const char *name, + fluid_int_update_t callback, void *data) +{ + fluid_setting_node_t *node; + fluid_int_setting_t *setting; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || node->type != FLUID_INT_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; + } + + setting = &node->i; + setting->update = callback; + setting->data = data; + + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_OK; +} + +void* fluid_settings_get_user_data(fluid_settings_t * settings, const char *name) +{ + fluid_setting_node_t *node; + void* retval = NULL; + + fluid_return_val_if_fail(settings != NULL, NULL); + fluid_return_val_if_fail(name != NULL, NULL); + fluid_return_val_if_fail(name[0] != '\0', NULL); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_NUM_TYPE) + { + fluid_num_setting_t *setting = &node->num; + retval = setting->data; + } + else if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + retval = setting->data; + } + else if(node->type == FLUID_INT_TYPE) + { + fluid_int_setting_t *setting = &node->i; + retval = setting->data; + } + } + + fluid_rec_mutex_unlock(settings->mutex); + return retval; +} + +/** + * Get the type of the setting with the given name + * + * @param settings a settings object + * @param name a setting's name + * @return the type for the named setting (see #fluid_types_enum), or #FLUID_NO_TYPE when it does not exist + */ +int +fluid_settings_get_type(fluid_settings_t *settings, const char *name) +{ + fluid_setting_node_t *node; + int type = FLUID_NO_TYPE; + + fluid_return_val_if_fail(settings != NULL, type); + fluid_return_val_if_fail(name != NULL, type); + fluid_return_val_if_fail(name[0] != '\0', type); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + type = node->type; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return type; +} + +/** + * Get the hints for the named setting as an integer bitmap + * + * @param settings a settings object + * @param name a setting's name + * @param hints set to the hints associated to the setting if it exists + * @return #FLUID_OK if hints associated to the named setting exist, #FLUID_FAILED otherwise + */ +int +fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *hints) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_NUM_TYPE) + { + fluid_num_setting_t *setting = &node->num; + *hints = setting->hints; + retval = FLUID_OK; + } + else if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + *hints = setting->hints; + retval = FLUID_OK; + } + else if(node->type == FLUID_INT_TYPE) + { + fluid_int_setting_t *setting = &node->i; + *hints = setting->hints; + retval = FLUID_OK; + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Ask whether the setting is changeable in real-time. + * + * @param settings a settings object + * @param name a setting's name + * @return TRUE if the setting is changeable in real-time, FALSE otherwise + * + * @note Before using this function, make sure the @p settings object has already been used to create + * a synthesizer, a MIDI driver, an audio driver, a MIDI player, or a command handler (depending on + * which settings you want to query). + */ +int +fluid_settings_is_realtime(fluid_settings_t *settings, const char *name) +{ + fluid_setting_node_t *node; + int isrealtime = FALSE; + + fluid_return_val_if_fail(settings != NULL, 0); + fluid_return_val_if_fail(name != NULL, 0); + fluid_return_val_if_fail(name[0] != '\0', 0); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_NUM_TYPE) + { + fluid_num_setting_t *setting = &node->num; + isrealtime = setting->update != NULL; + } + else if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + isrealtime = setting->update != NULL; + } + else if(node->type == FLUID_INT_TYPE) + { + fluid_int_setting_t *setting = &node->i; + isrealtime = setting->update != NULL; + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return isrealtime; +} + +/** + * Set a string value for a named setting + * + * @param settings a settings object + * @param name a setting's name + * @param str new string value + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise + */ +int +fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char *str) +{ + fluid_setting_node_t *node; + fluid_str_setting_t *setting; + char *new_value = NULL; + fluid_str_update_t callback = NULL; + void *data = NULL; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || (node->type != FLUID_STR_TYPE)) + { + FLUID_LOG(FLUID_ERR, "Unknown string setting '%s'", name); + goto error_recovery; + } + + setting = &node->str; + + if(setting->value) + { + FLUID_FREE(setting->value); + } + + if(str) + { + new_value = FLUID_STRDUP(str); + + if(new_value == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + } + + setting->value = new_value; + + callback = setting->update; + data = setting->data; + + /* Release the mutex before calling the update callback, to avoid + * possible deadlocks with FluidSynths API lock */ + fluid_rec_mutex_unlock(settings->mutex); + + if(callback) + { + (*callback)(data, name, new_value); + } + + return FLUID_OK; + +error_recovery: + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; +} + +/** + * Copy the value of a string setting into the provided buffer (thread safe) + * + * @param settings a settings object + * @param name a setting's name + * @param str Caller supplied buffer to copy string value to + * @param len Size of 'str' buffer (no more than len bytes will be written, which + * will always include a zero terminator) + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise + * + * @note A size of 256 should be more than sufficient for the string buffer. + * + * @since 1.1.0 + */ +int +fluid_settings_copystr(fluid_settings_t *settings, const char *name, + char *str, int len) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(str != NULL, retval); + fluid_return_val_if_fail(len > 0, retval); + + str[0] = 0; + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + + if(setting->value) + { + FLUID_STRNCPY(str, setting->value, len); + } + + retval = FLUID_OK; + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; + + if(setting->hints & FLUID_HINT_TOGGLED) + { + FLUID_STRNCPY(str, setting->value ? "yes" : "no", len); + + retval = FLUID_OK; + } + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Duplicate the value of a string setting + * + * @param settings a settings object + * @param name a setting's name + * @param str Location to store pointer to allocated duplicate string + * @return #FLUID_OK if the value exists and was successfully duplicated, #FLUID_FAILED otherwise + * + * Like fluid_settings_copystr() but allocates a new copy of the string. Caller + * owns the string and should free it with fluid_free() when done using it. + * + * @since 1.1.0 + */ +int +fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(str != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + + if(setting->value) + { + *str = FLUID_STRDUP(setting->value); + + if(!*str) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + } + + if(!setting->value || *str) + { + retval = FLUID_OK; /* Don't set to FLUID_OK if out of memory */ + } + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; + + if(setting->hints & FLUID_HINT_TOGGLED) + { + *str = FLUID_STRDUP(setting->value ? "yes" : "no"); + + if(!*str) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + + if(!setting->value || *str) + { + retval = FLUID_OK; /* Don't set to FLUID_OK if out of memory */ + } + } + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + + +/** + * Test a string setting for some value. + * + * @param settings a settings object + * @param name a setting's name + * @param s a string to be tested + * @return TRUE if the value exists and is equal to \c s, FALSE otherwise + */ +int +fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *s) +{ + fluid_setting_node_t *node; + int retval = FALSE; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(s != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + + if(setting->value) + { + retval = FLUID_STRCMP(setting->value, s) == 0; + } + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; + + if(setting->hints & FLUID_HINT_TOGGLED) + { + retval = FLUID_STRCMP(setting->value ? "yes" : "no", s) == 0; + } + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Get the default value of a string setting. + * + * @param settings a settings object + * @param name a setting's name + * @param def the default string value of the setting if it exists + * @return FLUID_OK if a default value exists, FLUID_FAILED otherwise + * + * @note The returned string is not owned by the caller and should not be modified or freed. + */ +int +fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def) +{ + fluid_setting_node_t *node; + char *retval = NULL; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + retval = setting->def; + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; + + if(setting->hints & FLUID_HINT_TOGGLED) + { + retval = setting->def ? "yes" : "no"; + } + } + } + + *def = retval; + fluid_rec_mutex_unlock(settings->mutex); + + return retval != NULL ? FLUID_OK : FLUID_FAILED; +} + +/** + * Add an option to a string setting (like an enumeration value). + * + * @param settings a settings object + * @param name a setting's name + * @param s option string to add + * @return #FLUID_OK if the setting exists and option was added, #FLUID_FAILED otherwise + * + * Causes the setting's #FLUID_HINT_OPTIONLIST hint to be set. + */ +int +fluid_settings_add_option(fluid_settings_t *settings, const char *name, const char *s) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(s != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_STR_TYPE)) + { + fluid_str_setting_t *setting = &node->str; + char *copy = FLUID_STRDUP(s); + setting->options = fluid_list_append(setting->options, copy); + setting->hints |= FLUID_HINT_OPTIONLIST; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Remove an option previously assigned by fluid_settings_add_option(). + * + * @param settings a settings object + * @param name a setting's name + * @param s option string to remove + * @return #FLUID_OK if the setting exists and option was removed, #FLUID_FAILED otherwise + */ +int +fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(s != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_STR_TYPE)) + { + + fluid_str_setting_t *setting = &node->str; + fluid_list_t *list = setting->options; + + while(list) + { + char *option = (char *) fluid_list_get(list); + + if(FLUID_STRCMP(s, option) == 0) + { + FLUID_FREE(option); + setting->options = fluid_list_remove_link(setting->options, list); + retval = FLUID_OK; + break; + } + + list = fluid_list_next(list); + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Set a numeric value for a named setting. + * + * @param settings a settings object + * @param name a setting's name + * @param val new setting's value + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise + */ +int +fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val) +{ + fluid_setting_node_t *node; + fluid_num_setting_t *setting; + fluid_num_update_t callback = NULL; + void *data = NULL; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || (node->type != FLUID_NUM_TYPE)) + { + FLUID_LOG(FLUID_ERR, "Unknown numeric setting '%s'", name); + goto error_recovery; + } + + setting = &node->num; + + if(val < setting->min || val > setting->max) + { + FLUID_LOG(FLUID_ERR, "requested set value for '%s' out of range", name); + goto error_recovery; + } + + setting->value = val; + + callback = setting->update; + data = setting->data; + + /* Release the mutex before calling the update callback, to avoid + * possible deadlocks with FluidSynths API lock */ + fluid_rec_mutex_unlock(settings->mutex); + + if(callback) + { + (*callback)(data, name, val); + } + + return FLUID_OK; + +error_recovery: + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; +} + +/** + * Get the numeric value of a named setting + * + * @param settings a settings object + * @param name a setting's name + * @param val variable pointer to receive the setting's numeric value + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise + */ +int +fluid_settings_getnum(fluid_settings_t *settings, const char *name, double *val) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_NUM_TYPE)) + { + fluid_num_setting_t *setting = &node->num; + *val = setting->value; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * float-typed wrapper for fluid_settings_getnum + * + * @param settings a settings object + * @param name a setting's name + * @param val variable pointer to receive the setting's float value + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise + */ +int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val) +{ + double tmp; + + if(fluid_settings_getnum(settings, name, &tmp) == FLUID_OK) + { + *val = tmp; + return FLUID_OK; + } + + return FLUID_FAILED; +} + +/** + * Get the range of values of a numeric setting + * + * @param settings a settings object + * @param name a setting's name + * @param min setting's range lower limit + * @param max setting's range upper limit + * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise + */ +int +fluid_settings_getnum_range(fluid_settings_t *settings, const char *name, + double *min, double *max) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(min != NULL, retval); + fluid_return_val_if_fail(max != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_NUM_TYPE)) + { + fluid_num_setting_t *setting = &node->num; + *min = setting->min; + *max = setting->max; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Get the default value of a named numeric (double) setting + * + * @param settings a settings object + * @param name a setting's name + * @param val set to the default value if the named setting exists + * @return #FLUID_OK if the default value of the named setting exists, #FLUID_FAILED otherwise + */ +int +fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_NUM_TYPE)) + { + fluid_num_setting_t *setting = &node->num; + *val = setting->def; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Set an integer value for a setting + * + * @param settings a settings object + * @param name a setting's name + * @param val new setting's integer value + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise + */ +int +fluid_settings_setint(fluid_settings_t *settings, const char *name, int val) +{ + fluid_setting_node_t *node; + fluid_int_setting_t *setting; + fluid_int_update_t callback = NULL; + void *data = NULL; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || (node->type != FLUID_INT_TYPE)) + { + FLUID_LOG(FLUID_ERR, "Unknown integer parameter '%s'", name); + goto error_recovery; + } + + setting = &node->i; + + if(val < setting->min || val > setting->max) + { + FLUID_LOG(FLUID_ERR, "requested set value for setting '%s' out of range", name); + goto error_recovery; + } + + setting->value = val; + + callback = setting->update; + data = setting->data; + + /* Release the mutex before calling the update callback, to avoid + * possible deadlocks with FluidSynths API lock */ + fluid_rec_mutex_unlock(settings->mutex); + + if(callback) + { + (*callback)(data, name, val); + } + + return FLUID_OK; + +error_recovery: + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; +} + +/** + * Get an integer value setting. + * + * @param settings a settings object + * @param name a setting's name + * @param val pointer to a variable to receive the setting's integer value + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise + */ +int +fluid_settings_getint(fluid_settings_t *settings, const char *name, int *val) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_INT_TYPE)) + { + fluid_int_setting_t *setting = &node->i; + *val = setting->value; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Get the range of values of an integer setting + * + * @param settings a settings object + * @param name a setting's name + * @param min setting's range lower limit + * @param max setting's range upper limit + * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise + */ +int +fluid_settings_getint_range(fluid_settings_t *settings, const char *name, + int *min, int *max) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(min != NULL, retval); + fluid_return_val_if_fail(max != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_INT_TYPE)) + { + fluid_int_setting_t *setting = &node->i; + *min = setting->min; + *max = setting->max; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Get the default value of an integer setting. + * + * @param settings a settings object + * @param name a setting's name + * @param val set to the setting's default integer value if it exists + * @return #FLUID_OK if the setting's default integer value exists, #FLUID_FAILED otherwise + */ +int fluid_settings_getint_default(fluid_settings_t *settings, const char *name, int *val) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_INT_TYPE)) + { + fluid_int_setting_t *setting = &node->i; + *val = setting->def; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Iterate the available options for a named string setting, calling the provided + * callback function for each existing option. + * + * @param settings a settings object + * @param name a setting's name + * @param data any user provided pointer + * @param func callback function to be called on each iteration + * + * @note Starting with FluidSynth 1.1.0 the \p func callback is called for each + * option in alphabetical order. Sort order was undefined in previous versions. + */ +void +fluid_settings_foreach_option(fluid_settings_t *settings, const char *name, + void *data, fluid_settings_foreach_option_t func) +{ + fluid_setting_node_t *node; + fluid_str_setting_t *setting; + fluid_list_t *p, *newlist = NULL; + + fluid_return_if_fail(settings != NULL); + fluid_return_if_fail(name != NULL); + fluid_return_if_fail(name[0] != '\0'); + fluid_return_if_fail(func != NULL); + + fluid_rec_mutex_lock(settings->mutex); /* ++ lock */ + + if(fluid_settings_get(settings, name, &node) != FLUID_OK + || node->type != FLUID_STR_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ + return; + } + + setting = &node->str; + + /* Duplicate option list */ + for(p = setting->options; p; p = p->next) + { + newlist = fluid_list_append(newlist, fluid_list_get(p)); + } + + /* Sort by name */ + newlist = fluid_list_sort(newlist, fluid_list_str_compare_func); + + for(p = newlist; p; p = p->next) + { + (*func)(data, name, (const char *)fluid_list_get(p)); + } + + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ + + delete_fluid_list(newlist); +} + +/** + * Count option string values for a string setting. + * + * @param settings a settings object + * @param name Name of setting + * @return Count of options for this string setting (0 if none, -1 if not found + * or not a string setting) + * + * @since 1.1.0 + */ +int +fluid_settings_option_count(fluid_settings_t *settings, const char *name) +{ + fluid_setting_node_t *node; + int count = -1; + + fluid_return_val_if_fail(settings != NULL, -1); + fluid_return_val_if_fail(name != NULL, -1); + fluid_return_val_if_fail(name[0] != '\0', -1); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && node->type == FLUID_STR_TYPE) + { + count = fluid_list_size(node->str.options); + } + + fluid_rec_mutex_unlock(settings->mutex); + + return (count); +} + +/** + * Concatenate options for a string setting together with a separator between. + * + * @param settings Settings object + * @param name Settings name + * @param separator String to use between options (NULL to use ", ") + * @return Newly allocated string or NULL on error (out of memory, not a valid + * setting \p name or not a string setting). Free the string when finished with it by using fluid_free(). + * + * @since 1.1.0 + */ +char * +fluid_settings_option_concat(fluid_settings_t *settings, const char *name, + const char *separator) +{ + fluid_setting_node_t *node; + fluid_list_t *p, *newlist = NULL; + size_t count, len; + char *str, *option; + + fluid_return_val_if_fail(settings != NULL, NULL); + fluid_return_val_if_fail(name != NULL, NULL); + fluid_return_val_if_fail(name[0] != '\0', NULL); + + if(!separator) + { + separator = ", "; + } + + fluid_rec_mutex_lock(settings->mutex); /* ++ lock */ + + if(fluid_settings_get(settings, name, &node) != FLUID_OK + || node->type != FLUID_STR_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ + return (NULL); + } + + /* Duplicate option list, count options and get total string length */ + for(p = node->str.options, count = 0, len = 0; p; p = p->next) + { + option = fluid_list_get(p); + + if(option) + { + newlist = fluid_list_append(newlist, option); + len += FLUID_STRLEN(option); + count++; + } + } + + if(count > 1) + { + len += (count - 1) * FLUID_STRLEN(separator); + } + + len++; /* For terminator */ + + /* Sort by name */ + newlist = fluid_list_sort(newlist, fluid_list_str_compare_func); + + str = FLUID_MALLOC(len); + + if(str) + { + str[0] = 0; + + for(p = newlist; p; p = p->next) + { + option = fluid_list_get(p); + strcat(str, option); + + if(p->next) + { + strcat(str, separator); + } + } + } + + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ + + delete_fluid_list(newlist); + + if(!str) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + + return (str); +} + +/* Structure passed to fluid_settings_foreach_iter recursive function */ +typedef struct +{ + char path[MAX_SETTINGS_LABEL + 1]; /* Maximum settings label length */ + fluid_list_t *names; /* For fluid_settings_foreach() */ +} fluid_settings_foreach_bag_t; + +static int +fluid_settings_foreach_iter(void *key, void *value, void *data) +{ + fluid_settings_foreach_bag_t *bag = data; + char *keystr = key; + fluid_setting_node_t *node = value; + size_t pathlen; + char *s; + + pathlen = FLUID_STRLEN(bag->path); + + if(pathlen > 0) + { + bag->path[pathlen] = '.'; + bag->path[pathlen + 1] = 0; + } + + strcat(bag->path, keystr); + + switch(node->type) + { + case FLUID_NUM_TYPE: + case FLUID_INT_TYPE: + case FLUID_STR_TYPE: + s = FLUID_STRDUP(bag->path); + + if(s) + { + bag->names = fluid_list_append(bag->names, s); + } + + break; + + case FLUID_SET_TYPE: + fluid_hashtable_foreach(node->set.hashtable, + fluid_settings_foreach_iter, bag); + break; + } + + bag->path[pathlen] = 0; + + return 0; +} + +/** + * Iterate the existing settings defined in a settings object, calling the + * provided callback function for each setting. + * + * @param settings a settings object + * @param data any user provided pointer + * @param func callback function to be called on each iteration + * + * @note Starting with FluidSynth 1.1.0 the \p func callback is called for each + * setting in alphabetical order. Sort order was undefined in previous versions. + */ +void +fluid_settings_foreach(fluid_settings_t *settings, void *data, + fluid_settings_foreach_t func) +{ + fluid_settings_foreach_bag_t bag; + fluid_setting_node_t *node; + fluid_list_t *p; + + fluid_return_if_fail(settings != NULL); + fluid_return_if_fail(func != NULL); + + bag.path[0] = 0; + bag.names = NULL; + + fluid_rec_mutex_lock(settings->mutex); + + /* Add all node names to the bag.names list */ + fluid_hashtable_foreach(settings, fluid_settings_foreach_iter, &bag); + + /* Sort names */ + bag.names = fluid_list_sort(bag.names, fluid_list_str_compare_func); + + /* Loop over names and call the callback */ + for(p = bag.names; p; p = p->next) + { + if(fluid_settings_get(settings, (const char *)(p->data), &node) == FLUID_OK + && node) + { + (*func)(data, (const char *)(p->data), node->type); + } + + FLUID_FREE(p->data); /* -- Free name */ + } + + fluid_rec_mutex_unlock(settings->mutex); + + delete_fluid_list(bag.names); /* -- Free names list */ +} + +/** + * Split a comma-separated list of integers and fill the passed + * in buffer with the parsed values. + * + * @param str the comma-separated string to split + * @param buf user-supplied buffer to hold the parsed numbers + * @param buf_len length of user-supplied buffer + * @return number of parsed values or -1 on failure + */ +int fluid_settings_split_csv(const char *str, int *buf, int buf_len) +{ + char *s; + char *tok; + char *tokstr; + int n = 0; + + s = tokstr = FLUID_STRDUP(str); + + if(s == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return -1; + } + + while((tok = fluid_strtok(&tokstr, ",")) && n < buf_len) + { + buf[n++] = atoi(tok); + } + + FLUID_FREE(s); + + return n; +} diff --git a/libs/fluidsynth/src/utils/fluid_settings.h b/libs/fluidsynth/src/utils/fluid_settings.h new file mode 100644 index 00000000000..73a63e86db5 --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_settings.h @@ -0,0 +1,65 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_SETTINGS_H +#define _FLUID_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +int fluid_settings_add_option(fluid_settings_t *settings, const char *name, const char *s); +int fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s); + + +typedef void (*fluid_str_update_t)(void *data, const char *name, const char *value); + +int fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints); +int fluid_settings_callback_str(fluid_settings_t *settings, const char *name, + fluid_str_update_t fun, void *data); + + +typedef void (*fluid_num_update_t)(void *data, const char *name, double value); + +int fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def, + double min, double max, int hints); +int fluid_settings_callback_num(fluid_settings_t *settings, const char *name, + fluid_num_update_t fun, void *data); + +/* Type specific wrapper for fluid_settings_getnum */ +int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val); + + +typedef void (*fluid_int_update_t)(void *data, const char *name, int value); +int fluid_settings_register_int(fluid_settings_t *settings, const char *name, int def, + int min, int max, int hints); +int fluid_settings_callback_int(fluid_settings_t *settings, const char *name, + fluid_int_update_t fun, void *data); + +int fluid_settings_split_csv(const char *str, int *buf, int buf_len); + +void* fluid_settings_get_user_data(fluid_settings_t * settings, const char *name); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUID_SETTINGS_H */ diff --git a/libs/fluidsynth/src/utils/fluid_sys.c b/libs/fluidsynth/src/utils/fluid_sys.c new file mode 100644 index 00000000000..e94123022b2 --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_sys.c @@ -0,0 +1,1803 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sys.h" + + +#if READLINE_SUPPORT +#include +#include +#endif + +#ifdef DBUS_SUPPORT +#include "fluid_rtkit.h" +#endif + +#if HAVE_PTHREAD_H && !defined(_WIN32) +// Do not include pthread on windows. It includes winsock.h, which collides with ws2tcpip.h from fluid_sys.h +// It isn't need on Windows anyway. +#include +#endif + +/* WIN32 HACK - Flag used to differentiate between a file descriptor and a socket. + * Should work, so long as no SOCKET or file descriptor ends up with this bit set. - JG */ +#ifdef _WIN32 +#define FLUID_SOCKET_FLAG 0x40000000 +#else +#define FLUID_SOCKET_FLAG 0x00000000 +#define SOCKET_ERROR -1 +#define INVALID_SOCKET -1 +#endif + +/* SCHED_FIFO priority for high priority timer threads */ +#define FLUID_SYS_TIMER_HIGH_PRIO_LEVEL 10 + + +typedef struct +{ + fluid_thread_func_t func; + void *data; + int prio_level; +} fluid_thread_info_t; + +struct _fluid_timer_t +{ + long msec; + + // Pointer to a function to be executed by the timer. + // This field is set to NULL once the timer is finished to indicate completion. + // This allows for timed waits, rather than waiting forever as fluid_timer_join() does. + fluid_timer_callback_t callback; + void *data; + fluid_thread_t *thread; + int cont; + int auto_destroy; +}; + +struct _fluid_server_socket_t +{ + fluid_socket_t socket; + fluid_thread_t *thread; + int cont; + fluid_server_func_t func; + void *data; +}; + + +static int fluid_istream_gets(fluid_istream_t in, char *buf, int len); + +static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL] = +{ + fluid_default_log_function, + fluid_default_log_function, + fluid_default_log_function, + fluid_default_log_function, +#ifdef DEBUG + fluid_default_log_function +#else + NULL +#endif +}; +static void *fluid_log_user_data[LAST_LOG_LEVEL] = { NULL }; + +static const char fluid_libname[] = "fluidsynth"; + +/** + * Installs a new log function for a specified log level. + * @param level Log level to install handler for. + * @param fun Callback function handler to call for logged messages + * @param data User supplied data pointer to pass to log function + * @return The previously installed function. + */ +fluid_log_function_t +fluid_set_log_function(int level, fluid_log_function_t fun, void *data) +{ + fluid_log_function_t old = NULL; + + if((level >= 0) && (level < LAST_LOG_LEVEL)) + { + old = fluid_log_function[level]; + fluid_log_function[level] = fun; + fluid_log_user_data[level] = data; + } + + return old; +} + +/** + * Default log function which prints to the stderr. + * @param level Log level + * @param message Log message + * @param data User supplied data (not used) + */ +void +fluid_default_log_function(int level, const char *message, void *data) +{ + FILE *out; + +#if defined(_WIN32) + out = stdout; +#else + out = stderr; +#endif + + switch(level) + { + case FLUID_PANIC: + FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message); + break; + + case FLUID_ERR: + FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message); + break; + + case FLUID_WARN: + FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message); + break; + + case FLUID_INFO: + FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); + break; + + case FLUID_DBG: + FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message); + break; + + default: + FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); + break; + } + + fflush(out); +} + +/** + * Print a message to the log. + * @param level Log level (#fluid_log_level). + * @param fmt Printf style format string for log message + * @param ... Arguments for printf 'fmt' message string + * @return Always returns #FLUID_FAILED + */ +int +fluid_log(int level, const char *fmt, ...) +{ + if((level >= 0) && (level < LAST_LOG_LEVEL)) + { + fluid_log_function_t fun = fluid_log_function[level]; + + if(fun != NULL) + { + char errbuf[1024]; + + va_list args; + va_start(args, fmt); + FLUID_VSNPRINTF(errbuf, sizeof(errbuf), fmt, args); + va_end(args); + + (*fun)(level, errbuf, fluid_log_user_data[level]); + } + } + + return FLUID_FAILED; +} + +void* fluid_alloc(size_t len) +{ + void* ptr = malloc(len); + +#if defined(DEBUG) && !defined(_MSC_VER) + // garbage initialize allocated memory for debug builds to ease reproducing + // bugs like 44453ff23281b3318abbe432fda90888c373022b . + // + // MSVC++ already garbage initializes allocated memory by itself (debug-heap). + // + // 0xCC because + // * it makes pointers reliably crash when dereferencing them, + // * floating points are still some valid but insanely huge negative number, and + // * if for whatever reason this allocated memory is executed, it'll trigger + // INT3 (...at least on x86) + if(ptr != NULL) + { + memset(ptr, 0xCC, len); + } +#endif + return ptr; +} + +/** + * Open a file with a UTF-8 string, even in Windows + * @param filename The name of the file to open + * @param mode The mode to open the file in + */ +FILE *fluid_fopen(const char *filename, const char *mode) +{ +#if defined(_WIN32) + wchar_t *wpath = NULL, *wmode = NULL; + FILE *file = NULL; + int length; + if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, NULL, 0)) == 0) + { + FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for filename '%s'. Error was: '%s'", filename, fluid_get_windows_error()); + errno = EINVAL; + goto error_recovery; + } + + wpath = FLUID_MALLOC(length * sizeof(wchar_t)); + if (wpath == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory."); + errno = EINVAL; + goto error_recovery; + } + + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, wpath, length); + + if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, NULL, 0)) == 0) + { + FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for mode '%s'. Error was: '%s'", mode, fluid_get_windows_error()); + errno = EINVAL; + goto error_recovery; + } + + wmode = FLUID_MALLOC(length * sizeof(wchar_t)); + if (wmode == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory."); + errno = EINVAL; + goto error_recovery; + } + + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, wmode, length); + + file = _wfopen(wpath, wmode); + +error_recovery: + FLUID_FREE(wpath); + FLUID_FREE(wmode); + + return file; +#else + return fopen(filename, mode); +#endif +} + +/** + * Wrapper for free() that satisfies at least C90 requirements. + * + * @param ptr Pointer to memory region that should be freed + * + * @note Only use this function when the API documentation explicitly says so. Otherwise use + * adequate \c delete_fluid_* functions. + * + * @warning Calling ::free() on memory that is advised to be freed with fluid_free() results in undefined behaviour! + * (cf.: "Potential Errors Passing CRT Objects Across DLL Boundaries" found in MS Docs) + * + * @since 2.0.7 + */ +void fluid_free(void* ptr) +{ + free(ptr); +} + +/** + * An improved strtok, still trashes the input string, but is portable and + * thread safe. Also skips token chars at beginning of token string and never + * returns an empty token (will return NULL if source ends in token chars though). + * NOTE: NOT part of public API + * @internal + * @param str Pointer to a string pointer of source to tokenize. Pointer gets + * updated on each invocation to point to beginning of next token. Note that + * token char gets overwritten with a 0 byte. String pointer is set to NULL + * when final token is returned. + * @param delim String of delimiter chars. + * @return Pointer to the next token or NULL if no more tokens. + */ +char *fluid_strtok(char **str, char *delim) +{ + char *s, *d, *token; + char c; + + if(str == NULL || delim == NULL || !*delim) + { + FLUID_LOG(FLUID_ERR, "Null pointer"); + return NULL; + } + + s = *str; + + if(!s) + { + return NULL; /* str points to a NULL pointer? (tokenize already ended) */ + } + + /* skip delimiter chars at beginning of token */ + do + { + c = *s; + + if(!c) /* end of source string? */ + { + *str = NULL; + return NULL; + } + + for(d = delim; *d; d++) /* is source char a token char? */ + { + if(c == *d) /* token char match? */ + { + s++; /* advance to next source char */ + break; + } + } + } + while(*d); /* while token char match */ + + token = s; /* start of token found */ + + /* search for next token char or end of source string */ + for(s = s + 1; *s; s++) + { + c = *s; + + for(d = delim; *d; d++) /* is source char a token char? */ + { + if(c == *d) /* token char match? */ + { + *s = '\0'; /* overwrite token char with zero byte to terminate token */ + *str = s + 1; /* update str to point to beginning of next token */ + return token; + } + } + } + + /* we get here only if source string ended */ + *str = NULL; + return token; +} + +/** + * Suspend the execution of the current thread for the specified amount of time. + * @param milliseconds to wait. + */ +void fluid_msleep(unsigned int msecs) +{ + g_usleep(msecs * 1000); +} + +/** + * Get time in milliseconds to be used in relative timing operations. + * @return Monotonic time in milliseconds. + */ +unsigned int fluid_curtime(void) +{ + double now; + static double initial_time = 0; + + if(initial_time == 0) + { + initial_time = fluid_utime(); + } + + now = fluid_utime(); + + return (unsigned int)((now - initial_time) / 1000.0); +} + +/** + * Get time in microseconds to be used in relative timing operations. + * @return time in microseconds. + * Note: When used for profiling we need high precision clock given + * by g_get_monotonic_time()if available (glib version >= 2.53.3). + * If glib version is too old and in the case of Windows the function + * uses high precision performance counter instead of g_getmonotic_time(). + */ +double +fluid_utime(void) +{ + double utime; + +#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 28 + /* use high precision monotonic clock if available (g_monotonic_time(). + * For Windows, if this clock is actually implemented as low prec. clock + * (i.e. in case glib is too old), high precision performance counter are + * used instead. + * see: https://bugzilla.gnome.org/show_bug.cgi?id=783340 + */ +#if defined(WITH_PROFILING) && defined(_WIN32) &&\ + /* glib < 2.53.3 */\ + (GLIB_MINOR_VERSION <= 53 && (GLIB_MINOR_VERSION < 53 || GLIB_MICRO_VERSION < 3)) + /* use high precision performance counter. */ + static LARGE_INTEGER freq_cache = {0, 0}; /* Performance Frequency */ + LARGE_INTEGER perf_cpt; + + if(! freq_cache.QuadPart) + { + QueryPerformanceFrequency(&freq_cache); /* Frequency value */ + } + + QueryPerformanceCounter(&perf_cpt); /* Counter value */ + utime = perf_cpt.QuadPart * 1000000.0 / freq_cache.QuadPart; /* time in micros */ +#else + utime = g_get_monotonic_time(); +#endif +#else + /* fallback to less precise clock */ + GTimeVal timeval; + g_get_current_time(&timeval); + utime = (timeval.tv_sec * 1000000.0 + timeval.tv_usec); +#endif + + return utime; +} + + + +#if defined(_WIN32) /* Windoze specific stuff */ + +void +fluid_thread_self_set_prio(int prio_level) +{ + if(prio_level > 0) + { + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); + } +} + + +#elif defined(__OS2__) /* OS/2 specific stuff */ + +void +fluid_thread_self_set_prio(int prio_level) +{ + if(prio_level > 0) + { + DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0); + } +} + +#else /* POSIX stuff.. Nice POSIX.. Good POSIX. */ + +void +fluid_thread_self_set_prio(int prio_level) +{ + struct sched_param priority; + + if(prio_level > 0) + { + + memset(&priority, 0, sizeof(priority)); + priority.sched_priority = prio_level; + + if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &priority) == 0) + { + return; + } + +#ifdef DBUS_SUPPORT + /* Try to gain high priority via rtkit */ + + if(fluid_rtkit_make_realtime(0, prio_level) == 0) + { + return; + } + +#endif + FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority"); + } +} + +#ifdef FPE_CHECK + +/*************************************************************** + * + * Floating point exceptions + * + * The floating point exception functions were taken from Ircam's + * jMax source code. https://www.ircam.fr/jmax + * + * FIXME: check in config for i386 machine + * + * Currently not used. I leave the code here in case we want to pick + * this up again some time later. + */ + +/* Exception flags */ +#define _FPU_STATUS_IE 0x001 /* Invalid Operation */ +#define _FPU_STATUS_DE 0x002 /* Denormalized Operand */ +#define _FPU_STATUS_ZE 0x004 /* Zero Divide */ +#define _FPU_STATUS_OE 0x008 /* Overflow */ +#define _FPU_STATUS_UE 0x010 /* Underflow */ +#define _FPU_STATUS_PE 0x020 /* Precision */ +#define _FPU_STATUS_SF 0x040 /* Stack Fault */ +#define _FPU_STATUS_ES 0x080 /* Error Summary Status */ + +/* Macros for accessing the FPU status word. */ + +/* get the FPU status */ +#define _FPU_GET_SW(sw) __asm__ ("fnstsw %0" : "=m" (*&sw)) + +/* clear the FPU status */ +#define _FPU_CLR_SW() __asm__ ("fnclex" : : ) + +/* Purpose: + * Checks, if the floating point unit has produced an exception, print a message + * if so and clear the exception. + */ +unsigned int fluid_check_fpe_i386(char *explanation) +{ + unsigned int s; + + _FPU_GET_SW(s); + _FPU_CLR_SW(); + + s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE; + + if(s) + { + FLUID_LOG(FLUID_WARN, "FPE exception (before or in %s): %s%s%s%s%s", explanation, + (s & _FPU_STATUS_IE) ? "Invalid operation " : "", + (s & _FPU_STATUS_DE) ? "Denormal number " : "", + (s & _FPU_STATUS_ZE) ? "Zero divide " : "", + (s & _FPU_STATUS_OE) ? "Overflow " : "", + (s & _FPU_STATUS_UE) ? "Underflow " : ""); + } + + return s; +} + +/* Purpose: + * Clear floating point exception. + */ +void fluid_clear_fpe_i386(void) +{ + _FPU_CLR_SW(); +} + +#endif // ifdef FPE_CHECK + + +#endif // #else (its POSIX) + + +/*************************************************************** + * + * Profiling (Linux, i586 only) + * + */ + +#if WITH_PROFILING +/* Profiling interface between profiling command shell and audio rendering API + (FluidProfile_0004.pdf- 3.2.2). + Macros are in defined in fluid_sys.h. +*/ + +/* + ----------------------------------------------------------------------------- + Shell task side | Profiling interface | Audio task side + ----------------------------------------------------------------------------- + profiling | Internal | | | Audio + command <---> |<-- profiling -->| Data |<--macros -->| <--> rendering + shell | API | | | API + +*/ +/* default parameters for shell command "prof_start" in fluid_sys.c */ +unsigned short fluid_profile_notes = 0; /* number of generated notes */ +/* preset bank:0 prog:16 (organ) */ +unsigned char fluid_profile_bank = FLUID_PROFILE_DEFAULT_BANK; +unsigned char fluid_profile_prog = FLUID_PROFILE_DEFAULT_PROG; + +/* print mode */ +unsigned char fluid_profile_print = FLUID_PROFILE_DEFAULT_PRINT; +/* number of measures */ +unsigned short fluid_profile_n_prof = FLUID_PROFILE_DEFAULT_N_PROF; +/* measure duration in ms */ +unsigned short fluid_profile_dur = FLUID_PROFILE_DEFAULT_DURATION; +/* lock between multiple-shell */ +fluid_atomic_int_t fluid_profile_lock = 0; +/**/ + +/*---------------------------------------------- + Profiling Data +-----------------------------------------------*/ +unsigned char fluid_profile_status = PROFILE_STOP; /* command and status */ +unsigned int fluid_profile_end_ticks = 0; /* ending position (in ticks) */ +fluid_profile_data_t fluid_profile_data[] = /* Data duration */ +{ + {"synth_write_* ------------>", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block ---------->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:clear ---->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:one voice->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:all voices>", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:reverb --->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:chorus --->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"voice:note --------------->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"voice:release ------------>", 1e10, 0.0, 0.0, 0, 0, 0} +}; + + +/*---------------------------------------------- + Internal profiling API +-----------------------------------------------*/ +/* logging profiling data (used on synthesizer instance deletion) */ +void fluid_profiling_print(void) +{ + int i; + + printf("fluid_profiling_print\n"); + + FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)"); + + for(i = 0; i < FLUID_PROFILE_NBR; i++) + { + if(fluid_profile_data[i].count > 0) + { + FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f", + fluid_profile_data[i].description, + fluid_profile_data[i].min, + fluid_profile_data[i].total / fluid_profile_data[i].count, + fluid_profile_data[i].max); + } + else + { + FLUID_LOG(FLUID_DBG, "%s: no profiling available", + fluid_profile_data[i].description); + } + } +} + +/* Macro that returns cpu load in percent (%) + * @dur: duration (micro second). + * @sample_rate: sample_rate used in audio driver (Hz). + * @n_amples: number of samples collected during 'dur' duration. +*/ +#define fluid_profile_load(dur,sample_rate,n_samples) \ + (dur * sample_rate / n_samples / 10000.0) + + +/* prints cpu loads only +* +* @param sample_rate the sample rate of audio output. +* @param out output stream device. +* +* ------------------------------------------------------------------------------ +* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices +* ------------------------------------------------------------------------------ +* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices +* -------|---------|---------|----------|---------|---------|------------------- +* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612 +*/ +static void fluid_profiling_print_load(double sample_rate, fluid_ostream_t out) +{ + unsigned int n_voices; /* voices number */ + static const char max_voices_not_available[] = " not available"; + const char *pmax_voices; + char max_voices_available[20]; + + /* First computes data to be printed */ + double total, voices, reverb, chorus, all_voices, voice; + /* voices number */ + n_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ? + fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_voices / + fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count : 0; + + /* total load (%) */ + total = fluid_profile_data[FLUID_PROF_WRITE].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_WRITE].total, sample_rate, + fluid_profile_data[FLUID_PROF_WRITE].n_samples) : 0; + + /* reverb load (%) */ + reverb = fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].total, + sample_rate, + fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].n_samples) : 0; + + /* chorus load (%) */ + chorus = fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].total, + sample_rate, + fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].n_samples) : 0; + + /* total voices load: total - reverb - chorus (%) */ + voices = total - reverb - chorus; + + /* One voice load (%): all_voices / n_voices. */ + all_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].total, + sample_rate, + fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_samples) : 0; + + voice = n_voices ? all_voices / n_voices : 0; + + /* estimated maximum voices number */ + if(voice > 0) + { + FLUID_SNPRINTF(max_voices_available, sizeof(max_voices_available), + "%17d", (unsigned int)((100.0 - reverb - chorus) / voice)); + pmax_voices = max_voices_available; + } + else + { + pmax_voices = max_voices_not_available; + } + + /* Now prints data */ + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " Cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond) and maximum voices\n", + sample_rate, 1000000.0 / sample_rate); + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " nVoices| total(%%)|voices(%%)| reverb(%%)|chorus(%%)| voice(%%)|estimated maxVoices\n"); + fluid_ostream_printf(out, + " -------|---------|---------|----------|---------|---------|-------------------\n"); + fluid_ostream_printf(out, + "%8d|%9.3f|%9.3f|%10.3f|%9.3f|%9.3f|%s\n", n_voices, total, voices, + reverb, chorus, voice, pmax_voices); +} + +/* +* prints profiling data (used by profile shell command: prof_start). +* The function is an internal profiling API between the "profile" command +* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). +* +* @param sample_rate the sample rate of audio output. +* @param out output stream device. +* +* When print mode is 1, the function prints all the information (see below). +* When print mode is 0, the function prints only the cpu loads. +* +* ------------------------------------------------------------------------------ +* Duration(microsecond) and cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) +* ------------------------------------------------------------------------------ +* Code under profiling |Voices| Duration (microsecond) | Load(%) +* | nbr| min| avg| max| +* ---------------------------|------|--------------------------------|---------- +* synth_write_* ------------>| 250| 3.91| 2188.82| 3275.00| 41.544 +* synth_one_block ---------->| 250| 1150.70| 2273.56| 3241.47| 41.100 +* synth_one_block:clear ---->| 250| 3.07| 4.62| 61.18| 0.084 +* synth_one_block:one voice->| 1| 4.19| 9.02| 1044.27| 0.163 +* synth_one_block:all voices>| 250| 1138.41| 2259.11| 3217.73| 40.839 +* synth_one_block:reverb --->| no profiling available +* synth_one_block:chorus --->| no profiling available +* voice:note --------------->| no profiling available +* voice:release ------------>| no profiling available +* ------------------------------------------------------------------------------ +* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices +* ------------------------------------------------------------------------------ +* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices +* -------|---------|---------|----------|---------|---------|------------------- +* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612 +*/ +void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out) +{ + int i; + + if(fluid_profile_print) + { + /* print all details: Duration(microsecond) and cpu loads(%) */ + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " Duration(microsecond) and cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond)\n", + sample_rate, 1000000.0 / sample_rate); + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " Code under profiling |Voices| Duration (microsecond) | Load(%%)\n"); + fluid_ostream_printf(out, + " | nbr| min| avg| max|\n"); + fluid_ostream_printf(out, + " ---------------------------|------|--------------------------------|----------\n"); + + for(i = 0; i < FLUID_PROFILE_NBR; i++) + { + unsigned int count = fluid_profile_data[i].count; + + if(count > 0) + { + /* data are available */ + + if(FLUID_PROF_WRITE <= i && i <= FLUID_PROF_ONE_BLOCK_CHORUS) + { + double load = fluid_profile_load(fluid_profile_data[i].total, sample_rate, + fluid_profile_data[i].n_samples); + fluid_ostream_printf(out, " %s|%6d|%10.2f|%10.2f|%10.2f|%8.3f\n", + fluid_profile_data[i].description, /* code under profiling */ + fluid_profile_data[i].n_voices / count, /* voices number */ + fluid_profile_data[i].min, /* minimum duration */ + fluid_profile_data[i].total / count, /* average duration */ + fluid_profile_data[i].max, /* maximum duration */ + load); /* cpu load */ + } + else + { + /* note and release duration */ + fluid_ostream_printf(out, " %s|%6d|%10.0f|%10.0f|%10.0f|\n", + fluid_profile_data[i].description, /* code under profiling */ + fluid_profile_data[i].n_voices / count, + fluid_profile_data[i].min, /* minimum duration */ + fluid_profile_data[i].total / count, /* average duration */ + fluid_profile_data[i].max); /* maximum duration */ + } + } + else + { + /* data aren't available */ + fluid_ostream_printf(out, + " %s| no profiling available\n", fluid_profile_data[i].description); + } + } + } + + /* prints cpu loads only */ + fluid_profiling_print_load(sample_rate, out);/* prints cpu loads */ +} + +/* + Returns true if the user cancels the current profiling measurement. + Actually this is implemented using the key. To add this functionality: + 1) Adds #define FLUID_PROFILE_CANCEL in fluid_sys.h. + 2) Adds the necessary code inside fluid_profile_is_cancel(). + + When FLUID_PROFILE_CANCEL is not defined, the function return FALSE. +*/ +int fluid_profile_is_cancel_req(void) +{ +#ifdef FLUID_PROFILE_CANCEL + +#if defined(_WIN32) /* Windows specific stuff */ + /* Profile cancellation is supported for Windows */ + /* returns TRUE if key is depressed */ + return(GetAsyncKeyState(VK_RETURN) & 0x1); + +#elif defined(__OS2__) /* OS/2 specific stuff */ + /* Profile cancellation isn't yet supported for OS2 */ + /* For OS2, replaces the following line with the function that returns + true when the keyboard key is depressed */ + return FALSE; /* default value */ + +#else /* POSIX stuff */ + /* Profile cancellation is supported for Linux */ + /* returns true is is depressed */ + { + /* Here select() is used to poll the standard input to see if an input + is ready. As the standard input is usually buffered, the user + needs to depress to set the input to a "ready" state. + */ + struct timeval tv; + fd_set fds; /* just one fds need to be polled */ + tv.tv_sec = 0; /* Setting both values to 0, means a 0 timeout */ + tv.tv_usec = 0; + FD_ZERO(&fds); /* reset fds */ + FD_SET(STDIN_FILENO, &fds); /* sets fds to poll standard input only */ + select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); /* polling */ + return (FD_ISSET(0, &fds)); /* returns true if standard input is ready */ + } +#endif /* OS stuff */ + +#else /* FLUID_PROFILE_CANCEL not defined */ + return FALSE; /* default value */ +#endif /* FLUID_PROFILE_CANCEL */ +} + +/** +* Returns status used in shell command "prof_start". +* The function is an internal profiling API between the "profile" command +* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). +* +* @return status +* - PROFILE_READY profiling data are ready. +* - PROFILE_RUNNING, profiling data are still under acquisition. +* - PROFILE_CANCELED, acquisition has been cancelled by the user. +* - PROFILE_STOP, no acquisition in progress. +* +* When status is PROFILE_RUNNING, the caller can do passive waiting, or other +* work before recalling the function later. +*/ +int fluid_profile_get_status(void) +{ + /* Checks if user has requested to cancel the current measurement */ + /* Cancellation must have precedence over other status */ + if(fluid_profile_is_cancel_req()) + { + fluid_profile_start_stop(0, 0); /* stops the measurement */ + return PROFILE_CANCELED; + } + + switch(fluid_profile_status) + { + case PROFILE_READY: + return PROFILE_READY; /* profiling data are ready */ + + case PROFILE_START: + return PROFILE_RUNNING;/* profiling data are under acquisition */ + + default: + return PROFILE_STOP; + } +} + +/** +* Starts or stops profiling measurement. +* The function is an internal profiling API between the "profile" command +* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). +* +* @param end_tick end position of the measure (in ticks). +* - If end_tick is greater then 0, the function starts a measure if a measure +* isn't running. If a measure is already running, the function does nothing +* and returns. +* - If end_tick is 0, the function stops a measure. +* @param clear_data, +* - If clear_data is 0, the function clears fluid_profile_data before starting +* a measure, otherwise, the data from the started measure will be accumulated +* within fluid_profile_data. +*/ +void fluid_profile_start_stop(unsigned int end_ticks, short clear_data) +{ + if(end_ticks) /* This is a "start" request */ + { + /* Checks if a measure is already running */ + if(fluid_profile_status != PROFILE_START) + { + short i; + fluid_profile_end_ticks = end_ticks; + + /* Clears profile data */ + if(clear_data == 0) + { + for(i = 0; i < FLUID_PROFILE_NBR; i++) + { + fluid_profile_data[i].min = 1e10;/* min sets to max value */ + fluid_profile_data[i].max = 0; /* maximum sets to min value */ + fluid_profile_data[i].total = 0; /* total duration microsecond */ + fluid_profile_data[i].count = 0; /* data count */ + fluid_profile_data[i].n_voices = 0; /* voices number */ + fluid_profile_data[i].n_samples = 0;/* audio samples number */ + } + } + + fluid_profile_status = PROFILE_START; /* starts profiling */ + } + + /* else do nothing when profiling is already started */ + } + else /* This is a "stop" request */ + { + /* forces the current running profile (if any) to stop */ + fluid_profile_status = PROFILE_STOP; /* stops profiling */ + } +} + +#endif /* WITH_PROFILING */ + +/*************************************************************** + * + * Threads + * + */ + +#if OLD_GLIB_THREAD_API + +/* Rather than inline this one, we just declare it as a function, to prevent + * GCC warning about inline failure. */ +fluid_cond_t * +new_fluid_cond(void) +{ + if(!g_thread_supported()) + { + g_thread_init(NULL); + } + + return g_cond_new(); +} + +#endif + +static gpointer +fluid_thread_high_prio(gpointer data) +{ + fluid_thread_info_t *info = data; + + fluid_thread_self_set_prio(info->prio_level); + + info->func(info->data); + FLUID_FREE(info); + + return NULL; +} + +/** + * Create a new thread. + * @param func Function to execute in new thread context + * @param data User defined data to pass to func + * @param prio_level Priority level. If greater than 0 then high priority scheduling will + * be used, with the given priority level (used by pthreads only). 0 uses normal scheduling. + * @param detach If TRUE, 'join' does not work and the thread destroys itself when finished. + * @return New thread pointer or NULL on error + */ +fluid_thread_t * +new_fluid_thread(const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach) +{ + GThread *thread; + fluid_thread_info_t *info = NULL; + GError *err = NULL; + + g_return_val_if_fail(func != NULL, NULL); + +#if OLD_GLIB_THREAD_API + + /* Make sure g_thread_init has been called. + * Probably not a good idea in a shared library, + * but what can we do *and* remain backwards compatible? */ + if(!g_thread_supported()) + { + g_thread_init(NULL); + } + +#endif + + if(prio_level > 0) + { + info = FLUID_NEW(fluid_thread_info_t); + + if(!info) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + info->func = func; + info->data = data; + info->prio_level = prio_level; +#if NEW_GLIB_THREAD_API + thread = g_thread_try_new(name, fluid_thread_high_prio, info, &err); +#else + thread = g_thread_create(fluid_thread_high_prio, info, detach == FALSE, &err); +#endif + } + + else + { +#if NEW_GLIB_THREAD_API + thread = g_thread_try_new(name, (GThreadFunc)func, data, &err); +#else + thread = g_thread_create((GThreadFunc)func, data, detach == FALSE, &err); +#endif + } + + if(!thread) + { + FLUID_LOG(FLUID_ERR, "Failed to create the thread: %s", + fluid_gerror_message(err)); + g_clear_error(&err); + FLUID_FREE(info); + return NULL; + } + +#if NEW_GLIB_THREAD_API + + if(detach) + { + g_thread_unref(thread); // Release thread reference, if caller wants to detach + } + +#endif + + return thread; +} + +/** + * Frees data associated with a thread (does not actually stop thread). + * @param thread Thread to free + */ +void +delete_fluid_thread(fluid_thread_t *thread) +{ + /* Threads free themselves when they quit, nothing to do */ +} + +/** + * Join a thread (wait for it to terminate). + * @param thread Thread to join + * @return FLUID_OK + */ +int +fluid_thread_join(fluid_thread_t *thread) +{ + g_thread_join(thread); + + return FLUID_OK; +} + + +static fluid_thread_return_t +fluid_timer_run(void *data) +{ + fluid_timer_t *timer; + int count = 0; + int cont; + long start; + long delay; + + timer = (fluid_timer_t *)data; + + /* keep track of the start time for absolute positioning */ + start = fluid_curtime(); + + while(timer->cont) + { + cont = (*timer->callback)(timer->data, fluid_curtime() - start); + + count++; + + if(!cont) + { + break; + } + + /* to avoid incremental time errors, calculate the delay between + two callbacks bringing in the "absolute" time (count * + timer->msec) */ + delay = (count * timer->msec) - (fluid_curtime() - start); + + if(delay > 0) + { + fluid_msleep(delay); + } + } + + FLUID_LOG(FLUID_DBG, "Timer thread finished"); + timer->callback = NULL; + + if(timer->auto_destroy) + { + FLUID_FREE(timer); + } + + return FLUID_THREAD_RETURN_VALUE; +} + +fluid_timer_t * +new_fluid_timer(int msec, fluid_timer_callback_t callback, void *data, + int new_thread, int auto_destroy, int high_priority) +{ + fluid_timer_t *timer; + + timer = FLUID_NEW(fluid_timer_t); + + if(timer == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + timer->msec = msec; + timer->callback = callback; + timer->data = data; + timer->cont = TRUE ; + timer->thread = NULL; + timer->auto_destroy = auto_destroy; + + if(new_thread) + { + timer->thread = new_fluid_thread("timer", fluid_timer_run, timer, high_priority + ? FLUID_SYS_TIMER_HIGH_PRIO_LEVEL : 0, FALSE); + + if(!timer->thread) + { + FLUID_FREE(timer); + return NULL; + } + } + else + { + fluid_timer_run(timer); /* Run directly, instead of as a separate thread */ + + if(auto_destroy) + { + /* do NOT return freed memory */ + return NULL; + } + } + + return timer; +} + +void +delete_fluid_timer(fluid_timer_t *timer) +{ + int auto_destroy; + fluid_return_if_fail(timer != NULL); + + auto_destroy = timer->auto_destroy; + + timer->cont = 0; + fluid_timer_join(timer); + + /* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */ + + if(!auto_destroy) + { + FLUID_FREE(timer); + } +} + +int +fluid_timer_join(fluid_timer_t *timer) +{ + int auto_destroy; + + if(timer->thread) + { + auto_destroy = timer->auto_destroy; + fluid_thread_join(timer->thread); + + if(!auto_destroy) + { + timer->thread = NULL; + } + } + + return FLUID_OK; +} + +int +fluid_timer_is_running(const fluid_timer_t *timer) +{ + // for unit test usage only + return timer->callback != NULL; +} + +long fluid_timer_get_interval(const fluid_timer_t * timer) +{ + // for unit test usage only + return timer->msec; +} + + +/*************************************************************** + * + * Sockets and I/O + * + */ + +/** + * Get standard in stream handle. + * @return Standard in stream. + */ +fluid_istream_t +fluid_get_stdin(void) +{ + return STDIN_FILENO; +} + +/** + * Get standard output stream handle. + * @return Standard out stream. + */ +fluid_ostream_t +fluid_get_stdout(void) +{ + return STDOUT_FILENO; +} + +/** + * Read a line from an input stream. + * @return 0 if end-of-stream, -1 if error, non zero otherwise + */ +int +fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt, + char *buf, int len) +{ +#if READLINE_SUPPORT + + if(in == fluid_get_stdin()) + { + char *line; + + line = readline(prompt); + + if(line == NULL) + { + return -1; + } + + FLUID_SNPRINTF(buf, len, "%s", line); + buf[len - 1] = 0; + + if(buf[0] != '\0') + { + add_history(buf); + } + + free(line); + return 1; + } + else +#endif + { + fluid_ostream_printf(out, "%s", prompt); + return fluid_istream_gets(in, buf, len); + } +} + +/** + * Reads a line from an input stream (socket). + * @param in The input socket + * @param buf Buffer to store data to + * @param len Maximum length to store to buf + * @return 1 if a line was read, 0 on end of stream, -1 on error + */ +static int +fluid_istream_gets(fluid_istream_t in, char *buf, int len) +{ + char c; + int n; + + buf[len - 1] = 0; + + while(--len > 0) + { +#ifndef _WIN32 + n = read(in, &c, 1); + + if(n == -1) + { + return -1; + } + +#else + + /* Handle read differently depending on if its a socket or file descriptor */ + if(!(in & FLUID_SOCKET_FLAG)) + { + // usually read() is supposed to return '\n' as last valid character of the user input + // when compiled with compatibility for WinXP however, read() may return 0 (EOF) rather than '\n' + // this would cause the shell to exit early + n = read(in, &c, 1); + + if(n == -1) + { + return -1; + } + } + else + { +#ifdef NETWORK_SUPPORT + n = recv(in & ~FLUID_SOCKET_FLAG, &c, 1, 0); + if(n == SOCKET_ERROR) +#endif + { + return -1; + } + } + +#endif + + if(n == 0) + { + *buf = 0; + // return 1 if read from stdin, else 0, to fix early exit of shell + return (in == STDIN_FILENO); + } + + if(c == '\n') + { + *buf = 0; + return 1; + } + + /* Store all characters excluding CR */ + if(c != '\r') + { + *buf++ = c; + } + } + + return -1; +} + +/** + * Send a printf style string with arguments to an output stream (socket). + * @param out Output stream + * @param format printf style format string + * @param ... Arguments for the printf format string + * @return Number of bytes written or -1 on error + */ +int +fluid_ostream_printf(fluid_ostream_t out, const char *format, ...) +{ + char buf[4096]; + va_list args; + int len; + + va_start(args, format); + len = FLUID_VSNPRINTF(buf, 4095, format, args); + va_end(args); + + if(len == 0) + { + return 0; + } + + if(len < 0) + { + printf("fluid_ostream_printf: buffer overflow"); + return -1; + } + + buf[4095] = 0; + +#ifndef _WIN32 + return write(out, buf, FLUID_STRLEN(buf)); +#else + { + int retval; + + /* Handle write differently depending on if its a socket or file descriptor */ + if(!(out & FLUID_SOCKET_FLAG)) + { + return write(out, buf, (unsigned int)FLUID_STRLEN(buf)); + } + +#ifdef NETWORK_SUPPORT + /* Socket */ + retval = send(out & ~FLUID_SOCKET_FLAG, buf, (int)FLUID_STRLEN(buf), 0); + return retval != SOCKET_ERROR ? retval : -1; +#else + return -1; +#endif + } +#endif +} + +#ifdef NETWORK_SUPPORT + +int fluid_server_socket_join(fluid_server_socket_t *server_socket) +{ + return fluid_thread_join(server_socket->thread); +} + +static int fluid_socket_init(void) +{ +#ifdef _WIN32 + WSADATA wsaData; + int res = WSAStartup(MAKEWORD(2, 2), &wsaData); + + if(res != 0) + { + FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", res); + return FLUID_FAILED; + } + +#endif + + return FLUID_OK; +} + +static void fluid_socket_cleanup(void) +{ +#ifdef _WIN32 + WSACleanup(); +#endif +} + +static int fluid_socket_get_error(void) +{ +#ifdef _WIN32 + return (int)WSAGetLastError(); +#else + return errno; +#endif +} + +fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock) +{ + return sock | FLUID_SOCKET_FLAG; +} + +fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock) +{ + return sock | FLUID_SOCKET_FLAG; +} + +void fluid_socket_close(fluid_socket_t sock) +{ + if(sock != INVALID_SOCKET) + { +#ifdef _WIN32 + closesocket(sock); + +#else + close(sock); +#endif + } +} + +static fluid_thread_return_t fluid_server_socket_run(void *data) +{ + fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data; + fluid_socket_t client_socket; +#ifdef IPV6_SUPPORT + struct sockaddr_in6 addr; +#else + struct sockaddr_in addr; +#endif + +#ifdef HAVE_INETNTOP +#ifdef IPV6_SUPPORT + char straddr[INET6_ADDRSTRLEN]; +#else + char straddr[INET_ADDRSTRLEN]; +#endif /* IPV6_SUPPORT */ +#endif /* HAVE_INETNTOP */ + + socklen_t addrlen = sizeof(addr); + int r; + FLUID_MEMSET((char *)&addr, 0, sizeof(addr)); + + FLUID_LOG(FLUID_DBG, "Server listening for connections"); + + while(server_socket->cont) + { + client_socket = accept(server_socket->socket, (struct sockaddr *)&addr, &addrlen); + + FLUID_LOG(FLUID_DBG, "New client connection"); + + if(client_socket == INVALID_SOCKET) + { + if(server_socket->cont) + { + FLUID_LOG(FLUID_ERR, "Failed to accept connection: %d", fluid_socket_get_error()); + } + + server_socket->cont = 0; + return FLUID_THREAD_RETURN_VALUE; + } + else + { +#ifdef HAVE_INETNTOP + +#ifdef IPV6_SUPPORT + inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr)); +#else + inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr)); +#endif + + r = server_socket->func(server_socket->data, client_socket, + straddr); +#else + r = server_socket->func(server_socket->data, client_socket, + inet_ntoa(addr.sin_addr)); +#endif + + if(r != 0) + { + fluid_socket_close(client_socket); + } + } + } + + FLUID_LOG(FLUID_DBG, "Server closing"); + + return FLUID_THREAD_RETURN_VALUE; +} + +fluid_server_socket_t * +new_fluid_server_socket(int port, fluid_server_func_t func, void *data) +{ + fluid_server_socket_t *server_socket; +#ifdef IPV6_SUPPORT + struct sockaddr_in6 addr; +#else + struct sockaddr_in addr; +#endif + + fluid_socket_t sock; + + fluid_return_val_if_fail(func != NULL, NULL); + + if(fluid_socket_init() != FLUID_OK) + { + return NULL; + } + +#ifdef IPV6_SUPPORT + sock = socket(AF_INET6, SOCK_STREAM, 0); + + if(sock == INVALID_SOCKET) + { + FLUID_LOG(FLUID_ERR, "Failed to create server socket: %d", fluid_socket_get_error()); + fluid_socket_cleanup(); + return NULL; + } + + FLUID_MEMSET(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = htons((uint16_t)port); + addr.sin6_addr = in6addr_any; +#else + + sock = socket(AF_INET, SOCK_STREAM, 0); + + if(sock == INVALID_SOCKET) + { + FLUID_LOG(FLUID_ERR, "Failed to create server socket: %d", fluid_socket_get_error()); + fluid_socket_cleanup(); + return NULL; + } + + FLUID_MEMSET(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons((uint16_t)port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); +#endif + + if(bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) + { + FLUID_LOG(FLUID_ERR, "Failed to bind server socket: %d", fluid_socket_get_error()); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } + + if(listen(sock, SOMAXCONN) == SOCKET_ERROR) + { + FLUID_LOG(FLUID_ERR, "Failed to listen on server socket: %d", fluid_socket_get_error()); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } + + server_socket = FLUID_NEW(fluid_server_socket_t); + + if(server_socket == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } + + server_socket->socket = sock; + server_socket->func = func; + server_socket->data = data; + server_socket->cont = 1; + + server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket, + 0, FALSE); + + if(server_socket->thread == NULL) + { + FLUID_FREE(server_socket); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } + + return server_socket; +} + +void delete_fluid_server_socket(fluid_server_socket_t *server_socket) +{ + fluid_return_if_fail(server_socket != NULL); + + server_socket->cont = 0; + + if(server_socket->socket != INVALID_SOCKET) + { + fluid_socket_close(server_socket->socket); + } + + if(server_socket->thread) + { + fluid_thread_join(server_socket->thread); + delete_fluid_thread(server_socket->thread); + } + + FLUID_FREE(server_socket); + + // Should be called the same number of times as fluid_socket_init() + fluid_socket_cleanup(); +} + +#endif // NETWORK_SUPPORT + +FILE* fluid_file_open(const char* path, const char** errMsg) +{ + static const char ErrExist[] = "File does not exist."; + static const char ErrRegular[] = "File is not regular, refusing to open it."; + static const char ErrNull[] = "File does not exists or insufficient permissions to open it."; + + FILE* handle = NULL; + + if(!fluid_file_test(path, FLUID_FILE_TEST_EXISTS)) + { + if(errMsg != NULL) + { + *errMsg = ErrExist; + } + } + else if(!fluid_file_test(path, FLUID_FILE_TEST_IS_REGULAR)) + { + if(errMsg != NULL) + { + *errMsg = ErrRegular; + } + } + else if((handle = FLUID_FOPEN(path, "rb")) == NULL) + { + if(errMsg != NULL) + { + *errMsg = ErrNull; + } + } + + return handle; +} + +fluid_long_long_t fluid_file_tell(FILE* f) +{ +#ifdef _WIN32 + // On Windows, long is only a 32 bit integer. Thus ftell() does not support to handle files >2GiB. + // We should use _ftelli64() in this case, however its availability depends on MS CRT and might not be + // available on WindowsXP, Win98, etc. + // + // The web recommends to fallback to _telli64() in this case. However, it's return value differs from + // _ftelli64() on Win10: https://github.com/FluidSynth/fluidsynth/pull/629#issuecomment-602238436 + // + // Thus, we use fgetpos(). + fpos_t pos; + if(fgetpos(f, &pos) != 0) + { + return (fluid_long_long_t)-1L; + } + return pos; +#else + return ftell(f); +#endif +} + +#if defined(_WIN32) || defined(__CYGWIN__) +// not thread-safe! +char* fluid_get_windows_error(void) +{ + static TCHAR err[1024]; + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + err, + sizeof(err)/sizeof(err[0]), + NULL); + +#ifdef _UNICODE + static char ascii_err[sizeof(err)]; + + WideCharToMultiByte(CP_UTF8, 0, err, -1, ascii_err, sizeof(ascii_err)/sizeof(ascii_err[0]), 0, 0); + return ascii_err; +#else + return err; +#endif +} +#endif diff --git a/libs/fluidsynth/src/utils/fluid_sys.h b/libs/fluidsynth/src/utils/fluid_sys.h new file mode 100644 index 00000000000..15f3f57b756 --- /dev/null +++ b/libs/fluidsynth/src/utils/fluid_sys.h @@ -0,0 +1,794 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/* + * @file fluid_sys.h + * + * This header contains a bunch of (mostly) system and machine + * dependent functions: + * + * - timers + * - current time in milliseconds and microseconds + * - debug logging + * - profiling + * - memory locking + * - checking for floating point exceptions + * + * fluidsynth's wrapper OSAL so to say; include it in .c files, be careful to include + * it in fluidsynth's private header files (see comment in fluid_coreaudio.c) + */ + +#ifndef _FLUID_SYS_H +#define _FLUID_SYS_H + +#include "fluidsynth_priv.h" + +#if HAVE_MATH_H +#include +#endif + +#if HAVE_ERRNO_H +#include +#endif + +#if HAVE_STDARG_H +#include +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_FCNTL_H +#include +#endif + +#if HAVE_SYS_MMAN_H +#include +#endif + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_SYS_STAT_H +#include +#endif + +#if HAVE_SYS_TIME_H +#include +#endif + +#if HAVE_SYS_SOCKET_H +#include +#endif + +#if HAVE_NETINET_IN_H +#include +#endif + +#if HAVE_NETINET_TCP_H +#include +#endif + +#if HAVE_ARPA_INET_H +#include +#endif + +#if HAVE_LIMITS_H +#include +#endif + +#if HAVE_OPENMP +#include +#endif + +#if HAVE_IO_H +#include // _open(), _close(), read(), write() on windows +#endif + +#if HAVE_SIGNAL_H +#include +#endif + +/** Integer types */ +#if HAVE_STDINT_H +#include + +#else + +/* Assume GLIB types */ +typedef gint8 int8_t; +typedef guint8 uint8_t; +typedef gint16 int16_t; +typedef guint16 uint16_t; +typedef gint32 int32_t; +typedef guint32 uint32_t; +typedef gint64 int64_t; +typedef guint64 uint64_t; +typedef guintptr uintptr_t; +typedef gintptr intptr_t; + +#endif + +/* + * CYGWIN has its own version of , which can be + * safely included together with POSIX includes. + * Thanks to this, CYGWIN can also run audio output and MIDI + * input drivers from traditional interfaces of Windows. + */ +#if defined(__CYGWIN__) && HAVE_WINDOWS_H +#include +#include +#endif + +#if defined(_WIN32) && HAVE_WINDOWS_H +#include +#include /* Provides also socklen_t */ + +/* WIN32 special defines */ +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +#ifdef _MSC_VER +#pragma warning(disable : 4244) +#pragma warning(disable : 4101) +#pragma warning(disable : 4305) +#pragma warning(disable : 4996) +#endif + +#endif + +/* Darwin special defines (taken from config_macosx.h) */ +#ifdef DARWIN +# define MACINTOSH +# define __Types__ +#endif + +#ifdef LADSPA +#include +#endif + +/* #include */ + +/** + * Macro used for safely accessing a message from a GError and using a default + * message if it is NULL. + * @param err Pointer to a GError to access the message field of. + * @return Message string + */ +#define fluid_gerror_message(err) ((err) ? err->message : "No error details") + +#if defined(_WIN32) || defined(__CYGWIN__) +char* fluid_get_windows_error(void); +#endif + +#define FLUID_INLINE inline + +#define FLUID_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) + +/* Integer<->pointer conversion */ +#define FLUID_POINTER_TO_UINT(x) ((unsigned int)(uintptr_t)(x)) +#define FLUID_UINT_TO_POINTER(x) ((void *)(uintptr_t)(x)) +#define FLUID_POINTER_TO_INT(x) ((signed int)(intptr_t)(x)) +#define FLUID_INT_TO_POINTER(x) ((void *)(intptr_t)(x)) + +/* Endian detection */ +#define FLUID_IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN) + +#define FLUID_LE32TOH(x) GINT32_FROM_LE(x) +#define FLUID_LE16TOH(x) GINT16_FROM_LE(x) + +#if FLUID_IS_BIG_ENDIAN +#define FLUID_FOURCC(_a, _b, _c, _d) \ + (uint32_t)(((uint32_t)(_a) << 24) | ((uint32_t)(_b) << 16) | ((uint32_t)(_c) << 8) | (uint32_t)(_d)) +#else +#define FLUID_FOURCC(_a, _b, _c, _d) \ + (uint32_t)(((uint32_t)(_d) << 24) | ((uint32_t)(_c) << 16) | ((uint32_t)(_b) << 8) | (uint32_t)(_a)) +#endif + +/* + * Utility functions + */ +char *fluid_strtok(char **str, char *delim); + +#define FLUID_FILE_TEST_EXISTS G_FILE_TEST_EXISTS +#define FLUID_FILE_TEST_IS_REGULAR G_FILE_TEST_IS_REGULAR +#define fluid_file_test(path, flags) g_file_test(path, flags) + +#define fluid_shell_parse_argv(command_line, argcp, argvp) g_shell_parse_argv(command_line, argcp, argvp, NULL) +#define fluid_strfreev g_strfreev + +#if defined(__OS2__) +#define INCL_DOS +#include + +/* Define socklen_t if not provided */ +#if !HAVE_SOCKLEN_T +typedef int socklen_t; +#endif +#endif + +/** + Time functions + + */ + +unsigned int fluid_curtime(void); +double fluid_utime(void); + + +/** + Timers + + */ + +/* if the callback function returns 1 the timer will continue; if it + returns 0 it will stop */ +typedef int (*fluid_timer_callback_t)(void *data, unsigned int msec); + +typedef struct _fluid_timer_t fluid_timer_t; + +fluid_timer_t *new_fluid_timer(int msec, fluid_timer_callback_t callback, + void *data, int new_thread, int auto_destroy, + int high_priority); + +void delete_fluid_timer(fluid_timer_t *timer); +int fluid_timer_join(fluid_timer_t *timer); +int fluid_timer_stop(fluid_timer_t *timer); +int fluid_timer_is_running(const fluid_timer_t *timer); +long fluid_timer_get_interval(const fluid_timer_t * timer); + +// Macros to use for pre-processor if statements to test which Glib thread API we have (pre or post 2.32) +#define NEW_GLIB_THREAD_API GLIB_CHECK_VERSION(2,32,0) +#define OLD_GLIB_THREAD_API !GLIB_CHECK_VERSION(2,32,0) + +/* Muteces */ + +#if NEW_GLIB_THREAD_API + +/* glib 2.32 and newer */ + +/* Regular mutex */ +typedef GMutex fluid_mutex_t; +#define FLUID_MUTEX_INIT { 0 } +#define fluid_mutex_init(_m) g_mutex_init (&(_m)) +#define fluid_mutex_destroy(_m) g_mutex_clear (&(_m)) +#define fluid_mutex_lock(_m) g_mutex_lock(&(_m)) +#define fluid_mutex_unlock(_m) g_mutex_unlock(&(_m)) + +/* Recursive lock capable mutex */ +typedef GRecMutex fluid_rec_mutex_t; +#define fluid_rec_mutex_init(_m) g_rec_mutex_init(&(_m)) +#define fluid_rec_mutex_destroy(_m) g_rec_mutex_clear(&(_m)) +#define fluid_rec_mutex_lock(_m) g_rec_mutex_lock(&(_m)) +#define fluid_rec_mutex_unlock(_m) g_rec_mutex_unlock(&(_m)) + +/* Dynamically allocated mutex suitable for fluid_cond_t use */ +typedef GMutex fluid_cond_mutex_t; +#define fluid_cond_mutex_lock(m) g_mutex_lock(m) +#define fluid_cond_mutex_unlock(m) g_mutex_unlock(m) + +static FLUID_INLINE fluid_cond_mutex_t * +new_fluid_cond_mutex(void) +{ + GMutex *mutex; + mutex = g_new(GMutex, 1); + g_mutex_init(mutex); + return (mutex); +} + +static FLUID_INLINE void +delete_fluid_cond_mutex(fluid_cond_mutex_t *m) +{ + fluid_return_if_fail(m != NULL); + g_mutex_clear(m); + g_free(m); +} + +/* Thread condition signaling */ +typedef GCond fluid_cond_t; +#define fluid_cond_signal(cond) g_cond_signal(cond) +#define fluid_cond_broadcast(cond) g_cond_broadcast(cond) +#define fluid_cond_wait(cond, mutex) g_cond_wait(cond, mutex) + +static FLUID_INLINE fluid_cond_t * +new_fluid_cond(void) +{ + GCond *cond; + cond = g_new(GCond, 1); + g_cond_init(cond); + return (cond); +} + +static FLUID_INLINE void +delete_fluid_cond(fluid_cond_t *cond) +{ + fluid_return_if_fail(cond != NULL); + g_cond_clear(cond); + g_free(cond); +} + +/* Thread private data */ + +#ifdef _WIN32 /* Wine-specific code */ + +typedef DWORD fluid_private_t; +#define fluid_private_init(_priv) (_priv = TlsAlloc()) +#define fluid_private_free(_priv) TlsFree(_priv); +#define fluid_private_get(_priv) TlsGetValue(_priv) +#define fluid_private_set(_priv, _data) TlsSetValue(_priv, _data) + +#else /* Wine-specific code */ + +typedef GPrivate fluid_private_t; +#define fluid_private_init(_priv) memset (&_priv, 0, sizeof (_priv)) +#define fluid_private_free(_priv) +#define fluid_private_get(_priv) g_private_get(&(_priv)) +#define fluid_private_set(_priv, _data) g_private_set(&(_priv), _data) + +#endif /* Wine-specific code */ + +#else + +/* glib prior to 2.32 */ + +/* Regular mutex */ +typedef GStaticMutex fluid_mutex_t; +#define FLUID_MUTEX_INIT G_STATIC_MUTEX_INIT +#define fluid_mutex_destroy(_m) g_static_mutex_free(&(_m)) +#define fluid_mutex_lock(_m) g_static_mutex_lock(&(_m)) +#define fluid_mutex_unlock(_m) g_static_mutex_unlock(&(_m)) + +#define fluid_mutex_init(_m) do { \ + if (!g_thread_supported ()) g_thread_init (NULL); \ + g_static_mutex_init (&(_m)); \ +} while(0) + +/* Recursive lock capable mutex */ +typedef GStaticRecMutex fluid_rec_mutex_t; +#define fluid_rec_mutex_destroy(_m) g_static_rec_mutex_free(&(_m)) +#define fluid_rec_mutex_lock(_m) g_static_rec_mutex_lock(&(_m)) +#define fluid_rec_mutex_unlock(_m) g_static_rec_mutex_unlock(&(_m)) + +#define fluid_rec_mutex_init(_m) do { \ + if (!g_thread_supported ()) g_thread_init (NULL); \ + g_static_rec_mutex_init (&(_m)); \ +} while(0) + +/* Dynamically allocated mutex suitable for fluid_cond_t use */ +typedef GMutex fluid_cond_mutex_t; +#define delete_fluid_cond_mutex(m) g_mutex_free(m) +#define fluid_cond_mutex_lock(m) g_mutex_lock(m) +#define fluid_cond_mutex_unlock(m) g_mutex_unlock(m) + +static FLUID_INLINE fluid_cond_mutex_t * +new_fluid_cond_mutex(void) +{ + if(!g_thread_supported()) + { + g_thread_init(NULL); + } + + return g_mutex_new(); +} + +/* Thread condition signaling */ +typedef GCond fluid_cond_t; +fluid_cond_t *new_fluid_cond(void); +#define delete_fluid_cond(cond) g_cond_free(cond) +#define fluid_cond_signal(cond) g_cond_signal(cond) +#define fluid_cond_broadcast(cond) g_cond_broadcast(cond) +#define fluid_cond_wait(cond, mutex) g_cond_wait(cond, mutex) + +/* Thread private data */ +typedef GStaticPrivate fluid_private_t; +#define fluid_private_get(_priv) g_static_private_get(&(_priv)) +#define fluid_private_set(_priv, _data) g_static_private_set(&(_priv), _data, NULL) +#define fluid_private_free(_priv) g_static_private_free(&(_priv)) + +#define fluid_private_init(_priv) do { \ + if (!g_thread_supported ()) g_thread_init (NULL); \ + g_static_private_init (&(_priv)); \ +} while(0) + +#endif + + +/* Atomic operations */ + +#define fluid_atomic_int_inc(_pi) g_atomic_int_inc(_pi) +#define fluid_atomic_int_get(_pi) g_atomic_int_get(_pi) +#define fluid_atomic_int_set(_pi, _val) g_atomic_int_set(_pi, _val) +#define fluid_atomic_int_dec_and_test(_pi) g_atomic_int_dec_and_test(_pi) +#define fluid_atomic_int_compare_and_exchange(_pi, _old, _new) \ + g_atomic_int_compare_and_exchange(_pi, _old, _new) + +#if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 30) +#define fluid_atomic_int_exchange_and_add(_pi, _add) \ + g_atomic_int_add(_pi, _add) +#define fluid_atomic_int_add(_pi, _add) \ + g_atomic_int_add(_pi, _add) +#else +#define fluid_atomic_int_exchange_and_add(_pi, _add) \ + g_atomic_int_exchange_and_add(_pi, _add) +#define fluid_atomic_int_add(_pi, _add) \ + g_atomic_int_exchange_and_add(_pi, _add) +#endif + +#define fluid_atomic_pointer_get(_pp) g_atomic_pointer_get(_pp) +#define fluid_atomic_pointer_set(_pp, val) g_atomic_pointer_set(_pp, val) +#define fluid_atomic_pointer_compare_and_exchange(_pp, _old, _new) \ + g_atomic_pointer_compare_and_exchange(_pp, _old, _new) + +static FLUID_INLINE void +fluid_atomic_float_set(fluid_atomic_float_t *fptr, float val) +{ + int32_t ival; + memcpy(&ival, &val, 4); + fluid_atomic_int_set((fluid_atomic_int_t *)fptr, ival); +} + +static FLUID_INLINE float +fluid_atomic_float_get(fluid_atomic_float_t *fptr) +{ + int32_t ival; + float fval; + ival = fluid_atomic_int_get((fluid_atomic_int_t *)fptr); + memcpy(&fval, &ival, 4); + return fval; +} + + +/* Threads */ + +/* other thread implementations might change this for their needs */ +typedef void *fluid_thread_return_t; +/* static return value for thread functions which requires a return value */ +#define FLUID_THREAD_RETURN_VALUE (NULL) + +typedef GThread fluid_thread_t; +typedef fluid_thread_return_t (*fluid_thread_func_t)(void *data); + +#define FLUID_THREAD_ID_NULL NULL /* A NULL "ID" value */ +#define fluid_thread_id_t GThread * /* Data type for a thread ID */ +#define fluid_thread_get_id() g_thread_self() /* Get unique "ID" for current thread */ + +fluid_thread_t *new_fluid_thread(const char *name, fluid_thread_func_t func, void *data, + int prio_level, int detach); +void delete_fluid_thread(fluid_thread_t *thread); +void fluid_thread_self_set_prio(int prio_level); +int fluid_thread_join(fluid_thread_t *thread); + +/* Dynamic Module Loading, currently only used by LADSPA subsystem */ +#ifdef LADSPA + +typedef GModule fluid_module_t; + +#define fluid_module_open(_name) g_module_open((_name), G_MODULE_BIND_LOCAL) +#define fluid_module_close(_mod) g_module_close(_mod) +#define fluid_module_error() g_module_error() +#define fluid_module_name(_mod) g_module_name(_mod) +#define fluid_module_symbol(_mod, _name, _ptr) g_module_symbol((_mod), (_name), (_ptr)) + +#endif /* LADSPA */ + +/* Sockets and I/O */ + +int fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt, char *buf, int len); +int fluid_ostream_printf(fluid_ostream_t out, const char *format, ...); + +#if defined(_WIN32) +typedef SOCKET fluid_socket_t; +#else +typedef int fluid_socket_t; +#endif + +/* The function should return 0 if no error occurred, non-zero + otherwise. If the function return non-zero, the socket will be + closed by the server. */ +typedef int (*fluid_server_func_t)(void *data, fluid_socket_t client_socket, char *addr); + +fluid_server_socket_t *new_fluid_server_socket(int port, fluid_server_func_t func, void *data); +void delete_fluid_server_socket(fluid_server_socket_t *sock); +int fluid_server_socket_join(fluid_server_socket_t *sock); +void fluid_socket_close(fluid_socket_t sock); +fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock); +fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock); + +/* File access */ +#define fluid_stat(_filename, _statbuf) g_stat((_filename), (_statbuf)) +#if !GLIB_CHECK_VERSION(2, 26, 0) + /* GStatBuf has not been introduced yet, manually typedef to what they had at that time: + * https://github.com/GNOME/glib/blob/e7763678b56e3be073cc55d707a6e92fc2055ee0/glib/gstdio.h#L98-L115 + */ + #if defined(_WIN32) || HAVE_WINDOWS_H // somehow reliably mock G_OS_WIN32?? + // Any effort from our side to reliably mock GStatBuf on Windows is in vain. E.g. glib-2.16 is broken as it uses struct stat rather than struct _stat32 on Win x86. + // Disable it (the user has been warned by cmake). + #undef fluid_stat + #define fluid_stat(_filename, _statbuf) (-1) + typedef struct _fluid_stat_buf_t{int st_mtime;} fluid_stat_buf_t; + #else + /* posix, OS/2, etc. */ + typedef struct stat fluid_stat_buf_t; + #endif +#else +typedef GStatBuf fluid_stat_buf_t; +#endif + +FILE* fluid_file_open(const char* filename, const char** errMsg); +fluid_long_long_t fluid_file_tell(FILE* f); + + +/* Profiling */ +#if WITH_PROFILING +/** profiling interface between Profiling command shell and Audio + rendering API (FluidProfile_0004.pdf- 3.2.2) +*/ + +/* + ----------------------------------------------------------------------------- + Shell task side | Profiling interface | Audio task side + ----------------------------------------------------------------------------- + profiling | Internal | | | Audio + command <---> |<-- profiling -->| Data |<--macros -->| <--> rendering + shell | API | | | API + +*/ + +/* default parameters for shell command "prof_start" in fluid_sys.c */ +#define FLUID_PROFILE_DEFAULT_BANK 0 /* default bank */ +#define FLUID_PROFILE_DEFAULT_PROG 16 /* default prog (organ) */ +#define FLUID_PROFILE_FIRST_KEY 12 /* first key generated */ +#define FLUID_PROFILE_LAST_KEY 108 /* last key generated */ +#define FLUID_PROFILE_DEFAULT_VEL 64 /* default note velocity */ +#define FLUID_PROFILE_VOICE_ATTEN -0.04f /* gain attenuation per voice (dB) */ + + +#define FLUID_PROFILE_DEFAULT_PRINT 0 /* default print mode */ +#define FLUID_PROFILE_DEFAULT_N_PROF 1 /* default number of measures */ +#define FLUID_PROFILE_DEFAULT_DURATION 500 /* default duration (ms) */ + + +extern unsigned short fluid_profile_notes; /* number of generated notes */ +extern unsigned char fluid_profile_bank; /* bank,prog preset used by */ +extern unsigned char fluid_profile_prog; /* generated notes */ +extern unsigned char fluid_profile_print; /* print mode */ + +extern unsigned short fluid_profile_n_prof;/* number of measures */ +extern unsigned short fluid_profile_dur; /* measure duration in ms */ +extern fluid_atomic_int_t fluid_profile_lock ; /* lock between multiple shell */ +/**/ + +/*---------------------------------------------- + Internal profiling API (in fluid_sys.c) +-----------------------------------------------*/ +/* Starts a profiling measure used in shell command "prof_start" */ +void fluid_profile_start_stop(unsigned int end_ticks, short clear_data); + +/* Returns status used in shell command "prof_start" */ +int fluid_profile_get_status(void); + +/* Prints profiling data used in shell command "prof_start" */ +void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out); + +/* Returns True if profiling cancellation has been requested */ +int fluid_profile_is_cancel_req(void); + +/* For OS that implement key for profile cancellation: + 1) Adds #define FLUID_PROFILE_CANCEL + 2) Adds the necessary code inside fluid_profile_is_cancel() see fluid_sys.c +*/ +#if defined(_WIN32) /* Profile cancellation is supported for Windows */ +#define FLUID_PROFILE_CANCEL + +#elif defined(__OS2__) /* OS/2 specific stuff */ +/* Profile cancellation isn't yet supported for OS2 */ + +#else /* POSIX stuff */ +#define FLUID_PROFILE_CANCEL /* Profile cancellation is supported for linux */ +#include /* STDIN_FILENO */ +#include /* select() */ +#endif /* posix */ + +/* logging profiling data (used on synthesizer instance deletion) */ +void fluid_profiling_print(void); + +/*---------------------------------------------- + Profiling Data (in fluid_sys.c) +-----------------------------------------------*/ +/** Profiling data. Keep track of min/avg/max values to profile a + piece of code. */ +typedef struct _fluid_profile_data_t +{ + const char *description; /* name of the piece of code under profiling */ + double min, max, total; /* duration (microsecond) */ + unsigned int count; /* total count */ + unsigned int n_voices; /* voices number */ + unsigned int n_samples; /* audio samples number */ +} fluid_profile_data_t; + +enum +{ + /* commands/status (profiling interface) */ + PROFILE_STOP, /* command to stop a profiling measure */ + PROFILE_START, /* command to start a profile measure */ + PROFILE_READY, /* status to signal that a profiling measure has finished + and ready to be printed */ + /*- State returned by fluid_profile_get_status() -*/ + /* between profiling commands and internal profiling API */ + PROFILE_RUNNING, /* a profiling measure is running */ + PROFILE_CANCELED,/* a profiling measure has been canceled */ +}; + +/* Data interface */ +extern unsigned char fluid_profile_status ; /* command and status */ +extern unsigned int fluid_profile_end_ticks; /* ending position (in ticks) */ +extern fluid_profile_data_t fluid_profile_data[]; /* Profiling data */ + +/*---------------------------------------------- + Probes macros +-----------------------------------------------*/ +/** Macro to obtain a time reference used for the profiling */ +#define fluid_profile_ref() fluid_utime() + +/** Macro to create a variable and assign the current reference time for profiling. + * So we don't get unused variable warnings when profiling is disabled. */ +#define fluid_profile_ref_var(name) double name = fluid_utime() + +/** + * Profile identifier numbers. List all the pieces of code you want to profile + * here. Be sure to add an entry in the fluid_profile_data table in + * fluid_sys.c + */ +enum +{ + FLUID_PROF_WRITE, + FLUID_PROF_ONE_BLOCK, + FLUID_PROF_ONE_BLOCK_CLEAR, + FLUID_PROF_ONE_BLOCK_VOICE, + FLUID_PROF_ONE_BLOCK_VOICES, + FLUID_PROF_ONE_BLOCK_REVERB, + FLUID_PROF_ONE_BLOCK_CHORUS, + FLUID_PROF_VOICE_NOTE, + FLUID_PROF_VOICE_RELEASE, + FLUID_PROFILE_NBR /* number of profile probes */ +}; +/** Those macros are used to calculate the min/avg/max. Needs a profile number, a + time reference, the voices and samples number. */ + +/* local macro : acquiere data */ +#define fluid_profile_data(_num, _ref, voices, samples)\ +{\ + double _now = fluid_utime();\ + double _delta = _now - _ref;\ + fluid_profile_data[_num].min = _delta < fluid_profile_data[_num].min ?\ + _delta : fluid_profile_data[_num].min; \ + fluid_profile_data[_num].max = _delta > fluid_profile_data[_num].max ?\ + _delta : fluid_profile_data[_num].max;\ + fluid_profile_data[_num].total += _delta;\ + fluid_profile_data[_num].count++;\ + fluid_profile_data[_num].n_voices += voices;\ + fluid_profile_data[_num].n_samples += samples;\ + _ref = _now;\ +} + +/** Macro to collect data, called from inner functions inside audio + rendering API */ +#define fluid_profile(_num, _ref, voices, samples)\ +{\ + if ( fluid_profile_status == PROFILE_START)\ + { /* acquires data */\ + fluid_profile_data(_num, _ref, voices, samples)\ + }\ +} + +/** Macro to collect data, called from audio rendering API (fluid_write_xxxx()). + This macro control profiling ending position (in ticks). +*/ +#define fluid_profile_write(_num, _ref, voices, samples)\ +{\ + if (fluid_profile_status == PROFILE_START)\ + {\ + /* acquires data first: must be done before checking that profile is + finished to ensure at least one valid data sample. + */\ + fluid_profile_data(_num, _ref, voices, samples)\ + if (fluid_synth_get_ticks(synth) >= fluid_profile_end_ticks)\ + {\ + /* profiling is finished */\ + fluid_profile_status = PROFILE_READY;\ + }\ + }\ +} + +#else + +/* No profiling */ +#define fluid_profiling_print() +#define fluid_profile_ref() 0 +#define fluid_profile_ref_var(name) +#define fluid_profile(_num,_ref,voices, samples) +#define fluid_profile_write(_num,_ref, voices, samples) +#endif /* WITH_PROFILING */ + +/** + + Memory locking + + Memory locking is used to avoid swapping of the large block of + sample data. + */ + +#if defined(HAVE_SYS_MMAN_H) && !defined(__OS2__) +#define fluid_mlock(_p,_n) mlock(_p, _n) +#define fluid_munlock(_p,_n) munlock(_p,_n) +#else +#define fluid_mlock(_p,_n) 0 +#define fluid_munlock(_p,_n) +#endif + + +/** + + Floating point exceptions + + fluid_check_fpe() checks for "unnormalized numbers" and other + exceptions of the floating point processor. +*/ +#ifdef FPE_CHECK +#define fluid_check_fpe(expl) fluid_check_fpe_i386(expl) +#define fluid_clear_fpe() fluid_clear_fpe_i386() +unsigned int fluid_check_fpe_i386(char *explanation_in_case_of_fpe); +void fluid_clear_fpe_i386(void); +#else +#define fluid_check_fpe(expl) +#define fluid_clear_fpe() +#endif + + +/* System control */ +void fluid_msleep(unsigned int msecs); + +/** + * Advances the given \c ptr to the next \c alignment byte boundary. + * Make sure you've allocated an extra of \c alignment bytes to avoid a buffer overflow. + * + * @note \c alignment must be a power of two + * @return Returned pointer is guaranteed to be aligned to \c alignment boundary and in range \f[ ptr <= returned_ptr < ptr + alignment \f]. + */ +static FLUID_INLINE void *fluid_align_ptr(const void *ptr, unsigned int alignment) +{ + uintptr_t ptr_int = (uintptr_t)ptr; + unsigned int offset = ptr_int & (alignment - 1); + unsigned int add = (alignment - offset) & (alignment - 1); // advance the pointer to the next alignment boundary + ptr_int += add; + + /* assert alignment is power of two */ + FLUID_ASSERT(!(alignment == 0) && !(alignment & (alignment - 1))); + + return (void *)ptr_int; +} + +#define FLUID_DEFAULT_ALIGNMENT (64U) + +#endif /* _FLUID_SYS_H */ diff --git a/libs/fluidsynth/src/utils/fluidsynth_priv.h b/libs/fluidsynth/src/utils/fluidsynth_priv.h new file mode 100644 index 00000000000..843c0143475 --- /dev/null +++ b/libs/fluidsynth/src/utils/fluidsynth_priv.h @@ -0,0 +1,331 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* + * @file fluidsynth_priv.h + * + * lightweight part of fluid_sys.h, containing forward declarations of fluidsynth's private types and private macros + * + * include this one file in fluidsynth's private header files + */ + +#ifndef _FLUIDSYNTH_PRIV_H +#define _FLUIDSYNTH_PRIV_H + +#include "config.h" + +#include + +#if HAVE_STDLIB_H +#include // malloc, free +#endif + +#if HAVE_STDIO_H +#include // printf +#endif + +#if HAVE_STRING_H +#include +#endif + +#if HAVE_STRINGS_H +#include +#endif + +#include "fluidsynth.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************************************** + * + * BASIC TYPES + */ + +#if defined(WITH_FLOAT) +typedef float fluid_real_t; +#else +typedef double fluid_real_t; +#endif + +#if defined(SUPPORTS_VLA) +# define FLUID_DECLARE_VLA(_type, _name, _len) \ + _type _name[_len] +#else +# define FLUID_DECLARE_VLA(_type, _name, _len) \ + _type* _name = g_newa(_type, (_len)) +#endif + + +/** Atomic types */ +typedef int fluid_atomic_int_t; +typedef unsigned int fluid_atomic_uint_t; +typedef float fluid_atomic_float_t; + + +/*************************************************************** + * + * FORWARD DECLARATIONS + */ +typedef struct _fluid_env_data_t fluid_env_data_t; +typedef struct _fluid_adriver_definition_t fluid_adriver_definition_t; +typedef struct _fluid_channel_t fluid_channel_t; +typedef struct _fluid_tuning_t fluid_tuning_t; +typedef struct _fluid_hashtable_t fluid_hashtable_t; +typedef struct _fluid_client_t fluid_client_t; +typedef struct _fluid_server_socket_t fluid_server_socket_t; +typedef struct _fluid_sample_timer_t fluid_sample_timer_t; +typedef struct _fluid_zone_range_t fluid_zone_range_t; +typedef struct _fluid_rvoice_eventhandler_t fluid_rvoice_eventhandler_t; + +/* Declare rvoice related typedefs here instead of fluid_rvoice.h, as it's needed + * in fluid_lfo.c and fluid_adsr.c as well */ +typedef union _fluid_rvoice_param_t +{ + void *ptr; + int i; + fluid_real_t real; +} fluid_rvoice_param_t; +enum { MAX_EVENT_PARAMS = 7 }; /**< Maximum number of #fluid_rvoice_param_t to be passed to an #fluid_rvoice_function_t */ +typedef void (*fluid_rvoice_function_t)(void *obj, const fluid_rvoice_param_t param[MAX_EVENT_PARAMS]); + +/* Macro for declaring an rvoice event function (#fluid_rvoice_function_t). The functions may only access + * those params that were previously set in fluid_voice.c + */ +#define DECLARE_FLUID_RVOICE_FUNCTION(name) void name(void* obj, const fluid_rvoice_param_t param[MAX_EVENT_PARAMS]) + + +/*************************************************************** + * + * CONSTANTS + */ + +#define FLUID_BUFSIZE 64 /**< FluidSynth internal buffer size (in samples) */ +#define FLUID_MIXER_MAX_BUFFERS_DEFAULT (8192/FLUID_BUFSIZE) /**< Number of buffers that can be processed in one rendering run */ +#define FLUID_MAX_EVENTS_PER_BUFSIZE 1024 /**< Maximum queued MIDI events per #FLUID_BUFSIZE */ +#define FLUID_MAX_RETURN_EVENTS 1024 /**< Maximum queued synthesis thread return events */ +#define FLUID_MAX_EVENT_QUEUES 16 /**< Maximum number of unique threads queuing events */ +#define FLUID_DEFAULT_AUDIO_RT_PRIO 60 /**< Default setting for audio.realtime-prio */ +#define FLUID_DEFAULT_MIDI_RT_PRIO 50 /**< Default setting for midi.realtime-prio */ +#define FLUID_NUM_MOD 64 /**< Maximum number of modulators in a voice */ + +/*************************************************************** + * + * SYSTEM INTERFACE + */ + +/* Math constants */ +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795 +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530941723212145818 +#endif + +#ifndef M_LN10 +#define M_LN10 2.3025850929940456840179914546844 +#endif + +#define FLUID_M_PI ((fluid_real_t)M_PI) +#define FLUID_M_LN2 ((fluid_real_t)M_LN2) +#define FLUID_M_LN10 ((fluid_real_t)M_LN10) + +/* Math functions */ +#if defined WITH_FLOAT && defined HAVE_SINF +#define FLUID_SIN sinf +#else +#define FLUID_SIN (fluid_real_t)sin +#endif + +#if defined WITH_FLOAT && defined HAVE_COSF +#define FLUID_COS cosf +#else +#define FLUID_COS (fluid_real_t)cos +#endif + +#if defined WITH_FLOAT && defined HAVE_FABSF +#define FLUID_FABS fabsf +#else +#define FLUID_FABS (fluid_real_t)fabs +#endif + +#if defined WITH_FLOAT && defined HAVE_POWF +#define FLUID_POW powf +#else +#define FLUID_POW (fluid_real_t)pow +#endif + +#if defined WITH_FLOAT && defined HAVE_SQRTF +#define FLUID_SQRT sqrtf +#else +#define FLUID_SQRT (fluid_real_t)sqrt +#endif + +#if defined WITH_FLOAT && defined HAVE_LOGF +#define FLUID_LOGF logf +#else +#define FLUID_LOGF (fluid_real_t)log +#endif + +/* Memory allocation */ +#define FLUID_MALLOC(_n) fluid_alloc(_n) +#define FLUID_REALLOC(_p,_n) realloc(_p,_n) +#define FLUID_FREE(_p) fluid_free(_p) +#define FLUID_NEW(_t) (_t*)FLUID_MALLOC(sizeof(_t)) +#define FLUID_ARRAY_ALIGNED(_t,_n,_a) (_t*)FLUID_MALLOC((_n)*sizeof(_t) + ((unsigned int)_a - 1u)) +#define FLUID_ARRAY(_t,_n) FLUID_ARRAY_ALIGNED(_t,_n,1u) + +void* fluid_alloc(size_t len); + +/* File access */ +#define FLUID_FOPEN(_f,_m) fluid_fopen(_f,_m) +#define FLUID_FCLOSE(_f) fclose(_f) +#define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f) + +FILE *fluid_fopen(const char *filename, const char *mode); + +#ifdef _WIN32 +#define FLUID_FSEEK(_f,_n,_set) _fseeki64(_f,_n,_set) +#else +#define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set) +#endif + +#define FLUID_FTELL(_f) fluid_file_tell(_f) + +/* Memory functions */ +#define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n) +#define FLUID_MEMSET(_s,_c,_n) memset(_s,_c,_n) + +/* String functions */ +#define FLUID_STRLEN(_s) strlen(_s) +#define FLUID_STRCMP(_s,_t) strcmp(_s,_t) +#define FLUID_STRNCMP(_s,_t,_n) strncmp(_s,_t,_n) +#define FLUID_STRCPY(_dst,_src) strcpy(_dst,_src) +#define FLUID_STRTOL(_s,_e,_b) strtol(_s,_e,_b) + +#define FLUID_STRNCPY(_dst,_src,_n) \ +do { strncpy(_dst,_src,_n-1); \ + (_dst)[(_n)-1]='\0'; \ +}while(0) + +#define FLUID_STRCHR(_s,_c) strchr(_s,_c) +#define FLUID_STRRCHR(_s,_c) strrchr(_s,_c) + +#ifdef strdup +#define FLUID_STRDUP(s) strdup(s) +#else +#define FLUID_STRDUP(s) FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s) +#endif + +#define FLUID_SPRINTF sprintf +#define FLUID_FPRINTF fprintf + +#if (defined(_WIN32) && _MSC_VER < 1900) || defined(MINGW32) +/* need to make sure we use a C99 compliant implementation of (v)snprintf(), + * i.e. not microsofts non compliant extension _snprintf() as it doesn't + * reliably null-terminate the buffer + */ +#define FLUID_SNPRINTF g_snprintf +#else +#define FLUID_SNPRINTF snprintf +#endif + +#if (defined(_WIN32) && _MSC_VER < 1500) || defined(MINGW32) +#define FLUID_VSNPRINTF g_vsnprintf +#else +#define FLUID_VSNPRINTF vsnprintf +#endif + +#if defined(_WIN32) && !defined(MINGW32) +#define FLUID_STRCASECMP _stricmp +#else +#define FLUID_STRCASECMP strcasecmp +#endif + +#if defined(_WIN32) && !defined(MINGW32) +#define FLUID_STRNCASECMP _strnicmp +#else +#define FLUID_STRNCASECMP strncasecmp +#endif + + +#define fluid_clip(_val, _min, _max) \ +{ (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); } + +#if WITH_FTS +#define FLUID_PRINTF post +#define FLUID_FLUSH() +#else +#define FLUID_PRINTF printf +#define FLUID_FLUSH() fflush(stdout) +#endif + +/* People who want to reduce the size of the may do this by entirely + * removing the logging system. This will cause all log messages to + * be discarded at compile time, allowing to save about 80 KiB for + * the compiled binary. + */ +#if 0 +#define FLUID_LOG (void)sizeof +#else +#define FLUID_LOG fluid_log +#endif + +#if defined(DEBUG) && !defined(NDEBUG) +#define FLUID_ASSERT(a) g_assert(a) +#else +#define FLUID_ASSERT(a) +#endif + +#define FLUID_LIKELY G_LIKELY +#define FLUID_UNLIKELY G_UNLIKELY + +/* Misc */ +#if defined(__INTEL_COMPILER) +#define FLUID_RESTRICT restrict +#elif defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#define FLUID_RESTRICT __restrict__ +#elif defined(_MSC_VER) && _MSC_VER >= 1400 +#define FLUID_RESTRICT __restrict +#else +#warning "Dont know how this compiler handles restrict pointers, refuse to use them." +#define FLUID_RESTRICT +#endif + +#define FLUID_N_ELEMENTS(struct) (sizeof (struct) / sizeof (struct[0])) +#define FLUID_MEMBER_SIZE(struct, member) ( sizeof (((struct *)0)->member) ) + + +#define fluid_return_if_fail(cond) \ +if(cond) \ + ; \ +else \ + return + +#define fluid_return_val_if_fail(cond, val) \ + fluid_return_if_fail(cond) (val) + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_PRIV_H */ From 2fd034812504e632e393c4940f7c69a16886266a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 8 Sep 2023 10:05:28 +0200 Subject: [PATCH 0987/1148] fluidsynth: Use Wine debugging facility for traces. (cherry picked from commit b5903214ab6f564acadaee5c4c934f8a72e442c5) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- libs/fluidsynth/src/utils/fluid_sys.c | 2 +- libs/fluidsynth/src/utils/fluidsynth_priv.h | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/libs/fluidsynth/src/utils/fluid_sys.c b/libs/fluidsynth/src/utils/fluid_sys.c index e94123022b2..0f8e6a608fc 100644 --- a/libs/fluidsynth/src/utils/fluid_sys.c +++ b/libs/fluidsynth/src/utils/fluid_sys.c @@ -640,7 +640,7 @@ void fluid_profiling_print(void) { int i; - printf("fluid_profiling_print\n"); + FLUID_LOG(FLUID_INFO, "fluid_profiling_print\n"); FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)"); diff --git a/libs/fluidsynth/src/utils/fluidsynth_priv.h b/libs/fluidsynth/src/utils/fluidsynth_priv.h index 843c0143475..1191ac59595 100644 --- a/libs/fluidsynth/src/utils/fluidsynth_priv.h +++ b/libs/fluidsynth/src/utils/fluidsynth_priv.h @@ -51,6 +51,10 @@ #include "fluidsynth.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(fluidsynth); + #ifdef __cplusplus extern "C" { #endif @@ -275,8 +279,8 @@ do { strncpy(_dst,_src,_n-1); \ #define FLUID_PRINTF post #define FLUID_FLUSH() #else -#define FLUID_PRINTF printf -#define FLUID_FLUSH() fflush(stdout) +#define FLUID_PRINTF WINE_TRACE +#define FLUID_FLUSH() #endif /* People who want to reduce the size of the may do this by entirely @@ -287,7 +291,12 @@ do { strncpy(_dst,_src,_n-1); \ #if 0 #define FLUID_LOG (void)sizeof #else -#define FLUID_LOG fluid_log +#define WINE_FLUID_DBG WINE_TRACE +#define WINE_FLUID_INFO WINE_TRACE +#define WINE_FLUID_WARN WINE_WARN +#define WINE_FLUID_ERR WINE_ERR +#define WINE_FLUID_PANIC WINE_ERR +#define FLUID_LOG( x, msg, ... ) do { WINE_ ## x( msg, ## __VA_ARGS__ ); WINE_ ## x( "\n" ); } while (0) #endif #if defined(DEBUG) && !defined(NDEBUG) From a1533ec39b116a498a2756f56f73710b628981b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 8 Sep 2023 09:10:04 +0200 Subject: [PATCH 0988/1148] dmsynth: Simplify IDirectMusicSynth8_Open checks. (cherry picked from commit fcc8a1be68d414ae037d555f77bb80de2d10a6b8) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 126 +++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 66 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 2b5a0905685..6582a9931e9 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -316,85 +316,79 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *params) { struct synth *This = impl_from_IDirectMusicSynth8(iface); - BOOL modified = FALSE; - const DMUS_PORTPARAMS def = { - .dwValidParams = DMUS_PORTPARAMS_VOICES|DMUS_PORTPARAMS_CHANNELGROUPS| - DMUS_PORTPARAMS_AUDIOCHANNELS|DMUS_PORTPARAMS_SAMPLERATE|DMUS_PORTPARAMS_EFFECTS| - DMUS_PORTPARAMS_SHARE|DMUS_PORTPARAMS_FEATURES, - .dwSize = sizeof(def), .dwVoices = 32, .dwChannelGroups = 2, .dwAudioChannels = 2, - .dwSampleRate = 22050, .dwEffectFlags = DMUS_EFFECT_REVERB + DMUS_PORTPARAMS actual = + { + .dwSize = sizeof(DMUS_PORTPARAMS), + .dwValidParams = DMUS_PORTPARAMS_VOICES | DMUS_PORTPARAMS_CHANNELGROUPS + | DMUS_PORTPARAMS_AUDIOCHANNELS | DMUS_PORTPARAMS_SAMPLERATE + | DMUS_PORTPARAMS_EFFECTS | DMUS_PORTPARAMS_SHARE | DMUS_PORTPARAMS_FEATURES, + .dwVoices = 32, + .dwChannelGroups = 2, + .dwAudioChannels = 2, + .dwSampleRate = 22050, + .dwEffectFlags = DMUS_EFFECT_REVERB, }; + UINT size = sizeof(DMUS_PORTPARAMS); + BOOL modified = FALSE; TRACE("(%p, %p)\n", This, params); - if (This->open) - return DMUS_E_ALREADYOPEN; - if (params && params->dwSize < sizeof(DMUS_PORTPARAMS7)) - return E_INVALIDARG; + if (This->open) return DMUS_E_ALREADYOPEN; - This->open = TRUE; + if (params) + { + if (params->dwSize < sizeof(DMUS_PORTPARAMS7)) return E_INVALIDARG; + if (size > params->dwSize) size = params->dwSize; - if (!params) { - memcpy(&This->params, &def, sizeof(This->params)); - return S_OK; - } + if ((params->dwValidParams & DMUS_PORTPARAMS_VOICES) && params->dwVoices) + { + actual.dwVoices = min(params->dwVoices, This->caps.dwMaxVoices); + modified |= actual.dwVoices != params->dwVoices; + } - if (params->dwValidParams & DMUS_PORTPARAMS_VOICES && params->dwVoices) { - if (params->dwVoices > This->caps.dwMaxVoices) { - modified = TRUE; - params->dwVoices = This->caps.dwMaxVoices; + if ((params->dwValidParams & DMUS_PORTPARAMS_CHANNELGROUPS) && params->dwChannelGroups) + { + actual.dwChannelGroups = min(params->dwChannelGroups, This->caps.dwMaxChannelGroups); + modified |= actual.dwChannelGroups != params->dwChannelGroups; } - } else - params->dwVoices = def.dwVoices; - if (params->dwValidParams & DMUS_PORTPARAMS_CHANNELGROUPS && params->dwChannelGroups) { - if (params->dwChannelGroups > This->caps.dwMaxChannelGroups) { - modified = TRUE; - params->dwChannelGroups = This->caps.dwMaxChannelGroups; + if ((params->dwValidParams & DMUS_PORTPARAMS_AUDIOCHANNELS) && params->dwAudioChannels) + { + actual.dwAudioChannels = min(params->dwAudioChannels, This->caps.dwMaxAudioChannels); + modified |= actual.dwAudioChannels != params->dwAudioChannels; } - } else - params->dwChannelGroups = def.dwChannelGroups; - if (params->dwValidParams & DMUS_PORTPARAMS_AUDIOCHANNELS && params->dwAudioChannels) { - if (params->dwAudioChannels > This->caps.dwMaxAudioChannels) { - modified = TRUE; - params->dwAudioChannels = This->caps.dwMaxAudioChannels; + if ((params->dwValidParams & DMUS_PORTPARAMS_SAMPLERATE) && params->dwSampleRate) + { + actual.dwSampleRate = min(max(params->dwSampleRate, 11025), 96000); + modified |= actual.dwSampleRate != params->dwSampleRate; } - } else - params->dwAudioChannels = def.dwAudioChannels; - - if (params->dwValidParams & DMUS_PORTPARAMS_SAMPLERATE && params->dwSampleRate) { - if (params->dwSampleRate > 96000) { - modified = TRUE; - params->dwSampleRate = 96000; - } else if (params->dwSampleRate < 11025) { - modified = TRUE; - params->dwSampleRate = 11025; + + if (params->dwValidParams & DMUS_PORTPARAMS_EFFECTS) + { + actual.dwEffectFlags = DMUS_EFFECT_REVERB; + modified |= actual.dwEffectFlags != params->dwEffectFlags; + } + + if (params->dwValidParams & DMUS_PORTPARAMS_SHARE) + { + actual.fShare = FALSE; + modified |= actual.fShare != params->fShare; + } + + if (params->dwSize < sizeof(*params)) + actual.dwValidParams &= ~DMUS_PORTPARAMS_FEATURES; + else if ((params->dwValidParams & DMUS_PORTPARAMS_FEATURES) && params->dwFeatures) + { + actual.dwFeatures = params->dwFeatures & (DMUS_PORT_FEATURE_AUDIOPATH | DMUS_PORT_FEATURE_STREAMING); + modified |= actual.dwFeatures != params->dwFeatures; } - } else - params->dwSampleRate = def.dwSampleRate; - - if (params->dwValidParams & DMUS_PORTPARAMS_EFFECTS && params->dwEffectFlags != def.dwEffectFlags) - modified = TRUE; - params->dwEffectFlags = def.dwEffectFlags; - - if (params->dwValidParams & DMUS_PORTPARAMS_SHARE && params->fShare) - modified = TRUE; - params->fShare = FALSE; - - if (params->dwSize >= sizeof(*params)) { - if (params->dwValidParams & DMUS_PORTPARAMS_FEATURES && params->dwFeatures) { - if (params->dwFeatures & ~(DMUS_PORT_FEATURE_AUDIOPATH|DMUS_PORT_FEATURE_STREAMING)) { - modified = TRUE; - params->dwFeatures &= DMUS_PORT_FEATURE_AUDIOPATH|DMUS_PORT_FEATURE_STREAMING; - } - } else - params->dwFeatures = def.dwFeatures; - params->dwValidParams = def.dwValidParams; - } else - params->dwValidParams = def.dwValidParams & ~DMUS_PORTPARAMS_FEATURES; - - memcpy(&This->params, params, min(params->dwSize, sizeof(This->params))); + + memcpy(params, &actual, size); + } + + This->params = actual; + This->open = TRUE; return modified ? S_FALSE : S_OK; } From 4294729dfc27220cba879f61875221948d082b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 2 Oct 2023 08:59:32 +0200 Subject: [PATCH 0989/1148] dmsynth: Create a fluid_synth instance on Open. (cherry picked from commit 3cfa740cd73200577fe8109aa02d2e17f0298fe5) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 9 ++++++++- dlls/dmsynth/tests/dmsynth.c | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 6582a9931e9..23ea57c3a0f 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -235,6 +235,7 @@ struct synth struct list waves; fluid_settings_t *fluid_settings; + fluid_synth_t *fluid_synth; }; static inline struct synth *impl_from_IDirectMusicSynth8(IDirectMusicSynth8 *iface) @@ -354,7 +355,8 @@ static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *par if ((params->dwValidParams & DMUS_PORTPARAMS_AUDIOCHANNELS) && params->dwAudioChannels) { - actual.dwAudioChannels = min(params->dwAudioChannels, This->caps.dwMaxAudioChannels); + /* FluidSynth only works with stereo */ + actual.dwAudioChannels = 2; modified |= actual.dwAudioChannels != params->dwAudioChannels; } @@ -387,6 +389,9 @@ static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *par memcpy(params, &actual, size); } + fluid_settings_setnum(This->fluid_settings, "synth.sample-rate", actual.dwSampleRate); + if (!(This->fluid_synth = new_fluid_synth(This->fluid_settings))) return E_OUTOFMEMORY; + This->params = actual; This->open = TRUE; @@ -402,6 +407,8 @@ static HRESULT WINAPI synth_Close(IDirectMusicSynth8 *iface) if (!This->open) return DMUS_E_ALREADYCLOSED; + delete_fluid_synth(This->fluid_synth); + This->fluid_synth = NULL; This->open = FALSE; return S_OK; diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 51504d68a17..ad4bb5c0ad5 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -437,7 +437,7 @@ static void test_dmsynth(void) ok(params.dwValidParams == all_params, "dwValidParams: %#lx\n", params.dwValidParams); ok(params.dwVoices == 1, "dwVoices: %ld\n", params.dwVoices); ok(params.dwChannelGroups == 1, "dwChannelGroups: %ld\n", params.dwChannelGroups); - ok(params.dwAudioChannels == 1, "dwAudioChannels: %ld\n", params.dwAudioChannels); + todo_wine ok(params.dwAudioChannels == 1, "dwAudioChannels: %ld\n", params.dwAudioChannels); ok(params.dwSampleRate == 11025, "dwSampleRate: %ld\n", params.dwSampleRate); test_synth_getformat(dmsynth, ¶ms, "min"); IDirectMusicSynth_Close(dmsynth); @@ -525,7 +525,7 @@ static void test_dmsynth(void) params.dwValidParams = DMUS_PORTPARAMS_AUDIOCHANNELS; params.dwAudioChannels = 1; hr = IDirectMusicSynth_Open(dmsynth, ¶ms); - ok(hr == S_OK, "Open failed: %#lx\n", hr); + todo_wine_if(SUCCEEDED(hr)) ok(hr == S_OK, "Open failed: %#lx\n", hr); hr = IDirectMusicSynthSink_GetDesiredBufferSize(dmsynth_sink, &size); ok(hr == S_OK, "IDirectMusicSynthSink_GetDesiredBufferSize returned: %#lx\n", hr); ok(size == params.dwSampleRate * params.dwAudioChannels * 4, "size: %ld\n", size); From d6e6e63e9a3e440ecfe5f06b02315304d9910638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 11:40:01 +0200 Subject: [PATCH 0990/1148] dmsynth: Create and register a fluid_sfont instance. (cherry picked from commit 907c67ce3cfef4a2b69315c226d3995831f0c5c7) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 23ea57c3a0f..9bb1e884229 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -235,6 +235,7 @@ struct synth struct list waves; fluid_settings_t *fluid_settings; + fluid_sfont_t *fluid_sfont; fluid_synth_t *fluid_synth; }; @@ -307,6 +308,9 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) wave_release(wave); } + fluid_sfont_set_data(This->fluid_sfont, NULL); + delete_fluid_sfont(This->fluid_sfont); + This->fluid_sfont = NULL; delete_fluid_settings(This->fluid_settings); free(This); } @@ -331,6 +335,7 @@ static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *par }; UINT size = sizeof(DMUS_PORTPARAMS); BOOL modified = FALSE; + UINT id; TRACE("(%p, %p)\n", This, params); @@ -391,6 +396,8 @@ static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *par fluid_settings_setnum(This->fluid_settings, "synth.sample-rate", actual.dwSampleRate); if (!(This->fluid_synth = new_fluid_synth(This->fluid_settings))) return E_OUTOFMEMORY; + if ((id = fluid_synth_add_sfont(This->fluid_synth, This->fluid_sfont)) == FLUID_FAILED) + WARN("Failed to add fluid_sfont to fluid_synth\n"); This->params = actual; This->open = TRUE; @@ -407,6 +414,7 @@ static HRESULT WINAPI synth_Close(IDirectMusicSynth8 *iface) if (!This->open) return DMUS_E_ALREADYCLOSED; + fluid_synth_remove_sfont(This->fluid_synth, This->fluid_sfont); delete_fluid_synth(This->fluid_synth); This->fluid_synth = NULL; This->open = FALSE; @@ -1066,6 +1074,44 @@ static const IKsControlVtbl synth_control_vtbl = synth_control_KsEvent, }; +static const char *synth_sfont_get_name(fluid_sfont_t *fluid_sfont) +{ + return "DirectMusicSynth"; +} + +static fluid_preset_t *synth_sfont_get_preset(fluid_sfont_t *fluid_sfont, int bank, int patch) +{ + struct synth *synth = fluid_sfont_get_data(fluid_sfont); + struct instrument *instrument; + + TRACE("(%p, %d, %d)\n", fluid_sfont, bank, patch); + + if (!synth) return NULL; + + LIST_FOR_EACH_ENTRY(instrument, &synth->instruments, struct instrument, entry) + if (instrument->patch == patch) break; + if (&instrument->entry == &synth->instruments) return NULL; + + FIXME("Preset not implemented yet\n"); + return NULL; +} + +static void synth_sfont_iter_start(fluid_sfont_t *fluid_sfont) +{ + FIXME("(%p): stub\n", fluid_sfont); +} + +static fluid_preset_t *synth_sfont_iter_next(fluid_sfont_t *fluid_sfont) +{ + FIXME("(%p): stub\n", fluid_sfont); + return NULL; +} + +static int synth_sfont_free(fluid_sfont_t *fluid_sfont) +{ + return 0; +} + HRESULT synth_create(IUnknown **ret_iface) { struct synth *obj; @@ -1094,12 +1140,17 @@ HRESULT synth_create(IUnknown **ret_iface) list_init(&obj->waves); if (!(obj->fluid_settings = new_fluid_settings())) goto failed; + if (!(obj->fluid_sfont = new_fluid_sfont(synth_sfont_get_name, synth_sfont_get_preset, + synth_sfont_iter_start, synth_sfont_iter_next, synth_sfont_free))) + goto failed; + fluid_sfont_set_data(obj->fluid_sfont, obj); TRACE("Created DirectMusicSynth %p\n", obj); *ret_iface = (IUnknown *)&obj->IDirectMusicSynth8_iface; return S_OK; failed: + delete_fluid_settings(obj->fluid_settings); free(obj); return E_OUTOFMEMORY; } From a9243c3be5396fb2540034598a0f43411a9b38c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 9 Oct 2023 15:57:36 +0200 Subject: [PATCH 0991/1148] include: Avoid narrowing warning in wine_dbgstr_fourcc. (cherry picked from commit bc7e51b48c9919cbb71a8bc0a7cb821c1dcbb5ec) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- include/wine/debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wine/debug.h b/include/wine/debug.h index c282f52a3b6..52e072e1f2b 100644 --- a/include/wine/debug.h +++ b/include/wine/debug.h @@ -317,7 +317,7 @@ static inline const char *wine_dbgstr_guid( const GUID *id ) static inline const char *wine_dbgstr_fourcc( unsigned int fourcc ) { - char str[4] = { fourcc, fourcc >> 8, fourcc >> 16, fourcc >> 24 }; + char str[4] = { (char)fourcc, (char)(fourcc >> 8), (char)(fourcc >> 16), (char)(fourcc >> 24) }; if (!fourcc) return "''"; if (isprint( str[0] ) && isprint( str[1] ) && isprint( str[2] ) && isprint( str[3] )) From 62ed4586de9038f0a2e593f6d40a1d518f2ff1b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Sep 2023 10:48:59 +0200 Subject: [PATCH 0992/1148] dmime: Get rid of the IDirectMusicWaveTrack typedef. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=9027 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=34751 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45135 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48220 (cherry picked from commit 51c664b2275e93468d3e8c684f84c3939ac86fbc) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/wavetrack.c | 105 ++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index 4150ea3bcd2..279cfb5c768 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -1,5 +1,4 @@ -/* IDirectMusicWaveTrack Implementation - * +/* * Copyright (C) 2003-2004 Rok Mandeljc * * This program is free software; you can redistribute it and/or @@ -22,9 +21,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmime); -/***************************************************************************** - * IDirectMusicWaveTrack implementation - */ struct wave_item { struct list entry; DMUS_IO_WAVE_ITEM_HEADER header; @@ -37,29 +33,30 @@ struct wave_part { struct list items; }; -typedef struct IDirectMusicWaveTrack { +struct wave_track +{ IDirectMusicTrack8 IDirectMusicTrack8_iface; struct dmobject dmobj; /* IPersistStream only */ LONG ref; DMUS_IO_WAVE_TRACK_HEADER header; struct list parts; -} IDirectMusicWaveTrack; +}; -/* IDirectMusicWaveTrack IDirectMusicTrack8 part: */ -static inline IDirectMusicWaveTrack *impl_from_IDirectMusicTrack8(IDirectMusicTrack8 *iface) +/* struct wave_track IDirectMusicTrack8 part: */ +static inline struct wave_track *impl_from_IDirectMusicTrack8(IDirectMusicTrack8 *iface) { - return CONTAINING_RECORD(iface, IDirectMusicWaveTrack, IDirectMusicTrack8_iface); + return CONTAINING_RECORD(iface, struct wave_track, IDirectMusicTrack8_iface); } -static inline IDirectMusicWaveTrack *impl_from_IPersistStream(IPersistStream *iface) +static inline struct wave_track *impl_from_IPersistStream(IPersistStream *iface) { - return CONTAINING_RECORD(iface, IDirectMusicWaveTrack, dmobj.IPersistStream_iface); + return CONTAINING_RECORD(iface, struct wave_track, dmobj.IPersistStream_iface); } static HRESULT WINAPI wave_track_QueryInterface(IDirectMusicTrack8 *iface, REFIID riid, void **ret_iface) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ret_iface); @@ -81,7 +78,7 @@ static HRESULT WINAPI wave_track_QueryInterface(IDirectMusicTrack8 *iface, REFII static ULONG WINAPI wave_track_AddRef(IDirectMusicTrack8 *iface) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); LONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) ref=%ld\n", This, ref); @@ -91,7 +88,7 @@ static ULONG WINAPI wave_track_AddRef(IDirectMusicTrack8 *iface) static ULONG WINAPI wave_track_Release(IDirectMusicTrack8 *iface) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); LONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) ref=%ld\n", This, ref); @@ -119,40 +116,42 @@ static ULONG WINAPI wave_track_Release(IDirectMusicTrack8 *iface) static HRESULT WINAPI wave_track_Init(IDirectMusicTrack8 *iface, IDirectMusicSegment *pSegment) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p): stub\n", This, pSegment); - return S_OK; + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %p): stub\n", This, pSegment); + return S_OK; } static HRESULT WINAPI wave_track_InitPlay(IDirectMusicTrack8 *iface, IDirectMusicSegmentState *pSegmentState, IDirectMusicPerformance *pPerformance, void **ppStateData, DWORD dwVirtualTrack8ID, DWORD dwFlags) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p, %p, %p, %ld, %ld): stub\n", This, pSegmentState, pPerformance, ppStateData, dwVirtualTrack8ID, dwFlags); - return S_OK; + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %p, %p, %p, %ld, %ld): stub\n", This, pSegmentState, pPerformance, ppStateData, + dwVirtualTrack8ID, dwFlags); + return S_OK; } static HRESULT WINAPI wave_track_EndPlay(IDirectMusicTrack8 *iface, void *pStateData) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p): stub\n", This, pStateData); - return S_OK; + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %p): stub\n", This, pStateData); + return S_OK; } static HRESULT WINAPI wave_track_Play(IDirectMusicTrack8 *iface, void *pStateData, MUSIC_TIME mtStart, MUSIC_TIME mtEnd, MUSIC_TIME mtOffset, DWORD dwFlags, IDirectMusicPerformance *pPerf, IDirectMusicSegmentState *pSegSt, DWORD dwVirtualID) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p, %ld, %ld, %ld, %ld, %p, %p, %ld): stub\n", This, pStateData, mtStart, mtEnd, mtOffset, dwFlags, pPerf, pSegSt, dwVirtualID); - return S_OK; + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %p, %ld, %ld, %ld, %ld, %p, %p, %ld): stub\n", This, pStateData, mtStart, mtEnd, + mtOffset, dwFlags, pPerf, pSegSt, dwVirtualID); + return S_OK; } static HRESULT WINAPI wave_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time, MUSIC_TIME *next, void *param) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s, %ld, %p, %p): not supported\n", This, debugstr_dmguid(type), time, next, param); return DMUS_E_GET_UNSUPPORTED; @@ -161,7 +160,7 @@ static HRESULT WINAPI wave_track_GetParam(IDirectMusicTrack8 *iface, REFGUID typ static HRESULT WINAPI wave_track_SetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time, void *param) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s, %ld, %p)\n", This, debugstr_dmguid(type), time, param); @@ -195,7 +194,7 @@ static HRESULT WINAPI wave_track_SetParam(IDirectMusicTrack8 *iface, REFGUID typ static HRESULT WINAPI wave_track_IsParamSupported(IDirectMusicTrack8 *iface, REFGUID type) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); static const GUID *valid[] = { &GUID_Disable_Auto_Download, &GUID_Download, @@ -218,7 +217,7 @@ static HRESULT WINAPI wave_track_IsParamSupported(IDirectMusicTrack8 *iface, REF static HRESULT WINAPI wave_track_AddNotificationType(IDirectMusicTrack8 *iface, REFGUID notiftype) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s): method not implemented\n", This, debugstr_dmguid(notiftype)); return E_NOTIMPL; @@ -227,7 +226,7 @@ static HRESULT WINAPI wave_track_AddNotificationType(IDirectMusicTrack8 *iface, static HRESULT WINAPI wave_track_RemoveNotificationType(IDirectMusicTrack8 *iface, REFGUID notiftype) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %s): method not implemented\n", This, debugstr_dmguid(notiftype)); return E_NOTIMPL; @@ -236,44 +235,45 @@ static HRESULT WINAPI wave_track_RemoveNotificationType(IDirectMusicTrack8 *ifac static HRESULT WINAPI wave_track_Clone(IDirectMusicTrack8 *iface, MUSIC_TIME mtStart, MUSIC_TIME mtEnd, IDirectMusicTrack **ppTrack) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %ld, %ld, %p): stub\n", This, mtStart, mtEnd, ppTrack); - return S_OK; + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %ld, %ld, %p): stub\n", This, mtStart, mtEnd, ppTrack); + return S_OK; } static HRESULT WINAPI wave_track_PlayEx(IDirectMusicTrack8 *iface, void *pStateData, REFERENCE_TIME rtStart, REFERENCE_TIME rtEnd, REFERENCE_TIME rtOffset, DWORD dwFlags, IDirectMusicPerformance *pPerf, IDirectMusicSegmentState *pSegSt, DWORD dwVirtualID) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p, 0x%s, 0x%s, 0x%s, %ld, %p, %p, %ld): stub\n", This, pStateData, wine_dbgstr_longlong(rtStart), - wine_dbgstr_longlong(rtEnd), wine_dbgstr_longlong(rtOffset), dwFlags, pPerf, pSegSt, dwVirtualID); - return S_OK; + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %p, 0x%s, 0x%s, 0x%s, %ld, %p, %p, %ld): stub\n", This, pStateData, + wine_dbgstr_longlong(rtStart), wine_dbgstr_longlong(rtEnd), + wine_dbgstr_longlong(rtOffset), dwFlags, pPerf, pSegSt, dwVirtualID); + return S_OK; } static HRESULT WINAPI wave_track_GetParamEx(IDirectMusicTrack8 *iface, REFGUID rguidType, REFERENCE_TIME rtTime, REFERENCE_TIME *prtNext, void *pParam, void *pStateData, DWORD dwFlags) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %s, 0x%s, %p, %p, %p, %ld): stub\n", This, debugstr_dmguid(rguidType), - wine_dbgstr_longlong(rtTime), prtNext, pParam, pStateData, dwFlags); - return S_OK; + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %s, 0x%s, %p, %p, %p, %ld): stub\n", This, debugstr_dmguid(rguidType), + wine_dbgstr_longlong(rtTime), prtNext, pParam, pStateData, dwFlags); + return S_OK; } static HRESULT WINAPI wave_track_SetParamEx(IDirectMusicTrack8 *iface, REFGUID rguidType, REFERENCE_TIME rtTime, void *pParam, void *pStateData, DWORD dwFlags) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %s, 0x%s, %p, %p, %ld): stub\n", This, debugstr_dmguid(rguidType), - wine_dbgstr_longlong(rtTime), pParam, pStateData, dwFlags); - return S_OK; + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); + FIXME("(%p, %s, 0x%s, %p, %p, %ld): stub\n", This, debugstr_dmguid(rguidType), + wine_dbgstr_longlong(rtTime), pParam, pStateData, dwFlags); + return S_OK; } static HRESULT WINAPI wave_track_Compose(IDirectMusicTrack8 *iface, IUnknown *context, DWORD trackgroup, IDirectMusicTrack **track) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %p, %ld, %p): method not implemented\n", This, context, trackgroup, track); return E_NOTIMPL; @@ -282,7 +282,7 @@ static HRESULT WINAPI wave_track_Compose(IDirectMusicTrack8 *iface, IUnknown *co static HRESULT WINAPI wave_track_Join(IDirectMusicTrack8 *iface, IDirectMusicTrack *newtrack, MUSIC_TIME join, IUnknown *context, DWORD trackgroup, IDirectMusicTrack **resulttrack) { - IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface); + struct wave_track *This = impl_from_IDirectMusicTrack8(iface); TRACE("(%p, %p, %ld, %p, %ld, %p): method not implemented\n", This, newtrack, join, context, trackgroup, resulttrack); return E_NOTIMPL; @@ -368,8 +368,7 @@ static HRESULT parse_wave_item(struct wave_part *part, IStream *stream, struct c return hr; } -static HRESULT parse_wave_part(IDirectMusicWaveTrack *This, IStream *stream, - struct chunk_entry *wavp) +static HRESULT parse_wave_part(struct wave_track *This, IStream *stream, struct chunk_entry *wavp) { struct chunk_entry chunk = {.parent = wavp}; struct wave_part *part; @@ -417,7 +416,7 @@ static HRESULT parse_wave_part(IDirectMusicWaveTrack *This, IStream *stream, static HRESULT WINAPI wave_IPersistStream_Load(IPersistStream *iface, IStream *stream) { - IDirectMusicWaveTrack *This = impl_from_IPersistStream(iface); + struct wave_track *This = impl_from_IPersistStream(iface); struct chunk_entry wavt = {0}; struct chunk_entry chunk = {.parent = &wavt}; HRESULT hr; @@ -469,7 +468,7 @@ static const IPersistStreamVtbl persiststream_vtbl = { /* for ClassFactory */ HRESULT create_dmwavetrack(REFIID lpcGUID, void **ppobj) { - IDirectMusicWaveTrack *track; + struct wave_track *track; HRESULT hr; *ppobj = NULL; From 6f69b52737f5bdb8b0096a145745f17a8031bb2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 10 Oct 2023 23:01:41 +0200 Subject: [PATCH 0993/1148] dmime: Include dmobject.h in dmime_private.h. (cherry picked from commit 6c1bf1f3a95fdf1398cc21eb163873d9e41108e3) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/audiopath.c | 1 - dlls/dmime/dmime_main.c | 1 - dlls/dmime/dmime_private.h | 2 ++ dlls/dmime/graph.c | 1 - dlls/dmime/lyricstrack.c | 1 - dlls/dmime/markertrack.c | 1 - dlls/dmime/paramcontroltrack.c | 1 - dlls/dmime/performance.c | 1 - dlls/dmime/segment.c | 1 - dlls/dmime/segmentstate.c | 1 - dlls/dmime/segtriggertrack.c | 1 - dlls/dmime/seqtrack.c | 1 - dlls/dmime/sysextrack.c | 1 - dlls/dmime/tempotrack.c | 1 - dlls/dmime/timesigtrack.c | 1 - dlls/dmime/wavetrack.c | 1 - 16 files changed, 2 insertions(+), 15 deletions(-) diff --git a/dlls/dmime/audiopath.c b/dlls/dmime/audiopath.c index aef48a5d2db..3310624eb00 100644 --- a/dlls/dmime/audiopath.c +++ b/dlls/dmime/audiopath.c @@ -18,7 +18,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/dmime_main.c b/dlls/dmime/dmime_main.c index c0aa4e31cfb..89e70bcd1bd 100644 --- a/dlls/dmime/dmime_main.c +++ b/dlls/dmime/dmime_main.c @@ -34,7 +34,6 @@ #include "dmusici.h" #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index e67797def30..94e0efbe771 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -42,6 +42,8 @@ #include "dmusics.h" #include "dmusicc.h" +#include "dmobject.h" + /***************************************************************************** * Interfaces */ diff --git a/dlls/dmime/graph.c b/dlls/dmime/graph.c index d8f6bb88701..e0aa94833ae 100644 --- a/dlls/dmime/graph.c +++ b/dlls/dmime/graph.c @@ -18,7 +18,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/lyricstrack.c b/dlls/dmime/lyricstrack.c index a1b299e9a7c..f7da1132867 100644 --- a/dlls/dmime/lyricstrack.c +++ b/dlls/dmime/lyricstrack.c @@ -18,7 +18,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/markertrack.c b/dlls/dmime/markertrack.c index 460e708bc5d..8d36a4cdd01 100644 --- a/dlls/dmime/markertrack.c +++ b/dlls/dmime/markertrack.c @@ -18,7 +18,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/paramcontroltrack.c b/dlls/dmime/paramcontroltrack.c index 9d32484bfd7..4abcf614c3a 100644 --- a/dlls/dmime/paramcontroltrack.c +++ b/dlls/dmime/paramcontroltrack.c @@ -18,7 +18,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 7d077db16cb..327f7d88c3b 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -20,7 +20,6 @@ #include "dmime_private.h" #include "wine/rbtree.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index af7729f34b9..1194f81b09a 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -19,7 +19,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index de47d85e9b8..1cb9daac69a 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -17,7 +17,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/segtriggertrack.c b/dlls/dmime/segtriggertrack.c index dc29ede4963..3f8e628b259 100644 --- a/dlls/dmime/segtriggertrack.c +++ b/dlls/dmime/segtriggertrack.c @@ -19,7 +19,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/seqtrack.c b/dlls/dmime/seqtrack.c index ae2f002da92..21b78ff26cd 100644 --- a/dlls/dmime/seqtrack.c +++ b/dlls/dmime/seqtrack.c @@ -17,7 +17,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/sysextrack.c b/dlls/dmime/sysextrack.c index f9c71abf08a..2c55d0dbe07 100644 --- a/dlls/dmime/sysextrack.c +++ b/dlls/dmime/sysextrack.c @@ -18,7 +18,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/tempotrack.c b/dlls/dmime/tempotrack.c index 6704448b71e..07f410118fe 100644 --- a/dlls/dmime/tempotrack.c +++ b/dlls/dmime/tempotrack.c @@ -19,7 +19,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); WINE_DECLARE_DEBUG_CHANNEL(dmfile); diff --git a/dlls/dmime/timesigtrack.c b/dlls/dmime/timesigtrack.c index e98807b7503..0ebf5e4256e 100644 --- a/dlls/dmime/timesigtrack.c +++ b/dlls/dmime/timesigtrack.c @@ -18,7 +18,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index 279cfb5c768..27e4d656163 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -17,7 +17,6 @@ */ #include "dmime_private.h" -#include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); From f70e220b3bfdb080540915fe9cc0e85398b2df39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 10 Oct 2023 22:59:32 +0200 Subject: [PATCH 0994/1148] dmusic: Split wave entry points to dmusic_wave.h. (cherry picked from commit 15c4c02eeba45d35c4c00dd5a2f8c16a2fe952e2) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/dmusic_private.h | 6 +----- dlls/dmusic/dmusic_wave.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 dlls/dmusic/dmusic_wave.h diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index f67bc5c4b2f..3efb69656c8 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -45,6 +45,7 @@ #include "dmksctrl.h" #include "dmobject.h" +#include "dmusic_wave.h" /***************************************************************************** * Interfaces @@ -94,7 +95,6 @@ extern HRESULT DMUSIC_CreateReferenceClockImpl (LPCGUID lpcGUID, LPVOID* ppobj, extern HRESULT download_create(DWORD size, IDirectMusicDownload **ret_iface); -struct soundfont; extern HRESULT instrument_create_from_soundfont(struct soundfont *soundfont, UINT index, struct collection *collection, DMUS_OBJECTDESC *desc, IDirectMusicInstrument **ret_iface); extern HRESULT instrument_create_from_chunk(IStream *stream, struct chunk_entry *parent, @@ -103,10 +103,6 @@ extern HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirec IDirectMusicDownloadedInstrument **downloaded); extern HRESULT instrument_unload_from_port(IDirectMusicDownloadedInstrument *iface, IDirectMusicPortDownload *port); -extern HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnknown **out); -extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **out); -extern HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id); - /***************************************************************************** * IDirectMusic8Impl implementation structure */ diff --git a/dlls/dmusic/dmusic_wave.h b/dlls/dmusic/dmusic_wave.h new file mode 100644 index 00000000000..0db0134ae17 --- /dev/null +++ b/dlls/dmusic/dmusic_wave.h @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Rémi Bernon for CodeWeavers + * + * This program 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 program 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 program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" + +#include "objbase.h" +#include "dmusici.h" + +struct soundfont; +struct chunk_entry; + +extern HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnknown **out); +extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **out); +extern HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id); From 934ce2212de842f652e97bacc5794bf30726edec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 10 Oct 2023 23:03:37 +0200 Subject: [PATCH 0995/1148] dmime: Create a wave track when loading a segment from a .wav. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=9027 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=34751 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45135 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48220 (cherry picked from commit 7bbd4be52a424e38495fa132885730bd4823a33e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/Makefile.in | 1 + dlls/dmime/dmime_private.h | 3 + dlls/dmime/segment.c | 111 ++++++++++++++----------------------- dlls/dmime/tests/dmime.c | 2 +- dlls/dmime/wavetrack.c | 43 ++++++++++++-- dlls/dmusic/dmusic_wave.h | 1 + dlls/dmusic/wave.c | 7 +++ 7 files changed, 94 insertions(+), 74 deletions(-) diff --git a/dlls/dmime/Makefile.in b/dlls/dmime/Makefile.in index ef4d0b9bee4..eccf7815475 100644 --- a/dlls/dmime/Makefile.in +++ b/dlls/dmime/Makefile.in @@ -18,6 +18,7 @@ C_SRCS = \ sysextrack.c \ tempotrack.c \ timesigtrack.c \ + wave.c \ wavetrack.c IDL_SRCS = dmime.idl diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 94e0efbe771..636004aed84 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -77,6 +77,9 @@ extern HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME sta extern HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance *performance); extern HRESULT segment_state_end_play(IDirectMusicSegmentState *iface); +extern HRESULT wave_track_create_from_chunk(IStream *stream, struct chunk_entry *parent, + IDirectMusicTrack8 **ret_iface); + /***************************************************************************** * Auxiliary definitions */ diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 1194f81b09a..fc277ba73f9 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -741,13 +741,17 @@ static inline void dump_segment_header(DMUS_IO_SEGMENT_HEADER *h, DWORD size) } } -static HRESULT parse_segment_form(struct segment *This, IStream *stream, const struct chunk_entry *riff) +static HRESULT parse_dmsg_chunk(struct segment *This, IStream *stream, const struct chunk_entry *riff) { struct chunk_entry chunk = {.parent = riff}; HRESULT hr; TRACE("Parsing segment form in %p: %s\n", stream, debugstr_chunk(riff)); + if (FAILED(hr = dmobj_parsedescriptor(stream, riff, &This->dmobj.desc, DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) + || FAILED(hr = stream_reset_chunk_data(stream, riff))) + return hr; + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { switch (chunk.id) { case DMUS_FOURCC_SEGMENT_CHUNK: @@ -786,89 +790,58 @@ static inline struct segment *impl_from_IPersistStream(IPersistStream *iface) return CONTAINING_RECORD(iface, struct segment, dmobj.IPersistStream_iface); } -static HRESULT parse_wave_form(struct segment *This, IStream *stream, const struct chunk_entry *riff) +static HRESULT WINAPI segment_persist_stream_Load(IPersistStream *iface, IStream *stream) { + struct segment *This = impl_from_IPersistStream(iface); + struct chunk_entry chunk = {0}; HRESULT hr; - struct chunk_entry chunk = {.parent = riff}; - TRACE("Parsing segment wave in %p: %s\n", stream, debugstr_chunk(riff)); + TRACE("(%p, %p): Loading\n", This, stream); - 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, - sizeof(This->wave_format))) ) - 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); - if (This->wave_data) - ERR("Multiple data streams detected\n"); - 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; - } - } - } + if (!stream) return E_POINTER; - return SUCCEEDED(hr) ? S_OK : hr; -} + if ((hr = stream_get_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_RIFF, DMUS_FOURCC_SEGMENT_FORM): + hr = parse_dmsg_chunk(This, stream, &chunk); + break; -static HRESULT WINAPI segment_persist_stream_Load(IPersistStream *iface, IStream *stream) -{ - struct segment *This = impl_from_IPersistStream(iface); - struct chunk_entry riff = {0}; - HRESULT hr; + case mmioFOURCC('M','T','h','d'): + FIXME("MIDI file loading not supported\n"); + break; - TRACE("(%p, %p): Loading\n", This, stream); + case MAKE_IDTYPE(FOURCC_RIFF, mmioFOURCC('W','A','V','E')): + { + IDirectMusicTrack8 *track; + HRESULT hr; - if (!stream) - return E_POINTER; + TRACE("Loading segment %p from wave file\n", This); - if (stream_get_chunk(stream, &riff) != S_OK || - (riff.id != FOURCC_RIFF && riff.id != mmioFOURCC('M','T','h','d'))) - return DMUS_E_UNSUPPORTED_STREAM; - stream_reset_chunk_start(stream, &riff); + This->header.mtLength = 1; + if (FAILED(hr = wave_track_create_from_chunk(stream, &chunk, &track))) break; + hr = segment_append_track(This, (IDirectMusicTrack *)track, 1, 0); + break; + } - if (riff.id == mmioFOURCC('M','T','h','d')) { - FIXME("MIDI file loading not supported\n"); - return S_OK; + default: + WARN("Invalid segment chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + hr = DMUS_E_UNSUPPORTED_STREAM; + break; + } } - hr = IDirectMusicObject_ParseDescriptor(&This->dmobj.IDirectMusicObject_iface, stream, - &This->dmobj.desc); if (FAILED(hr)) - return hr; - stream_reset_chunk_data(stream, &riff); - - if (riff.type == DMUS_FOURCC_SEGMENT_FORM) - hr = parse_segment_form(This, stream, &riff); - else if(riff.type == mmioFOURCC('W','A','V','E')) - hr = parse_wave_form(This, stream, &riff); - else { - FIXME("Unknown type %s\n", debugstr_chunk(&riff)); - hr = S_OK; + { + WARN("Failed to load segment from stream %p, hr %#lx\n", stream, hr); + return DMUS_E_UNSUPPORTED_STREAM; } - return hr; + This->dmobj.desc.guidClass = CLSID_DirectMusicSegment; + This->dmobj.desc.dwValidData |= DMUS_OBJ_CLASS; + + return S_OK; } static const IPersistStreamVtbl segment_persist_stream_vtbl = diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index e390f86f1bf..8c42398eadc 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3279,7 +3279,7 @@ static void test_wave_pmsg(void) length = 0xdeadbeef; hr = IDirectMusicSegment_GetLength(segment, &length); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(length == 1, "got %lu\n", length); + ok(length == 1, "got %lu\n", length); /* without Download, no DMUS_PMSGT_WAVE is sent */ diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index 27e4d656163..d9d6334adad 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -17,13 +17,14 @@ */ #include "dmime_private.h" +#include "dmusic_wave.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); struct wave_item { struct list entry; DMUS_IO_WAVE_ITEM_HEADER header; - IDirectMusicObject *object; + IUnknown *object; }; struct wave_part { @@ -100,8 +101,7 @@ static ULONG WINAPI wave_track_Release(IDirectMusicTrack8 *iface) list_remove(&part->entry); LIST_FOR_EACH_ENTRY_SAFE(item, item2, &part->items, struct wave_item, entry) { list_remove(&item->entry); - if (item->object) - IDirectMusicObject_Release(item->object); + if (item->object) IUnknown_Release(item->object); free(item); } free(part); @@ -355,7 +355,7 @@ static HRESULT parse_wave_item(struct wave_part *part, IStream *stream, struct c hr = DMUS_E_UNSUPPORTED_STREAM; goto error; } - if (FAILED(hr = dmobj_parsereference(stream, &chunk, &item->object))) + if (FAILED(hr = dmobj_parsereference(stream, &chunk, (IDirectMusicObject **)&item->object))) goto error; list_add_tail(&part->items, &item->entry); @@ -484,3 +484,38 @@ HRESULT create_dmwavetrack(REFIID lpcGUID, void **ppobj) return hr; } + +HRESULT wave_track_create_from_chunk(IStream *stream, struct chunk_entry *parent, + IDirectMusicTrack8 **ret_iface) +{ + IDirectMusicTrack8 *iface; + struct wave_track *This; + struct wave_item *item; + struct wave_part *part; + HRESULT hr; + + if (FAILED(hr = create_dmwavetrack(&IID_IDirectMusicTrack8, (void **)&iface))) return hr; + This = impl_from_IDirectMusicTrack8(iface); + + if (!(part = calloc(1, sizeof(*part)))) + { + IDirectMusicTrack8_Release(iface); + return E_OUTOFMEMORY; + } + list_init(&part->items); + list_add_tail(&This->parts, &part->entry); + + if (!(item = calloc(1, sizeof(*item))) + || FAILED(hr = wave_create_from_chunk(stream, parent, &item->object))) + { + IDirectMusicTrack8_Release(iface); + free(item); + return hr; + } + if (FAILED(hr = wave_get_duration(item->object, &item->header.rtDuration))) + WARN("Failed to get wave duration, hr %#lx\n", hr); + list_add_tail(&part->items, &item->entry); + + *ret_iface = iface; + return S_OK; +} diff --git a/dlls/dmusic/dmusic_wave.h b/dlls/dmusic/dmusic_wave.h index 0db0134ae17..3f7209e250e 100644 --- a/dlls/dmusic/dmusic_wave.h +++ b/dlls/dmusic/dmusic_wave.h @@ -32,3 +32,4 @@ struct chunk_entry; extern HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnknown **out); extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **out); extern HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id); +extern HRESULT wave_get_duration(IUnknown *iface, REFERENCE_TIME *duration); diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index 40a8c9e129f..2f5e1409106 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -338,3 +338,10 @@ HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, D IDirectMusicDownload_Release(download); return hr; } + +HRESULT wave_get_duration(IUnknown *iface, REFERENCE_TIME *duration) +{ + struct wave *This = impl_from_IUnknown(iface); + *duration = (REFERENCE_TIME)This->data_size * 10000000 / This->format->nAvgBytesPerSec; + return S_OK; +} From 2e83b39b7dcc38154256c3f00f395bd11d2a06a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Sep 2023 11:21:38 +0200 Subject: [PATCH 0996/1148] dmime: Implement GUID_(Download|Unload)FromAudioPath for wave track. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=9027 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=34751 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45135 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48220 (cherry picked from commit 4a7a8a7ecc8bc259da58470a7e4be64b9ba2cd8e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 2 ++ dlls/dmime/performance.c | 14 ++++++++ dlls/dmime/wavetrack.c | 70 +++++++++++++++++++++++++++++++++----- dlls/dmusic/dmusic_wave.h | 1 + dlls/dmusic/wave.c | 39 +++++++++++++++++++++ 5 files changed, 118 insertions(+), 8 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 636004aed84..c1f777f3949 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -80,6 +80,8 @@ extern HRESULT segment_state_end_play(IDirectMusicSegmentState *iface); extern HRESULT wave_track_create_from_chunk(IStream *stream, struct chunk_entry *parent, IDirectMusicTrack8 **ret_iface); +extern HRESULT performance_get_dsound(IDirectMusicPerformance8 *iface, IDirectSound **dsound); + /***************************************************************************** * Auxiliary definitions */ diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 327f7d88c3b..84b4b4b8575 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1731,3 +1731,17 @@ HRESULT create_dmperformance(REFIID iid, void **ret_iface) IDirectMusicPerformance_Release(&obj->IDirectMusicPerformance8_iface); return hr; } + +static inline struct performance *unsafe_impl_from_IDirectMusicPerformance8(IDirectMusicPerformance8 *iface) +{ + if (iface->lpVtbl != &performance_vtbl) return NULL; + return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface); +} + +HRESULT performance_get_dsound(IDirectMusicPerformance8 *iface, IDirectSound **dsound) +{ + struct performance *This = unsafe_impl_from_IDirectMusicPerformance8(iface); + if (!This || !(*dsound = This->dsound)) return E_FAIL; + IDirectSound_AddRef(*dsound); + return S_OK; +} diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index d9d6334adad..7c72065520a 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -25,6 +25,7 @@ struct wave_item { struct list entry; DMUS_IO_WAVE_ITEM_HEADER header; IUnknown *object; + IDirectSoundBuffer *buffer; }; struct wave_part { @@ -97,13 +98,18 @@ static ULONG WINAPI wave_track_Release(IDirectMusicTrack8 *iface) struct wave_item *item, *item2; struct wave_part *part, *part2; - LIST_FOR_EACH_ENTRY_SAFE(part, part2, &This->parts, struct wave_part, entry) { + LIST_FOR_EACH_ENTRY_SAFE(part, part2, &This->parts, struct wave_part, entry) + { list_remove(&part->entry); - LIST_FOR_EACH_ENTRY_SAFE(item, item2, &part->items, struct wave_item, entry) { + + LIST_FOR_EACH_ENTRY_SAFE(item, item2, &part->items, struct wave_item, entry) + { list_remove(&item->entry); + if (item->buffer) IDirectSoundBuffer_Release(item->buffer); if (item->object) IUnknown_Release(item->object); free(item); } + free(part); } @@ -171,9 +177,46 @@ static HRESULT WINAPI wave_track_SetParam(IDirectMusicTrack8 *iface, REFGUID typ FIXME("GUID_Download not handled yet\n"); return S_OK; } - if (IsEqualGUID(type, &GUID_DownloadToAudioPath)) { - FIXME("GUID_DownloadToAudioPath not handled yet\n"); - return S_OK; + if (IsEqualGUID(type, &GUID_DownloadToAudioPath)) + { + IDirectMusicPerformance8 *performance; + IDirectMusicAudioPath *audio_path; + IUnknown *object = param; + struct wave_part *part; + struct wave_item *item; + IDirectSound *dsound; + HRESULT hr; + + if (FAILED(hr = IDirectMusicAudioPath_QueryInterface(object, &IID_IDirectMusicPerformance8, (void **)&performance)) + && SUCCEEDED(hr = IDirectMusicAudioPath_QueryInterface(object, &IID_IDirectMusicAudioPath, (void **)&audio_path))) + { + hr = IDirectMusicAudioPath_GetObjectInPath(audio_path, DMUS_PCHANNEL_ALL, DMUS_PATH_PERFORMANCE, 0, + &GUID_All_Objects, 0, &IID_IDirectMusicPerformance8, (void **)&performance); + IDirectMusicAudioPath_Release(audio_path); + } + + if (SUCCEEDED(hr)) + hr = performance_get_dsound(performance, &dsound); + IDirectMusicPerformance_Release(performance); + + if (FAILED(hr)) + { + WARN("Failed to get direct sound from param %p, hr %#lx\n", param, hr); + return hr; + } + + LIST_FOR_EACH_ENTRY(part, &This->parts, struct wave_part, entry) + { + LIST_FOR_EACH_ENTRY(item, &part->items, struct wave_item, entry) + { + if (item->buffer) continue; + if (FAILED(hr = wave_download_to_dsound(item->object, dsound, &item->buffer))) + { + WARN("Failed to download wave %p to direct sound, hr %#lx\n", item->object, hr); + return hr; + } + } + } } if (IsEqualGUID(type, &GUID_Enable_Auto_Download)) { FIXME("GUID_Enable_Auto_Download not handled yet\n"); @@ -183,9 +226,20 @@ static HRESULT WINAPI wave_track_SetParam(IDirectMusicTrack8 *iface, REFGUID typ FIXME("GUID_Unload not handled yet\n"); return S_OK; } - if (IsEqualGUID(type, &GUID_UnloadFromAudioPath)) { - FIXME("GUID_UnloadFromAudioPath not handled yet\n"); - return S_OK; + if (IsEqualGUID(type, &GUID_UnloadFromAudioPath)) + { + struct wave_part *part; + struct wave_item *item; + + LIST_FOR_EACH_ENTRY(part, &This->parts, struct wave_part, entry) + { + LIST_FOR_EACH_ENTRY(item, &part->items, struct wave_item, entry) + { + if (!item->buffer) continue; + IDirectSoundBuffer_Release(item->buffer); + item->buffer = NULL; + } + } } return DMUS_E_TYPE_UNSUPPORTED; diff --git a/dlls/dmusic/dmusic_wave.h b/dlls/dmusic/dmusic_wave.h index 3f7209e250e..43396250261 100644 --- a/dlls/dmusic/dmusic_wave.h +++ b/dlls/dmusic/dmusic_wave.h @@ -32,4 +32,5 @@ struct chunk_entry; extern HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnknown **out); extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **out); extern HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id); +extern HRESULT wave_download_to_dsound(IUnknown *iface, IDirectSound *dsound, IDirectSoundBuffer **ret_iface); extern HRESULT wave_get_duration(IUnknown *iface, REFERENCE_TIME *duration); diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index 2f5e1409106..ef1cd2990ff 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -339,6 +339,45 @@ HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, D return hr; } +HRESULT wave_download_to_dsound(IUnknown *iface, IDirectSound *dsound, IDirectSoundBuffer **ret_iface) +{ + struct wave *This = impl_from_IUnknown(iface); + DSBUFFERDESC desc = + { + .dwSize = sizeof(desc), + .dwBufferBytes = This->data_size, + .lpwfxFormat = This->format, + }; + IDirectSoundBuffer *buffer; + HRESULT hr; + void *data; + DWORD size; + + TRACE("%p, %p, %p\n", This, dsound, ret_iface); + + if (FAILED(hr = IDirectSound_CreateSoundBuffer(dsound, &desc, &buffer, NULL))) + { + WARN("Failed to create direct sound buffer, hr %#lx\n", hr); + return hr; + } + + if (SUCCEEDED(hr = IDirectSoundBuffer_Lock(buffer, 0, This->data_size, &data, &size, NULL, 0, 0))) + { + memcpy(data, This->data, This->data_size); + hr = IDirectSoundBuffer_Unlock(buffer, data, This->data_size, NULL, 0); + } + + if (FAILED(hr)) + { + WARN("Failed to download wave to dsound, hr %#lx\n", hr); + IDirectSoundBuffer_Release(buffer); + return hr; + } + + *ret_iface = buffer; + return S_OK; +} + HRESULT wave_get_duration(IUnknown *iface, REFERENCE_TIME *duration) { struct wave *This = impl_from_IUnknown(iface); From 9783f295071931f6f4d340dad97a9dc4db45ad52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Sep 2023 11:28:51 +0200 Subject: [PATCH 0997/1148] dmime: Implement IDirectMusicTrack_Play for the wave track. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=9027 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=34751 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45135 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48220 (cherry picked from commit edad7809227642fadc187a00b2df5f3326c567c8) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 8 +---- dlls/dmime/wavetrack.c | 68 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 8c42398eadc..412cccce781 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3320,8 +3320,6 @@ static void test_wave_pmsg(void) ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&wave); ok(!ret, "got %#lx\n", ret); - if (wave->dwType == DMUS_PMSGT_WAVE) - { ok(wave->dwType == DMUS_PMSGT_WAVE, "got %p\n", wave); ok(!!wave->punkUser, "got %p\n", wave->punkUser); ok(wave->rtStartOffset == 0, "got %I64d\n", wave->rtStartOffset); @@ -3330,18 +3328,14 @@ static void test_wave_pmsg(void) ok(wave->lVolume == 0, "got %lu\n", wave->lVolume); ok(wave->lPitch == 0, "got %lu\n", wave->lPitch); ok(wave->bFlags == 0, "got %#x\n", wave->bFlags); - } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)wave); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %p\n", msg); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - } hr = IDirectMusicSegment8_Unload((IDirectMusicSegment8 *)segment, (IUnknown *)performance); ok(hr == S_OK, "got %#lx\n", hr); diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index 7c72065520a..def1e59fb23 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -143,14 +143,70 @@ static HRESULT WINAPI wave_track_EndPlay(IDirectMusicTrack8 *iface, void *pState return S_OK; } -static HRESULT WINAPI wave_track_Play(IDirectMusicTrack8 *iface, void *pStateData, - MUSIC_TIME mtStart, MUSIC_TIME mtEnd, MUSIC_TIME mtOffset, DWORD dwFlags, - IDirectMusicPerformance *pPerf, IDirectMusicSegmentState *pSegSt, DWORD dwVirtualID) +static HRESULT WINAPI wave_track_Play(IDirectMusicTrack8 *iface, void *state_data, + MUSIC_TIME start_time, MUSIC_TIME end_time, MUSIC_TIME time_offset, DWORD segment_flags, + IDirectMusicPerformance *performance, IDirectMusicSegmentState *segment_state, DWORD track_id) { struct wave_track *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p, %ld, %ld, %ld, %ld, %p, %p, %ld): stub\n", This, pStateData, mtStart, mtEnd, - mtOffset, dwFlags, pPerf, pSegSt, dwVirtualID); - return S_OK; + LONG volume = This->header.lVolume; + IDirectMusicGraph *graph; + struct wave_part *part; + struct wave_item *item; + HRESULT hr; + + TRACE("(%p, %p, %ld, %ld, %ld, %#lx, %p, %p, %ld)\n", This, state_data, start_time, end_time, + time_offset, segment_flags, performance, segment_state, track_id); + + if (start_time != 0) FIXME("start_time %ld not implemented\n", start_time); + if (end_time != -1) FIXME("end_time %ld not implemented\n", end_time); + if (time_offset != 0) FIXME("time_offset %ld not implemented\n", time_offset); + if (segment_flags) FIXME("segment_flags %#lx not implemented\n", segment_flags); + if (segment_state) FIXME("segment_state %p not implemented\n", segment_state); + + if (FAILED(hr = IDirectMusicPerformance_QueryInterface(performance, + &IID_IDirectMusicGraph, (void **)&graph))) + return hr; + + LIST_FOR_EACH_ENTRY(part, &This->parts, struct wave_part, entry) + { + volume += part->header.lVolume; + + LIST_FOR_EACH_ENTRY(item, &part->items, struct wave_item, entry) + { + DMUS_WAVE_PMSG *msg; + + if (!item->buffer) continue; + + if (FAILED(hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(*msg), + (DMUS_PMSG **)&msg))) + break; + + msg->mtTime = item->header.rtTime; + msg->dwFlags = DMUS_PMSGF_MUSICTIME; + msg->dwPChannel = part->header.dwPChannel; + msg->dwVirtualTrackID = track_id; + msg->dwType = DMUS_PMSGT_WAVE; + msg->punkUser = (IUnknown *)item->buffer; + IDirectSoundBuffer_AddRef(item->buffer); + + msg->rtStartOffset = item->header.rtStartOffset; + msg->rtDuration = item->header.rtDuration; + msg->lVolume = volume + item->header.lVolume; + msg->lPitch = item->header.lPitch; + + if (FAILED(hr = IDirectMusicGraph_StampPMsg(graph, (DMUS_PMSG *)msg)) + || FAILED(hr = IDirectMusicPerformance_SendPMsg(performance, (DMUS_PMSG *)msg))) + { + IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)msg); + break; + } + } + + volume -= part->header.lVolume; + } + + IDirectMusicGraph_Release(graph); + return hr; } static HRESULT WINAPI wave_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time, From 2b9fbcb3190e14a0bda12f99c3dd40509278ba78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 25 Sep 2023 11:30:42 +0200 Subject: [PATCH 0998/1148] dmime: Play direct sound buffer from DMUS_PMSGT_WAVE message. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=9027 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=34751 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45135 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48220 (cherry picked from commit 16f9bfd23f3ca245a73722e9ab0028bf7d1b311d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 84b4b4b8575..2018fa013de 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1631,6 +1631,7 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, { struct performance *This = impl_from_IDirectMusicTool(iface); struct message *message = message_from_DMUS_PMSG(msg); + HRESULT hr; FIXME("(%p, %p, %p): semi-stub\n", This, performance, msg); @@ -1641,7 +1642,6 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)msg; struct message *previous; BOOL enabled = FALSE; - HRESULT hr; if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_SEGMENT) && notif->dwNotificationOption == DMUS_NOTIFICATION_SEGEND) @@ -1671,6 +1671,11 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, return S_OK; } + case DMUS_PMSGT_WAVE: + if (FAILED(hr = IDirectSoundBuffer_Play((IDirectSoundBuffer *)msg->punkUser, 0, 0, 0))) + WARN("Failed to play wave buffer, hr %#lx\n", hr); + break; + default: FIXME("Unhandled message type %#lx\n", msg->dwType); break; From bd8cee88f8d3c0ed79a74b9e365ee283ebff8a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 23 Sep 2023 13:47:39 +0200 Subject: [PATCH 0999/1148] dmsynth: Fix synth download of articulations list. (cherry picked from commit a1e8352f54153a76b6e2759b58f2f3a15d1b9ca1) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 9bb1e884229..4640b871d93 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -445,7 +445,7 @@ static HRESULT synth_download_articulation2(struct synth *This, ULONG *offsets, { articulation_info = (DMUS_ARTICULATION2 *)(data + offsets[index]); list = (CONNECTIONLIST *)(data + offsets[articulation_info->ulArtIdx]); - connections = (CONNECTION *)list + 1; + connections = (CONNECTION *)(list + 1); if (TRACE_ON(dmsynth)) dump_connectionlist(list); if (articulation_info->ulFirstExtCkIdx) FIXME("Articulation extensions not implemented\n"); From 8cf80e80c5a21fb70063f9b71c082a91f84518a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 8 Sep 2023 09:49:06 +0200 Subject: [PATCH 1000/1148] dmsynth: Improve debug traces of DLS2 connections. (cherry picked from commit 456ba9925525162deba07869e6dc6b599667d244) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 91 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 8 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 4640b871d93..0c85e3cf6b3 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -28,11 +28,93 @@ #include "dmksctrl.h" #include "dmsynth_private.h" +#include "dls2.h" #include WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); +#define CONN_SRC_CC2 0x0082 +#define CONN_SRC_RPN0 0x0100 + +static const char *debugstr_conn_src(UINT src) +{ + switch (src) + { + case CONN_SRC_NONE: return "SRC_NONE"; + case CONN_SRC_LFO: return "SRC_LFO"; + case CONN_SRC_KEYONVELOCITY: return "SRC_KEYONVELOCITY"; + case CONN_SRC_KEYNUMBER: return "SRC_KEYNUMBER"; + case CONN_SRC_EG1: return "SRC_EG1"; + case CONN_SRC_EG2: return "SRC_EG2"; + case CONN_SRC_PITCHWHEEL: return "SRC_PITCHWHEEL"; + case CONN_SRC_CC1: return "SRC_CC1"; + case CONN_SRC_CC7: return "SRC_CC7"; + case CONN_SRC_CC10: return "SRC_CC10"; + case CONN_SRC_CC11: return "SRC_CC11"; + case CONN_SRC_POLYPRESSURE: return "SRC_POLYPRESSURE"; + case CONN_SRC_CHANNELPRESSURE: return "SRC_CHANNELPRESSURE"; + case CONN_SRC_VIBRATO: return "SRC_VIBRATO"; + case CONN_SRC_MONOPRESSURE: return "SRC_MONOPRESSURE"; + case CONN_SRC_CC91: return "SRC_CC91"; + case CONN_SRC_CC93: return "SRC_CC93"; + + case CONN_SRC_CC2: return "SRC_CC2"; + case CONN_SRC_RPN0: return "SRC_RPN0"; + } + + return wine_dbg_sprintf("%#x", src); +} + +static const char *debugstr_conn_dst(UINT dst) +{ + switch (dst) + { + case CONN_DST_NONE: return "DST_NONE"; + /* case CONN_DST_ATTENUATION: return "DST_ATTENUATION"; Same as CONN_DST_GAIN */ + case CONN_DST_PITCH: return "DST_PITCH"; + case CONN_DST_PAN: return "DST_PAN"; + case CONN_DST_LFO_FREQUENCY: return "DST_LFO_FREQUENCY"; + case CONN_DST_LFO_STARTDELAY: return "DST_LFO_STARTDELAY"; + case CONN_DST_EG1_ATTACKTIME: return "DST_EG1_ATTACKTIME"; + case CONN_DST_EG1_DECAYTIME: return "DST_EG1_DECAYTIME"; + case CONN_DST_EG1_RELEASETIME: return "DST_EG1_RELEASETIME"; + case CONN_DST_EG1_SUSTAINLEVEL: return "DST_EG1_SUSTAINLEVEL"; + case CONN_DST_EG2_ATTACKTIME: return "DST_EG2_ATTACKTIME"; + case CONN_DST_EG2_DECAYTIME: return "DST_EG2_DECAYTIME"; + case CONN_DST_EG2_RELEASETIME: return "DST_EG2_RELEASETIME"; + case CONN_DST_EG2_SUSTAINLEVEL: return "DST_EG2_SUSTAINLEVEL"; + case CONN_DST_GAIN: return "DST_GAIN"; + case CONN_DST_KEYNUMBER: return "DST_KEYNUMBER"; + case CONN_DST_LEFT: return "DST_LEFT"; + case CONN_DST_RIGHT: return "DST_RIGHT"; + case CONN_DST_CENTER: return "DST_CENTER"; + case CONN_DST_LEFTREAR: return "DST_LEFTREAR"; + case CONN_DST_RIGHTREAR: return "DST_RIGHTREAR"; + case CONN_DST_LFE_CHANNEL: return "DST_LFE_CHANNEL"; + case CONN_DST_CHORUS: return "DST_CHORUS"; + case CONN_DST_REVERB: return "DST_REVERB"; + case CONN_DST_VIB_FREQUENCY: return "DST_VIB_FREQUENCY"; + case CONN_DST_VIB_STARTDELAY: return "DST_VIB_STARTDELAY"; + case CONN_DST_EG1_DELAYTIME: return "DST_EG1_DELAYTIME"; + case CONN_DST_EG1_HOLDTIME: return "DST_EG1_HOLDTIME"; + case CONN_DST_EG1_SHUTDOWNTIME: return "DST_EG1_SHUTDOWNTIME"; + case CONN_DST_EG2_DELAYTIME: return "DST_EG2_DELAYTIME"; + case CONN_DST_EG2_HOLDTIME: return "DST_EG2_HOLDTIME"; + case CONN_DST_FILTER_CUTOFF: return "DST_FILTER_CUTOFF"; + case CONN_DST_FILTER_Q: return "DST_FILTER_Q"; + } + + return wine_dbg_sprintf("%#x", dst); +} + +static const char *debugstr_connection(const CONNECTION *conn) +{ + return wine_dbg_sprintf("%s (%#x) x %s (%#x) -> %s (%#x): %ld", debugstr_conn_src(conn->usSource), + (conn->usTransform >> 10) & 0x3f, debugstr_conn_src(conn->usControl), (conn->usTransform >> 4) & 0x3f, + debugstr_conn_dst(conn->usDestination), (conn->usTransform & 0xf), conn->lScale); +} + static void dump_dmus_instrument(DMUS_INSTRUMENT *instrument) { TRACE("DMUS_INSTRUMENT:\n"); @@ -88,14 +170,7 @@ static void dump_connectionlist(CONNECTIONLIST *list) TRACE(" - cConnections = %lu\n", list->cConnections); for (i = 0; i < list->cConnections; i++) - { - TRACE("- CONNECTION[%u]:\n", i); - TRACE(" - usSource = %u\n", connections[i].usSource); - TRACE(" - usControl = %u\n", connections[i].usControl); - TRACE(" - usDestination = %u\n", connections[i].usDestination); - TRACE(" - usTransform = %u\n", connections[i].usTransform); - TRACE(" - lScale = %lu\n", connections[i].lScale); - } + TRACE("- CONNECTION[%u]: %s\n", i, debugstr_connection(connections + i)); } static void dump_dmus_wave(DMUS_WAVE *wave) From 1170289e360f8be6a4f22a04e94d2c2a6522c25a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 11 Oct 2023 14:19:24 +0200 Subject: [PATCH 1001/1148] dmsynth: Guard synth members with a CS. (cherry picked from commit a4e933a3abebd1e4022d14f0aa1b812cc153897b) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 45 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 0c85e3cf6b3..52a2143fe73 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -306,6 +306,7 @@ struct synth BOOL open; IDirectMusicSynthSink *sink; + CRITICAL_SECTION cs; struct list instruments; struct list waves; @@ -387,6 +388,10 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) delete_fluid_sfont(This->fluid_sfont); This->fluid_sfont = NULL; delete_fluid_settings(This->fluid_settings); + + This->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&This->cs); + free(This); } @@ -414,11 +419,21 @@ static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *par TRACE("(%p, %p)\n", This, params); - if (This->open) return DMUS_E_ALREADYOPEN; + EnterCriticalSection(&This->cs); + if (This->open) + { + LeaveCriticalSection(&This->cs); + return DMUS_E_ALREADYOPEN; + } if (params) { - if (params->dwSize < sizeof(DMUS_PORTPARAMS7)) return E_INVALIDARG; + if (params->dwSize < sizeof(DMUS_PORTPARAMS7)) + { + LeaveCriticalSection(&This->cs); + return E_INVALIDARG; + } + if (size > params->dwSize) size = params->dwSize; if ((params->dwValidParams & DMUS_PORTPARAMS_VOICES) && params->dwVoices) @@ -476,6 +491,7 @@ static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *par This->params = actual; This->open = TRUE; + LeaveCriticalSection(&This->cs); return modified ? S_FALSE : S_OK; } @@ -486,13 +502,18 @@ static HRESULT WINAPI synth_Close(IDirectMusicSynth8 *iface) TRACE("(%p)\n", This); + EnterCriticalSection(&This->cs); if (!This->open) + { + LeaveCriticalSection(&This->cs); return DMUS_E_ALREADYCLOSED; + } fluid_synth_remove_sfont(This->fluid_synth, This->fluid_sfont); delete_fluid_synth(This->fluid_synth); This->fluid_synth = NULL; This->open = FALSE; + LeaveCriticalSection(&This->cs); return S_OK; } @@ -552,14 +573,17 @@ static struct wave *synth_find_wave_from_id(struct synth *This, DWORD id) { struct wave *wave; + EnterCriticalSection(&This->cs); LIST_FOR_EACH_ENTRY(wave, &This->waves, struct wave, entry) { if (wave->id == id) { wave_addref(wave); + LeaveCriticalSection(&This->cs); return wave; } } + LeaveCriticalSection(&This->cs); WARN("Failed to find wave with id %#lx\n", id); return NULL; @@ -630,9 +654,11 @@ static HRESULT synth_download_instrument(struct synth *This, DMUS_DOWNLOADINFO * instrument_info->ulGlobalArtIdx, &instrument->articulations))) goto error; + EnterCriticalSection(&This->cs); list_add_tail(&This->instruments, &instrument->entry); - *ret_handle = instrument; + LeaveCriticalSection(&This->cs); + *ret_handle = instrument; return S_OK; error: @@ -696,9 +722,11 @@ static HRESULT synth_download_wave(struct synth *This, DMUS_DOWNLOADINFO *info, } } + EnterCriticalSection(&This->cs); list_add_tail(&This->waves, &wave->entry); - *ret_handle = wave; + LeaveCriticalSection(&This->cs); + *ret_handle = wave; return S_OK; } @@ -760,11 +788,14 @@ static HRESULT WINAPI synth_Unload(IDirectMusicSynth8 *iface, HANDLE handle, TRACE("(%p)->(%p, %p, %p)\n", This, handle, callback, user_data); if (callback) FIXME("Unload callbacks not implemented\n"); + EnterCriticalSection(&This->cs); LIST_FOR_EACH_ENTRY(instrument, &This->instruments, struct instrument, entry) { if (instrument == handle) { list_remove(&instrument->entry); + LeaveCriticalSection(&This->cs); + instrument_release(instrument); return S_OK; } @@ -775,10 +806,13 @@ static HRESULT WINAPI synth_Unload(IDirectMusicSynth8 *iface, HANDLE handle, if (wave == handle) { list_remove(&wave->entry); + LeaveCriticalSection(&This->cs); + wave_release(wave); return S_OK; } } + LeaveCriticalSection(&This->cs); return E_FAIL; } @@ -1220,6 +1254,9 @@ HRESULT synth_create(IUnknown **ret_iface) goto failed; fluid_sfont_set_data(obj->fluid_sfont, obj); + InitializeCriticalSection(&obj->cs); + obj->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + TRACE("Created DirectMusicSynth %p\n", obj); *ret_iface = (IUnknown *)&obj->IDirectMusicSynth8_iface; return S_OK; From 9ad91bc283275902c9e9850154d33e01d1f5c64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 11:40:15 +0200 Subject: [PATCH 1002/1148] dmsynth: Parse MIDI events in IDirectMusicSynth_PlayBuffer. (cherry picked from commit d3b5c6bb29e17ab638842be096a6c7b5be93a938) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 56 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 52a2143fe73..d830b2619b8 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -34,6 +34,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); +#define ROUND_ADDR(addr, mask) ((void *)((UINT_PTR)(addr) & ~(UINT_PTR)(mask))) + #define CONN_SRC_CC2 0x0082 #define CONN_SRC_RPN0 0x0100 @@ -294,6 +296,13 @@ static void instrument_release(struct instrument *instrument) } } +struct event +{ + struct list entry; + LONGLONG position; + BYTE midi[3]; +}; + struct synth { IDirectMusicSynth8 IDirectMusicSynth8_iface; @@ -309,6 +318,7 @@ struct synth CRITICAL_SECTION cs; struct list instruments; struct list waves; + struct list events; fluid_settings_t *fluid_settings; fluid_sfont_t *fluid_sfont; @@ -369,6 +379,7 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) if (!ref) { struct instrument *instrument; + struct event *event; struct wave *wave; void *next; @@ -384,6 +395,12 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) wave_release(wave); } + LIST_FOR_EACH_ENTRY_SAFE(event, next, &This->events, struct event, entry) + { + list_remove(&event->entry); + free(event); + } + fluid_sfont_set_data(This->fluid_sfont, NULL); delete_fluid_sfont(This->fluid_sfont); This->fluid_sfont = NULL; @@ -818,11 +835,45 @@ static HRESULT WINAPI synth_Unload(IDirectMusicSynth8 *iface, HANDLE handle, } static HRESULT WINAPI synth_PlayBuffer(IDirectMusicSynth8 *iface, - REFERENCE_TIME rt, BYTE *buffer, DWORD size) + REFERENCE_TIME time, BYTE *buffer, DWORD size) { struct synth *This = impl_from_IDirectMusicSynth8(iface); + DMUS_EVENTHEADER *head = (DMUS_EVENTHEADER *)buffer; + BYTE *end = buffer + size, *data; + HRESULT hr; + + TRACE("(%p, %I64d, %p, %lu)\n", This, time, buffer, size); + + while ((data = (BYTE *)(head + 1)) < end) + { + DMUS_EVENTHEADER *next = ROUND_ADDR(data + head->cbEvent + 7, 7); + struct event *event, *next_event; + LONGLONG position; + + if ((BYTE *)next > end) return E_INVALIDARG; + if (FAILED(hr = IDirectMusicSynthSink_RefTimeToSample(This->sink, + time + head->rtDelta, &position))) + return hr; + + if (!(head->dwFlags & DMUS_EVENT_STRUCTURED)) + FIXME("Unstructured events not implemeted\n"); + else if (head->cbEvent > 3) + FIXME("Unexpected MIDI event size %lu\n", head->cbEvent); + else + { + if (!(event = calloc(1, sizeof(*event)))) return E_OUTOFMEMORY; + memcpy(event->midi, data, head->cbEvent); + event->position = position; + + EnterCriticalSection(&This->cs); + LIST_FOR_EACH_ENTRY(next_event, &This->events, struct event, entry) + if (next_event->position >= event->position) break; + list_add_before(&next_event->entry, &event->entry); + LeaveCriticalSection(&This->cs); + } - FIXME("(%p, 0x%s, %p, %lu): stub\n", This, wine_dbgstr_longlong(rt), buffer, size); + head = next; + } return S_OK; } @@ -1247,6 +1298,7 @@ HRESULT synth_create(IUnknown **ret_iface) list_init(&obj->instruments); list_init(&obj->waves); + list_init(&obj->events); if (!(obj->fluid_settings = new_fluid_settings())) goto failed; if (!(obj->fluid_sfont = new_fluid_sfont(synth_sfont_get_name, synth_sfont_get_preset, From f31ba9870e74f1a532eec70c00c31ab37223bb59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 11:40:15 +0200 Subject: [PATCH 1003/1148] dmsynth: Play some MIDI events in IDirectMusicSynth_Render. (cherry picked from commit 07aa6b5dcbfc29d95c3ba0f37249c9432f216c26) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 45 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index d830b2619b8..6516d3ceeeb 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -982,9 +982,52 @@ static HRESULT WINAPI synth_Render(IDirectMusicSynth8 *iface, short *buffer, DWORD length, LONGLONG position) { struct synth *This = impl_from_IDirectMusicSynth8(iface); + struct event *event, *next; - FIXME("(%p, %p, %ld, 0x%s): stub\n", This, buffer, length, wine_dbgstr_longlong(position)); + TRACE("(%p, %p, %ld, %I64d)\n", This, buffer, length, position); + EnterCriticalSection(&This->cs); + LIST_FOR_EACH_ENTRY_SAFE(event, next, &This->events, struct event, entry) + { + BYTE status = event->midi[0] & 0xf0, chan = event->midi[0] & 0x0f; + LONGLONG offset = event->position - position; + + if (offset >= length) break; + if (offset > 0) + { + fluid_synth_write_s16(This->fluid_synth, offset, buffer, 0, 2, buffer, 1, 2); + buffer += offset * 2; + position += offset; + length -= offset; + } + + TRACE("status %#x chan %#x midi %#x %#x\n", status, chan, event->midi[1], event->midi[2]); + + switch (status) + { + case 0x80: + fluid_synth_noteoff(This->fluid_synth, chan, event->midi[1]); + break; + case 0x90: + fluid_synth_noteon(This->fluid_synth, chan, event->midi[1], event->midi[2]); + break; + case 0xb0: + fluid_synth_cc(This->fluid_synth, chan, event->midi[1], event->midi[2]); + break; + case 0xc0: + fluid_synth_program_change(This->fluid_synth, chan, event->midi[1]); + break; + default: + FIXME("MIDI event not implemented: %#x %#x %#x\n", event->midi[0], event->midi[1], event->midi[2]); + break; + } + + list_remove(&event->entry); + free(event); + } + LeaveCriticalSection(&This->cs); + + if (length) fluid_synth_write_s16(This->fluid_synth, length, buffer, 0, 2, buffer, 1, 2); return S_OK; } From ca279ff3479f33b278d2f8d1171fb554b6525bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 8 Sep 2023 09:49:06 +0200 Subject: [PATCH 1004/1148] dmsynth: Create fluid_preset and fluid_voice from instrument. (cherry picked from commit ccc5af8bf893c81a624a4d6f78a3b8e2ac5e7bbf) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 302 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 299 insertions(+), 3 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 6516d3ceeeb..af578610c16 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -39,6 +39,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); #define CONN_SRC_CC2 0x0082 #define CONN_SRC_RPN0 0x0100 +#define CONN_TRN_BIPOLAR (1<<4) +#define CONN_TRN_INVERT (1<<5) + static const char *debugstr_conn_src(UINT src) { switch (src) @@ -270,6 +273,11 @@ struct instrument struct synth *synth; }; +static void instrument_addref(struct instrument *instrument) +{ + InterlockedIncrement(&instrument->ref); +} + static void instrument_release(struct instrument *instrument) { ULONG ref = InterlockedDecrement(&instrument->ref); @@ -1277,6 +1285,277 @@ static const IKsControlVtbl synth_control_vtbl = synth_control_KsEvent, }; +static const char *synth_preset_get_name(fluid_preset_t *fluid_preset) +{ + return "DirectMusicSynth"; +} + +static int synth_preset_get_bank(fluid_preset_t *fluid_preset) +{ + TRACE("(%p)\n", fluid_preset); + return 0; +} + +static int synth_preset_get_num(fluid_preset_t *fluid_preset) +{ + struct instrument *instrument = fluid_preset_get_data(fluid_preset); + + TRACE("(%p)\n", fluid_preset); + + if (!instrument) return 0; + return instrument->patch; +} + +static BOOL fluid_gen_from_connection(CONNECTION *conn, UINT *gen) +{ + switch (conn->usDestination) + { + case CONN_DST_FILTER_CUTOFF: *gen = GEN_FILTERFC; return TRUE; + case CONN_DST_FILTER_Q: *gen = GEN_FILTERQ; return TRUE; + case CONN_DST_CHORUS: *gen = GEN_CHORUSSEND; return TRUE; + case CONN_DST_REVERB: *gen = GEN_REVERBSEND; return TRUE; + case CONN_DST_PAN: *gen = GEN_PAN; return TRUE; + case CONN_DST_LFO_STARTDELAY: *gen = GEN_MODLFODELAY; return TRUE; + case CONN_DST_LFO_FREQUENCY: *gen = GEN_MODLFOFREQ; return TRUE; + case CONN_DST_VIB_STARTDELAY: *gen = GEN_VIBLFODELAY; return TRUE; + case CONN_DST_VIB_FREQUENCY: *gen = GEN_VIBLFOFREQ; return TRUE; + case CONN_DST_EG2_DELAYTIME: *gen = GEN_MODENVDELAY; return TRUE; + case CONN_DST_EG2_ATTACKTIME: *gen = GEN_MODENVATTACK; return TRUE; + case CONN_DST_EG2_HOLDTIME: *gen = GEN_MODENVHOLD; return TRUE; + case CONN_DST_EG2_DECAYTIME: *gen = GEN_MODENVDECAY; return TRUE; + case CONN_DST_EG2_SUSTAINLEVEL: *gen = GEN_MODENVSUSTAIN; return TRUE; + case CONN_DST_EG2_RELEASETIME: *gen = GEN_MODENVRELEASE; return TRUE; + case CONN_DST_EG1_DELAYTIME: *gen = GEN_VOLENVDELAY; return TRUE; + case CONN_DST_EG1_ATTACKTIME: *gen = GEN_VOLENVATTACK; return TRUE; + case CONN_DST_EG1_HOLDTIME: *gen = GEN_VOLENVHOLD; return TRUE; + case CONN_DST_EG1_DECAYTIME: *gen = GEN_VOLENVDECAY; return TRUE; + case CONN_DST_EG1_SUSTAINLEVEL: *gen = GEN_VOLENVSUSTAIN; return TRUE; + case CONN_DST_EG1_RELEASETIME: *gen = GEN_VOLENVRELEASE; return TRUE; + case CONN_DST_GAIN: *gen = GEN_ATTENUATION; return TRUE; + case CONN_DST_PITCH: *gen = GEN_PITCH; return TRUE; + default: FIXME("Unsupported connection %s\n", debugstr_connection(conn)); return FALSE; + } +} + +static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn) +{ + UINT gen; + + if (conn->usControl != CONN_SRC_NONE) return FALSE; + if (conn->usTransform != CONN_TRN_NONE) return FALSE; + + if (conn->usSource == CONN_SRC_NONE) + { + if (!fluid_gen_from_connection(conn, &gen)) return FALSE; + } + if (conn->usSource == CONN_SRC_KEYNUMBER) + { + switch (conn->usDestination) + { + case CONN_DST_EG2_HOLDTIME: gen = GEN_KEYTOMODENVHOLD; break; + case CONN_DST_EG2_DECAYTIME: gen = GEN_KEYTOMODENVDECAY; break; + case CONN_DST_EG1_HOLDTIME: gen = GEN_KEYTOVOLENVHOLD; break; + case CONN_DST_EG1_DECAYTIME: gen = GEN_KEYTOVOLENVDECAY; break; + default: return FALSE; + } + } + else if (conn->usSource == CONN_SRC_LFO) + { + switch (conn->usDestination) + { + case CONN_DST_PITCH: gen = GEN_MODLFOTOPITCH; break; + case CONN_DST_FILTER_CUTOFF: gen = GEN_MODLFOTOFILTERFC; break; + case CONN_DST_GAIN: gen = GEN_MODLFOTOVOL; break; + default: return FALSE; + } + } + else if (conn->usSource == CONN_SRC_EG2) + { + switch (conn->usDestination) + { + case CONN_DST_PITCH: gen = GEN_MODENVTOPITCH; break; + case CONN_DST_FILTER_CUTOFF: gen = GEN_MODENVTOFILTERFC; break; + default: return FALSE; + } + } + else if (conn->usSource == CONN_SRC_VIBRATO) + { + switch (conn->usDestination) + { + case CONN_DST_PITCH: gen = GEN_VIBLFOTOPITCH; break; + default: return FALSE; + } + } + else + { + return FALSE; + } + + fluid_voice_gen_set(fluid_voice, gen, conn->lScale); + return TRUE; +} + +static BOOL fluid_source_from_connection(USHORT source, USHORT transform, UINT *fluid_source, UINT *fluid_flags) +{ + UINT flags = FLUID_MOD_GC; + if (source >= CONN_SRC_CC1 && source <= CONN_SRC_CC1 + 0x7f) + { + *fluid_source = source; + flags = FLUID_MOD_CC; + } + else switch (source) + { + case CONN_SRC_NONE: *fluid_source = FLUID_MOD_NONE; break; + case CONN_SRC_KEYONVELOCITY: *fluid_source = FLUID_MOD_VELOCITY; break; + case CONN_SRC_KEYNUMBER: *fluid_source = FLUID_MOD_KEY; break; + case CONN_SRC_PITCHWHEEL: *fluid_source = FLUID_MOD_PITCHWHEEL; break; + case CONN_SRC_POLYPRESSURE: *fluid_source = FLUID_MOD_KEYPRESSURE; break; + case CONN_SRC_CHANNELPRESSURE: *fluid_source = FLUID_MOD_CHANNELPRESSURE; break; + case CONN_SRC_RPN0: *fluid_source = FLUID_MOD_PITCHWHEELSENS; break; + default: return FALSE; + } + + if (transform & CONN_TRN_INVERT) flags |= FLUID_MOD_NEGATIVE; + if (transform & CONN_TRN_BIPOLAR) flags |= FLUID_MOD_BIPOLAR; + switch (transform & CONN_TRN_SWITCH) + { + case CONN_TRN_NONE: flags |= FLUID_MOD_LINEAR; break; + case CONN_TRN_CONCAVE: flags |= FLUID_MOD_CONCAVE; break; + case CONN_TRN_CONVEX: flags |= FLUID_MOD_CONVEX; break; + case CONN_TRN_SWITCH: flags |= FLUID_MOD_SWITCH; break; + } + + *fluid_flags = flags; + return TRUE; +} + +static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn, UINT src1, UINT flags1, + UINT src2, UINT flags2) +{ + fluid_mod_t *mod; + UINT gen = -1; + + switch (MAKELONG(conn->usSource, conn->usDestination)) + { + case MAKELONG(CONN_SRC_LFO, CONN_DST_PITCH): gen = GEN_MODLFOTOPITCH; break; + case MAKELONG(CONN_SRC_VIBRATO, CONN_DST_PITCH): gen = GEN_VIBLFOTOPITCH; break; + case MAKELONG(CONN_SRC_EG2, CONN_DST_PITCH): gen = GEN_MODENVTOPITCH; break; + case MAKELONG(CONN_SRC_LFO, CONN_DST_FILTER_CUTOFF): gen = GEN_MODLFOTOFILTERFC; break; + case MAKELONG(CONN_SRC_EG2, CONN_DST_FILTER_CUTOFF): gen = GEN_MODENVTOFILTERFC; break; + case MAKELONG(CONN_SRC_LFO, CONN_DST_GAIN): gen = GEN_MODLFOTOVOL; break; + case MAKELONG(CONN_SRC_KEYNUMBER, CONN_DST_EG2_HOLDTIME): gen = GEN_KEYTOMODENVHOLD; break; + case MAKELONG(CONN_SRC_KEYNUMBER, CONN_DST_EG2_DECAYTIME): gen = GEN_KEYTOMODENVDECAY; break; + case MAKELONG(CONN_SRC_KEYNUMBER, CONN_DST_EG1_HOLDTIME): gen = GEN_KEYTOVOLENVHOLD; break; + case MAKELONG(CONN_SRC_KEYNUMBER, CONN_DST_EG1_DECAYTIME): gen = GEN_KEYTOVOLENVDECAY; break; + } + + if (conn->usControl != CONN_SRC_NONE && gen != -1) + { + src1 = src2; + flags1 = flags2; + src2 = 0; + flags2 = 0; + } + + if (gen == -1 && !fluid_gen_from_connection(conn, &gen)) return FALSE; + + if (!(mod = new_fluid_mod())) return FALSE; + fluid_mod_set_source1(mod, src1, flags1); + fluid_mod_set_source2(mod, src2, flags2); + fluid_mod_set_dest(mod, gen); + fluid_mod_set_amount(mod, conn->lScale); + fluid_voice_add_mod(fluid_voice, mod, FLUID_VOICE_OVERWRITE); + + return TRUE; +} + +static void fluid_voice_add_articulation(fluid_voice_t *fluid_voice, struct articulation *articulation) +{ + UINT i; + + for (i = 0; i < articulation->list.cConnections; i++) + { + UINT src1 = FLUID_MOD_NONE, flags1 = 0, src2 = FLUID_MOD_NONE, flags2 = 0; + CONNECTION *conn = articulation->connections + i; + + if (set_gen_from_connection(fluid_voice, conn)) continue; + + if (!fluid_source_from_connection(conn->usSource, (conn->usTransform >> 10) & 0x3f, + &src1, &flags1)) + continue; + if (!fluid_source_from_connection(conn->usControl, (conn->usControl >> 4) & 0x3f, + &src2, &flags2)) + continue; + add_mod_from_connection(fluid_voice, conn, src1, flags1, src2, flags2); + } +} + +static void fluid_voice_add_articulations(fluid_voice_t *fluid_voice, struct list *list) +{ + struct articulation *articulation; + + LIST_FOR_EACH_ENTRY(articulation, list, struct articulation, entry) + fluid_voice_add_articulation(fluid_voice, articulation); +} + +static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *fluid_synth, int chan, int key, int vel) +{ + struct instrument *instrument = fluid_preset_get_data(fluid_preset); + struct synth *synth = instrument->synth; + fluid_sample_t *fluid_sample; + fluid_voice_t *fluid_voice; + struct region *region; + + TRACE("(%p, %p, %u, %u, %u)\n", fluid_preset, fluid_synth, chan, key, vel); + + if (!instrument) return FLUID_FAILED; + + LIST_FOR_EACH_ENTRY(region, &instrument->regions, struct region, entry) + { + struct wave *wave = region->wave; + + if (key < region->key_range.usLow || key > region->key_range.usHigh) continue; + if (vel < region->vel_range.usLow || vel > region->vel_range.usHigh) continue; + + if (!(fluid_sample = new_fluid_sample())) + { + WARN("Failed to allocate FluidSynth sample\n"); + return FLUID_FAILED; + } + + fluid_sample_set_sound_data(fluid_sample, wave->samples, NULL, wave->sample_count, + wave->format.nSamplesPerSec, TRUE); + if (region->wave_sample.cSampleLoops) + { + WLOOP *loop = region->wave_loops; + fluid_sample_set_loop(fluid_sample, loop->ulStart, loop->ulStart + loop->ulLength); + } + fluid_sample_set_pitch(fluid_sample, region->wave_sample.usUnityNote, region->wave_sample.sFineTune); + + if (!(fluid_voice = fluid_synth_alloc_voice(synth->fluid_synth, fluid_sample, chan, key, vel))) + { + WARN("Failed to allocate FluidSynth voice\n"); + delete_fluid_sample(fluid_sample); + return FLUID_FAILED; + } + + fluid_voice_add_articulations(fluid_voice, &instrument->articulations); + fluid_voice_add_articulations(fluid_voice, ®ion->articulations); + fluid_synth_start_voice(synth->fluid_synth, fluid_voice); + return FLUID_OK; + } + + WARN("Failed to find instrument matching note / velocity\n"); + return FLUID_FAILED; +} + +static void synth_preset_free(fluid_preset_t *fluid_preset) +{ + struct instrument *instrument = fluid_preset_get_data(fluid_preset); + fluid_preset_set_data(fluid_preset, NULL); + if (instrument) instrument_release(instrument); +} + static const char *synth_sfont_get_name(fluid_sfont_t *fluid_sfont) { return "DirectMusicSynth"; @@ -1286,17 +1565,34 @@ static fluid_preset_t *synth_sfont_get_preset(fluid_sfont_t *fluid_sfont, int ba { struct synth *synth = fluid_sfont_get_data(fluid_sfont); struct instrument *instrument; + fluid_preset_t *fluid_preset; TRACE("(%p, %d, %d)\n", fluid_sfont, bank, patch); if (!synth) return NULL; + EnterCriticalSection(&synth->cs); + LIST_FOR_EACH_ENTRY(instrument, &synth->instruments, struct instrument, entry) if (instrument->patch == patch) break; - if (&instrument->entry == &synth->instruments) return NULL; - FIXME("Preset not implemented yet\n"); - return NULL; + if (&instrument->entry == &synth->instruments) + { + fluid_preset = NULL; + WARN("Could not find instrument with patch %#x\n", patch); + } + else if ((fluid_preset = new_fluid_preset(fluid_sfont, synth_preset_get_name, synth_preset_get_bank, + synth_preset_get_num, synth_preset_noteon, synth_preset_free))) + { + fluid_preset_set_data(fluid_preset, instrument); + instrument_addref(instrument); + + TRACE("Created fluid_preset %p for instrument %p\n", fluid_preset, instrument); + } + + LeaveCriticalSection(&synth->cs); + + return fluid_preset; } static void synth_sfont_iter_start(fluid_sfont_t *fluid_sfont) From f16356faf43f7de40af5f3d7669d21a873736bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 10 Sep 2023 14:41:51 +0200 Subject: [PATCH 1005/1148] dmband: Download / unload bands when initializing / ending band track. (cherry picked from commit b0573f9dc3aab086069a16b97dc1d04ae2709cb9) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/bandtrack.c | 39 +++++++++++++++++++++++++++++++++----- dlls/dmband/tests/dmband.c | 2 -- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index 845345a9fa2..6233d39b9c0 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -104,10 +104,13 @@ static ULONG WINAPI band_track_Release(IDirectMusicTrack8 *iface) return ref; } -static HRESULT WINAPI band_track_Init(IDirectMusicTrack8 *iface, IDirectMusicSegment *pSegment) +static HRESULT WINAPI band_track_Init(IDirectMusicTrack8 *iface, IDirectMusicSegment *segment) { struct band_track *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p): stub\n", This, pSegment); + + FIXME("(%p, %p): stub\n", This, segment); + + if (!segment) return E_POINTER; return S_OK; } @@ -116,16 +119,42 @@ static HRESULT WINAPI band_track_InitPlay(IDirectMusicTrack8 *iface, void **state_data, DWORD virtual_track8id, DWORD flags) { struct band_track *This = impl_from_IDirectMusicTrack8(iface); + struct band_entry *entry; + HRESULT hr; + + FIXME("(%p, %p, %p, %p, %ld, %lx): semi-stub\n", This, segment_state, performance, state_data, virtual_track8id, flags); + + if (!performance) return E_POINTER; - FIXME("(%p, %p, %p, %p, %ld, %lx): stub\n", This, segment_state, performance, state_data, virtual_track8id, flags); + if (This->header.bAutoDownload) + { + LIST_FOR_EACH_ENTRY(entry, &This->bands, struct band_entry, entry) + { + if (FAILED(hr = IDirectMusicBand_Download(entry->band, performance))) + return hr; + } + } return S_OK; } -static HRESULT WINAPI band_track_EndPlay(IDirectMusicTrack8 *iface, void *pStateData) +static HRESULT WINAPI band_track_EndPlay(IDirectMusicTrack8 *iface, void *state_data) { struct band_track *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p): stub\n", This, pStateData); + struct band_entry *entry; + HRESULT hr; + + FIXME("(%p, %p): semi-stub\n", This, state_data); + + if (This->header.bAutoDownload) + { + LIST_FOR_EACH_ENTRY(entry, &This->bands, struct band_entry, entry) + { + if (FAILED(hr = IDirectMusicBand_Unload(entry->band, NULL))) + return hr; + } + } + return S_OK; } diff --git a/dlls/dmband/tests/dmband.c b/dlls/dmband/tests/dmband.c index 07ffcf4e7fe..ee45ca33714 100644 --- a/dlls/dmband/tests/dmband.c +++ b/dlls/dmband/tests/dmband.c @@ -226,12 +226,10 @@ static void test_bandtrack(void) ok(hr == S_OK, "DirectMusicBandTrack create failed: %#lx, expected S_OK\n", hr); /* IDirectMusicTrack8 */ - todo_wine { hr = IDirectMusicTrack8_Init(dmt8, NULL); ok(hr == E_POINTER, "IDirectMusicTrack8_Init failed: %#lx\n", hr); hr = IDirectMusicTrack8_InitPlay(dmt8, NULL, NULL, NULL, 0, 0); ok(hr == E_POINTER, "IDirectMusicTrack8_InitPlay failed: %#lx\n", hr); - } hr = IDirectMusicTrack8_EndPlay(dmt8, NULL); ok(hr == S_OK, "IDirectMusicTrack8_EndPlay failed: %#lx\n", hr); hr = IDirectMusicTrack8_Play(dmt8, NULL, 0, 0, 0, 0, NULL, NULL, 0); From 87a5a594822c9370707d046fd4531206cfaa14f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 11 Sep 2023 00:04:46 +0200 Subject: [PATCH 1006/1148] dmime: Implement sequence track IDirectMusicTrack_Play. (cherry picked from commit fd68076c36555d97593213a024d6ab6246de6e52) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/seqtrack.c | 95 +++++++++++++++++++++++++++++++++++++--- dlls/dmime/tests/dmime.c | 13 +----- 2 files changed, 91 insertions(+), 17 deletions(-) diff --git a/dlls/dmime/seqtrack.c b/dlls/dmime/seqtrack.c index 21b78ff26cd..a6043cb4a89 100644 --- a/dlls/dmime/seqtrack.c +++ b/dlls/dmime/seqtrack.c @@ -108,13 +108,96 @@ static HRESULT WINAPI sequence_track_EndPlay(IDirectMusicTrack8 *iface, void *pS return S_OK; } -static HRESULT WINAPI sequence_track_Play(IDirectMusicTrack8 *iface, void *pStateData, - MUSIC_TIME mtStart, MUSIC_TIME mtEnd, MUSIC_TIME mtOffset, DWORD dwFlags, - IDirectMusicPerformance *pPerf, IDirectMusicSegmentState *pSegSt, DWORD dwVirtualID) +static HRESULT WINAPI sequence_track_Play(IDirectMusicTrack8 *iface, void *state_data, + MUSIC_TIME start_time, MUSIC_TIME end_time, MUSIC_TIME time_offset, DWORD segment_flags, + IDirectMusicPerformance *performance, IDirectMusicSegmentState *segment_state, DWORD track_id) { - struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); - FIXME("(%p, %p, %ld, %ld, %ld, %ld, %p, %p, %ld): stub\n", This, pStateData, mtStart, mtEnd, mtOffset, dwFlags, pPerf, pSegSt, dwVirtualID); - return S_OK; + struct sequence_track *This = impl_from_IDirectMusicTrack8(iface); + IDirectMusicGraph *graph; + HRESULT hr; + UINT i; + + TRACE("(%p, %p, %ld, %ld, %ld, %#lx, %p, %p, %ld)\n", This, state_data, start_time, end_time, + time_offset, segment_flags, performance, segment_state, track_id); + + if (start_time != 0) FIXME("start_time %ld not implemented\n", start_time); + if (end_time != -1) FIXME("end_time %ld not implemented\n", end_time); + if (time_offset != 0) FIXME("time_offset %ld not implemented\n", time_offset); + if (segment_flags) FIXME("segment_flags %#lx not implemented\n", segment_flags); + if (segment_state) FIXME("segment_state %p not implemented\n", segment_state); + + if (FAILED(hr = IDirectMusicPerformance_QueryInterface(performance, + &IID_IDirectMusicGraph, (void **)&graph))) + return hr; + + for (i = 0; SUCCEEDED(hr) &&i < This->count; i++) + { + DMUS_IO_SEQ_ITEM *item = This->items + i; + DMUS_NOTE_PMSG *msg; + + if (FAILED(hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(*msg), + (DMUS_PMSG **)&msg))) + break; + + msg->mtTime = item->mtTime; + msg->dwFlags = DMUS_PMSGF_MUSICTIME; + msg->dwPChannel = item->dwPChannel; + msg->dwVirtualTrackID = track_id; + msg->dwType = DMUS_PMSGT_NOTE; + msg->dwGroupID = 1; + msg->mtDuration = item->mtDuration; + msg->wMusicValue = item->bByte1; + msg->nOffset = item->nOffset; + msg->bVelocity = item->bByte2; + msg->bFlags = 1; + msg->bMidiValue = item->bByte1; + + if (FAILED(hr = IDirectMusicGraph_StampPMsg(graph, (DMUS_PMSG *)msg)) + || FAILED(hr = IDirectMusicPerformance_SendPMsg(performance, (DMUS_PMSG *)msg))) + { + IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)msg); + break; + } + } + + for (i = 0; SUCCEEDED(hr) &&i < This->curve_count; i++) + { + DMUS_IO_CURVE_ITEM *item = This->curve_items + i; + DMUS_CURVE_PMSG *msg; + + if (FAILED(hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(*msg), + (DMUS_PMSG **)&msg))) + break; + + msg->mtTime = item->mtStart; + msg->dwFlags = DMUS_PMSGF_MUSICTIME; + msg->dwPChannel = item->dwPChannel; + msg->dwVirtualTrackID = track_id; + msg->dwType = DMUS_PMSGT_CURVE; + msg->mtDuration = item->mtDuration; + msg->mtOriginalStart = item->mtStart; + msg->mtResetDuration = item->mtResetDuration; + msg->nStartValue = item->nStartValue; + msg->nEndValue = item->nEndValue; + msg->nResetValue = item->nResetValue; + msg->nOffset = item->nOffset; + msg->bType = item->bType; + msg->bCurveShape = item->bCurveShape; + msg->bCCData = item->bCCData; + msg->bFlags = item->bFlags; + msg->wParamType = item->wParamType; + msg->wMergeIndex = item->wMergeIndex; + + if (FAILED(hr = IDirectMusicGraph_StampPMsg(graph, (DMUS_PMSG *)msg)) + || FAILED(hr = IDirectMusicPerformance_SendPMsg(performance, (DMUS_PMSG *)msg))) + { + IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)msg); + break; + } + } + + IDirectMusicGraph_Release(graph); + return hr; } static HRESULT WINAPI sequence_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type, diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 412cccce781..6881eac80c6 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3502,30 +3502,21 @@ static void test_sequence_track(void) ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬e); ok(!ret, "got %#lx\n", ret); - if (note->dwType == DMUS_PMSGT_NOTE) - { check_dmus_note_pmsg(note, 0, 0, 500, 60, 120); - } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)note); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬e); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); check_dmus_note_pmsg(note, 1000, 1, 200, 50, 100); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)note); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - } IDirectMusicSegment_Release(segment); From 5142de0278b1439499ca4a4652463fea741eaffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 12 Oct 2023 09:02:05 +0200 Subject: [PATCH 1007/1148] dmime: Implement band track IDirectMusicTrack_Play. (cherry picked from commit e3b23cb66eaf0f63ce9ab351c1d7da1af0017225) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 34 ++++++++++++++++++++++++++++++++ dlls/dmband/bandtrack.c | 38 +++++++++++++++++++++++++----------- dlls/dmband/dmband_private.h | 2 ++ dlls/dmband/tests/dmband.c | 1 - dlls/dmime/tests/dmime.c | 18 +++-------------- 5 files changed, 66 insertions(+), 27 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index 4b3c053c586..456bb20143e 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -486,3 +486,37 @@ HRESULT band_connect_to_collection(IDirectMusicBand *iface, IDirectMusicCollecti return S_OK; } + +HRESULT band_send_messages(IDirectMusicBand *iface, IDirectMusicPerformance *performance, + IDirectMusicGraph *graph, MUSIC_TIME time, DWORD track_id) +{ + struct band *This = impl_from_IDirectMusicBand(iface); + struct instrument_entry *entry; + HRESULT hr = S_OK; + + LIST_FOR_EACH_ENTRY_REV(entry, &This->instruments, struct instrument_entry, entry) + { + DMUS_PATCH_PMSG *msg; + + if (FAILED(hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(*msg), + (DMUS_PMSG **)&msg))) + break; + + msg->mtTime = time; + msg->dwFlags = DMUS_PMSGF_MUSICTIME; + msg->dwPChannel = entry->instrument.dwPChannel; + msg->dwVirtualTrackID = track_id; + msg->dwType = DMUS_PMSGT_PATCH; + msg->dwGroupID = 1; + msg->byInstrument = entry->instrument.dwPatch; + + if (FAILED(hr = IDirectMusicGraph_StampPMsg(graph, (DMUS_PMSG *)msg)) + || FAILED(hr = IDirectMusicPerformance_SendPMsg(performance, (DMUS_PMSG *)msg))) + { + IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)msg); + break; + } + } + + return hr; +} diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index 6233d39b9c0..e0acb6ff62d 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -159,22 +159,38 @@ static HRESULT WINAPI band_track_EndPlay(IDirectMusicTrack8 *iface, void *state_ } static HRESULT WINAPI band_track_Play(IDirectMusicTrack8 *iface, void *state_data, - MUSIC_TIME mtStart, MUSIC_TIME mtEnd, MUSIC_TIME mtOffset, DWORD flags, - IDirectMusicPerformance *performance, IDirectMusicSegmentState *segment_state, - DWORD virtual_id) + MUSIC_TIME start_time, MUSIC_TIME end_time, MUSIC_TIME time_offset, DWORD segment_flags, + IDirectMusicPerformance *performance, IDirectMusicSegmentState *segment_state, DWORD track_id) { struct band_track *This = impl_from_IDirectMusicTrack8(iface); + IDirectMusicGraph *graph; + struct band_entry *entry; + HRESULT hr; - FIXME("(%p, %p, %ld, %ld, %ld, %lx, %p, %p, %ld): semi-stub\n", This, state_data, mtStart, mtEnd, mtOffset, flags, performance, segment_state, virtual_id); + TRACE("(%p, %p, %ld, %ld, %ld, %#lx, %p, %p, %ld)\n", This, state_data, start_time, end_time, + time_offset, segment_flags, performance, segment_state, track_id); - /* Sends following pMSG: - - DMUS_PATCH_PMSG - - DMUS_TRANSPOSE_PMSG - - DMUS_CHANNEL_PRIORITY_PMSG - - DMUS_MIDI_PMSG - */ + if (!performance) return DMUS_S_END; - return S_OK; + if (start_time != 0) FIXME("start_time %ld not implemented\n", start_time); + if (end_time != -1) FIXME("end_time %ld not implemented\n", end_time); + if (time_offset != 0) FIXME("time_offset %ld not implemented\n", time_offset); + if (segment_flags) FIXME("segment_flags %#lx not implemented\n", segment_flags); + if (segment_state) FIXME("segment_state %p not implemented\n", segment_state); + + if (FAILED(hr = IDirectMusicPerformance_QueryInterface(performance, + &IID_IDirectMusicGraph, (void **)&graph))) + return hr; + + LIST_FOR_EACH_ENTRY(entry, &This->bands, struct band_entry, entry) + { + if (FAILED(hr = band_send_messages(entry->band, performance, graph, + entry->head.lBandTimeLogical, track_id))) + break; + } + + IDirectMusicGraph_Release(graph); + return hr; } static HRESULT WINAPI band_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time, diff --git a/dlls/dmband/dmband_private.h b/dlls/dmband/dmband_private.h index a12b9f8cc82..48c5ec02aeb 100644 --- a/dlls/dmband/dmband_private.h +++ b/dlls/dmband/dmband_private.h @@ -48,5 +48,7 @@ extern HRESULT create_dmband(REFIID riid, void **ret_iface); extern HRESULT create_dmbandtrack(REFIID riid, void **ret_iface); extern HRESULT band_connect_to_collection(IDirectMusicBand *iface, IDirectMusicCollection *collection); +extern HRESULT band_send_messages(IDirectMusicBand *iface, IDirectMusicPerformance *performance, + IDirectMusicGraph *graph, MUSIC_TIME time, DWORD track_id); #endif /* __WINE_DMBAND_PRIVATE_H */ diff --git a/dlls/dmband/tests/dmband.c b/dlls/dmband/tests/dmband.c index ee45ca33714..d88606976e4 100644 --- a/dlls/dmband/tests/dmband.c +++ b/dlls/dmband/tests/dmband.c @@ -233,7 +233,6 @@ static void test_bandtrack(void) hr = IDirectMusicTrack8_EndPlay(dmt8, NULL); ok(hr == S_OK, "IDirectMusicTrack8_EndPlay failed: %#lx\n", hr); hr = IDirectMusicTrack8_Play(dmt8, NULL, 0, 0, 0, 0, NULL, NULL, 0); - todo_wine ok(hr == DMUS_S_END, "IDirectMusicTrack8_Play failed: %#lx\n", hr); hr = IDirectMusicTrack8_GetParam(dmt8, NULL, 0, NULL, NULL); ok(hr == E_POINTER, "IDirectMusicTrack8_GetParam failed: %#lx\n", hr); diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 6881eac80c6..6dc9d8e0f5e 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3756,39 +3756,27 @@ static void test_band_track_play(void) ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&patch); ok(!ret, "got %#lx\n", ret); - if (patch->dwType == DMUS_PMSGT_PATCH) - { check_dmus_patch_pmsg(patch, 0, 1, 0, 1); - } hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)patch); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&patch); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); check_dmus_patch_pmsg(patch, 1000, 2, 0, 3); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)patch); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&patch); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); check_dmus_patch_pmsg(patch, 1000, 1, 0, 2); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)patch); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - } IDirectMusicSegment_Release(segment); From 2752e137788720d70abedc70007d1a39797f62d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 6 Sep 2023 09:44:26 +0200 Subject: [PATCH 1008/1148] dmime: Output DMUS_MIDI_PMSG into a music buffer on the port. (cherry picked from commit a4b006e723528036822f241911533bd6e8f67062) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 2018fa013de..782f300eb4b 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1637,6 +1637,39 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, switch (msg->dwType) { + case DMUS_PMSGT_MIDI: + { + static const UINT event_size = sizeof(DMUS_EVENTHEADER) + sizeof(DWORD); + DMUS_BUFFERDESC desc = {.dwSize = sizeof(desc), .cbBuffer = 2 * event_size}; + DMUS_MIDI_PMSG *midi = (DMUS_MIDI_PMSG *)msg; + IDirectMusicBuffer *buffer; + IDirectMusicPort *port; + DWORD group, channel; + UINT value = 0; + + if (FAILED(hr = IDirectMusicPerformance_PChannelInfo(performance, msg->dwPChannel, + &port, &group, &channel))) + { + WARN("Failed to get message port, hr %#lx\n", hr); + return DMUS_S_FREE; + } + + value |= channel; + value |= (UINT)midi->bStatus; + value |= (UINT)midi->bByte1 << 8; + value |= (UINT)midi->bByte2 << 16; + + if (SUCCEEDED(hr = IDirectMusic_CreateMusicBuffer(This->dmusic, &desc, &buffer, NULL))) + { + hr = IDirectMusicBuffer_PackStructured(buffer, msg->rtTime, group, value); + if (SUCCEEDED(hr)) hr = IDirectMusicPort_PlayBuffer(port, buffer); + IDirectMusicBuffer_Release(buffer); + } + + IDirectMusicPort_Release(port); + break; + } + case DMUS_PMSGT_NOTIFICATION: { DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)msg; From 9f36d033245164d5dfb72d2b9e66580d5233a631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 21 Sep 2023 15:54:22 +0200 Subject: [PATCH 1009/1148] dmime: Translate DMUS_PMSGT_NOTE to DMUS_PMSGT_MIDI messages. (cherry picked from commit f74fe2e26b45f8f557eb121be260a3a81b1f6f29) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 782f300eb4b..b12a8315330 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1626,6 +1626,35 @@ static HRESULT WINAPI performance_tool_GetMediaTypes(IDirectMusicTool *iface, DW return E_NOTIMPL; } +static HRESULT performance_send_midi_pmsg(struct performance *This, DMUS_PMSG *msg, UINT flags, + BYTE status, BYTE byte1, BYTE byte2) +{ + IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface; + DMUS_MIDI_PMSG *midi; + HRESULT hr; + + if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*midi), + (DMUS_PMSG **)&midi))) + return hr; + + if (flags & DMUS_PMSGF_REFTIME) midi->rtTime = msg->rtTime; + if (flags & DMUS_PMSGF_MUSICTIME) midi->mtTime = msg->mtTime; + midi->dwFlags = flags; + midi->dwPChannel = msg->dwPChannel; + midi->dwVirtualTrackID = msg->dwVirtualTrackID; + midi->dwVoiceID = msg->dwVoiceID; + midi->dwGroupID = msg->dwGroupID; + midi->dwType = DMUS_PMSGT_MIDI; + midi->bStatus = status; + midi->bByte1 = byte1; + midi->bByte2 = byte2; + + if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)midi))) + IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)midi); + + return hr; +} + static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, IDirectMusicPerformance *performance, DMUS_PMSG *msg) { @@ -1670,6 +1699,22 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, break; } + case DMUS_PMSGT_NOTE: + { + DMUS_NOTE_PMSG *note = (DMUS_NOTE_PMSG *)msg; + + if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, + 0x90 /* NOTE_ON */, note->bMidiValue, note->bVelocity))) + WARN("Failed to translate message to MIDI, hr %#lx\n", hr); + + msg->mtTime += note->mtDuration; + if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE, + 0x80 /* NOTE_OFF */, note->bMidiValue, 0))) + WARN("Failed to translate message to MIDI, hr %#lx\n", hr); + + break; + } + case DMUS_PMSGT_NOTIFICATION: { DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)msg; From b68bca844dcabcf97647b2848e9faaef8b51a287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 19 Sep 2023 09:10:48 +0200 Subject: [PATCH 1010/1148] dmime: Translate DMUS_PMSGT_PATCH to DMUS_PMSGT_MIDI messages. (cherry picked from commit a6710afc26f599c5e3e592e452032f1d3a0ab4c8) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index b12a8315330..63429023da2 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1715,6 +1715,25 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, break; } + case DMUS_PMSGT_PATCH: + { + DMUS_PATCH_PMSG *patch = (DMUS_PATCH_PMSG *)msg; + + if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, + 0xb0 /* Control Change */, 0x00 /* CC: Bank MSB */, patch->byMSB))) + WARN("Failed to translate message to MIDI, hr %#lx\n", hr); + + if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, + 0xb0 /* Control Change */, 0x20 /* CC: Bank LSB */, patch->byLSB))) + WARN("Failed to translate message to MIDI, hr %#lx\n", hr); + + if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, + 0xc0 /* Program Change */, patch->byInstrument, 0))) + WARN("Failed to translate message to MIDI, hr %#lx\n", hr); + + break; + } + case DMUS_PMSGT_NOTIFICATION: { DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)msg; From 4a7237e901998781b021f8a83045194b30f96e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 10:31:04 +0200 Subject: [PATCH 1011/1148] include: Fix debugstr_fourcc printf format to print at most 4 chars. (cherry picked from commit df75c9ed3c313e7e3760faa1b478f180ab10f505) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- include/wine/debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wine/debug.h b/include/wine/debug.h index 52e072e1f2b..40da1385950 100644 --- a/include/wine/debug.h +++ b/include/wine/debug.h @@ -321,7 +321,7 @@ static inline const char *wine_dbgstr_fourcc( unsigned int fourcc ) if (!fourcc) return "''"; if (isprint( str[0] ) && isprint( str[1] ) && isprint( str[2] ) && isprint( str[3] )) - return wine_dbg_sprintf( "'%4s'", str ); + return wine_dbg_sprintf( "'%.4s'", str ); return wine_dbg_sprintf( "0x%08x", fourcc ); } From ebcadafed729c8b4bd4db93fe6554b544832fcd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 15:14:11 +0200 Subject: [PATCH 1012/1148] dmime: Rewrite message thread with a condition variable. (cherry picked from commit d5e156fa7fc9ff47c1c99af342047319f0f23bfa) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 105 ++++++++++++--------------------------- 1 file changed, 33 insertions(+), 72 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 63429023da2..14e290658e0 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -54,11 +54,10 @@ struct performance REFERENCE_TIME rtLatencyTime; DWORD dwBumperLength; DWORD dwPrepareTime; - /** Message Processing */ - HANDLE procThread; - DWORD procThreadId; - BOOL procThreadTicStarted; + + HANDLE message_thread; CRITICAL_SECTION safe; + CONDITION_VARIABLE cond; IReferenceClock *master_clock; REFERENCE_TIME init_time; @@ -124,25 +123,20 @@ static HRESULT performance_process_message(struct performance *This, DMUS_PMSG * return hr; } -#define PROCESSMSG_START (WM_APP + 0) -#define PROCESSMSG_EXIT (WM_APP + 1) -#define PROCESSMSG_REMOVE (WM_APP + 2) -#define PROCESSMSG_ADD (WM_APP + 4) - -static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) +static DWORD WINAPI message_thread_proc(void *args) { - struct performance *This = lpParam; - DWORD timeout = INFINITE; - MSG msg; - HRESULT hr; + struct performance *This = args; struct message *message, *next; + HRESULT hr; - while (TRUE) - { - if (timeout > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeout, QS_POSTMESSAGE | QS_SENDMESSAGE | QS_TIMER); - timeout = INFINITE; + TRACE("performance %p message thread\n", This); + SetThreadDescription(GetCurrentThread(), L"wine_dmime_message"); - EnterCriticalSection(&This->safe); + EnterCriticalSection(&This->safe); + + while (This->message_thread) + { + DWORD timeout = INFINITE; LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry) { @@ -154,59 +148,15 @@ static DWORD WINAPI ProcessMsgThread(LPVOID lpParam) if (hr != S_OK) break; } - LeaveCriticalSection(&This->safe); - - while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) - { - /** if hwnd we suppose that is a windows event ... */ - if (NULL != msg.hwnd) - { - TranslateMessage(&msg); - DispatchMessageA(&msg); - } - else - { - switch (msg.message) - { - case WM_QUIT: - case PROCESSMSG_EXIT: goto outofthread; - case PROCESSMSG_START: break; - case PROCESSMSG_ADD: break; - case PROCESSMSG_REMOVE: break; - default: ERR("Unhandled message %u. Critical Path\n", msg.message); break; - } - } - } - - /** here we should run a little of current AudioPath */ + SleepConditionVariableCS(&This->cond, &This->safe, timeout); } -outofthread: - TRACE("(%p): Exiting\n", This); + LeaveCriticalSection(&This->safe); + TRACE("(%p): Exiting\n", This); return 0; } -static BOOL PostMessageToProcessMsgThread(struct performance *This, UINT iMsg) { - if (FALSE == This->procThreadTicStarted && PROCESSMSG_EXIT != iMsg) { - BOOL res; - This->procThread = CreateThread(NULL, 0, ProcessMsgThread, This, 0, &This->procThreadId); - if (NULL == This->procThread) return FALSE; - SetThreadPriority(This->procThread, THREAD_PRIORITY_TIME_CRITICAL); - This->procThreadTicStarted = TRUE; - while(1) { - res = PostThreadMessageA(This->procThreadId, iMsg, 0, 0); - /* Let the thread creates its message queue (with MsgWaitForMultipleObjects call) by yielding and retrying */ - if (!res && (GetLastError() == ERROR_INVALID_THREAD_ID)) - Sleep(0); - else - break; - } - return res; - } - return PostThreadMessageA(This->procThreadId, iMsg, 0, 0); -} - static HRESULT performance_send_dirty_pmsg(struct performance *This, MUSIC_TIME music_time) { IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface; @@ -424,7 +374,12 @@ static HRESULT WINAPI performance_Init(IDirectMusicPerformance8 *iface, IDirectM return hr; } - PostMessageToProcessMsgThread(This, PROCESSMSG_START); + if (!(This->message_thread = CreateThread(NULL, 0, message_thread_proc, This, 0, NULL))) + { + ERR("Failed to start performance message thread, error %lu\n", GetLastError()); + IDirectMusicPerformance_CloseDown(iface); + return HRESULT_FROM_WIN32(GetLastError()); + } if (dmusic && !*dmusic) { @@ -549,13 +504,13 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS LIST_FOR_EACH_ENTRY(next, &This->messages, struct message, entry) if (next->msg.rtTime > message->msg.rtTime) break; list_add_before(&next->entry, &message->entry); - PostThreadMessageW(This->procThreadId, PROCESSMSG_ADD, 0, 0); hr = S_OK; } done: LeaveCriticalSection(&This->safe); + if (SUCCEEDED(hr)) WakeConditionVariable(&This->cond); return hr; } @@ -1033,14 +988,20 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct message *message, *next; + HANDLE message_thread; HRESULT hr; FIXME("(%p): semi-stub\n", This); - if (PostMessageToProcessMsgThread(This, PROCESSMSG_EXIT)) { - WaitForSingleObject(This->procThread, INFINITE); - This->procThreadTicStarted = FALSE; - CloseHandle(This->procThread); + if ((message_thread = This->message_thread)) + { + EnterCriticalSection(&This->safe); + This->message_thread = NULL; + LeaveCriticalSection(&This->safe); + WakeConditionVariable(&This->cond); + + WaitForSingleObject(message_thread, INFINITE); + CloseHandle(message_thread); } This->notification_performance = FALSE; From 0250d6a53eb547b427b7306b8e0c131845cb4292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Sep 2023 16:20:57 +0200 Subject: [PATCH 1013/1148] dmusic: Set synth sink master clock when creating port. (cherry picked from commit f00d8639e6f34f81d0acd16a8a0e6f05f210fda8) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/port.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index 0eff1c5cb02..de5b701663d 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -714,6 +714,9 @@ HRESULT synth_port_create(IDirectMusic8Impl *parent, DMUS_PORTPARAMS *port_param if (SUCCEEDED(hr)) hr = IDirectMusicSynth_SetMasterClock(obj->synth, obj->parent->master_clock); + if (SUCCEEDED(hr)) + hr = IDirectMusicSynthSink_SetMasterClock(obj->synth_sink, obj->parent->master_clock); + if (SUCCEEDED(hr)) hr = IDirectMusicSynth_Open(obj->synth, port_params); From 7810855523cc3deba40793d021bcfce16a6f85df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 29 Aug 2023 14:40:05 +0200 Subject: [PATCH 1014/1148] dmsynth: Do nothing in IDirectMusicSynth_SetMasterClock. (cherry picked from commit 29a76fb5d1b022c1c460b694238c6be1a4c211a5) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 7 +++---- dlls/dmsynth/tests/dmsynth.c | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index af578610c16..6a780dcbb59 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -918,10 +918,9 @@ static HRESULT WINAPI synth_SetMasterClock(IDirectMusicSynth8 *iface, TRACE("(%p)->(%p)\n", This, clock); - if (!This->sink) - return DMUS_E_NOSYNTHSINK; - - return IDirectMusicSynthSink_SetMasterClock(This->sink, clock); + if (!clock) + return E_POINTER; + return S_OK; } static HRESULT WINAPI synth_GetLatencyClock(IDirectMusicSynth8 *iface, diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index ad4bb5c0ad5..0deaa161a3b 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1036,19 +1036,19 @@ static void test_IDirectMusicSynth(void) /* SetMasterClock does nothing */ hr = IDirectMusicSynth_SetMasterClock(synth, NULL); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicSynth_SetMasterClock(synth, clock); ok(hr == S_OK, "got %#lx\n", hr); ref = get_refcount(clock); todo_wine ok(ref == 1, "got %lu\n", ref); hr = IDirectMusicSynth_Activate(synth, TRUE); - todo_wine ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); + ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); /* SetMasterClock needs to be called on the sink */ hr = IDirectMusicSynthSink_SetMasterClock(sink, clock); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicSynth_Activate(synth, TRUE); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicSynth_Activate(synth, TRUE); ok(hr == S_FALSE, "got %#lx\n", hr); From 49f990d780f691f9a7465c27b021d227aa8f999f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 12 Oct 2023 11:13:33 +0200 Subject: [PATCH 1015/1148] dmusic: Forward IDirectMusicPort_Activate to synth and sink. (cherry picked from commit 72165fc8ec2cfb86ff641ed817b1fb7cace24dfe) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/port.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/dlls/dmusic/port.c b/dlls/dmusic/port.c index de5b701663d..812fb8c94e3 100644 --- a/dlls/dmusic/port.c +++ b/dlls/dmusic/port.c @@ -274,30 +274,36 @@ static HRESULT WINAPI synth_port_GetNumChannelGroups(IDirectMusicPort *iface, DW static HRESULT WINAPI synth_port_Activate(IDirectMusicPort *iface, BOOL active) { struct synth_port *This = synth_from_IDirectMusicPort(iface); + HRESULT hr; - FIXME("(%p/%p)->(%d): semi-stub\n", iface, This, active); + TRACE("(%p/%p)->(%d)\n", iface, This, active); - if (This->active == active) - return S_FALSE; + if (This->active == active) return S_FALSE; - if (active) { - /* Acquire the dsound */ - if (!This->dsound) { - IDirectSound_AddRef(This->parent->dsound); - This->dsound = This->parent->dsound; - } - IDirectSound_AddRef(This->dsound); - } else { - /* Release the dsound */ - IDirectSound_Release(This->dsound); - IDirectSound_Release(This->parent->dsound); - if (This->dsound == This->parent->dsound) - This->dsound = NULL; + if (active) + { + if (!This->dsound && FAILED(hr = IDirectMusicPort_SetDirectSound(iface, + This->parent->dsound, NULL))) + return hr; + if (FAILED(hr = IDirectMusicSynthSink_SetDirectSound(This->synth_sink, + This->dsound, This->dsbuffer))) + return hr; + + if (FAILED(hr = IDirectMusicSynth_Activate(This->synth, active))) + return hr; + This->active = TRUE; } + else + { + if (FAILED(hr = IDirectMusicSynth_Activate(This->synth, FALSE))) return hr; + This->active = FALSE; - This->active = active; + if (FAILED(hr = IDirectMusicSynthSink_SetDirectSound(This->synth_sink, NULL, NULL))) + return hr; + hr = IDirectMusicPort_SetDirectSound(iface, NULL, NULL); + } - return S_OK; + return hr; } static HRESULT WINAPI synth_port_SetChannelPriority(IDirectMusicPort *iface, DWORD channel_group, From ba1b2412a0207413210e51fe6e727e69d0c97311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 11:52:37 +0200 Subject: [PATCH 1016/1148] dmime: Use port latency time for messages with -1 time. (cherry picked from commit a8763aab6fbcab804282a3d909e16e93ead2a867) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 14e290658e0..4dffadb405d 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -490,7 +490,7 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS if (!(msg->dwFlags & DMUS_PMSGF_REFTIME)) { if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(iface, - msg->mtTime, &msg->rtTime))) + msg->mtTime == -1 ? 0 : msg->mtTime, &msg->rtTime))) goto done; msg->dwFlags |= DMUS_PMSGF_REFTIME; } @@ -1632,6 +1632,8 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, static const UINT event_size = sizeof(DMUS_EVENTHEADER) + sizeof(DWORD); DMUS_BUFFERDESC desc = {.dwSize = sizeof(desc), .cbBuffer = 2 * event_size}; DMUS_MIDI_PMSG *midi = (DMUS_MIDI_PMSG *)msg; + IReferenceClock *latency_clock; + REFERENCE_TIME latency_time; IDirectMusicBuffer *buffer; IDirectMusicPort *port; DWORD group, channel; @@ -1644,6 +1646,13 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, return DMUS_S_FREE; } + if (SUCCEEDED(hr = IDirectMusicPort_GetLatencyClock(port, &latency_clock))) + { + if (FAILED(hr = IReferenceClock_GetTime(latency_clock, &latency_time))) + ERR("Failed to get port latency time, hr %#lx\n", hr); + IReferenceClock_Release(latency_clock); + } + value |= channel; value |= (UINT)midi->bStatus; value |= (UINT)midi->bByte1 << 8; @@ -1651,6 +1660,7 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, if (SUCCEEDED(hr = IDirectMusic_CreateMusicBuffer(This->dmusic, &desc, &buffer, NULL))) { + if (msg->rtTime == -1) msg->rtTime = latency_time; hr = IDirectMusicBuffer_PackStructured(buffer, msg->rtTime, group, value); if (SUCCEEDED(hr)) hr = IDirectMusicPort_PlayBuffer(port, buffer); IDirectMusicBuffer_Release(buffer); From 493fc4123fbda402a6fc411fe96cdd2c9724e973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 11:32:58 +0200 Subject: [PATCH 1017/1148] dmime: Update performance latency time with port latency. (cherry picked from commit d0f544669615fd3672c88504fa39822f070dfdfa) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 56 +++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 4dffadb405d..a56a35a2f45 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -51,7 +51,7 @@ struct performance BOOL audio_paths_enabled; IDirectMusicAudioPath *pDefaultPath; - REFERENCE_TIME rtLatencyTime; + REFERENCE_TIME latency_offset; DWORD dwBumperLength; DWORD dwPrepareTime; @@ -737,6 +737,31 @@ static HRESULT WINAPI performance_RemoveNotificationType(IDirectMusicPerformance return hr; } +static void performance_update_latency_time(struct performance *This, IDirectMusicPort *port, + REFERENCE_TIME *ret_time) +{ + IDirectMusicPerformance8 *iface = &This->IDirectMusicPerformance8_iface; + REFERENCE_TIME latency_time, current_time; + IReferenceClock *latency_clock; + HRESULT hr; + + if (!ret_time) ret_time = &latency_time; + if (SUCCEEDED(hr = IDirectMusicPort_GetLatencyClock(port, &latency_clock))) + { + hr = IReferenceClock_GetTime(latency_clock, ret_time); + if (SUCCEEDED(hr)) hr = IDirectMusicPerformance8_GetTime(iface, ¤t_time, NULL); + if (SUCCEEDED(hr) && This->latency_offset < (*ret_time - current_time)) + { + TRACE("Updating performance %p latency %I64d -> %I64d\n", This, + This->latency_offset, *ret_time - current_time); + This->latency_offset = *ret_time - current_time; + } + IReferenceClock_Release(latency_clock); + } + + if (FAILED(hr)) ERR("Failed to update performance %p latency, hr %#lx\n", This, hr); +} + static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *params) { IDirectMusicPort *port; @@ -760,6 +785,7 @@ static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *par for (i = 0; i < params->dwChannelGroups; i++) pchannel_block_set(&perf->pchannels, i, port, i + 1, FALSE); + performance_update_latency_time(perf, port, NULL); return S_OK; } @@ -787,6 +813,8 @@ static HRESULT WINAPI performance_AddPort(IDirectMusicPerformance8 *iface, IDire * We should remember added Ports (for example using a list) * and control if Port is registered for each api who use ports */ + + performance_update_latency_time(This, port, NULL); return S_OK; } @@ -958,13 +986,18 @@ static HRESULT WINAPI performance_SetGlobalParam(IDirectMusicPerformance8 *iface return S_OK; } -static HRESULT WINAPI performance_GetLatencyTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtTime) +static HRESULT WINAPI performance_GetLatencyTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *ret_time) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + REFERENCE_TIME current_time; + HRESULT hr; - TRACE("(%p, %p): stub\n", This, prtTime); - *prtTime = This->rtLatencyTime; - return S_OK; + TRACE("(%p, %p)\n", This, ret_time); + + if (SUCCEEDED(hr = IDirectMusicPerformance8_GetTime(iface, ¤t_time, NULL))) + *ret_time = current_time + This->latency_offset; + + return hr; } static HRESULT WINAPI performance_GetQueueTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME *prtTime) @@ -1632,7 +1665,6 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, static const UINT event_size = sizeof(DMUS_EVENTHEADER) + sizeof(DWORD); DMUS_BUFFERDESC desc = {.dwSize = sizeof(desc), .cbBuffer = 2 * event_size}; DMUS_MIDI_PMSG *midi = (DMUS_MIDI_PMSG *)msg; - IReferenceClock *latency_clock; REFERENCE_TIME latency_time; IDirectMusicBuffer *buffer; IDirectMusicPort *port; @@ -1645,13 +1677,7 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, WARN("Failed to get message port, hr %#lx\n", hr); return DMUS_S_FREE; } - - if (SUCCEEDED(hr = IDirectMusicPort_GetLatencyClock(port, &latency_clock))) - { - if (FAILED(hr = IReferenceClock_GetTime(latency_clock, &latency_time))) - ERR("Failed to get port latency time, hr %#lx\n", hr); - IReferenceClock_Release(latency_clock); - } + performance_update_latency_time(This, port, &latency_time); value |= channel; value |= (UINT)midi->bStatus; @@ -1796,7 +1822,7 @@ HRESULT create_dmperformance(REFIID iid, void **ret_iface) list_init(&obj->messages); list_init(&obj->notifications); - obj->rtLatencyTime = 100; /* 100 ms TO FIX */ + obj->latency_offset = 50; obj->dwBumperLength = 50; /* 50 ms default */ obj->dwPrepareTime = 1000; /* 1000 ms default */ From 992b169079e7ee6582c553d9d9976b049387b5e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 11:41:34 +0200 Subject: [PATCH 1018/1148] dmime: Use latency time to decide when to process messages. (cherry picked from commit 1014bab951f89a51226a17ec1d6d8d1225c46c5c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index a56a35a2f45..dc6ef575713 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -89,10 +89,10 @@ static HRESULT performance_process_message(struct performance *This, DMUS_PMSG * do { - REFERENCE_TIME current, offset = 0; + REFERENCE_TIME latency, offset = 0; IDirectMusicTool *tool; - if (FAILED(hr = IDirectMusicPerformance_GetTime(performance, ¤t, NULL))) return hr; + if (FAILED(hr = IDirectMusicPerformance_GetLatencyTime(performance, &latency))) return hr; if (!(tool = msg->pTool)) tool = &This->IDirectMusicTool_iface; switch (msg->dwFlags & delivery_flags) @@ -107,9 +107,9 @@ static HRESULT performance_process_message(struct performance *This, DMUS_PMSG * offset = This->dwBumperLength * 10000; /* fallthrough */ case DMUS_PMSGF_TOOL_ATTIME: - if (msg->rtTime >= offset && msg->rtTime - offset >= current) + if (msg->rtTime >= offset && msg->rtTime - offset >= latency) { - if (timeout) *timeout = (msg->rtTime - offset - current) / 10000; + if (timeout) *timeout = (msg->rtTime - offset - latency) / 10000; return DMUS_S_REQUEUE; } From 8794a12e7df128e1b7320f683d674d56717ee1de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Sep 2023 13:43:07 +0200 Subject: [PATCH 1019/1148] dmsynth: Create a render thread on sink activation. (cherry picked from commit 0d56c54d8b5d754ab71576232b04df39b5e46414) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synthsink.c | 153 ++++++++++++++++++++++++++++++++++- dlls/dmsynth/tests/dmsynth.c | 4 +- 2 files changed, 154 insertions(+), 3 deletions(-) diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index db1fecb8d48..906e37e8959 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -27,6 +27,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); +#define BUFFER_SUBDIVISIONS 8 + struct synth_sink { IDirectMusicSynthSink IDirectMusicSynthSink_iface; @@ -37,10 +39,17 @@ struct synth_sink IReferenceClock *master_clock; IDirectMusicSynth *synth; /* No reference hold! */ IDirectSound *dsound; + IDirectSoundBuffer *dsound_buffer; BOOL active; REFERENCE_TIME activate_time; + + CRITICAL_SECTION cs; REFERENCE_TIME latency_time; + + DWORD written; /* number of bytes written out */ + HANDLE stop_event; + HANDLE render_thread; }; static inline struct synth_sink *impl_from_IDirectMusicSynthSink(IDirectMusicSynthSink *iface) @@ -67,8 +76,93 @@ static void synth_sink_get_format(struct synth_sink *This, WAVEFORMATEX *format) } } +struct render_thread_params +{ + struct synth_sink *sink; + IDirectMusicSynth *synth; + IDirectSoundBuffer *buffer; + HANDLE started_event; +}; + +static DWORD CALLBACK synth_sink_render_thread(void *args) +{ + struct render_thread_params *params = args; + DSBCAPS caps = {.dwSize = sizeof(DSBCAPS)}; + IDirectSoundBuffer *buffer = params->buffer; + IDirectMusicSynth *synth = params->synth; + struct synth_sink *sink = params->sink; + IDirectSoundNotify *notify; + HANDLE buffer_event; + HRESULT hr; + + TRACE("Starting thread, args %p\n", args); + SetThreadDescription(GetCurrentThread(), L"wine_dmsynth_sink"); + + if (FAILED(hr = IDirectSoundBuffer_Stop(buffer))) + ERR("Failed to stop sound buffer, hr %#lx.\n", hr); + + if (!(buffer_event = CreateEventW(NULL, FALSE, FALSE, NULL))) + ERR("Failed to create buffer event, error %lu\n", GetLastError()); + else if (FAILED(hr = IDirectSoundBuffer_GetCaps(buffer, &caps))) + ERR("Failed to query sound buffer caps, hr %#lx.\n", hr); + else if (FAILED(hr = IDirectSoundBuffer_QueryInterface(buffer, &IID_IDirectSoundNotify, + (void **)¬ify))) + ERR("Failed to query IDirectSoundNotify iface, hr %#lx.\n", hr); + else + { + DSBPOSITIONNOTIFY positions[BUFFER_SUBDIVISIONS] = {{.dwOffset = 0, .hEventNotify = buffer_event}}; + int i; + + for (i = 1; i < ARRAY_SIZE(positions); ++i) + { + positions[i] = positions[i - 1]; + positions[i].dwOffset += caps.dwBufferBytes / ARRAY_SIZE(positions); + } + + if (FAILED(hr = IDirectSoundNotify_SetNotificationPositions(notify, + ARRAY_SIZE(positions), positions))) + ERR("Failed to set notification positions, hr %#lx\n", hr); + + IDirectSoundNotify_Release(notify); + } + + if (FAILED(hr = IDirectSoundBuffer_Play(buffer, 0, 0, DSBPLAY_LOOPING))) + ERR("Failed to start sound buffer, hr %#lx.\n", hr); + SetEvent(params->started_event); + + while (hr == S_OK) + { + HANDLE handles[] = {sink->stop_event, buffer_event}; + DWORD ret; + + if (!(ret = WaitForMultipleObjects(ARRAY_SIZE(handles), handles, FALSE, INFINITE)) + || ret >= ARRAY_SIZE(handles)) + { + ERR("WaitForMultipleObjects returned %lu\n", ret); + hr = HRESULT_FROM_WIN32(ret); + break; + } + } + + if (FAILED(hr)) + { + ERR("Thread unexpected termination, hr %#lx\n", hr); + return hr; + } + + IDirectSoundBuffer_Release(buffer); + IDirectMusicSynth_Release(synth); + CloseHandle(buffer_event); + + return 0; +} + static HRESULT synth_sink_activate(struct synth_sink *This) { + IDirectMusicSynthSink *iface = &This->IDirectMusicSynthSink_iface; + DSBUFFERDESC desc = {.dwSize = sizeof(DSBUFFERDESC)}; + struct render_thread_params params; + WAVEFORMATEX format; HRESULT hr; if (!This->synth) return DMUS_E_SYNTHNOTCONFIGURED; @@ -79,13 +173,54 @@ static HRESULT synth_sink_activate(struct synth_sink *This) if (FAILED(hr = IReferenceClock_GetTime(This->master_clock, &This->activate_time))) return hr; This->latency_time = This->activate_time; + if ((params.buffer = This->dsound_buffer)) + IDirectMusicBuffer_AddRef(params.buffer); + else + { + synth_sink_get_format(This, &format); + desc.lpwfxFormat = (WAVEFORMATEX *)&format; + desc.dwBufferBytes = format.nAvgBytesPerSec; + if (FAILED(hr = IDirectMusicSynthSink_GetDesiredBufferSize(iface, &desc.dwBufferBytes))) + ERR("Failed to get desired buffer size, hr %#lx\n", hr); + + desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY; + if (FAILED(hr = IDirectSound8_CreateSoundBuffer(This->dsound, &desc, ¶ms.buffer, NULL))) + { + ERR("Failed to create sound buffer, hr %#lx.\n", hr); + return hr; + } + } + + params.sink = This; + params.synth = This->synth; + IDirectMusicSynth_AddRef(This->synth); + + if (!(params.started_event = CreateEventW(NULL, FALSE, FALSE, NULL)) + || !(This->render_thread = CreateThread(NULL, 0, synth_sink_render_thread, ¶ms, 0, NULL))) + { + ERR("Failed to create render thread, error %lu\n", GetLastError()); + hr = HRESULT_FROM_WIN32(GetLastError()); + IDirectSoundBuffer_Release(params.buffer); + IDirectMusicSynth_Release(params.synth); + CloseHandle(params.started_event); + return hr; + } + + WaitForSingleObject(params.started_event, INFINITE); + CloseHandle(params.started_event); This->active = TRUE; return S_OK; } static HRESULT synth_sink_deactivate(struct synth_sink *This) { + if (!This->active) return S_OK; + + SetEvent(This->stop_event); + WaitForSingleObject(This->render_thread, INFINITE); + This->render_thread = NULL; This->active = FALSE; + return S_OK; } @@ -135,8 +270,15 @@ static ULONG WINAPI synth_sink_Release(IDirectMusicSynthSink *iface) TRACE("(%p): new ref = %lu\n", This, ref); if (!ref) { + if (This->active) + IDirectMusicSynthSink_Activate(iface, FALSE); if (This->master_clock) IReferenceClock_Release(This->master_clock); + + This->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&This->cs); + CloseHandle(This->stop_event); + free(This); } @@ -240,15 +382,17 @@ static HRESULT WINAPI synth_sink_SetDirectSound(IDirectMusicSynthSink *iface, TRACE("(%p)->(%p, %p)\n", This, dsound, dsound_buffer); - if (dsound_buffer) FIXME("Ignoring IDirectSoundBuffer parameter.\n"); if (This->active) return DMUS_E_SYNTHACTIVE; if (This->dsound) IDirectSound_Release(This->dsound); This->dsound = NULL; + if (This->dsound_buffer) IDirectSoundBuffer_Release(This->dsound_buffer); + This->dsound_buffer = NULL; if (!dsound) return S_OK; if (!This->synth) return DMUS_E_SYNTHNOTCONFIGURED; if ((This->dsound = dsound)) IDirectSound_AddRef(This->dsound); + if ((This->dsound_buffer = dsound_buffer)) IDirectSoundBuffer_AddRef(This->dsound_buffer); return S_OK; } @@ -415,7 +559,10 @@ static HRESULT WINAPI latency_clock_GetTime(IReferenceClock *iface, REFERENCE_TI if (!time) return E_INVALIDARG; if (!This->active) return E_FAIL; + + EnterCriticalSection(&This->cs); *time = This->latency_time; + LeaveCriticalSection(&This->cs); return S_OK; } @@ -464,6 +611,10 @@ HRESULT synth_sink_create(IUnknown **ret_iface) obj->IReferenceClock_iface.lpVtbl = &latency_clock_vtbl; obj->ref = 1; + obj->stop_event = CreateEventW(NULL, FALSE, FALSE, NULL); + InitializeCriticalSection(&obj->cs); + obj->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + TRACE("Created DirectMusicSynthSink %p\n", obj); *ret_iface = (IUnknown *)&obj->IDirectMusicSynthSink_iface; return S_OK; diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 0deaa161a3b..c25150ed9f6 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1294,7 +1294,7 @@ static void test_IDirectMusicSynthSink(void) hr = IDirectMusicSynthSink_Activate(sink, TRUE); ok(hr == DMUS_E_SYNTHACTIVE, "got %#lx\n", hr); ref = get_refcount(synth); - ok(ref == 1, "got %#lx\n", ref); + todo_wine ok(ref == 1, "got %#lx\n", ref); hr = IDirectMusicSynthSink_GetDesiredBufferSize(sink, &size); ok(hr == S_OK, "got %#lx\n", hr); @@ -1328,7 +1328,7 @@ static void test_IDirectMusicSynthSink(void) hr = IDirectMusicSynthSink_Init(sink, NULL); ok(hr == S_OK, "got %#lx\n", hr); ref = get_refcount(synth); - ok(ref == 1, "got %#lx\n", ref); + todo_wine ok(ref == 1, "got %#lx\n", ref); hr = IDirectMusicSynthSink_Activate(sink, TRUE); ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr); From 087bc17cf8987b22e8b76cacbfda4411593e9779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 12 Oct 2023 11:59:36 +0200 Subject: [PATCH 1020/1148] dmsynth: Implement sink rendering to DirectSound buffer. (cherry picked from commit 7274902a3bd79824a43dfa8d11bbfd8c2cead0e4) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synthsink.c | 148 ++++++++++++++++++++++++++++++++++- dlls/dmsynth/tests/dmsynth.c | 2 +- 2 files changed, 147 insertions(+), 3 deletions(-) diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index 906e37e8959..bb77323a70c 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -27,7 +27,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); -#define BUFFER_SUBDIVISIONS 8 +#define BUFFER_SUBDIVISIONS 100 struct synth_sink { @@ -76,6 +76,128 @@ static void synth_sink_get_format(struct synth_sink *This, WAVEFORMATEX *format) } } +static HRESULT synth_sink_write_data(struct synth_sink *sink, IDirectSoundBuffer *buffer, + DSBCAPS *caps, WAVEFORMATEX *format, const void *data, DWORD size) +{ + DWORD write_end, size1, size2, current_pos; + void *data1, *data2; + HRESULT hr; + + TRACE("sink %p, data %p, size %#lx\n", sink, data, size); + + current_pos = sink->written % caps->dwBufferBytes; + + if (sink->written) + { + DWORD play_pos, write_pos; + + if (FAILED(hr = IDirectSoundBuffer_GetCurrentPosition(buffer, &play_pos, &write_pos))) return hr; + + if (current_pos - play_pos <= write_pos - play_pos) + { + ERR("Underrun detected, sink %p, play pos %#lx, write pos %#lx, current pos %#lx!\n", + buffer, play_pos, write_pos, current_pos); + current_pos = write_pos; + } + + write_end = (current_pos + size) % caps->dwBufferBytes; + if (write_end - current_pos >= play_pos - current_pos) return S_FALSE; + } + + if (FAILED(hr = IDirectSoundBuffer_Lock(buffer, current_pos, size, + &data1, &size1, &data2, &size2, 0))) + { + ERR("IDirectSoundBuffer_Lock failed, hr %#lx\n", hr); + return hr; + } + + if (!data) + { + memset(data1, format->wBitsPerSample == 8 ? 128 : 0, size1); + memset(data2, format->wBitsPerSample == 8 ? 128 : 0, size2); + } + else + { + memcpy(data1, data, size1); + data = (char *)data + size1; + memcpy(data2, data, size2); + } + + if (FAILED(hr = IDirectSoundBuffer_Unlock(buffer, data1, size1, data2, size2))) + { + ERR("IDirectSoundBuffer_Unlock failed, hr %#lx\n", hr); + return hr; + } + + sink->written += size; + TRACE("Written size %#lx, total %#lx\n", size, sink->written); + return S_OK; +} + +static HRESULT synth_sink_wait_play_end(struct synth_sink *sink, IDirectSoundBuffer *buffer, + DSBCAPS *caps, WAVEFORMATEX *format, HANDLE buffer_event) +{ + DWORD current_pos, start_pos, play_pos, written, played = 0; + HRESULT hr; + + if (FAILED(hr = IDirectSoundBuffer_GetCurrentPosition(buffer, &start_pos, NULL))) + { + ERR("IDirectSoundBuffer_GetCurrentPosition failed, hr %#lx\n", hr); + return hr; + } + + current_pos = sink->written % caps->dwBufferBytes; + written = current_pos - start_pos + (current_pos < start_pos ? caps->dwBufferBytes : 0); + if (FAILED(hr = synth_sink_write_data(sink, buffer, caps, format, NULL, caps->dwBufferBytes / 2))) return hr; + + for (;;) + { + DWORD ret; + + if (FAILED(hr = IDirectSoundBuffer_GetCurrentPosition(buffer, &play_pos, NULL))) + { + ERR("IDirectSoundBuffer_GetCurrentPosition failed, hr %#lx\n", hr); + return hr; + } + + played += play_pos - start_pos + (play_pos < start_pos ? caps->dwBufferBytes : 0); + if (played >= written) break; + + TRACE("Waiting for EOS, start_pos %#lx, play_pos %#lx, written %#lx, played %#lx\n", + start_pos, play_pos, written, played); + if ((ret = WaitForMultipleObjects(1, &buffer_event, FALSE, INFINITE))) + { + ERR("WaitForMultipleObjects returned %#lx\n", ret); + break; + } + + start_pos = play_pos; + } + + return S_OK; +} + +static HRESULT synth_sink_render_data(struct synth_sink *sink, IDirectMusicSynth *synth, + IDirectSoundBuffer *buffer, WAVEFORMATEX *format, short *samples, DWORD samples_size) +{ + REFERENCE_TIME sample_time; + HRESULT hr; + + if (FAILED(hr = IDirectMusicSynth_Render(synth, samples, samples_size / format->nBlockAlign, + sink->written / format->nBlockAlign))) + ERR("Failed to render synthesizer samples, hr %#lx\n", hr); + + if (FAILED(hr = IDirectMusicSynthSink_SampleToRefTime(&sink->IDirectMusicSynthSink_iface, + (sink->written + samples_size) / format->nBlockAlign, &sample_time))) + ERR("Failed to convert sample position to time, hr %#lx\n", hr); + + EnterCriticalSection(&sink->cs); + sink->latency_time = sample_time; + LeaveCriticalSection(&sink->cs); + + return hr; +} + struct render_thread_params { struct synth_sink *sink; @@ -92,7 +214,10 @@ static DWORD CALLBACK synth_sink_render_thread(void *args) IDirectMusicSynth *synth = params->synth; struct synth_sink *sink = params->sink; IDirectSoundNotify *notify; + WAVEFORMATEX format; HANDLE buffer_event; + DWORD samples_size; + short *samples; HRESULT hr; TRACE("Starting thread, args %p\n", args); @@ -105,6 +230,8 @@ static DWORD CALLBACK synth_sink_render_thread(void *args) ERR("Failed to create buffer event, error %lu\n", GetLastError()); else if (FAILED(hr = IDirectSoundBuffer_GetCaps(buffer, &caps))) ERR("Failed to query sound buffer caps, hr %#lx.\n", hr); + else if (FAILED(hr = IDirectSoundBuffer_GetFormat(buffer, &format, sizeof(format), NULL))) + ERR("Failed to query sound buffer format, hr %#lx.\n", hr); else if (FAILED(hr = IDirectSoundBuffer_QueryInterface(buffer, &IID_IDirectSoundNotify, (void **)¬ify))) ERR("Failed to query IDirectSoundNotify iface, hr %#lx.\n", hr); @@ -126,15 +253,28 @@ static DWORD CALLBACK synth_sink_render_thread(void *args) IDirectSoundNotify_Release(notify); } + samples_size = caps.dwBufferBytes / BUFFER_SUBDIVISIONS; + if (!(samples = malloc(samples_size))) + { + ERR("Failed to allocate memory for samples\n"); + goto done; + } + + if (FAILED(hr = synth_sink_render_data(sink, synth, buffer, &format, samples, samples_size))) + ERR("Failed to render initial buffer data, hr %#lx.\n", hr); if (FAILED(hr = IDirectSoundBuffer_Play(buffer, 0, 0, DSBPLAY_LOOPING))) ERR("Failed to start sound buffer, hr %#lx.\n", hr); SetEvent(params->started_event); - while (hr == S_OK) + while (SUCCEEDED(hr) && SUCCEEDED(hr = synth_sink_write_data(sink, buffer, + &caps, &format, samples, samples_size))) { HANDLE handles[] = {sink->stop_event, buffer_event}; DWORD ret; + if (hr == S_OK) /* if successfully written, render more data */ + hr = synth_sink_render_data(sink, synth, buffer, &format, samples, samples_size); + if (!(ret = WaitForMultipleObjects(ARRAY_SIZE(handles), handles, FALSE, INFINITE)) || ret >= ARRAY_SIZE(handles)) { @@ -150,6 +290,10 @@ static DWORD CALLBACK synth_sink_render_thread(void *args) return hr; } + synth_sink_wait_play_end(sink, buffer, &caps, &format, buffer_event); + free(samples); + +done: IDirectSoundBuffer_Release(buffer); IDirectMusicSynth_Release(synth); CloseHandle(buffer_event); diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index c25150ed9f6..518ea2363e5 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1317,7 +1317,7 @@ static void test_IDirectMusicSynthSink(void) tmp_time = time; hr = IReferenceClock_GetTime(latency_clock, &tmp_time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(tmp_time > time, "got %I64d\n", tmp_time - time); + ok(tmp_time > time, "got %I64d\n", tmp_time - time); ok(tmp_time - time <= 2000000, "got %I64d\n", tmp_time - time); /* setting the clock while active is fine */ From c2e6c3c33cdfdf6d556c73475e253fdd7bec37fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 19:07:07 +0200 Subject: [PATCH 1021/1148] dmsynth: Correctly lookup instrument from the default drum bank. (cherry picked from commit 1728f82a2af115bb72aab8ce676ceb6fa491cc95) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 6a780dcbb59..d5b1cb64198 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -1573,7 +1573,10 @@ static fluid_preset_t *synth_sfont_get_preset(fluid_sfont_t *fluid_sfont, int ba EnterCriticalSection(&synth->cs); LIST_FOR_EACH_ENTRY(instrument, &synth->instruments, struct instrument, entry) - if (instrument->patch == patch) break; + { + if (bank == 128 && instrument->patch == (0x80000000 | patch)) break; + else if (instrument->patch == ((bank << 8) | patch)) break; + } if (&instrument->entry == &synth->instruments) { From a66c5328ced15b84e0e7b6981ec3f2ecb52203ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 17:17:37 +0200 Subject: [PATCH 1022/1148] dmsynth: Avoid using fluid_ prefix for internal helpers. (cherry picked from commit 832a2127cda368a1435863c0262ffe3416195191) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index d5b1cb64198..0c4ae68c66a 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -1305,7 +1305,7 @@ static int synth_preset_get_num(fluid_preset_t *fluid_preset) return instrument->patch; } -static BOOL fluid_gen_from_connection(CONNECTION *conn, UINT *gen) +static BOOL gen_from_connection(const CONNECTION *conn, UINT *gen) { switch (conn->usDestination) { @@ -1336,7 +1336,7 @@ static BOOL fluid_gen_from_connection(CONNECTION *conn, UINT *gen) } } -static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn) +static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, const CONNECTION *conn) { UINT gen; @@ -1345,7 +1345,7 @@ static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn if (conn->usSource == CONN_SRC_NONE) { - if (!fluid_gen_from_connection(conn, &gen)) return FALSE; + if (!gen_from_connection(conn, &gen)) return FALSE; } if (conn->usSource == CONN_SRC_KEYNUMBER) { @@ -1394,7 +1394,7 @@ static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn return TRUE; } -static BOOL fluid_source_from_connection(USHORT source, USHORT transform, UINT *fluid_source, UINT *fluid_flags) +static BOOL mod_from_connection(USHORT source, USHORT transform, UINT *fluid_source, UINT *fluid_flags) { UINT flags = FLUID_MOD_GC; if (source >= CONN_SRC_CC1 && source <= CONN_SRC_CC1 + 0x7f) @@ -1428,8 +1428,8 @@ static BOOL fluid_source_from_connection(USHORT source, USHORT transform, UINT * return TRUE; } -static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn, UINT src1, UINT flags1, - UINT src2, UINT flags2) +static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, const CONNECTION *conn, + UINT src1, UINT flags1, UINT src2, UINT flags2) { fluid_mod_t *mod; UINT gen = -1; @@ -1456,7 +1456,7 @@ static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn flags2 = 0; } - if (gen == -1 && !fluid_gen_from_connection(conn, &gen)) return FALSE; + if (gen == -1 && !gen_from_connection(conn, &gen)) return FALSE; if (!(mod = new_fluid_mod())) return FALSE; fluid_mod_set_source1(mod, src1, flags1); @@ -1468,35 +1468,28 @@ static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn return TRUE; } -static void fluid_voice_add_articulation(fluid_voice_t *fluid_voice, struct articulation *articulation) +static void add_voice_connections(fluid_voice_t *fluid_voice, const CONNECTIONLIST *list, + const CONNECTION *connections) { UINT i; - for (i = 0; i < articulation->list.cConnections; i++) + for (i = 0; i < list->cConnections; i++) { UINT src1 = FLUID_MOD_NONE, flags1 = 0, src2 = FLUID_MOD_NONE, flags2 = 0; - CONNECTION *conn = articulation->connections + i; + const CONNECTION *conn = connections + i; if (set_gen_from_connection(fluid_voice, conn)) continue; - if (!fluid_source_from_connection(conn->usSource, (conn->usTransform >> 10) & 0x3f, + if (!mod_from_connection(conn->usSource, (conn->usTransform >> 10) & 0x3f, &src1, &flags1)) continue; - if (!fluid_source_from_connection(conn->usControl, (conn->usControl >> 4) & 0x3f, + if (!mod_from_connection(conn->usControl, (conn->usControl >> 4) & 0x3f, &src2, &flags2)) continue; add_mod_from_connection(fluid_voice, conn, src1, flags1, src2, flags2); } } -static void fluid_voice_add_articulations(fluid_voice_t *fluid_voice, struct list *list) -{ - struct articulation *articulation; - - LIST_FOR_EACH_ENTRY(articulation, list, struct articulation, entry) - fluid_voice_add_articulation(fluid_voice, articulation); -} - static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *fluid_synth, int chan, int key, int vel) { struct instrument *instrument = fluid_preset_get_data(fluid_preset); @@ -1511,6 +1504,7 @@ static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *flui LIST_FOR_EACH_ENTRY(region, &instrument->regions, struct region, entry) { + struct articulation *articulation; struct wave *wave = region->wave; if (key < region->key_range.usLow || key > region->key_range.usHigh) continue; @@ -1538,8 +1532,10 @@ static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *flui return FLUID_FAILED; } - fluid_voice_add_articulations(fluid_voice, &instrument->articulations); - fluid_voice_add_articulations(fluid_voice, ®ion->articulations); + LIST_FOR_EACH_ENTRY(articulation, &instrument->articulations, struct articulation, entry) + add_voice_connections(fluid_voice, &articulation->list, articulation->connections); + LIST_FOR_EACH_ENTRY(articulation, ®ion->articulations, struct articulation, entry) + add_voice_connections(fluid_voice, &articulation->list, articulation->connections); fluid_synth_start_voice(synth->fluid_synth, fluid_voice); return FLUID_OK; } From 4859b2c99f9e1a36819fd2029ac90af3462f737d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 18 Oct 2023 11:54:01 +0200 Subject: [PATCH 1023/1148] dmsynth: Fix DLS2 to FluidSynth conversion for CONN_SRC_CCx. (cherry picked from commit 0eaa06b51d9acee7a7fdf2ccb4ad63a6ac35f156) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 0c4ae68c66a..d37d095d206 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -36,6 +36,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); #define ROUND_ADDR(addr, mask) ((void *)((UINT_PTR)(addr) & ~(UINT_PTR)(mask))) +#define CONN_SRC_CC 0x0080 #define CONN_SRC_CC2 0x0082 #define CONN_SRC_RPN0 0x0100 @@ -1397,9 +1398,9 @@ static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, const CONNECTION static BOOL mod_from_connection(USHORT source, USHORT transform, UINT *fluid_source, UINT *fluid_flags) { UINT flags = FLUID_MOD_GC; - if (source >= CONN_SRC_CC1 && source <= CONN_SRC_CC1 + 0x7f) + if (source >= CONN_SRC_CC && source <= CONN_SRC_CC + 0x7f) { - *fluid_source = source; + *fluid_source = source - CONN_SRC_CC; flags = FLUID_MOD_CC; } else switch (source) From 68c129313377d85d5e4a093acfdd522ecaec5cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 18 Oct 2023 11:54:39 +0200 Subject: [PATCH 1024/1148] dmsynth: Fix FluidSynth generators for direct connections. (cherry picked from commit c7ca1643f752f2f5f85b52ede9c4cda8e959b564) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index d37d095d206..2faa6149617 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -1348,7 +1348,7 @@ static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, const CONNECTION { if (!gen_from_connection(conn, &gen)) return FALSE; } - if (conn->usSource == CONN_SRC_KEYNUMBER) + else if (conn->usSource == CONN_SRC_KEYNUMBER) { switch (conn->usDestination) { From 6909f86942adaaf367380153fc9cc8f4b51ea8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 19:44:37 +0200 Subject: [PATCH 1025/1148] dmsynth: Convert modulator values from DLS2 to SF2 convention. (cherry picked from commit 7f629f7f542a14cdd0a58ac2af8bb26d7d0d2d32) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 2faa6149617..12dda640de4 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -1339,6 +1339,7 @@ static BOOL gen_from_connection(const CONNECTION *conn, UINT *gen) static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, const CONNECTION *conn) { + double value; UINT gen; if (conn->usControl != CONN_SRC_NONE) return FALSE; @@ -1391,7 +1392,16 @@ static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, const CONNECTION return FALSE; } - fluid_voice_gen_set(fluid_voice, gen, conn->lScale); + /* SF2 / FluidSynth use 0.1% as "Sustain Level" unit, DLS2 uses percent, meaning is also reversed */ + if (gen == GEN_MODENVSUSTAIN || gen == GEN_VOLENVSUSTAIN) value = 1000 - conn->lScale * 10 / 65536.; + /* FIXME: SF2 and FluidSynth use 1200 * log2(f / 8.176) for absolute freqs, + * whereas DLS2 uses (1200 * log2(f / 440.) + 6900) * 65536. The values + * are very close but not strictly identical and we may need a conversion. + */ + else if (conn->lScale == 0x80000000) value = -32768; + else value = conn->lScale / 65536.; + fluid_voice_gen_set(fluid_voice, gen, value); + return TRUE; } @@ -1434,6 +1444,7 @@ static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, const CONNECTION { fluid_mod_t *mod; UINT gen = -1; + double value; switch (MAKELONG(conn->usSource, conn->usDestination)) { @@ -1463,7 +1474,17 @@ static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, const CONNECTION fluid_mod_set_source1(mod, src1, flags1); fluid_mod_set_source2(mod, src2, flags2); fluid_mod_set_dest(mod, gen); - fluid_mod_set_amount(mod, conn->lScale); + + /* SF2 / FluidSynth use 0.1% as "Sustain Level" unit, DLS2 uses percent, meaning is also reversed */ + if (gen == GEN_MODENVSUSTAIN || gen == GEN_VOLENVSUSTAIN) value = 1000 - conn->lScale * 10 / 65536.; + /* FIXME: SF2 and FluidSynth use 1200 * log2(f / 8.176) for absolute freqs, + * whereas DLS2 uses (1200 * log2(f / 440.) + 6900) * 65536. The values + * are very close but not strictly identical and we may need a conversion. + */ + else if (conn->lScale == 0x80000000) value = -32768; + else value = conn->lScale / 65536.; + fluid_mod_set_amount(mod, value); + fluid_voice_add_mod(fluid_voice, mod, FLUID_VOICE_OVERWRITE); return TRUE; From e4df1b494706f3f021a3be8ce60437d0a4e967a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 19:44:37 +0200 Subject: [PATCH 1026/1148] dmsynth: Set default modulators according to the DLS2 spec. (cherry picked from commit 4106217718248369943932a7e048c2bfe281b27b) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 210 ++++++++++++++++++++++++ libs/fluidsynth/src/synth/fluid_gen.c | 4 + libs/fluidsynth/src/synth/fluid_synth.c | 2 + 3 files changed, 216 insertions(+) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 12dda640de4..c6e5786c6a9 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -31,6 +31,7 @@ #include "dls2.h" #include +#include WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); @@ -39,10 +40,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); #define CONN_SRC_CC 0x0080 #define CONN_SRC_CC2 0x0082 #define CONN_SRC_RPN0 0x0100 +#define CONN_SRC_RPN1 0x0101 +#define CONN_SRC_RPN2 0x0102 #define CONN_TRN_BIPOLAR (1<<4) #define CONN_TRN_INVERT (1<<5) +#define CONN_TRANSFORM(src, ctrl, dst) (((src) & 0x3f) << 10) | (((ctrl) & 0x3f) << 4) | ((dst) & 0xf) + static const char *debugstr_conn_src(UINT src) { switch (src) @@ -67,6 +72,7 @@ static const char *debugstr_conn_src(UINT src) case CONN_SRC_CC2: return "SRC_CC2"; case CONN_SRC_RPN0: return "SRC_RPN0"; + case CONN_SRC_RPN2: return "SRC_RPN2"; } return wine_dbg_sprintf("%#x", src); @@ -424,6 +430,52 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) return ref; } +static void synth_reset_default_values(struct synth *This) +{ + BYTE chan; + + fluid_synth_system_reset(This->fluid_synth); + + for (chan = 0; chan < 0x10; chan++) + { + fluid_synth_cc(This->fluid_synth, chan | 0xe0 /* PITCH_BEND */, 0, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xd0 /* CHANNEL_PRESSURE */, 0, 0); + + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x01 /* MODULATION_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x21 /* MODULATION_LSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x07 /* VOLUME_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x27 /* VOLUME_LSB */, 100); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x0a /* PAN_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x0a /* PAN_LSB */, 64); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x0b /* EXPRESSION_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x2b /* EXPRESSION_LSB */, 127); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x40 /* SUSTAIN_SWITCH */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x5b /* EFFECTS_DEPTH1 / Reverb Send */, 40); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x5d /* EFFECTS_DEPTH3 / Chorus Send */, 0); + + /* RPN0 Pitch Bend Range */ + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x64 /* RPN_LSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x65 /* RPN_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x06 /* DATA_ENTRY_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x26 /* DATA_ENTRY_LSB */, 2); + + /* RPN1 Fine Tuning */ + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x64 /* RPN_LSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x65 /* RPN_MSB */, 1); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x06 /* DATA_ENTRY_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x26 /* DATA_ENTRY_LSB */, 0); + + /* RPN2 Coarse Tuning */ + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x64 /* RPN_LSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x65 /* RPN_MSB */, 1); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x06 /* DATA_ENTRY_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x26 /* DATA_ENTRY_LSB */, 0); + + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x64 /* RPN_LSB */, 127); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x65 /* RPN_MSB */, 127); + } +} + static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *params) { struct synth *This = impl_from_IDirectMusicSynth8(iface); @@ -514,6 +566,7 @@ static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *par if (!(This->fluid_synth = new_fluid_synth(This->fluid_settings))) return E_OUTOFMEMORY; if ((id = fluid_synth_add_sfont(This->fluid_synth, This->fluid_sfont)) == FLUID_FAILED) WARN("Failed to add fluid_sfont to fluid_synth\n"); + synth_reset_default_values(This); This->params = actual; This->open = TRUE; @@ -1512,6 +1565,162 @@ static void add_voice_connections(fluid_voice_t *fluid_voice, const CONNECTIONLI } } +static void set_default_voice_connections(fluid_voice_t *fluid_voice) +{ + const CONNECTION connections[] = + { +#define ABS_PITCH_HZ(f) (LONG)((1200 * log2((f) / 440.) + 6900) * 65536) +#define ABS_TIME_MS(x) ((x) ? (LONG)(1200 * log2((x) / 1000.) * 65536) : 0x80000000) +#define REL_PITCH_CTS(x) ((x) * 65536) +#define REL_GAIN_DB(x) ((-x) * 10 * 65536) + + /* Modulator LFO */ + {.usDestination = CONN_DST_LFO_FREQUENCY, .lScale = ABS_PITCH_HZ(5)}, + {.usDestination = CONN_DST_LFO_STARTDELAY, .lScale = ABS_TIME_MS(10)}, + + /* Vibrato LFO */ + {.usDestination = CONN_DST_VIB_FREQUENCY, .lScale = ABS_PITCH_HZ(5)}, + {.usDestination = CONN_DST_VIB_STARTDELAY, .lScale = ABS_TIME_MS(10)}, + /* Vol EG */ + {.usDestination = CONN_DST_EG1_DELAYTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG1_ATTACKTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG1_HOLDTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG1_DECAYTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG1_SUSTAINLEVEL, .lScale = 100 * 65536}, + {.usDestination = CONN_DST_EG1_RELEASETIME, .lScale = ABS_TIME_MS(0)}, + /* FIXME: {.usDestination = CONN_DST_EG1_SHUTDOWNTIME, .lScale = ABS_TIME_MS(15)}, */ + {.usSource = CONN_SRC_KEYONVELOCITY, .usDestination = CONN_DST_EG1_ATTACKTIME, .lScale = 0}, + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_EG1_DECAYTIME, .lScale = 0}, + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_EG1_HOLDTIME, .lScale = 0}, + + /* Modulator EG */ + {.usDestination = CONN_DST_EG2_DELAYTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG2_ATTACKTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG2_HOLDTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG2_DECAYTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG2_SUSTAINLEVEL, .lScale = 100 * 65536}, + {.usDestination = CONN_DST_EG2_RELEASETIME, .lScale = ABS_TIME_MS(0)}, + {.usSource = CONN_SRC_KEYONVELOCITY, .usDestination = CONN_DST_EG2_ATTACKTIME, .lScale = 0}, + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_EG2_DECAYTIME, .lScale = 0}, + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_EG2_HOLDTIME, .lScale = 0}, + + /* Key number generator */ + /* FIXME: This doesn't seem to be supported by FluidSynth, there's also no MIDINOTE source */ + /* {.usSource = CONN_SRC_MIDINOTE?, .usDestination = CONN_DST_KEYNUMBER, .lScale = REL_PITCH_CTS(12800)}, */ + { + .usSource = CONN_SRC_RPN2, .usDestination = CONN_DST_KEYNUMBER, .lScale = REL_PITCH_CTS(6400), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + + /* Filter */ + {.usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0x7fffffff}, + {.usDestination = CONN_DST_FILTER_Q, .lScale = 0}, + { + .usSource = CONN_SRC_LFO, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0, + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CC1, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0, + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CHANNELPRESSURE, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0, + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + {.usSource = CONN_SRC_EG2, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0}, + {.usSource = CONN_SRC_KEYONVELOCITY, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0}, + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0}, + + /* Gain */ + { + .usSource = CONN_SRC_LFO, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CC1, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CHANNELPRESSURE, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_KEYONVELOCITY, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(-96), + .usTransform = CONN_TRANSFORM(CONN_TRN_CONCAVE | CONN_TRN_INVERT, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_CC7, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(-96), + .usTransform = CONN_TRANSFORM(CONN_TRN_CONCAVE | CONN_TRN_INVERT, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_CC11, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(-96), + .usTransform = CONN_TRANSFORM(CONN_TRN_CONCAVE | CONN_TRN_INVERT, CONN_TRN_NONE, CONN_TRN_NONE), + }, + + /* Pitch */ + {.usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0)}, + { + .usSource = CONN_SRC_PITCHWHEEL, .usControl = CONN_SRC_RPN0, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(12800), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + /* FIXME: key to pitch default should be 12800 but FluidSynth GEN_PITCH modulator doesn't work as expected */ + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0)}, + { + .usSource = CONN_SRC_RPN1, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(100), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_VIBRATO, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_VIBRATO, .usControl = CONN_SRC_CC1, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_VIBRATO, .usControl = CONN_SRC_CHANNELPRESSURE, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CC1, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CHANNELPRESSURE, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + {.usSource = CONN_SRC_EG2, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0)}, + + /* Output */ + {.usDestination = CONN_DST_PAN, .lScale = 0}, + { + .usSource = CONN_SRC_CC10, .usDestination = CONN_DST_PAN, .lScale = 508 * 65536, + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + {.usSource = CONN_SRC_CC91, .usDestination = CONN_DST_REVERB, .lScale = 1000 * 65536}, + {.usDestination = CONN_DST_REVERB, .lScale = 0}, + {.usSource = CONN_SRC_CC93, .usDestination = CONN_DST_CHORUS, .lScale = 1000 * 65536}, + {.usDestination = CONN_DST_CHORUS, .lScale = 0}, + +#undef ABS_PITCH_HZ +#undef ABS_TIME_MS +#undef REL_PITCH_CTS +#undef REL_GAIN_DB + }; + CONNECTIONLIST list = {.cbSize = sizeof(CONNECTIONLIST), .cConnections = ARRAY_SIZE(connections)}; + + fluid_voice_gen_set(fluid_voice, GEN_KEYNUM, -1.); + fluid_voice_gen_set(fluid_voice, GEN_VELOCITY, -1.); + fluid_voice_gen_set(fluid_voice, GEN_SCALETUNE, 100.0); + fluid_voice_gen_set(fluid_voice, GEN_OVERRIDEROOTKEY, -1.); + + add_voice_connections(fluid_voice, &list, connections); +} + static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *fluid_synth, int chan, int key, int vel) { struct instrument *instrument = fluid_preset_get_data(fluid_preset); @@ -1554,6 +1763,7 @@ static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *flui return FLUID_FAILED; } + set_default_voice_connections(fluid_voice); LIST_FOR_EACH_ENTRY(articulation, &instrument->articulations, struct articulation, entry) add_voice_connections(fluid_voice, &articulation->list, articulation->connections); LIST_FOR_EACH_ENTRY(articulation, ®ion->articulations, struct articulation, entry) diff --git a/libs/fluidsynth/src/synth/fluid_gen.c b/libs/fluidsynth/src/synth/fluid_gen.c index 2ce2b0f74b5..232ea294fd9 100644 --- a/libs/fluidsynth/src/synth/fluid_gen.c +++ b/libs/fluidsynth/src/synth/fluid_gen.c @@ -109,7 +109,11 @@ fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel) gen[i].flags = GEN_UNUSED; gen[i].mod = 0.0; gen[i].nrpn = (channel == NULL) ? 0.0 : fluid_channel_get_gen(channel, i); +#if 0 /* unused in Wine */ gen[i].val = fluid_gen_info[i].def; +#else + gen[i].val = 0.0; +#endif } } diff --git a/libs/fluidsynth/src/synth/fluid_synth.c b/libs/fluidsynth/src/synth/fluid_synth.c index 3b83e3d8b73..0580ed271f6 100644 --- a/libs/fluidsynth/src/synth/fluid_synth.c +++ b/libs/fluidsynth/src/synth/fluid_synth.c @@ -300,6 +300,7 @@ fluid_synth_init(void) init_dither(); +#if 0 /* unused in Wine */ /* custom_breath2att_mod is not a default modulator specified in SF2.01. it is intended to replace default_vel2att_mod on demand using API fluid_set_breath_mode() or command shell setbreathmode. @@ -480,6 +481,7 @@ fluid_synth_init(void) fluid_mod_set_dest(&custom_balance_mod, GEN_CUSTOM_BALANCE); /* Destination: stereo balance */ /* Amount: 96 dB of attenuation (on the opposite channel) */ fluid_mod_set_amount(&custom_balance_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ +#endif /* unused in Wine */ #if defined(LIBINSTPATCH_SUPPORT) /* defer libinstpatch init to fluid_instpatch.c to avoid #include "libinstpatch.h" */ From 621bcbccab63ad5c6b1c00f8371c6d17250f93f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 12 Oct 2023 22:18:06 +0200 Subject: [PATCH 1027/1148] dmband: Download segment tracks if performance auto-download is set. (cherry picked from commit 10a1e533c3de65ae29078be607148531a22e654c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/bandtrack.c | 4 ++-- dlls/dmime/dmime_private.h | 2 +- dlls/dmime/performance.c | 2 +- dlls/dmime/segmentstate.c | 17 ++++++++++++++--- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index e0acb6ff62d..5229204b284 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -234,7 +234,7 @@ static HRESULT WINAPI band_track_SetParam(IDirectMusicTrack8 *iface, REFGUID typ band_connect_to_collection(entry->band, param); } else if (IsEqualGUID(type, &GUID_Disable_Auto_Download)) - FIXME("GUID_Disable_Auto_Download not handled yet\n"); + This->header.bAutoDownload = FALSE; else if (IsEqualGUID(type, &GUID_Download)) FIXME("GUID_Download not handled yet\n"); else if (IsEqualGUID(type, &GUID_DownloadToAudioPath)) @@ -265,7 +265,7 @@ static HRESULT WINAPI band_track_SetParam(IDirectMusicTrack8 *iface, REFGUID typ IDirectMusicPerformance_Release(performance); } else if (IsEqualGUID(type, &GUID_Enable_Auto_Download)) - FIXME("GUID_Enable_Auto_Download not handled yet\n"); + This->header.bAutoDownload = TRUE; else if (IsEqualGUID(type, &GUID_IDirectMusicBand)) FIXME("GUID_IDirectMusicBand not handled yet\n"); else if (IsEqualGUID(type, &GUID_StandardMIDIFile)) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index c1f777f3949..7f1937eaa0f 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -75,7 +75,7 @@ extern void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath*,IDirectSo extern HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, IDirectMusicPerformance *performance, IDirectMusicSegmentState **ret_iface); extern HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance *performance); -extern HRESULT segment_state_end_play(IDirectMusicSegmentState *iface); +extern HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance *performance); extern HRESULT wave_track_create_from_chunk(IStream *stream, struct chunk_entry *parent, IDirectMusicTrack8 **ret_iface); diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index dc6ef575713..ea2a3cc52dd 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1740,7 +1740,7 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_SEGMENT) && notif->dwNotificationOption == DMUS_NOTIFICATION_SEGEND) { - if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)notif->punkUser))) + if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)notif->punkUser, performance))) WARN("Failed to end segment state %p, hr %#lx\n", notif->punkUser, hr); } diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index 1cb9daac69a..c43d833fedd 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -51,6 +51,7 @@ struct segment_state MUSIC_TIME start_time; MUSIC_TIME start_point; MUSIC_TIME end_point; + BOOL auto_download; struct list tracks; }; @@ -103,7 +104,7 @@ static ULONG WINAPI segment_state_Release(IDirectMusicSegmentState8 *iface) if (!ref) { - segment_state_end_play((IDirectMusicSegmentState *)iface); + segment_state_end_play((IDirectMusicSegmentState *)iface, NULL); if (This->segment) IDirectMusicSegment_Release(This->segment); free(This); } @@ -224,8 +225,13 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time This->segment = segment; IDirectMusicSegment_AddRef(This->segment); + if (SUCCEEDED(hr = IDirectMusicPerformance_GetGlobalParam(performance, &GUID_PerfAutoDownload, + &This->auto_download, sizeof(This->auto_download))) && This->auto_download) + hr = IDirectMusicSegment_SetParam(segment, &GUID_DownloadToAudioPath, -1, + DMUS_SEG_ALLTRACKS, 0, performance); + This->start_time = start_time; - hr = IDirectMusicSegment_GetStartPoint(segment, &This->start_point); + if (SUCCEEDED(hr)) hr = IDirectMusicSegment_GetStartPoint(segment, &This->start_point); if (SUCCEEDED(hr)) hr = IDirectMusicSegment_GetLength(segment, &This->end_point); for (i = 0; SUCCEEDED(hr); i++) @@ -283,10 +289,11 @@ HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerforma return hr; } -HRESULT segment_state_end_play(IDirectMusicSegmentState *iface) +HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance *performance) { struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); struct track_entry *entry, *next; + HRESULT hr; LIST_FOR_EACH_ENTRY_SAFE(entry, next, &This->tracks, struct track_entry, entry) { @@ -294,5 +301,9 @@ HRESULT segment_state_end_play(IDirectMusicSegmentState *iface) track_entry_destroy(entry); } + if (performance && This->auto_download && FAILED(hr = IDirectMusicSegment_SetParam(This->segment, + &GUID_UnloadFromAudioPath, -1, DMUS_SEG_ALLTRACKS, 0, performance))) + ERR("Failed to unload segment from performance, hr %#lx\n", hr); + return S_OK; } From 125621d3e288f6be2ea1f8bd5c8e869459a3f5a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 19:06:34 +0200 Subject: [PATCH 1028/1148] dmband: Set DMUS_PATCH_PMSG bank LSB/MSB from instrument patch. (cherry picked from commit e9fdbe4d5524db1748bc4e7222ba41477aaa918e) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index 456bb20143e..4c593741516 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -496,6 +496,7 @@ HRESULT band_send_messages(IDirectMusicBand *iface, IDirectMusicPerformance *per LIST_FOR_EACH_ENTRY_REV(entry, &This->instruments, struct instrument_entry, entry) { + DWORD patch = entry->instrument.dwPatch; DMUS_PATCH_PMSG *msg; if (FAILED(hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(*msg), @@ -510,6 +511,13 @@ HRESULT band_send_messages(IDirectMusicBand *iface, IDirectMusicPerformance *per msg->dwGroupID = 1; msg->byInstrument = entry->instrument.dwPatch; + msg->byInstrument = patch & 0x7F; + patch >>= 8; + msg->byLSB = patch & 0x7f; + patch >>= 8; + msg->byMSB = patch & 0x7f; + patch >>= 8; + if (FAILED(hr = IDirectMusicGraph_StampPMsg(graph, (DMUS_PMSG *)msg)) || FAILED(hr = IDirectMusicPerformance_SendPMsg(performance, (DMUS_PMSG *)msg))) { From a4ca3a4655c0c0e16677a61b299e49c16d2beddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 12 Oct 2023 22:18:06 +0200 Subject: [PATCH 1029/1148] dmime: Only use index if group is set in IDirectMusicSegment_SetParam. (cherry picked from commit 12d3ccb495dbe9f95625ba84a21be655744c6269) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index fc277ba73f9..280abaa004c 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -403,8 +403,11 @@ static HRESULT WINAPI segment_SetParam(IDirectMusicSegment8 *iface, REFGUID type LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) { - if (group != -1 && !(group & entry->dwGroupBits)) continue; - if (index != DMUS_SEG_ALLTRACKS && index--) continue; + if (group != -1) + { + if (!(group & entry->dwGroupBits)) continue; + if (index != DMUS_SEG_ALLTRACKS && index--) continue; + } hr = IDirectMusicTrack_IsParamSupported(entry->pTrack, type); if (hr == DMUS_E_TYPE_UNSUPPORTED) continue; From 238720b17321fb1e9d0aeeb0829554d55c445b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 12 Oct 2023 22:46:06 +0200 Subject: [PATCH 1030/1148] dmime: Don't interrupt track iteration if SetParam failed. (cherry picked from commit 5a66857fb483e9bc5c2171364554d3731e09de67) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 280abaa004c..d7402911f00 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -409,11 +409,9 @@ static HRESULT WINAPI segment_SetParam(IDirectMusicSegment8 *iface, REFGUID type if (index != DMUS_SEG_ALLTRACKS && index--) continue; } - hr = IDirectMusicTrack_IsParamSupported(entry->pTrack, type); - if (hr == DMUS_E_TYPE_UNSUPPORTED) continue; - - hr = IDirectMusicTrack_SetParam(entry->pTrack, type, music_time, param); - if (FAILED(hr)) break; + if (SUCCEEDED(hr = IDirectMusicTrack_IsParamSupported(entry->pTrack, type)) + && FAILED(hr = IDirectMusicTrack_SetParam(entry->pTrack, type, music_time, param))) + WARN("SetParam for track %p failed, hr %#lx\n", entry->pTrack, hr); } return S_OK; From aac7f71f6a6ab5e8b7b27b2af07a3e63348395be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 20:12:03 +0200 Subject: [PATCH 1031/1148] dmime: Adjust MIDI message time with DMUS_NOTE_PMSG nOffset. (cherry picked from commit 65e388137c6117aaf8f99cef808d32afa29c7639) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index ea2a3cc52dd..8ea01b168d7 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1700,6 +1700,7 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, { DMUS_NOTE_PMSG *note = (DMUS_NOTE_PMSG *)msg; + msg->mtTime += note->nOffset; if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, 0x90 /* NOTE_ON */, note->bMidiValue, note->bVelocity))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); From c1b23e5d3818b4e8f106f07f910df4e7051e3e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 20:03:19 +0200 Subject: [PATCH 1032/1148] dmusic: Use a dmusic_midi.h header for MIDI messages. (cherry picked from commit 2c4fc0adcf66e47455e01651e464b71c8d244b0c) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 11 ++++++----- dlls/dmsynth/Makefile.in | 1 + dlls/dmsynth/synth.c | 9 +++++---- dlls/dmusic/dmusic_midi.h | 40 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 dlls/dmusic/dmusic_midi.h diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 8ea01b168d7..f120cc6f0a9 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -19,6 +19,7 @@ */ #include "dmime_private.h" +#include "dmusic_midi.h" #include "wine/rbtree.h" WINE_DEFAULT_DEBUG_CHANNEL(dmime); @@ -1702,12 +1703,12 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, msg->mtTime += note->nOffset; if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, - 0x90 /* NOTE_ON */, note->bMidiValue, note->bVelocity))) + MIDI_NOTE_ON, note->bMidiValue, note->bVelocity))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); msg->mtTime += note->mtDuration; if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE, - 0x80 /* NOTE_OFF */, note->bMidiValue, 0))) + MIDI_NOTE_OFF, note->bMidiValue, 0))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); break; @@ -1718,15 +1719,15 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, DMUS_PATCH_PMSG *patch = (DMUS_PATCH_PMSG *)msg; if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, - 0xb0 /* Control Change */, 0x00 /* CC: Bank MSB */, patch->byMSB))) + MIDI_CONTROL_CHANGE, MIDI_CC_BANK_MSB, patch->byMSB))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, - 0xb0 /* Control Change */, 0x20 /* CC: Bank LSB */, patch->byLSB))) + MIDI_CONTROL_CHANGE, MIDI_CC_BANK_LSB, patch->byLSB))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, - 0xc0 /* Program Change */, patch->byInstrument, 0))) + MIDI_PROGRAM_CHANGE, patch->byInstrument, 0))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); break; diff --git a/dlls/dmsynth/Makefile.in b/dlls/dmsynth/Makefile.in index 7c6d2db721b..85cac5912ac 100644 --- a/dlls/dmsynth/Makefile.in +++ b/dlls/dmsynth/Makefile.in @@ -1,6 +1,7 @@ MODULE = dmsynth.dll IMPORTS = $(FLUIDSYNTH_PE_LIBS) dxguid uuid ole32 advapi32 user32 EXTRAINCL = $(FLUIDSYNTH_PE_CFLAGS) +PARENTSRC = ../dmusic C_SRCS = \ dmsynth_main.c \ diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index c6e5786c6a9..c3e0c90880e 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -28,6 +28,7 @@ #include "dmksctrl.h" #include "dmsynth_private.h" +#include "dmusic_midi.h" #include "dls2.h" #include @@ -1066,16 +1067,16 @@ static HRESULT WINAPI synth_Render(IDirectMusicSynth8 *iface, short *buffer, switch (status) { - case 0x80: + case MIDI_NOTE_OFF: fluid_synth_noteoff(This->fluid_synth, chan, event->midi[1]); break; - case 0x90: + case MIDI_NOTE_ON: fluid_synth_noteon(This->fluid_synth, chan, event->midi[1], event->midi[2]); break; - case 0xb0: + case MIDI_CONTROL_CHANGE: fluid_synth_cc(This->fluid_synth, chan, event->midi[1], event->midi[2]); break; - case 0xc0: + case MIDI_PROGRAM_CHANGE: fluid_synth_program_change(This->fluid_synth, chan, event->midi[1]); break; default: diff --git a/dlls/dmusic/dmusic_midi.h b/dlls/dmusic/dmusic_midi.h new file mode 100644 index 00000000000..e545bcaf8cb --- /dev/null +++ b/dlls/dmusic/dmusic_midi.h @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Rémi Bernon for CodeWeavers + * + * This program 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 program 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 program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "stdarg.h" +#include "stddef.h" + +#include "windef.h" +#include "winbase.h" + +enum midi_message +{ + MIDI_NOTE_OFF = 0x80, + MIDI_NOTE_ON = 0x90, + MIDI_KEY_PRESSURE = 0xa0, + MIDI_CONTROL_CHANGE = 0xb0, + MIDI_PROGRAM_CHANGE = 0xc0, + MIDI_CHANNEL_PRESSURE = 0xd0, + MIDI_PITCH_BEND_CHANGE = 0xe0, +}; + +enum midi_control +{ + MIDI_CC_BANK_MSB = 0x00, + MIDI_CC_BANK_LSB = 0x20, +}; From 0b67f352fe3a958c1aafbc0244d26061a90eb622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 19:07:48 +0200 Subject: [PATCH 1033/1148] dmime: Translate some DMUS_CURVE_PMSG messages to MIDI. (cherry picked from commit 8a4989f3a63827a56ac03f34c31762e3689f9ddf) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index f120cc6f0a9..815e8295ed8 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1714,6 +1714,27 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, break; } + case DMUS_PMSGT_CURVE: + { + DMUS_CURVE_PMSG *curve = (DMUS_CURVE_PMSG *)msg; + + msg->mtTime += curve->nOffset; + switch (curve->dwType) + { + case DMUS_CURVET_CCCURVE: + if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, + MIDI_CONTROL_CHANGE, curve->bCCData, curve->nStartValue))) + WARN("Failed to translate message to MIDI, hr %#lx\n", hr); + break; + case DMUS_CURVET_RPNCURVE: + case DMUS_CURVET_NRPNCURVE: + FIXME("Unhandled curve type %#lx\n", curve->dwType); + break; + } + + break; + } + case DMUS_PMSGT_PATCH: { DMUS_PATCH_PMSG *patch = (DMUS_PATCH_PMSG *)msg; From 71a67adf7e99552f5c6e45d5b573a128df0ab764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 16 Oct 2023 20:16:20 +0200 Subject: [PATCH 1034/1148] dmime: Remove FIXME from methods now mostly implemented. (cherry picked from commit 6631e6bc2d4a0639824f8ca563b827ff952c3488) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 815e8295ed8..a8fcd869331 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -468,7 +468,7 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS struct message *message, *next; HRESULT hr; - FIXME("(%p, %p): semi-stub\n", This, msg); + TRACE("(%p, %p)\n", This, msg); if (!(message = message_from_DMUS_PMSG(msg))) return E_POINTER; if (!This->dmusic) return DMUS_E_NO_MASTER_CLOCK; @@ -519,9 +519,11 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, REFERENCE_TIME *time) { + static int once; struct performance *This = impl_from_IDirectMusicPerformance8(iface); - FIXME("(%p, %ld, %p): semi-stub\n", This, music_time, time); + if (!once++) FIXME("(%p, %ld, %p): semi-stub\n", This, music_time, time); + else TRACE("(%p, %ld, %p)\n", This, music_time, time); if (!time) return E_POINTER; *time = 0; @@ -538,9 +540,11 @@ static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 static HRESULT WINAPI performance_ReferenceToMusicTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME time, MUSIC_TIME *music_time) { + static int once; struct performance *This = impl_from_IDirectMusicPerformance8(iface); - FIXME("(%p, %I64d, %p): semi-stub\n", This, time, music_time); + if (!once++) FIXME("(%p, %I64d, %p): semi-stub\n", This, time, music_time); + else TRACE("(%p, %I64d, %p)\n", This, time, music_time); if (!music_time) return E_POINTER; *music_time = 0; @@ -1657,7 +1661,7 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, struct message *message = message_from_DMUS_PMSG(msg); HRESULT hr; - FIXME("(%p, %p, %p): semi-stub\n", This, performance, msg); + TRACE("(%p, %p, %p)\n", This, performance, msg); switch (msg->dwType) { From e3935b5b0256a48c87d744a746dbd0cf8850aaef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 11:23:16 +0200 Subject: [PATCH 1035/1148] dmime: Avoid crashing when purging notification messages. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55792 (cherry picked from commit 4d0c3d89a46029697cbc5414fc882cae3136b3d4) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index a8fcd869331..a2e611523a6 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1783,6 +1783,7 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, do { previous = LIST_ENTRY(list_head(&This->notifications), struct message, entry); + if (This->notification_timeout <= 0) break; /* negative values may be used to keep everything */ if (message->msg.rtTime - previous->msg.rtTime <= This->notification_timeout) break; list_remove(&previous->entry); list_init(&previous->entry); From 047c3fdae558650d5053f8c0e8e77512244e526c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 11:28:42 +0200 Subject: [PATCH 1036/1148] dmime: Return S_OK from wave track SetParam GUID_UnloadFromAudioPath. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55792 (cherry picked from commit 0431c88a9dffce2bd9360fc412e26ef913f8f974) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/wavetrack.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index def1e59fb23..79169a2a6fa 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -296,6 +296,8 @@ static HRESULT WINAPI wave_track_SetParam(IDirectMusicTrack8 *iface, REFGUID typ item->buffer = NULL; } } + + return S_OK; } return DMUS_E_TYPE_UNSUPPORTED; From bb30262e449a37f9cc0dd0a5454e59a0584106c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 11:29:18 +0200 Subject: [PATCH 1037/1148] dmime: Return hr from wave track SetParam GUID_DownloadToAudioPath. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55792 (cherry picked from commit b1bfc52676cbb41e3fbc0f114588acc486028c7d) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/wavetrack.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index 79169a2a6fa..fcf57c1148d 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -273,6 +273,8 @@ static HRESULT WINAPI wave_track_SetParam(IDirectMusicTrack8 *iface, REFGUID typ } } } + + return hr; } if (IsEqualGUID(type, &GUID_Enable_Auto_Download)) { FIXME("GUID_Enable_Auto_Download not handled yet\n"); From 7dd8837004c320cdf833cb193f96c2884d1038c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 11:46:17 +0200 Subject: [PATCH 1038/1148] dmusic: Implement IDirectMusicObject interface on wave objects. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55792 (cherry picked from commit dc0431b8190b09aefa9991810c28f658802cd730) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/wave.c | 131 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 119 insertions(+), 12 deletions(-) diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index ef1cd2990ff..e5e7c4dc2a0 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -32,6 +32,7 @@ C_ASSERT(sizeof(struct sample) == offsetof(struct sample, loops[0])); struct wave { IUnknown IUnknown_iface; + struct dmobject dmobj; LONG ref; struct sample *sample; @@ -58,6 +59,20 @@ static HRESULT WINAPI wave_QueryInterface(IUnknown *iface, REFIID riid, void **r return S_OK; } + if (IsEqualIID(riid, &IID_IDirectMusicObject)) + { + *ret_iface = &This->dmobj.IDirectMusicObject_iface; + IDirectMusicObject_AddRef(&This->dmobj.IDirectMusicObject_iface); + return S_OK; + } + + if (IsEqualIID(riid, &IID_IPersistStream)) + { + *ret_iface = &This->dmobj.IPersistStream_iface; + IDirectMusicObject_AddRef(&This->dmobj.IPersistStream_iface); + return S_OK; + } + WARN("(%p, %s, %p): not found\n", This, debugstr_dmguid(riid), ret_iface); *ret_iface = NULL; return E_NOINTERFACE; @@ -96,18 +111,6 @@ static const IUnknownVtbl unknown_vtbl = wave_Release, }; -static HRESULT wave_create(IUnknown **ret_iface) -{ - struct wave *obj; - - if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->IUnknown_iface.lpVtbl = &unknown_vtbl; - obj->ref = 1; - - *ret_iface = &obj->IUnknown_iface; - return S_OK; -} - static HRESULT parse_wsmp_chunk(struct wave *This, IStream *stream, struct chunk_entry *chunk) { struct sample *sample; @@ -167,6 +170,110 @@ static HRESULT parse_wave_chunk(struct wave *This, IStream *stream, struct chunk return hr; } +static HRESULT WINAPI wave_object_ParseDescriptor(IDirectMusicObject *iface, + IStream *stream, DMUS_OBJECTDESC *desc) +{ + struct chunk_entry chunk = {0}; + HRESULT hr; + + TRACE("(%p, %p, %p)\n", iface, stream, desc); + + if (!stream || !desc) return E_POINTER; + + if ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_RIFF, mmioFOURCC('W','A','V','E')): + hr = dmobj_parsedescriptor(stream, &chunk, desc, + DMUS_OBJ_NAME_INFO | DMUS_OBJ_OBJECT | DMUS_OBJ_VERSION); + break; + + default: + WARN("Invalid wave chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + hr = DMUS_E_CHUNKNOTFOUND; + break; + } + } + + if (FAILED(hr)) return hr; + + TRACE("returning descriptor:\n"); + dump_DMUS_OBJECTDESC(desc); + return S_OK; +} + +static const IDirectMusicObjectVtbl wave_object_vtbl = +{ + dmobj_IDirectMusicObject_QueryInterface, + dmobj_IDirectMusicObject_AddRef, + dmobj_IDirectMusicObject_Release, + dmobj_IDirectMusicObject_GetDescriptor, + dmobj_IDirectMusicObject_SetDescriptor, + wave_object_ParseDescriptor, +}; + +static inline struct wave *impl_from_IPersistStream(IPersistStream *iface) +{ + return CONTAINING_RECORD(iface, struct wave, dmobj.IPersistStream_iface); +} + +static HRESULT WINAPI wave_persist_stream_Load(IPersistStream *iface, IStream *stream) +{ + struct wave *This = impl_from_IPersistStream(iface); + struct chunk_entry chunk = {0}; + HRESULT hr; + + TRACE("(%p, %p)\n", This, stream); + + if (!stream) return E_POINTER; + + if ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_RIFF, mmioFOURCC('W','A','V','E')): + hr = parse_wave_chunk(This, stream, &chunk); + break; + + default: + WARN("Invalid wave chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + hr = DMUS_E_UNSUPPORTED_STREAM; + break; + } + } + + stream_skip_chunk(stream, &chunk); + return hr; +} + +static const IPersistStreamVtbl wave_persist_stream_vtbl = +{ + dmobj_IPersistStream_QueryInterface, + dmobj_IPersistStream_AddRef, + dmobj_IPersistStream_Release, + dmobj_IPersistStream_GetClassID, + unimpl_IPersistStream_IsDirty, + wave_persist_stream_Load, + unimpl_IPersistStream_Save, + unimpl_IPersistStream_GetSizeMax, +}; + +static HRESULT wave_create(IUnknown **ret_iface) +{ + struct wave *obj; + + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; + obj->IUnknown_iface.lpVtbl = &unknown_vtbl; + obj->ref = 1; + dmobject_init(&obj->dmobj, &CLSID_DirectSoundWave, &obj->IUnknown_iface); + obj->dmobj.IDirectMusicObject_iface.lpVtbl = &wave_object_vtbl; + obj->dmobj.IPersistStream_iface.lpVtbl = &wave_persist_stream_vtbl; + + *ret_iface = &obj->IUnknown_iface; + return S_OK; +} + HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **ret_iface) { struct wave *This; From 6868d455291065f66d198616d8a5d85a3cc3a40c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 13:08:38 +0200 Subject: [PATCH 1039/1148] dmusic: Use the IDirectMusicObject interface for waves. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55792 (cherry picked from commit 80e6310a884f59c1faeccc441aa808888ab0898b) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/wavetrack.c | 6 +++--- dlls/dmusic/collection.c | 8 ++++---- dlls/dmusic/dmusic_private.h | 2 +- dlls/dmusic/dmusic_wave.h | 11 ++++++----- dlls/dmusic/instrument.c | 4 ++-- dlls/dmusic/wave.c | 35 ++++++++++++++++++++--------------- 6 files changed, 36 insertions(+), 30 deletions(-) diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index fcf57c1148d..5ba9fb675f5 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -24,7 +24,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmime); struct wave_item { struct list entry; DMUS_IO_WAVE_ITEM_HEADER header; - IUnknown *object; + IDirectMusicObject *object; IDirectSoundBuffer *buffer; }; @@ -106,7 +106,7 @@ static ULONG WINAPI wave_track_Release(IDirectMusicTrack8 *iface) { list_remove(&item->entry); if (item->buffer) IDirectSoundBuffer_Release(item->buffer); - if (item->object) IUnknown_Release(item->object); + if (item->object) IDirectMusicObject_Release(item->object); free(item); } @@ -469,7 +469,7 @@ static HRESULT parse_wave_item(struct wave_part *part, IStream *stream, struct c hr = DMUS_E_UNSUPPORTED_STREAM; goto error; } - if (FAILED(hr = dmobj_parsereference(stream, &chunk, (IDirectMusicObject **)&item->object))) + if (FAILED(hr = dmobj_parsereference(stream, &chunk, &item->object))) goto error; list_add_tail(&part->items, &item->entry); diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index bf21d3148e3..5cf129cfdd1 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -42,7 +42,7 @@ C_ASSERT(sizeof(struct pool) == offsetof(struct pool, cues[0])); struct wave_entry { struct list entry; - IUnknown *wave; + IDirectMusicObject *wave; DWORD offset; }; @@ -59,13 +59,13 @@ struct collection struct list waves; }; -extern void collection_internal_addref(struct collection *collection) +void collection_internal_addref(struct collection *collection) { ULONG ref = InterlockedIncrement( &collection->internal_ref ); TRACE( "collection %p, internal ref %lu.\n", collection, ref ); } -extern void collection_internal_release(struct collection *collection) +void collection_internal_release(struct collection *collection) { ULONG ref = InterlockedDecrement( &collection->internal_ref ); TRACE( "collection %p, internal ref %lu.\n", collection, ref ); @@ -74,7 +74,7 @@ extern void collection_internal_release(struct collection *collection) free(collection); } -extern HRESULT collection_get_wave(struct collection *collection, DWORD index, IUnknown **out) +HRESULT collection_get_wave(struct collection *collection, DWORD index, IDirectMusicObject **out) { struct wave_entry *wave_entry; DWORD offset; diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 3efb69656c8..9ef9557407e 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -83,7 +83,7 @@ typedef struct port_info { struct collection; extern void collection_internal_addref(struct collection *collection); extern void collection_internal_release(struct collection *collection); -extern HRESULT collection_get_wave(struct collection *collection, DWORD index, IUnknown **out); +extern HRESULT collection_get_wave(struct collection *collection, DWORD index, IDirectMusicObject **out); /* CLSID */ extern HRESULT music_create(IUnknown **ret_iface); diff --git a/dlls/dmusic/dmusic_wave.h b/dlls/dmusic/dmusic_wave.h index 43396250261..f5ebaed2892 100644 --- a/dlls/dmusic/dmusic_wave.h +++ b/dlls/dmusic/dmusic_wave.h @@ -29,8 +29,9 @@ struct soundfont; struct chunk_entry; -extern HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnknown **out); -extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **out); -extern HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id); -extern HRESULT wave_download_to_dsound(IUnknown *iface, IDirectSound *dsound, IDirectSoundBuffer **ret_iface); -extern HRESULT wave_get_duration(IUnknown *iface, REFERENCE_TIME *duration); +extern HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IDirectMusicObject **out); +extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IDirectMusicObject **out); +extern HRESULT wave_download_to_port(IDirectMusicObject *iface, IDirectMusicPortDownload *port, DWORD *id); +extern HRESULT wave_download_to_dsound(IDirectMusicObject *iface, IDirectSound *dsound, + IDirectSoundBuffer **ret_iface); +extern HRESULT wave_get_duration(IDirectMusicObject *iface, REFERENCE_TIME *duration); diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 7a0f8a2f1eb..8311c690877 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -747,7 +747,7 @@ HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicP IDirectMusicDownload *download; DWORD size, offset_count; struct region *region; - IUnknown *wave; + IDirectMusicObject *wave; HRESULT hr; if (This->download) goto done; @@ -827,7 +827,7 @@ HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicP if (SUCCEEDED(hr = collection_get_wave(This->collection, region->wave_link.ulTableIndex, &wave))) { hr = wave_download_to_port(wave, port, &dmus_region->WaveLink.ulTableIndex); - IUnknown_Release(wave); + IDirectMusicObject_Release(wave); } if (FAILED(hr)) goto failed; diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index e5e7c4dc2a0..0db895f2038 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -170,6 +170,11 @@ static HRESULT parse_wave_chunk(struct wave *This, IStream *stream, struct chunk return hr; } +static inline struct wave *impl_from_IDirectMusicObject(IDirectMusicObject *iface) +{ + return CONTAINING_RECORD(iface, struct wave, dmobj.IDirectMusicObject_iface); +} + static HRESULT WINAPI wave_object_ParseDescriptor(IDirectMusicObject *iface, IStream *stream, DMUS_OBJECTDESC *desc) { @@ -259,7 +264,7 @@ static const IPersistStreamVtbl wave_persist_stream_vtbl = unimpl_IPersistStream_GetSizeMax, }; -static HRESULT wave_create(IUnknown **ret_iface) +static HRESULT wave_create(IDirectMusicObject **ret_iface) { struct wave *obj; @@ -270,24 +275,24 @@ static HRESULT wave_create(IUnknown **ret_iface) obj->dmobj.IDirectMusicObject_iface.lpVtbl = &wave_object_vtbl; obj->dmobj.IPersistStream_iface.lpVtbl = &wave_persist_stream_vtbl; - *ret_iface = &obj->IUnknown_iface; + *ret_iface = &obj->dmobj.IDirectMusicObject_iface; return S_OK; } -HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **ret_iface) +HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IDirectMusicObject **ret_iface) { struct wave *This; - IUnknown *iface; + IDirectMusicObject *iface; HRESULT hr; TRACE("(%p, %p, %p)\n", stream, parent, ret_iface); if (FAILED(hr = wave_create(&iface))) return hr; - This = impl_from_IUnknown(iface); + This = impl_from_IDirectMusicObject(iface); if (FAILED(hr = parse_wave_chunk(This, stream, parent))) { - IUnknown_Release(iface); + IDirectMusicObject_Release(iface); return DMUS_E_UNSUPPORTED_STREAM; } @@ -324,7 +329,7 @@ HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnk return S_OK; } -HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnknown **ret_iface) +HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IDirectMusicObject **ret_iface) { struct sf_sample *sf_sample = soundfont->shdr + index; struct sample *sample = NULL; @@ -333,7 +338,7 @@ HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnk UINT data_size, offset; struct wave *This; void *data = NULL; - IUnknown *iface; + IDirectMusicObject *iface; TRACE("(%p, %u, %p)\n", soundfont, index, ret_iface); @@ -360,7 +365,7 @@ HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnk if (FAILED(hr = wave_create(&iface))) goto failed; - This = impl_from_IUnknown(iface); + This = impl_from_IDirectMusicObject(iface); This->format = format; This->sample = sample; This->data_size = data_size; @@ -403,7 +408,7 @@ HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IUnk return hr; } -HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, DWORD *id) +HRESULT wave_download_to_port(IDirectMusicObject *iface, IDirectMusicPortDownload *port, DWORD *id) { struct download_buffer { @@ -413,7 +418,7 @@ HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, D DMUS_WAVEDATA data; } *buffer; - struct wave *This = impl_from_IUnknown(iface); + struct wave *This = impl_from_IDirectMusicObject(iface); DWORD size = offsetof(struct download_buffer, data.byData[This->data_size]); IDirectMusicDownload *download; HRESULT hr; @@ -446,9 +451,9 @@ HRESULT wave_download_to_port(IUnknown *iface, IDirectMusicPortDownload *port, D return hr; } -HRESULT wave_download_to_dsound(IUnknown *iface, IDirectSound *dsound, IDirectSoundBuffer **ret_iface) +HRESULT wave_download_to_dsound(IDirectMusicObject *iface, IDirectSound *dsound, IDirectSoundBuffer **ret_iface) { - struct wave *This = impl_from_IUnknown(iface); + struct wave *This = impl_from_IDirectMusicObject(iface); DSBUFFERDESC desc = { .dwSize = sizeof(desc), @@ -485,9 +490,9 @@ HRESULT wave_download_to_dsound(IUnknown *iface, IDirectSound *dsound, IDirectSo return S_OK; } -HRESULT wave_get_duration(IUnknown *iface, REFERENCE_TIME *duration) +HRESULT wave_get_duration(IDirectMusicObject *iface, REFERENCE_TIME *duration) { - struct wave *This = impl_from_IUnknown(iface); + struct wave *This = impl_from_IDirectMusicObject(iface); *duration = (REFERENCE_TIME)This->data_size * 10000000 / This->format->nAvgBytesPerSec; return S_OK; } From 06ede00b99fa6ed525e0629e685e865387936e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 11:46:17 +0200 Subject: [PATCH 1040/1148] dswave: Use the dmusic wave object implementation. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55792 (cherry picked from commit 7c9e8b9c4e4e9e375b6bd6e14d5b98bfec61f8d0) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/dmusic_wave.h | 1 + dlls/dmusic/wave.c | 2 +- dlls/dswave/Makefile.in | 4 +- dlls/dswave/dswave.c | 191 ----------------------------------- dlls/dswave/dswave_main.c | 16 +-- dlls/dswave/dswave_private.h | 5 - 6 files changed, 14 insertions(+), 205 deletions(-) delete mode 100644 dlls/dswave/dswave.c diff --git a/dlls/dmusic/dmusic_wave.h b/dlls/dmusic/dmusic_wave.h index f5ebaed2892..bdad8ca9605 100644 --- a/dlls/dmusic/dmusic_wave.h +++ b/dlls/dmusic/dmusic_wave.h @@ -29,6 +29,7 @@ struct soundfont; struct chunk_entry; +extern HRESULT wave_create(IDirectMusicObject **ret_iface); extern HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IDirectMusicObject **out); extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IDirectMusicObject **out); extern HRESULT wave_download_to_port(IDirectMusicObject *iface, IDirectMusicPortDownload *port, DWORD *id); diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index 0db895f2038..dd0b8a44779 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -264,7 +264,7 @@ static const IPersistStreamVtbl wave_persist_stream_vtbl = unimpl_IPersistStream_GetSizeMax, }; -static HRESULT wave_create(IDirectMusicObject **ret_iface) +HRESULT wave_create(IDirectMusicObject **ret_iface) { struct wave *obj; diff --git a/dlls/dswave/Makefile.in b/dlls/dswave/Makefile.in index 99b1e3ff9dc..8535e8fc27b 100644 --- a/dlls/dswave/Makefile.in +++ b/dlls/dswave/Makefile.in @@ -4,8 +4,8 @@ PARENTSRC = ../dmusic C_SRCS = \ dmobject.c \ - dswave.c \ - dswave_main.c + dswave_main.c \ + wave.c IDL_SRCS = dswave.idl diff --git a/dlls/dswave/dswave.c b/dlls/dswave/dswave.c deleted file mode 100644 index 5bdd83dfa2c..00000000000 --- a/dlls/dswave/dswave.c +++ /dev/null @@ -1,191 +0,0 @@ -/* IDirectMusicWave Implementation - * - * Copyright (C) 2003-2004 Rok Mandeljc - * - * This program 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 program 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 program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "dswave_private.h" -#include "dmobject.h" - -WINE_DEFAULT_DEBUG_CHANNEL(dswave); - -/* an interface that is, according to my tests, obtained by loader after loading object; is it acting - as some sort of bridge between object and loader? */ -static const GUID IID_IDirectMusicWavePRIVATE = {0x69e934e4,0x97f1,0x4f1d,{0x88,0xe8,0xf2,0xac,0x88,0x67,0x13,0x27}}; - -/***************************************************************************** - * IDirectMusicWaveImpl implementation - */ -typedef struct IDirectMusicWaveImpl { - IUnknown IUnknown_iface; - struct dmobject dmobj; - LONG ref; -} IDirectMusicWaveImpl; - -/* IDirectMusicWaveImpl IUnknown part: */ -static inline IDirectMusicWaveImpl *impl_from_IUnknown(IUnknown *iface) -{ - return CONTAINING_RECORD(iface, IDirectMusicWaveImpl, IUnknown_iface); -} - -static HRESULT WINAPI IUnknownImpl_QueryInterface(IUnknown *iface, REFIID riid, void **ret_iface) -{ - IDirectMusicWaveImpl *This = impl_from_IUnknown(iface); - - TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ret_iface); - - *ret_iface = NULL; - - if (IsEqualIID(riid, &IID_IUnknown)) - *ret_iface = iface; - else if (IsEqualIID(riid, &IID_IDirectMusicObject)) - *ret_iface = &This->dmobj.IDirectMusicObject_iface; - else if (IsEqualIID(riid, &IID_IPersistStream)) - *ret_iface = &This->dmobj.IPersistStream_iface; - else if (IsEqualIID(riid, &IID_IDirectMusicWavePRIVATE)) { - FIXME("(%p, %s, %p): Unsupported private interface\n", This, debugstr_guid(riid), ret_iface); - return E_NOINTERFACE; - } else { - WARN("(%p, %s, %p): not found\n", This, debugstr_dmguid(riid), ret_iface); - return E_NOINTERFACE; - } - - IUnknown_AddRef((IUnknown*)*ret_iface); - return S_OK; -} - -static ULONG WINAPI IUnknownImpl_AddRef(IUnknown *iface) -{ - IDirectMusicWaveImpl *This = impl_from_IUnknown(iface); - LONG ref = InterlockedIncrement(&This->ref); - - TRACE("(%p) ref=%ld\n", This, ref); - - return ref; -} - -static ULONG WINAPI IUnknownImpl_Release(IUnknown *iface) -{ - IDirectMusicWaveImpl *This = impl_from_IUnknown(iface); - LONG ref = InterlockedDecrement(&This->ref); - - TRACE("(%p) ref=%ld\n", This, ref); - - if (!ref) free(This); - - return ref; -} - -static const IUnknownVtbl unknown_vtbl = { - IUnknownImpl_QueryInterface, - IUnknownImpl_AddRef, - IUnknownImpl_Release -}; - -/* IDirectMusicWaveImpl IDirectMusicObject part: */ -static HRESULT WINAPI wave_IDirectMusicObject_ParseDescriptor(IDirectMusicObject *iface, - IStream *stream, DMUS_OBJECTDESC *desc) -{ - struct chunk_entry riff = {0}; - HRESULT hr; - - TRACE("(%p, %p, %p)\n", iface, stream, desc); - - if (!stream || !desc) - return E_POINTER; - - if ((hr = stream_get_chunk(stream, &riff)) != S_OK) - return hr; - if (riff.id != FOURCC_RIFF || riff.type != mmioFOURCC('W','A','V','E')) { - TRACE("loading failed: unexpected %s\n", debugstr_chunk(&riff)); - stream_skip_chunk(stream, &riff); - return DMUS_E_CHUNKNOTFOUND; - } - - hr = dmobj_parsedescriptor(stream, &riff, desc, - DMUS_OBJ_NAME_INFO | DMUS_OBJ_OBJECT | DMUS_OBJ_VERSION); - if (FAILED(hr)) - return hr; - - TRACE("returning descriptor:\n"); - dump_DMUS_OBJECTDESC(desc); - return S_OK; -} - -static const IDirectMusicObjectVtbl dmobject_vtbl = { - dmobj_IDirectMusicObject_QueryInterface, - dmobj_IDirectMusicObject_AddRef, - dmobj_IDirectMusicObject_Release, - dmobj_IDirectMusicObject_GetDescriptor, - dmobj_IDirectMusicObject_SetDescriptor, - wave_IDirectMusicObject_ParseDescriptor -}; - -/* IDirectMusicWaveImpl IPersistStream part: */ -static inline IDirectMusicWaveImpl *impl_from_IPersistStream(IPersistStream *iface) -{ - return CONTAINING_RECORD(iface, IDirectMusicWaveImpl, dmobj.IPersistStream_iface); -} - -static HRESULT WINAPI wave_IPersistStream_Load(IPersistStream *iface, IStream *stream) -{ - IDirectMusicWaveImpl *This = impl_from_IPersistStream(iface); - struct chunk_entry riff = {0}; - - /* Without the private interface the implementation should go to dmime/segment.c */ - FIXME("(%p, %p): loading not implemented (only descriptor is loaded)\n", This, stream); - - if (!stream) - return E_POINTER; - - if (stream_get_chunk(stream, &riff) != S_OK || riff.id != FOURCC_RIFF || - riff.type != mmioFOURCC('W','A','V','E')) - return DMUS_E_UNSUPPORTED_STREAM; - stream_reset_chunk_start(stream, &riff); - - return IDirectMusicObject_ParseDescriptor(&This->dmobj.IDirectMusicObject_iface, stream, - &This->dmobj.desc); -} - -static const IPersistStreamVtbl persiststream_vtbl = { - dmobj_IPersistStream_QueryInterface, - dmobj_IPersistStream_AddRef, - dmobj_IPersistStream_Release, - dmobj_IPersistStream_GetClassID, - unimpl_IPersistStream_IsDirty, - wave_IPersistStream_Load, - unimpl_IPersistStream_Save, - unimpl_IPersistStream_GetSizeMax -}; - -/* for ClassFactory */ -HRESULT create_dswave(REFIID lpcGUID, void **ppobj) -{ - IDirectMusicWaveImpl *obj; - HRESULT hr; - - *ppobj = NULL; - if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; - obj->IUnknown_iface.lpVtbl = &unknown_vtbl; - obj->ref = 1; - dmobject_init(&obj->dmobj, &CLSID_DirectSoundWave, &obj->IUnknown_iface); - obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl; - obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; - - hr = IUnknown_QueryInterface(&obj->IUnknown_iface, lpcGUID, ppobj); - IUnknown_Release(&obj->IUnknown_iface); - return hr; -} diff --git a/dlls/dswave/dswave_main.c b/dlls/dswave/dswave_main.c index f2ce7bb950f..100d5cf8fc9 100644 --- a/dlls/dswave/dswave_main.c +++ b/dlls/dswave/dswave_main.c @@ -35,6 +35,7 @@ #include "dmusici.h" #include "dswave_private.h" +#include "dmusic_wave.h" #include "dmobject.h" WINE_DEFAULT_DEBUG_CHANNEL(dswave); @@ -79,14 +80,17 @@ static ULONG WINAPI WaveCF_Release(IClassFactory * iface) static HRESULT WINAPI WaveCF_CreateInstance(IClassFactory * iface, IUnknown *outer_unk, REFIID riid, void **ret_iface) { - TRACE ("(%p, %s, %p)\n", outer_unk, debugstr_dmguid(riid), ret_iface); + IDirectMusicObject *object; + HRESULT hr; - if (outer_unk) { - *ret_iface = NULL; - return CLASS_E_NOAGGREGATION; - } + TRACE("(%p, %s, %p)\n", outer_unk, debugstr_dmguid(riid), ret_iface); - return create_dswave(riid, ret_iface); + *ret_iface = NULL; + if (outer_unk) return CLASS_E_NOAGGREGATION; + if (FAILED(hr = wave_create(&object))) return hr; + hr = IDirectMusicObject_QueryInterface(object, riid, ret_iface); + IDirectMusicObject_Release(object); + return hr; } static HRESULT WINAPI WaveCF_LockServer(IClassFactory * iface, BOOL dolock) diff --git a/dlls/dswave/dswave_private.h b/dlls/dswave/dswave_private.h index 50406c676c1..080bb961e94 100644 --- a/dlls/dswave/dswave_private.h +++ b/dlls/dswave/dswave_private.h @@ -39,9 +39,4 @@ #include "dmusicf.h" #include "dmusics.h" -/***************************************************************************** - * ClassFactory - */ -extern HRESULT create_dswave(REFIID lpcGUID, void **ret_iface); - #endif /* __WINE_DSWAVE_PRIVATE_H */ From de4d3f50dafc63f99667ee92aa653622a5eaff00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 17 Oct 2023 12:41:40 +0200 Subject: [PATCH 1041/1148] dmime: Avoid releasing the newly created graph twice. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55790 (cherry picked from commit 231dd330cf281a5f33eba25d1d562368bb176229) CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/audiopath.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dlls/dmime/audiopath.c b/dlls/dmime/audiopath.c index 3310624eb00..65691f4705f 100644 --- a/dlls/dmime/audiopath.c +++ b/dlls/dmime/audiopath.c @@ -195,8 +195,6 @@ static HRESULT WINAPI IDirectMusicAudioPathImpl_GetObjectInPath (IDirectMusicAud if (FAILED(hr)) return hr; IDirectMusicPerformance8_SetGraph(This->pPerf, pGraph); - /* we need release as SetGraph do an AddRef */ - IDirectMusicGraph_Release(pGraph); pPerfoGraph = pGraph; } *ppObject = pPerfoGraph; From b0d870066b6d2d5138254d1060f8f3505a4ec628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 22:49:46 +0200 Subject: [PATCH 1042/1148] dmime/tests: Remove flaky track playing state test. This fails regularly on Win7. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 6dc9d8e0f5e..151dbead3c1 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3971,11 +3971,6 @@ static void test_segment_state(void) ok(hr == S_OK, "got %#lx\n", hr); - /* This might be timing dependent and if PlaySegment is already - * late, the tracks are played synchronously and right away. - */ - todo_wine check_track_state(track, playing, FALSE); - ret = test_track_wait_playing(track, 50); ok(ret == 0, "got %#lx\n", ret); From db6384f117f0dc7c23ee0b12dd3b56fcbaca840c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 25 Oct 2023 14:19:31 +0200 Subject: [PATCH 1043/1148] dmime/tests: Avoid checking message segment state reference count. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 151dbead3c1..7ed6568ecd4 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -38,8 +38,8 @@ static ULONG get_refcount(void *iface) return IUnknown_Release(unknown); } -#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) -static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) +#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c, FALSE) +static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported, BOOL check_refs) { ULONG expect_ref = get_refcount(iface_ptr); IUnknown *iface = iface_ptr; @@ -52,10 +52,10 @@ static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOO if (SUCCEEDED(hr)) { LONG ref = get_refcount(unk); - ok_(__FILE__, line)(ref == expect_ref + 1, "got %ld\n", ref); + if (check_refs) ok_(__FILE__, line)(ref == expect_ref + 1, "got %ld\n", ref); IUnknown_Release(unk); ref = get_refcount(iface_ptr); - ok_(__FILE__, line)(ref == expect_ref, "got %ld\n", ref); + if (check_refs) ok_(__FILE__, line)(ref == expect_ref, "got %ld\n", ref); } } @@ -3024,8 +3024,8 @@ static void check_dmus_notification_pmsg_(int line, DMUS_NOTIFICATION_PMSG *msg, ok_(__FILE__, line)(!msg->punkUser, "got punkUser %p\n", msg->punkUser); else { - check_interface_(line, msg->punkUser, &IID_IDirectMusicSegmentState, TRUE); - check_interface_(line, msg->punkUser, &IID_IDirectMusicSegmentState8, TRUE); + check_interface_(line, msg->punkUser, &IID_IDirectMusicSegmentState, TRUE, FALSE); + check_interface_(line, msg->punkUser, &IID_IDirectMusicSegmentState8, TRUE, FALSE); } } From ee83a689c142f86ed9c0938f8c0c8dfa718b89f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 22:53:41 +0200 Subject: [PATCH 1044/1148] dmime/tests: Ignore failure on missing gm.dls in test_band_track_play. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55722 CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 7ed6568ecd4..85ea8fcfeff 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3722,7 +3722,10 @@ static void test_band_track_play(void) &IID_IDirectMusicLoader8, (void **)&loader); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicLoader_SetObject(loader, &desc); - ok(hr == S_OK, "got %#lx\n", hr); + if (hr == DMUS_E_LOADER_FAILEDOPEN) + skip("Failed to open gm.dls, missing system SoundFont?\n"); + else + ok(hr == S_OK, "got %#lx\n", hr); hr = test_loader_stream_create(stream, loader, &loader_stream); ok(hr == S_OK, "got %#lx\n", hr); From 0efdd51681600962223780d946f3eab28d3f78a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 22:57:25 +0200 Subject: [PATCH 1045/1148] dmusic/tests: Skip test_default_gm_collection if gm.dls is missing. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55688 CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/tests/dmusic.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dlls/dmusic/tests/dmusic.c b/dlls/dmusic/tests/dmusic.c index 51a6b0d8774..f71c0600366 100644 --- a/dlls/dmusic/tests/dmusic.c +++ b/dlls/dmusic/tests/dmusic.c @@ -1608,6 +1608,11 @@ static void test_default_gm_collection(void) ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicLoader_GetObject(loader, &desc, &IID_IDirectMusicCollection, (void **)&collection); + if (hr == DMUS_E_LOADER_NOFILENAME) + { + skip("Failed to open default GM collection, skipping tests. Missing system SoundFont?\n"); + goto skip_tests; + } ok(hr == S_OK, "got %#lx\n", hr); for (i = 0; hr == S_OK && i < ARRAY_SIZE(results); i++) @@ -1635,6 +1640,8 @@ static void test_default_gm_collection(void) } IDirectMusicCollection_Release(collection); + +skip_tests: IDirectMusicLoader_Release(loader); } From b15482692c014814224b44102cd6b212bc977ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 22 Oct 2023 16:21:41 +0200 Subject: [PATCH 1046/1148] dmsynth: Set loop and sample generators on the fluid_voice. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index c3e0c90880e..379d80f7346 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -49,6 +49,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); #define CONN_TRANSFORM(src, ctrl, dst) (((src) & 0x3f) << 10) | (((ctrl) & 0x3f) << 4) | ((dst) & 0xf) +/* from src/rvoice/fluid_rvoice.h */ +#define FLUID_LOOP_DURING_RELEASE 1 +#define FLUID_LOOP_UNTIL_RELEASE 3 + static const char *debugstr_conn_src(UINT src) { switch (src) @@ -1750,11 +1754,6 @@ static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *flui fluid_sample_set_sound_data(fluid_sample, wave->samples, NULL, wave->sample_count, wave->format.nSamplesPerSec, TRUE); - if (region->wave_sample.cSampleLoops) - { - WLOOP *loop = region->wave_loops; - fluid_sample_set_loop(fluid_sample, loop->ulStart, loop->ulStart + loop->ulLength); - } fluid_sample_set_pitch(fluid_sample, region->wave_sample.usUnityNote, region->wave_sample.sFineTune); if (!(fluid_voice = fluid_synth_alloc_voice(synth->fluid_synth, fluid_sample, chan, key, vel))) @@ -1765,6 +1764,20 @@ static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *flui } set_default_voice_connections(fluid_voice); + if (region->wave_sample.cSampleLoops) + { + WLOOP *loop = region->wave_loops; + + if (loop->ulType == WLOOP_TYPE_FORWARD) + fluid_voice_gen_set(fluid_voice, GEN_SAMPLEMODE, FLUID_LOOP_DURING_RELEASE); + else if (loop->ulType == WLOOP_TYPE_RELEASE) + fluid_voice_gen_set(fluid_voice, GEN_SAMPLEMODE, FLUID_LOOP_UNTIL_RELEASE); + else + FIXME("Unsupported loop type %lu\n", loop->ulType); + + fluid_voice_gen_set(fluid_voice, GEN_STARTLOOPADDROFS, loop->ulStart); + fluid_voice_gen_set(fluid_voice, GEN_ENDLOOPADDROFS, loop->ulStart + loop->ulLength); + } LIST_FOR_EACH_ENTRY(articulation, &instrument->articulations, struct articulation, entry) add_voice_connections(fluid_voice, &articulation->list, articulation->connections); LIST_FOR_EACH_ENTRY(articulation, ®ion->articulations, struct articulation, entry) From 4490f3c8138e665136987b9e2d6ffa81b9819c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sun, 22 Oct 2023 15:51:05 +0200 Subject: [PATCH 1047/1148] dmime: Force recompute MIDI message reference time. Fixes 65e388137c6117aaf8f99cef808d32afa29c7639, which is otherwise no-op without this change. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index a2e611523a6..e0da0516c79 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1706,7 +1706,7 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, DMUS_NOTE_PMSG *note = (DMUS_NOTE_PMSG *)msg; msg->mtTime += note->nOffset; - if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, + if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, MIDI_NOTE_ON, note->bMidiValue, note->bVelocity))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr); From c1b5cad81b6d760ed246b160e2b0a20a85c80a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 24 Oct 2023 14:25:07 +0200 Subject: [PATCH 1048/1148] dmime: Pass IDirectMusicPerformance8 to segment state functions. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 6 +++--- dlls/dmime/performance.c | 7 ++++--- dlls/dmime/segmentstate.c | 12 ++++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 7f1937eaa0f..860b4ef385c 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -73,9 +73,9 @@ extern void set_audiopath_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffe extern void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*); extern HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, - IDirectMusicPerformance *performance, IDirectMusicSegmentState **ret_iface); -extern HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance *performance); -extern HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance *performance); + IDirectMusicPerformance8 *performance, IDirectMusicSegmentState **ret_iface); +extern HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); +extern HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); extern HRESULT wave_track_create_from_chunk(IStream *stream, struct chunk_entry *parent, IDirectMusicTrack8 **ret_iface); diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index e0da0516c79..8459de00b3f 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1196,7 +1196,7 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, if (FAILED(hr = IUnknown_QueryInterface(source, &IID_IDirectMusicSegment, (void **)&segment))) return hr; - if (FAILED(hr = segment_state_create(segment, start_time, (IDirectMusicPerformance *)iface, &state))) + if (FAILED(hr = segment_state_create(segment, start_time, iface, &state))) { IDirectMusicSegment_Release(segment); return hr; @@ -1213,7 +1213,7 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, hr = performance_send_dirty_pmsg(This, start_time); if (SUCCEEDED(hr)) - hr = segment_state_play(state, (IDirectMusicPerformance *)iface); + hr = segment_state_play(state, iface); if (SUCCEEDED(hr)) hr = performance_send_notification_pmsg(This, start_time + length, This->notification_segment, @@ -1767,7 +1767,8 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_SEGMENT) && notif->dwNotificationOption == DMUS_NOTIFICATION_SEGEND) { - if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)notif->punkUser, performance))) + if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)notif->punkUser, + (IDirectMusicPerformance8 *)performance))) WARN("Failed to end segment state %p, hr %#lx\n", notif->punkUser, hr); } diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index c43d833fedd..89534f7342d 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -209,7 +209,7 @@ HRESULT create_dmsegmentstate(REFIID riid, void **ret_iface) } HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, - IDirectMusicPerformance *performance, IDirectMusicSegmentState **ret_iface) + IDirectMusicPerformance8 *performance, IDirectMusicSegmentState **ret_iface) { IDirectMusicSegmentState *iface; struct segment_state *This; @@ -225,7 +225,7 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time This->segment = segment; IDirectMusicSegment_AddRef(This->segment); - if (SUCCEEDED(hr = IDirectMusicPerformance_GetGlobalParam(performance, &GUID_PerfAutoDownload, + if (SUCCEEDED(hr = IDirectMusicPerformance8_GetGlobalParam(performance, &GUID_PerfAutoDownload, &This->auto_download, sizeof(This->auto_download))) && This->auto_download) hr = IDirectMusicSegment_SetParam(segment, &GUID_DownloadToAudioPath, -1, DMUS_SEG_ALLTRACKS, 0, performance); @@ -247,7 +247,7 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time if (!(entry = malloc(sizeof(*entry)))) hr = E_OUTOFMEMORY; - else if (SUCCEEDED(hr = IDirectMusicTrack_InitPlay(track, iface, performance, + else if (SUCCEEDED(hr = IDirectMusicTrack_InitPlay(track, iface, (IDirectMusicPerformance *)performance, &entry->state_data, track_id, 0))) { entry->track = track; @@ -268,7 +268,7 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time return hr; } -HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance *performance) +HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance) { struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); DWORD track_flags = DMUS_TRACKF_DIRTY | DMUS_TRACKF_START | DMUS_TRACKF_SEEK; @@ -279,7 +279,7 @@ HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerforma LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) { if (FAILED(hr = IDirectMusicTrack_Play(entry->track, entry->state_data, start_time, - end_time, 0, track_flags, performance, iface, entry->track_id))) + end_time, 0, track_flags, (IDirectMusicPerformance *)performance, iface, entry->track_id))) { WARN("Failed to play track %p, hr %#lx\n", entry->track, hr); break; @@ -289,7 +289,7 @@ HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerforma return hr; } -HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance *performance) +HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance) { struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); struct track_entry *entry, *next; From 73bc9f6150dda86c03ec645dc71db1a37e58c1b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 24 Oct 2023 14:05:52 +0200 Subject: [PATCH 1049/1148] dmime: Pass segment start time as track time offset. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segmentstate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index 89534f7342d..b4bb784de0d 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -278,8 +278,8 @@ HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerforma LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) { - if (FAILED(hr = IDirectMusicTrack_Play(entry->track, entry->state_data, start_time, - end_time, 0, track_flags, (IDirectMusicPerformance *)performance, iface, entry->track_id))) + if (FAILED(hr = IDirectMusicTrack_Play(entry->track, entry->state_data, start_time, end_time, + This->start_time, track_flags, (IDirectMusicPerformance *)performance, iface, entry->track_id))) { WARN("Failed to play track %p, hr %#lx\n", entry->track, hr); break; From 6b3cfa9968a2bdb70a0162d228b1119b673daa71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 11:55:24 +0200 Subject: [PATCH 1050/1148] dmband: Use time_offset to align track start with music time. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/bandtrack.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index 5229204b284..0c6aa81dd5f 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -174,7 +174,6 @@ static HRESULT WINAPI band_track_Play(IDirectMusicTrack8 *iface, void *state_dat if (start_time != 0) FIXME("start_time %ld not implemented\n", start_time); if (end_time != -1) FIXME("end_time %ld not implemented\n", end_time); - if (time_offset != 0) FIXME("time_offset %ld not implemented\n", time_offset); if (segment_flags) FIXME("segment_flags %#lx not implemented\n", segment_flags); if (segment_state) FIXME("segment_state %p not implemented\n", segment_state); @@ -184,8 +183,9 @@ static HRESULT WINAPI band_track_Play(IDirectMusicTrack8 *iface, void *state_dat LIST_FOR_EACH_ENTRY(entry, &This->bands, struct band_entry, entry) { - if (FAILED(hr = band_send_messages(entry->band, performance, graph, - entry->head.lBandTimeLogical, track_id))) + MUSIC_TIME music_time = entry->head.lBandTimeLogical; + if (music_time != -1) music_time += time_offset; + if (FAILED(hr = band_send_messages(entry->band, performance, graph, music_time, track_id))) break; } From 2428539f6d3d0e178a549c930028b010dc9ddff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 11:55:24 +0200 Subject: [PATCH 1051/1148] dmime: Use time_offset to align track start with music time. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/seqtrack.c | 5 ++--- dlls/dmime/wavetrack.c | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/dlls/dmime/seqtrack.c b/dlls/dmime/seqtrack.c index a6043cb4a89..997bff241d8 100644 --- a/dlls/dmime/seqtrack.c +++ b/dlls/dmime/seqtrack.c @@ -122,7 +122,6 @@ static HRESULT WINAPI sequence_track_Play(IDirectMusicTrack8 *iface, void *state if (start_time != 0) FIXME("start_time %ld not implemented\n", start_time); if (end_time != -1) FIXME("end_time %ld not implemented\n", end_time); - if (time_offset != 0) FIXME("time_offset %ld not implemented\n", time_offset); if (segment_flags) FIXME("segment_flags %#lx not implemented\n", segment_flags); if (segment_state) FIXME("segment_state %p not implemented\n", segment_state); @@ -139,7 +138,7 @@ static HRESULT WINAPI sequence_track_Play(IDirectMusicTrack8 *iface, void *state (DMUS_PMSG **)&msg))) break; - msg->mtTime = item->mtTime; + msg->mtTime = item->mtTime + time_offset; msg->dwFlags = DMUS_PMSGF_MUSICTIME; msg->dwPChannel = item->dwPChannel; msg->dwVirtualTrackID = track_id; @@ -169,7 +168,7 @@ static HRESULT WINAPI sequence_track_Play(IDirectMusicTrack8 *iface, void *state (DMUS_PMSG **)&msg))) break; - msg->mtTime = item->mtStart; + msg->mtTime = item->mtStart + time_offset; msg->dwFlags = DMUS_PMSGF_MUSICTIME; msg->dwPChannel = item->dwPChannel; msg->dwVirtualTrackID = track_id; diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index 5ba9fb675f5..974e656cfe5 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -159,7 +159,6 @@ static HRESULT WINAPI wave_track_Play(IDirectMusicTrack8 *iface, void *state_dat if (start_time != 0) FIXME("start_time %ld not implemented\n", start_time); if (end_time != -1) FIXME("end_time %ld not implemented\n", end_time); - if (time_offset != 0) FIXME("time_offset %ld not implemented\n", time_offset); if (segment_flags) FIXME("segment_flags %#lx not implemented\n", segment_flags); if (segment_state) FIXME("segment_state %p not implemented\n", segment_state); @@ -181,7 +180,7 @@ static HRESULT WINAPI wave_track_Play(IDirectMusicTrack8 *iface, void *state_dat (DMUS_PMSG **)&msg))) break; - msg->mtTime = item->header.rtTime; + msg->mtTime = item->header.rtTime + time_offset; msg->dwFlags = DMUS_PMSGF_MUSICTIME; msg->dwPChannel = part->header.dwPChannel; msg->dwVirtualTrackID = track_id; From d30a56289e9564822d7fd3d3e2663e649cd44504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 25 Oct 2023 09:34:12 +0200 Subject: [PATCH 1052/1148] dmime: Use an internal performance message for segment end. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 2 ++ dlls/dmime/performance.c | 50 ++++++++++++++++++++++++++------------ dlls/dmime/segmentstate.c | 5 +++- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 860b4ef385c..28836694ed9 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -81,6 +81,8 @@ extern HRESULT wave_track_create_from_chunk(IStream *stream, struct chunk_entry IDirectMusicTrack8 **ret_iface); extern HRESULT performance_get_dsound(IDirectMusicPerformance8 *iface, IDirectSound **dsound); +extern HRESULT performance_send_segment_end(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, + IDirectMusicSegmentState *state); /***************************************************************************** * Auxiliary definitions diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 8459de00b3f..942452d353e 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -24,6 +24,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmime); +enum dmus_internal_message_type +{ + DMUS_PMSGT_INTERNAL_FIRST = 0x10, + DMUS_PMSGT_INTERNAL_SEGMENT_END = DMUS_PMSGT_INTERNAL_FIRST, +}; + struct pchannel_block { DWORD block_num; /* Block 0 is PChannels 0-15, Block 1 is PChannels 16-31, etc */ struct { @@ -158,7 +164,8 @@ static DWORD WINAPI message_thread_proc(void *args) return 0; } -static HRESULT performance_send_dirty_pmsg(struct performance *This, MUSIC_TIME music_time) +static HRESULT performance_send_pmsg(struct performance *This, MUSIC_TIME music_time, DWORD flags, + DWORD type, IUnknown *object) { IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface; IDirectMusicGraph *graph = &This->IDirectMusicGraph_iface; @@ -169,10 +176,11 @@ static HRESULT performance_send_dirty_pmsg(struct performance *This, MUSIC_TIME return hr; msg->mtTime = music_time; - msg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE; - msg->dwType = DMUS_PMSGT_DIRTY; + msg->dwFlags = DMUS_PMSGF_MUSICTIME | flags; + msg->dwType = type; + if ((msg->punkUser = object)) IUnknown_AddRef(object); - if (FAILED(hr = IDirectMusicGraph_StampPMsg(graph, msg)) + if ((type < DMUS_PMSGT_INTERNAL_FIRST && FAILED(hr = IDirectMusicGraph_StampPMsg(graph, msg))) || FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, msg))) IDirectMusicPerformance8_FreePMsg(performance, msg); @@ -252,6 +260,19 @@ static inline struct performance *impl_from_IDirectMusicPerformance8(IDirectMusi return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface); } +HRESULT performance_send_segment_end(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, + IDirectMusicSegmentState *state) +{ + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + HRESULT hr; + + if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_ATTIME, + DMUS_PMSGT_INTERNAL_SEGMENT_END, (IUnknown *)state))) + return hr; + + return S_OK; +} + /* IDirectMusicPerformance8 IUnknown part: */ static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ret_iface) { @@ -1050,8 +1071,7 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) list_remove(&message->entry); list_init(&message->entry); - /* process notifications to end any pending segment states */ - if (message->msg.dwType == DMUS_PMSGT_NOTIFICATION) + if (message->msg.dwType == DMUS_PMSGT_INTERNAL_SEGMENT_END) hr = IDirectMusicTool_ProcessPMsg(&This->IDirectMusicTool_iface, (IDirectMusicPerformance *)iface, &message->msg); else @@ -1210,7 +1230,7 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, hr = performance_send_notification_pmsg(This, start_time, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART, (IUnknown *)state); if (SUCCEEDED(hr)) - hr = performance_send_dirty_pmsg(This, start_time); + hr = performance_send_pmsg(This, start_time, DMUS_PMSGF_TOOL_QUEUE, DMUS_PMSGT_DIRTY, NULL); if (SUCCEEDED(hr)) hr = segment_state_play(state, iface); @@ -1222,7 +1242,7 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, hr = performance_send_notification_pmsg(This, start_time + length, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state); if (SUCCEEDED(hr)) - hr = performance_send_dirty_pmsg(This, start_time + length); + hr = performance_send_pmsg(This, start_time + length, DMUS_PMSGF_TOOL_QUEUE, DMUS_PMSGT_DIRTY, NULL); if (SUCCEEDED(hr)) hr = performance_send_notification_pmsg(This, start_time + length, This->notification_performance, GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED, NULL); @@ -1764,14 +1784,6 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, struct message *previous; BOOL enabled = FALSE; - if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_SEGMENT) - && notif->dwNotificationOption == DMUS_NOTIFICATION_SEGEND) - { - if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)notif->punkUser, - (IDirectMusicPerformance8 *)performance))) - WARN("Failed to end segment state %p, hr %#lx\n", notif->punkUser, hr); - } - if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_PERFORMANCE)) enabled = This->notification_performance; if (IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_SEGMENT)) @@ -1799,6 +1811,12 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, WARN("Failed to play wave buffer, hr %#lx\n", hr); break; + case DMUS_PMSGT_INTERNAL_SEGMENT_END: + if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)msg->punkUser, + (IDirectMusicPerformance8 *)performance))) + WARN("Failed to end segment state %p, hr %#lx\n", msg->punkUser, hr); + break; + default: FIXME("Unhandled message type %#lx\n", msg->dwType); break; diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index b4bb784de0d..21b98ccc14c 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -214,8 +214,8 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time IDirectMusicSegmentState *iface; struct segment_state *This; IDirectMusicTrack *track; + UINT i, duration; HRESULT hr; - UINT i; TRACE("(%p, %lu, %p)\n", segment, start_time, ret_iface); @@ -263,6 +263,9 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time } } + duration = This->end_point - This->start_point; + if (SUCCEEDED(hr)) hr = performance_send_segment_end(performance, start_time + duration, iface); + if (SUCCEEDED(hr)) *ret_iface = iface; else IDirectMusicSegmentState_Release(iface); return hr; From 1503baebe142ebd5c7c6b81afc94a36e9875bdad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 25 Oct 2023 10:13:17 +0200 Subject: [PATCH 1053/1148] dmime: Send notification messages with DMUS_PMSGF_TOOL_IMMEDIATE. Then send them again with DMUS_PMSGF_TOOL_ATTIME for the notification queue. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 942452d353e..b58a7d9cd18 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -199,7 +199,7 @@ static HRESULT performance_send_notification_pmsg(struct performance *This, MUSI return hr; msg->mtTime = music_time; - msg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE; + msg->dwFlags = DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE; msg->dwType = DMUS_PMSGT_NOTIFICATION; if ((msg->punkUser = object)) IUnknown_AddRef(object); msg->guidNotificationType = type; @@ -1230,7 +1230,7 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, hr = performance_send_notification_pmsg(This, start_time, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART, (IUnknown *)state); if (SUCCEEDED(hr)) - hr = performance_send_pmsg(This, start_time, DMUS_PMSGF_TOOL_QUEUE, DMUS_PMSGT_DIRTY, NULL); + hr = performance_send_pmsg(This, start_time, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL); if (SUCCEEDED(hr)) hr = segment_state_play(state, iface); @@ -1242,7 +1242,7 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, hr = performance_send_notification_pmsg(This, start_time + length, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state); if (SUCCEEDED(hr)) - hr = performance_send_pmsg(This, start_time + length, DMUS_PMSGF_TOOL_QUEUE, DMUS_PMSGT_DIRTY, NULL); + hr = performance_send_pmsg(This, start_time + length, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL); if (SUCCEEDED(hr)) hr = performance_send_notification_pmsg(This, start_time + length, This->notification_performance, GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED, NULL); @@ -1790,6 +1790,21 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, enabled = This->notification_segment; if (!enabled) return DMUS_S_FREE; + if (msg->dwFlags & DMUS_PMSGF_TOOL_IMMEDIATE) + { + /* re-send the message for queueing at the expected time */ + msg->dwFlags &= ~DMUS_PMSGF_TOOL_IMMEDIATE; + msg->dwFlags |= DMUS_PMSGF_TOOL_ATTIME; + + if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)msg))) + { + ERR("Failed to send notification message, hr %#lx\n", hr); + return DMUS_S_FREE; + } + + return S_OK; + } + list_add_tail(&This->notifications, &message->entry); /* discard old notification messages */ From 8c04f165fbbb4c4f336b2c285fb152382ea8056e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 24 Oct 2023 10:09:23 +0200 Subject: [PATCH 1054/1148] dmime/tests: Add a track and longer segment to notifications tests. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 105 +++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 27 deletions(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 85ea8fcfeff..35a6fbc98f4 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -566,6 +566,7 @@ struct test_track BOOL initialized; BOOL downloaded; BOOL playing; + BOOL test_play; HANDLE playing_event; }; @@ -659,6 +660,8 @@ static HRESULT WINAPI test_track_Play(IDirectMusicTrack *iface, void *state_data { struct test_track *This = impl_from_IDirectMusicTrack(iface); + if (!This->test_play) return S_OK; + ok(state_data == &This->data, "got %p\n", state_data); ok(start_time == 50, "got %lu\n", start_time); ok(end_time == 100, "got %lu\n", end_time); @@ -718,15 +721,15 @@ static HRESULT WINAPI test_track_IsParamSupported(IDirectMusicTrack *iface, REFG static HRESULT WINAPI test_track_AddNotificationType(IDirectMusicTrack *iface, REFGUID type) { - struct test_track *This = impl_from_IDirectMusicTrack(iface); - ok(0, "unexpected %s %p\n", __func__, This); + ok(IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT) || IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE), + "got %s\n", debugstr_guid(type)); return E_NOTIMPL; } static HRESULT WINAPI test_track_RemoveNotificationType(IDirectMusicTrack *iface, REFGUID type) { - struct test_track *This = impl_from_IDirectMusicTrack(iface); - ok(0, "unexpected %s %p\n", __func__, This); + ok(IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT) || IsEqualGUID(type, &GUID_NOTIFICATION_PERFORMANCE), + "got %s\n", debugstr_guid(type)); return E_NOTIMPL; } @@ -755,7 +758,7 @@ static const IDirectMusicTrackVtbl test_track_vtbl = test_track_Clone, }; -static HRESULT test_track_create(IDirectMusicTrack **ret_iface) +static HRESULT test_track_create(IDirectMusicTrack **ret_iface, BOOL test_play) { struct test_track *track; @@ -763,6 +766,7 @@ static HRESULT test_track_create(IDirectMusicTrack **ret_iface) if (!(track = calloc(1, sizeof(*track)))) return E_OUTOFMEMORY; track->IDirectMusicTrack_iface.lpVtbl = &test_track_vtbl; track->ref = 1; + track->test_play = test_play; track->playing_event = CreateEventW(NULL, FALSE, FALSE, NULL); ok(!!track->playing_event, "CreateEventW failed, error %lu\n", GetLastError()); @@ -3015,6 +3019,8 @@ static void check_dmus_notification_pmsg_(int line, DMUS_NOTIFICATION_PMSG *msg, "got dwType %#lx\n", msg->dwType); ok_(__FILE__, line)(IsEqualGUID(&msg->guidNotificationType, type), "got guidNotificationType %s\n", debugstr_guid(&msg->guidNotificationType)); + todo_wine_if(IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT) && + (option == DMUS_NOTIFICATION_SEGALMOSTEND || option == DMUS_NOTIFICATION_SEGEND)) ok_(__FILE__, line)(msg->dwNotificationOption == option, "got dwNotificationOption %#lx\n", msg->dwNotificationOption); ok_(__FILE__, line)(!msg->dwField1, "got dwField1 %lu\n", msg->dwField1); @@ -3038,8 +3044,11 @@ static void test_notification_pmsg(void) DMUS_PMSGT_WAVE, }; IDirectMusicPerformance *performance; + IDirectMusicSegmentState *state; DMUS_NOTIFICATION_PMSG *notif; + MUSIC_TIME music_time, length; IDirectMusicSegment *segment; + IDirectMusicTrack *track; IDirectMusicGraph *graph; IDirectMusicTool *tool; DMUS_PMSG *msg; @@ -3070,6 +3079,21 @@ static void test_notification_pmsg(void) &IID_IDirectMusicSegment, (void **)&segment); ok(hr == S_OK, "got %#lx\n", hr); + + /* native needs a track or the segment will end immediately */ + + hr = test_track_create(&track, FALSE); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSegment_InsertTrack(segment, track, 1); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicTrack_Release(track); + + /* native sends dirty / notification by batch of 1s, shorter length + * will cause all messages to be received immediately */ + length = 10005870 / 6510; + hr = IDirectMusicSegment_SetLength(segment, length); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicSegment8_Download((IDirectMusicSegment8 *)segment, (IUnknown *)performance); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicSegment8_Unload((IDirectMusicSegment8 *)segment, (IUnknown *)performance); @@ -3077,14 +3101,18 @@ static void test_notification_pmsg(void) hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, NULL); ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_GetTime(performance, NULL, &music_time); + ok(hr == S_OK, "got %#lx\n", hr); - ret = test_tool_wait_message(tool, 500, &msg); + ret = test_tool_wait_message(tool, 50, &msg); ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - ret = test_tool_wait_message(tool, 500, &msg); + ret = test_tool_wait_message(tool, 50, &msg); + todo_wine ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); + if (ret == WAIT_TIMEOUT) ret = test_tool_wait_message(tool, 500, &msg); ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); @@ -3104,22 +3132,24 @@ static void test_notification_pmsg(void) hr = IDirectMusicPerformance_AddNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); ok(hr == S_FALSE, "got %#lx\n", hr); - hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, NULL); + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, &state); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_GetTime(performance, NULL, &music_time); ok(hr == S_OK, "got %#lx\n", hr); - ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); + ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); + ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - ret = test_tool_wait_message(tool, 500, &msg); + ret = test_tool_wait_message(tool, 50, &msg); ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); @@ -3127,37 +3157,35 @@ static void test_notification_pmsg(void) ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND); + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); + ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND); + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - ret = test_tool_wait_message(tool, 500, &msg); + ret = test_tool_wait_message(tool, 50, &msg); ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); - ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); + ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - ret = test_tool_wait_message(tool, 100, &msg); + + /* wait enough time for notifications to be delivered */ + + ret = test_tool_wait_message(tool, 2000, &msg); ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); ok(!msg, "got %p\n", msg); - hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_PERFORMANCE); - ok(hr == S_OK, "got %#lx\n", hr); - hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); - ok(hr == S_OK, "got %#lx\n", hr); - /* notification messages are also queued for direct access */ @@ -3175,13 +3203,13 @@ static void test_notification_pmsg(void) hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); ok(hr == S_OK, "got %#lx\n", hr); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND); + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); ok(hr == S_OK, "got %#lx\n", hr); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND); + check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); @@ -3194,6 +3222,13 @@ static void test_notification_pmsg(void) hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); ok(hr == S_FALSE, "got %#lx\n", hr); + IDirectMusicSegmentState_Release(state); + + hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_PERFORMANCE); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); + ok(hr == S_OK, "got %#lx\n", hr); + /* RemoveNotificationType returns S_FALSE if already removed */ @@ -3205,10 +3240,12 @@ static void test_notification_pmsg(void) hr = IDirectMusicPerformance_AddNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); ok(hr == S_OK, "got %#lx\n", hr); - hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, NULL); + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, &state); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_GetTime(performance, NULL, &music_time); ok(hr == S_OK, "got %#lx\n", hr); - ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); + ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); @@ -3216,8 +3253,22 @@ static void test_notification_pmsg(void) hr = IDirectMusicPerformance_CloseDown(performance); ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); + ok(hr == S_FALSE, "got %#lx\n", hr); hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); ok(hr == S_FALSE, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 50, &msg); + ok(!ret, "got %#lx\n", ret); + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); + if (!ret) IDirectMusicPerformance_FreePMsg(performance, msg); + + IDirectMusicSegmentState_Release(state); IDirectMusicSegment_Release(segment); @@ -3856,7 +3907,7 @@ static void test_segment_state(void) hr = test_tool_create(message_types, ARRAY_SIZE(message_types), &tool); ok(hr == S_OK, "got %#lx\n", hr); - hr = test_track_create(&track); + hr = test_track_create(&track, TRUE); ok(hr == S_OK, "got %#lx\n", hr); hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, From a741b94661e274de49ba3a73d2ac3f0173981218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 24 Oct 2023 10:09:23 +0200 Subject: [PATCH 1055/1148] dmime: Use the current time if PlaySegmentEx start_time is 0. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index b58a7d9cd18..3fc5df80b58 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1203,8 +1203,8 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, { struct performance *This = impl_from_IDirectMusicPerformance8(iface); IDirectMusicSegmentState *state; + MUSIC_TIME length, music_time; IDirectMusicSegment *segment; - MUSIC_TIME length; HRESULT hr; FIXME("(%p, %p, %s, %p, %#lx, %I64d, %p, %p, %p): stub\n", This, source, debugstr_w(segment_name), @@ -1216,7 +1216,8 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, if (FAILED(hr = IUnknown_QueryInterface(source, &IID_IDirectMusicSegment, (void **)&segment))) return hr; - if (FAILED(hr = segment_state_create(segment, start_time, iface, &state))) + if ((!(music_time = start_time) && FAILED(hr = IDirectMusicPerformance8_GetTime(iface, NULL, &music_time))) + || FAILED(hr = segment_state_create(segment, music_time, iface, &state))) { IDirectMusicSegment_Release(segment); return hr; @@ -1224,27 +1225,27 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, hr = IDirectMusicSegment_GetLength(segment, &length); if (SUCCEEDED(hr)) - hr = performance_send_notification_pmsg(This, start_time, This->notification_performance, + hr = performance_send_notification_pmsg(This, music_time, This->notification_performance, GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED, NULL); if (SUCCEEDED(hr)) - hr = performance_send_notification_pmsg(This, start_time, This->notification_segment, + hr = performance_send_notification_pmsg(This, music_time, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART, (IUnknown *)state); if (SUCCEEDED(hr)) - hr = performance_send_pmsg(This, start_time, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL); + hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL); if (SUCCEEDED(hr)) hr = segment_state_play(state, iface); if (SUCCEEDED(hr)) - hr = performance_send_notification_pmsg(This, start_time + length, This->notification_segment, + hr = performance_send_notification_pmsg(This, music_time + length, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state); if (SUCCEEDED(hr)) - hr = performance_send_notification_pmsg(This, start_time + length, This->notification_segment, + hr = performance_send_notification_pmsg(This, music_time + length, This->notification_segment, GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state); if (SUCCEEDED(hr)) - hr = performance_send_pmsg(This, start_time + length, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL); + hr = performance_send_pmsg(This, music_time + length, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL); if (SUCCEEDED(hr)) - hr = performance_send_notification_pmsg(This, start_time + length, This->notification_performance, + hr = performance_send_notification_pmsg(This, music_time + length, This->notification_performance, GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED, NULL); if (SUCCEEDED(hr) && segment_state) From 8028bc15838c434d410a70498e2ae9c655858206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 25 Oct 2023 10:04:00 +0200 Subject: [PATCH 1056/1148] dmime: Send DMUS_NOTIFICATION_SEGALMOSTEND before DMUS_NOTIFICATION_SEGEND. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 6 +++--- dlls/dmime/tests/dmime.c | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 3fc5df80b58..83c2401c083 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1237,11 +1237,11 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, hr = segment_state_play(state, iface); if (SUCCEEDED(hr)) - hr = performance_send_notification_pmsg(This, music_time + length, This->notification_segment, - GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state); + hr = performance_send_notification_pmsg(This, music_time + length - 1450, This->notification_segment, + GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state); if (SUCCEEDED(hr)) hr = performance_send_notification_pmsg(This, music_time + length, This->notification_segment, - GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state); + GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state); if (SUCCEEDED(hr)) hr = performance_send_pmsg(This, music_time + length, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL); if (SUCCEEDED(hr)) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 35a6fbc98f4..dc9e98aab0f 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3019,8 +3019,6 @@ static void check_dmus_notification_pmsg_(int line, DMUS_NOTIFICATION_PMSG *msg, "got dwType %#lx\n", msg->dwType); ok_(__FILE__, line)(IsEqualGUID(&msg->guidNotificationType, type), "got guidNotificationType %s\n", debugstr_guid(&msg->guidNotificationType)); - todo_wine_if(IsEqualGUID(type, &GUID_NOTIFICATION_SEGMENT) && - (option == DMUS_NOTIFICATION_SEGALMOSTEND || option == DMUS_NOTIFICATION_SEGEND)) ok_(__FILE__, line)(msg->dwNotificationOption == option, "got dwNotificationOption %#lx\n", msg->dwNotificationOption); ok_(__FILE__, line)(!msg->dwField1, "got dwField1 %lu\n", msg->dwField1); From 7da868dcadb8b42e8927d12a1d99b9d88fb08743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 24 Oct 2023 10:09:23 +0200 Subject: [PATCH 1057/1148] dmime/tests: Check more notification / dirty messages fields. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 91 ++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 21 deletions(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index dc9e98aab0f..05dc653a935 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3011,12 +3011,50 @@ static void test_performance_pmsg(void) IDirectMusicTool_Release(tool); } -#define check_dmus_notification_pmsg(a, b, c) check_dmus_notification_pmsg_(__LINE__, a, b, c) -static void check_dmus_notification_pmsg_(int line, DMUS_NOTIFICATION_PMSG *msg, - const GUID *type, DWORD option) +#define check_dmus_dirty_pmsg(a, b) check_dmus_dirty_pmsg_(__LINE__, a, b) +static void check_dmus_dirty_pmsg_(int line, DMUS_PMSG *msg, MUSIC_TIME music_time) { - ok_(__FILE__, line)(msg->dwType == DMUS_PMSGT_NOTIFICATION, - "got dwType %#lx\n", msg->dwType); + ok_(__FILE__, line)(msg->dwSize == sizeof(*msg), "got dwSize %#lx\n", msg->dwSize); + ok_(__FILE__, line)(abs(msg->mtTime - music_time) <= 100, "got mtTime %ld\n", msg->mtTime); + ok_(__FILE__, line)(msg->dwFlags == (DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_REFTIME | DMUS_PMSGF_TOOL_IMMEDIATE), + "got dwFlags %#lx\n", msg->dwFlags); + ok_(__FILE__, line)(msg->dwPChannel == 0, "got dwPChannel %#lx\n", msg->dwPChannel); + ok_(__FILE__, line)(msg->dwVirtualTrackID == 0, "got dwVirtualTrackID %#lx\n", msg->dwVirtualTrackID); + ok_(__FILE__, line)(msg->pTool != 0, "got pTool %p\n", msg->pTool); + ok_(__FILE__, line)(msg->pGraph != 0, "got pGraph %p\n", msg->pGraph); + ok_(__FILE__, line)(msg->dwType == DMUS_PMSGT_DIRTY, "got dwType %#lx\n", msg->dwType); + ok_(__FILE__, line)(msg->dwVoiceID == 0, "got dwVoiceID %#lx\n", msg->dwVoiceID); + todo_wine ok_(__FILE__, line)(msg->dwGroupID == -1, "got dwGroupID %#lx\n", msg->dwGroupID); + ok_(__FILE__, line)(msg->punkUser == 0, "got punkUser %p\n", msg->punkUser); +} + +#define check_dmus_notification_pmsg(a, b, c, d, e, f) check_dmus_notification_pmsg_(__LINE__, a, b, c, d, e, f) +static void check_dmus_notification_pmsg_(int line, DMUS_NOTIFICATION_PMSG *msg, MUSIC_TIME music_time, DWORD flags, + const GUID *type, DWORD option, void *user) +{ + ok_(__FILE__, line)(msg->dwSize == sizeof(*msg), "got dwSize %#lx\n", msg->dwSize); + ok_(__FILE__, line)(msg->rtTime > 0, "got rtTime %I64d\n", msg->rtTime); + ok_(__FILE__, line)(abs(msg->mtTime - music_time) <= 100, "got mtTime %ld\n", msg->mtTime); + todo_wine_if(flags == DMUS_PMSGF_TOOL_ATTIME) + ok_(__FILE__, line)(msg->dwFlags == (DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_REFTIME | flags), + "got dwFlags %#lx\n", msg->dwFlags); + ok_(__FILE__, line)(msg->dwPChannel == 0, "got dwPChannel %#lx\n", msg->dwPChannel); + ok_(__FILE__, line)(msg->dwVirtualTrackID == 0, "got dwVirtualTrackID %#lx\n", msg->dwVirtualTrackID); + if (flags == DMUS_PMSGF_TOOL_ATTIME) + { + todo_wine ok_(__FILE__, line)(msg->pTool == 0, "got pTool %p\n", msg->pTool); + ok_(__FILE__, line)(msg->pGraph == 0, "got pGraph %p\n", msg->pGraph); + } + else + { + ok_(__FILE__, line)(msg->pTool != 0, "got pTool %p\n", msg->pTool); + ok_(__FILE__, line)(msg->pGraph != 0, "got pGraph %p\n", msg->pGraph); + } + ok_(__FILE__, line)(msg->dwType == DMUS_PMSGT_NOTIFICATION, "got dwType %#lx\n", msg->dwType); + ok_(__FILE__, line)(msg->dwVoiceID == 0, "got dwVoiceID %#lx\n", msg->dwVoiceID); + todo_wine ok_(__FILE__, line)(msg->dwGroupID == -1, "got dwGroupID %#lx\n", msg->dwGroupID); + ok_(__FILE__, line)(msg->punkUser == (IUnknown *)user, "got punkUser %p\n", msg->punkUser); + ok_(__FILE__, line)(IsEqualGUID(&msg->guidNotificationType, type), "got guidNotificationType %s\n", debugstr_guid(&msg->guidNotificationType)); ok_(__FILE__, line)(msg->dwNotificationOption == option, @@ -3104,7 +3142,7 @@ static void test_notification_pmsg(void) ret = test_tool_wait_message(tool, 50, &msg); ok(!ret, "got %#lx\n", ret); - ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + check_dmus_dirty_pmsg(msg, music_time); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); @@ -3112,7 +3150,7 @@ static void test_notification_pmsg(void) todo_wine ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); if (ret == WAIT_TIMEOUT) ret = test_tool_wait_message(tool, 500, &msg); ok(!ret, "got %#lx\n", ret); - ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + check_dmus_dirty_pmsg(msg, music_time + length); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); @@ -3137,43 +3175,48 @@ static void test_notification_pmsg(void) ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED); + check_dmus_notification_pmsg(notif, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, &GUID_NOTIFICATION_PERFORMANCE, + DMUS_NOTIFICATION_MUSICSTARTED, NULL); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); + check_dmus_notification_pmsg(notif, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, &GUID_NOTIFICATION_SEGMENT, + DMUS_NOTIFICATION_SEGSTART, state); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 50, &msg); ok(!ret, "got %#lx\n", ret); - ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + check_dmus_dirty_pmsg(msg, music_time); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND); + check_dmus_notification_pmsg(notif, music_time + length - 1450, DMUS_PMSGF_TOOL_IMMEDIATE, &GUID_NOTIFICATION_SEGMENT, + DMUS_NOTIFICATION_SEGALMOSTEND, state); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND); + check_dmus_notification_pmsg(notif, music_time + length, DMUS_PMSGF_TOOL_IMMEDIATE, &GUID_NOTIFICATION_SEGMENT, + DMUS_NOTIFICATION_SEGEND, state); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 50, &msg); ok(!ret, "got %#lx\n", ret); - ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + check_dmus_dirty_pmsg(msg, music_time + length); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED); + check_dmus_notification_pmsg(notif, music_time + length, DMUS_PMSGF_TOOL_IMMEDIATE, &GUID_NOTIFICATION_PERFORMANCE, + DMUS_NOTIFICATION_MUSICSTOPPED, NULL); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); @@ -3189,31 +3232,36 @@ static void test_notification_pmsg(void) hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); ok(hr == S_OK, "got %#lx\n", hr); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED); + check_dmus_notification_pmsg(notif, music_time, DMUS_PMSGF_TOOL_ATTIME, &GUID_NOTIFICATION_PERFORMANCE, + DMUS_NOTIFICATION_MUSICSTARTED, NULL); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); ok(hr == S_OK, "got %#lx\n", hr); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); + check_dmus_notification_pmsg(notif, music_time, DMUS_PMSGF_TOOL_ATTIME, &GUID_NOTIFICATION_SEGMENT, + DMUS_NOTIFICATION_SEGSTART, state); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); ok(hr == S_OK, "got %#lx\n", hr); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND); + check_dmus_notification_pmsg(notif, music_time + length - 1450, DMUS_PMSGF_TOOL_ATTIME, &GUID_NOTIFICATION_SEGMENT, + DMUS_NOTIFICATION_SEGALMOSTEND, state); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); ok(hr == S_OK, "got %#lx\n", hr); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND); + check_dmus_notification_pmsg(notif, music_time + length, DMUS_PMSGF_TOOL_ATTIME, &GUID_NOTIFICATION_SEGMENT, + DMUS_NOTIFICATION_SEGEND, state); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); hr = IDirectMusicPerformance_GetNotificationPMsg(performance, ¬if); ok(hr == S_OK, "got %#lx\n", hr); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED); + check_dmus_notification_pmsg(notif, music_time + length, DMUS_PMSGF_TOOL_ATTIME, &GUID_NOTIFICATION_PERFORMANCE, + DMUS_NOTIFICATION_MUSICSTOPPED, NULL); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); @@ -3245,7 +3293,8 @@ static void test_notification_pmsg(void) ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); ok(!ret, "got %#lx\n", ret); - check_dmus_notification_pmsg(notif, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART); + check_dmus_notification_pmsg(notif, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, &GUID_NOTIFICATION_SEGMENT, + DMUS_NOTIFICATION_SEGSTART, state); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); @@ -3258,7 +3307,7 @@ static void test_notification_pmsg(void) ret = test_tool_wait_message(tool, 50, &msg); ok(!ret, "got %#lx\n", ret); - ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + check_dmus_dirty_pmsg(msg, music_time); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); From 3400cde05800fc8b5d4b473659c5aa9a7fea2674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 24 Oct 2023 14:53:48 +0200 Subject: [PATCH 1058/1148] dmband: Support start_time and end_time Play parameters. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/bandtrack.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index 0c6aa81dd5f..e8ebc234f73 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -159,7 +159,7 @@ static HRESULT WINAPI band_track_EndPlay(IDirectMusicTrack8 *iface, void *state_ } static HRESULT WINAPI band_track_Play(IDirectMusicTrack8 *iface, void *state_data, - MUSIC_TIME start_time, MUSIC_TIME end_time, MUSIC_TIME time_offset, DWORD segment_flags, + MUSIC_TIME start_time, MUSIC_TIME end_time, MUSIC_TIME time_offset, DWORD track_flags, IDirectMusicPerformance *performance, IDirectMusicSegmentState *segment_state, DWORD track_id) { struct band_track *This = impl_from_IDirectMusicTrack8(iface); @@ -168,13 +168,11 @@ static HRESULT WINAPI band_track_Play(IDirectMusicTrack8 *iface, void *state_dat HRESULT hr; TRACE("(%p, %p, %ld, %ld, %ld, %#lx, %p, %p, %ld)\n", This, state_data, start_time, end_time, - time_offset, segment_flags, performance, segment_state, track_id); + time_offset, track_flags, performance, segment_state, track_id); if (!performance) return DMUS_S_END; - if (start_time != 0) FIXME("start_time %ld not implemented\n", start_time); - if (end_time != -1) FIXME("end_time %ld not implemented\n", end_time); - if (segment_flags) FIXME("segment_flags %#lx not implemented\n", segment_flags); + if (track_flags) FIXME("track_flags %#lx not implemented\n", track_flags); if (segment_state) FIXME("segment_state %p not implemented\n", segment_state); if (FAILED(hr = IDirectMusicPerformance_QueryInterface(performance, @@ -184,7 +182,13 @@ static HRESULT WINAPI band_track_Play(IDirectMusicTrack8 *iface, void *state_dat LIST_FOR_EACH_ENTRY(entry, &This->bands, struct band_entry, entry) { MUSIC_TIME music_time = entry->head.lBandTimeLogical; - if (music_time != -1) music_time += time_offset; + if (music_time == -1 && !(track_flags & DMUS_TRACKF_START)) continue; + else if (music_time != -1) + { + if (music_time < start_time || music_time >= end_time) continue; + music_time += time_offset; + } + if (FAILED(hr = band_send_messages(entry->band, performance, graph, music_time, track_id))) break; } From e413a3591c0d0efefda1b1e38b5f2e867e3664a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 24 Oct 2023 14:54:23 +0200 Subject: [PATCH 1059/1148] dmime: Support start_time and end_time Play parameters. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/seqtrack.c | 8 ++++++-- dlls/dmime/wavetrack.c | 11 ++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/dlls/dmime/seqtrack.c b/dlls/dmime/seqtrack.c index 997bff241d8..586bc742472 100644 --- a/dlls/dmime/seqtrack.c +++ b/dlls/dmime/seqtrack.c @@ -120,8 +120,6 @@ static HRESULT WINAPI sequence_track_Play(IDirectMusicTrack8 *iface, void *state TRACE("(%p, %p, %ld, %ld, %ld, %#lx, %p, %p, %ld)\n", This, state_data, start_time, end_time, time_offset, segment_flags, performance, segment_state, track_id); - if (start_time != 0) FIXME("start_time %ld not implemented\n", start_time); - if (end_time != -1) FIXME("end_time %ld not implemented\n", end_time); if (segment_flags) FIXME("segment_flags %#lx not implemented\n", segment_flags); if (segment_state) FIXME("segment_state %p not implemented\n", segment_state); @@ -134,6 +132,9 @@ static HRESULT WINAPI sequence_track_Play(IDirectMusicTrack8 *iface, void *state DMUS_IO_SEQ_ITEM *item = This->items + i; DMUS_NOTE_PMSG *msg; + if (item->mtTime < start_time) continue; + if (item->mtTime >= end_time) continue; + if (FAILED(hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(*msg), (DMUS_PMSG **)&msg))) break; @@ -164,6 +165,9 @@ static HRESULT WINAPI sequence_track_Play(IDirectMusicTrack8 *iface, void *state DMUS_IO_CURVE_ITEM *item = This->curve_items + i; DMUS_CURVE_PMSG *msg; + if (item->mtStart < start_time) continue; + if (item->mtStart >= end_time) continue; + if (FAILED(hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(*msg), (DMUS_PMSG **)&msg))) break; diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index 974e656cfe5..7f2fe4a8e5f 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -144,7 +144,7 @@ static HRESULT WINAPI wave_track_EndPlay(IDirectMusicTrack8 *iface, void *pState } static HRESULT WINAPI wave_track_Play(IDirectMusicTrack8 *iface, void *state_data, - MUSIC_TIME start_time, MUSIC_TIME end_time, MUSIC_TIME time_offset, DWORD segment_flags, + MUSIC_TIME start_time, MUSIC_TIME end_time, MUSIC_TIME time_offset, DWORD track_flags, IDirectMusicPerformance *performance, IDirectMusicSegmentState *segment_state, DWORD track_id) { struct wave_track *This = impl_from_IDirectMusicTrack8(iface); @@ -155,12 +155,11 @@ static HRESULT WINAPI wave_track_Play(IDirectMusicTrack8 *iface, void *state_dat HRESULT hr; TRACE("(%p, %p, %ld, %ld, %ld, %#lx, %p, %p, %ld)\n", This, state_data, start_time, end_time, - time_offset, segment_flags, performance, segment_state, track_id); + time_offset, track_flags, performance, segment_state, track_id); - if (start_time != 0) FIXME("start_time %ld not implemented\n", start_time); - if (end_time != -1) FIXME("end_time %ld not implemented\n", end_time); - if (segment_flags) FIXME("segment_flags %#lx not implemented\n", segment_flags); + if (track_flags) FIXME("track_flags %#lx not implemented\n", track_flags); if (segment_state) FIXME("segment_state %p not implemented\n", segment_state); + if (!(track_flags & DMUS_TRACKF_START)) return S_OK; if (FAILED(hr = IDirectMusicPerformance_QueryInterface(performance, &IID_IDirectMusicGraph, (void **)&graph))) @@ -175,6 +174,8 @@ static HRESULT WINAPI wave_track_Play(IDirectMusicTrack8 *iface, void *state_dat DMUS_WAVE_PMSG *msg; if (!item->buffer) continue; + if (item->header.rtTime < start_time) continue; + if (item->header.rtTime >= end_time) continue; if (FAILED(hr = IDirectMusicPerformance_AllocPMsg(performance, sizeof(*msg), (DMUS_PMSG **)&msg))) From 7d2ca4479f45c87b0277651ae88164e517993d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 24 Oct 2023 14:31:42 +0200 Subject: [PATCH 1060/1148] dmime: Fix performance message requeue-ing from the message thread. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 83c2401c083..7ab11773314 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -88,6 +88,19 @@ static inline struct message *message_from_DMUS_PMSG(DMUS_PMSG *msg) return msg ? CONTAINING_RECORD(msg, struct message, msg) : NULL; } +static void performance_queue_message(struct performance *This, struct message *message, struct list *hint) +{ + struct message *prev; + + LIST_FOR_EACH_ENTRY_REV(prev, hint ? hint : &This->messages, struct message, entry) + { + if (&prev->entry == &This->messages) break; + if (prev->msg.rtTime <= message->msg.rtTime) break; + } + + list_add_after(&prev->entry, &message->entry); +} + static HRESULT performance_process_message(struct performance *This, DMUS_PMSG *msg, DWORD *timeout) { static const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME; @@ -133,8 +146,8 @@ static HRESULT performance_process_message(struct performance *This, DMUS_PMSG * static DWORD WINAPI message_thread_proc(void *args) { struct performance *This = args; - struct message *message, *next; - HRESULT hr; + HRESULT hr = DMUS_S_REQUEUE; + struct list *ptr; TRACE("performance %p message thread\n", This); SetThreadDescription(GetCurrentThread(), L"wine_dmime_message"); @@ -145,13 +158,15 @@ static DWORD WINAPI message_thread_proc(void *args) { DWORD timeout = INFINITE; - LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry) + while ((ptr = list_head(&This->messages))) { + struct message *message = LIST_ENTRY(ptr, struct message, entry); + struct list *next = ptr->next; list_remove(&message->entry); list_init(&message->entry); hr = performance_process_message(This, &message->msg, &timeout); - if (hr == DMUS_S_REQUEUE) list_add_before(&next->entry, &message->entry); + if (hr == DMUS_S_REQUEUE) performance_queue_message(This, message, next); if (hr != S_OK) break; } @@ -486,7 +501,7 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS { const DWORD delivery_flags = DMUS_PMSGF_TOOL_IMMEDIATE | DMUS_PMSGF_TOOL_QUEUE | DMUS_PMSGF_TOOL_ATTIME; struct performance *This = impl_from_IDirectMusicPerformance8(iface); - struct message *message, *next; + struct message *message; HRESULT hr; TRACE("(%p, %p)\n", This, msg); @@ -523,10 +538,7 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS if (hr != DMUS_S_REQUEUE) goto done; } - LIST_FOR_EACH_ENTRY(next, &This->messages, struct message, entry) - if (next->msg.rtTime > message->msg.rtTime) break; - list_add_before(&next->entry, &message->entry); - + performance_queue_message(This, message, NULL); hr = S_OK; } From 1d5f8c1268926f654bb210c744f7d10f1b95c5b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 25 Oct 2023 09:37:24 +0200 Subject: [PATCH 1061/1148] dmime: Implement segment state chunked playback. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 3 ++ dlls/dmime/performance.c | 66 +++++++++++++++++++++++++++++++++----- dlls/dmime/segmentstate.c | 53 ++++++++++++++++++++++++++---- 3 files changed, 107 insertions(+), 15 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 28836694ed9..212ee254192 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -75,12 +75,15 @@ extern void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath*,IDirectSo extern HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, IDirectMusicPerformance8 *performance, IDirectMusicSegmentState **ret_iface); extern HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); +extern HRESULT segment_state_tick(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); extern HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); extern HRESULT wave_track_create_from_chunk(IStream *stream, struct chunk_entry *parent, IDirectMusicTrack8 **ret_iface); extern HRESULT performance_get_dsound(IDirectMusicPerformance8 *iface, IDirectSound **dsound); +extern HRESULT performance_send_segment_tick(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, + IDirectMusicSegmentState *state); extern HRESULT performance_send_segment_end(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, IDirectMusicSegmentState *state); diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 7ab11773314..6f77f8677ba 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -28,6 +28,7 @@ enum dmus_internal_message_type { DMUS_PMSGT_INTERNAL_FIRST = 0x10, DMUS_PMSGT_INTERNAL_SEGMENT_END = DMUS_PMSGT_INTERNAL_FIRST, + DMUS_PMSGT_INTERNAL_SEGMENT_TICK, }; struct pchannel_block { @@ -275,6 +276,43 @@ static inline struct performance *impl_from_IDirectMusicPerformance8(IDirectMusi return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface); } +static HRESULT performance_send_segment_start(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, + IDirectMusicSegmentState *state) +{ + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + HRESULT hr; + + if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_performance, + GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED, NULL))) + return hr; + if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment, + GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART, (IUnknown *)state))) + return hr; + if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, + DMUS_PMSGT_DIRTY, NULL))) + return hr; + + return S_OK; +} + +HRESULT performance_send_segment_tick(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, + IDirectMusicSegmentState *state) +{ + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + REFERENCE_TIME time; + HRESULT hr; + + if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(iface, music_time, &time))) + return hr; + if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(iface, time + 2000000, &music_time))) + return hr; + if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_QUEUE, + DMUS_PMSGT_INTERNAL_SEGMENT_TICK, (IUnknown *)state))) + return hr; + + return S_OK; +} + HRESULT performance_send_segment_end(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, IDirectMusicSegmentState *state) { @@ -1235,16 +1273,11 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, return hr; } + EnterCriticalSection(&This->safe); + hr = IDirectMusicSegment_GetLength(segment, &length); if (SUCCEEDED(hr)) - hr = performance_send_notification_pmsg(This, music_time, This->notification_performance, - GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTARTED, NULL); - if (SUCCEEDED(hr)) - hr = performance_send_notification_pmsg(This, music_time, This->notification_segment, - GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGSTART, (IUnknown *)state); - if (SUCCEEDED(hr)) - hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL); - + hr = performance_send_segment_start(iface, music_time, state); if (SUCCEEDED(hr)) hr = segment_state_play(state, iface); @@ -1266,6 +1299,8 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, IDirectMusicSegmentState_AddRef(state); } + LeaveCriticalSection(&This->safe); + IDirectMusicSegmentState_Release(state); IDirectMusicSegment_Release(segment); return hr; @@ -1839,6 +1874,21 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, WARN("Failed to play wave buffer, hr %#lx\n", hr); break; + case DMUS_PMSGT_INTERNAL_SEGMENT_TICK: + msg->rtTime += 10000000; + msg->dwFlags &= ~DMUS_PMSGF_MUSICTIME; + + /* re-send the tick message until segment_state_tick returns S_FALSE */ + if (FAILED(hr = segment_state_tick((IDirectMusicSegmentState *)msg->punkUser, + (IDirectMusicPerformance8 *)performance))) + ERR("Failed to tick segment state %p, hr %#lx\n", msg->punkUser, hr); + else if (hr == S_FALSE) + return DMUS_S_FREE; /* done ticking */ + else if (FAILED(hr = IDirectMusicPerformance_SendPMsg(performance, msg))) + ERR("Failed to queue tick for segment state %p, hr %#lx\n", msg->punkUser, hr); + + return S_OK; + case DMUS_PMSGT_INTERNAL_SEGMENT_END: if (FAILED(hr = segment_state_end_play((IDirectMusicSegmentState *)msg->punkUser, (IDirectMusicPerformance8 *)performance))) diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index 21b98ccc14c..fa49d06836b 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -51,6 +51,7 @@ struct segment_state MUSIC_TIME start_time; MUSIC_TIME start_point; MUSIC_TIME end_point; + MUSIC_TIME played; BOOL auto_download; struct list tracks; @@ -271,25 +272,63 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time return hr; } -HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance) +static HRESULT segment_state_play_chunk(struct segment_state *This, IDirectMusicPerformance8 *performance, + REFERENCE_TIME duration, DWORD track_flags) { - struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); - DWORD track_flags = DMUS_TRACKF_DIRTY | DMUS_TRACKF_START | DMUS_TRACKF_SEEK; - MUSIC_TIME start_time = This->start_point, end_time = This->end_point; + IDirectMusicSegmentState *iface = (IDirectMusicSegmentState *)&This->IDirectMusicSegmentState8_iface; + MUSIC_TIME next_time, played; struct track_entry *entry; + REFERENCE_TIME time; HRESULT hr = S_OK; + if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(performance, + This->start_time + This->played, &time))) + return hr; + if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(performance, + time + duration, &next_time))) + return hr; + played = min(next_time - This->start_time, This->end_point - This->start_point); + LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) { - if (FAILED(hr = IDirectMusicTrack_Play(entry->track, entry->state_data, start_time, end_time, - This->start_time, track_flags, (IDirectMusicPerformance *)performance, iface, entry->track_id))) + if (FAILED(hr = IDirectMusicTrack_Play(entry->track, entry->state_data, + This->start_point + This->played, This->start_point + played, + This->start_time, track_flags, (IDirectMusicPerformance *)performance, + iface, entry->track_id))) { WARN("Failed to play track %p, hr %#lx\n", entry->track, hr); break; } } - return hr; + This->played = played; + if (This->start_point + This->played >= This->end_point) + return S_FALSE; + return S_OK; +} + +HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance) +{ + struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); + HRESULT hr; + + TRACE("%p %p\n", iface, performance); + + if (FAILED(hr = segment_state_play_chunk(This, performance, 10000000, + DMUS_TRACKF_START | DMUS_TRACKF_SEEK | DMUS_TRACKF_DIRTY))) + return hr; + + if (hr == S_FALSE) return S_OK; + return performance_send_segment_tick(performance, This->start_time, iface); +} + +HRESULT segment_state_tick(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance) +{ + struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); + + TRACE("%p %p\n", iface, performance); + + return segment_state_play_chunk(This, performance, 10000000, 0); } HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance) From 9ee93c7779d3d887141c651dfd272217ca67f679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 25 Oct 2023 09:47:48 +0200 Subject: [PATCH 1062/1148] dmime: Send notification messages from segment_play_chunk. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 2 ++ dlls/dmime/performance.c | 38 +++++++++++++++++--------------------- dlls/dmime/segmentstate.c | 22 ++++++++++++++++++---- dlls/dmime/tests/dmime.c | 7 +++---- 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 212ee254192..e1a2374facf 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -82,6 +82,8 @@ extern HRESULT wave_track_create_from_chunk(IStream *stream, struct chunk_entry IDirectMusicTrack8 **ret_iface); extern HRESULT performance_get_dsound(IDirectMusicPerformance8 *iface, IDirectSound **dsound); +extern HRESULT performance_send_segment_start(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, + IDirectMusicSegmentState *state); extern HRESULT performance_send_segment_tick(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, IDirectMusicSegmentState *state); extern HRESULT performance_send_segment_end(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 6f77f8677ba..a08c00502d9 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -276,7 +276,7 @@ static inline struct performance *impl_from_IDirectMusicPerformance8(IDirectMusi return CONTAINING_RECORD(iface, struct performance, IDirectMusicPerformance8_iface); } -static HRESULT performance_send_segment_start(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, +HRESULT performance_send_segment_start(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, IDirectMusicSegmentState *state) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); @@ -319,6 +319,18 @@ HRESULT performance_send_segment_end(IDirectMusicPerformance8 *iface, MUSIC_TIME struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr; + if (FAILED(hr = performance_send_notification_pmsg(This, music_time - 1450, This->notification_segment, + GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state))) + return hr; + if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment, + GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state))) + return hr; + if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, + DMUS_PMSGT_DIRTY, NULL))) + return hr; + if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_performance, + GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED, NULL))) + return hr; if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_ATTIME, DMUS_PMSGT_INTERNAL_SEGMENT_END, (IUnknown *)state))) return hr; @@ -1253,8 +1265,8 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, { struct performance *This = impl_from_IDirectMusicPerformance8(iface); IDirectMusicSegmentState *state; - MUSIC_TIME length, music_time; IDirectMusicSegment *segment; + MUSIC_TIME music_time; HRESULT hr; FIXME("(%p, %p, %s, %p, %#lx, %I64d, %p, %p, %p): stub\n", This, source, debugstr_w(segment_name), @@ -1275,25 +1287,9 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, EnterCriticalSection(&This->safe); - hr = IDirectMusicSegment_GetLength(segment, &length); - if (SUCCEEDED(hr)) - hr = performance_send_segment_start(iface, music_time, state); - if (SUCCEEDED(hr)) - hr = segment_state_play(state, iface); - - if (SUCCEEDED(hr)) - hr = performance_send_notification_pmsg(This, music_time + length - 1450, This->notification_segment, - GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state); - if (SUCCEEDED(hr)) - hr = performance_send_notification_pmsg(This, music_time + length, This->notification_segment, - GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state); - if (SUCCEEDED(hr)) - hr = performance_send_pmsg(This, music_time + length, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL); - if (SUCCEEDED(hr)) - hr = performance_send_notification_pmsg(This, music_time + length, This->notification_performance, - GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED, NULL); - - if (SUCCEEDED(hr) && segment_state) + if (FAILED(hr = segment_state_play(state, iface))) + ERR("Failed to play segment state, hr %#lx\n", hr); + else if (segment_state) { *segment_state = state; IDirectMusicSegmentState_AddRef(state); diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index fa49d06836b..f2f895de881 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -215,8 +215,8 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time IDirectMusicSegmentState *iface; struct segment_state *This; IDirectMusicTrack *track; - UINT i, duration; HRESULT hr; + UINT i; TRACE("(%p, %lu, %p)\n", segment, start_time, ret_iface); @@ -264,9 +264,6 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time } } - duration = This->end_point - This->start_point; - if (SUCCEEDED(hr)) hr = performance_send_segment_end(performance, start_time + duration, iface); - if (SUCCEEDED(hr)) *ret_iface = iface; else IDirectMusicSegmentState_Release(iface); return hr; @@ -303,7 +300,18 @@ static HRESULT segment_state_play_chunk(struct segment_state *This, IDirectMusic This->played = played; if (This->start_point + This->played >= This->end_point) + { + MUSIC_TIME end_time = This->start_time + This->played; + + if (FAILED(hr = performance_send_segment_end(performance, end_time, iface))) + { + ERR("Failed to send segment end, hr %#lx\n", hr); + return hr; + } + return S_FALSE; + } + return S_OK; } @@ -314,6 +322,12 @@ HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerforma TRACE("%p %p\n", iface, performance); + if (FAILED(hr = performance_send_segment_start(performance, This->start_time, iface))) + { + ERR("Failed to send segment start, hr %#lx\n", hr); + return hr; + } + if (FAILED(hr = segment_state_play_chunk(This, performance, 10000000, DMUS_TRACKF_START | DMUS_TRACKF_SEEK | DMUS_TRACKF_DIRTY))) return hr; diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 05dc653a935..79fbb902868 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3147,8 +3147,8 @@ static void test_notification_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 50, &msg); - todo_wine ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); - if (ret == WAIT_TIMEOUT) ret = test_tool_wait_message(tool, 500, &msg); + ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); + ret = test_tool_wait_message(tool, 500, &msg); ok(!ret, "got %#lx\n", ret); check_dmus_dirty_pmsg(msg, music_time + length); hr = IDirectMusicPerformance_FreePMsg(performance, msg); @@ -3312,8 +3312,7 @@ static void test_notification_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 500, &msg); - todo_wine ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); - if (!ret) IDirectMusicPerformance_FreePMsg(performance, msg); + ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); IDirectMusicSegmentState_Release(state); IDirectMusicSegment_Release(segment); From 5c2c98e034d1946d26677d54dcea16c847a83100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 23:59:27 +0200 Subject: [PATCH 1063/1148] dmime/tests: Test IDirectMusicPerformance_GetSegmentState. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 79fbb902868..b15c3a1ac37 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -4053,6 +4053,12 @@ static void test_segment_state(void) check_track_state(track, initialized, FALSE); check_track_state(track, playing, FALSE); + hr = IDirectMusicPerformance_GetSegmentState(performance, NULL, 0); + todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicPerformance_GetSegmentState(performance, &tmp_state, 0); + todo_wine ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + + tmp_state = state; state = (void *)0xdeadbeef; hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 20, &state); @@ -4062,6 +4068,22 @@ static void test_segment_state(void) ok(state != tmp_state, "got %p\n", state); IDirectMusicSegmentState_Release(tmp_state); + tmp_state = (void *)0xdeadbeef; + hr = IDirectMusicPerformance_GetSegmentState(performance, &tmp_state, 0); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(state == tmp_state, "got %p\n", state); + if (tmp_state != (void *)0xdeadbeef) IDirectMusicSegmentState_Release(tmp_state); + + tmp_state = (void *)0xdeadbeef; + hr = IDirectMusicPerformance_GetSegmentState(performance, &tmp_state, 69); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(state == tmp_state, "got %p\n", state); + if (tmp_state != (void *)0xdeadbeef) IDirectMusicSegmentState_Release(tmp_state); + + hr = IDirectMusicPerformance_GetSegmentState(performance, &tmp_state, 70); + todo_wine ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + + check_track_state(track, downloaded, FALSE); check_track_state(track, initialized, TRUE); @@ -4074,6 +4096,9 @@ static void test_segment_state(void) ret = test_track_wait_playing(track, 50); ok(ret == 0, "got %#lx\n", ret); + hr = IDirectMusicPerformance_GetSegmentState(performance, &tmp_state, 0); + todo_wine ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + tmp_segment = (void *)0xdeadbeef; hr = IDirectMusicSegmentState_GetSegment(state, &tmp_segment); From 2224d6d34387b6b99ee79a24db835cd967e47ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 24 Oct 2023 23:27:57 +0200 Subject: [PATCH 1064/1148] dmime: Implement IDirectMusicPerformance_GetSegmentState semi-stub. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 1 + dlls/dmime/performance.c | 91 +++++++++++++++++++++++++++++++++++--- dlls/dmime/segmentstate.c | 6 +++ dlls/dmime/tests/dmime.c | 12 ++--- 4 files changed, 98 insertions(+), 12 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index e1a2374facf..3c7fc1e332b 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -77,6 +77,7 @@ extern HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME sta extern HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); extern HRESULT segment_state_tick(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); extern HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); +extern BOOL segment_state_has_segment(IDirectMusicSegmentState *iface, IDirectMusicSegment *segment); extern HRESULT wave_track_create_from_chunk(IStream *stream, struct chunk_entry *parent, IDirectMusicTrack8 **ret_iface); diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index a08c00502d9..17fbe772f5b 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -76,6 +76,8 @@ struct performance HANDLE notification_event; BOOL notification_performance; BOOL notification_segment; + + IDirectMusicSegment *primary_segment; }; struct message @@ -338,6 +340,12 @@ HRESULT performance_send_segment_end(IDirectMusicPerformance8 *iface, MUSIC_TIME return S_OK; } +static void performance_set_primary_segment(struct performance *This, IDirectMusicSegment *segment) +{ + if (This->primary_segment) IDirectMusicSegment_Release(This->primary_segment); + if ((This->primary_segment = segment)) IDirectMusicSegment_AddRef(This->primary_segment); +} + /* IDirectMusicPerformance8 IUnknown part: */ static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ret_iface) { @@ -487,6 +495,42 @@ static HRESULT WINAPI performance_PlaySegment(IDirectMusicPerformance8 *iface, I segment_flags, start_time, ret_state, NULL, NULL); } +struct state_entry +{ + struct list entry; + IDirectMusicSegmentState *state; +}; + +static void state_entry_destroy(struct state_entry *entry) +{ + list_remove(&entry->entry); + IDirectMusicSegmentState_Release(entry->state); + free(entry); +} + +static void enum_segment_states(struct performance *This, IDirectMusicSegment *segment, struct list *list) +{ + struct state_entry *entry; + struct message *message; + + LIST_FOR_EACH_ENTRY(message, &This->messages, struct message, entry) + { + IDirectMusicSegmentState *message_state; + + if (message->msg.dwType != DMUS_PMSGT_INTERNAL_SEGMENT_TICK + && message->msg.dwType != DMUS_PMSGT_INTERNAL_SEGMENT_END) + continue; + + message_state = (IDirectMusicSegmentState *)message->msg.punkUser; + if (segment && !segment_state_has_segment(message_state, segment)) continue; + + if (!(entry = malloc(sizeof(*entry)))) return; + entry->state = message_state; + IDirectMusicSegmentState_AddRef(entry->state); + list_add_tail(list, &entry->entry); + } +} + static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, IDirectMusicSegmentState *pSegmentState, MUSIC_TIME mtTime, DWORD dwFlags) { @@ -497,12 +541,36 @@ static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectM } static HRESULT WINAPI performance_GetSegmentState(IDirectMusicPerformance8 *iface, - IDirectMusicSegmentState **ppSegmentState, MUSIC_TIME mtTime) + IDirectMusicSegmentState **state, MUSIC_TIME time) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct list *ptr, states = LIST_INIT(states); + struct state_entry *entry, *next; + HRESULT hr = S_OK; - FIXME("(%p,%p, %ld): stub\n", This, ppSegmentState, mtTime); - return S_OK; + TRACE("(%p, %p, %ld)\n", This, state, time); + + if (!state) return E_POINTER; + + EnterCriticalSection(&This->safe); + + enum_segment_states(This, This->primary_segment, &states); + + if (!(ptr = list_head(&states))) hr = DMUS_E_NOT_FOUND; + else + { + entry = LIST_ENTRY(ptr, struct state_entry, entry); + + *state = entry->state; + IDirectMusicSegmentState_AddRef(entry->state); + + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &states, struct state_entry, entry) + state_entry_destroy(entry); + } + + LeaveCriticalSection(&This->safe); + + return hr; } static HRESULT WINAPI performance_SetPrepareTime(IDirectMusicPerformance8 *iface, DWORD dwMilliSeconds) @@ -1152,6 +1220,8 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) WARN("Failed to free message %p, hr %#lx\n", message, hr); } + performance_set_primary_segment(This, NULL); + if (This->master_clock) { IReferenceClock_Release(This->master_clock); @@ -1278,17 +1348,26 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, if (FAILED(hr = IUnknown_QueryInterface(source, &IID_IDirectMusicSegment, (void **)&segment))) return hr; + + EnterCriticalSection(&This->safe); + + performance_set_primary_segment(This, segment); + if ((!(music_time = start_time) && FAILED(hr = IDirectMusicPerformance8_GetTime(iface, NULL, &music_time))) || FAILED(hr = segment_state_create(segment, music_time, iface, &state))) { + performance_set_primary_segment(This, NULL); + LeaveCriticalSection(&This->safe); + IDirectMusicSegment_Release(segment); return hr; } - EnterCriticalSection(&This->safe); - if (FAILED(hr = segment_state_play(state, iface))) + { ERR("Failed to play segment state, hr %#lx\n", hr); + performance_set_primary_segment(This, NULL); + } else if (segment_state) { *segment_state = state; diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index f2f895de881..3e13687254e 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -363,3 +363,9 @@ HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerf return S_OK; } + +BOOL segment_state_has_segment(IDirectMusicSegmentState *iface, IDirectMusicSegment *segment) +{ + struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); + return !segment || This->segment == segment; +} diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index b15c3a1ac37..7d2dd912237 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -4054,9 +4054,9 @@ static void test_segment_state(void) check_track_state(track, playing, FALSE); hr = IDirectMusicPerformance_GetSegmentState(performance, NULL, 0); - todo_wine ok(hr == E_POINTER, "got %#lx\n", hr); + ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicPerformance_GetSegmentState(performance, &tmp_state, 0); - todo_wine ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); + ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); tmp_state = state; @@ -4071,14 +4071,14 @@ static void test_segment_state(void) tmp_state = (void *)0xdeadbeef; hr = IDirectMusicPerformance_GetSegmentState(performance, &tmp_state, 0); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(state == tmp_state, "got %p\n", state); - if (tmp_state != (void *)0xdeadbeef) IDirectMusicSegmentState_Release(tmp_state); + ok(state == tmp_state, "got %p\n", state); + IDirectMusicSegmentState_Release(tmp_state); tmp_state = (void *)0xdeadbeef; hr = IDirectMusicPerformance_GetSegmentState(performance, &tmp_state, 69); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(state == tmp_state, "got %p\n", state); - if (tmp_state != (void *)0xdeadbeef) IDirectMusicSegmentState_Release(tmp_state); + ok(state == tmp_state, "got %p\n", state); + IDirectMusicSegmentState_Release(tmp_state); hr = IDirectMusicPerformance_GetSegmentState(performance, &tmp_state, 70); todo_wine ok(hr == DMUS_E_NOT_FOUND, "got %#lx\n", hr); From 93afd7a88854581aeee09e78f57669e8af289070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 19:52:40 +0200 Subject: [PATCH 1065/1148] dmime/tests: Test tempo track Play and DMUS_PMSGT_TEMPO messages. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 174 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 7d2dd912237..344571d2801 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3888,6 +3888,179 @@ static void test_band_track_play(void) IDirectMusicTool_Release(tool); } +#define check_dmus_tempo_pmsg(a, b, c) check_dmus_tempo_pmsg_(__LINE__, a, b, c) +static void check_dmus_tempo_pmsg_(int line, DMUS_TEMPO_PMSG *msg, MUSIC_TIME time, double tempo) +{ + ok_(__FILE__, line)(msg->dwSize == sizeof(*msg), "got dwSize %lu\n", msg->dwSize); + ok_(__FILE__, line)(msg->rtTime != 0, "got rtTime %I64u\n", msg->rtTime); + ok_(__FILE__, line)(abs(msg->mtTime - time) < 10, "got mtTime %lu\n", msg->mtTime); + ok_(__FILE__, line)(!msg->dwPChannel, "got dwPChannel %lu\n", msg->dwPChannel); + ok_(__FILE__, line)(!!msg->dwVirtualTrackID, "got dwVirtualTrackID %lu\n", msg->dwVirtualTrackID); + ok_(__FILE__, line)(msg->dwType == DMUS_PMSGT_TEMPO, "got dwType %#lx\n", msg->dwType); + ok_(__FILE__, line)(!msg->dwVoiceID, "got dwVoiceID %lu\n", msg->dwVoiceID); + ok_(__FILE__, line)(msg->dwGroupID == -1, "got dwGroupID %lu\n", msg->dwGroupID); + ok_(__FILE__, line)(!msg->punkUser, "got punkUser %p\n", msg->punkUser); + ok_(__FILE__, line)(msg->dblTempo == tempo, "got tempo %f\n", msg->dblTempo); +} + +static void test_tempo_track_play(void) +{ + static const DWORD message_types[] = + { + DMUS_PMSGT_MIDI, + DMUS_PMSGT_NOTE, + DMUS_PMSGT_SYSEX, + DMUS_PMSGT_NOTIFICATION, + DMUS_PMSGT_TEMPO, + DMUS_PMSGT_CURVE, + DMUS_PMSGT_TIMESIG, + DMUS_PMSGT_PATCH, + DMUS_PMSGT_TRANSPOSE, + DMUS_PMSGT_CHANNEL_PRIORITY, + DMUS_PMSGT_STOP, + DMUS_PMSGT_DIRTY, + DMUS_PMSGT_WAVE, + DMUS_PMSGT_LYRIC, + DMUS_PMSGT_SCRIPTLYRIC, + DMUS_PMSGT_USER, + }; + static const LARGE_INTEGER zero = {0}; + DMUS_IO_TEMPO_ITEM tempo_items[] = + { + {.lTime = 100, .dblTempo = 80}, + {.lTime = 300, .dblTempo = 60}, + {.lTime = 200, .dblTempo = 20}, + {.lTime = 4000, .dblTempo = 50}, + }; + IDirectMusicPerformance *performance; + IDirectMusicSegment *segment; + IDirectMusicGraph *graph; + IDirectMusicTrack *track; + IPersistStream *persist; + IDirectMusicTool *tool; + DMUS_TEMPO_PMSG *tempo; + DMUS_TEMPO_PARAM param; + IStream *stream; + DMUS_PMSG *msg; + HRESULT hr; + DWORD ret; + + hr = test_tool_create(message_types, ARRAY_SIZE(message_types), &tool); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicPerformance, (void **)&performance); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = CoCreateInstance(&CLSID_DirectMusicGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicGraph, (void **)&graph); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicGraph_InsertTool(graph, (IDirectMusicTool *)tool, NULL, 0, -1); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_SetGraph(performance, graph); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicGraph_Release(graph); + + + /* create a segment and load a simple RIFF stream */ + + hr = CoCreateInstance(&CLSID_DirectMusicSegment, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicSegment, (void **)&segment); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicSegment_QueryInterface(segment, &IID_IPersistStream, (void **)&persist); + ok(hr == S_OK, "got %#lx\n", hr); + hr = CreateStreamOnHGlobal(0, TRUE, &stream); + ok(hr == S_OK, "got %#lx\n", hr); + + CHUNK_RIFF(stream, "DMSG") + { + /* set a non-zero segment length, or nothing will be played */ + DMUS_IO_SEGMENT_HEADER head = {.mtLength = 1000}; + CHUNK_DATA(stream, "segh", head); + CHUNK_DATA(stream, "guid", CLSID_DirectMusicSegment); + } + CHUNK_END; + + hr = IStream_Seek(stream, zero, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IPersistStream_Load(persist, stream); + ok(hr == S_OK, "got %#lx\n", hr); + IPersistStream_Release(persist); + IStream_Release(stream); + + + /* add a tempo track to our segment */ + + hr = CoCreateInstance(&CLSID_DirectMusicTempoTrack, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectMusicTrack, (void **)&track); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicSegment_QueryInterface(track, &IID_IPersistStream, (void **)&persist); + ok(hr == S_OK, "got %#lx\n", hr); + hr = CreateStreamOnHGlobal(0, TRUE, &stream); + ok(hr == S_OK, "got %#lx\n", hr); + CHUNK_ARRAY(stream, "tetr", tempo_items); + hr = IStream_Seek(stream, zero, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IPersistStream_Load(persist, stream); + ok(hr == S_OK, "got %#lx\n", hr); + IPersistStream_Release(persist); + IStream_Release(stream); + + hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 0, NULL, ¶m); + ok(hr == DMUS_E_TRACK_NOT_FOUND, "got %#lx\n", hr); + + hr = IDirectMusicSegment_InsertTrack(segment, (IDirectMusicTrack *)track, 1); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicTrack_Release(track); + + + /* now play the segment, and check produced messages */ + + hr = IDirectMusicPerformance_Init(performance, NULL, 0, 0); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0x800, 0, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, &msg); + ok(!ret, "got %#lx\n", ret); + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&tempo); + ok(!ret, "got %#lx\n", ret); + todo_wine ok(tempo->dwType == DMUS_PMSGT_TEMPO, "got %#lx\n", tempo->dwType); + if (tempo->dwType != DMUS_PMSGT_TEMPO) goto skip_tests; + check_dmus_tempo_pmsg(tempo, 100, 80); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)tempo); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&tempo); + todo_wine ok(!ret, "got %#lx\n", ret); + check_dmus_tempo_pmsg(tempo, 300, 60); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)tempo); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 500, &msg); + todo_wine ok(!ret, "got %#lx\n", ret); + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + +skip_tests: + IDirectMusicSegment_Release(segment); + + + hr = IDirectMusicPerformance_CloseDown(performance); + ok(hr == S_OK, "got %#lx\n", hr); + + IDirectMusicPerformance_Release(performance); + IDirectMusicTool_Release(tool); +} + static void test_connect_to_collection(void) { IDirectMusicCollection *collection; @@ -4208,6 +4381,7 @@ START_TEST(dmime) test_wave_pmsg(); test_sequence_track(); test_band_track_play(); + test_tempo_track_play(); test_connect_to_collection(); test_segment_state(); From f931c8eed283d76b52223a04c9326ed17d45e7e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 20:01:36 +0200 Subject: [PATCH 1066/1148] dmime/tests: Test tempo track GetParam with GUID_TempoParam. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 344571d2801..2f2592da6fe 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3933,6 +3933,7 @@ static void test_tempo_track_play(void) {.lTime = 4000, .dblTempo = 50}, }; IDirectMusicPerformance *performance; + MUSIC_TIME next_time; IDirectMusicSegment *segment; IDirectMusicGraph *graph; IDirectMusicTrack *track; @@ -4015,6 +4016,49 @@ static void test_tempo_track_play(void) ok(hr == S_OK, "got %#lx\n", hr); IDirectMusicTrack_Release(track); + hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 0, NULL, NULL); + ok(hr == E_POINTER, "got %#lx\n", hr); + hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 0, NULL, ¶m); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + + memset(¶m, 0xcd, sizeof(param)); + next_time = 0xdeadbeef; + hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 0, &next_time, ¶m); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(next_time == 100, "got next_time %lu\n", next_time); + todo_wine ok(param.mtTime == 100, "got mtTime %ld\n", param.mtTime); + todo_wine ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); + hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 100, &next_time, ¶m); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(next_time == 200, "got next_time %lu\n", next_time); + ok(param.mtTime == 0, "got mtTime %ld\n", param.mtTime); + todo_wine ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); + hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 199, &next_time, ¶m); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(next_time == 101, "got next_time %lu\n", next_time); + todo_wine ok(param.mtTime == -99, "got mtTime %ld\n", param.mtTime); + todo_wine ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); + hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 200, &next_time, ¶m); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(next_time == 100, "got next_time %lu\n", next_time); + todo_wine ok(param.mtTime == -100, "got mtTime %ld\n", param.mtTime); + todo_wine ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); + hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 299, &next_time, ¶m); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(next_time == 1, "got next_time %lu\n", next_time); + todo_wine ok(param.mtTime == -199, "got mtTime %ld\n", param.mtTime); + todo_wine ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); + hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 300, &next_time, ¶m); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(next_time == 3700, "got next_time %lu\n", next_time); + todo_wine ok(param.mtTime == -100, "got mtTime %ld\n", param.mtTime); + todo_wine ok(param.dblTempo == 20, "got dblTempo %f\n", param.dblTempo); + hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 5000, &next_time, ¶m); + todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(next_time == 0, "got next_time %lu\n", next_time); + todo_wine ok(param.mtTime == -1000, "got mtTime %ld\n", param.mtTime); + todo_wine ok(param.dblTempo == 50, "got dblTempo %f\n", param.dblTempo); + /* now play the segment, and check produced messages */ From b2e6601c9ae89e277b5a8d8c958475a30f41a8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 16:51:31 +0200 Subject: [PATCH 1067/1148] dmime: Fix tempo track GetParam with GUID_TempoParam implementation. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tempotrack.c | 46 ++++++++++++++-------------------- dlls/dmime/tests/dmime.c | 54 ++++++++++++++++++++-------------------- 2 files changed, 45 insertions(+), 55 deletions(-) diff --git a/dlls/dmime/tempotrack.c b/dlls/dmime/tempotrack.c index 07f410118fe..028ebc8879c 100644 --- a/dlls/dmime/tempotrack.c +++ b/dlls/dmime/tempotrack.c @@ -140,42 +140,32 @@ static HRESULT WINAPI tempo_track_Play(IDirectMusicTrack8 *iface, void *pStateDa return S_OK; } -static HRESULT WINAPI tempo_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time, - MUSIC_TIME *next, void *param) +static HRESULT WINAPI tempo_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME music_time, + MUSIC_TIME *next_time, void *param) { IDirectMusicTempoTrack *This = impl_from_IDirectMusicTrack8(iface); - DMUS_TEMPO_PARAM *prm = param; - unsigned int i; - - TRACE("(%p, %s, %ld, %p, %p)\n", This, debugstr_dmguid(type), time, next, param); + DMUS_IO_TEMPO_ITEM *item = This->items, *end = item + This->count; + DMUS_TEMPO_PARAM *tempo = param; - if (!param) - return E_POINTER; - if (!IsEqualGUID(type, &GUID_TempoParam)) - return DMUS_E_GET_UNSUPPORTED; + TRACE("(%p, %s, %ld, %p, %p)\n", This, debugstr_dmguid(type), music_time, next_time, param); - FIXME("Partial support for GUID_TempoParam\n"); + if (!param) return E_POINTER; + if (!IsEqualGUID(type, &GUID_TempoParam)) return DMUS_E_GET_UNSUPPORTED; + if (item == end) return DMUS_E_NOT_FOUND; - if (next) - *next = 0; - prm->mtTime = 0; - prm->dblTempo = 0.123456; + tempo->mtTime = item->lTime - music_time; + tempo->dblTempo = item->dblTempo; - for (i = 0; i < This->count; i++) { - if (This->items[i].lTime <= time) { - MUSIC_TIME ofs = This->items[i].lTime - time; - if (ofs > prm->mtTime) { - prm->mtTime = ofs; - prm->dblTempo = This->items[i].dblTempo; - } - if (next && This->items[i].lTime > time && This->items[i].lTime < *next) - *next = This->items[i].lTime; - } + for (; item < end; item++) + { + MUSIC_TIME time = item->lTime - music_time; + if (next_time) *next_time = item->lTime - music_time; + if (time > 0) break; + tempo->mtTime = time; + tempo->dblTempo = item->dblTempo; } - if (0.123456 == prm->dblTempo) - return DMUS_E_NOT_FOUND; - + if (next_time && item == end) *next_time = 0; return S_OK; } diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 2f2592da6fe..8be73d93b54 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -4019,45 +4019,45 @@ static void test_tempo_track_play(void) hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 0, NULL, NULL); ok(hr == E_POINTER, "got %#lx\n", hr); hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 0, NULL, ¶m); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); memset(¶m, 0xcd, sizeof(param)); next_time = 0xdeadbeef; hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 0, &next_time, ¶m); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(next_time == 100, "got next_time %lu\n", next_time); - todo_wine ok(param.mtTime == 100, "got mtTime %ld\n", param.mtTime); - todo_wine ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); + ok(hr == S_OK, "got %#lx\n", hr); + ok(next_time == 100, "got next_time %lu\n", next_time); + ok(param.mtTime == 100, "got mtTime %ld\n", param.mtTime); + ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 100, &next_time, ¶m); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(next_time == 200, "got next_time %lu\n", next_time); + ok(hr == S_OK, "got %#lx\n", hr); + ok(next_time == 200, "got next_time %lu\n", next_time); ok(param.mtTime == 0, "got mtTime %ld\n", param.mtTime); - todo_wine ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); + ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 199, &next_time, ¶m); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(next_time == 101, "got next_time %lu\n", next_time); - todo_wine ok(param.mtTime == -99, "got mtTime %ld\n", param.mtTime); - todo_wine ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); + ok(hr == S_OK, "got %#lx\n", hr); + ok(next_time == 101, "got next_time %lu\n", next_time); + ok(param.mtTime == -99, "got mtTime %ld\n", param.mtTime); + ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 200, &next_time, ¶m); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(next_time == 100, "got next_time %lu\n", next_time); - todo_wine ok(param.mtTime == -100, "got mtTime %ld\n", param.mtTime); - todo_wine ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); + ok(hr == S_OK, "got %#lx\n", hr); + ok(next_time == 100, "got next_time %lu\n", next_time); + ok(param.mtTime == -100, "got mtTime %ld\n", param.mtTime); + ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 299, &next_time, ¶m); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(next_time == 1, "got next_time %lu\n", next_time); - todo_wine ok(param.mtTime == -199, "got mtTime %ld\n", param.mtTime); - todo_wine ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); + ok(hr == S_OK, "got %#lx\n", hr); + ok(next_time == 1, "got next_time %lu\n", next_time); + ok(param.mtTime == -199, "got mtTime %ld\n", param.mtTime); + ok(param.dblTempo == 80, "got dblTempo %f\n", param.dblTempo); hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 300, &next_time, ¶m); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(next_time == 3700, "got next_time %lu\n", next_time); - todo_wine ok(param.mtTime == -100, "got mtTime %ld\n", param.mtTime); - todo_wine ok(param.dblTempo == 20, "got dblTempo %f\n", param.dblTempo); + ok(hr == S_OK, "got %#lx\n", hr); + ok(next_time == 3700, "got next_time %lu\n", next_time); + ok(param.mtTime == -100, "got mtTime %ld\n", param.mtTime); + ok(param.dblTempo == 20, "got dblTempo %f\n", param.dblTempo); hr = IDirectMusicSegment_GetParam(segment, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, 5000, &next_time, ¶m); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); + ok(hr == S_OK, "got %#lx\n", hr); ok(next_time == 0, "got next_time %lu\n", next_time); - todo_wine ok(param.mtTime == -1000, "got mtTime %ld\n", param.mtTime); - todo_wine ok(param.dblTempo == 50, "got dblTempo %f\n", param.dblTempo); + ok(param.mtTime == -1000, "got mtTime %ld\n", param.mtTime); + ok(param.dblTempo == 50, "got dblTempo %f\n", param.dblTempo); /* now play the segment, and check produced messages */ From 1a43a415ed010488a772ce0a2e667367bd87a2b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 19:51:49 +0200 Subject: [PATCH 1068/1148] dmime/tests: Add helpers to scale and check music time with tempo. Be flexible on the comparison to ignore rounding errors. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 8be73d93b54..953dac5ed2e 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -2671,6 +2671,17 @@ static void test_performance_graph(void) IDirectMusicTool_Release(tool); } +static double scale_music_time(MUSIC_TIME time, double tempo) +{ + return (600000000. * time) / (tempo * 768.); +} + +#define check_music_time(a, b) check_music_time_(__LINE__, a, b) +static void check_music_time_(int line, MUSIC_TIME time, MUSIC_TIME expect) +{ + ok_(__FILE__, line)(abs(time - expect) <= 1, "got %ld, expected %ld\n", time, expect); +} + static void test_performance_time(void) { IDirectMusicPerformance *performance; @@ -2720,29 +2731,26 @@ static void test_performance_time(void) time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 1, &time); ok(hr == S_OK, "got %#lx\n", hr); - ok(time - init_time >= 6505, "got %I64d\n", time - init_time); - ok(time - init_time <= 6515, "got %I64d\n", time - init_time); + check_music_time(time - init_time, scale_music_time(1, 120)); time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 1000, &time); ok(hr == S_OK, "got %#lx\n", hr); - ok(time - init_time >= 1000 * 6505, "got %I64d\n", time - init_time); - ok(time - init_time <= 1000 * 6515, "got %I64d\n", time - init_time); + todo_wine check_music_time(time - init_time, scale_music_time(1000, 120)); time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 2000, &time); ok(hr == S_OK, "got %#lx\n", hr); - ok(time - init_time >= 2000 * 6505, "got %I64d\n", time - init_time); - ok(time - init_time <= 2000 * 6515, "got %I64d\n", time - init_time); + todo_wine check_music_time(time - init_time, scale_music_time(2000, 120)); music_time = 0xdeadbeef; hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time, &music_time); ok(hr == S_OK, "got %#lx\n", hr); ok(music_time == 0, "got %ld\n", music_time); music_time = 0xdeadbeef; - hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + 1000 * 6510, &music_time); + hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + scale_music_time(1000, 120), &music_time); ok(hr == S_OK, "got %#lx\n", hr); ok(music_time == 1000, "got %ld\n", music_time); music_time = 0xdeadbeef; - hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + 2000 * 6510, &music_time); + hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + scale_music_time(2000, 120), &music_time); ok(hr == S_OK, "got %#lx\n", hr); ok(music_time == 2000, "got %ld\n", music_time); From da3313e7b8a5b68e59853403ea9a12c730ce3b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 19:53:37 +0200 Subject: [PATCH 1069/1148] dmime/tests: Test playing tempo track effect on performance times. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 48 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 953dac5ed2e..5dbc7ece8d7 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3941,7 +3941,8 @@ static void test_tempo_track_play(void) {.lTime = 4000, .dblTempo = 50}, }; IDirectMusicPerformance *performance; - MUSIC_TIME next_time; + MUSIC_TIME music_time, next_time; + REFERENCE_TIME init_time, time; IDirectMusicSegment *segment; IDirectMusicGraph *graph; IDirectMusicTrack *track; @@ -4076,12 +4077,57 @@ static void test_tempo_track_play(void) hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0x800, 0, NULL); ok(hr == S_OK, "got %#lx\n", hr); + /* the tempo track only takes effect after DMUS_PMSGT_DIRTY */ + ret = test_tool_wait_message(tool, 500, &msg); ok(!ret, "got %#lx\n", ret); ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); hr = IDirectMusicPerformance_FreePMsg(performance, msg); ok(hr == S_OK, "got %#lx\n", hr); + + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 0, &time); + ok(hr == S_OK, "got %#lx\n", hr); + init_time = time; + + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 1, &time); + ok(hr == S_OK, "got %#lx\n", hr); + check_music_time(time - init_time, scale_music_time(1, 120)); + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 100, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine check_music_time(time - init_time, scale_music_time(100, 120)); + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 150, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine check_music_time(time - init_time, scale_music_time(100, 120) + scale_music_time(50, 80)); + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 200, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine check_music_time(time - init_time, scale_music_time(100, 120) + scale_music_time(100, 80)); + time = 0xdeadbeef; + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 400, &time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine check_music_time(time - init_time, scale_music_time(100, 120) + scale_music_time(200, 80) + scale_music_time(100, 20)); + + music_time = 0xdeadbeef; + hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time, &music_time); + ok(hr == S_OK, "got %#lx\n", hr); + ok(music_time == 0, "got %ld\n", music_time); + music_time = 0xdeadbeef; + time = scale_music_time(100, 120) + scale_music_time(50, 80); + hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + time, &music_time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(music_time == 150, "got %ld\n", music_time); + music_time = 0xdeadbeef; + time = scale_music_time(100, 120) + scale_music_time(200, 80) + scale_music_time(100, 20); + hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + time, &music_time); + ok(hr == S_OK, "got %#lx\n", hr); + todo_wine ok(music_time == 400, "got %ld\n", music_time); + + ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&tempo); ok(!ret, "got %#lx\n", ret); todo_wine ok(tempo->dwType == DMUS_PMSGT_TEMPO, "got %#lx\n", tempo->dwType); From 3bc57984ed46f9436517deb817ea0cd8282979d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 13:21:14 +0200 Subject: [PATCH 1070/1148] dmime: Support playing secondary and control segments. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 17fbe772f5b..06d541efb7e 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -78,6 +78,7 @@ struct performance BOOL notification_segment; IDirectMusicSegment *primary_segment; + IDirectMusicSegment *control_segment; }; struct message @@ -346,6 +347,12 @@ static void performance_set_primary_segment(struct performance *This, IDirectMus if ((This->primary_segment = segment)) IDirectMusicSegment_AddRef(This->primary_segment); } +static void performance_set_control_segment(struct performance *This, IDirectMusicSegment *segment) +{ + if (This->control_segment) IDirectMusicSegment_Release(This->control_segment); + if ((This->control_segment = segment)) IDirectMusicSegment_AddRef(This->control_segment); +} + /* IDirectMusicPerformance8 IUnknown part: */ static HRESULT WINAPI performance_QueryInterface(IDirectMusicPerformance8 *iface, REFIID riid, void **ret_iface) { @@ -1221,6 +1228,7 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) } performance_set_primary_segment(This, NULL); + performance_set_control_segment(This, NULL); if (This->master_clock) { @@ -1333,6 +1341,7 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, WCHAR *segment_name, IUnknown *transition, DWORD segment_flags, INT64 start_time, IDirectMusicSegmentState **segment_state, IUnknown *from, IUnknown *audio_path) { + BOOL primary = !(segment_flags & DMUS_SEGF_SECONDARY), control = (segment_flags & DMUS_SEGF_CONTROL); struct performance *This = impl_from_IDirectMusicPerformance8(iface); IDirectMusicSegmentState *state; IDirectMusicSegment *segment; @@ -1351,12 +1360,14 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, EnterCriticalSection(&This->safe); - performance_set_primary_segment(This, segment); + if (primary) performance_set_primary_segment(This, segment); + if (control) performance_set_control_segment(This, segment); if ((!(music_time = start_time) && FAILED(hr = IDirectMusicPerformance8_GetTime(iface, NULL, &music_time))) || FAILED(hr = segment_state_create(segment, music_time, iface, &state))) { - performance_set_primary_segment(This, NULL); + if (primary) performance_set_primary_segment(This, NULL); + if (control) performance_set_control_segment(This, NULL); LeaveCriticalSection(&This->safe); IDirectMusicSegment_Release(segment); @@ -1366,7 +1377,8 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, if (FAILED(hr = segment_state_play(state, iface))) { ERR("Failed to play segment state, hr %#lx\n", hr); - performance_set_primary_segment(This, NULL); + if (primary) performance_set_primary_segment(This, NULL); + if (control) performance_set_control_segment(This, NULL); } else if (segment_state) { From 716565bd07d3f430ee09de7fa7a897088fbfafc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 23 Oct 2023 15:25:40 +0200 Subject: [PATCH 1071/1148] dmime: Better implement performance times with tempo track. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 85 ++++++++++++++++++++++++++++++++-------- dlls/dmime/tests/dmime.c | 18 ++++----- 2 files changed, 77 insertions(+), 26 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 06d541efb7e..624b7e6bf70 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -21,6 +21,7 @@ #include "dmime_private.h" #include "dmusic_midi.h" #include "wine/rbtree.h" +#include WINE_DEFAULT_DEBUG_CHANNEL(dmime); @@ -677,20 +678,39 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, REFERENCE_TIME *time) { - static int once; struct performance *This = impl_from_IDirectMusicPerformance8(iface); + MUSIC_TIME tempo_time, next = 0; + DMUS_TEMPO_PARAM param; + double tempo, duration; + HRESULT hr; - if (!once++) FIXME("(%p, %ld, %p): semi-stub\n", This, music_time, time); - else TRACE("(%p, %ld, %p)\n", This, music_time, time); + TRACE("(%p, %ld, %p)\n", This, music_time, time); if (!time) return E_POINTER; *time = 0; if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK; - /* FIXME: This should be (music_time * 60) / (DMUS_PPQ * tempo) - * but it gives innacurate results */ - *time = This->init_time + (music_time * 6510); + EnterCriticalSection(&This->safe); + + for (tempo = 120., duration = tempo_time = 0; music_time > 0; tempo_time += next) + { + if (FAILED(hr = IDirectMusicPerformance_GetParam(iface, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, + tempo_time, &next, ¶m))) + break; + + if (!next) next = music_time; + else next = min(next, music_time); + + if (param.mtTime <= 0) tempo = param.dblTempo; + duration += (600000000. * next) / (tempo * DMUS_PPQ); + music_time -= next; + } + + duration += (600000000. * music_time) / (tempo * DMUS_PPQ); + *time = This->init_time + duration; + + LeaveCriticalSection(&This->safe); return S_OK; } @@ -698,20 +718,38 @@ static HRESULT WINAPI performance_MusicToReferenceTime(IDirectMusicPerformance8 static HRESULT WINAPI performance_ReferenceToMusicTime(IDirectMusicPerformance8 *iface, REFERENCE_TIME time, MUSIC_TIME *music_time) { - static int once; struct performance *This = impl_from_IDirectMusicPerformance8(iface); + MUSIC_TIME tempo_time, next = 0; + double tempo, duration, step; + DMUS_TEMPO_PARAM param; + HRESULT hr; - if (!once++) FIXME("(%p, %I64d, %p): semi-stub\n", This, time, music_time); - else TRACE("(%p, %I64d, %p)\n", This, time, music_time); + TRACE("(%p, %I64d, %p)\n", This, time, music_time); if (!music_time) return E_POINTER; *music_time = 0; if (!This->master_clock) return DMUS_E_NO_MASTER_CLOCK; - /* FIXME: This should be (time * DMUS_PPQ * tempo) / 60 - * but it gives innacurate results */ - *music_time = (time - This->init_time) / 6510; + EnterCriticalSection(&This->safe); + + duration = time - This->init_time; + + for (tempo = 120., tempo_time = 0; duration > 0; tempo_time += next, duration -= step) + { + if (FAILED(hr = IDirectMusicPerformance_GetParam(iface, &GUID_TempoParam, -1, DMUS_SEG_ALLTRACKS, + tempo_time, &next, ¶m))) + break; + + if (param.mtTime <= 0) tempo = param.dblTempo; + step = (600000000. * next) / (tempo * DMUS_PPQ); + if (!next || duration < step) break; + *music_time = *music_time + next; + } + + *music_time = *music_time + round((duration * tempo * DMUS_PPQ) / 600000000.); + + LeaveCriticalSection(&This->safe); return S_OK; } @@ -1085,13 +1123,26 @@ static HRESULT WINAPI performance_Invalidate(IDirectMusicPerformance8 *iface, MU return S_OK; } -static HRESULT WINAPI performance_GetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, - DWORD dwGroupBits, DWORD dwIndex, MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam) +static HRESULT WINAPI performance_GetParam(IDirectMusicPerformance8 *iface, REFGUID type, + DWORD group, DWORD index, MUSIC_TIME music_time, MUSIC_TIME *next_time, void *param) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + HRESULT hr; - FIXME("(%p, %s, %ld, %ld, %ld, %p, %p): stub\n", This, debugstr_dmguid(rguidType), dwGroupBits, dwIndex, mtTime, pmtNext, pParam); - return S_OK; + TRACE("(%p, %s, %ld, %ld, %ld, %p, %p)\n", This, debugstr_dmguid(type), group, index, music_time, next_time, param); + + if (next_time) *next_time = 0; + + if (!This->control_segment) hr = DMUS_E_NOT_FOUND; + else hr = IDirectMusicSegment_GetParam(This->control_segment, type, group, index, music_time, next_time, param); + + if (FAILED(hr)) + { + if (!This->primary_segment) hr = DMUS_E_NOT_FOUND; + else hr = IDirectMusicSegment_GetParam(This->primary_segment, type, group, index, music_time, next_time, param); + } + + return hr; } static HRESULT WINAPI performance_SetParam(IDirectMusicPerformance8 *iface, REFGUID rguidType, diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 5dbc7ece8d7..2745fd94db9 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -2735,11 +2735,11 @@ static void test_performance_time(void) time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 1000, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine check_music_time(time - init_time, scale_music_time(1000, 120)); + check_music_time(time - init_time, scale_music_time(1000, 120)); time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 2000, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine check_music_time(time - init_time, scale_music_time(2000, 120)); + check_music_time(time - init_time, scale_music_time(2000, 120)); music_time = 0xdeadbeef; hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time, &music_time); @@ -4098,19 +4098,19 @@ static void test_tempo_track_play(void) time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 100, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine check_music_time(time - init_time, scale_music_time(100, 120)); + check_music_time(time - init_time, scale_music_time(100, 120)); time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 150, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine check_music_time(time - init_time, scale_music_time(100, 120) + scale_music_time(50, 80)); + check_music_time(time - init_time, scale_music_time(100, 120) + scale_music_time(50, 80)); time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 200, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine check_music_time(time - init_time, scale_music_time(100, 120) + scale_music_time(100, 80)); + check_music_time(time - init_time, scale_music_time(100, 120) + scale_music_time(100, 80)); time = 0xdeadbeef; hr = IDirectMusicPerformance_MusicToReferenceTime(performance, 400, &time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine check_music_time(time - init_time, scale_music_time(100, 120) + scale_music_time(200, 80) + scale_music_time(100, 20)); + check_music_time(time - init_time, scale_music_time(100, 120) + scale_music_time(200, 80) + scale_music_time(100, 20)); music_time = 0xdeadbeef; hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time, &music_time); @@ -4120,15 +4120,15 @@ static void test_tempo_track_play(void) time = scale_music_time(100, 120) + scale_music_time(50, 80); hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + time, &music_time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(music_time == 150, "got %ld\n", music_time); + ok(music_time == 150, "got %ld\n", music_time); music_time = 0xdeadbeef; time = scale_music_time(100, 120) + scale_music_time(200, 80) + scale_music_time(100, 20); hr = IDirectMusicPerformance_ReferenceToMusicTime(performance, init_time + time, &music_time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(music_time == 400, "got %ld\n", music_time); + ok(music_time == 400, "got %ld\n", music_time); - ret = test_tool_wait_message(tool, 500, (DMUS_PMSG **)&tempo); + ret = test_tool_wait_message(tool, 2000, (DMUS_PMSG **)&tempo); ok(!ret, "got %#lx\n", ret); todo_wine ok(tempo->dwType == DMUS_PMSGT_TEMPO, "got %#lx\n", tempo->dwType); if (tempo->dwType != DMUS_PMSGT_TEMPO) goto skip_tests; From b837cdf0e3c9b950c2b6d3a809573395e63ad67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 24 Oct 2023 09:18:32 +0200 Subject: [PATCH 1072/1148] dmime/tests: Test that IDirectMusicPerformance_Stop clears messages. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/tests/dmime.c | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 2745fd94db9..0268bd954ef 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3290,6 +3290,55 @@ static void test_notification_pmsg(void) ok(hr == S_FALSE, "got %#lx\n", hr); + /* Stop finishes segment immediately and skips notification messages */ + + hr = IDirectMusicPerformance_AddNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_PlaySegment(performance, segment, 0, 0, &state); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_GetTime(performance, NULL, &music_time); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); + ok(!ret, "got %#lx\n", ret); + check_dmus_notification_pmsg(notif, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, &GUID_NOTIFICATION_SEGMENT, + DMUS_NOTIFICATION_SEGSTART, state); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_Stop(performance, NULL, NULL, 0, DMUS_SEGF_DEFAULT); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_RemoveNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 50, &msg); + ok(!ret, "got %#lx\n", ret); + ok(msg->dwType == DMUS_PMSGT_DIRTY, "got %#lx\n", msg->dwType); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + check_dmus_notification_pmsg(notif, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, &GUID_NOTIFICATION_SEGMENT, + DMUS_NOTIFICATION_SEGABORT, state); + hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); + ok(hr == S_OK, "got %#lx\n", hr); + } + + ret = test_tool_wait_message(tool, 500, &msg); + ok(!ret, "got %#lx\n", ret); + check_dmus_dirty_pmsg(msg, music_time + length); + hr = IDirectMusicPerformance_FreePMsg(performance, msg); + ok(hr == S_OK, "got %#lx\n", hr); + + ret = test_tool_wait_message(tool, 100, &msg); + ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret); + + IDirectMusicSegmentState_Release(state); + + /* CloseDown removes all notifications and notification messages */ hr = IDirectMusicPerformance_AddNotificationType(performance, &GUID_NOTIFICATION_SEGMENT); From 0bfcb71520755e1515bc8e65ff8649e8710faa9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 25 Oct 2023 17:24:56 +0200 Subject: [PATCH 1073/1148] dmime: Implement IDirectMusicTrack_EndPlay for wave track. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/wavetrack.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c index 7f2fe4a8e5f..3dabbc645e6 100644 --- a/dlls/dmime/wavetrack.c +++ b/dlls/dmime/wavetrack.c @@ -139,7 +139,20 @@ static HRESULT WINAPI wave_track_InitPlay(IDirectMusicTrack8 *iface, static HRESULT WINAPI wave_track_EndPlay(IDirectMusicTrack8 *iface, void *pStateData) { struct wave_track *This = impl_from_IDirectMusicTrack8(iface); + struct wave_part *part; + struct wave_item *item; + FIXME("(%p, %p): stub\n", This, pStateData); + + LIST_FOR_EACH_ENTRY(part, &This->parts, struct wave_part, entry) + { + LIST_FOR_EACH_ENTRY(item, &part->items, struct wave_item, entry) + { + if (!item->buffer) continue; + IDirectSoundBuffer_Stop(item->buffer); + } + } + return S_OK; } From c94d93b841574092cf2e9e21983820309ce42b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 12:55:43 +0200 Subject: [PATCH 1074/1148] dmime: Clear all pending messages in IDirectMusicPerformance_Stop. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 4 +- dlls/dmime/performance.c | 165 +++++++++++++++++++++++++++++-------- dlls/dmime/segmentstate.c | 23 +++++- dlls/dmime/tests/dmime.c | 5 +- 4 files changed, 158 insertions(+), 39 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index 3c7fc1e332b..ed1589ece1a 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -76,8 +76,10 @@ extern HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME sta IDirectMusicPerformance8 *performance, IDirectMusicSegmentState **ret_iface); extern HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); extern HRESULT segment_state_tick(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); +extern HRESULT segment_state_stop(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); extern HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); extern BOOL segment_state_has_segment(IDirectMusicSegmentState *iface, IDirectMusicSegment *segment); +extern BOOL segment_state_has_track(IDirectMusicSegmentState *iface, DWORD track_id); extern HRESULT wave_track_create_from_chunk(IStream *stream, struct chunk_entry *parent, IDirectMusicTrack8 **ret_iface); @@ -88,7 +90,7 @@ extern HRESULT performance_send_segment_start(IDirectMusicPerformance8 *iface, M extern HRESULT performance_send_segment_tick(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, IDirectMusicSegmentState *state); extern HRESULT performance_send_segment_end(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, - IDirectMusicSegmentState *state); + IDirectMusicSegmentState *state, BOOL abort); /***************************************************************************** * Auxiliary definitions diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 624b7e6bf70..7b7a1d8959e 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -318,24 +318,28 @@ HRESULT performance_send_segment_tick(IDirectMusicPerformance8 *iface, MUSIC_TIM } HRESULT performance_send_segment_end(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time, - IDirectMusicSegmentState *state) + IDirectMusicSegmentState *state, BOOL abort) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); HRESULT hr; - if (FAILED(hr = performance_send_notification_pmsg(This, music_time - 1450, This->notification_segment, - GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state))) - return hr; - if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment, - GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state))) - return hr; + if (!abort) + { + if (FAILED(hr = performance_send_notification_pmsg(This, music_time - 1450, This->notification_segment, + GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGALMOSTEND, (IUnknown *)state))) + return hr; + if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment, + GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGEND, (IUnknown *)state))) + return hr; + } + if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, DMUS_PMSGT_DIRTY, NULL))) return hr; if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_performance, GUID_NOTIFICATION_PERFORMANCE, DMUS_NOTIFICATION_MUSICSTOPPED, NULL))) return hr; - if (FAILED(hr = performance_send_pmsg(This, music_time, DMUS_PMSGF_TOOL_ATTIME, + if (FAILED(hr = performance_send_pmsg(This, music_time, abort ? DMUS_PMSGF_TOOL_IMMEDIATE : DMUS_PMSGF_TOOL_ATTIME, DMUS_PMSGT_INTERNAL_SEGMENT_END, (IUnknown *)state))) return hr; @@ -539,13 +543,118 @@ static void enum_segment_states(struct performance *This, IDirectMusicSegment *s } } -static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectMusicSegment *pSegment, - IDirectMusicSegmentState *pSegmentState, MUSIC_TIME mtTime, DWORD dwFlags) +static BOOL message_needs_flushing(struct message *message, IDirectMusicSegmentState *state) { - struct performance *This = impl_from_IDirectMusicPerformance8(iface); + if (!state) return TRUE; - FIXME("(%p, %p, %p, %ld, %ld): stub\n", This, pSegment, pSegmentState, mtTime, dwFlags); - return S_OK; + switch (message->msg.dwType) + { + case DMUS_PMSGT_MIDI: + case DMUS_PMSGT_NOTE: + case DMUS_PMSGT_CURVE: + case DMUS_PMSGT_PATCH: + case DMUS_PMSGT_WAVE: + if (!segment_state_has_track(state, message->msg.dwVirtualTrackID)) return FALSE; + break; + + case DMUS_PMSGT_NOTIFICATION: + { + DMUS_NOTIFICATION_PMSG *notif = (DMUS_NOTIFICATION_PMSG *)&message->msg; + if (!IsEqualGUID(¬if->guidNotificationType, &GUID_NOTIFICATION_SEGMENT)) return FALSE; + if ((IDirectMusicSegmentState *)message->msg.punkUser != state) return FALSE; + break; + } + + case DMUS_PMSGT_INTERNAL_SEGMENT_TICK: + case DMUS_PMSGT_INTERNAL_SEGMENT_END: + if ((IDirectMusicSegmentState *)message->msg.punkUser != state) return FALSE; + break; + + default: + FIXME("Unhandled message type %#lx\n", message->msg.dwType); + break; + } + + return TRUE; +} + +static void performance_flush_messages(struct performance *This, IDirectMusicSegmentState *state) +{ + IDirectMusicPerformance *iface = (IDirectMusicPerformance *)&This->IDirectMusicPerformance8_iface; + struct message *message, *next; + HRESULT hr; + + LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry) + { + if (!message_needs_flushing(message, state)) continue; + + list_remove(&message->entry); + list_init(&message->entry); + + if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg))) + ERR("Failed to free message %p, hr %#lx\n", message, hr); + } + + LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->notifications, struct message, entry) + { + if (!message_needs_flushing(message, state)) continue; + + list_remove(&message->entry); + list_init(&message->entry); + + if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg))) + ERR("Failed to free message %p, hr %#lx\n", message, hr); + } +} + +static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectMusicSegment *segment, + IDirectMusicSegmentState *state, MUSIC_TIME music_time, DWORD flags) +{ + struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct list states = LIST_INIT(states); + struct state_entry *entry, *next; + HRESULT hr; + + FIXME("(%p, %p, %p, %ld, %ld): semi-stub\n", This, segment, state, music_time, flags); + + if (music_time) FIXME("time parameter %lu not implemented\n", music_time); + if (flags != DMUS_SEGF_DEFAULT) FIXME("flags parameter %#lx not implemented\n", flags); + + if (!music_time && FAILED(hr = IDirectMusicPerformance8_GetTime(iface, NULL, &music_time))) + return hr; + + EnterCriticalSection(&This->safe); + + if (!state) + enum_segment_states(This, segment, &states); + else if ((entry = malloc(sizeof(*entry)))) + { + entry->state = state; + IDirectMusicSegmentState_AddRef(entry->state); + list_add_tail(&states, &entry->entry); + } + + if (!segment && !state) + performance_flush_messages(This, NULL); + else LIST_FOR_EACH_ENTRY(entry, &states, struct state_entry, entry) + performance_flush_messages(This, entry->state); + + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &states, struct state_entry, entry) + { + if (FAILED(hr = performance_send_notification_pmsg(This, music_time, This->notification_segment, + GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGABORT, (IUnknown *)entry->state))) + ERR("Failed to send DMUS_NOTIFICATION_SEGABORT, hr %#lx\n", hr); + if (FAILED(hr = segment_state_stop(entry->state, iface))) + ERR("Failed to stop segment state, hr %#lx\n", hr); + + IDirectMusicSegmentState_Release(entry->state); + list_remove(&entry->entry); + free(entry); + } + + LeaveCriticalSection(&This->safe); + + return S_OK; } static HRESULT WINAPI performance_GetSegmentState(IDirectMusicPerformance8 *iface, @@ -1234,7 +1343,8 @@ static HRESULT WINAPI performance_AdjustTime(IDirectMusicPerformance8 *iface, RE static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); - struct message *message, *next; + struct list states = LIST_INIT(states); + struct state_entry *entry, *next; HANDLE message_thread; HRESULT hr; @@ -1254,28 +1364,17 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) This->notification_performance = FALSE; This->notification_segment = FALSE; - LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->messages, struct message, entry) - { - list_remove(&message->entry); - list_init(&message->entry); - - if (message->msg.dwType == DMUS_PMSGT_INTERNAL_SEGMENT_END) - hr = IDirectMusicTool_ProcessPMsg(&This->IDirectMusicTool_iface, - (IDirectMusicPerformance *)iface, &message->msg); - else - hr = DMUS_S_FREE; + enum_segment_states(This, NULL, &states); + performance_flush_messages(This, NULL); - if (hr == DMUS_S_FREE && FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg))) - WARN("Failed to free message %p, hr %#lx\n", message, hr); - } - - LIST_FOR_EACH_ENTRY_SAFE(message, next, &This->notifications, struct message, entry) + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &states, struct state_entry, entry) { - list_remove(&message->entry); - list_init(&message->entry); + if (FAILED(hr = segment_state_end_play(entry->state, iface))) + ERR("Failed to stop segment state, hr %#lx\n", hr); - if (FAILED(hr = IDirectMusicPerformance8_FreePMsg(iface, &message->msg))) - WARN("Failed to free message %p, hr %#lx\n", message, hr); + IDirectMusicSegmentState_Release(entry->state); + list_remove(&entry->entry); + free(entry); } performance_set_primary_segment(This, NULL); diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index 3e13687254e..544620b0045 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -303,7 +303,7 @@ static HRESULT segment_state_play_chunk(struct segment_state *This, IDirectMusic { MUSIC_TIME end_time = This->start_time + This->played; - if (FAILED(hr = performance_send_segment_end(performance, end_time, iface))) + if (FAILED(hr = performance_send_segment_end(performance, end_time, iface, FALSE))) { ERR("Failed to send segment end, hr %#lx\n", hr); return hr; @@ -345,6 +345,16 @@ HRESULT segment_state_tick(IDirectMusicSegmentState *iface, IDirectMusicPerforma return segment_state_play_chunk(This, performance, 10000000, 0); } +HRESULT segment_state_stop(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance) +{ + struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); + + TRACE("%p %p\n", iface, performance); + + This->played = This->end_point - This->start_point; + return performance_send_segment_end(performance, This->start_time + This->played, iface, TRUE); +} + HRESULT segment_state_end_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance) { struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); @@ -369,3 +379,14 @@ BOOL segment_state_has_segment(IDirectMusicSegmentState *iface, IDirectMusicSegm struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); return !segment || This->segment == segment; } + +BOOL segment_state_has_track(IDirectMusicSegmentState *iface, DWORD track_id) +{ + struct segment_state *This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); + struct track_entry *entry; + + LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) + if (entry->track_id == track_id) return TRUE; + + return FALSE; +} diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 0268bd954ef..07cd3b34516 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -3318,14 +3318,11 @@ static void test_notification_pmsg(void) ok(hr == S_OK, "got %#lx\n", hr); ret = test_tool_wait_message(tool, 50, (DMUS_PMSG **)¬if); - todo_wine ok(!ret, "got %#lx\n", ret); - if (!ret) - { + ok(!ret, "got %#lx\n", ret); check_dmus_notification_pmsg(notif, music_time, DMUS_PMSGF_TOOL_IMMEDIATE, &GUID_NOTIFICATION_SEGMENT, DMUS_NOTIFICATION_SEGABORT, state); hr = IDirectMusicPerformance_FreePMsg(performance, (DMUS_PMSG *)notif); ok(hr == S_OK, "got %#lx\n", hr); - } ret = test_tool_wait_message(tool, 500, &msg); ok(!ret, "got %#lx\n", ret); From 804b853aabd8c1308d6cf08b48deda9b1912bbf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 12:55:43 +0200 Subject: [PATCH 1075/1148] dmime: Send MIDI_SYSTEM_RESET message on performance reset. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 71 +++++++++++++++++++++++---------------- dlls/dmusic/dmusic_midi.h | 1 + 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 7b7a1d8959e..a808ce3b358 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -454,6 +454,35 @@ static HRESULT performance_init_dmusic(struct performance *This, IDirectSound *d return hr; } +static HRESULT performance_send_midi_pmsg(struct performance *This, DMUS_PMSG *msg, UINT flags, + BYTE status, BYTE byte1, BYTE byte2) +{ + IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface; + DMUS_MIDI_PMSG *midi; + HRESULT hr; + + if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*midi), + (DMUS_PMSG **)&midi))) + return hr; + + if (flags & DMUS_PMSGF_REFTIME) midi->rtTime = msg->rtTime; + if (flags & DMUS_PMSGF_MUSICTIME) midi->mtTime = msg->mtTime; + midi->dwFlags = flags; + midi->dwPChannel = msg->dwPChannel; + midi->dwVirtualTrackID = msg->dwVirtualTrackID; + midi->dwVoiceID = msg->dwVoiceID; + midi->dwGroupID = msg->dwGroupID; + midi->dwType = DMUS_PMSGT_MIDI; + midi->bStatus = status; + midi->bByte1 = byte1; + midi->bByte2 = byte2; + + if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)midi))) + IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)midi); + + return hr; +} + static HRESULT WINAPI performance_Init(IDirectMusicPerformance8 *iface, IDirectMusic **dmusic, IDirectSound *dsound, HWND hwnd) { @@ -613,6 +642,7 @@ static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectM struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct list states = LIST_INIT(states); struct state_entry *entry, *next; + DMUS_PMSG msg = {.mtTime = -1}; HRESULT hr; FIXME("(%p, %p, %p, %ld, %ld): semi-stub\n", This, segment, state, music_time, flags); @@ -652,6 +682,13 @@ static HRESULT WINAPI performance_Stop(IDirectMusicPerformance8 *iface, IDirectM free(entry); } + if (!state && !segment) + { + if (FAILED(hr = performance_send_midi_pmsg(This, &msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, + MIDI_SYSTEM_RESET, 0, 0))) + ERR("Failed to send MIDI_SYSTEM_RESET message, hr %#lx\n", hr); + } + LeaveCriticalSection(&This->safe); return S_OK; @@ -1345,6 +1382,7 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct list states = LIST_INIT(states); struct state_entry *entry, *next; + DMUS_PMSG msg = {.mtTime = -1}; HANDLE message_thread; HRESULT hr; @@ -1377,6 +1415,10 @@ static HRESULT WINAPI performance_CloseDown(IDirectMusicPerformance8 *iface) free(entry); } + if (FAILED(hr = performance_send_midi_pmsg(This, &msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, + MIDI_SYSTEM_RESET, 0, 0))) + ERR("Failed to send MIDI_SYSTEM_RESET message, hr %#lx\n", hr); + performance_set_primary_segment(This, NULL); performance_set_control_segment(This, NULL); @@ -1930,35 +1972,6 @@ static HRESULT WINAPI performance_tool_GetMediaTypes(IDirectMusicTool *iface, DW return E_NOTIMPL; } -static HRESULT performance_send_midi_pmsg(struct performance *This, DMUS_PMSG *msg, UINT flags, - BYTE status, BYTE byte1, BYTE byte2) -{ - IDirectMusicPerformance8 *performance = &This->IDirectMusicPerformance8_iface; - DMUS_MIDI_PMSG *midi; - HRESULT hr; - - if (FAILED(hr = IDirectMusicPerformance8_AllocPMsg(performance, sizeof(*midi), - (DMUS_PMSG **)&midi))) - return hr; - - if (flags & DMUS_PMSGF_REFTIME) midi->rtTime = msg->rtTime; - if (flags & DMUS_PMSGF_MUSICTIME) midi->mtTime = msg->mtTime; - midi->dwFlags = flags; - midi->dwPChannel = msg->dwPChannel; - midi->dwVirtualTrackID = msg->dwVirtualTrackID; - midi->dwVoiceID = msg->dwVoiceID; - midi->dwGroupID = msg->dwGroupID; - midi->dwType = DMUS_PMSGT_MIDI; - midi->bStatus = status; - midi->bByte1 = byte1; - midi->bByte2 = byte2; - - if (FAILED(hr = IDirectMusicPerformance8_SendPMsg(performance, (DMUS_PMSG *)midi))) - IDirectMusicPerformance8_FreePMsg(performance, (DMUS_PMSG *)midi); - - return hr; -} - static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, IDirectMusicPerformance *performance, DMUS_PMSG *msg) { diff --git a/dlls/dmusic/dmusic_midi.h b/dlls/dmusic/dmusic_midi.h index e545bcaf8cb..574961c12d0 100644 --- a/dlls/dmusic/dmusic_midi.h +++ b/dlls/dmusic/dmusic_midi.h @@ -31,6 +31,7 @@ enum midi_message MIDI_PROGRAM_CHANGE = 0xc0, MIDI_CHANNEL_PRESSURE = 0xd0, MIDI_PITCH_BEND_CHANGE = 0xe0, + MIDI_SYSTEM_RESET = 0xff, }; enum midi_control From e692b7638a51a197074217b87fdb458b78df92e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 25 Oct 2023 10:57:54 +0200 Subject: [PATCH 1076/1148] dmsynth: Reset synthesizer defaults on MIDI_SYSTEM_RESET. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmsynth/synth.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 379d80f7346..b8d1b3be7be 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -1069,7 +1069,9 @@ static HRESULT WINAPI synth_Render(IDirectMusicSynth8 *iface, short *buffer, TRACE("status %#x chan %#x midi %#x %#x\n", status, chan, event->midi[1], event->midi[2]); - switch (status) + if (event->midi[0] == MIDI_SYSTEM_RESET) + synth_reset_default_values(This); + else switch (status) { case MIDI_NOTE_OFF: fluid_synth_noteoff(This->fluid_synth, chan, event->midi[1]); From 5319d38626fee56bffd68b5b10218668c836a57f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 25 Oct 2023 09:28:59 +0200 Subject: [PATCH 1077/1148] dmime: Implement segment state repeat and looping. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segmentstate.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index 544620b0045..39a8874c724 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -53,6 +53,7 @@ struct segment_state MUSIC_TIME end_point; MUSIC_TIME played; BOOL auto_download; + DWORD repeats; struct list tracks; }; @@ -234,6 +235,7 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time This->start_time = start_time; if (SUCCEEDED(hr)) hr = IDirectMusicSegment_GetStartPoint(segment, &This->start_point); if (SUCCEEDED(hr)) hr = IDirectMusicSegment_GetLength(segment, &This->end_point); + if (SUCCEEDED(hr)) hr = IDirectMusicSegment_GetRepeats(segment, &This->repeats); for (i = 0; SUCCEEDED(hr); i++) { @@ -284,6 +286,7 @@ static HRESULT segment_state_play_chunk(struct segment_state *This, IDirectMusic if (FAILED(hr = IDirectMusicPerformance8_ReferenceToMusicTime(performance, time + duration, &next_time))) return hr; +play_more: played = min(next_time - This->start_time, This->end_point - This->start_point); LIST_FOR_EACH_ENTRY(entry, &This->tracks, struct track_entry, entry) @@ -303,6 +306,21 @@ static HRESULT segment_state_play_chunk(struct segment_state *This, IDirectMusic { MUSIC_TIME end_time = This->start_time + This->played; + if (This->repeats) + { + if (FAILED(hr = IDirectMusicSegment_GetLoopPoints(This->segment, + &This->played, &This->end_point))) + { + ERR("Failed to get segment loop points, hr %#lx\n", hr); + return hr; + } + This->start_time += This->end_point - This->start_point; + This->repeats--; + + if (next_time - This->start_time > 0 && This->end_point - This->start_point > 0) goto play_more; + return S_OK; + } + if (FAILED(hr = performance_send_segment_end(performance, end_time, iface, FALSE))) { ERR("Failed to send segment end, hr %#lx\n", hr); From 88aa8d6efb0f6392126a98e1a6f6ac23f10046aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 14:19:31 +0200 Subject: [PATCH 1078/1148] dmime: Stop previously playing primary segment in PlaySegmentEx. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index a808ce3b358..55c65daa5a1 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1550,6 +1550,13 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, if (FAILED(hr = IUnknown_QueryInterface(source, &IID_IDirectMusicSegment, (void **)&segment))) return hr; + if (primary && SUCCEEDED(hr = IDirectMusicPerformance8_GetSegmentState(iface, &state, start_time))) + { + if (FAILED(hr = IDirectMusicPerformance_Stop(&This->IDirectMusicPerformance8_iface, NULL, state, start_time, 0))) + ERR("Failed to stop current previous segment, hr %#lx\n", hr); + IDirectMusicSegmentState_Release(state); + } + EnterCriticalSection(&This->safe); if (primary) performance_set_primary_segment(This, segment); From 42e8559dde16751536dd64236bfbe9dc15eaca8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 14:26:41 +0200 Subject: [PATCH 1079/1148] HACK: dmime: Don't send segment end message for secondary segments. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/dmime_private.h | 2 +- dlls/dmime/performance.c | 2 +- dlls/dmime/segmentstate.c | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dlls/dmime/dmime_private.h b/dlls/dmime/dmime_private.h index ed1589ece1a..4644e060828 100644 --- a/dlls/dmime/dmime_private.h +++ b/dlls/dmime/dmime_private.h @@ -72,7 +72,7 @@ extern void set_audiopath_perf_pointer(IDirectMusicAudioPath*,IDirectMusicPerfor extern void set_audiopath_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*); extern void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath*,IDirectSoundBuffer*); -extern HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, +extern HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, DWORD segment_flags, IDirectMusicPerformance8 *performance, IDirectMusicSegmentState **ret_iface); extern HRESULT segment_state_play(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); extern HRESULT segment_state_tick(IDirectMusicSegmentState *iface, IDirectMusicPerformance8 *performance); diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 55c65daa5a1..92ee72a8759 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1563,7 +1563,7 @@ static HRESULT WINAPI performance_PlaySegmentEx(IDirectMusicPerformance8 *iface, if (control) performance_set_control_segment(This, segment); if ((!(music_time = start_time) && FAILED(hr = IDirectMusicPerformance8_GetTime(iface, NULL, &music_time))) - || FAILED(hr = segment_state_create(segment, music_time, iface, &state))) + || FAILED(hr = segment_state_create(segment, music_time, segment_flags, iface, &state))) { if (primary) performance_set_primary_segment(This, NULL); if (control) performance_set_control_segment(This, NULL); diff --git a/dlls/dmime/segmentstate.c b/dlls/dmime/segmentstate.c index 39a8874c724..60e6bef701e 100644 --- a/dlls/dmime/segmentstate.c +++ b/dlls/dmime/segmentstate.c @@ -54,6 +54,7 @@ struct segment_state MUSIC_TIME played; BOOL auto_download; DWORD repeats; + DWORD flags; struct list tracks; }; @@ -106,7 +107,7 @@ static ULONG WINAPI segment_state_Release(IDirectMusicSegmentState8 *iface) if (!ref) { - segment_state_end_play((IDirectMusicSegmentState *)iface, NULL); + if (!(This->flags & DMUS_SEGF_SECONDARY)) segment_state_end_play((IDirectMusicSegmentState *)iface, NULL); if (This->segment) IDirectMusicSegment_Release(This->segment); free(This); } @@ -210,7 +211,7 @@ HRESULT create_dmsegmentstate(REFIID riid, void **ret_iface) return hr; } -HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, +HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time, DWORD segment_flags, IDirectMusicPerformance8 *performance, IDirectMusicSegmentState **ret_iface) { IDirectMusicSegmentState *iface; @@ -224,6 +225,7 @@ HRESULT segment_state_create(IDirectMusicSegment *segment, MUSIC_TIME start_time if (FAILED(hr = create_dmsegmentstate(&IID_IDirectMusicSegmentState, (void **)&iface))) return hr; This = impl_from_IDirectMusicSegmentState8((IDirectMusicSegmentState8 *)iface); + This->flags = segment_flags; This->segment = segment; IDirectMusicSegment_AddRef(This->segment); @@ -321,6 +323,7 @@ static HRESULT segment_state_play_chunk(struct segment_state *This, IDirectMusic return S_OK; } + if (This->flags & DMUS_SEGF_SECONDARY) return S_OK; if (FAILED(hr = performance_send_segment_end(performance, end_time, iface, FALSE))) { ERR("Failed to send segment end, hr %#lx\n", hr); From c663f9fd67a5e07f3dc7c1c6044024618f2e8d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 14:36:33 +0200 Subject: [PATCH 1080/1148] dmband: Skip band / band track chunk on parsing failure. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmband/band.c | 2 +- dlls/dmband/bandtrack.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/dmband/band.c b/dlls/dmband/band.c index 4c593741516..78ec68ae035 100644 --- a/dlls/dmband/band.c +++ b/dlls/dmband/band.c @@ -422,6 +422,7 @@ static HRESULT WINAPI band_persist_stream_Load(IPersistStream *iface, IStream *s } } + stream_skip_chunk(stream, &chunk); if (FAILED(hr)) return hr; if (TRACE_ON(dmband)) @@ -439,7 +440,6 @@ static HRESULT WINAPI band_persist_stream_Load(IPersistStream *iface, IStream *s } } - stream_skip_chunk(stream, &chunk); return S_OK; } diff --git a/dlls/dmband/bandtrack.c b/dlls/dmband/bandtrack.c index e8ebc234f73..4756ed194ef 100644 --- a/dlls/dmband/bandtrack.c +++ b/dlls/dmband/bandtrack.c @@ -572,6 +572,7 @@ static HRESULT WINAPI band_track_persist_stream_Load(IPersistStream *iface, IStr } } + stream_skip_chunk(stream, &chunk); if (FAILED(hr)) return hr; if (TRACE_ON(dmband)) @@ -594,7 +595,6 @@ static HRESULT WINAPI band_track_persist_stream_Load(IPersistStream *iface, IStr } } - stream_skip_chunk(stream, &chunk); return S_OK; } From 4f70bb1d9cd3f79d263936fc26bc6ac003f8e067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 14:37:40 +0200 Subject: [PATCH 1081/1148] dmime: Skip sequence track chunk on parsing failure. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/seqtrack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dmime/seqtrack.c b/dlls/dmime/seqtrack.c index 586bc742472..3252580afb6 100644 --- a/dlls/dmime/seqtrack.c +++ b/dlls/dmime/seqtrack.c @@ -407,6 +407,7 @@ static HRESULT WINAPI track_IPersistStream_Load(IPersistStream *iface, IStream * } } + stream_skip_chunk(stream, &chunk); if (FAILED(hr)) return hr; if (TRACE_ON(dmime)) @@ -449,7 +450,6 @@ static HRESULT WINAPI track_IPersistStream_Load(IPersistStream *iface, IStream * } } - stream_skip_chunk(stream, &chunk); return S_OK; } From 63a2a89f3841db6a2d3794abf1cc28523048f522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 14:38:06 +0200 Subject: [PATCH 1082/1148] dmime: Ignore badly formed wave if format and data have been found. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmusic/wave.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index dd0b8a44779..8ee713bd4f0 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -167,6 +167,7 @@ static HRESULT parse_wave_chunk(struct wave *This, IStream *stream, struct chunk if (FAILED(hr)) break; } + if (This->format && This->data) return S_OK; return hr; } From b29e4f3020e915d9b22a8f29bb3f005aeb90b74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 14:38:43 +0200 Subject: [PATCH 1083/1148] dmime: Remove shadowing local hr variable. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index d7402911f00..552a3e2fcfa 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -816,7 +816,6 @@ static HRESULT WINAPI segment_persist_stream_Load(IPersistStream *iface, IStream case MAKE_IDTYPE(FOURCC_RIFF, mmioFOURCC('W','A','V','E')): { IDirectMusicTrack8 *track; - HRESULT hr; TRACE("Loading segment %p from wave file\n", This); From 9b449e04c52a030b56e91b974c75c38b731b4bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 14:39:03 +0200 Subject: [PATCH 1084/1148] dmime: Skip segment chunk on parsing failure (or success). CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/segment.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/dmime/segment.c b/dlls/dmime/segment.c index 552a3e2fcfa..126bc44335d 100644 --- a/dlls/dmime/segment.c +++ b/dlls/dmime/segment.c @@ -832,6 +832,7 @@ static HRESULT WINAPI segment_persist_stream_Load(IPersistStream *iface, IStream } } + stream_skip_chunk(stream, &chunk); if (FAILED(hr)) { WARN("Failed to load segment from stream %p, hr %#lx\n", stream, hr); From 95af8970a0ae11c7e9a60477007b76c7198a12d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 15:32:29 +0200 Subject: [PATCH 1085/1148] dmime: Rename struct pchannel_block to struct channel_block. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 92ee72a8759..11b4ad58e75 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -32,7 +32,8 @@ enum dmus_internal_message_type DMUS_PMSGT_INTERNAL_SEGMENT_TICK, }; -struct pchannel_block { +struct channel_block +{ DWORD block_num; /* Block 0 is PChannels 0-15, Block 1 is PChannels 16-31, etc */ struct { DWORD channel; /* MIDI channel */ @@ -56,7 +57,7 @@ struct performance float fMasterTempo; long lMasterVolume; /* performance channels */ - struct wine_rb_tree pchannels; + struct wine_rb_tree channel_blocks; BOOL audio_paths_enabled; IDirectMusicAudioPath *pDefaultPath; @@ -233,30 +234,28 @@ static HRESULT performance_send_notification_pmsg(struct performance *This, MUSI return hr; } -static int pchannel_block_compare(const void *key, const struct wine_rb_entry *entry) +static int channel_block_compare(const void *key, const struct wine_rb_entry *entry) { - const struct pchannel_block *b = WINE_RB_ENTRY_VALUE(entry, const struct pchannel_block, entry); - + const struct channel_block *b = WINE_RB_ENTRY_VALUE(entry, const struct channel_block, entry); return *(DWORD *)key - b->block_num; } -static void pchannel_block_free(struct wine_rb_entry *entry, void *context) +static void channel_block_free(struct wine_rb_entry *entry, void *context) { - struct pchannel_block *b = WINE_RB_ENTRY_VALUE(entry, struct pchannel_block, entry); - + struct channel_block *b = WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry); free(b); } -static struct pchannel_block *pchannel_block_set(struct wine_rb_tree *tree, DWORD block_num, +static struct channel_block *channel_block_set(struct wine_rb_tree *tree, DWORD block_num, IDirectMusicPort *port, DWORD group, BOOL only_set_new) { - struct pchannel_block *block; + struct channel_block *block; struct wine_rb_entry *entry; unsigned int i; entry = wine_rb_get(tree, &block_num); if (entry) { - block = WINE_RB_ENTRY_VALUE(entry, struct pchannel_block, entry); + block = WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry); if (only_set_new) return block; } else { @@ -412,7 +411,7 @@ static ULONG WINAPI performance_Release(IDirectMusicPerformance8 *iface) TRACE("(%p): ref=%ld\n", This, ref); if (ref == 0) { - wine_rb_destroy(&This->pchannels, pchannel_block_free, NULL); + wine_rb_destroy(&This->channel_blocks, channel_block_free, NULL); This->safe.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->safe); free(This); @@ -1130,7 +1129,7 @@ static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *par } for (i = 0; i < params->dwChannelGroups; i++) - pchannel_block_set(&perf->pchannels, i, port, i + 1, FALSE); + channel_block_set(&perf->channel_blocks, i, port, i + 1, FALSE); performance_update_latency_time(perf, port, NULL); return S_OK; @@ -1187,7 +1186,7 @@ static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 * if (block_num > MAXDWORD / 16) return E_INVALIDARG; if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; - pchannel_block_set(&This->pchannels, block_num, port, group, FALSE); + channel_block_set(&This->channel_blocks, block_num, port, group, FALSE); return S_OK; } @@ -1196,14 +1195,14 @@ static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface IDirectMusicPort *port, DWORD group, DWORD channel) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); - struct pchannel_block *block; + struct channel_block *block; FIXME("(%p)->(%ld, %p, %ld, %ld) semi-stub\n", This, pchannel, port, group, channel); if (!port) return E_POINTER; if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; - block = pchannel_block_set(&This->pchannels, pchannel / 16, port, 0, TRUE); + block = channel_block_set(&This->channel_blocks, pchannel / 16, port, 0, TRUE); if (block) { block->pchannel[pchannel % 16].group = group; block->pchannel[pchannel % 16].channel = channel; @@ -1216,17 +1215,17 @@ static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, IDirectMusicPort **port, DWORD *group, DWORD *channel) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); - struct pchannel_block *block; + struct channel_block *block; struct wine_rb_entry *entry; DWORD block_num = pchannel / 16; unsigned int index = pchannel % 16; TRACE("(%p)->(%ld, %p, %p, %p)\n", This, pchannel, port, group, channel); - entry = wine_rb_get(&This->pchannels, &block_num); + entry = wine_rb_get(&This->channel_blocks, &block_num); if (!entry) return E_INVALIDARG; - block = WINE_RB_ENTRY_VALUE(entry, struct pchannel_block, entry); + block = WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry); if (port) { *port = block->pchannel[index].port; @@ -2199,7 +2198,7 @@ HRESULT create_dmperformance(REFIID iid, void **ret_iface) obj->pDefaultPath = NULL; InitializeCriticalSection(&obj->safe); obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": performance->safe"); - wine_rb_init(&obj->pchannels, pchannel_block_compare); + wine_rb_init(&obj->channel_blocks, channel_block_compare); list_init(&obj->messages); list_init(&obj->notifications); From 2205e43504acbef8152bcbb736d59d651907e1b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 15:34:09 +0200 Subject: [PATCH 1086/1148] dmime: Use a dedicated struct channel to hold performance channels. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 57 ++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 11b4ad58e75..c70b4096cfe 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -32,14 +32,17 @@ enum dmus_internal_message_type DMUS_PMSGT_INTERNAL_SEGMENT_TICK, }; +struct channel +{ + DWORD midi_group; + DWORD midi_channel; + IDirectMusicPort *port; +}; + struct channel_block { DWORD block_num; /* Block 0 is PChannels 0-15, Block 1 is PChannels 16-31, etc */ - struct { - DWORD channel; /* MIDI channel */ - DWORD group; /* MIDI group */ - IDirectMusicPort *port; - } pchannel[16]; + struct channel channels[16]; struct wine_rb_entry entry; }; @@ -247,7 +250,7 @@ static void channel_block_free(struct wine_rb_entry *entry, void *context) } static struct channel_block *channel_block_set(struct wine_rb_tree *tree, DWORD block_num, - IDirectMusicPort *port, DWORD group, BOOL only_set_new) + IDirectMusicPort *port, DWORD midi_group, BOOL only_set_new) { struct channel_block *block; struct wine_rb_entry *entry; @@ -264,9 +267,9 @@ static struct channel_block *channel_block_set(struct wine_rb_tree *tree, DWORD } for (i = 0; i < 16; ++i) { - block->pchannel[i].port = port; - block->pchannel[i].group = group; - block->pchannel[i].channel = i; + block->channels[i].port = port; + block->channels[i].midi_group = midi_group; + block->channels[i].midi_channel = i; } if (!entry) wine_rb_put(tree, &block->block_num, &block->entry); @@ -1176,51 +1179,51 @@ static HRESULT WINAPI performance_RemovePort(IDirectMusicPerformance8 *iface, ID } static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 *iface, - DWORD block_num, IDirectMusicPort *port, DWORD group) + DWORD block_num, IDirectMusicPort *port, DWORD midi_group) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); - FIXME("(%p, %ld, %p, %ld): semi-stub\n", This, block_num, port, group); + FIXME("(%p, %ld, %p, %ld): semi-stub\n", This, block_num, port, midi_group); if (!port) return E_POINTER; if (block_num > MAXDWORD / 16) return E_INVALIDARG; if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; - channel_block_set(&This->channel_blocks, block_num, port, group, FALSE); + channel_block_set(&This->channel_blocks, block_num, port, midi_group, FALSE); return S_OK; } -static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface, DWORD pchannel, - IDirectMusicPort *port, DWORD group, DWORD channel) +static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface, DWORD channel_num, + IDirectMusicPort *port, DWORD midi_group, DWORD midi_channel) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct channel_block *block; - FIXME("(%p)->(%ld, %p, %ld, %ld) semi-stub\n", This, pchannel, port, group, channel); + FIXME("(%p)->(%ld, %p, %ld, %ld) semi-stub\n", This, channel_num, port, midi_group, midi_channel); if (!port) return E_POINTER; if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; - block = channel_block_set(&This->channel_blocks, pchannel / 16, port, 0, TRUE); + block = channel_block_set(&This->channel_blocks, channel_num / 16, port, 0, TRUE); if (block) { - block->pchannel[pchannel % 16].group = group; - block->pchannel[pchannel % 16].channel = channel; + block->channels[channel_num % 16].midi_group = midi_group; + block->channels[channel_num % 16].midi_channel = midi_channel; } return S_OK; } -static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, DWORD pchannel, - IDirectMusicPort **port, DWORD *group, DWORD *channel) +static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, DWORD channel_num, + IDirectMusicPort **port, DWORD *midi_group, DWORD *midi_channel) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); struct channel_block *block; struct wine_rb_entry *entry; - DWORD block_num = pchannel / 16; - unsigned int index = pchannel % 16; + DWORD block_num = channel_num / 16; + unsigned int index = channel_num % 16; - TRACE("(%p)->(%ld, %p, %p, %p)\n", This, pchannel, port, group, channel); + TRACE("(%p)->(%ld, %p, %p, %p)\n", This, channel_num, port, midi_group, midi_channel); entry = wine_rb_get(&This->channel_blocks, &block_num); if (!entry) @@ -1228,13 +1231,11 @@ static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, block = WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry); if (port) { - *port = block->pchannel[index].port; + *port = block->channels[index].port; IDirectMusicPort_AddRef(*port); } - if (group) - *group = block->pchannel[index].group; - if (channel) - *channel = block->pchannel[index].channel; + if (midi_group) *midi_group = block->channels[index].midi_group; + if (midi_channel) *midi_channel = block->channels[index].midi_channel; return S_OK; } From 422cbfb123a1bf4fbc70b14007f297494541dda6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 15:25:13 +0200 Subject: [PATCH 1087/1148] dmime: Introduce a new performance_get_channel helper. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index c70b4096cfe..5418bc04236 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -249,6 +249,14 @@ static void channel_block_free(struct wine_rb_entry *entry, void *context) free(b); } +static struct channel *performance_get_channel(struct performance *This, DWORD channel_num) +{ + DWORD block_num = channel_num / 16; + struct wine_rb_entry *entry; + if (!(entry = wine_rb_get(&This->channel_blocks, &block_num))) return NULL; + return WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry)->channels + channel_num % 16; +} + static struct channel_block *channel_block_set(struct wine_rb_tree *tree, DWORD block_num, IDirectMusicPort *port, DWORD midi_group, BOOL only_set_new) { @@ -1198,18 +1206,17 @@ static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface IDirectMusicPort *port, DWORD midi_group, DWORD midi_channel) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); - struct channel_block *block; + struct channel *channel; FIXME("(%p)->(%ld, %p, %ld, %ld) semi-stub\n", This, channel_num, port, midi_group, midi_channel); if (!port) return E_POINTER; if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; - block = channel_block_set(&This->channel_blocks, channel_num / 16, port, 0, TRUE); - if (block) { - block->channels[channel_num % 16].midi_group = midi_group; - block->channels[channel_num % 16].midi_channel = midi_channel; - } + if (!(channel = performance_get_channel(This, channel_num))) return DMUS_E_NOT_FOUND; + channel->midi_group = midi_group; + channel->midi_channel = midi_channel; + channel->port = port; return S_OK; } @@ -1218,24 +1225,18 @@ static HRESULT WINAPI performance_PChannelInfo(IDirectMusicPerformance8 *iface, IDirectMusicPort **port, DWORD *midi_group, DWORD *midi_channel) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); - struct channel_block *block; - struct wine_rb_entry *entry; - DWORD block_num = channel_num / 16; - unsigned int index = channel_num % 16; + struct channel *channel; TRACE("(%p)->(%ld, %p, %p, %p)\n", This, channel_num, port, midi_group, midi_channel); - entry = wine_rb_get(&This->channel_blocks, &block_num); - if (!entry) - return E_INVALIDARG; - block = WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry); - - if (port) { - *port = block->channels[index].port; + if (!(channel = performance_get_channel(This, channel_num))) return E_INVALIDARG; + if (port) + { + *port = channel->port; IDirectMusicPort_AddRef(*port); } - if (midi_group) *midi_group = block->channels[index].midi_group; - if (midi_channel) *midi_channel = block->channels[index].midi_channel; + if (midi_group) *midi_group = channel->midi_group; + if (midi_channel) *midi_channel = channel->midi_channel; return S_OK; } From 6247e5fbc8eefda9ef2414b78586d7d863ce6a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 15:38:12 +0200 Subject: [PATCH 1088/1148] dmime: Set channel block info using IDirectMusicPerformance8_AssignPChannelBlock. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index 5418bc04236..e284c61fdf6 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -257,31 +257,19 @@ static struct channel *performance_get_channel(struct performance *This, DWORD c return WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry)->channels + channel_num % 16; } -static struct channel_block *channel_block_set(struct wine_rb_tree *tree, DWORD block_num, - IDirectMusicPort *port, DWORD midi_group, BOOL only_set_new) +static struct channel_block *performance_get_channel_block(struct performance *This, DWORD block_num) { struct channel_block *block; struct wine_rb_entry *entry; - unsigned int i; - entry = wine_rb_get(tree, &block_num); - if (entry) { + if ((entry = wine_rb_get(&This->channel_blocks, &block_num))) block = WINE_RB_ENTRY_VALUE(entry, struct channel_block, entry); - if (only_set_new) - return block; - } else { - if (!(block = malloc(sizeof(*block)))) return NULL; + else if ((block = malloc(sizeof(*block)))) + { block->block_num = block_num; + wine_rb_put(&This->channel_blocks, &block->block_num, &block->entry); } - for (i = 0; i < 16; ++i) { - block->channels[i].port = port; - block->channels[i].midi_group = midi_group; - block->channels[i].midi_channel = i; - } - if (!entry) - wine_rb_put(tree, &block->block_num, &block->entry); - return block; } @@ -1121,6 +1109,7 @@ static void performance_update_latency_time(struct performance *This, IDirectMus static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *params) { + IDirectMusicPerformance8 *iface = &perf->IDirectMusicPerformance8_iface; IDirectMusicPort *port; GUID guid; unsigned int i; @@ -1140,7 +1129,10 @@ static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *par } for (i = 0; i < params->dwChannelGroups; i++) - channel_block_set(&perf->channel_blocks, i, port, i + 1, FALSE); + { + if (FAILED(hr = IDirectMusicPerformance8_AssignPChannelBlock(iface, i, port, i + 1))) + ERR("Failed to set performance channel block info, hr %#lx\n", hr); + } performance_update_latency_time(perf, port, NULL); return S_OK; @@ -1190,6 +1182,8 @@ static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 * DWORD block_num, IDirectMusicPort *port, DWORD midi_group) { struct performance *This = impl_from_IDirectMusicPerformance8(iface); + struct channel_block *block; + UINT i; FIXME("(%p, %ld, %p, %ld): semi-stub\n", This, block_num, port, midi_group); @@ -1197,7 +1191,14 @@ static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 * if (block_num > MAXDWORD / 16) return E_INVALIDARG; if (This->audio_paths_enabled) return DMUS_E_AUDIOPATHS_IN_USE; - channel_block_set(&This->channel_blocks, block_num, port, midi_group, FALSE); + if (!(block = performance_get_channel_block(This, block_num))) return E_OUTOFMEMORY; + for (i = 0; i < ARRAY_SIZE(block->channels); ++i) + { + struct channel *channel = block->channels + i; + channel->midi_group = midi_group; + channel->midi_channel = i; + channel->port = port; + } return S_OK; } From 9c5c67a07f0bb6c13b2571e88cf92ab85b24037e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 26 Oct 2023 15:25:13 +0200 Subject: [PATCH 1089/1148] dmime: Avoid leaking performance channel block ports. CW-Bug-Id: #16680 CW-Bug-Id: #20424 CW-Bug-Id: #22409 --- dlls/dmime/performance.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index e284c61fdf6..c19efe022bf 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -1135,6 +1135,7 @@ static HRESULT perf_dmport_create(struct performance *perf, DMUS_PORTPARAMS *par } performance_update_latency_time(perf, port, NULL); + IDirectMusicPort_Release(port); return S_OK; } @@ -1198,6 +1199,8 @@ static HRESULT WINAPI performance_AssignPChannelBlock(IDirectMusicPerformance8 * channel->midi_group = midi_group; channel->midi_channel = i; channel->port = port; + if (channel->port) IDirectMusicPort_Release(channel->port); + if ((channel->port = port)) IDirectMusicPort_AddRef(channel->port); } return S_OK; @@ -1218,6 +1221,7 @@ static HRESULT WINAPI performance_AssignPChannel(IDirectMusicPerformance8 *iface channel->midi_group = midi_group; channel->midi_channel = midi_channel; channel->port = port; + IDirectMusicPort_AddRef(port); return S_OK; } From 44fbee72d2d9e0ae095f87d01bf179a18f1b3ea3 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 1 Nov 2023 01:55:28 +0000 Subject: [PATCH 1090/1148] rtworkq: Avoid use-after-free. queue_release_pending_item releases the work_item reference but later accesses `item->queue`, which is a potential use-after-free. --- dlls/rtworkq/queue.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dlls/rtworkq/queue.c b/dlls/rtworkq/queue.c index c088d892f39..b748768de7d 100644 --- a/dlls/rtworkq/queue.c +++ b/dlls/rtworkq/queue.c @@ -734,14 +734,16 @@ static HRESULT invoke_async_callback(IRtwqAsyncResult *result) static void queue_release_pending_item(struct work_item *item) { - EnterCriticalSection(&item->queue->cs); + struct queue *queue = item->queue; + EnterCriticalSection(&queue->cs); if (item->key) { list_remove(&item->entry); item->key = 0; IUnknown_Release(&item->IUnknown_iface); } - LeaveCriticalSection(&item->queue->cs); + LeaveCriticalSection(&queue->cs); + } static void CALLBACK waiting_item_callback(TP_CALLBACK_INSTANCE *instance, void *context, TP_WAIT *wait, From 5efa97e4722f5b7b933908c2d5b79c3ec386d4b5 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 1 Nov 2023 02:23:20 +0000 Subject: [PATCH 1091/1148] mfplat: tests: Validate MFCancelWorkItem releases the callback. --- dlls/mfplat/tests/mfplat.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 7264db7f79b..a3b39ccec57 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -3200,6 +3200,7 @@ static void test_scheduled_items(void) IMFAsyncResult *result; MFWORKITEM_KEY key, key2; HRESULT hr; + ULONG refcount; callback = create_test_callback(NULL); @@ -3212,6 +3213,9 @@ static void test_scheduled_items(void) hr = MFCancelWorkItem(key); ok(hr == S_OK, "Failed to cancel item, hr %#lx.\n", hr); + refcount = IMFAsyncCallback_Release(&callback->IMFAsyncCallback_iface); + ok(refcount == 0, "Unexpected refcount %lu.\n", refcount); + hr = MFCancelWorkItem(key); ok(hr == MF_E_NOT_FOUND || broken(hr == S_OK) /* < win10 */, "Unexpected hr %#lx.\n", hr); @@ -3221,6 +3225,8 @@ static void test_scheduled_items(void) return; } + callback = create_test_callback(NULL); + hr = MFCreateAsyncResult(NULL, &callback->IMFAsyncCallback_iface, NULL, &result); ok(hr == S_OK, "Failed to create result, hr %#lx.\n", hr); From 3f088c2df1302aca0fcb03147388dffc9a0b90c9 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 1 Nov 2023 02:05:38 +0000 Subject: [PATCH 1092/1148] rtworkq: Release cancelled work items. And avoiding race condition while doing so. Usually the threadpool holds a reference to the work_item, which is released when the work_item's callback is invoked. However queue_cancel_item closes the threadpool object without releasing it, leading to a leak. The fix is not as simple as adding a IUnknown_Release, because the work_item's callback can still be called after CloseThreadpoolTimer/Wait has returned. In fact its callback might currently be running. In which case we would double release the reference. We have to stop any further callbacks to be queued, wait for any currently running callbacks to finish, then finally we can close the threadpool object. After that, if the callback wasn't called, we can safely release the work_item reference. --- dlls/rtworkq/queue.c | 73 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/dlls/rtworkq/queue.c b/dlls/rtworkq/queue.c index b748768de7d..c5a269c2402 100644 --- a/dlls/rtworkq/queue.c +++ b/dlls/rtworkq/queue.c @@ -880,32 +880,87 @@ static HRESULT queue_submit_timer(struct queue *queue, IRtwqAsyncResult *result, return S_OK; } -static HRESULT queue_cancel_item(struct queue *queue, RTWQWORKITEM_KEY key) +static HRESULT queue_cancel_item(struct queue *queue, const RTWQWORKITEM_KEY key) { HRESULT hr = RTWQ_E_NOT_FOUND; + union { TP_WAIT *wait_object; TP_TIMER *timer_object; } work_object; + enum work_item_type work_object_type; struct work_item *item; + const UINT64 mask = key >> 32; EnterCriticalSection(&queue->cs); LIST_FOR_EACH_ENTRY(item, &queue->pending_items, struct work_item, entry) { if (item->key == key) { - key >>= 32; - if ((key & WAIT_ITEM_KEY_MASK) == WAIT_ITEM_KEY_MASK) + hr = S_OK; + if ((mask & WAIT_ITEM_KEY_MASK) == WAIT_ITEM_KEY_MASK) + { + if (item->type != WORK_ITEM_WAIT) + WARN("Item %p is not a wait item, but its key has wait item mask.\n", item); + + work_object_type = WORK_ITEM_WAIT; + work_object.wait_object = item->u.wait_object; + item->u.wait_object = NULL; + } + else if ((mask & SCHEDULED_ITEM_KEY_MASK) == SCHEDULED_ITEM_KEY_MASK) + { + if (item->type != WORK_ITEM_TIMER) + WARN("Item %p is not a timer item, but its key has timer item mask.\n", item); + + work_object_type = WORK_ITEM_TIMER; + work_object.timer_object = item->u.timer_object; + item->u.timer_object = NULL; + } + else + { + WARN("Unknown item key mask %#I64x.\n", mask); + queue_release_pending_item(item); + goto out; + } + break; + } + } + + if (FAILED(hr)) + goto out; + + LeaveCriticalSection(&queue->cs); + + // Safely either stop the thread pool object, or if the callback is already running, wait for it to finish. + // This way, we can safely release the reference to the work item. + if (work_object_type == WORK_ITEM_WAIT) + { + SetThreadpoolWait(work_object.wait_object, NULL, NULL); + WaitForThreadpoolWaitCallbacks(work_object.wait_object, TRUE); + CloseThreadpoolWait(work_object.wait_object); + } + else if (work_object_type == WORK_ITEM_TIMER) + { + SetThreadpoolTimer(work_object.timer_object, NULL, 0, 0); + WaitForThreadpoolTimerCallbacks(work_object.timer_object, TRUE); + CloseThreadpoolTimer(work_object.timer_object); + } + + // If the work item is still in pending items, its callback hasn't been invoked yet; + // we remove it. Otherwise its callback would have already released it. + EnterCriticalSection(&queue->cs); + LIST_FOR_EACH_ENTRY(item, &queue->pending_items, struct work_item, entry) + { + if (item->key == key) + { + if (work_object_type == WORK_ITEM_WAIT) { IRtwqAsyncResult_SetStatus(item->result, RTWQ_E_OPERATION_CANCELLED); invoke_async_callback(item->result); - CloseThreadpoolWait(item->u.wait_object); } - else if ((key & SCHEDULED_ITEM_KEY_MASK) == SCHEDULED_ITEM_KEY_MASK) - CloseThreadpoolTimer(item->u.timer_object); - else - WARN("Unknown item key mask %#I64x.\n", key); queue_release_pending_item(item); - hr = S_OK; + IUnknown_Release(&item->IUnknown_iface); break; } } + +out: LeaveCriticalSection(&queue->cs); return hr; From 78c3b5f46d38969a66a0ad023761fd087f4ef41e Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 6 Nov 2023 17:18:42 +0200 Subject: [PATCH 1093/1148] Revert "win32u: Ignore emulated mouse messages on touch-enabled windows." This reverts commit ea2ee1a6e991d98a00a402ee656fe0b03cad3b9a. --- dlls/win32u/message.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 6284f0452b9..6a9e77f796c 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -1639,12 +1639,6 @@ static BOOL process_mouse_message( MSG *msg, UINT hw_id, ULONG_PTR extra_info, H msg->pt = point_phys_to_win_dpi( msg->hwnd, msg->pt ); SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( msg->hwnd )); - if ((extra_info & 0xffffff00) == 0xff515700 && NtUserIsTouchWindow( msg->hwnd, NULL )) - { - accept_hardware_message( hw_id ); - return FALSE; - } - if ((extra_info & 0xffffff00) != 0xff515700 && enable_mouse_in_pointer) { WORD flags = POINTER_MESSAGE_FLAG_PRIMARY; From 271b699bf7357ebdce993f615b134d69d5e7c6bd Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 7 Nov 2023 15:10:52 -0600 Subject: [PATCH 1094/1148] fixup! fshack: winex11: Support opengl scaling according to fake resolution. NULL get_win_data() may happen if the window is from the other process. CW-Bug-Id: #22950 --- dlls/winex11.drv/opengl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 0380aa4cc63..ba1a42855aa 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -1530,10 +1530,12 @@ static struct gl_drawable *create_gl_drawable( HWND hwnd, const struct wgl_pixel gl->window = create_client_window( hwnd, visual ); if (gl->window) gl->drawable = pglXCreateWindow( gdi_display, gl->format->fbconfig, gl->window, NULL ); - 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 ); + if ((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 client %lx drawable %lx\n", hwnd, gl->window, gl->drawable ); } #ifdef SONAME_LIBXCOMPOSITE From 4a2cac9b1a197ae550a7f8530333eacd3d034da7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 7 Nov 2023 15:17:51 -0600 Subject: [PATCH 1095/1148] fixup! winex11.drv: fshack/GL: Support fshack on offscreen drawables. CW-Bug-Id: #22950 --- dlls/winex11.drv/opengl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index ba1a42855aa..4455f9464e9 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -1550,10 +1550,12 @@ 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 ); + if ((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 ); + } if (gl->layered_type) detach_client_window( hwnd, 0 ); TRACE( "%p created child %lx drawable %lx\n", hwnd, gl->window, gl->drawable ); } From 05383dc54ff0b16bf0878b44d32264f92ed486af Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 8 Nov 2023 12:43:13 -0600 Subject: [PATCH 1096/1148] wine.inf: Enable builtin amd_ags_x64 for Assassin's Creed Mirage. CW-Bug-Id: #22962 --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index f0395ecc4ca..90a1ec682c9 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2854,6 +2854,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\ACMirage.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 71e696cb7d01ceb8f4eae18bcab1bd8f9152b596 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 8 Nov 2023 17:32:04 -0600 Subject: [PATCH 1097/1148] user32: Return empty string from LoadStringW() if resource is not found. CW-Bug-Id: #22967 --- dlls/user32/resource.c | 9 +++++++-- dlls/user32/tests/resource.c | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/dlls/user32/resource.c b/dlls/user32/resource.c index 3714b05ce8d..a634d79948b 100644 --- a/dlls/user32/resource.c +++ b/dlls/user32/resource.c @@ -164,9 +164,9 @@ INT WINAPI DECLSPEC_HOTPATCH LoadStringW( HINSTANCE instance, UINT resource_id, /* Use loword (incremented by 1) as resourceid */ hrsrc = FindResourceW( instance, MAKEINTRESOURCEW((LOWORD(resource_id) >> 4) + 1), (LPWSTR)RT_STRING ); - if (!hrsrc) return 0; + if (!hrsrc) goto error; hmem = LoadResource( instance, hrsrc ); - if (!hmem) return 0; + if (!hmem) goto error; p = LockResource(hmem); string_num = resource_id & 0x000f; @@ -196,6 +196,11 @@ INT WINAPI DECLSPEC_HOTPATCH LoadStringW( HINSTANCE instance, UINT resource_id, TRACE("%s loaded !\n", debugstr_w(buffer)); return i; + +error: + TRACE( "Failed to load string.\n" ); + if (buflen > 0) buffer[0] = 0; + return 0; } /********************************************************************** diff --git a/dlls/user32/tests/resource.c b/dlls/user32/tests/resource.c index fff962b955c..9c2b9eba0a2 100644 --- a/dlls/user32/tests/resource.c +++ b/dlls/user32/tests/resource.c @@ -50,6 +50,7 @@ static void test_LoadStringW(void) win_skip( "LoadStringW does not return a pointer to the resource\n" ); return; } + length2 = LoadStringW(hInst, 2, returnedstringw, ARRAY_SIZE(returnedstringw)); /* get resource string */ ok(length2 > 0, "LoadStringW failed to load resource 2, ret %d, err %ld\n", length2, GetLastError()); ok(length1 == length2, "LoadStringW returned different values dependent on buflen. ret1 %d, ret2 %d\n", @@ -75,6 +76,30 @@ static void test_LoadStringW(void) /* check again, with a different buflen value, that calling LoadStringW with buffer = NULL returns zero */ retvalue = LoadStringW(hInst, 2, NULL, 128); ok(!retvalue, "LoadStringW returned a non-zero value when called with buffer = NULL, retvalue = %d\n", retvalue); + + /* Test missing resource. */ + SetLastError(0xdeadbeef); + memset(returnedstringw, 0xcc, sizeof(returnedstringw)); + length1 = LoadStringW(hInst, 0xdeadbeef, returnedstringw, ARRAY_SIZE(returnedstringw)); + ok(!length1, "got %d.\n", length1); + ok(GetLastError() == ERROR_RESOURCE_NAME_NOT_FOUND, "got %lu.\n", GetLastError()); + ok(!returnedstringw[0], "got %#x.\n", returnedstringw[0]); + ok(returnedstringw[1] == 0xcccc, "got %#x.\n", returnedstringw[1]); + + SetLastError(0xdeadbeef); + memset(returnedstringw, 0xcc, sizeof(returnedstringw)); + length1 = LoadStringW(hInst, 0xdeadbeef, returnedstringw, 0); + ok(!length1, "got %d.\n", length1); + ok(GetLastError() == ERROR_RESOURCE_NAME_NOT_FOUND, "got %lu.\n", GetLastError()); + ok(returnedstringw[0] == 0xcccc, "got %#x.\n", returnedstringw[1]); + + SetLastError(0xdeadbeef); + memset(returnedstringw, 0xcc, sizeof(returnedstringw)); + length1 = LoadStringW(hInst, 0xdeadbeef, returnedstringw, 1); + ok(!length1, "got %d.\n", length1); + ok(GetLastError() == ERROR_RESOURCE_NAME_NOT_FOUND, "got %lu.\n", GetLastError()); + ok(!returnedstringw[0], "got %#x.\n", returnedstringw[0]); + ok(returnedstringw[1] == 0xcccc, "got %#x.\n", returnedstringw[1]); } static void test_LoadStringA (void) From f3fcbdc3809723a11ee451ea83e028c962aaa0bd Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 8 Nov 2023 17:56:17 -0600 Subject: [PATCH 1098/1148] user32: Put 0 to output string even for 1 char buffer in LoadStringW(). CW-Bug-Id: #22967 --- dlls/user32/resource.c | 6 +----- dlls/user32/tests/resource.c | 9 +++++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/dlls/user32/resource.c b/dlls/user32/resource.c index a634d79948b..c09ee9bb65c 100644 --- a/dlls/user32/resource.c +++ b/dlls/user32/resource.c @@ -187,12 +187,8 @@ INT WINAPI DECLSPEC_HOTPATCH LoadStringW( HINSTANCE instance, UINT resource_id, if (i > 0) { memcpy(buffer, p + 1, i * sizeof (WCHAR)); buffer[i] = 0; - } else { - if (buflen > 1) { - buffer[0] = 0; - return 0; - } } + else goto error; TRACE("%s loaded !\n", debugstr_w(buffer)); return i; diff --git a/dlls/user32/tests/resource.c b/dlls/user32/tests/resource.c index 9c2b9eba0a2..9379b5c7732 100644 --- a/dlls/user32/tests/resource.c +++ b/dlls/user32/tests/resource.c @@ -100,6 +100,15 @@ static void test_LoadStringW(void) ok(GetLastError() == ERROR_RESOURCE_NAME_NOT_FOUND, "got %lu.\n", GetLastError()); ok(!returnedstringw[0], "got %#x.\n", returnedstringw[0]); ok(returnedstringw[1] == 0xcccc, "got %#x.\n", returnedstringw[1]); + + /* Test short buffer */ + SetLastError(0xdeadbeef); + memset(returnedstringw, 0xcc, sizeof(returnedstringw)); + length1 = LoadStringW(hInst, 2, returnedstringw, 1); /* get resource string */ + ok(!length1, "got %d.\n", length1); + ok(GetLastError() == 0xdeadbeef, "got %lu.\n", GetLastError()); + ok(!returnedstringw[0], "got %#x.\n", returnedstringw[0]); + ok(returnedstringw[1] == 0xcccc, "got %#x.\n", returnedstringw[1]); } static void test_LoadStringA (void) From ddb9b1e2ffed7cd795fef688b9d4cf9fac9e2aab Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 8 Nov 2023 17:38:10 -0600 Subject: [PATCH 1099/1148] kernelbase: Return empty string from LoadStringW() if resource is not found. CW-Bug-Id: #22967 --- dlls/kernelbase/string.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dlls/kernelbase/string.c b/dlls/kernelbase/string.c index 798bc1f3d63..1a792dcadb1 100644 --- a/dlls/kernelbase/string.c +++ b/dlls/kernelbase/string.c @@ -1231,9 +1231,9 @@ INT WINAPI DECLSPEC_HOTPATCH LoadStringW(HINSTANCE instance, UINT resource_id, L /* Use loword (incremented by 1) as resourceid */ hrsrc = FindResourceW(instance, MAKEINTRESOURCEW((LOWORD(resource_id) >> 4) + 1), (LPWSTR)RT_STRING); - if (!hrsrc) return 0; + if (!hrsrc) goto error; hmem = LoadResource(instance, hrsrc); - if (!hmem) return 0; + if (!hmem) goto error; p = LockResource(hmem); string_num = resource_id & 0x000f; @@ -1267,6 +1267,11 @@ INT WINAPI DECLSPEC_HOTPATCH LoadStringW(HINSTANCE instance, UINT resource_id, L TRACE("returning %s\n", debugstr_w(buffer)); return i; + +error: + TRACE( "Failed to load string.\n" ); + if (buflen > 0) buffer[0] = 0; + return 0; } INT WINAPI DECLSPEC_HOTPATCH LoadStringA(HINSTANCE instance, UINT resource_id, LPSTR buffer, INT buflen) From 939fe522ecad9b81f7157ad2426fa092a96c771e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 8 Nov 2023 17:58:41 -0600 Subject: [PATCH 1100/1148] kernelbase: Put 0 to output string even for 1 char buffer in LoadStringW(). CW-Bug-Id: #22967 --- dlls/kernelbase/string.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/dlls/kernelbase/string.c b/dlls/kernelbase/string.c index 1a792dcadb1..1df1f54aafd 100644 --- a/dlls/kernelbase/string.c +++ b/dlls/kernelbase/string.c @@ -1256,14 +1256,7 @@ INT WINAPI DECLSPEC_HOTPATCH LoadStringW(HINSTANCE instance, UINT resource_id, L memcpy(buffer, p + 1, i * sizeof (WCHAR)); buffer[i] = 0; } - else - { - if (buflen > 1) - { - buffer[0] = 0; - return 0; - } - } + else goto error; TRACE("returning %s\n", debugstr_w(buffer)); return i; From 1d57f07242f5dc0b1ec9fded5a65f239dcf9687b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 9 Nov 2023 10:59:27 +0100 Subject: [PATCH 1101/1148] windows.gaming.input: Fix inverted start / select gamepad buttons. CW-Bug-Id: #22968 --- dlls/windows.gaming.input/gamepad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/windows.gaming.input/gamepad.c b/dlls/windows.gaming.input/gamepad.c index 112ec49a1d3..0d7cd690821 100644 --- a/dlls/windows.gaming.input/gamepad.c +++ b/dlls/windows.gaming.input/gamepad.c @@ -273,8 +273,8 @@ static HRESULT WINAPI gamepad_GetCurrentReading( IGamepad *iface, struct Gamepad if (state.buttons[3]) value->Buttons |= GamepadButtons_Y; if (state.buttons[4]) value->Buttons |= GamepadButtons_LeftShoulder; if (state.buttons[5]) value->Buttons |= GamepadButtons_RightShoulder; - if (state.buttons[6]) value->Buttons |= GamepadButtons_Menu; - if (state.buttons[7]) value->Buttons |= GamepadButtons_View; + if (state.buttons[6]) value->Buttons |= GamepadButtons_View; + if (state.buttons[7]) value->Buttons |= GamepadButtons_Menu; if (state.buttons[8]) value->Buttons |= GamepadButtons_LeftThumbstick; if (state.buttons[9]) value->Buttons |= GamepadButtons_RightThumbstick; From e1f28ec731ce08c2ef7157baca7eec4f569c6255 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 9 Nov 2023 16:06:37 -0600 Subject: [PATCH 1102/1148] amd_ags_x64: Implement agsDriverExtensionsDX11_DeInit(). CW-Bug-Id: #22976 --- dlls/amd_ags_x64/amd_ags_x64.spec | 2 +- dlls/amd_ags_x64/amd_ags_x64_main.c | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64.spec b/dlls/amd_ags_x64/amd_ags_x64.spec index a825a450cfa..d8126fa961a 100644 --- a/dlls/amd_ags_x64/amd_ags_x64.spec +++ b/dlls/amd_ags_x64/amd_ags_x64.spec @@ -8,7 +8,7 @@ @ stub agsDriverExtensionsDX11_CreateTexture1D @ stub agsDriverExtensionsDX11_CreateTexture2D @ stub agsDriverExtensionsDX11_CreateTexture3D -@ stub agsDriverExtensionsDX11_DeInit +@ stdcall agsDriverExtensionsDX11_DeInit(ptr) @ stub agsDriverExtensionsDX11_Destroy @ stdcall -norelay -arch=win64 agsDriverExtensionsDX11_DestroyDevice() @ stdcall -norelay -arch=win64 agsDriverExtensionsDX11_EndUAVOverlap() DX11_EndUAVOverlap_impl diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 1ee87f77a1e..3a9f99c1bc4 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -1078,6 +1078,19 @@ AGSReturnCode WINAPI agsDriverExtensionsDX11_Init( AGSContext *context, ID3D11De return AGS_SUCCESS; } +AGSReturnCode WINAPI agsDriverExtensionsDX11_DeInit( AGSContext* context ) +{ + TRACE("context %p.\n", context); + + if (context->d3d11_context) + { + ID3D11DeviceContext_Release(context->d3d11_context); + context->d3d11_context = NULL; + } + + return AGS_SUCCESS; +} + BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { TRACE("%p, %u, %p.\n", instance, reason, reserved); From a381bee7fea437914e7e419e8033e07f89663cd6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 9 Nov 2023 16:22:26 -0600 Subject: [PATCH 1103/1148] amd_ags_x64: Factor out get_version_number(). CW-Bug-Id: #22976 --- dlls/amd_ags_x64/amd_ags_x64_main.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 3a9f99c1bc4..96f952aed96 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -251,6 +251,20 @@ static AGSReturnCode vk_get_physical_device_properties(unsigned int *out_count, return ret; } +static enum amd_ags_version get_version_number(int ags_version) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(amd_ags_info); i++) + if (AGS_MAKE_VERSION(amd_ags_info[i].major, amd_ags_info[i].minor, amd_ags_info[i].patch) == ags_version) + { + TRACE("Found AGS v%d.%d.%d.\n", amd_ags_info[i].major, amd_ags_info[i].minor, amd_ags_info[i].patch); + return i; + } + ERR("Unknown ags_version %#x, using 5.4.1.\n", ags_version); + return AMD_AGS_VERSION_5_4_1; +} + static enum amd_ags_version determine_ags_version(void) { /* AMD AGS is not binary compatible between versions (even minor versions), and the game @@ -265,7 +279,7 @@ static enum amd_ags_version determine_ags_version(void) DWORD infosize; void *infobuf = NULL; void *val; - UINT vallen, i; + UINT vallen; VS_FIXEDFILEINFO *info; UINT16 major, minor, patch; WCHAR dllname[MAX_PATH], temp_path[MAX_PATH], temp_name[MAX_PATH]; @@ -318,17 +332,7 @@ static enum amd_ags_version determine_ags_version(void) minor = info->dwFileVersionMS; patch = info->dwFileVersionLS >> 16; TRACE("Found amd_ags_x64.dll v%d.%d.%d\n", major, minor, patch); - - for (i = 0; i < ARRAY_SIZE(amd_ags_info); i++) - { - if ((major == amd_ags_info[i].major) && - (minor == amd_ags_info[i].minor) && - (patch == amd_ags_info[i].patch)) - { - ret = i; - break; - } - } + ret = get_version_number(AGS_MAKE_VERSION(major, minor, patch)); done: if (*temp_name) From e3e5b48d9e943929e887b61037dc3220a83d524d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 27 Sep 2023 20:32:35 -0600 Subject: [PATCH 1104/1148] amd_ags_x64: Use version provided for agsInitialize(). CW-Bug-Id: #22976 --- dlls/amd_ags_x64/amd_ags_x64_main.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 96f952aed96..1d31494b986 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -265,7 +265,7 @@ static enum amd_ags_version get_version_number(int ags_version) return AMD_AGS_VERSION_5_4_1; } -static enum amd_ags_version determine_ags_version(void) +static enum amd_ags_version determine_ags_version(int ags_version) { /* AMD AGS is not binary compatible between versions (even minor versions), and the game * does not request a specific version when calling agsInit(). @@ -284,6 +284,11 @@ static enum amd_ags_version determine_ags_version(void) UINT16 major, minor, patch; WCHAR dllname[MAX_PATH], temp_path[MAX_PATH], temp_name[MAX_PATH]; + TRACE("ags_version %#x.\n", ags_version); + + if (ags_version) + return get_version_number(ags_version); + *temp_name = 0; if (!(infosize = GetModuleFileNameW(GetModuleHandleW(L"amd_ags_x64.dll"), dllname, ARRAY_SIZE(dllname))) || infosize == ARRAY_SIZE(dllname)) @@ -581,7 +586,7 @@ static void init_device_displays_511(const char *adapter_name, AGSDisplayInfo_51 } -static AGSReturnCode init_ags_context(AGSContext *context) +static AGSReturnCode init_ags_context(AGSContext *context, int ags_version) { AGSReturnCode ret; unsigned int i, j; @@ -589,7 +594,7 @@ static AGSReturnCode init_ags_context(AGSContext *context) memset(context, 0, sizeof(*context)); - context->version = determine_ags_version(); + context->version = determine_ags_version(ags_version); ret = vk_get_physical_device_properties(&context->device_count, &context->properties, &context->memory_properties); if (ret != AGS_SUCCESS || !context->device_count) @@ -690,7 +695,7 @@ AGSReturnCode WINAPI agsInit(AGSContext **context, const AGSConfiguration *confi if (!(object = heap_alloc(sizeof(*object)))) return AGS_OUT_OF_MEMORY; - if ((ret = init_ags_context(object)) != AGS_SUCCESS) + if ((ret = init_ags_context(object, 0)) != AGS_SUCCESS) { heap_free(object); return ret; @@ -728,7 +733,7 @@ AGSReturnCode WINAPI agsInitialize(int ags_version, const AGSConfiguration *conf if (!(object = heap_alloc(sizeof(*object)))) return AGS_OUT_OF_MEMORY; - if ((ret = init_ags_context(object)) != AGS_SUCCESS) + if ((ret = init_ags_context(object, ags_version)) != AGS_SUCCESS) { heap_free(object); return ret; @@ -1051,7 +1056,7 @@ AGSDriverVersionResult WINAPI agsCheckDriverVersion(const char* version_reported int WINAPI agsGetVersionNumber(void) { - enum amd_ags_version version = determine_ags_version(); + enum amd_ags_version version = determine_ags_version(0); TRACE("version %d.\n", version); From 7f2e6bc27710c80cf33b86de2d809cf1cd026221 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 9 Nov 2023 16:57:24 -0600 Subject: [PATCH 1105/1148] amd_ags_x64: Factor out get_ags_version_from_resource(). CW-Bug-Id: #22976 --- dlls/amd_ags_x64/amd_ags_x64_main.c | 90 ++++++++++++++++------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 1d31494b986..c2a94703285 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -265,6 +265,52 @@ static enum amd_ags_version get_version_number(int ags_version) return AMD_AGS_VERSION_5_4_1; } +static BOOL get_ags_version_from_resource(const WCHAR *filename, enum amd_ags_version *ret) +{ + DWORD infosize; + void *infobuf; + void *val; + UINT vallen; + VS_FIXEDFILEINFO *info; + UINT16 major, minor, patch; + + infosize = GetFileVersionInfoSizeW(filename, NULL); + if (!infosize) + { + ERR("File version info not found, err %u.\n", GetLastError()); + return FALSE; + } + + if (!(infobuf = heap_alloc(infosize))) + { + ERR("Failed to allocate memory.\n"); + return FALSE; + } + + if (!GetFileVersionInfoW(filename, 0, infosize, infobuf)) + { + ERR("GetFileVersionInfoW failed, err %u.\n", GetLastError()); + heap_free(infobuf); + return FALSE; + } + + if (!VerQueryValueW(infobuf, L"\\", &val, &vallen) || (vallen != sizeof(VS_FIXEDFILEINFO))) + { + ERR("Version value not found, err %u.\n", GetLastError()); + heap_free(infobuf); + return FALSE; + } + + info = val; + major = info->dwFileVersionMS >> 16; + minor = info->dwFileVersionMS; + patch = info->dwFileVersionLS >> 16; + TRACE("Found amd_ags_x64.dll v%d.%d.%d\n", major, minor, patch); + *ret = get_version_number(AGS_MAKE_VERSION(major, minor, patch)); + heap_free(infobuf); + return TRUE; +} + static enum amd_ags_version determine_ags_version(int ags_version) { /* AMD AGS is not binary compatible between versions (even minor versions), and the game @@ -276,13 +322,8 @@ static enum amd_ags_version determine_ags_version(int ags_version) * In case of an error, assume it's that version. */ enum amd_ags_version ret = AMD_AGS_VERSION_5_4_1; - DWORD infosize; - void *infobuf = NULL; - void *val; - UINT vallen; - VS_FIXEDFILEINFO *info; - UINT16 major, minor, patch; WCHAR dllname[MAX_PATH], temp_path[MAX_PATH], temp_name[MAX_PATH]; + DWORD size; TRACE("ags_version %#x.\n", ags_version); @@ -290,8 +331,8 @@ static enum amd_ags_version determine_ags_version(int ags_version) return get_version_number(ags_version); *temp_name = 0; - if (!(infosize = GetModuleFileNameW(GetModuleHandleW(L"amd_ags_x64.dll"), dllname, ARRAY_SIZE(dllname))) - || infosize == ARRAY_SIZE(dllname)) + if (!(size = GetModuleFileNameW(GetModuleHandleW(L"amd_ags_x64.dll"), dllname, ARRAY_SIZE(dllname))) + || size == ARRAY_SIZE(dllname)) { ERR("GetModuleFileNameW failed.\n"); goto done; @@ -307,43 +348,12 @@ static enum amd_ags_version determine_ags_version(int ags_version) goto done; } - infosize = GetFileVersionInfoSizeW(temp_name, NULL); - if (!infosize) - { - ERR("Unable to determine desired version of amd_ags_x64.dll.\n"); - goto done; - } - - if (!(infobuf = heap_alloc(infosize))) - { - ERR("Failed to allocate memory.\n"); - goto done; - } - - if (!GetFileVersionInfoW(temp_name, 0, infosize, infobuf)) - { - ERR("Unable to determine desired version of amd_ags_x64.dll.\n"); - goto done; - } - - if (!VerQueryValueW(infobuf, L"\\", &val, &vallen) || (vallen != sizeof(VS_FIXEDFILEINFO))) - { - ERR("Unable to determine desired version of amd_ags_x64.dll.\n"); - goto done; - } - - info = val; - major = info->dwFileVersionMS >> 16; - minor = info->dwFileVersionMS; - patch = info->dwFileVersionLS >> 16; - TRACE("Found amd_ags_x64.dll v%d.%d.%d\n", major, minor, patch); - ret = get_version_number(AGS_MAKE_VERSION(major, minor, patch)); + get_ags_version_from_resource(temp_name, &ret); done: if (*temp_name) DeleteFileW(temp_name); - heap_free(infobuf); TRACE("Using AGS v%d.%d.%d interface\n", amd_ags_info[ret].major, amd_ags_info[ret].minor, amd_ags_info[ret].patch); return ret; From fa07dce0be65031d07fe3a5034b064ba42ad6046 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 9 Nov 2023 17:08:32 -0600 Subject: [PATCH 1106/1148] amd_ags_x64: Try to get version from agsGetVersionNumber() if there is no version resource. CW-Bug-Id: #22976 --- dlls/amd_ags_x64/amd_ags_x64_main.c | 21 ++++++++++++++++++++- 1 file changed, 20 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 c2a94703285..c51ec4b2507 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -323,6 +323,8 @@ static enum amd_ags_version determine_ags_version(int ags_version) */ enum amd_ags_version ret = AMD_AGS_VERSION_5_4_1; WCHAR dllname[MAX_PATH], temp_path[MAX_PATH], temp_name[MAX_PATH]; + int (WINAPI *pagsGetVersionNumber)(void); + HMODULE hnative = NULL; DWORD size; TRACE("ags_version %#x.\n", ags_version); @@ -348,9 +350,26 @@ static enum amd_ags_version determine_ags_version(int ags_version) goto done; } - get_ags_version_from_resource(temp_name, &ret); + if (get_ags_version_from_resource(temp_name, &ret)) + goto done; + + if (!(hnative = LoadLibraryW(temp_name))) + { + ERR("LoadLibraryW failed for %s.\n", debugstr_w(temp_name)); + goto done; + } + + if ((pagsGetVersionNumber = (void *)GetProcAddress(hnative, "agsGetVersionNumber"))) + { + ags_version = pagsGetVersionNumber(); + ret = get_version_number(ags_version); + TRACE("Got version %#x (%d) from agsGetVersionNumber.\n", ags_version, ret); + } done: + if (hnative) + FreeLibrary(hnative); + if (*temp_name) DeleteFileW(temp_name); From 5b058903f7747a0dea6d7d0bc92131424a6003dc Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 9 Nov 2023 17:19:25 -0600 Subject: [PATCH 1107/1148] amd_ags_x64: Distinguish some versions through available exports if other methods failed. CW-Bug-Id: #22976 --- dlls/amd_ags_x64/amd_ags_x64_main.c | 23 ++++++++++++++++++++--- 1 file changed, 20 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 c51ec4b2507..ad1681b9abc 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -311,15 +311,29 @@ static BOOL get_ags_version_from_resource(const WCHAR *filename, enum amd_ags_ve return TRUE; } +static enum amd_ags_version guess_version_from_exports(HMODULE hnative) +{ + /* Known DLL versions without version info: + * - An update to AGS 5.4.1 included an amd_ags_x64.dll with no file version info; + * - CoD: Modern Warfare Remastered (2017) ships dll without version info which is version 5.0.1 + * (not tagged in AGSSDK history), compatible with 5.0.5. + */ + if (GetProcAddress(hnative, "agsDriverExtensionsDX11_Init")) + { + /* agsDriverExtensionsDX11_Init was deprecated in 5.3.0 */ + TRACE("agsDriverExtensionsDX11_Init found.\n"); + return AMD_AGS_VERSION_5_0_5; + } + TRACE("Returning 5.4.1.\n"); + return AMD_AGS_VERSION_5_4_1; +} + static enum amd_ags_version determine_ags_version(int ags_version) { /* AMD AGS is not binary compatible between versions (even minor versions), and the game * does not request a specific version when calling agsInit(). * Checking the version of amd_ags_x64.dll shipped with the game is the only way to * determine what version the game was built against. - * - * An update to AGS 5.4.1 included an amd_ags_x64.dll with no file version info. - * In case of an error, assume it's that version. */ enum amd_ags_version ret = AMD_AGS_VERSION_5_4_1; WCHAR dllname[MAX_PATH], temp_path[MAX_PATH], temp_name[MAX_PATH]; @@ -364,8 +378,11 @@ static enum amd_ags_version determine_ags_version(int ags_version) ags_version = pagsGetVersionNumber(); ret = get_version_number(ags_version); TRACE("Got version %#x (%d) from agsGetVersionNumber.\n", ags_version, ret); + goto done; } + ret = guess_version_from_exports(hnative); + done: if (hnative) FreeLibrary(hnative); From 4b9b6a471b4fd50734205329ba8b1797b1dd8bf5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 9 Nov 2023 17:53:47 -0600 Subject: [PATCH 1108/1148] amd_ags_x64: Recognize version 6.2.0. CW-Bug-Id: #22976 --- dlls/amd_ags_x64/amd_ags.h | 14 ++++++++------ dlls/amd_ags_x64/amd_ags_x64_main.c | 12 +++++++----- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags.h b/dlls/amd_ags_x64/amd_ags.h index 52276cc1935..aac9fb1413c 100644 --- a/dlls/amd_ags_x64/amd_ags.h +++ b/dlls/amd_ags_x64/amd_ags.h @@ -207,7 +207,8 @@ typedef enum AGSReturnCode AGS_NO_AMD_DRIVER_INSTALLED, ///< Returned if the AMD GPU driver does not appear to be installed AGS_EXTENSION_NOT_SUPPORTED, ///< Returned if the driver does not support the requested driver extension AGS_ADL_FAILURE, ///< Failure in ADL (the AMD Display Library) - AGS_DX_FAILURE ///< Failure from DirectX runtime + AGS_DX_FAILURE, ///< Failure from DirectX runtime + AGS_D3DDEVICE_NOT_CREATED, ///< Failure due to not creating the D3D device successfully via AGS. } AGSReturnCode; /// The DirectX11 extension support bits @@ -268,7 +269,7 @@ typedef enum AGSDriverExtensionDX12 } AGSDriverExtensionDX12; /// The space id for DirectX12 intrinsic support -const unsigned int AGS_DX12_SHADER_INSTRINSICS_SPACE_ID = 0x7FFF0ADE; // 2147420894 +const unsigned int AGS_DX12_SHADER_INTRINSICS_SPACE_ID = 0x7FFF0ADE; // 2147420894 /// The display flags describing various properties of the display. typedef enum AGSDisplayFlags @@ -942,7 +943,8 @@ typedef struct AGSDX12ReturnedParams unsigned int floatConversion : 1; ///< Supported in Radeon Software Version 20.5.1 onwards. unsigned int readLaneAt : 1; ///< Supported in Radeon Software Version 20.11.2 onwards. unsigned int rayHitToken : 1; ///< Supported in Radeon Software Version 20.11.2 onwards. - unsigned int padding : 20; ///< Reserved + unsigned int shaderClock : 1; ///< Supported in Radeon Software Version 23.1.1 onwards. + unsigned int padding : 19; ///< Reserved } ExtensionsSupported; ExtensionsSupported extensionsSupported; ///< List of supported extensions */ @@ -960,16 +962,16 @@ typedef struct AGSDX12ReturnedParams /// * The intrinsic instructions require a 5.1 shader model. /// * The Root Signature will need to reserve an extra UAV resource slot. This is not a real resource that requires allocating, it is just used to encode the intrinsic instructions. /// -/// The easiest way to set up the reserved UAV slot is to specify it at u0. The register space id will automatically be assumed to be \ref AGS_DX12_SHADER_INSTRINSICS_SPACE_ID. +/// The easiest way to set up the reserved UAV slot is to specify it at u0. The register space id will automatically be assumed to be \ref AGS_DX12_SHADER_INTRINSICS_SPACE_ID. /// The HLSL expects this as default and the set up code would look similar to this: /// \code{.cpp} /// CD3DX12_DESCRIPTOR_RANGE range[]; /// ... -/// range[ 0 ].Init( D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, AGS_DX12_SHADER_INSTRINSICS_SPACE_ID ); // u0 at driver-reserved space id +/// range[ 0 ].Init( D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, AGS_DX12_SHADER_INTRINSICS_SPACE_ID ); // u0 at driver-reserved space id /// \endcode /// /// Newer drivers also support a user-specified slot in which case the register space id is assumed to be 0. It is important that the \ref AGSDX12ReturnedParams::ExtensionsSupported::UAVBindSlot bit is set. -/// to ensure the driver can support this. If not, then u0 and \ref AGS_DX12_SHADER_INSTRINSICS_SPACE_ID must be used. +/// to ensure the driver can support this. If not, then u0 and \ref AGS_DX12_SHADER_INTRINSICS_SPACE_ID must be used. /// If the driver does support this feature and a non zero slot is required, then the HLSL must also define AMD_EXT_SHADER_INTRINSIC_UAV_OVERRIDE as the matching slot value. /// /// \param [in] context Pointer to a context. This is generated by \ref agsInitialize diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index ad1681b9abc..154e74cf1d6 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -39,6 +39,7 @@ enum amd_ags_version AMD_AGS_VERSION_6_0_0, AMD_AGS_VERSION_6_0_1, AMD_AGS_VERSION_6_1_0, + AMD_AGS_VERSION_6_2_0, AMD_AGS_VERSION_COUNT }; @@ -64,27 +65,28 @@ amd_ags_info[AMD_AGS_VERSION_COUNT] = {6, 0, 0, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600)}, {6, 0, 1, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600)}, {6, 1, 0, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600)}, + {6, 2, 0, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600)}, }; #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)}} + 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_511, name), offsetof(AGSDeviceInfo_520, name), \ offsetof(AGSDeviceInfo_520, name), offsetof(AGSDeviceInfo_520, name), -1, \ - -1, -1, -1, -1, -1}} + -1, -1, -1, -1, -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)}} + offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name)}} #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}} + -1, -1, -1, -1}} #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}} + -1, -1, -1}} #define DEVICE_FIELD_adapterString 0 #define DEVICE_FIELD_architectureVersion 1 From fc98a09ed758c6947436bb5a084ace1f337d0c0c Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Mon, 14 Aug 2023 17:17:05 +0800 Subject: [PATCH 1109/1148] winex11.drv: Set _NET_WM_FULLSCREEN_MONITORS only when necessary. If _NET_WM_FULLSCREEN_MONITORS is set then the property needs to be updated because it can't be deleted by sending a _NET_WM_FULLSCREEN_MONITORS client message to the root window according to the wm-spec version 1.4. Having the window spanning more than two monitors also needs the property set. In other cases, _NET_WM_FULLSCREEN_MONITORS doesn't need to be set. What's more, setting _NET_WM_FULLSCREEN_MONITORS adds a constraint on Mutter so that such a window can't be moved to another monitor by using the Shift+Super+Up/Down/Left/Right shortcut. So the property should be added only when necessary. CW-Bug-Id: #22540 --- dlls/winex11.drv/window.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 4aa9878ad76..a65fa46cde9 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1216,6 +1216,38 @@ static void update_net_wm_fullscreen_monitors( struct x11drv_win_data *data ) return; } + /* If _NET_WM_FULLSCREEN_MONITORS is not set and the fullscreen monitors are spanning only one + * monitor then do not set _NET_WM_FULLSCREEN_MONITORS. + * + * If _NET_WM_FULLSCREEN_MONITORS is set then the property needs to be updated because it can't + * be deleted by sending a _NET_WM_FULLSCREEN_MONITORS client message to the root window + * according to the wm-spec version 1.4. Having the window spanning more than two monitors also + * needs the property set. In other cases, _NET_WM_FULLSCREEN_MONITORS doesn't need to be set. + * What's more, setting _NET_WM_FULLSCREEN_MONITORS adds a constraint on Mutter so that such a + * window can't be moved to another monitor by using the Shift+Super+Up/Down/Left/Right + * shortcut. So the property should be added only when necessary. */ + if (monitors[0] == monitors[1] && monitors[1] == monitors[2] && monitors[2] == monitors[3]) + { + unsigned long count, remaining; + BOOL prop_found = FALSE; + long *prop_data; + int format; + Atom type; + + if (!XGetWindowProperty( data->display, data->whole_window, + x11drv_atom(_NET_WM_FULLSCREEN_MONITORS), 0, ~0, False, + XA_CARDINAL, &type, &format, &count, &remaining, + (unsigned char **)&prop_data )) + { + if (type == XA_CARDINAL && format == 32 && count == 4) + prop_found = TRUE; + XFree(prop_data); + } + + if (!prop_found) + return; + } + if (!data->mapped) { XChangeProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_FULLSCREEN_MONITORS), From 793404ff4dcc709f5597bd6d3bc0aaae4c8a4a51 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Tue, 22 Aug 2023 15:26:05 +0800 Subject: [PATCH 1110/1148] fixup! fshack: winex11: Implement _NET_FULLSCREEN_MONITOR support. Obviously, xinerama_get_fullscreen_monitors() won't be able to find multiple monitors with a single monitor rectangle from fs_hack_real_mode(). Fix a regression from 01dad2c92a56e141225a992ba09599c444e4a586. Restore Project Cars 3 triple-monitor support. CW-Bug-Id: #22609 --- dlls/winex11.drv/window.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index a65fa46cde9..46411d9dba3 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1180,8 +1180,6 @@ void update_user_time( Time time ) * windows spanning multiple monitors */ static void update_net_wm_fullscreen_monitors( struct x11drv_win_data *data ) { - RECT window_rect = data->whole_rect, monitor_rect; - HMONITOR hmonitor; long monitors[4]; XEvent xev; @@ -1194,25 +1192,9 @@ static void update_net_wm_fullscreen_monitors( struct x11drv_win_data *data ) if (!X11DRV_DisplayDevices_SupportEventHandlers()) return; - if (!(hmonitor = fs_hack_monitor_from_hwnd( data->hwnd ))) + if (!xinerama_get_fullscreen_monitors( &data->whole_rect, monitors )) { - ERR( "Failed to find monitor for %p at %s\n", data->hwnd, wine_dbgstr_rect(&data->whole_rect) ); - return; - } - - monitor_rect = fs_hack_current_mode( hmonitor ); - intersect_rect( &window_rect, &monitor_rect, &window_rect ); - if (!EqualRect( &window_rect, &data->whole_rect )) - { - ERR( "hwnd %p at %s is outside of monitor %p at %s, ignoring\n", data->hwnd, - wine_dbgstr_rect(&data->whole_rect), hmonitor, wine_dbgstr_rect(&monitor_rect) ); - return; - } - - monitor_rect = fs_hack_real_mode( hmonitor ); - if (!xinerama_get_fullscreen_monitors( &monitor_rect, monitors )) - { - ERR( "Failed to find xinerama monitors for %p at %s\n", hmonitor, wine_dbgstr_rect(&monitor_rect) ); + ERR( "Failed to find xinerama monitors at %s\n", wine_dbgstr_rect(&data->whole_rect) ); return; } From a351a90e3927f0bf9eff70f5a49ccd8556f4435f Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Sat, 4 Nov 2023 16:09:02 +0800 Subject: [PATCH 1111/1148] fshack: winex11.drv: Use fs_hack_monitor_from_rect() in X11DRV_ShowWindow(). fs_hack_monitor_from_hwnd() returns the primary monitor when the window is minimized. So a fullscreen window on the secondary monitor that later gets minimized may be moved to the primary monitor when fs_hack_monitor_from_hwnd() returns the primary monitor. Instead, we should use fs_hack_monitor_from_rect() and use the updated window rectangle to retrieve the monitor a window should be fullscreen on. fs_hack_monitor_from_hwnd() should only be used when not expecting window position changes. --- 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 46411d9dba3..ffcd91cc1db 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3410,7 +3410,7 @@ UINT X11DRV_ShowWindow( HWND hwnd, INT cmd, RECT *rect, UINT swp ) &root, &x, &y, &width, &height, &border, &depth ); XTranslateCoordinates( thread_data->display, data->whole_window, root, 0, 0, &x, &y, &top ); pos = root_to_virtual_screen( x, y ); - monitor = fs_hack_monitor_from_hwnd( hwnd ); + monitor = fs_hack_monitor_from_rect( rect ); if (data->fs_hack || (fs_hack_enabled( monitor ) && fs_hack_matches_current_mode( monitor, rect->right - rect->left, rect->bottom - rect->top ))) From 1d2deeeccf3113a57c8815c1639fce3b64954806 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Tue, 29 Aug 2023 13:33:32 -0300 Subject: [PATCH 1112/1148] mf: Handle errors with source event generator in session. (cherry picked from commit 0170cd3a4c67bd99291234dd8e0d638a824d7715) --- dlls/mf/session.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 2b7b4361c61..837782f829a 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -3691,7 +3691,9 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM if (FAILED(hr = IMFMediaEventGenerator_EndGetEvent(event_source, result, &event))) { WARN("Failed to get event from %p, hr %#lx.\n", event_source, hr); - goto failed; + IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MEError, &GUID_NULL, hr, NULL); + IMFMediaEventGenerator_Release(event_source); + return hr; } if (FAILED(hr = IMFMediaEvent_GetType(event, &event_type))) @@ -3884,11 +3886,15 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM PropVariantClear(&value); failed: - if (event) - IMFMediaEvent_Release(event); if (FAILED(hr = IMFMediaEventGenerator_BeginGetEvent(event_source, iface, (IUnknown *)event_source))) + { WARN("Failed to re-subscribe, hr %#lx.\n", hr); + IMFMediaEventQueue_QueueEvent(session->event_queue, event); + } + + if (event) + IMFMediaEvent_Release(event); IMFMediaEventGenerator_Release(event_source); From 15815c8c55cf79e8b7d538889062f523c2f229df Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Tue, 31 Oct 2023 20:44:53 -0300 Subject: [PATCH 1113/1148] mf: Signal event_cond in wg_parser_stream_disable. A workaround to fix a hang in media_source_Shutdown in old media source, because the wg_parser receives an EOS signal after media_source_Shutdown disabled the streams, and event_cond never gets signaled so it never stops being busy. --- dlls/winegstreamer/wg_parser.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 3e5afe49bf0..456112affe7 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -243,6 +243,7 @@ static NTSTATUS wg_parser_stream_disable(void *args) stream->enabled = false; stream->current_format.major_type = WG_MAJOR_TYPE_UNKNOWN; pthread_mutex_unlock(&parser->mutex); + pthread_cond_signal(&stream->event_cond); pthread_cond_signal(&stream->event_empty_cond); return S_OK; } From 0ebbd573f302708010d954330d24a0149b106e18 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 8 Nov 2023 13:36:20 -0600 Subject: [PATCH 1114/1148] winex11.drv: Don't call XIconifyWindow() on Gamescope. CW-Bug-Id: #22953 --- dlls/winex11.drv/window.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index ffcd91cc1db..fab1cd93879 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1450,7 +1450,7 @@ static void map_window( HWND hwnd, DWORD new_style ) sync_window_style( data ); XMapWindow( data->display, data->whole_window ); /* Mutter always unminimizes windows when handling map requests. Restore iconic state */ - if (new_style & WS_MINIMIZE) + if (new_style & WS_MINIMIZE && !wm_is_steamcompmgr( data->display )) XIconifyWindow( data->display, data->whole_window, data->vis.screen ); XFlush( data->display ); if (data->surface && data->vis.visualid != default_visual.visualid) @@ -3319,7 +3319,16 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags, TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic ); if (data->iconic) { - XIconifyWindow( data->display, data->whole_window, data->vis.screen ); + if (!wm_is_steamcompmgr( data->display )) + { + /* XIconifyWindow is essentially a no-op on Gamescope but has an undesirable side effect. + * Gamescope handles wm state change to iconic and immediately changes it back to normal. + * Upon that change back we would receive WM_STATE change notification and kick the window + * out of minimized state even if the window is not focused by Gamescope. Upon focusing the + * window Gamescope will change WM_STATE regardless and we will get the window out of + * minimized state correctly. */ + XIconifyWindow( data->display, data->whole_window, data->vis.screen ); + } } else if (is_window_rect_mapped( rectWindow )) { From 96bec82e3b819bd65bd32a53f053e9c0ea1aa1e8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 15 Nov 2023 19:03:11 -0600 Subject: [PATCH 1115/1148] win32u: Store effective AA flags in gdi_font. CW-Bug-Id: #22992 --- dlls/win32u/font.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index d079c41f35a..8d2a44abbfd 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -4689,6 +4689,7 @@ static HFONT CDECL font_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags ) *aa_flags = font_smoothing; } *aa_flags = font_funcs->get_aa_flags( font, *aa_flags, antialias_fakes ); + font->aa_flags = *aa_flags; } TRACE( "%p %s %d aa %x\n", hfont, debugstr_w(lf.lfFaceName), (int)lf.lfHeight, *aa_flags ); pthread_mutex_unlock( &font_lock ); From 47cf83e0446074c9f83076013080bc391fa8ee69 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 15 Nov 2023 21:37:09 -0600 Subject: [PATCH 1116/1148] win32u: Set all glyph load flags in get_load_flags(). CW-Bug-Id: #22992 --- dlls/win32u/freetype.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/dlls/win32u/freetype.c b/dlls/win32u/freetype.c index 416927c3a67..e0dd8aaf03d 100644 --- a/dlls/win32u/freetype.c +++ b/dlls/win32u/freetype.c @@ -3418,10 +3418,13 @@ static unsigned int get_bezier_glyph_outline(FT_Outline *outline, unsigned int b return needed; } -static FT_Int get_load_flags( UINT format ) +static FT_Int get_load_flags( UINT format, BOOL vertical_metrics, BOOL force_no_bitmap ) { FT_Int load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; + if (vertical_metrics) load_flags |= FT_LOAD_VERTICAL_LAYOUT; + if (force_no_bitmap || format != GGO_BITMAP) load_flags |= FT_LOAD_NO_BITMAP; + if (format & GGO_UNHINTED) return load_flags | FT_LOAD_NO_HINTING; @@ -3461,7 +3464,7 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT FT_Glyph_Metrics metrics; FT_Error err; FT_BBox bbox; - FT_Int load_flags = get_load_flags(format); + FT_Int load_flags; FT_Matrix transform_matrices[3], *matrices = NULL; BOOL vertical_metrics; @@ -3471,8 +3474,6 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT font->matrix.eM11, font->matrix.eM12, font->matrix.eM21, font->matrix.eM22); - format &= ~GGO_UNHINTED; - matrices = get_transform_matrices( font, tategaki, lpmat, transform_matrices ); vertical_metrics = (tategaki && FT_HAS_VERTICAL(ft_face)); @@ -3480,9 +3481,7 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT properly scaled and correct in 2.4.0 or greater */ if (vertical_metrics && FT_SimpleVersion < FT_VERSION_VALUE(2, 4, 0)) vertical_metrics = FALSE; - - if (matrices || format != GGO_BITMAP) load_flags |= FT_LOAD_NO_BITMAP; - if (vertical_metrics) load_flags |= FT_LOAD_VERTICAL_LAYOUT; + load_flags = get_load_flags(format, vertical_metrics, !!matrices); err = pFT_Load_Glyph(ft_face, glyph, load_flags); if (err && !(load_flags & FT_LOAD_NO_HINTING)) @@ -3497,6 +3496,8 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT return GDI_ERROR; } + format &= ~GGO_UNHINTED; + metrics = ft_face->glyph->metrics; if(font->fake_bold) { if (!get_bold_glyph_outline(ft_face->glyph, font->ppem, &metrics) && metrics.width) From 61198d90ab21a583074bda3b31a8c244886dc3f6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 15 Nov 2023 21:30:12 -0600 Subject: [PATCH 1117/1148] win32u: Use font AA flags when querying glyph outline with GGO_METRICS. CW-Bug-Id: #22992 --- dlls/win32u/freetype.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/freetype.c b/dlls/win32u/freetype.c index e0dd8aaf03d..2c8249e9c1f 100644 --- a/dlls/win32u/freetype.c +++ b/dlls/win32u/freetype.c @@ -3467,6 +3467,7 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT FT_Int load_flags; FT_Matrix transform_matrices[3], *matrices = NULL; BOOL vertical_metrics; + UINT effective_format = format; TRACE("%p, %04x, %08x, %p, %08x, %p, %p\n", font, glyph, format, lpgm, buflen, buf, lpmat); @@ -3476,14 +3477,22 @@ static UINT freetype_get_glyph_outline( struct gdi_font *font, UINT glyph, UINT matrices = get_transform_matrices( font, tategaki, lpmat, transform_matrices ); + if ((format & ~GGO_GLYPH_INDEX) == GGO_METRICS) + effective_format = font->aa_flags | (format & GGO_GLYPH_INDEX); vertical_metrics = (tategaki && FT_HAS_VERTICAL(ft_face)); /* there is a freetype bug where vertical metrics are only properly scaled and correct in 2.4.0 or greater */ if (vertical_metrics && FT_SimpleVersion < FT_VERSION_VALUE(2, 4, 0)) vertical_metrics = FALSE; - load_flags = get_load_flags(format, vertical_metrics, !!matrices); + load_flags = get_load_flags(effective_format, vertical_metrics, !!matrices); err = pFT_Load_Glyph(ft_face, glyph, load_flags); + if (err && format != effective_format) + { + WARN("Failed to load glyph %#x, retrying with GGO_METRICS. Error %#x.\n", glyph, err); + load_flags = get_load_flags(effective_format, vertical_metrics, !!matrices); + err = pFT_Load_Glyph(ft_face, glyph, load_flags); + } if (err && !(load_flags & FT_LOAD_NO_HINTING)) { WARN("Failed to load glyph %#x, retrying without hinting. Error %#x.\n", glyph, err); From bf2a7bc93b727f0138aceaab99ebb2fa558a3fd6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 16 Nov 2023 17:23:07 -0600 Subject: [PATCH 1118/1148] Revert "dsound: Get rid of the global device GUID arrays." This reverts commit 39b8fe1a353b715c1d580fe0334257846a744005. CW-Bug-Id: #23002 --- dlls/dsound/dsound_main.c | 31 ++++++++++++++++++++----------- dlls/dsound/dsound_private.h | 5 ++++- dlls/dsound/propset.c | 12 ++++++++---- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c index d18733294d0..3347f0243ab 100644 --- a/dlls/dsound/dsound_main.c +++ b/dlls/dsound/dsound_main.c @@ -76,6 +76,9 @@ static CRITICAL_SECTION_DEBUG DSOUND_renderers_lock_debug = }; CRITICAL_SECTION DSOUND_renderers_lock = { &DSOUND_renderers_lock_debug, -1, 0, 0, 0, 0 }; +GUID DSOUND_renderer_guids[MAXWAVEDRIVERS]; +GUID DSOUND_capture_guids[MAXWAVEDRIVERS]; + const WCHAR wine_vxd_drv[] = L"winemm.vxd"; /* All default settings, you most likely don't want to touch these, see wiki on UsefulRegistryKeys */ @@ -388,13 +391,13 @@ HRESULT get_mmdevice(EDataFlow flow, const GUID *tgt, IMMDevice **device) return DSERR_INVALIDPARAM; } -static BOOL send_device(IMMDevice *device, LPDSENUMCALLBACKW cb, void *user) +static BOOL send_device(IMMDevice *device, GUID *guid, + LPDSENUMCALLBACKW cb, void *user) { IPropertyStore *ps; PROPVARIANT pv; BOOL keep_going; HRESULT hr; - GUID guid; PropVariantInit(&pv); @@ -404,7 +407,7 @@ static BOOL send_device(IMMDevice *device, LPDSENUMCALLBACKW cb, void *user) return TRUE; } - hr = get_mmdevice_guid(device, ps, &guid); + hr = get_mmdevice_guid(device, ps, guid); if(FAILED(hr)){ IPropertyStore_Release(ps); return TRUE; @@ -418,10 +421,10 @@ static BOOL send_device(IMMDevice *device, LPDSENUMCALLBACKW cb, void *user) return TRUE; } - TRACE("Calling back with %s (%s)\n", wine_dbgstr_guid(&guid), + TRACE("Calling back with %s (%s)\n", wine_dbgstr_guid(guid), wine_dbgstr_w(pv.pwszVal)); - keep_going = cb(&guid, pv.pwszVal, wine_vxd_drv, user); + keep_going = cb(guid, pv.pwszVal, wine_vxd_drv, user); PropVariantClear(&pv); IPropertyStore_Release(ps); @@ -431,12 +434,13 @@ static BOOL send_device(IMMDevice *device, LPDSENUMCALLBACKW cb, void *user) /* S_FALSE means the callback returned FALSE at some point * S_OK means the callback always returned TRUE */ -HRESULT enumerate_mmdevices(EDataFlow flow, LPDSENUMCALLBACKW cb, void *user) +HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids, + LPDSENUMCALLBACKW cb, void *user) { IMMDeviceEnumerator *devenum; IMMDeviceCollection *coll; IMMDevice *defdev = NULL; - UINT count, i; + UINT count, i, n; BOOL keep_going; HRESULT hr, init_hr; @@ -475,8 +479,10 @@ HRESULT enumerate_mmdevices(EDataFlow flow, LPDSENUMCALLBACKW cb, void *user) eMultimedia, &defdev); if(FAILED(hr)){ defdev = NULL; + n = 0; }else{ - keep_going = send_device(defdev, cb, user); + keep_going = send_device(defdev, &guids[0], cb, user); + n = 1; } } @@ -490,7 +496,8 @@ HRESULT enumerate_mmdevices(EDataFlow flow, LPDSENUMCALLBACKW cb, void *user) } if(device != defdev){ - keep_going = send_device(device, cb, user); + keep_going = send_device(device, &guids[n], cb, user); + ++n; } IMMDevice_Release(device); @@ -533,7 +540,8 @@ HRESULT WINAPI DirectSoundEnumerateW( setup_dsound_options(); - hr = enumerate_mmdevices(eRender, lpDSEnumCallback, lpContext); + hr = enumerate_mmdevices(eRender, DSOUND_renderer_guids, + lpDSEnumCallback, lpContext); return SUCCEEDED(hr) ? DS_OK : hr; } @@ -596,7 +604,8 @@ DirectSoundCaptureEnumerateW( setup_dsound_options(); - hr = enumerate_mmdevices(eCapture, lpDSEnumCallback, lpContext); + hr = enumerate_mmdevices(eCapture, DSOUND_capture_guids, + lpDSEnumCallback, lpContext); return SUCCEEDED(hr) ? DS_OK : hr; } diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h index 081a56b2ee7..09fa219ecca 100644 --- a/dlls/dsound/dsound_private.h +++ b/dlls/dsound/dsound_private.h @@ -254,6 +254,8 @@ HRESULT IDirectSoundCaptureImpl_Create(IUnknown *outer_unk, REFIID riid, void ** extern CRITICAL_SECTION DSOUND_renderers_lock DECLSPEC_HIDDEN; extern struct list DSOUND_renderers DECLSPEC_HIDDEN; +extern GUID DSOUND_renderer_guids[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; +extern GUID DSOUND_capture_guids[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; extern const WCHAR wine_vxd_drv[] DECLSPEC_HIDDEN; @@ -263,7 +265,8 @@ HRESULT get_mmdevice(EDataFlow flow, const GUID *tgt, IMMDevice **device) DECLSP BOOL DSOUND_check_supported(IAudioClient *client, DWORD rate, DWORD depth, WORD channels) DECLSPEC_HIDDEN; -HRESULT enumerate_mmdevices(EDataFlow flow, LPDSENUMCALLBACKW cb, void *user) DECLSPEC_HIDDEN; +HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids, + LPDSENUMCALLBACKW cb, void *user) DECLSPEC_HIDDEN; static inline WCHAR *strdupW( const WCHAR *str ) { diff --git a/dlls/dsound/propset.c b/dlls/dsound/propset.c index e72757bdb4b..f42cfbf4163 100644 --- a/dlls/dsound/propset.c +++ b/dlls/dsound/propset.c @@ -136,9 +136,11 @@ static HRESULT DSPROPERTY_WaveDeviceMappingW( search.found_guid = &ppd->DeviceId; if (ppd->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_RENDER) - hr = enumerate_mmdevices(eRender, search_callback, &search); + hr = enumerate_mmdevices(eRender, DSOUND_renderer_guids, + search_callback, &search); else if (ppd->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE) - hr = enumerate_mmdevices(eCapture, search_callback, &search); + hr = enumerate_mmdevices(eCapture, DSOUND_capture_guids, + search_callback, &search); else return DSERR_INVALIDPARAM; @@ -316,10 +318,12 @@ static HRESULT DSPROPERTY_EnumerateW( return E_PROP_ID_UNSUPPORTED; } - hr = enumerate_mmdevices(eRender, enum_callback, ppd); + hr = enumerate_mmdevices(eRender, DSOUND_renderer_guids, + enum_callback, ppd); if(hr == S_OK) - hr = enumerate_mmdevices(eCapture, enum_callback, ppd); + hr = enumerate_mmdevices(eCapture, DSOUND_capture_guids, + enum_callback, ppd); return SUCCEEDED(hr) ? DS_OK : hr; } From 8cb26d47157232e2ac4cc637d5a12b458055099a Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Thu, 19 Oct 2023 22:39:07 -0600 Subject: [PATCH 1119/1148] dsound: Dynamically allocate the global device GUID arrays. This removes the arbitrary limit on the number of renderers and capturers while satisfying applications that expect the GUIDs to remain valid after DirectSoundCaptureEnumerate returns. (cherry picked from commit 5a81b6ac43e1057c0c6f9953fd5cbb1bc5d0f11bi) CW-Bug-Id: #23002 --- dlls/dsound/dsound_main.c | 15 +++++++++++++-- dlls/dsound/dsound_private.h | 4 ++-- include/mmsystem.h | 6 ------ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c index 3347f0243ab..0f566139a65 100644 --- a/dlls/dsound/dsound_main.c +++ b/dlls/dsound/dsound_main.c @@ -76,8 +76,11 @@ static CRITICAL_SECTION_DEBUG DSOUND_renderers_lock_debug = }; CRITICAL_SECTION DSOUND_renderers_lock = { &DSOUND_renderers_lock_debug, -1, 0, 0, 0, 0 }; -GUID DSOUND_renderer_guids[MAXWAVEDRIVERS]; -GUID DSOUND_capture_guids[MAXWAVEDRIVERS]; +/* Some applications expect the GUID pointers emitted from DirectSoundCaptureEnumerate to remain + * valid at least until the next time DirectSoundCaptureEnumerate is called, so we store them in + * these dynamically allocated arrays. */ +GUID *DSOUND_renderer_guids; +GUID *DSOUND_capture_guids; const WCHAR wine_vxd_drv[] = L"winemm.vxd"; @@ -464,11 +467,19 @@ HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids, return DS_OK; } + free(guids); if(count == 0){ IMMDeviceCollection_Release(coll); release_mmdevenum(devenum, init_hr); + guids = NULL; return DS_OK; } + guids = malloc((count + 1) * sizeof(GUID)); + if(!guids){ + IMMDeviceCollection_Release(coll); + release_mmdevenum(devenum, init_hr); + return E_OUTOFMEMORY; + } TRACE("Calling back with NULL (Primary Sound Driver)\n"); keep_going = cb(NULL, L"Primary Sound Driver", L"", user); diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h index 09fa219ecca..6f8051f6f25 100644 --- a/dlls/dsound/dsound_private.h +++ b/dlls/dsound/dsound_private.h @@ -254,8 +254,8 @@ HRESULT IDirectSoundCaptureImpl_Create(IUnknown *outer_unk, REFIID riid, void ** extern CRITICAL_SECTION DSOUND_renderers_lock DECLSPEC_HIDDEN; extern struct list DSOUND_renderers DECLSPEC_HIDDEN; -extern GUID DSOUND_renderer_guids[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; -extern GUID DSOUND_capture_guids[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; +extern GUID *DSOUND_renderer_guids; +extern GUID *DSOUND_capture_guids; extern const WCHAR wine_vxd_drv[] DECLSPEC_HIDDEN; diff --git a/include/mmsystem.h b/include/mmsystem.h index 5684ff20683..04864cf5f63 100644 --- a/include/mmsystem.h +++ b/include/mmsystem.h @@ -54,12 +54,6 @@ typedef HWAVEOUT *LPHWAVEOUT; typedef LRESULT (CALLBACK *DRIVERPROC)(DWORD_PTR,HDRVR,UINT,LPARAM,LPARAM); -#define MAXWAVEDRIVERS 10 -#define MAXMIDIDRIVERS 10 -#define MAXAUXDRIVERS 10 -#define MAXMCIDRIVERS 32 -#define MAXMIXERDRIVERS 10 - #define MAXPNAMELEN 32 /* max product name length (including NULL) */ #define MAXERRORLENGTH 256 /* max error text length (including NULL) */ #define MAX_JOYSTICKOEMVXDNAME 260 From 9a04c16d326da7b7396936d7415e1ac07e1c83c9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 16 Nov 2023 21:37:15 -0600 Subject: [PATCH 1120/1148] gdiplus: Round width and height in gdip_format_string(). CW-Bug-Id: #23002 --- dlls/gdiplus/graphics.c | 4 ++-- dlls/gdiplus/tests/graphics.c | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index fca66c8654b..ea6a6ba3874 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -5195,8 +5195,8 @@ GpStatus gdip_format_string(HDC hdc, if (!format) format = &default_drawstring_format; - nwidth = rect->Width; - nheight = rect->Height; + nwidth = (int)(rect->Width + 0.005f); + nheight = (int)(rect->Height + 0.005f); if (ignore_empty_clip) { if (!nwidth) nwidth = INT_MAX; diff --git a/dlls/gdiplus/tests/graphics.c b/dlls/gdiplus/tests/graphics.c index 0ee2b73c413..76d18433f41 100644 --- a/dlls/gdiplus/tests/graphics.c +++ b/dlls/gdiplus/tests/graphics.c @@ -4672,7 +4672,7 @@ static void test_measure_string(void) set_rect_empty(&rect); rect.Height = height; - rect.Width = width_2 - 0.05; + rect.Width = width_2 - 0.006; set_rect_empty(&bounds); status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, &glyphs, &lines); expect(Ok, status); @@ -4683,6 +4683,19 @@ static void test_measure_string(void) expectf_(width_1, bounds.Width, 0.01); expectf(height, bounds.Height); + set_rect_empty(&rect); + rect.Height = height; + rect.Width = width_2 - 0.004; + set_rect_empty(&bounds); + status = GdipMeasureString(graphics, string, -1, font, &rect, format, &bounds, &glyphs, &lines); + expect(Ok, status); + expect(2, glyphs); + expect(1, lines); + expectf(0.0, bounds.X); + expectf(0.0, bounds.Y); + expectf_(width_2, bounds.Width, 0.01); + expectf(height, bounds.Height); + /* Default (Near) alignment */ rect.X = 5.0; rect.Y = 5.0; From 790e189738602cb056423e6e1bb6be4b9dbd64c5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 17 Nov 2023 09:06:37 -0600 Subject: [PATCH 1121/1148] fixup! wine.inf: Enable builtin amd_ags_x64 for Assassin's Creed Mirage. CW-Bug-Id: #22962 --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 90a1ec682c9..4709eb28154 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2855,6 +2855,7 @@ 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\ACMirage.exe\DllOverrides,"amd_ags_x64",0x2,"builtin" +HKCU,Software\Wine\AppDefaults\ACMirage_plus.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 f000f115c00596fd5a170283acbbe27145c9a82b Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sat, 18 Feb 2023 01:15:01 +0000 Subject: [PATCH 1122/1148] bcryptprimitives: ProcessPrng stub. ProcessPrng is the only publicly documented function exported by bcryptprimitives. This stub simply forwards it to RtlGenRandom in advapi32. (cherry picked from commit 83d4075202ea595f6beb4a39ced54dbabc6a2e21) CW-Bug-Id: #23012 --- configure.ac | 1 + dlls/bcryptprimitives/Makefile.in | 5 ++++ dlls/bcryptprimitives/bcryptprimitives.spec | 1 + dlls/bcryptprimitives/main.c | 27 +++++++++++++++++++++ 4 files changed, 34 insertions(+) create mode 100644 dlls/bcryptprimitives/Makefile.in create mode 100644 dlls/bcryptprimitives/bcryptprimitives.spec create mode 100644 dlls/bcryptprimitives/main.c diff --git a/configure.ac b/configure.ac index 360d4ec5e25..0cf68772deb 100644 --- a/configure.ac +++ b/configure.ac @@ -2442,6 +2442,7 @@ WINE_CONFIG_MAKEFILE(dlls/avifile.dll16,enable_win16) WINE_CONFIG_MAKEFILE(dlls/avrt) WINE_CONFIG_MAKEFILE(dlls/bcrypt) WINE_CONFIG_MAKEFILE(dlls/bcrypt/tests) +WINE_CONFIG_MAKEFILE(dlls/bcryptprimitives) WINE_CONFIG_MAKEFILE(dlls/bluetoothapis) WINE_CONFIG_MAKEFILE(dlls/browseui) WINE_CONFIG_MAKEFILE(dlls/browseui/tests) diff --git a/dlls/bcryptprimitives/Makefile.in b/dlls/bcryptprimitives/Makefile.in new file mode 100644 index 00000000000..537383ba530 --- /dev/null +++ b/dlls/bcryptprimitives/Makefile.in @@ -0,0 +1,5 @@ +MODULE = bcryptprimitives.dll +IMPORTS = advapi32 + +C_SRCS = \ + main.c diff --git a/dlls/bcryptprimitives/bcryptprimitives.spec b/dlls/bcryptprimitives/bcryptprimitives.spec new file mode 100644 index 00000000000..928cb06afcd --- /dev/null +++ b/dlls/bcryptprimitives/bcryptprimitives.spec @@ -0,0 +1 @@ +@ stdcall ProcessPrng(ptr long) diff --git a/dlls/bcryptprimitives/main.c b/dlls/bcryptprimitives/main.c new file mode 100644 index 00000000000..6562d672389 --- /dev/null +++ b/dlls/bcryptprimitives/main.c @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Christopher S. Denton + * + * 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 "ntsecapi.h" + +BOOL WINAPI ProcessPrng(BYTE *data, SIZE_T size) +{ + return RtlGenRandom(data, size); +} From bcc1b140acd55bbc3e97db8de5892a517c4443c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 17 Nov 2023 21:28:10 +0100 Subject: [PATCH 1123/1148] evr: Set last presented sample atomically. Fixes a race condition and crashes in Secret of Mana. The queue critical section is held in video_presenter_sample_present while GetCurrentImage only locks the presenter CS. Double locking is tricky and atomic operation is appropriate to swap the sample pointer. CW-Bug-Id: #21713 --- dlls/evr/presenter.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/dlls/evr/presenter.c b/dlls/evr/presenter.c index 2dc01608caf..eed6a84c83d 100644 --- a/dlls/evr/presenter.c +++ b/dlls/evr/presenter.c @@ -530,12 +530,9 @@ static void video_presenter_sample_present(struct video_presenter *presenter, IM WARN("Failed to get a backbuffer, hr %#lx.\n", hr); } - EnterCriticalSection(&queue->cs); - if (queue->last_presented) - IMFSample_Release(queue->last_presented); - queue->last_presented = sample; - IMFSample_AddRef(queue->last_presented); - LeaveCriticalSection(&queue->cs); + IMFSample_AddRef(sample); + if ((sample = InterlockedExchangePointer((void **)&queue->last_presented, sample))) + IMFSample_Release(sample); IDirect3DSurface9_Release(surface); } @@ -752,6 +749,9 @@ static HRESULT video_presenter_start_streaming(struct video_presenter *presenter static HRESULT video_presenter_end_streaming(struct video_presenter *presenter) { + struct sample_queue *queue = &presenter->thread.queue; + IMFSample *sample; + if (!presenter->thread.hthread) return S_OK; @@ -762,8 +762,9 @@ static HRESULT video_presenter_end_streaming(struct video_presenter *presenter) TRACE("Terminated streaming thread tid %#lx.\n", presenter->thread.tid); - if (presenter->thread.queue.last_presented) - IMFSample_Release(presenter->thread.queue.last_presented); + if ((sample = InterlockedExchangePointer((void **)&queue->last_presented, NULL))) + IMFSample_Release(sample); + video_presenter_sample_queue_free(presenter); memset(&presenter->thread, 0, sizeof(presenter->thread)); video_presenter_set_allocator_callback(presenter, NULL); @@ -1488,6 +1489,7 @@ static HRESULT WINAPI video_presenter_control_GetCurrentImage(IMFVideoDisplayCon BYTE **dib, DWORD *dib_size, LONGLONG *timestamp) { struct video_presenter *presenter = impl_from_IMFVideoDisplayControl(iface); + struct sample_queue *queue = &presenter->thread.queue; IDirect3DSurface9 *readback = NULL, *surface; D3DSURFACE_DESC surface_desc; D3DLOCKED_RECT mapped_rect; @@ -1500,10 +1502,7 @@ static HRESULT WINAPI video_presenter_control_GetCurrentImage(IMFVideoDisplayCon EnterCriticalSection(&presenter->cs); - sample = presenter->thread.queue.last_presented; - presenter->thread.queue.last_presented = NULL; - - if (!sample) + if (!(sample = InterlockedExchangePointer((void **)&queue->last_presented, NULL))) { hr = MF_E_INVALIDREQUEST; } From cfdebd96a531cb2131cc03fd455a5c764a2928b2 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 21 Nov 2023 11:25:17 +0200 Subject: [PATCH 1124/1148] fixup! HACK: winegstreamer: Do not report live latency for some games. --- dlls/winegstreamer/h264_decoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 6a686b93381..54d5c4f6685 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -119,7 +119,7 @@ static HRESULT try_create_wg_transform(struct h264_decoder *decoder) { const char *sgi; - if ((sgi = getenv("SteamGameId")) && (!strcmp(sgi, "2009100"))) + if ((sgi = getenv("SteamGameId")) && ((!strcmp(sgi, "2009100")) || (!strcmp(sgi, "2555360")))) attrs.low_latency = FALSE; } From ed69f61a8d4b595f82acf7aed4173c587b01655e Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 21 Nov 2023 11:25:54 +0200 Subject: [PATCH 1125/1148] fixup! HACK: winegstreamer: Don't add unnecessary and slow? videoflip for some games. --- dlls/winegstreamer/wg_transform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index a6df4a4d0e0..b89eb54ff28 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -403,7 +403,7 @@ NTSTATUS wg_transform_create(void *args) case WG_MAJOR_TYPE_VIDEO: { const char *sgi; - if ((sgi = getenv("SteamGameId")) && (!strcmp(sgi, "2009100"))) + if ((sgi = getenv("SteamGameId")) && ((!strcmp(sgi, "2009100")) || (!strcmp(sgi, "2555360")))) { if (!(element = create_element("videoconvert", "base")) || !append_element(transform->container, element, &first, &last)) From b242e938b8d84d47a066e84a0815a92104533fac Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 21 Nov 2023 11:26:10 +0200 Subject: [PATCH 1126/1148] fixup! HACK: winegstreamer: Disable MF_SA_D3D11_AWARE for some games. --- dlls/winegstreamer/h264_decoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 54d5c4f6685..c5733c69acb 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -896,7 +896,7 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) { const char *sgi; - if ((sgi = getenv("SteamGameId")) && (!strcmp(sgi, "2009100"))) + if ((sgi = getenv("SteamGameId")) && ((!strcmp(sgi, "2009100")) || (!strcmp(sgi, "2555360")))) IMFAttributes_SetUINT32(decoder->attributes, &MF_SA_D3D11_AWARE, FALSE); } From 4b6ba2abe0034a92651b1293c31fc22b3e8456bb Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 17 Nov 2023 16:15:32 -0600 Subject: [PATCH 1127/1148] windowscodecs: Enable WICPixelFormat32bppBGRA in BMP encoder. (cherry picked from commit f27dd7740cdc1290debe1f4806bb7907c54b7847) CW-Bug-Id: #23002 --- dlls/windowscodecs/bmpencode.c | 3 -- dlls/windowscodecs/tests/bmpformat.c | 69 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/dlls/windowscodecs/bmpencode.c b/dlls/windowscodecs/bmpencode.c index cd981ee9d3b..80737eff899 100644 --- a/dlls/windowscodecs/bmpencode.c +++ b/dlls/windowscodecs/bmpencode.c @@ -54,10 +54,7 @@ static const struct bmp_pixelformat formats[] = { {&GUID_WICPixelFormat16bppBGR555, 16, 0, BI_RGB}, {&GUID_WICPixelFormat16bppBGR565, 16, 0, BI_BITFIELDS, 0xf800, 0x7e0, 0x1f, 0}, {&GUID_WICPixelFormat32bppBGR, 32, 0, BI_RGB}, -#if 0 - /* Windows doesn't seem to support this one. */ {&GUID_WICPixelFormat32bppBGRA, 32, 0, BI_BITFIELDS, 0xff0000, 0xff00, 0xff, 0xff000000}, -#endif {NULL} }; diff --git a/dlls/windowscodecs/tests/bmpformat.c b/dlls/windowscodecs/tests/bmpformat.c index e21fa1ee035..39a576d6c69 100644 --- a/dlls/windowscodecs/tests/bmpformat.c +++ b/dlls/windowscodecs/tests/bmpformat.c @@ -1263,6 +1263,74 @@ static void test_writesource_palette(void) IWICImagingFactory_Release(factory); } +static void test_encoder_formats(void) +{ + static const struct + { + const GUID *format; + BOOL supported; + const char *name; + } + tests[] = + { + {&GUID_WICPixelFormat24bppBGR, TRUE, "WICPixelFormat24bppBGR"}, + {&GUID_WICPixelFormatBlackWhite, FALSE, "WICPixelFormatBlackWhite"}, + {&GUID_WICPixelFormat1bppIndexed, TRUE, "WICPixelFormat1bppIndexed"}, + {&GUID_WICPixelFormat2bppIndexed, FALSE, "WICPixelFormat2bppIndexed"}, + {&GUID_WICPixelFormat4bppIndexed, TRUE, "WICPixelFormat4bppIndexed"}, + {&GUID_WICPixelFormat8bppIndexed, TRUE, "WICPixelFormat8bppIndexed"}, + {&GUID_WICPixelFormat16bppBGR555, TRUE, "WICPixelFormat16bppBGR555"}, + {&GUID_WICPixelFormat16bppBGR565, TRUE, "WICPixelFormat16bppBGR565"}, + {&GUID_WICPixelFormat32bppBGR, TRUE, "WICPixelFormat32bppBGR"}, + {&GUID_WICPixelFormat32bppBGRA, TRUE, "WICPixelFormat32bppBGRA"}, + {&GUID_WICPixelFormat8bppGray, FALSE, "WICPixelFormat8bppGray"}, + }; + + IWICImagingFactory *factory; + HRESULT hr; + IStream *stream; + IWICBitmapEncoder *encoder; + IWICBitmapFrameEncode *frame_encode; + GUID pixelformat; + LONG refcount; + unsigned int i; + BOOL supported; + + hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, + &IID_IWICImagingFactory, (void **)&factory); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = IWICImagingFactory_CreateEncoder(factory, &GUID_ContainerFormatBmp, &GUID_VendorMicrosoft, &encoder); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frame_encode, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IWICBitmapFrameEncode_Initialize(frame_encode, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + winetest_push_context(tests[i].name); + pixelformat = *tests[i].format; + hr = IWICBitmapFrameEncode_SetPixelFormat(frame_encode, &pixelformat); + ok(hr == S_OK, "got %#lx.\n", hr); + supported = !memcmp(&pixelformat, tests[i].format, sizeof(pixelformat)); + ok(supported == tests[i].supported, "got %d.\n", supported); + winetest_pop_context(); + } + + IWICBitmapFrameEncode_Release(frame_encode); + refcount = IWICBitmapEncoder_Release(encoder); + ok(!refcount, "got %ld.\n", refcount); + IStream_Release(stream); + IWICImagingFactory_Release(factory); +} + START_TEST(bmpformat) { CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); @@ -1276,6 +1344,7 @@ START_TEST(bmpformat) test_createfromstream(); test_create_decoder(); test_writesource_palette(); + test_encoder_formats(); CoUninitialize(); } From d8f4c3a734d5bd79f85dd9a1a14c30181758ba33 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 20 Nov 2023 12:27:02 -0600 Subject: [PATCH 1128/1148] msvcp110: Implement std::_Xbad_function_call(). (cherry picked from commit a2d3b27b6c90101729299314da9cae534a3c11ba) CW-Bug-Id: #23002 --- dlls/msvcp110/msvcp110.spec | 2 +- dlls/msvcp120/msvcp120.spec | 2 +- dlls/msvcp120_app/msvcp120_app.spec | 2 +- dlls/msvcp140/msvcp140.spec | 2 +- dlls/msvcp90/exception.c | 50 +++++++++++++++++++++++++++++ dlls/msvcp_win/msvcp_win.spec | 2 +- 6 files changed, 55 insertions(+), 5 deletions(-) diff --git a/dlls/msvcp110/msvcp110.spec b/dlls/msvcp110/msvcp110.spec index 2de95bf94ef..3a9192b3706 100644 --- a/dlls/msvcp110/msvcp110.spec +++ b/dlls/msvcp110/msvcp110.spec @@ -1833,7 +1833,7 @@ @ cdecl ?_XLgamma@std@@YANN@Z(double) std__XLgamma_double @ cdecl ?_XLgamma@std@@YAOO@Z(double) std__XLgamma_double @ cdecl ?_Xbad_alloc@std@@YAXXZ() _Xmem -@ stub ?_Xbad_function_call@std@@YAXXZ +@ cdecl ?_Xbad_function_call@std@@YAXXZ() _Xbad_function_call @ cdecl -arch=win32 ?_Xinvalid_argument@std@@YAXPBD@Z(str) _Xinvalid_argument @ cdecl -arch=win64 ?_Xinvalid_argument@std@@YAXPEBD@Z(str) _Xinvalid_argument @ cdecl -arch=win32 ?_Xlength_error@std@@YAXPBD@Z(str) _Xlength_error diff --git a/dlls/msvcp120/msvcp120.spec b/dlls/msvcp120/msvcp120.spec index e0adc83113f..c720710349d 100644 --- a/dlls/msvcp120/msvcp120.spec +++ b/dlls/msvcp120/msvcp120.spec @@ -1794,7 +1794,7 @@ @ cdecl ?_XLgamma@std@@YANN@Z(double) std__XLgamma_double @ cdecl ?_XLgamma@std@@YAOO@Z(double) std__XLgamma_double @ cdecl ?_Xbad_alloc@std@@YAXXZ() _Xmem -@ stub ?_Xbad_function_call@std@@YAXXZ +@ cdecl ?_Xbad_function_call@std@@YAXXZ() _Xbad_function_call @ cdecl -arch=win32 ?_Xinvalid_argument@std@@YAXPBD@Z(str) _Xinvalid_argument @ cdecl -arch=win64 ?_Xinvalid_argument@std@@YAXPEBD@Z(str) _Xinvalid_argument @ cdecl -arch=win32 ?_Xlength_error@std@@YAXPBD@Z(str) _Xlength_error diff --git a/dlls/msvcp120_app/msvcp120_app.spec b/dlls/msvcp120_app/msvcp120_app.spec index dd8e3ebf173..59b989ba2d9 100644 --- a/dlls/msvcp120_app/msvcp120_app.spec +++ b/dlls/msvcp120_app/msvcp120_app.spec @@ -1794,7 +1794,7 @@ @ cdecl ?_XLgamma@std@@YANN@Z(double) msvcp120.?_XLgamma@std@@YANN@Z @ cdecl ?_XLgamma@std@@YAOO@Z(double) msvcp120.?_XLgamma@std@@YAOO@Z @ cdecl ?_Xbad_alloc@std@@YAXXZ() msvcp120.?_Xbad_alloc@std@@YAXXZ -@ stub ?_Xbad_function_call@std@@YAXXZ +@ cdecl ?_Xbad_function_call@std@@YAXXZ() msvcp120.?_Xbad_function_call@std@@YAXXZ @ cdecl -arch=win32 ?_Xinvalid_argument@std@@YAXPBD@Z(str) msvcp120.?_Xinvalid_argument@std@@YAXPBD@Z @ cdecl -arch=win64 ?_Xinvalid_argument@std@@YAXPEBD@Z(str) msvcp120.?_Xinvalid_argument@std@@YAXPEBD@Z @ cdecl -arch=win32 ?_Xlength_error@std@@YAXPBD@Z(str) msvcp120.?_Xlength_error@std@@YAXPBD@Z diff --git a/dlls/msvcp140/msvcp140.spec b/dlls/msvcp140/msvcp140.spec index dd619c637e1..8b4e44c494d 100644 --- a/dlls/msvcp140/msvcp140.spec +++ b/dlls/msvcp140/msvcp140.spec @@ -1677,7 +1677,7 @@ @ cdecl ?_XLgamma@std@@YANN@Z(double) std__XLgamma_double @ cdecl ?_XLgamma@std@@YAOO@Z(double) std__XLgamma_double @ cdecl ?_Xbad_alloc@std@@YAXXZ() _Xmem -@ stub ?_Xbad_function_call@std@@YAXXZ +@ cdecl ?_Xbad_function_call@std@@YAXXZ() _Xbad_function_call @ cdecl -arch=win32 ?_Xinvalid_argument@std@@YAXPBD@Z(str) _Xinvalid_argument @ cdecl -arch=win64 ?_Xinvalid_argument@std@@YAXPEBD@Z(str) _Xinvalid_argument @ cdecl -arch=win32 ?_Xlength_error@std@@YAXPBD@Z(str) _Xlength_error diff --git a/dlls/msvcp90/exception.c b/dlls/msvcp90/exception.c index 8ceaa91e884..1149cc8ee5d 100644 --- a/dlls/msvcp90/exception.c +++ b/dlls/msvcp90/exception.c @@ -67,6 +67,8 @@ extern const vtable_ptr failure_vtable; extern const vtable_ptr bad_cast_vtable; /* ??_7range_error@std@@6B@ */ extern const vtable_ptr range_error_vtable; +/* ??_7bad_function_call@std@@6B@ */ +extern const vtable_ptr bad_function_call_vtable; /* ??0exception@@QAE@ABQBD@Z */ /* ??0exception@@QEAA@AEBQEBD@Z */ @@ -867,6 +869,33 @@ range_error* __thiscall MSVCP_range_error_assign(range_error *this, const range_ DEFINE_RTTI_DATA2(range_error, 0, &runtime_error_rtti_base_descriptor, &exception_rtti_base_descriptor, ".?AVrange_error@std@@") DEFINE_CXX_DATA2(range_error, &runtime_error_cxx_type_info, &exception_cxx_type_info, MSVCP_runtime_error_dtor) +#if _MSVCP_VER > 90 +/* bad_function_call class data */ +typedef exception bad_function_call; + +static bad_function_call* MSVCP_bad_function_call_ctor(bad_function_call *this) +{ + static const char *name = "bad function call"; + + TRACE("%p\n", this); + MSVCP_exception_ctor(this, EXCEPTION_NAME(name)); + this->vtable = &bad_function_call_vtable; + return this; +} + +DEFINE_THISCALL_WRAPPER(bad_function_call_copy_ctor, 8) +bad_function_call* __thiscall bad_function_call_copy_ctor(bad_function_call *this, const bad_function_call *rhs) +{ + TRACE("%p %p\n", this, rhs); + exception_copy_ctor(this, rhs); + this->vtable = &bad_function_call_vtable; + return this; +} + +DEFINE_RTTI_DATA1(bad_function_call, 0, &exception_rtti_base_descriptor, ".?AVbad_function_call@std@@") +DEFINE_CXX_DATA1(bad_function_call, &exception_cxx_type_info, MSVCP_exception_dtor) +#endif + /* ?_Nomemory@std@@YAXXZ */ void __cdecl DECLSPEC_NORETURN _Nomemory(void) { @@ -941,6 +970,19 @@ void __cdecl DECLSPEC_NORETURN _Xruntime_error(const char *str) _CxxThrowException(&e, &runtime_error_cxx_type); } +#if _MSVCP_VER > 90 +/* ?_Xbad_function_call@std@@YAXXZ() */ +void __cdecl _Xbad_function_call(void) +{ + exception e; + + TRACE("()\n"); + + MSVCP_bad_function_call_ctor(&e); + _CxxThrowException(&e, &bad_function_call_cxx_type); +} +#endif + /* ?uncaught_exception@std@@YA_NXZ */ bool __cdecl MSVCP__uncaught_exception(void) { @@ -1356,6 +1398,9 @@ __ASM_BLOCK_BEGIN(exception_vtables) EXCEPTION_VTABLE(system_error, VTABLE_ADD_FUNC(MSVCP_failure_vector_dtor) VTABLE_ADD_FUNC(MSVCP_failure_what)); + EXCEPTION_VTABLE(bad_function_call, + VTABLE_ADD_FUNC(MSVCP_exception_vector_dtor) + VTABLE_ADD_FUNC(MSVCP_exception_what)); #endif EXCEPTION_VTABLE(failure, VTABLE_ADD_FUNC(MSVCP_failure_vector_dtor) @@ -1366,6 +1411,7 @@ __ASM_BLOCK_BEGIN(exception_vtables) EXCEPTION_VTABLE(range_error, VTABLE_ADD_FUNC(MSVCP_runtime_error_vector_dtor) VTABLE_ADD_FUNC(MSVCP_runtime_error_what)); + __ASM_BLOCK_END /* Internal: throws exception */ @@ -1414,6 +1460,7 @@ void init_exception(void *base) #endif #if _MSVCP_VER > 90 init_system_error_rtti(base); + init_bad_function_call_rtti(base); #endif init_failure_rtti(base); init_bad_cast_rtti(base); @@ -1431,6 +1478,9 @@ void init_exception(void *base) #endif #if _MSVCP_VER > 90 init_system_error_cxx_type_info(base); +#endif +#if _MSVCP_VER > 90 + init_bad_function_call_cxx(base); #endif init_failure_cxx(base); init_range_error_cxx(base); diff --git a/dlls/msvcp_win/msvcp_win.spec b/dlls/msvcp_win/msvcp_win.spec index d2fae9f1e14..975da98631b 100644 --- a/dlls/msvcp_win/msvcp_win.spec +++ b/dlls/msvcp_win/msvcp_win.spec @@ -1677,7 +1677,7 @@ @ cdecl ?_XLgamma@std@@YANN@Z(double) msvcp140.?_XLgamma@std@@YANN@Z @ cdecl ?_XLgamma@std@@YAOO@Z(double) msvcp140.?_XLgamma@std@@YAOO@Z @ cdecl ?_Xbad_alloc@std@@YAXXZ() msvcp140.?_Xbad_alloc@std@@YAXXZ -@ stub ?_Xbad_function_call@std@@YAXXZ +@ cdecl ?_Xbad_function_call@std@@YAXXZ() msvcp140.?_Xbad_function_call@std@@YAXXZ @ cdecl -arch=win32 ?_Xinvalid_argument@std@@YAXPBD@Z(str) msvcp140.?_Xinvalid_argument@std@@YAXPBD@Z @ cdecl -arch=win64 ?_Xinvalid_argument@std@@YAXPEBD@Z(str) msvcp140.?_Xinvalid_argument@std@@YAXPEBD@Z @ cdecl -arch=win32 ?_Xlength_error@std@@YAXPBD@Z(str) msvcp140.?_Xlength_error@std@@YAXPBD@Z From 30c64cfaaaa4640d95089c228a2a85b8157cc298 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Thu, 28 Sep 2023 12:35:29 +1000 Subject: [PATCH 1129/1148] d3dx9: Support empty mesh in D3DXLoadMeshHierarchyFromXInMemory(). (cherry picked from commit 21e91690b1688289a3b06951cd2152274998c5ba) CW-Bug-Id: #23002 --- dlls/d3dx9_36/mesh.c | 51 ++++++++++++++++++++++++++++++-------- dlls/d3dx9_36/tests/mesh.c | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c index 47a0cadde67..d380ca61415 100644 --- a/dlls/d3dx9_36/mesh.c +++ b/dlls/d3dx9_36/mesh.c @@ -3506,6 +3506,19 @@ HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD opt hr = parse_mesh(filedata, &mesh_data, provide_flags); if (FAILED(hr)) goto cleanup; + if (!mesh_data.num_vertices) + { + if (adjacency_out) + *adjacency_out = NULL; + if (materials_out) + *materials_out = NULL; + if (effects_out) + *effects_out = NULL; + *mesh_out = NULL; + hr = D3D_OK; + goto cleanup; + } + total_vertices = mesh_data.num_vertices; if (mesh_data.fvf & D3DFVF_NORMAL) { /* duplicate vertices with multiple normals */ @@ -3815,12 +3828,15 @@ static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options hr = filedata_get_name(filedata, &name); if (FAILED(hr)) goto cleanup; - hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data, - materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL, - effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL, - num_materials, - adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL, - skin_info, mesh_container); + if (mesh_data.pMesh) + { + hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data, + materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL, + effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL, + num_materials, + adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL, + skin_info, mesh_container); + } cleanup: if (materials) ID3DXBuffer_Release(materials); @@ -4200,13 +4216,21 @@ static HRESULT parse_frame(struct ID3DXFileData *filedata, DWORD options, struct hr = E_OUTOFMEMORY; goto err; } - list_add_tail(container_list, &container->entry); - container->transform = transform; hr = D3DXLoadSkinMeshFromXof(child, options, device, (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL, (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL, NULL, &container->num_materials, NULL, &container->mesh); + + if (container->mesh) + { + list_add_tail(container_list, &container->entry); + container->transform = transform; + } + else + { + HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, container); + } } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) { D3DXMATRIX new_transform; hr = parse_transform_matrix(child, &new_transform); @@ -4294,13 +4318,20 @@ HRESULT WINAPI D3DXLoadMeshFromXInMemory(const void *memory, DWORD memory_size, hr = E_OUTOFMEMORY; goto cleanup; } - list_add_tail(&container_list, &container_ptr->entry); - D3DXMatrixIdentity(&container_ptr->transform); hr = D3DXLoadSkinMeshFromXof(filedata, options, device, (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL, (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL, NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh); + if (container_ptr->mesh) + { + list_add_tail(&container_list, &container_ptr->entry); + D3DXMatrixIdentity(&container_ptr->transform); + } + else + { + HeapFree(GetProcessHeap(), 0, container_ptr); + } } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) { hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags); } diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c index c988c368382..97586e1e419 100644 --- a/dlls/d3dx9_36/tests/mesh.c +++ b/dlls/d3dx9_36/tests/mesh.c @@ -2068,6 +2068,14 @@ static void D3DXLoadMeshTest(void) "}" "Mesh { 3; 0.0; 0.0; 0.0;, 0.0; 1.0; 0.0;, 3.0; 1.0; 0.0;; 1; 3; 0, 1, 2;; }" "}"; + static const char framed_xfile_empty[] = + "xof 0303txt 0032" + "Frame Box01 {" + " Mesh { 0;; 0;;" + " MeshNormals { 0;; 0;; }" + " }" + "}"; + static const WORD framed_index_buffer[] = { 0, 1, 2 }; static const D3DXVECTOR3 framed_vertex_buffers[3][3] = { {{0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 1.0, 0.0}}, @@ -2513,6 +2521,16 @@ static void D3DXLoadMeshTest(void) frame_hier = NULL; } + hr = D3DXLoadMeshHierarchyFromXInMemory(framed_xfile_empty, sizeof(framed_xfile_empty) - 1, + D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + container = frame_hier->pMeshContainer; + ok(!strcmp(frame_hier->Name, "Box01"), "Unexpected name %s.\n", debugstr_a(frame_hier->Name)); + ok(!container, "Unexpected container %p.\n", container); + + hr = D3DXFrameDestroy(frame_hier, &alloc_hier); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + frame_hier = NULL; hr = D3DXLoadMeshFromXInMemory(NULL, 0, D3DXMESH_MANAGED, device, NULL, NULL, NULL, NULL, &mesh); @@ -11323,6 +11341,9 @@ static void test_load_skin_mesh_from_xof(void) "1;" "3; 0, 1, 2;;" "}"; + static const char simple_xfile_empty[] = + "xof 0303txt 0032" + "Mesh { 0;; 0;; }"; static const D3DVERTEXELEMENT9 expected_declaration[] = { {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, @@ -11434,6 +11455,28 @@ static void test_load_skin_mesh_from_xof(void) mesh->lpVtbl->Release(mesh); adjacency->lpVtbl->Release(adjacency); file_data->lpVtbl->Release(file_data); + + /* Empty Mesh Test */ + file_data = get_mesh_data(simple_xfile_empty, sizeof(simple_xfile_empty) - 1); + ok(!!file_data, "Failed to load mesh data.\n"); + + adjacency = materials = effects = (void *)0xdeadbeef; + count = 0xdeadbeefu; + skin_info = (void *)0xdeadbeef; + mesh = (void *)0xdeadbeef; + + hr = D3DXLoadSkinMeshFromXof(file_data, 0, device, &adjacency, &materials, &effects, &count, + &skin_info, &mesh); + todo_wine ok(hr == D3DXERR_LOADEDMESHASNODATA, "Unexpected hr %#lx.\n", hr); + ok(!adjacency, "Unexpected adjacency %p.\n", adjacency); + ok(!materials, "Unexpected materials %p.\n", materials); + ok(!effects, "Unexpected effects %p.\n", effects); + ok(count == 0xdeadbeefu, "Unexpected count %lu.\n", count); + ok(skin_info == (void *)0xdeadbeef, "Unexpected skin_info %p.\n", skin_info); + ok(!mesh, "Unexpected mesh %p.\n", mesh); + + file_data->lpVtbl->Release(file_data); + refcount = IDirect3DDevice9_Release(device); ok(!refcount, "Device has %lu references left.\n", refcount); DestroyWindow(hwnd); From 8e7dfbb43f6a962db7977e74d5d342bbe285f85a Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Fri, 17 Nov 2023 17:27:21 -0600 Subject: [PATCH 1130/1148] d3dx9: Implement D3DXComputeTangent CW-Bug-Id: #23002 --- dlls/d3dx9_24/d3dx9_24.spec | 2 +- dlls/d3dx9_25/d3dx9_25.spec | 2 +- dlls/d3dx9_26/d3dx9_26.spec | 2 +- dlls/d3dx9_27/d3dx9_27.spec | 2 +- dlls/d3dx9_28/d3dx9_28.spec | 2 +- dlls/d3dx9_29/d3dx9_29.spec | 2 +- dlls/d3dx9_30/d3dx9_30.spec | 2 +- dlls/d3dx9_31/d3dx9_31.spec | 2 +- dlls/d3dx9_32/d3dx9_32.spec | 2 +- dlls/d3dx9_33/d3dx9_33.spec | 2 +- dlls/d3dx9_34/d3dx9_34.spec | 2 +- dlls/d3dx9_35/d3dx9_35.spec | 2 +- dlls/d3dx9_36/d3dx9_36.spec | 2 +- dlls/d3dx9_36/mesh.c | 18 ++++++++++++++++++ dlls/d3dx9_37/d3dx9_37.spec | 2 +- dlls/d3dx9_38/d3dx9_38.spec | 2 +- dlls/d3dx9_39/d3dx9_39.spec | 2 +- dlls/d3dx9_40/d3dx9_40.spec | 2 +- dlls/d3dx9_41/d3dx9_41.spec | 2 +- dlls/d3dx9_42/d3dx9_42.spec | 2 +- dlls/d3dx9_43/d3dx9_43.spec | 2 +- 21 files changed, 38 insertions(+), 20 deletions(-) diff --git a/dlls/d3dx9_24/d3dx9_24.spec b/dlls/d3dx9_24/d3dx9_24.spec index 8791e4ec044..b3cb0bf2382 100644 --- a/dlls/d3dx9_24/d3dx9_24.spec +++ b/dlls/d3dx9_24/d3dx9_24.spec @@ -20,7 +20,7 @@ @ stdcall D3DXComputeBoundingSphere(ptr long long ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_25/d3dx9_25.spec b/dlls/d3dx9_25/d3dx9_25.spec index 0431a9f7f75..41e0235c00d 100644 --- a/dlls/d3dx9_25/d3dx9_25.spec +++ b/dlls/d3dx9_25/d3dx9_25.spec @@ -20,7 +20,7 @@ @ stdcall D3DXComputeBoundingSphere(ptr long long ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_26/d3dx9_26.spec b/dlls/d3dx9_26/d3dx9_26.spec index 5ab0a1d9fea..095a0cb4695 100644 --- a/dlls/d3dx9_26/d3dx9_26.spec +++ b/dlls/d3dx9_26/d3dx9_26.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_27/d3dx9_27.spec b/dlls/d3dx9_27/d3dx9_27.spec index 5ab0a1d9fea..095a0cb4695 100644 --- a/dlls/d3dx9_27/d3dx9_27.spec +++ b/dlls/d3dx9_27/d3dx9_27.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_28/d3dx9_28.spec b/dlls/d3dx9_28/d3dx9_28.spec index af5b6077202..bbd47d69fcf 100644 --- a/dlls/d3dx9_28/d3dx9_28.spec +++ b/dlls/d3dx9_28/d3dx9_28.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_29/d3dx9_29.spec b/dlls/d3dx9_29/d3dx9_29.spec index af5b6077202..bbd47d69fcf 100644 --- a/dlls/d3dx9_29/d3dx9_29.spec +++ b/dlls/d3dx9_29/d3dx9_29.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_30/d3dx9_30.spec b/dlls/d3dx9_30/d3dx9_30.spec index af5b6077202..bbd47d69fcf 100644 --- a/dlls/d3dx9_30/d3dx9_30.spec +++ b/dlls/d3dx9_30/d3dx9_30.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_31/d3dx9_31.spec b/dlls/d3dx9_31/d3dx9_31.spec index 8f77dc666a2..1250de7843d 100644 --- a/dlls/d3dx9_31/d3dx9_31.spec +++ b/dlls/d3dx9_31/d3dx9_31.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_32/d3dx9_32.spec b/dlls/d3dx9_32/d3dx9_32.spec index 2fdd2a00615..0691d18995b 100644 --- a/dlls/d3dx9_32/d3dx9_32.spec +++ b/dlls/d3dx9_32/d3dx9_32.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_33/d3dx9_33.spec b/dlls/d3dx9_33/d3dx9_33.spec index 2fdd2a00615..0691d18995b 100644 --- a/dlls/d3dx9_33/d3dx9_33.spec +++ b/dlls/d3dx9_33/d3dx9_33.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_34/d3dx9_34.spec b/dlls/d3dx9_34/d3dx9_34.spec index 2fdd2a00615..0691d18995b 100644 --- a/dlls/d3dx9_34/d3dx9_34.spec +++ b/dlls/d3dx9_34/d3dx9_34.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_35/d3dx9_35.spec b/dlls/d3dx9_35/d3dx9_35.spec index 2fdd2a00615..0691d18995b 100644 --- a/dlls/d3dx9_35/d3dx9_35.spec +++ b/dlls/d3dx9_35/d3dx9_35.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_36/d3dx9_36.spec b/dlls/d3dx9_36/d3dx9_36.spec index 5b7070145de..e2ea5b6cc0c 100644 --- a/dlls/d3dx9_36/d3dx9_36.spec +++ b/dlls/d3dx9_36/d3dx9_36.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c index d380ca61415..d024f54ba05 100644 --- a/dlls/d3dx9_36/mesh.c +++ b/dlls/d3dx9_36/mesh.c @@ -7579,6 +7579,24 @@ HRESULT WINAPI D3DXComputeTangentFrameEx(ID3DXMesh *mesh, DWORD texture_in_seman return hr; } +/************************************************************************* + * D3DXComputeTangent (D3DX9_36.@) + */ +HRESULT WINAPI D3DXComputeTangent(ID3DXMesh *mesh, DWORD stage_idx, DWORD tangent_idx, + DWORD binorm_idx, DWORD wrap, const DWORD *adjacency) +{ + TRACE("mesh %p, stage_idx %ld, tangent_idx %ld, binorm_idx %ld, wrap %ld, adjacency %p.\n", + mesh, stage_idx, tangent_idx, binorm_idx, wrap, adjacency); + + return D3DXComputeTangentFrameEx( mesh, D3DDECLUSAGE_TEXCOORD, stage_idx, + ( binorm_idx == D3DX_DEFAULT ) ? D3DX_DEFAULT : D3DDECLUSAGE_BINORMAL, + binorm_idx, + ( tangent_idx == D3DX_DEFAULT ) ? D3DX_DEFAULT : D3DDECLUSAGE_TANGENT, + tangent_idx, D3DX_DEFAULT, 0, + ( wrap ? D3DXTANGENT_WRAP_UV : 0 ) | D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_ORTHOGONALIZE_FROM_U, + adjacency, -1.01f, -0.01f, -1.01f, NULL, NULL); +} + /************************************************************************* * D3DXComputeNormals (D3DX9_36.@) */ diff --git a/dlls/d3dx9_37/d3dx9_37.spec b/dlls/d3dx9_37/d3dx9_37.spec index 5b7070145de..e2ea5b6cc0c 100644 --- a/dlls/d3dx9_37/d3dx9_37.spec +++ b/dlls/d3dx9_37/d3dx9_37.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_38/d3dx9_38.spec b/dlls/d3dx9_38/d3dx9_38.spec index 5b7070145de..e2ea5b6cc0c 100644 --- a/dlls/d3dx9_38/d3dx9_38.spec +++ b/dlls/d3dx9_38/d3dx9_38.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_39/d3dx9_39.spec b/dlls/d3dx9_39/d3dx9_39.spec index 5b7070145de..e2ea5b6cc0c 100644 --- a/dlls/d3dx9_39/d3dx9_39.spec +++ b/dlls/d3dx9_39/d3dx9_39.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_40/d3dx9_40.spec b/dlls/d3dx9_40/d3dx9_40.spec index 5b7070145de..e2ea5b6cc0c 100644 --- a/dlls/d3dx9_40/d3dx9_40.spec +++ b/dlls/d3dx9_40/d3dx9_40.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_41/d3dx9_41.spec b/dlls/d3dx9_41/d3dx9_41.spec index 5b7070145de..e2ea5b6cc0c 100644 --- a/dlls/d3dx9_41/d3dx9_41.spec +++ b/dlls/d3dx9_41/d3dx9_41.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_42/d3dx9_42.spec b/dlls/d3dx9_42/d3dx9_42.spec index 4a418d1508a..1792886d9ac 100644 --- a/dlls/d3dx9_42/d3dx9_42.spec +++ b/dlls/d3dx9_42/d3dx9_42.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) diff --git a/dlls/d3dx9_43/d3dx9_43.spec b/dlls/d3dx9_43/d3dx9_43.spec index 4a418d1508a..1792886d9ac 100644 --- a/dlls/d3dx9_43/d3dx9_43.spec +++ b/dlls/d3dx9_43/d3dx9_43.spec @@ -24,7 +24,7 @@ @ stub D3DXComputeIMTFromTexture(ptr ptr long long ptr ptr ptr) @ stdcall D3DXComputeNormalMap(ptr ptr ptr long long float) @ stdcall D3DXComputeNormals(ptr ptr) -@ stub D3DXComputeTangent(ptr long long long long ptr) +@ stdcall D3DXComputeTangent(ptr long long long long ptr) @ stub D3DXComputeTangentFrame(ptr long) @ stdcall D3DXComputeTangentFrameEx(ptr long long long long long long long long long ptr float float float ptr ptr) @ stub D3DXConcatenateMeshes(ptr long long ptr ptr ptr ptr ptr) From 74a03fce7f3cadae096996f645337b3895ae3776 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 21 Nov 2023 10:01:08 -0600 Subject: [PATCH 1131/1148] include: Fix ID3DXLoadUserData definition. CW-Bug-Id: #23002 --- include/d3dx9anim.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/d3dx9anim.h b/include/d3dx9anim.h index 81b8e2f6b2c..741ed5a3a56 100644 --- a/include/d3dx9anim.h +++ b/include/d3dx9anim.h @@ -183,9 +183,9 @@ DECLARE_INTERFACE(ID3DXAllocateHierarchy) #define INTERFACE ID3DXLoadUserData DECLARE_INTERFACE(ID3DXLoadUserData) { - STDMETHOD(LoadTopLevelData)(ID3DXFileData *child_data) PURE; - STDMETHOD(LoadFrameChildData)(D3DXFRAME *frame, ID3DXFileData *child_data) PURE; - STDMETHOD(LoadMeshChildData)(D3DXMESHCONTAINER *mesh_container, ID3DXFileData *child_data) PURE; + STDMETHOD(LoadTopLevelData)(ID3DXLoadUserData *user_data, ID3DXFileData *child_data) PURE; + STDMETHOD(LoadFrameChildData)(ID3DXLoadUserData *user_data, D3DXFRAME *frame, ID3DXFileData *child_data) PURE; + STDMETHOD(LoadMeshChildData)(ID3DXLoadUserData *user_data, D3DXMESHCONTAINER *mesh_container, ID3DXFileData *child_data) PURE; }; #undef INTERFACE From 528e12d4fc11b10a6f74dd98582a4802222f9220 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 21 Nov 2023 09:02:32 -0600 Subject: [PATCH 1132/1148] d3dx9/tests: Add test for user data in D3DXLoadMeshHierarchyFromXInMemory(). CW-Bug-Id: #23002 --- dlls/d3dx9_36/tests/mesh.c | 202 ++++++++++++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 1 deletion(-) diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c index 97586e1e419..aad05217d20 100644 --- a/dlls/d3dx9_36/tests/mesh.c +++ b/dlls/d3dx9_36/tests/mesh.c @@ -2030,6 +2030,132 @@ static void test_LoadMeshFromX_(int line, IDirect3DDevice9 *device, const char * } } +#define MAX_USER_DATA_COUNT 32 +enum user_data_type +{ + USER_DATA_TYPE_TOP, + USER_DATA_TYPE_FRAME_CHILD, + USER_DATA_TYPE_MESH_CHILD, +}; + +struct test_user_data +{ + enum user_data_type data_type; + GUID type; + SIZE_T size; + unsigned int value; + BOOL mesh_container; + unsigned int num_materials; +}; + +struct test_load_user_data +{ + ID3DXLoadUserData iface; + + unsigned int data_count; + struct test_user_data data[MAX_USER_DATA_COUNT]; +}; + +static void record_common_user_data(struct test_load_user_data *data, ID3DXFileData *filedata, + enum user_data_type data_type) +{ + struct test_user_data *d = &data->data[data->data_count]; + const void *ptr; + HRESULT hr; + SIZE_T sz; + + ok(data->data_count < MAX_USER_DATA_COUNT, "got %u.\n", data->data_count); + + d->data_type = data_type; + hr = filedata->lpVtbl->GetType(filedata, &d->type); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = filedata->lpVtbl->Lock(filedata, &sz, &ptr); + ok(hr == S_OK, "got %#lx.\n", hr); + d->size = sz; + ok(sz >= sizeof(int), "got %Iu.\n", sz); + d->value = *(unsigned int *)ptr; + hr = filedata->lpVtbl->Unlock(filedata); + ok(hr == S_OK, "got %#lx.\n", hr); + ++data->data_count; +} + +static struct test_load_user_data *impl_from_ID3DXLoadUserData(ID3DXLoadUserData *iface) +{ + return CONTAINING_RECORD(iface, struct test_load_user_data, iface); +} + +static HRESULT STDMETHODCALLTYPE load_top_level_data(ID3DXLoadUserData *iface, ID3DXFileData *filedata) +{ + struct test_load_user_data *user_data = impl_from_ID3DXLoadUserData(iface); + + record_common_user_data(user_data, filedata, USER_DATA_TYPE_TOP); + return S_OK; +} +static HRESULT STDMETHODCALLTYPE load_frame_child_data(ID3DXLoadUserData *iface, D3DXFRAME *frame, + ID3DXFileData *filedata) +{ + struct test_load_user_data *user_data = impl_from_ID3DXLoadUserData(iface); + + ok(!frame->pFrameSibling, "got %p.\n", frame->pFrameSibling); + ok(!frame->pFrameFirstChild, "got %p.\n", frame->pFrameFirstChild); + + user_data->data[user_data->data_count].mesh_container = !!frame->pMeshContainer; + record_common_user_data(user_data, filedata, USER_DATA_TYPE_FRAME_CHILD); + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE load_mesh_child_data(ID3DXLoadUserData *iface, D3DXMESHCONTAINER *mesh_container, + ID3DXFileData *filedata) +{ + struct test_load_user_data *user_data = impl_from_ID3DXLoadUserData(iface); + + user_data->data[user_data->data_count].num_materials = mesh_container->NumMaterials; + + record_common_user_data(user_data, filedata, USER_DATA_TYPE_MESH_CHILD); + return S_OK; +} + +static const struct ID3DXLoadUserDataVtbl load_user_data_vtbl = +{ + load_top_level_data, + load_frame_child_data, + load_mesh_child_data, +}; + +static void init_load_user_data(struct test_load_user_data *data) +{ + memset(data, 0, sizeof(*data)); + data->iface.lpVtbl = &load_user_data_vtbl; +} + +static void check_user_data(struct test_load_user_data *user_data, unsigned int expected_count, + struct test_user_data *expected) +{ + unsigned int i; + + ok(user_data->data_count == expected_count, "got %u, expected %u.\n", user_data->data_count, expected_count); + expected_count = min(expected_count, user_data->data_count); + for (i = 0; i < expected_count; ++i) + { + winetest_push_context("i %u", i); + ok(user_data->data[i].data_type == expected[i].data_type, "got %u, expected %u.\n", + user_data->data[i].data_type, expected[i].data_type); + ok(IsEqualGUID(&user_data->data[i].type, &expected[i].type), "got %s, expected %s.\n", + debugstr_guid(&user_data->data[i].type), debugstr_guid(&expected[i].type)); + ok(user_data->data[i].size == expected[i].size, "got %Iu, expected %Iu.\n", + user_data->data[i].size, expected[i].size); + ok(user_data->data[i].value == expected[i].value, "got %u, expected %u.\n", + user_data->data[i].value, expected[i].value); + ok(user_data->data[i].mesh_container == expected[i].mesh_container, "got %u, expected %u.\n", + user_data->data[i].mesh_container, expected[i].mesh_container); + ok(user_data->data[i].num_materials == expected[i].num_materials, "got %u, expected %u.\n", + user_data->data[i].num_materials, expected[i].num_materials); + winetest_pop_context(); + } +} + +DEFINE_GUID(TID_TestDataGuid, 0x12345678, 0x1234, 0x1234, 0x12, 0x34, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11); + static void D3DXLoadMeshTest(void) { static const char empty_xfile[] = "xof 0303txt 0032"; @@ -2051,14 +2177,42 @@ static void D3DXLoadMeshTest(void) const DWORD simple_fvf = D3DFVF_XYZ; static const char framed_xfile[] = "xof 0303txt 0032" + "template TestData {" + "<12345678-1234-1234-1234-111111111111>" + "DWORD value;" + "}" + "TestData {" + "1;;" + "}" + "Material {" + /* ColorRGBA faceColor; */ + "0.0; 0.0; 1.0; 1.0;;" + /* FLOAT power; */ + "0.5;" + /* ColorRGB specularColor; */ + "1.0; 1.0; 1.0;;" + /* ColorRGB emissiveColor; */ + "0.0; 0.0; 0.0;;" + "}" + "Frame {" - "Mesh { 3; 0.0; 0.0; 0.0;, 0.0; 1.0; 0.0;, 1.0; 1.0; 0.0;; 1; 3; 0, 1, 2;; }" + "TestData {" + "2;;" + "}" + "Mesh { 3; 0.0; 0.0; 0.0;, 0.0; 1.0; 0.0;, 1.0; 1.0; 0.0;; 1; 3; 0, 1, 2;;" + "TestData {" + "3;;" + "}" + "}" "FrameTransformMatrix {" /* translation (0.0, 0.0, 2.0) */ "1.0, 0.0, 0.0, 0.0," "0.0, 1.0, 0.0, 0.0," "0.0, 0.0, 1.0, 0.0," "0.0, 0.0, 2.0, 1.0;;" "}" + "TestData {" + "4;;" + "}" "Mesh { 3; 0.0; 0.0; 0.0;, 0.0; 1.0; 0.0;, 2.0; 1.0; 0.0;; 1; 3; 0, 1, 2;; }" "FrameTransformMatrix {" /* translation (0.0, 0.0, 3.0) */ "1.0, 0.0, 0.0, 0.0," @@ -2068,6 +2222,16 @@ static void D3DXLoadMeshTest(void) "}" "Mesh { 3; 0.0; 0.0; 0.0;, 0.0; 1.0; 0.0;, 3.0; 1.0; 0.0;; 1; 3; 0, 1, 2;; }" "}"; + + struct test_user_data framed_xfile_expected_user_data[] = + { + { USER_DATA_TYPE_TOP, TID_TestDataGuid, 4, 1, 0, 0}, + { USER_DATA_TYPE_TOP, TID_D3DRMMaterial, 44, 0, 0, 0}, + { USER_DATA_TYPE_FRAME_CHILD, TID_TestDataGuid, 4, 2, 0, 0}, + { USER_DATA_TYPE_MESH_CHILD, TID_TestDataGuid, 4, 3, 0, 0}, + { USER_DATA_TYPE_FRAME_CHILD, TID_TestDataGuid, 4, 4, 1, 0}, + }; + static const char framed_xfile_empty[] = "xof 0303txt 0032" "Frame Box01 {" @@ -2093,6 +2257,10 @@ static void D3DXLoadMeshTest(void) /*________________________*/ static const char box_xfile[] = "xof 0303txt 0032" + "template TestData {" + "<12345678-1234-1234-1234-111111111111>" + "DWORD value;" + "}" "Mesh {" "8;" /* DWORD nVertices; */ /* array Vector vertices[nVertices]; */ @@ -2130,6 +2298,9 @@ static void D3DXLoadMeshTest(void) "4; 4, 4, 4, 4;," "4; 5, 5, 5, 5;;" "}" + "TestData {" + "1;;" + "}" "MeshMaterialList materials {" "2;" /* DWORD nMaterials; */ "6;" /* DWORD nFaceIndexes; */ @@ -2157,6 +2328,10 @@ static void D3DXLoadMeshTest(void) "TextureFilename { \"texture.jpg\"; }" "}" "}" + "TestData {" + "2;;" + "}" + "MeshVertexColors {" "8;" /* DWORD nVertexColors; */ /* array IndexedColor vertexColors[nVertexColors]; */ @@ -2182,6 +2357,12 @@ static void D3DXLoadMeshTest(void) "0.0; 0.0;;" "}" "}"; + struct test_user_data box_xfile_expected_user_data[] = + { + { USER_DATA_TYPE_MESH_CHILD, TID_TestDataGuid, 4, 1, 0, 2}, + { USER_DATA_TYPE_MESH_CHILD, TID_TestDataGuid, 4, 2, 0, 2}, + }; + static const WORD box_index_buffer[] = { 0, 1, 3, 0, 3, 2, @@ -2391,6 +2572,7 @@ static void D3DXLoadMeshTest(void) D3DXMATRIX transform; struct test_context *test_context; ID3DXAnimationController *controller; + struct test_load_user_data load_user_data; if (!(test_context = new_test_context())) { @@ -2490,6 +2672,20 @@ static void D3DXLoadMeshTest(void) frame_hier = NULL; } + init_load_user_data(&load_user_data); + hr = D3DXLoadMeshHierarchyFromXInMemory(box_xfile, sizeof(box_xfile) - 1, + D3DXMESH_MANAGED, device, &alloc_hier, &load_user_data.iface, &frame_hier, &controller); + todo_wine ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); + if (SUCCEEDED(hr)) + { + winetest_push_context("box_xfile"); + check_user_data(&load_user_data, ARRAY_SIZE(box_xfile_expected_user_data), box_xfile_expected_user_data); + winetest_pop_context(); + hr = D3DXFrameDestroy(frame_hier, &alloc_hier); + ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); + } + frame_hier = NULL; + hr = D3DXLoadMeshHierarchyFromXInMemory(framed_xfile, sizeof(framed_xfile) - 1, D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL); ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); @@ -2520,6 +2716,10 @@ static void D3DXLoadMeshTest(void) ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); frame_hier = NULL; } + ok(container == NULL, "Expected NULL, got %p\n", container); + hr = D3DXFrameDestroy(frame_hier, &alloc_hier); + ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); + frame_hier = NULL; hr = D3DXLoadMeshHierarchyFromXInMemory(framed_xfile_empty, sizeof(framed_xfile_empty) - 1, D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL); From 7b301f307668b2af37d5160b300209cf5154d5a5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 21 Nov 2023 14:42:15 -0600 Subject: [PATCH 1133/1148] d3dx9: Implement loading top and frame user data in D3DXLoadMeshHierarchyFromXInMemory(). CW-Bug-Id: #23002 --- dlls/d3dx9_36/mesh.c | 17 ++++++++++------- dlls/d3dx9_36/tests/mesh.c | 34 +++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c index d024f54ba05..05e4a6e17e9 100644 --- a/dlls/d3dx9_36/mesh.c +++ b/dlls/d3dx9_36/mesh.c @@ -3880,7 +3880,7 @@ static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *trans } static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device, - struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out) + struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out, struct ID3DXLoadUserData *load_user_data) { HRESULT hr; GUID type; @@ -3923,9 +3923,12 @@ static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) { hr = parse_transform_matrix(child, &frame->TransformationMatrix); } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) { - hr = load_frame(child, options, device, alloc_hier, next_child); + hr = load_frame(child, options, device, alloc_hier, next_child, load_user_data); if (SUCCEEDED(hr)) next_child = &(*next_child)->pFrameSibling; + } else if (load_user_data) { + TRACE("Loading %s as user data.\n", debugstr_guid(&type)); + hr = load_user_data->lpVtbl->LoadFrameChildData(load_user_data, frame, child); } if (FAILED(hr)) goto err; @@ -3961,10 +3964,7 @@ HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memo if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier) return D3DERR_INVALIDCALL; if (load_user_data) - { - FIXME("Loading user data not implemented.\n"); - return E_NOTIMPL; - } + FIXME("Loading mesh user data not implemented for mesh.\n"); hr = D3DXFileCreate(&d3dxfile); if (FAILED(hr)) goto cleanup; @@ -4001,8 +4001,11 @@ HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memo hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer); if (FAILED(hr)) goto cleanup; } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) { - hr = load_frame(filedata, options, device, alloc_hier, next_frame); + hr = load_frame(filedata, options, device, alloc_hier, next_frame, load_user_data); if (FAILED(hr)) goto cleanup; + } else if (load_user_data) { + TRACE("Loading %s as user data.\n", debugstr_guid(&guid)); + hr = load_user_data->lpVtbl->LoadTopLevelData(load_user_data, filedata); } while (*next_frame) next_frame = &(*next_frame)->pFrameSibling; diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c index aad05217d20..59cef8a6879 100644 --- a/dlls/d3dx9_36/tests/mesh.c +++ b/dlls/d3dx9_36/tests/mesh.c @@ -2133,20 +2133,20 @@ static void check_user_data(struct test_load_user_data *user_data, unsigned int { unsigned int i; - ok(user_data->data_count == expected_count, "got %u, expected %u.\n", user_data->data_count, expected_count); + todo_wine ok(user_data->data_count == expected_count, "got %u, expected %u.\n", user_data->data_count, expected_count); expected_count = min(expected_count, user_data->data_count); for (i = 0; i < expected_count; ++i) { winetest_push_context("i %u", i); - ok(user_data->data[i].data_type == expected[i].data_type, "got %u, expected %u.\n", + todo_wine_if(i == 3) ok(user_data->data[i].data_type == expected[i].data_type, "got %u, expected %u.\n", user_data->data[i].data_type, expected[i].data_type); ok(IsEqualGUID(&user_data->data[i].type, &expected[i].type), "got %s, expected %s.\n", debugstr_guid(&user_data->data[i].type), debugstr_guid(&expected[i].type)); ok(user_data->data[i].size == expected[i].size, "got %Iu, expected %Iu.\n", user_data->data[i].size, expected[i].size); - ok(user_data->data[i].value == expected[i].value, "got %u, expected %u.\n", + todo_wine_if(i == 3) ok(user_data->data[i].value == expected[i].value, "got %u, expected %u.\n", user_data->data[i].value, expected[i].value); - ok(user_data->data[i].mesh_container == expected[i].mesh_container, "got %u, expected %u.\n", + todo_wine_if(i == 3) ok(user_data->data[i].mesh_container == expected[i].mesh_container, "got %u, expected %u.\n", user_data->data[i].mesh_container, expected[i].mesh_container); ok(user_data->data[i].num_materials == expected[i].num_materials, "got %u, expected %u.\n", user_data->data[i].num_materials, expected[i].num_materials); @@ -2675,15 +2675,12 @@ static void D3DXLoadMeshTest(void) init_load_user_data(&load_user_data); hr = D3DXLoadMeshHierarchyFromXInMemory(box_xfile, sizeof(box_xfile) - 1, D3DXMESH_MANAGED, device, &alloc_hier, &load_user_data.iface, &frame_hier, &controller); - todo_wine ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); - if (SUCCEEDED(hr)) - { - winetest_push_context("box_xfile"); - check_user_data(&load_user_data, ARRAY_SIZE(box_xfile_expected_user_data), box_xfile_expected_user_data); - winetest_pop_context(); - hr = D3DXFrameDestroy(frame_hier, &alloc_hier); - ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); - } + ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); + winetest_push_context("box_xfile"); + check_user_data(&load_user_data, ARRAY_SIZE(box_xfile_expected_user_data), box_xfile_expected_user_data); + winetest_pop_context(); + hr = D3DXFrameDestroy(frame_hier, &alloc_hier); + ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); frame_hier = NULL; hr = D3DXLoadMeshHierarchyFromXInMemory(framed_xfile, sizeof(framed_xfile) - 1, @@ -2721,6 +2718,17 @@ static void D3DXLoadMeshTest(void) ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); frame_hier = NULL; + init_load_user_data(&load_user_data); + hr = D3DXLoadMeshHierarchyFromXInMemory(framed_xfile, sizeof(framed_xfile) - 1, + D3DXMESH_MANAGED, device, &alloc_hier, &load_user_data.iface, &frame_hier, NULL); + ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); + hr = D3DXFrameDestroy(frame_hier, &alloc_hier); + ok(hr == D3D_OK, "Expected D3D_OK, got %#lx\n", hr); + winetest_push_context("framed_xfile"); + check_user_data(&load_user_data, ARRAY_SIZE(framed_xfile_expected_user_data), framed_xfile_expected_user_data); + winetest_pop_context(); + frame_hier = NULL; + hr = D3DXLoadMeshHierarchyFromXInMemory(framed_xfile_empty, sizeof(framed_xfile_empty) - 1, D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL); ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); From e0f3c313eb337202498eaff4d157d74b873e284b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 21 Nov 2023 15:00:45 -0600 Subject: [PATCH 1134/1148] d3dx9: Unify calling parse_mesh helper functions. CW-Bug-Id: #23002 --- dlls/d3dx9_36/mesh.c | 92 ++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c index 05e4a6e17e9..ff08b1df5f1 100644 --- a/dlls/d3dx9_36/mesh.c +++ b/dlls/d3dx9_36/mesh.c @@ -37,6 +37,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3dx); +/* for provide_flags parameters */ +#define PROVIDE_MATERIALS 0x1 +#define PROVIDE_SKININFO 0x2 +#define PROVIDE_ADJACENCY 0x4 + struct d3dx9_mesh { ID3DXMesh ID3DXMesh_iface; @@ -2611,6 +2616,7 @@ struct mesh_data { struct ID3DXSkinInfo *skin_info; unsigned int bone_count; + unsigned int skin_weights_info_count; }; static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out) @@ -2752,7 +2758,7 @@ static void destroy_materials(struct mesh_data *mesh) mesh->material_indices = NULL; } -static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh) +static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh, DWORD flags) { ID3DXFileData *child = NULL; unsigned int material_count; @@ -2764,6 +2770,9 @@ static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *me HRESULT hr; GUID type; + if (!(flags & PROVIDE_MATERIALS)) + return S_OK; + destroy_materials(mesh); hr = filedata->lpVtbl->Lock(filedata, &data_size, &data); @@ -2867,7 +2876,7 @@ static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *me return hr; } -static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh) +static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh, DWORD flags) { const uint32_t *data; SIZE_T data_size; @@ -2925,7 +2934,7 @@ static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *m return hr; } -static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh) +static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh, DWORD flags) { unsigned int color_count, i; const uint32_t *data; @@ -3004,7 +3013,7 @@ static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *me return hr; } -static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh) +static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh, DWORD flags) { unsigned int num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces; DWORD *index_out_ptr; @@ -3110,7 +3119,7 @@ static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh) return hr; } -static HRESULT parse_skin_mesh_header(ID3DXFileData *filedata, struct mesh_data *mesh_data) +static HRESULT parse_skin_mesh_header(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD flags) { const BYTE *data; SIZE_T data_size; @@ -3118,6 +3127,15 @@ static HRESULT parse_skin_mesh_header(ID3DXFileData *filedata, struct mesh_data TRACE("filedata %p, mesh_data %p.\n", filedata, mesh_data); + if (!(flags & PROVIDE_SKININFO)) + return S_OK; + + if (mesh_data->skin_info) + { + WARN("Skin mesh header already encountered\n"); + return E_FAIL; + } + if (FAILED(hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data))) return hr; @@ -3136,8 +3154,9 @@ static HRESULT parse_skin_mesh_header(ID3DXFileData *filedata, struct mesh_data return hr; } -static HRESULT parse_skin_weights_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, unsigned int index) +static HRESULT parse_skin_weights_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD flags) { + unsigned int index = mesh_data->skin_weights_info_count; unsigned int influence_count; const char *name; const BYTE *data; @@ -3146,6 +3165,15 @@ static HRESULT parse_skin_weights_info(ID3DXFileData *filedata, struct mesh_data TRACE("filedata %p, mesh_data %p, index %u.\n", filedata, mesh_data, index); + if (!(flags & PROVIDE_SKININFO)) + return S_OK; + + if (!mesh_data->skin_info) + { + WARN("Skin weights found but skin mesh header not encountered yet.\n"); + return E_FAIL; + } + if (FAILED(hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data))) return hr; @@ -3173,17 +3201,13 @@ static HRESULT parse_skin_weights_info(ID3DXFileData *filedata, struct mesh_data hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index, (const D3DMATRIX *)(data + influence_count * (sizeof(uint32_t) + sizeof(float)))); + if (SUCCEEDED(hr)) + ++mesh_data->skin_weights_info_count; return hr; } -/* for provide_flags parameters */ -#define PROVIDE_MATERIALS 0x1 -#define PROVIDE_SKININFO 0x2 -#define PROVIDE_ADJACENCY 0x4 - static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags) { - unsigned int skin_weights_info_count = 0; ID3DXFileData *child = NULL; const BYTE *data, *in_ptr; DWORD *index_out_ptr; @@ -3203,6 +3227,8 @@ static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, * } */ + mesh_data->skin_weights_info_count = 0; + hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data); if (FAILED(hr)) return hr; @@ -3308,37 +3334,19 @@ static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, goto end; if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) { - hr = parse_normals(child, mesh_data); + hr = parse_normals(child, mesh_data, provide_flags); } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) { - hr = parse_vertex_colors(child, mesh_data); + hr = parse_vertex_colors(child, mesh_data, provide_flags); } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) { - hr = parse_texture_coords(child, mesh_data); - } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList) && - (provide_flags & PROVIDE_MATERIALS)) - { - hr = parse_material_list(child, mesh_data); - } else if (provide_flags & PROVIDE_SKININFO) { - if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) { - if (mesh_data->skin_info) { - WARN("Skin mesh header already encountered\n"); - hr = E_FAIL; - goto end; - } - hr = parse_skin_mesh_header(child, mesh_data); - if (FAILED(hr)) - goto end; - } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) { - if (!mesh_data->skin_info) { - WARN("Skin weights found but skin mesh header not encountered yet\n"); - hr = E_FAIL; - goto end; - } - hr = parse_skin_weights_info(child, mesh_data, skin_weights_info_count); - if (FAILED(hr)) - goto end; - skin_weights_info_count++; - } + hr = parse_texture_coords(child, mesh_data, provide_flags); + } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList)) { + hr = parse_material_list(child, mesh_data, provide_flags); + } else if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) { + hr = parse_skin_mesh_header(child, mesh_data, provide_flags); + } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) { + hr = parse_skin_weights_info(child, mesh_data, provide_flags); } + if (FAILED(hr)) goto end; @@ -3346,10 +3354,10 @@ static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, child = NULL; } - if (mesh_data->skin_info && (skin_weights_info_count != mesh_data->bone_count)) + if (mesh_data->skin_info && (mesh_data->skin_weights_info_count != mesh_data->bone_count)) { WARN("Mismatch between skin weights info count %u and bones count %u from skin mesh header.\n", - skin_weights_info_count, mesh_data->bone_count); + mesh_data->skin_weights_info_count, mesh_data->bone_count); hr = E_FAIL; goto end; } From b66053a57f4e8e4216b3b813ce3a6991fafd68e4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 21 Nov 2023 15:09:34 -0600 Subject: [PATCH 1135/1148] d3dx9: Factor out mesh_get_parse_func(). CW-Bug-Id: #23002 --- dlls/d3dx9_36/mesh.c | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c index ff08b1df5f1..459c60082c1 100644 --- a/dlls/d3dx9_36/mesh.c +++ b/dlls/d3dx9_36/mesh.c @@ -3206,9 +3206,37 @@ static HRESULT parse_skin_weights_info(ID3DXFileData *filedata, struct mesh_data return hr; } +typedef HRESULT (*mesh_parse_func)(ID3DXFileData *, struct mesh_data *, DWORD); + +static mesh_parse_func mesh_get_parse_func(const GUID *type) +{ + static const struct + { + const GUID *type; + mesh_parse_func func; + } + funcs[] = + { + {&TID_D3DRMMeshNormals, parse_normals}, + {&TID_D3DRMMeshVertexColors, parse_vertex_colors}, + {&TID_D3DRMMeshTextureCoords, parse_texture_coords}, + {&TID_D3DRMMeshMaterialList, parse_material_list}, + {&DXFILEOBJ_XSkinMeshHeader, parse_skin_mesh_header}, + {&DXFILEOBJ_SkinWeights, parse_skin_weights_info}, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(funcs); ++i) + if (IsEqualGUID(type, funcs[i].type)) + return funcs[i].func; + + return NULL; +} + static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags) { ID3DXFileData *child = NULL; + mesh_parse_func parse_func; const BYTE *data, *in_ptr; DWORD *index_out_ptr; SIZE_T child_count; @@ -3333,20 +3361,8 @@ static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, if (FAILED(hr)) goto end; - if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) { - hr = parse_normals(child, mesh_data, provide_flags); - } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) { - hr = parse_vertex_colors(child, mesh_data, provide_flags); - } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) { - hr = parse_texture_coords(child, mesh_data, provide_flags); - } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList)) { - hr = parse_material_list(child, mesh_data, provide_flags); - } else if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) { - hr = parse_skin_mesh_header(child, mesh_data, provide_flags); - } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) { - hr = parse_skin_weights_info(child, mesh_data, provide_flags); - } - + if ((parse_func = mesh_get_parse_func(&type))) + hr = parse_func(child, mesh_data, provide_flags); if (FAILED(hr)) goto end; From 314da8d8924e9c2ffa030f1054f35721dd7dba87 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 21 Nov 2023 12:35:35 -0600 Subject: [PATCH 1136/1148] d3dx9: Support loading mesh user data in D3DXLoadMeshHierarchyFromXInMemory(). CW-Bug-Id: #23002 --- dlls/d3dx9_36/mesh.c | 49 ++++++++++++++++++++++++++++---------- dlls/d3dx9_36/tests/mesh.c | 8 +++---- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c index 459c60082c1..789ab76532c 100644 --- a/dlls/d3dx9_36/mesh.c +++ b/dlls/d3dx9_36/mesh.c @@ -3830,7 +3830,8 @@ static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name) } static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device, - struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container) + struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container, + struct ID3DXLoadUserData *load_user_data) { HRESULT hr; ID3DXBuffer *adjacency = NULL; @@ -3840,6 +3841,10 @@ static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options D3DXMESHDATA mesh_data; DWORD num_materials = 0; char *name = NULL; + SIZE_T child_count; + ID3DXFileData *child = NULL; + GUID type; + unsigned int i; mesh_data.Type = D3DXMESHTYPE_MESH; mesh_data.pMesh = NULL; @@ -3852,17 +3857,38 @@ static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options hr = filedata_get_name(filedata, &name); if (FAILED(hr)) goto cleanup; - if (mesh_data.pMesh) + if (!mesh_data.pMesh) + goto cleanup; + hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data, + materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL, + effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL, + num_materials, + adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL, + skin_info, mesh_container); + if (FAILED(hr) || !load_user_data) + goto cleanup; + + hr = filedata->lpVtbl->GetChildren(filedata, &child_count); + if (FAILED(hr)) + goto cleanup; + + for (i = 0; i < child_count; i++) { - hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data, - materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL, - effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL, - num_materials, - adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL, - skin_info, mesh_container); + if (FAILED(hr = filedata->lpVtbl->GetChild(filedata, i, &child))) + goto cleanup; + if (FAILED(hr = child->lpVtbl->GetType(child, &type))) + goto cleanup; + + if (!mesh_get_parse_func(&type) + && FAILED(hr = load_user_data->lpVtbl->LoadMeshChildData(load_user_data, *mesh_container, child))) + goto cleanup; + + IUnknown_Release(child); + child = NULL; } cleanup: + if (child) IUnknown_Release(child); if (materials) ID3DXBuffer_Release(materials); if (effects) ID3DXBuffer_Release(effects); if (adjacency) ID3DXBuffer_Release(adjacency); @@ -3941,7 +3967,7 @@ static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct goto err; if (IsEqualGUID(&type, &TID_D3DRMMesh)) { - hr = load_mesh_container(child, options, device, alloc_hier, next_container); + hr = load_mesh_container(child, options, device, alloc_hier, next_container, load_user_data); if (SUCCEEDED(hr)) next_container = &(*next_container)->pNextMeshContainer; } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) { @@ -3987,8 +4013,6 @@ HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memo if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier) return D3DERR_INVALIDCALL; - if (load_user_data) - FIXME("Loading mesh user data not implemented for mesh.\n"); hr = D3DXFileCreate(&d3dxfile); if (FAILED(hr)) goto cleanup; @@ -4022,7 +4046,8 @@ HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memo D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix); - hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer); + hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer, + load_user_data); if (FAILED(hr)) goto cleanup; } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) { hr = load_frame(filedata, options, device, alloc_hier, next_frame, load_user_data); diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c index 59cef8a6879..d567417145a 100644 --- a/dlls/d3dx9_36/tests/mesh.c +++ b/dlls/d3dx9_36/tests/mesh.c @@ -2133,20 +2133,20 @@ static void check_user_data(struct test_load_user_data *user_data, unsigned int { unsigned int i; - todo_wine ok(user_data->data_count == expected_count, "got %u, expected %u.\n", user_data->data_count, expected_count); + ok(user_data->data_count == expected_count, "got %u, expected %u.\n", user_data->data_count, expected_count); expected_count = min(expected_count, user_data->data_count); for (i = 0; i < expected_count; ++i) { winetest_push_context("i %u", i); - todo_wine_if(i == 3) ok(user_data->data[i].data_type == expected[i].data_type, "got %u, expected %u.\n", + ok(user_data->data[i].data_type == expected[i].data_type, "got %u, expected %u.\n", user_data->data[i].data_type, expected[i].data_type); ok(IsEqualGUID(&user_data->data[i].type, &expected[i].type), "got %s, expected %s.\n", debugstr_guid(&user_data->data[i].type), debugstr_guid(&expected[i].type)); ok(user_data->data[i].size == expected[i].size, "got %Iu, expected %Iu.\n", user_data->data[i].size, expected[i].size); - todo_wine_if(i == 3) ok(user_data->data[i].value == expected[i].value, "got %u, expected %u.\n", + ok(user_data->data[i].value == expected[i].value, "got %u, expected %u.\n", user_data->data[i].value, expected[i].value); - todo_wine_if(i == 3) ok(user_data->data[i].mesh_container == expected[i].mesh_container, "got %u, expected %u.\n", + ok(user_data->data[i].mesh_container == expected[i].mesh_container, "got %u, expected %u.\n", user_data->data[i].mesh_container, expected[i].mesh_container); ok(user_data->data[i].num_materials == expected[i].num_materials, "got %u, expected %u.\n", user_data->data[i].num_materials, expected[i].num_materials); From bd282d2df354a0625a3ab01e39bf69d8414611ae Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 21 Nov 2023 18:06:59 -0600 Subject: [PATCH 1137/1148] ntdll: HACK: Enable fsync_yield_to_waiters for LIGHTNING RETURNS: FFXIII. CW-Bug-Id: #23021 --- dlls/ntdll/unix/loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index b9b6f66c52f..5500bd75e50 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2351,7 +2351,7 @@ static void hacks_init(void) env_str = getenv("WINE_FSYNC_YIELD_TO_WAITERS"); if (env_str) fsync_yield_to_waiters = !!atoi(env_str); - else if (sgi) fsync_yield_to_waiters = !strcmp(sgi, "292120"); + else if (sgi) fsync_yield_to_waiters = !strcmp(sgi, "292120") || !strcmp(sgi, "345350"); if (fsync_yield_to_waiters) ERR("HACK: fsync: yield to waiters.\n"); From 154ef96f01291644992bbe5058eff3e3d122361d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 23 Nov 2023 14:30:36 -0600 Subject: [PATCH 1138/1148] amd_ags_x64: Add Unix library. CW-Bug-Id: #22976 --- configure.ac | 9 +++++ dlls/amd_ags_x64/Makefile.in | 6 +++- dlls/amd_ags_x64/amd_ags_x64_main.c | 24 +++++++++++++ dlls/amd_ags_x64/unixlib.c | 54 +++++++++++++++++++++++++++++ dlls/amd_ags_x64/unixlib.h | 26 ++++++++++++++ 5 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 dlls/amd_ags_x64/unixlib.c create mode 100644 dlls/amd_ags_x64/unixlib.h diff --git a/configure.ac b/configure.ac index 0cf68772deb..f2dfbb2f2b4 100644 --- a/configure.ac +++ b/configure.ac @@ -1434,6 +1434,15 @@ then [WINE_CHECK_SONAME(gmp,__gmpz_init,,[GMP_CFLAGS=""],[$GMP_LIBS],[[libgmp-*]])])]) fi +dnl **** Check for libdrm **** +WINE_PACKAGE_FLAGS(DRM,[libdrm],,,, + [AC_CHECK_HEADERS([xf86drm.h], + [WINE_CHECK_SONAME(drm,drmOpen,,,[$DRM_LIBS])])]) + +WINE_PACKAGE_FLAGS(DRMAMDGPU,[libdrm_amdgpu],,,, + [AC_CHECK_HEADERS([amdgpu_drm.h], + [WINE_CHECK_SONAME(drm_amdgpu,amdgpu_query_info,,,[$DRMAMDGPU_LIBS])])]) + dnl **** Check for SANE **** if test "x$with_sane" != "xno" then diff --git a/dlls/amd_ags_x64/Makefile.in b/dlls/amd_ags_x64/Makefile.in index 5efcc5e0ffc..e167498caae 100644 --- a/dlls/amd_ags_x64/Makefile.in +++ b/dlls/amd_ags_x64/Makefile.in @@ -1,12 +1,16 @@ EXTRADEFS = -DWINE_NO_LONG_TYPES MODULE = amd_ags_x64.dll +UNIXLIB = amd_ags_x64.so +UNIX_CFLAGS = $(DRM_CFLAGS) +UNIX_LIBS = $(DRM_LIBS) $(DRMAMDGPU_LIBS) IMPORTS = version vulkan-1 user32 IMPORTLIB = amd_ags_x64 EXTRADLLFLAGS = -mno-cygwin -Wb,--prefer-native C_SRCS = \ - amd_ags_x64_main.c + amd_ags_x64_main.c \ + unixlib.c IDL_SRCS = \ dxvk_interfaces.idl diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 154e74cf1d6..e4abe1abf0c 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -2,8 +2,11 @@ #include #include +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" +#include "winternl.h" #include "wine/debug.h" #include "wine/heap.h" @@ -21,8 +24,27 @@ #include "amd_ags.h" +#include "unixlib.h" + WINE_DEFAULT_DEBUG_CHANNEL(amd_ags); +#define AMD_AGS_CALL(func, args) WINE_UNIX_CALL( unix_ ## func, args ) + +static INIT_ONCE unix_init_once = INIT_ONCE_STATIC_INIT; +static BOOL unix_lib_initialized; + +static BOOL WINAPI init_unix_lib_once( INIT_ONCE *once, void *param, void **context ) +{ + unix_lib_initialized = !__wine_init_unix_call() && !AMD_AGS_CALL( init, NULL ); + return TRUE; +} + +static BOOL init_unix_lib(void) +{ + InitOnceExecuteOnce( &unix_init_once, init_unix_lib_once, NULL, NULL ); + return unix_lib_initialized; +} + static const char driver_version[] = "23.19.02-230831a-396538C-AMD-Software-Adrenalin-Edition"; static const char radeon_version[] = "23.10.2"; @@ -684,6 +706,8 @@ static AGSReturnCode init_ags_context(AGSContext *context, int ags_version) SET_DEVICE_FIELD(device, deviceId, int, context->version, vk_properties->deviceID); if (vk_properties->vendorID == 0x1002) { + if (!init_unix_lib()) + ERR("Failed to load unix lib.\n"); SET_DEVICE_FIELD(device, architectureVersion, ArchitectureVersion, context->version, ArchitectureVersion_GCN); SET_DEVICE_FIELD(device, asicFamily, AsicFamily, context->version, AsicFamily_GCN4); if (vk_properties->deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) diff --git a/dlls/amd_ags_x64/unixlib.c b/dlls/amd_ags_x64/unixlib.c new file mode 100644 index 00000000000..513c1903e4f --- /dev/null +++ b/dlls/amd_ags_x64/unixlib.c @@ -0,0 +1,54 @@ +/* + * Unix library for amd_ags_x64 functions + * + * Copyright 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 + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include +#include +#include + +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" + +#include "wine/debug.h" + +#include "unixlib.h" + +WINE_DEFAULT_DEBUG_CHANNEL(amd_ags); + +static NTSTATUS init( void *args ) +{ + TRACE(".\n"); + return STATUS_SUCCESS; +} + +const unixlib_entry_t __wine_unix_call_funcs[] = +{ + init, +}; diff --git a/dlls/amd_ags_x64/unixlib.h b/dlls/amd_ags_x64/unixlib.h new file mode 100644 index 00000000000..d3427519867 --- /dev/null +++ b/dlls/amd_ags_x64/unixlib.h @@ -0,0 +1,26 @@ +/* + * Unix library interface + * + * Copyright 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 "wine/unixlib.h" + +enum amd_ags_funcs +{ + unix_init, +}; From c7df0b6740c75de1b01854499f2f348d5f5df34d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 23 Nov 2023 15:27:38 -0600 Subject: [PATCH 1139/1148] amd_ags_x64: Load libdrm amdgpu info. CW-Bug-Id: #22976 --- dlls/amd_ags_x64/unixlib.c | 58 +++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/dlls/amd_ags_x64/unixlib.c b/dlls/amd_ags_x64/unixlib.c index 513c1903e4f..47a14a615c8 100644 --- a/dlls/amd_ags_x64/unixlib.c +++ b/dlls/amd_ags_x64/unixlib.c @@ -27,9 +27,13 @@ #include #include #include +#include +#include +#include #include #include +#include #include "ntstatus.h" #define WIN32_NO_STATUS @@ -42,9 +46,61 @@ WINE_DEFAULT_DEBUG_CHANNEL(amd_ags); +#define MAX_DEVICE_COUNT 64 + +static unsigned int device_count; +static struct drm_amdgpu_info_device *amd_info; + static NTSTATUS init( void *args ) { - TRACE(".\n"); + drmDevicePtr devices[MAX_DEVICE_COUNT]; + amdgpu_device_handle h; + uint32_t major, minor; + int i, count, fd, ret; + + device_count = 0; + + if ((count = drmGetDevices(devices, MAX_DEVICE_COUNT)) <= 0) + { + ERR("drmGetDevices failed, err %d.\n", count); + return STATUS_UNSUCCESSFUL; + } + TRACE("Got %d devices.\n", count); + for (i = 0; i < count; ++i) + { + if (!devices[i] || !devices[i]->nodes[DRM_NODE_RENDER]) + { + TRACE("No render node, skipping.\n"); + continue; + } + if ((fd = open(devices[i]->nodes[DRM_NODE_RENDER], O_RDONLY | O_CLOEXEC)) < 0) + { + ERR("Failed to open device %s, errno %d.\n", devices[i]->nodes[DRM_NODE_RENDER], errno); + continue; + } + if ((ret = amdgpu_device_initialize(fd, &major, &minor, &h))) + { + WARN("Failed to initialize amdgpu device bustype %d, %04x:%04x, err %d.\n", devices[i]->bustype, + devices[i]->deviceinfo.pci->vendor_id, devices[i]->deviceinfo.pci->device_id, ret); + close(fd); + continue; + } + amd_info = realloc(amd_info, (device_count + 1) * sizeof(*amd_info)); + if (!(ret = amdgpu_query_info(h, AMDGPU_INFO_DEV_INFO, sizeof(*amd_info), &amd_info[device_count]))) + { + TRACE("Got amdgpu info for device id %04x, family %#x, external_rev %#x, chip_rev %#x.\n", + amd_info[device_count].device_id, amd_info[device_count].family, amd_info[device_count].external_rev, + amd_info[device_count].chip_rev); + ++device_count; + } + else + { + ERR("amdgpu_query_info failed, ret %d.\n", ret); + } + amdgpu_device_deinitialize(h); + close(fd); + } + drmFreeDevices(devices, count); return STATUS_SUCCESS; } From d2baf1bc17ad9a4c3d718b415278a310ac7eecfa Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 23 Nov 2023 15:41:50 -0600 Subject: [PATCH 1140/1148] amd_ags_x64: Try to guess asicFamily from amdgpu info. CW-Bug-Id: #22976 --- dlls/amd_ags_x64/amd_ags_x64_main.c | 42 +++++++---- dlls/amd_ags_x64/unixlib.c | 106 ++++++++++++++++++++++++++++ dlls/amd_ags_x64/unixlib.h | 9 +++ 3 files changed, 142 insertions(+), 15 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index e4abe1abf0c..7a485a6cf61 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -73,21 +73,22 @@ static const struct int patch; unsigned int device_size; unsigned int dx11_returned_params_size; + int max_asicFamily; } 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)}, - {5, 3, 0, sizeof(AGSDeviceInfo_520), sizeof(AGSDX11ReturnedParams_520)}, - {5, 4, 0, sizeof(AGSDeviceInfo_540), sizeof(AGSDX11ReturnedParams_520)}, - {5, 4, 1, sizeof(AGSDeviceInfo_541), sizeof(AGSDX11ReturnedParams_520)}, - {5, 4, 2, sizeof(AGSDeviceInfo_542), sizeof(AGSDX11ReturnedParams_520)}, - {6, 0, 0, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600)}, - {6, 0, 1, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600)}, - {6, 1, 0, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600)}, - {6, 2, 0, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600)}, + {5, 0, 5, sizeof(AGSDeviceInfo_511), sizeof(AGSDX11ReturnedParams_511), 0}, + {5, 1, 1, sizeof(AGSDeviceInfo_511), sizeof(AGSDX11ReturnedParams_511), 0}, + {5, 2, 0, sizeof(AGSDeviceInfo_520), sizeof(AGSDX11ReturnedParams_520), 0}, + {5, 2, 1, sizeof(AGSDeviceInfo_520), sizeof(AGSDX11ReturnedParams_520), 0}, + {5, 3, 0, sizeof(AGSDeviceInfo_520), sizeof(AGSDX11ReturnedParams_520), 0}, + {5, 4, 0, sizeof(AGSDeviceInfo_540), sizeof(AGSDX11ReturnedParams_520), AsicFamily_RDNA}, + {5, 4, 1, sizeof(AGSDeviceInfo_541), sizeof(AGSDX11ReturnedParams_520), AsicFamily_RDNA}, + {5, 4, 2, sizeof(AGSDeviceInfo_542), sizeof(AGSDX11ReturnedParams_520), AsicFamily_RDNA}, + {6, 0, 0, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600), AsicFamily_RDNA2}, + {6, 0, 1, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600), AsicFamily_RDNA2}, + {6, 1, 0, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600), AsicFamily_RDNA3}, + {6, 2, 0, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600), AsicFamily_RDNA3}, }; #define DEF_FIELD(name) {DEVICE_FIELD_##name, {offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_520, name), \ @@ -706,10 +707,21 @@ static AGSReturnCode init_ags_context(AGSContext *context, int ags_version) SET_DEVICE_FIELD(device, deviceId, int, context->version, vk_properties->deviceID); if (vk_properties->vendorID == 0x1002) { - if (!init_unix_lib()) - ERR("Failed to load unix lib.\n"); + struct get_device_info_params params = + { + .device_id = vk_properties->deviceID, + }; + SET_DEVICE_FIELD(device, architectureVersion, ArchitectureVersion, context->version, ArchitectureVersion_GCN); - SET_DEVICE_FIELD(device, asicFamily, AsicFamily, context->version, AsicFamily_GCN4); + if (init_unix_lib() && !AMD_AGS_CALL(get_device_info, ¶ms)) + { + SET_DEVICE_FIELD(device, asicFamily, AsicFamily, context->version, + min(params.asic_family, amd_ags_info[context->version].max_asicFamily)); + } + else + { + SET_DEVICE_FIELD(device, asicFamily, AsicFamily, context->version, AsicFamily_GCN4); + } if (vk_properties->deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { if (context->version >= AMD_AGS_VERSION_6_0_0) diff --git a/dlls/amd_ags_x64/unixlib.c b/dlls/amd_ags_x64/unixlib.c index 47a14a615c8..72d2f5d2efb 100644 --- a/dlls/amd_ags_x64/unixlib.c +++ b/dlls/amd_ags_x64/unixlib.c @@ -104,7 +104,113 @@ static NTSTATUS init( void *args ) return STATUS_SUCCESS; } +typedef enum AsicFamily +{ + AsicFamily_Unknown, ///< Unknown architecture, potentially from another IHV. Check \ref AGSDeviceInfo::vendorId + AsicFamily_PreGCN, ///< Pre GCN architecture. + AsicFamily_GCN1, ///< AMD GCN 1 architecture: Oland, Cape Verde, Pitcairn & Tahiti. + AsicFamily_GCN2, ///< AMD GCN 2 architecture: Hawaii & Bonaire. This also includes APUs Kaveri and Carrizo. + AsicFamily_GCN3, ///< AMD GCN 3 architecture: Tonga & Fiji. + AsicFamily_GCN4, ///< AMD GCN 4 architecture: Polaris. + AsicFamily_Vega, ///< AMD Vega architecture, including Raven Ridge (ie AMD Ryzen CPU + AMD Vega GPU). + AsicFamily_RDNA, ///< AMD RDNA architecture + AsicFamily_RDNA2, ///< AMD RDNA2 architecture + AsicFamily_RDNA3, ///< AMD RDNA3 architecture +} AsicFamily; + +/* Constants from Mesa source. */ +#define FAMILY_UNKNOWN 0x00 +#define FAMILY_TN 0x69 /* # 105 / Trinity APUs */ +#define FAMILY_SI 0x6E /* # 110 / Southern Islands: Tahiti, Pitcairn, CapeVerde, Oland, Hainan */ +#define FAMILY_CI 0x78 /* # 120 / Sea Islands: Bonaire, Hawaii */ +#define FAMILY_KV 0x7D /* # 125 / Kaveri APUs: Spectre, Spooky, Kalindi, Godavari */ +#define FAMILY_VI 0x82 /* # 130 / Volcanic Islands: Iceland, Tonga, Fiji */ +#define FAMILY_POLARIS 0x82 /* # 130 / Polaris: 10, 11, 12 */ +#define FAMILY_CZ 0x87 /* # 135 / Carrizo APUs: Carrizo, Stoney */ +#define FAMILY_AI 0x8D /* # 141 / Vega: 10, 20 */ +#define FAMILY_RV 0x8E /* # 142 / Raven */ +#define FAMILY_NV 0x8F /* # 143 / Navi: 10 */ +#define FAMILY_VGH 0x90 /* # 144 / Van Gogh */ +#define FAMILY_NV3 0x91 /* # 145 / Navi: 3x */ +#define FAMILY_RMB 0x92 /* # 146 / Rembrandt */ +#define FAMILY_RPL 0x95 /* # 149 / Raphael */ +#define FAMILY_GFX1103 0x94 +#define FAMILY_GFX1150 0x96 +#define FAMILY_MDN 0x97 /* # 151 / Mendocino */ + +static void fill_device_info(struct drm_amdgpu_info_device *info, struct get_device_info_params *out) +{ + uint32_t erev = info->external_rev; + + out->asic_family = AsicFamily_Unknown; + switch (info->family) + { + case FAMILY_AI: + case FAMILY_RV: + out->asic_family = AsicFamily_Vega; + break; + + /* Treat pre-Polaris cards as Polaris. */ + case FAMILY_CZ: + case FAMILY_SI: + case FAMILY_CI: + case FAMILY_KV: + case FAMILY_POLARIS: + out->asic_family = AsicFamily_GCN4; + break; + + case FAMILY_NV: + if (erev >= 0x01 && erev < 0x28) + out->asic_family = AsicFamily_RDNA; + else if (erev >= 0x28 && erev < 0x50) + out->asic_family = AsicFamily_RDNA2; + break; + + case FAMILY_RMB: + case FAMILY_RPL: + case FAMILY_MDN: + case FAMILY_VGH: + out->asic_family = AsicFamily_RDNA2; + break; + + case FAMILY_NV3: + case FAMILY_GFX1103: + case FAMILY_GFX1150: + out->asic_family = AsicFamily_RDNA3; + break; + } + TRACE("family %u, erev %#x -> asicFamily %d.\n", info->family, erev, out->asic_family); + if (out->asic_family == AsicFamily_Unknown && info->family != FAMILY_UNKNOWN) + { + if (info->family > FAMILY_GFX1150) + out->asic_family = AsicFamily_RDNA3; + else + out->asic_family = AsicFamily_GCN4; + + FIXME("Unrecognized family %u, erev %#x -> defaulting to %d.\n", info->family, erev, + out->asic_family); + } +} + +static NTSTATUS get_device_info( void *args ) +{ + struct get_device_info_params *params = args; + unsigned int i; + + for (i = 0; i < device_count; ++i) + { + if (amd_info[i].device_id != params->device_id) + continue; + TRACE("device %04x found.\n", params->device_id); + fill_device_info(&amd_info[i], params); + return STATUS_SUCCESS; + } + TRACE("Device %04x not found.\n", params->device_id); + return STATUS_NOT_FOUND; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { init, + get_device_info, }; diff --git a/dlls/amd_ags_x64/unixlib.h b/dlls/amd_ags_x64/unixlib.h index d3427519867..c895a0499fc 100644 --- a/dlls/amd_ags_x64/unixlib.h +++ b/dlls/amd_ags_x64/unixlib.h @@ -23,4 +23,13 @@ enum amd_ags_funcs { unix_init, + unix_get_device_info, +}; + +struct get_device_info_params +{ + uint32_t device_id; + uint32_t _pad; + /* Output parameters. */ + uint32_t asic_family; }; From 34aa2bb571a0cddf01b752449b4829e1a76cb755 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 23 Nov 2023 18:11:41 -0600 Subject: [PATCH 1141/1148] amd_ags_x64: Fill more device info fields. CW-Bug-Id: #22976 --- dlls/amd_ags_x64/amd_ags_x64_main.c | 26 +++++++++++++++ dlls/amd_ags_x64/unixlib.c | 51 +++++++++++++++++++++++++++++ dlls/amd_ags_x64/unixlib.h | 7 ++++ 3 files changed, 84 insertions(+) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 7a485a6cf61..a6cc83d6699 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -98,6 +98,10 @@ amd_ags_info[AMD_AGS_VERSION_COUNT] = #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, -1}} +#define DEF_FIELD_520_UP(name) {DEVICE_FIELD_##name, {-1, -1, 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), offsetof(AGSDeviceInfo_600, name)}} #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), \ @@ -122,6 +126,14 @@ amd_ags_info[AMD_AGS_VERSION_COUNT] = #define DEVICE_FIELD_displays 8 #define DEVICE_FIELD_isAPU 9 +#define DEVICE_FIELD_numCUs 10 +#define DEVICE_FIELD_coreClock 11 +#define DEVICE_FIELD_memoryClock 12 +#define DEVICE_FIELD_teraFlops 13 +#define DEVICE_FIELD_numWGPs 14 +#define DEVICE_FIELD_numROPs 15 +#define DEVICE_FIELD_memoryBandwidth 16 + static const struct { unsigned int field_index; @@ -139,6 +151,13 @@ device_struct_fields[] = DEF_FIELD(numDisplays), DEF_FIELD(displays), DEF_FIELD_540_600(isAPU), + DEF_FIELD(numCUs), + DEF_FIELD(coreClock), + DEF_FIELD(memoryClock), + DEF_FIELD(teraFlops), + DEF_FIELD_540_UP(numWGPs), + DEF_FIELD_520_UP(numROPs), + DEF_FIELD_520_UP(memoryBandwidth), }; #undef DEF_FIELD @@ -717,6 +736,13 @@ static AGSReturnCode init_ags_context(AGSContext *context, int ags_version) { SET_DEVICE_FIELD(device, asicFamily, AsicFamily, context->version, min(params.asic_family, amd_ags_info[context->version].max_asicFamily)); + SET_DEVICE_FIELD(device, numCUs, int, context->version, params.num_cu); + SET_DEVICE_FIELD(device, numWGPs, int, context->version, params.num_wgp); + SET_DEVICE_FIELD(device, numROPs, int, context->version, params.num_rops); + SET_DEVICE_FIELD(device, coreClock, int, context->version, params.core_clock); + SET_DEVICE_FIELD(device, memoryClock, int, context->version, params.memory_clock); + SET_DEVICE_FIELD(device, memoryBandwidth, int, context->version, params.memory_bandwidth); + SET_DEVICE_FIELD(device, teraFlops, float, context->version, params.teraflops); } else { diff --git a/dlls/amd_ags_x64/unixlib.c b/dlls/amd_ags_x64/unixlib.c index 72d2f5d2efb..479ce8258a5 100644 --- a/dlls/amd_ags_x64/unixlib.c +++ b/dlls/amd_ags_x64/unixlib.c @@ -104,6 +104,42 @@ static NTSTATUS init( void *args ) return STATUS_SUCCESS; } +#ifndef AMDGPU_VRAM_TYPE_DDR5 +# define AMDGPU_VRAM_TYPE_DDR5 10 +#endif +#ifndef AMDGPU_VRAM_TYPE_LPDDR4 +# define AMDGPU_VRAM_TYPE_LPDDR4 11 +#endif +#ifndef AMDGPU_VRAM_TYPE_LPDDR5 +# define AMDGPU_VRAM_TYPE_LPDDR5 12 +#endif + +/* From Mesa source. */ +static uint32_t memory_ops_per_clock(uint32_t vram_type) +{ + /* Based on MemoryOpsPerClockTable from PAL. */ + switch (vram_type) { + case AMDGPU_VRAM_TYPE_GDDR1: + case AMDGPU_VRAM_TYPE_GDDR3: /* last in low-end Evergreen */ + case AMDGPU_VRAM_TYPE_GDDR4: /* last in R7xx, not used much */ + case AMDGPU_VRAM_TYPE_UNKNOWN: + default: + return 0; + case AMDGPU_VRAM_TYPE_DDR2: + case AMDGPU_VRAM_TYPE_DDR3: + case AMDGPU_VRAM_TYPE_DDR4: + case AMDGPU_VRAM_TYPE_LPDDR4: + case AMDGPU_VRAM_TYPE_HBM: /* same for HBM2 and HBM3 */ + return 2; + case AMDGPU_VRAM_TYPE_DDR5: + case AMDGPU_VRAM_TYPE_LPDDR5: + case AMDGPU_VRAM_TYPE_GDDR5: /* last in Polaris and low-end Navi14 */ + return 4; + case AMDGPU_VRAM_TYPE_GDDR6: + return 16; + } +} + typedef enum AsicFamily { AsicFamily_Unknown, ///< Unknown architecture, potentially from another IHV. Check \ref AGSDeviceInfo::vendorId @@ -138,6 +174,8 @@ typedef enum AsicFamily #define FAMILY_GFX1150 0x96 #define FAMILY_MDN 0x97 /* # 151 / Mendocino */ +#define ROUND_DIV(value, div) (((value) + (div) / 2) / (div)) + static void fill_device_info(struct drm_amdgpu_info_device *info, struct get_device_info_params *out) { uint32_t erev = info->external_rev; @@ -190,6 +228,19 @@ static void fill_device_info(struct drm_amdgpu_info_device *info, struct get_dev FIXME("Unrecognized family %u, erev %#x -> defaulting to %d.\n", info->family, erev, out->asic_family); } + + out->num_cu = info->cu_active_number; + out->num_wgp = out->asic_family >= AsicFamily_RDNA ? out->num_cu / 2 : 0; + out->num_rops = info->num_rb_pipes * 4; + TRACE("num_cu %d, num_wgp %d, num_rops %d.\n", out->num_cu, out->num_wgp, out->num_rops); + out->core_clock = ROUND_DIV(info->max_engine_clock, 1000); + out->memory_clock = ROUND_DIV(info->max_memory_clock, 1000); + out->memory_bandwidth = ROUND_DIV(info->max_memory_clock * memory_ops_per_clock(info->vram_type) + * info->vram_bit_width / 8, 1000); + TRACE("core_clock %uMHz, memory_clock %uMHz, memory_bandwidth %u.\n", + out->core_clock, out->memory_clock, out->memory_bandwidth); + out->teraflops = 1e-9f * info->max_engine_clock * info->cu_active_number * 64 * 2; + TRACE("teraflops %.2f.\n", out->teraflops); } static NTSTATUS get_device_info( void *args ) diff --git a/dlls/amd_ags_x64/unixlib.h b/dlls/amd_ags_x64/unixlib.h index c895a0499fc..72422e1535c 100644 --- a/dlls/amd_ags_x64/unixlib.h +++ b/dlls/amd_ags_x64/unixlib.h @@ -32,4 +32,11 @@ struct get_device_info_params uint32_t _pad; /* Output parameters. */ uint32_t asic_family; + uint32_t num_cu; + uint32_t num_wgp; + uint32_t num_rops; + uint32_t core_clock; + uint32_t memory_clock; + uint32_t memory_bandwidth; + float teraflops; }; From a58b925768fd6b21fdd06a9ee924d3139517743d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 23 Nov 2023 18:53:03 -0600 Subject: [PATCH 1142/1148] amd_ags_x64: Downgrade agsCheckDriverVersion() message to WARN. CW-Bug-Id: #22976 --- dlls/amd_ags_x64/amd_ags_x64_main.c | 2 +- 1 file changed, 1 insertion(+), 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 a6cc83d6699..cbd60af6af3 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -1159,7 +1159,7 @@ AGSReturnCode WINAPI agsDriverExtensionsDX12_DestroyDevice(AGSContext* context, AGSDriverVersionResult WINAPI agsCheckDriverVersion(const char* version_reported, unsigned int version_required) { - FIXME("version_reported %s, version_required %d semi-stub.\n", debugstr_a(version_reported), version_required); + WARN("version_reported %s, version_required %d semi-stub.\n", debugstr_a(version_reported), version_required); return AGS_SOFTWAREVERSIONCHECK_OK; } From 4a69ec68673889c1bf5444051f63577e945a3a69 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 24 Nov 2023 14:36:47 +0000 Subject: [PATCH 1143/1148] amend! mfmediaengine: Be a bit more conservative with locks in engine Shutdown. mfmediaengine: Be a bit more conservative with locks in engine Shutdown. During engine shutdown we acquire engine lock first, then locks of its constituents (e.g. sample grabbers); whereas normally the order is the other way around (e.g. timer callback -> acquire sample grabber lock -> OnProcessSample callback -> engine lock). This is deadlock prone. With this commit, engine lock is released before we shutdown the inner media session. --- dlls/mfmediaengine/main.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index ed9bd75f797..1138a7c9734 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -2201,7 +2201,7 @@ static HRESULT WINAPI media_engine_GetVideoAspectRatio(IMFMediaEngineEx *iface, static HRESULT WINAPI media_engine_Shutdown(IMFMediaEngineEx *iface) { struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - IMFMediaSession *session; + IMFMediaSession *session = NULL; HRESULT hr = S_OK; TRACE("%p.\n", iface); @@ -2218,8 +2218,11 @@ static HRESULT WINAPI media_engine_Shutdown(IMFMediaEngineEx *iface) } LeaveCriticalSection(&engine->cs); - IMFMediaSession_Shutdown(session); - IMFMediaSession_Release(session); + if (SUCCEEDED(hr)) + { + IMFMediaSession_Shutdown(session); + IMFMediaSession_Release(session); + } return hr; } From c724349a01855b82355507ecbadd3bc5b4cfc2ad Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 24 Nov 2023 11:16:34 -0600 Subject: [PATCH 1144/1148] fixup! amd_ags_x64: Load libdrm amdgpu info. CW-Bug-Id: #22976 --- dlls/amd_ags_x64/unixlib.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/amd_ags_x64/unixlib.c b/dlls/amd_ags_x64/unixlib.c index 479ce8258a5..be94b75eab9 100644 --- a/dlls/amd_ags_x64/unixlib.c +++ b/dlls/amd_ags_x64/unixlib.c @@ -86,6 +86,9 @@ static NTSTATUS init( void *args ) continue; } amd_info = realloc(amd_info, (device_count + 1) * sizeof(*amd_info)); + /* amdgpu_query_info() doesn't fail on short buffer (filling in the available buffer size). So older or + * newer DRM version should be fine but zero init the structure to avoid random values. */ + memset(&amd_info[device_count], 0, sizeof(*amd_info)); if (!(ret = amdgpu_query_info(h, AMDGPU_INFO_DEV_INFO, sizeof(*amd_info), &amd_info[device_count]))) { TRACE("Got amdgpu info for device id %04x, family %#x, external_rev %#x, chip_rev %#x.\n", From 92810f4a145b17067a851c3940cee6f04f466fc5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 24 Nov 2023 11:24:03 -0600 Subject: [PATCH 1145/1148] amd_ags_x64: Workaround zero clock reporting on Vangogh GPU. CW-Bug-Id: #22976 --- dlls/amd_ags_x64/unixlib.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/dlls/amd_ags_x64/unixlib.c b/dlls/amd_ags_x64/unixlib.c index be94b75eab9..7e5bc5bf4d6 100644 --- a/dlls/amd_ags_x64/unixlib.c +++ b/dlls/amd_ags_x64/unixlib.c @@ -182,6 +182,7 @@ typedef enum AsicFamily static void fill_device_info(struct drm_amdgpu_info_device *info, struct get_device_info_params *out) { uint32_t erev = info->external_rev; + uint64_t max_engine_clock_khz, max_memory_clock_khz; out->asic_family = AsicFamily_Unknown; switch (info->family) @@ -236,13 +237,19 @@ static void fill_device_info(struct drm_amdgpu_info_device *info, struct get_dev out->num_wgp = out->asic_family >= AsicFamily_RDNA ? out->num_cu / 2 : 0; out->num_rops = info->num_rb_pipes * 4; TRACE("num_cu %d, num_wgp %d, num_rops %d.\n", out->num_cu, out->num_wgp, out->num_rops); - out->core_clock = ROUND_DIV(info->max_engine_clock, 1000); - out->memory_clock = ROUND_DIV(info->max_memory_clock, 1000); - out->memory_bandwidth = ROUND_DIV(info->max_memory_clock * memory_ops_per_clock(info->vram_type) + /* These numbers are zero on Vangogh, workaround that (similar to how it is currently done + * in Mesa src/amd/common/ac_rgp.c. */ + if (!(max_engine_clock_khz = info->max_engine_clock)) + max_engine_clock_khz = 1300000; + if (!(max_memory_clock_khz = info->max_memory_clock)) + max_memory_clock_khz = 687000; + out->core_clock = ROUND_DIV(max_engine_clock_khz, 1000); + out->memory_clock = ROUND_DIV(max_memory_clock_khz, 1000); + out->memory_bandwidth = ROUND_DIV(max_memory_clock_khz * memory_ops_per_clock(info->vram_type) * info->vram_bit_width / 8, 1000); TRACE("core_clock %uMHz, memory_clock %uMHz, memory_bandwidth %u.\n", out->core_clock, out->memory_clock, out->memory_bandwidth); - out->teraflops = 1e-9f * info->max_engine_clock * info->cu_active_number * 64 * 2; + out->teraflops = 1e-9f * max_engine_clock_khz * info->cu_active_number * 64 * 2; TRACE("teraflops %.2f.\n", out->teraflops); } From 34e4e3bcacbb2682ceb1b9d09553e99e84dc82cf Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 24 Nov 2023 15:39:13 -0600 Subject: [PATCH 1146/1148] setupapi: Add stubs for SetupDiGetCustomDeviceProperty{A|W}(). CW-Bug-Id: #23045 --- dlls/setupapi/devinst.c | 20 ++++++++++++++++++++ dlls/setupapi/setupapi.spec | 2 ++ 2 files changed, 22 insertions(+) diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c index 93d7c849ee5..09928788c8c 100644 --- a/dlls/setupapi/devinst.c +++ b/dlls/setupapi/devinst.c @@ -5368,3 +5368,23 @@ BOOL WINAPI SetupDiInstallDevice(HDEVINFO devinfo, SP_DEVINFO_DATA *device_data) return TRUE; } + +BOOL WINAPI SetupDiGetCustomDevicePropertyA(HDEVINFO devinfo, SP_DEVINFO_DATA *data, const char *name, DWORD flags, + DWORD *reg_type, BYTE *buffer, DWORD bufsize, DWORD *required) +{ + FIXME("devinfo %p, data %p, name %s, flags %#lx, reg_type %p, buffer %p, bufsize %lu, required %p stub.\n", + devinfo, data, debugstr_a(name), flags, reg_type, buffer, bufsize, required); + + SetLastError(ERROR_INVALID_DATA); + return FALSE; +} + +BOOL WINAPI SetupDiGetCustomDevicePropertyW(HDEVINFO devinfo, SP_DEVINFO_DATA *data, const WCHAR *name, DWORD flags, + DWORD *reg_type, BYTE *buffer, DWORD bufsize, DWORD *required) +{ + FIXME("devinfo %p, data %p, name %s, flags %#lx, reg_type %p, buffer %p, bufsize %lu, required %p stub.\n", + devinfo, data, debugstr_w(name), flags, reg_type, buffer, bufsize, required); + + SetLastError(ERROR_INVALID_DATA); + return FALSE; +} diff --git a/dlls/setupapi/setupapi.spec b/dlls/setupapi/setupapi.spec index 7578fb25c9c..4d144fe739b 100644 --- a/dlls/setupapi/setupapi.spec +++ b/dlls/setupapi/setupapi.spec @@ -337,6 +337,8 @@ @ stub SetupDiGetClassInstallParamsA @ stub SetupDiGetClassInstallParamsW @ stdcall SetupDiGetClassRegistryPropertyW(ptr long ptr ptr long ptr wstr ptr) +@ stdcall SetupDiGetCustomDevicePropertyA(ptr ptr str long ptr ptr long ptr) +@ stdcall SetupDiGetCustomDevicePropertyW(ptr ptr wstr long ptr ptr long ptr) @ stub SetupDiGetDeviceInfoListClass @ stdcall SetupDiGetDeviceInfoListDetailA(ptr ptr) @ stdcall SetupDiGetDeviceInfoListDetailW(ptr ptr) From 83a713801b989294d8c414ca81a1852b939497c6 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 26 Nov 2023 11:06:49 -0500 Subject: [PATCH 1147/1148] winex11.drv: fshack: Fix initializing fs_monitor dmDeviceName with uninitialized string. --- dlls/winex11.drv/fs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/winex11.drv/fs.c b/dlls/winex11.drv/fs.c index 4113d1eb4e6..dd919ad823f 100644 --- a/dlls/winex11.drv/fs.c +++ b/dlls/winex11.drv/fs.c @@ -432,7 +432,6 @@ static BOOL fs_get_current_mode( ULONG_PTR adapter_id, DEVMODEW *mode ) static LONG fs_set_current_mode( ULONG_PTR adapter_id, const DEVMODEW *user_mode ) { - WCHAR device_name[CCHDEVICENAME]; struct fs_monitor *fs_monitor; DEVMODEW real_mode; double scale; @@ -462,7 +461,7 @@ static LONG fs_set_current_mode( ULONG_PTR adapter_id, const DEVMODEW *user_mode fs_monitor->user_mode = *user_mode; fs_monitor->real_mode = real_mode; - lstrcpyW( fs_monitor->user_mode.dmDeviceName, device_name ); + lstrcpyW( fs_monitor->user_mode.dmDeviceName, L"fshack" ); if (is_detached_mode( user_mode )) { From a391b3f61d249d7c46f038e3b5e24a0e9cd68ff3 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Thu, 14 Dec 2023 03:40:09 +0000 Subject: [PATCH 1148/1148] winevulkan: HACK: Add WINE_HIDE_AMD_GPU --- dlls/winevulkan/loader.c | 32 +++++++++++++++++++++++--------- dlls/winex11.drv/display.c | 11 +++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c index ba2a1d23bc7..6f82ed43f94 100644 --- a/dlls/winevulkan/loader.c +++ b/dlls/winevulkan/loader.c @@ -430,17 +430,31 @@ static void fixup_device_id(VkPhysicalDeviceProperties *properties) } } } - else if (properties->vendorID && properties->vendorID == 0x1002 && properties->deviceID == 0x163f) + else if (properties->vendorID && properties->vendorID == 0x1002) { - /* AMD VAN GOGH */ - BOOL hide; - sgi = getenv("WINE_HIDE_VANGOGH_GPU"); - if (sgi) - hide = *sgi != '0'; + sgi = getenv("WINE_HIDE_AMD_GPU"); + if (sgi && *sgi != '0') + { + { + properties->vendorID = 0x10de; /* NVIDIA */ + properties->deviceID = 0x2204; /* RTX 3090 */ + } + } else - hide = (sgi = getenv("SteamGameId")) && !strcmp(sgi, "257420"); - if (hide) - properties->deviceID = 0x687f; /* Radeon RX Vega 56/64 */ + { + if (properties->deviceID == 0x163f) + { + /* AMD VAN GOGH */ + BOOL hide; + sgi = getenv("WINE_HIDE_VANGOGH_GPU"); + if (sgi) + hide = *sgi != '0'; + else + hide = (sgi = getenv("SteamGameId")) && !strcmp(sgi, "257420"); + if (hide) + properties->deviceID = 0x687f; /* Radeon RX Vega 56/64 */ + } + } } } diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index 88c96d52c1e..39162f52f0f 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -601,6 +601,17 @@ BOOL X11DRV_UpdateDisplayDevices( const struct gdi_device_manager *device_manage gpus[gpu].device_id = 0x67df; /* RX 480 */ } } + + sgi = getenv("WINE_HIDE_AMD_GPU"); + if (sgi && *sgi != '0') + { + if (gpus[gpu].vendor_id == 0x1002 /* AMD */) + { + gpus[gpu].vendor_id = 0x10de; /* NVIDIA */ + gpus[gpu].device_id = 0x2204; /* RTX 3090 */ + } + } + if (gpus[gpu].vendor_id == 0x1002 && gpus[gpu].device_id == 0x163f && (sgi = getenv("WINE_HIDE_VANGOGH_GPU")) && *sgi != '0') {