[PATCH 4/5] windows.media.speech: Implement ResultGenerated event.

Bernhard Kölbl besentv at gmail.com
Wed Mar 16 11:31:03 CDT 2022


Also add helpers to manage typed event handlers.

Signed-off-by: Bernhard Kölbl <besentv at gmail.com>
---
 dlls/windows.media.speech/Makefile.in      |   1 +
 dlls/windows.media.speech/event_handlers.c | 108 +++++++++++++++++++++
 dlls/windows.media.speech/private.h        |   7 ++
 dlls/windows.media.speech/recognizer.c     |  17 +++-
 dlls/windows.media.speech/tests/speech.c   |  90 ++++++++++++++++-
 include/windows.foundation.idl             |   1 +
 6 files changed, 218 insertions(+), 6 deletions(-)
 create mode 100644 dlls/windows.media.speech/event_handlers.c

diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in
index 2f9ca159e2a..5dbdb03cf2f 100644
--- a/dlls/windows.media.speech/Makefile.in
+++ b/dlls/windows.media.speech/Makefile.in
@@ -2,6 +2,7 @@ MODULE = windows.media.speech.dll
 IMPORTS = combase uuid
 
 C_SRCS = \
+	event_handlers.c \
 	listconstraint.c \
 	main.c \
 	recognizer.c \
diff --git a/dlls/windows.media.speech/event_handlers.c b/dlls/windows.media.speech/event_handlers.c
new file mode 100644
index 00000000000..7b5db971d1a
--- /dev/null
+++ b/dlls/windows.media.speech/event_handlers.c
@@ -0,0 +1,108 @@
+/* WinRT Windows.Media.SpeechRecognition implementation
+ *
+ * Copyright 2022 Bernhard Kölbl
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "private.h"
+
+static CRITICAL_SECTION handlers_cs;
+static CRITICAL_SECTION_DEBUG handlers_cs_debug =
+{
+    0, 0, &handlers_cs,
+    { &handlers_cs_debug.ProcessLocksList, &handlers_cs_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": handlers_cs") }
+};
+static CRITICAL_SECTION handlers_cs = { &handlers_cs_debug, -1, 0, 0, 0, 0 };
+static EventRegistrationToken next_token = {.value = 1};
+
+struct typed_event_handler_entry
+{
+    struct list entry;
+    EventRegistrationToken token;
+    ITypedEventHandler_IInspectable_IInspectable *handler;
+};
+
+HRESULT typed_event_handlers_append( struct list *list, ITypedEventHandler_IInspectable_IInspectable *handler, EventRegistrationToken *token )
+{
+    struct typed_event_handler_entry *entry;
+
+    if (!(entry = calloc(1, sizeof(*entry)))) return E_OUTOFMEMORY;
+    ITypedEventHandler_IInspectable_IInspectable_AddRef((entry->handler = handler));
+
+    EnterCriticalSection(&handlers_cs);
+
+    *token = entry->token = next_token;
+    next_token.value++;
+    list_add_tail(list, &entry->entry);
+
+    LeaveCriticalSection(&handlers_cs);
+
+    return S_OK;
+}
+
+HRESULT typed_event_handlers_remove( struct list *list, EventRegistrationToken *token )
+{
+    struct typed_event_handler_entry *entry;
+    BOOL found = FALSE;
+
+    EnterCriticalSection(&handlers_cs);
+
+    LIST_FOR_EACH_ENTRY(entry, list, struct typed_event_handler_entry, entry)
+        if ((found = !memcmp(&entry->token, token, sizeof(*token)))) break;
+    if (found) list_remove(&entry->entry);
+
+    LeaveCriticalSection(&handlers_cs);
+
+    if (found)
+    {
+        ITypedEventHandler_IInspectable_IInspectable_Release(entry->handler);
+        free(entry);
+    }
+
+    return S_OK;
+}
+
+HRESULT typed_event_handlers_notify( struct list *list, IInspectable *sender, IInspectable *args )
+{
+    struct typed_event_handler_entry *entry;
+
+    EnterCriticalSection(&handlers_cs);
+
+    LIST_FOR_EACH_ENTRY(entry, list, struct typed_event_handler_entry, entry)
+        ITypedEventHandler_IInspectable_IInspectable_Invoke(entry->handler, sender, args);
+
+    LeaveCriticalSection(&handlers_cs);
+
+    return S_OK;
+}
+
+HRESULT typed_event_handlers_clear( struct list* list )
+{
+    struct typed_event_handler_entry *entry, *entry_cursor2;
+
+    EnterCriticalSection(&handlers_cs);
+
+    LIST_FOR_EACH_ENTRY_SAFE(entry, entry_cursor2, list, struct typed_event_handler_entry, entry)
+    {
+        list_remove(&entry->entry);
+        free(entry);
+    }
+
+    LeaveCriticalSection(&handlers_cs);
+
+    return S_OK;
+}
diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h
index c31b8825c74..3e2fa9aed38 100644
--- a/dlls/windows.media.speech/private.h
+++ b/dlls/windows.media.speech/private.h
@@ -40,6 +40,8 @@
 #define WIDL_using_Windows_Media_SpeechRecognition
 #include "windows.media.speechrecognition.h"
 
+#include "wine/list.h"
+
 /*
  *
  * Windows.Media.SpeechRecognition
@@ -59,6 +61,11 @@ extern IActivationFactory *synthesizer_factory;
 
 
 
+HRESULT typed_event_handlers_append( struct list *list, ITypedEventHandler_IInspectable_IInspectable *handler, EventRegistrationToken *token );
+HRESULT typed_event_handlers_remove( struct list *list, EventRegistrationToken *token );
+HRESULT typed_event_handlers_notify( struct list *list, IInspectable *sender, IInspectable *args );
+HRESULT typed_event_handlers_clear( struct list* list );
+
 #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr )             \
     static inline impl_type *impl_from( iface_type *iface )                                        \
     {                                                                                              \
diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c
index 47c6e62f417..7f4c7429929 100644
--- a/dlls/windows.media.speech/recognizer.c
+++ b/dlls/windows.media.speech/recognizer.c
@@ -33,6 +33,8 @@ struct session
 {
     ISpeechContinuousRecognitionSession ISpeechContinuousRecognitionSession_iface;
     LONG ref;
+
+    struct list result_handlers;
 };
 
 /*
@@ -80,7 +82,10 @@ static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface
     TRACE("iface %p, ref %lu.\n", iface, ref);
 
     if (!ref)
+    {
+        typed_event_handlers_clear(&impl->result_handlers);
         free(impl);
+    }
 
     return ref;
 }
@@ -171,14 +176,17 @@ static HRESULT WINAPI session_add_ResultGenerated( ISpeechContinuousRecognitionS
                                                    ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgs *handler,
                                                    EventRegistrationToken *token)
 {
-    FIXME("iface %p, handler %p, token %p stub!\n", iface, handler, token);
-    return E_NOTIMPL;
+    struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface);
+    TRACE("iface %p, handler %p, token %p.\n", iface, handler, token);
+    if (!handler) return E_INVALIDARG;
+    return typed_event_handlers_append(&impl->result_handlers, (ITypedEventHandler_IInspectable_IInspectable *)handler, token);
 }
 
 static HRESULT WINAPI session_remove_ResultGenerated( ISpeechContinuousRecognitionSession *iface, EventRegistrationToken token )
 {
-    FIXME("iface %p, token.value %#I64x stub!\n", iface, token.value);
-    return E_NOTIMPL;
+    struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface);
+    TRACE("iface %p, token.value %#I64x.\n", iface, token.value);
+    return typed_event_handlers_remove(&impl->result_handlers, &token);
 }
 
 static const struct ISpeechContinuousRecognitionSessionVtbl session_vtbl =
@@ -637,6 +645,7 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface
 
     session->ISpeechContinuousRecognitionSession_iface.lpVtbl = &session_vtbl;
     session->ref = 1;
+    list_init(&session->result_handlers);
 
     impl->ISpeechRecognizer_iface.lpVtbl = &speech_recognizer_vtbl;
     impl->IClosable_iface.lpVtbl = &closable_vtbl;
diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c
index e22ab35928e..80e5d9af634 100644
--- a/dlls/windows.media.speech/tests/speech.c
+++ b/dlls/windows.media.speech/tests/speech.c
@@ -40,6 +40,12 @@
 
 #define SPERR_WINRT_INTERNAL_ERROR 0x800455a0
 
+#define IHandler_RecognitionResult ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgs
+#define IHandler_RecognitionResultVtbl ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgsVtbl
+#define IID_IHandler_RecognitionResult IID_ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgs
+#define impl_from_IHandler_RecognitionResult impl_from_ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgs
+#define IHandler_RecognitionResult_iface ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgs_iface
+
 HRESULT WINAPI (*pDllGetActivationFactory)(HSTRING, IActivationFactory **);
 static BOOL is_win10_1507 = FALSE;
 
@@ -80,6 +86,72 @@ static const char *debugstr_hstring(HSTRING hstr)
     return wine_dbgstr_wn(str, len);
 }
 
+struct recognition_result_handler
+{
+    IHandler_RecognitionResult IHandler_RecognitionResult_iface;
+    LONG ref;
+};
+
+static inline struct recognition_result_handler *impl_from_IHandler_RecognitionResult( IHandler_RecognitionResult *iface )
+{
+    return CONTAINING_RECORD(iface, struct recognition_result_handler, IHandler_RecognitionResult_iface);
+}
+
+HRESULT WINAPI recognition_result_handler_QueryInterface( IHandler_RecognitionResult *iface, REFIID iid, void **out )
+{
+    if (IsEqualGUID(iid, &IID_IUnknown) ||
+        IsEqualGUID(iid, &IID_IHandler_RecognitionResult))
+    {
+        IUnknown_AddRef(iface);
+        *out = iface;
+        return S_OK;
+    }
+
+    trace("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+ULONG WINAPI recognition_result_handler_AddRef( IHandler_RecognitionResult *iface )
+{
+    struct recognition_result_handler *impl = impl_from_IHandler_RecognitionResult(iface);
+    ULONG ref = InterlockedIncrement(&impl->ref);
+    return ref;
+}
+
+ULONG WINAPI recognition_result_handler_Release( IHandler_RecognitionResult *iface )
+{
+    struct recognition_result_handler *impl = impl_from_IHandler_RecognitionResult(iface);
+    ULONG ref = InterlockedDecrement(&impl->ref);
+    return ref;
+}
+
+HRESULT WINAPI recognition_result_handler_Invoke( IHandler_RecognitionResult *iface,
+                                                  ISpeechContinuousRecognitionSession *sender,
+                                                  ISpeechContinuousRecognitionResultGeneratedEventArgs *args )
+{
+    trace("iface %p, sender %p, args %p.\n", iface, sender, args);
+    return S_OK;
+}
+
+static const struct IHandler_RecognitionResultVtbl recognition_result_handler_vtbl =
+{
+    /* IUnknown methods */
+    recognition_result_handler_QueryInterface,
+    recognition_result_handler_AddRef,
+    recognition_result_handler_Release,
+    /* ITypedEventHandler<SpeechContinuousRecognitionSession*, SpeechContinuousRecognitionResultGeneratedEventArgs* > methods */
+    recognition_result_handler_Invoke
+};
+
+static HRESULT WINAPI recognition_result_handler_create_static( struct recognition_result_handler *impl )
+{
+    impl->IHandler_RecognitionResult_iface.lpVtbl = &recognition_result_handler_vtbl;
+    impl->ref = 1;
+
+    return S_OK;
+}
+
 struct iterator_hstring
 {
     IIterator_HSTRING IIterator_HSTRING_iface;
@@ -192,7 +264,7 @@ static const struct IIterator_HSTRINGVtbl iterator_hstring_vtbl =
     iterator_hstring_GetMany
 };
 
-static HRESULT WINAPI iterator_hstring_create_static(struct iterator_hstring *impl, HSTRING *strings, UINT32 size)
+static HRESULT WINAPI iterator_hstring_create_static( struct iterator_hstring *impl, HSTRING *strings, UINT32 size )
 {
     impl->IIterator_HSTRING_iface.lpVtbl = &iterator_hstring_vtbl;
     impl->ref = 1;
@@ -294,7 +366,7 @@ static const struct IIterable_HSTRINGVtbl iterable_hstring_vtbl =
     iterable_hstring_First
 };
 
-static HRESULT WINAPI iterable_hstring_create_static(struct iterable_hstring *impl, struct iterator_hstring *iterator)
+static HRESULT WINAPI iterable_hstring_create_static( struct iterable_hstring *impl, struct iterator_hstring *iterator )
 {
     impl->IIterable_HSTRING_iface.lpVtbl = &iterable_hstring_vtbl;
     impl->ref = 1;
@@ -579,6 +651,8 @@ static void test_SpeechRecognizer(void)
     IInspectable *inspectable = NULL;
     IClosable *closable = NULL;
     ILanguage *language = NULL;
+    struct recognition_result_handler result_handler;
+    EventRegistrationToken token = { .value = 0 };
     HSTRING hstr, hstr_lang;
     HRESULT hr;
     LONG ref;
@@ -654,6 +728,18 @@ static void test_SpeechRecognizer(void)
         check_refcount(session, 2);
         check_refcount(inspectable, 3);
 
+        hr = ISpeechContinuousRecognitionSession_add_ResultGenerated(session, NULL, &token);
+        ok(hr == E_INVALIDARG, "ISpeechContinuousRecognitionSession_add_ResultGenerated failed, hr %#lx.\n", hr);
+
+        token.value = 0xdeadbeef;
+        recognition_result_handler_create_static(&result_handler);
+        hr = ISpeechContinuousRecognitionSession_add_ResultGenerated(session, &result_handler.IHandler_RecognitionResult_iface, &token);
+        ok(hr == S_OK, "ISpeechContinuousRecognitionSession_add_ResultGenerated failed, hr %#lx.\n", hr);
+        ok(token.value != 0xdeadbeef, "Got unexpexted token: %#I64x.\n", token.value);
+
+        hr = ISpeechContinuousRecognitionSession_remove_ResultGenerated(session, token);
+        ok(hr == S_OK, "ISpeechContinuousRecognitionSession_remove_ResultGenerated failed, hr %#lx.\n", hr);
+
         ref = ISpeechContinuousRecognitionSession_Release(session);
         ok(ref == 1, "Got unexpected ref %lu.\n", ref);
 
diff --git a/include/windows.foundation.idl b/include/windows.foundation.idl
index 27bec63b0ef..87aaac0f160 100644
--- a/include/windows.foundation.idl
+++ b/include/windows.foundation.idl
@@ -171,6 +171,7 @@ namespace Windows {
             interface Windows.Foundation.AsyncOperationCompletedHandler<boolean>;
             interface Windows.Foundation.IAsyncOperation<boolean>;
             interface Windows.Foundation.IReference<INT32>;
+            interface Windows.Foundation.TypedEventHandler<IInspectable *, IInspectable *>;
         }
     }
 }
-- 
2.35.1




More information about the wine-devel mailing list