diff options
Diffstat (limited to 'utils/sapi51/include/speventq.h')
| -rw-r--r-- | utils/sapi51/include/speventq.h | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/utils/sapi51/include/speventq.h b/utils/sapi51/include/speventq.h new file mode 100644 index 0000000..b0447dc --- /dev/null +++ b/utils/sapi51/include/speventq.h @@ -0,0 +1,605 @@ +/******************************************************************************* +* SPEventQ.h * +*------------* +* Description: +* This is the header file for the SAPI5 event queue implementation. +*------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +*******************************************************************************/ +#ifndef SPEventQ_h +#define SPEventQ_h + +#ifndef SPHelper_h +#include <SPHelper.h> +#endif + +#ifndef SPCollec_h +#include <SPCollec.h> +#endif + +//=== Inline helpers for copying and deleting events ============================ + + +//=== Class definition ========================================================== + +class CSpEventNode : public CSpEvent +{ +public: + CSpEventNode * m_pNext; + static LONG Compare(const CSpEventNode * p1, const CSpEventNode *p2) + { + // Assumes offsets DO or DO NOT reset when stream number changes + if (p1->ulStreamNum < p2->ulStreamNum) + { + return -1; + } + else if (p1->ulStreamNum > p2->ulStreamNum) + { + return 1; + } + else if (p1->ullAudioStreamOffset < p2->ullAudioStreamOffset) + { + return -1; + } + else if (p1->ullAudioStreamOffset > p2->ullAudioStreamOffset) + { + return 1; + } + return 0; + } +}; + + +typedef CSpBasicQueue<CSpEventNode, TRUE, TRUE> CSpEventList; + +#define DECLARE_SPNOTIFYSOURCE_METHODS(T) \ +STDMETHODIMP SetNotifySink(ISpNotifySink * pNotifySink) \ +{ return T._SetNotifySink(pNotifySink); } \ +STDMETHODIMP SetNotifyWindowMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) \ +{ return T._SetNotifyWindowMessage(hWnd, Msg, wParam, lParam); } \ +STDMETHODIMP SetNotifyCallbackFunction(SPNOTIFYCALLBACK * pfnCallback, WPARAM wParam, LPARAM lParam) \ +{ return T._SetNotifyCallbackFunction(pfnCallback, wParam, lParam); } \ +STDMETHODIMP SetNotifyCallbackInterface(ISpNotifyCallback * pSpCallback, WPARAM wParam, LPARAM lParam) \ +{ return T._SetNotifyCallbackInterface(pSpCallback, wParam, lParam); } \ +STDMETHODIMP SetNotifyWin32Event() \ +{ return T._SetNotifyWin32Event(); } \ +STDMETHODIMP WaitForNotifyEvent(DWORD dwMilliseconds) \ +{ return T._WaitForNotifyEvent(dwMilliseconds); } \ +STDMETHODIMP_(HANDLE) GetNotifyEventHandle() \ +{ return T._GetNotifyEventHandle(); } + +#define DECLARE_SPEVENTSOURCE_METHODS(T) \ +DECLARE_SPNOTIFYSOURCE_METHODS(T) \ +STDMETHODIMP SetInterest(ULONGLONG ullEventInterest, ULONGLONG ullQueuedInterest) \ +{ return T._SetInterest(ullEventInterest, ullQueuedInterest); } \ +STDMETHODIMP GetEvents(ULONG ulCount, SPEVENT* pEventArray, ULONG * pulFetched) \ +{ return T._GetEvents(ulCount, pEventArray, pulFetched); } \ +STDMETHODIMP GetInfo(SPEVENTSOURCEINFO *pInfo) \ +{ return T._GetInfo(pInfo); } + + + +class CSpEventSource +{ + public: + CSpEventSource(CComObjectRootEx<CComMultiThreadModel> * pParent) : + m_pParent(pParent) + { + m_ullEventInterest = 0; m_ullQueuedInterest = 0; + m_ulStreamNum = 0; + } + HRESULT _SetNotifySink(ISpNotifySink * pNotifySink); + HRESULT _SetNotifyWindowMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + HRESULT _SetNotifyCallbackFunction(SPNOTIFYCALLBACK * pfnCallback, WPARAM wParam, LPARAM lParam); + HRESULT _SetNotifyCallbackInterface(ISpNotifyCallback * pSpCallback, WPARAM wParam, LPARAM lParam); + HRESULT _SetNotifyWin32Event(); + HRESULT _WaitForNotifyEvent(DWORD dwMilliseconds); + HANDLE _GetNotifyEventHandle(); + + HRESULT _SetInterest(ULONGLONG ullEventInterest , ULONGLONG ullQueuedInterest); + HRESULT _GetEvents( ULONG ulCount, SPEVENT* pEventArray, ULONG * pulFetched ); + HRESULT _GetInfo(SPEVENTSOURCEINFO *pInfo ); + + /*--- Non interface methods ---*/ + HRESULT _CompleteEvents( ULONGLONG ullPos = 0xFFFFFFFFFFFFFFFF ); + inline void _MoveAllToFreeList(CSpEventList * pList); + inline void _RemoveAllEvents(); + inline HRESULT _AddEvent(const SPEVENT & Event); + inline HRESULT _AddEvents(const SPEVENT* pEventArray, ULONG ulCount); + inline HRESULT _DeserializeAndAddEvent(const BYTE * pBuffer, ULONG * pcbUsed); + inline HRESULT _GetStreamNumber(const ULONGLONG ullAudioOffset, ULONG *pulStreamNum); + //=== Data members ============================== + public: + ULONGLONG m_ullEventInterest; + ULONGLONG m_ullQueuedInterest; + ULONG m_ulStreamNum; + CSpEventList m_PendingList; + CSpEventList m_CompletedList; + CSpEventList m_FreeList; + CComPtr<ISpNotifySink> m_cpNotifySink; + CComPtr<ISpNotifyTranslator> m_cpEventTranslator; // If non-NULL then Win32 events being used + CComObjectRootEx<CComMultiThreadModel> * m_pParent; + CComAutoCriticalSection m_NotifyObjChangeCrit; // Critical section used to make sure that + // the notify object (m_cpNotifySink) not changed + // while waiting on it. + +}; + + +// +//=== Inlines ========================================================= +// + +// +// WARNING: If this logic changes, you will need to change the logic in SetNotifyWin32Event also. +// +inline HRESULT CSpEventSource::_SetNotifySink(ISpNotifySink * pNotifySink) +{ + if (SP_IS_BAD_OPTIONAL_INTERFACE_PTR(pNotifySink)) + { + return E_INVALIDARG; + } + else + { + m_pParent->Lock(); + m_NotifyObjChangeCrit.Lock(); + m_cpEventTranslator.Release(); + m_cpNotifySink = pNotifySink; + if (m_cpNotifySink && m_CompletedList.GetHead()) + { + m_cpNotifySink->Notify(); + } + m_NotifyObjChangeCrit.Unlock(); + m_pParent->Unlock(); + return S_OK; + } +} + +/**************************************************************************** +* CSpEventSource::_SetNotifyWindowMessage * +*-----------------------------------------* +* Description: +* +* Returns: +* +********************************************************************* RAL ***/ + +inline HRESULT CSpEventSource::_SetNotifyWindowMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + SPDBG_FUNC("CSpEventSource::_SetNotifyWindowMessage"); + HRESULT hr = S_OK; + CComPtr<ISpNotifyTranslator> cpTranslator; + hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); + if (SUCCEEDED(hr)) + { + hr = cpTranslator->InitWindowMessage(hWnd, Msg, wParam, lParam); + } + if (SUCCEEDED(hr)) + { + hr = _SetNotifySink(cpTranslator); + } + return hr; +} +/**************************************************************************** +* CSpEventSource::_SetNotifyCallbackFunction * +*--------------------------------------------* +* Description: +* +* Returns: +* +********************************************************************* RAL ***/ + +inline HRESULT CSpEventSource::_SetNotifyCallbackFunction(SPNOTIFYCALLBACK * pfnCallback, WPARAM wParam, LPARAM lParam) +{ + SPDBG_FUNC("CSpEventSource::_SetNotifyCallbackFunction"); + HRESULT hr = S_OK; + CComPtr<ISpNotifyTranslator> cpTranslator; + hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); + if (SUCCEEDED(hr)) + { + hr = cpTranslator->InitCallback(pfnCallback, wParam, lParam); + } + if (SUCCEEDED(hr)) + { + hr = _SetNotifySink(cpTranslator); + } + return hr; +} +/**************************************************************************** +* CSpEventSource::_SetNotifyCallbackInterface * +*---------------------------------------------* +* Description: +* +* Returns: +* +********************************************************************* RAL ***/ + +inline HRESULT CSpEventSource::_SetNotifyCallbackInterface(ISpNotifyCallback * pSpCallback, WPARAM wParam, LPARAM lParam) +{ + SPDBG_FUNC("CSpEventSource::_SetNotifyCallbackInterface"); + HRESULT hr = S_OK; + CComPtr<ISpNotifyTranslator> cpTranslator; + hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); + if (SUCCEEDED(hr)) + { + hr = cpTranslator->InitSpNotifyCallback(pSpCallback, wParam, lParam); + } + if (SUCCEEDED(hr)) + { + hr = _SetNotifySink(cpTranslator); + } + return hr; +} +/**************************************************************************** +* CSpEventSource::_SetNotifyWin32Event * +*--------------------------------------* +* Description: +* +* Returns: +* +********************************************************************* RAL ***/ + +inline HRESULT CSpEventSource::_SetNotifyWin32Event(void) +{ + SPDBG_FUNC("CSpEventSource::_SetNotifyWin32Event"); + HRESULT hr = S_OK; + CComPtr<ISpNotifyTranslator> cpTranslator; + hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); + if (SUCCEEDED(hr)) + { + hr = cpTranslator->InitWin32Event(NULL, TRUE); + } + if (SUCCEEDED(hr)) + { + // + // In this case we do NOT call _SetNotify sink since we want to set the cpEventTranslator + // + m_pParent->Lock(); + m_NotifyObjChangeCrit.Lock(); + m_cpEventTranslator = cpTranslator; + m_cpNotifySink = cpTranslator; + if (m_cpNotifySink && m_CompletedList.GetHead()) + { + m_cpNotifySink->Notify(); + } + m_NotifyObjChangeCrit.Unlock(); + m_pParent->Unlock(); + } + return hr; +} +/**************************************************************************** +* CSpEventSource::_WaitForNotifyEvent * +*-------------------------------------* +* Description: +* +* Returns: +* +********************************************************************* RAL ***/ + +inline HRESULT CSpEventSource::_WaitForNotifyEvent(DWORD dwMilliseconds) +{ + SPDBG_FUNC("CSpEventSource::_WaitForNotifyEvent"); + HRESULT hr = S_OK; + m_NotifyObjChangeCrit.Lock(); + if (m_cpEventTranslator) + { + hr = m_cpEventTranslator->Wait(dwMilliseconds); + } + else + { + if (m_cpNotifySink) + { + hr = SPERR_ALREADY_INITIALIZED; + } + else + { + hr = _SetNotifyWin32Event(); + if (SUCCEEDED(hr)) + { + hr = m_cpEventTranslator->Wait(dwMilliseconds); + } + } + } + m_NotifyObjChangeCrit.Unlock(); + return hr; +} +/**************************************************************************** +* CSpEventSource::_GetNotifyEventHandle * +*---------------------------------------* +* Description: +* +* Returns: +* +********************************************************************* RAL ***/ + +inline HANDLE CSpEventSource::_GetNotifyEventHandle() +{ + HANDLE h = NULL; + SPDBG_FUNC("CSpEventSource::_GetNotifyEventHandle"); + m_NotifyObjChangeCrit.Lock(); + if (!m_cpNotifySink) + { + _SetNotifyWin32Event(); + } + if (m_cpEventTranslator) + { + h = m_cpEventTranslator->GetEventHandle(); + } + m_NotifyObjChangeCrit.Unlock(); + return h; +} + + +inline HRESULT CSpEventSource::_SetInterest( ULONGLONG ullEventInterest, ULONGLONG ullQueuedInterest ) +{ + HRESULT hr = S_OK; + m_pParent->Lock(); + + if(ullEventInterest && SPFEI_FLAGCHECK != (ullEventInterest & SPFEI_FLAGCHECK)) + { + hr = E_INVALIDARG; + } + else if(ullQueuedInterest && SPFEI_FLAGCHECK != (ullQueuedInterest & SPFEI_FLAGCHECK)) + { + hr = E_INVALIDARG; + } + else if ((ullQueuedInterest | ullEventInterest) != ullEventInterest) + { + hr = E_INVALIDARG; + } + else + { + m_ullEventInterest = ullEventInterest; + m_ullQueuedInterest = ullQueuedInterest; + } + m_pParent->Unlock(); + return hr; +} + + +// +// Same as AddEvents except: No param validation, and caller must take the critical section +// prior to calling. +// +inline HRESULT CSpEventSource::_AddEvents( const SPEVENT* pEventArray, ULONG ulCount ) +{ + HRESULT hr = S_OK; + for( ULONG i = 0; i < ulCount && SUCCEEDED(hr = _AddEvent(pEventArray[i])); ++i ) {} + return hr; +} + +inline HRESULT CSpEventSource::_AddEvent(const SPEVENT & Event) +{ + SPDBG_ASSERT(Event.eEventId < 64); + SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_UNDEFINED || + Event.elParamType == SPET_LPARAM_IS_TOKEN || + Event.elParamType == SPET_LPARAM_IS_OBJECT || + Event.elParamType == SPET_LPARAM_IS_POINTER || + Event.elParamType == SPET_LPARAM_IS_STRING); +#ifdef _DEBUG + if (Event.eEventId == SPEI_VOICE_CHANGE) + { + SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_TOKEN); + } + else if (Event.eEventId == SPEI_RECOGNITION || Event.eEventId == SPEI_FALSE_RECOGNITION || Event.eEventId == SPEI_HYPOTHESIS) + { + SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_OBJECT); + } + else if (Event.eEventId ==SPEI_REQUEST_UI || Event.eEventId == SPEI_TTS_BOOKMARK) + { + SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_STRING); + } +#endif + + if ( (1i64 << Event.eEventId) & m_ullEventInterest ) + { + CSpEventNode *pNode = m_FreeList.RemoveHead(); + if (pNode == NULL) + { + pNode = new CSpEventNode(); + if (pNode == NULL) + { + return E_OUTOFMEMORY; + } + } + pNode->CopyFrom(&Event); + m_PendingList.InsertSorted(pNode); + } + return S_OK; +} + +inline HRESULT CSpEventSource:: + _DeserializeAndAddEvent(const BYTE *pBuffer, ULONG * pcbUsed) +{ + HRESULT hr = S_OK; + const SPEVENT * pSrcEvent = (const SPEVENT *)pBuffer; + SPDBG_ASSERT(pSrcEvent->eEventId < 64); + if ( (1i64 << pSrcEvent->eEventId) & m_ullEventInterest ) + { + CSpEventNode *pNode = m_FreeList.RemoveHead(); + if (pNode == NULL) + { + pNode = new CSpEventNode(); + if (pNode == NULL) + { + hr = E_OUTOFMEMORY; + } + } + if (SUCCEEDED(hr)) + { + hr = pNode->Deserialize(((const SPSERIALIZEDEVENT64 *)(pBuffer)), pcbUsed); + if (SUCCEEDED(hr)) + { + m_PendingList.InsertSorted(pNode); + } + else + { + m_FreeList.InsertHead(pNode); + } + } + } + else + { +// WCE compiler does not work propertly with template +#ifndef _WIN32_WCE + *pcbUsed = SpEventSerializeSize<SPSERIALIZEDEVENT64>(pSrcEvent); +#else + *pcbUsed = SpEventSerializeSize(pSrcEvent, sizeof(SPSERIALIZEDEVENT64)); +#endif + } + return hr; +} + +inline HRESULT CSpEventSource::_GetEvents( ULONG ulCount, SPEVENT* pEventArray, ULONG *pulFetched ) +{ + HRESULT hr = S_OK; + m_pParent->Lock(); + if( SPIsBadWritePtr( pEventArray, sizeof( SPEVENT ) * ulCount ) || + SP_IS_BAD_OPTIONAL_WRITE_PTR(pulFetched) ) + { + hr = E_INVALIDARG; + } + else + { + ULONG ulCopied = 0; + ULONG ulRemaining = ulCount; + CSpEventNode * pCur = m_CompletedList.m_pHead; + CSpEventNode * pLastCopied = NULL; + while (ulRemaining && pCur) + { + pCur->Detach(pEventArray + ulCopied); + pLastCopied = pCur; + ulCopied++; + pCur = pCur->m_pNext; + ulRemaining--; + } + if (ulCopied) + { + if (m_FreeList.m_pHead == NULL) + { + m_FreeList.m_pTail = pLastCopied; + } + pLastCopied->m_pNext = m_FreeList.m_pHead; + m_FreeList.m_pHead = m_CompletedList.m_pHead; + m_CompletedList.m_pHead = pCur; + m_CompletedList.m_cElements -= ulCopied; + m_FreeList.m_cElements += ulCopied; + } + if (ulCopied < ulCount) + { + hr = S_FALSE; + } + if (pulFetched) + { + *pulFetched = ulCopied; + } + } + m_pParent->Unlock(); + return hr; +} + + +inline HRESULT CSpEventSource::_GetInfo( SPEVENTSOURCEINFO * pInfo ) +{ + HRESULT hr = S_OK; + m_pParent->Lock(); + if( SP_IS_BAD_WRITE_PTR( pInfo ) ) + { + hr = E_POINTER; + } + else + { + pInfo->ulCount = m_CompletedList.GetCount(); + pInfo->ullEventInterest = m_ullEventInterest; + pInfo->ullQueuedInterest= m_ullQueuedInterest; + } + m_pParent->Unlock(); + return hr; +} + + + +// +// The caller must call this function with the critical section owned +// +inline HRESULT CSpEventSource::_CompleteEvents( ULONGLONG ullPos ) +{ + HRESULT hr = S_OK; + + if (m_PendingList.m_pHead && m_PendingList.m_pHead->ullAudioStreamOffset <= ullPos) + { + BOOL bNotify = FALSE; + while (m_PendingList.m_pHead && + m_PendingList.m_pHead->ullAudioStreamOffset <= ullPos) + { + CSpEventNode *pNode = m_PendingList.RemoveHead(); + if(pNode->ulStreamNum != m_ulStreamNum) + { + m_ulStreamNum = pNode->ulStreamNum; + } + if ( (1i64 << pNode->eEventId) & m_ullEventInterest ) + { + bNotify = TRUE; + // + // NOTE: If we're forwarding events to an event sink then we'll only + // pay attention to the Interest flags. If we're going to notify, then + // we'll only queue completed events that the user has explicitly asked + // us to store as completed events. + // + if ( (1i64 << pNode->eEventId) & m_ullQueuedInterest ) + { + m_CompletedList.InsertSorted(pNode); + } + else + { + pNode->Clear(); + m_FreeList.InsertHead(pNode); + } + } + else + { + pNode->Clear(); + m_FreeList.InsertHead(pNode); + } + } + if (bNotify && m_cpNotifySink) + { + hr = m_cpNotifySink->Notify(); + } + } + return hr; +}; + + +inline void CSpEventSource::_MoveAllToFreeList(CSpEventList * pList) +{ + CSpEventNode * pNode; + while ((pNode = pList->RemoveHead()) != NULL) + { + pNode->Clear(); + m_FreeList.InsertHead(pNode); + } +} +inline void CSpEventSource::_RemoveAllEvents( ) +{ + m_pParent->Lock(); + + _MoveAllToFreeList(&m_CompletedList); + _MoveAllToFreeList(&m_PendingList); + m_pParent->Unlock(); +} + +inline HRESULT CSpEventSource::_GetStreamNumber(const ULONGLONG ullAudioOffset, ULONG *pulStreamNum) +{ + CSpEventNode *pNode = m_PendingList.m_pHead; + *pulStreamNum = m_ulStreamNum; + for(;pNode && pNode->ullAudioStreamOffset <= ullAudioOffset; pNode = pNode->m_pNext) + { + *pulStreamNum = pNode->ulStreamNum; + } + return S_OK; +} + + + +#endif //--- This must be the last line in this file |