How to do call UiaRaiseNotificationEvent on Windows
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*************************************************************************** | |
* Copyright 2019-2022 Leonard de Ruijter, James Teh - OSARA * | |
* Copyright 2017 The Qt Company Ltd. * | |
* Copyright (C) 2022 by Vadim Peretokin - vadim.peretokin@mudlet.org * | |
* * | |
* This program is free software; you can redistribute it and/or modify * | |
* it under the terms of the GNU General Public License as published by * | |
* the Free Software Foundation; either version 2 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 General Public License for more details. * | |
* * | |
* You should have received a copy of the GNU General Public License * | |
* along with this program; if not, write to the * | |
* Free Software Foundation, Inc., * | |
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | |
***************************************************************************/ | |
#ifndef ANNOUNCER_H | |
#define ANNOUNCER_H | |
#include <QAccessible> | |
#include <QAccessibleInterface> | |
#include <QAccessibleWidget> | |
#include <QLibrary> | |
#include <QObject> | |
#include <QWidget> | |
#if defined(Q_OS_LINUX) | |
class InvisibleNotification : public QWidget { | |
Q_OBJECT | |
public: | |
Q_DISABLE_COPY(InvisibleNotification) | |
explicit InvisibleNotification(QWidget *parent); | |
void setText(const QString &text); | |
QString text(); | |
private: | |
QString mText; | |
}; | |
// create a new class InvisibleStatusbar based on QWidget | |
class InvisibleStatusbar : public QWidget { | |
Q_OBJECT | |
public: | |
Q_DISABLE_COPY(InvisibleStatusbar) | |
explicit InvisibleStatusbar(QWidget *parent); | |
}; | |
class InvisibleAccessibleNotification : public QAccessibleWidget { | |
public: | |
explicit InvisibleAccessibleNotification(QWidget *w) | |
: QAccessibleWidget(w, QAccessible::Role::Notification) {} | |
private: | |
InvisibleNotification *notification() const; | |
protected: | |
QString text(QAccessible::Text t) const override; | |
}; | |
class FakeAccessibleStatusbar : public QAccessibleWidget { | |
public: | |
explicit FakeAccessibleStatusbar(QWidget *w) | |
: QAccessibleWidget(w, QAccessible::Role::StatusBar) {} | |
}; | |
#endif | |
class Announcer : public QWidget { | |
Q_OBJECT | |
public: | |
Q_DISABLE_COPY_MOVE(Announcer) | |
explicit Announcer(QWidget *parent = nullptr); | |
void announce(const QString text); | |
#if defined(Q_OS_LINUX) | |
static QAccessibleInterface *accessibleFactory(const QString &classname, | |
QObject *object) { | |
// mingw compilation breaks without this | |
#undef interface | |
QAccessibleInterface *interface = nullptr; | |
if (classname == QLatin1String("InvisibleNotification") && object && | |
object->isWidgetType()) { | |
interface = | |
new InvisibleAccessibleNotification(static_cast<QWidget *>(object)); | |
} else if (classname == QLatin1String("InvisibleStatusbar") && object && | |
object->isWidgetType()) { | |
interface = new FakeAccessibleStatusbar(static_cast<QWidget *>(object)); | |
} | |
return interface; | |
} | |
#endif | |
private: | |
#if defined(Q_OS_LINUX) | |
InvisibleNotification *notification; | |
InvisibleStatusbar *statusbar; | |
#endif | |
#if defined(Q_OS_WIN) | |
QScopedPointer<QLibrary> mpUiaLibrary; | |
bool initializeUia(); | |
class UiaProvider; | |
UiaProvider *uiaProvider{}; | |
#endif | |
}; | |
#endif // ANNOUNCER_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*************************************************************************** | |
* Copyright 2019-2022 Leonard de Ruijter, James Teh - OSARA * | |
* Copyright 2017 The Qt Company Ltd. * | |
* Copyright (C) 2022 by Vadim Peretokin - vadim.peretokin@mudlet.org * | |
* * | |
* This program is free software; you can redistribute it and/or modify * | |
* it under the terms of the GNU General Public License as published by * | |
* the Free Software Foundation; either version 2 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 General Public License for more details. * | |
* * | |
* You should have received a copy of the GNU General Public License * | |
* along with this program; if not, write to the * | |
* Free Software Foundation, Inc., * | |
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | |
***************************************************************************/ | |
#include "Announcer.h" | |
#include "uiawrapper.h" | |
#include "utils.h" | |
#include <QAccessible> | |
#include <QDebug> | |
#include <QLibrary> | |
#include <memory> | |
#include <ole2.h> | |
#include <tlhelp32.h> | |
#include <uiautomation.h> | |
#include <utility> | |
#include <oleacc.h> | |
#include <uiautomationclient.h> | |
#include <uiautomationcore.h> | |
#include <uiautomationcoreapi.h> | |
// mingw 7.30's uiautomationclient.h is outdated, lacks this define | |
#define UIA_CustomControlTypeId (50025) | |
// this class is largely inspired by OSARA's UiaProvider: | |
// https://github.com/jcsteh/osara/blob/master/src/uia.cpp | |
class Announcer::UiaProvider : public IRawElementProviderSimple { | |
public: | |
UiaProvider(_In_ HWND hwnd) : refCount(0), controlHWnd(hwnd) {} | |
ULONG STDMETHODCALLTYPE AddRef() { return InterlockedIncrement(&refCount); } | |
ULONG STDMETHODCALLTYPE Release() { | |
long val = InterlockedDecrement(&refCount); | |
if (val == 0) { | |
delete this; | |
} | |
return val; | |
} | |
HRESULT STDMETHODCALLTYPE QueryInterface(_In_ REFIID riid, | |
_Outptr_ void **ppInterface) { | |
if (ppInterface) { | |
return E_INVALIDARG; | |
} | |
if (riid == __uuidof(IUnknown)) { | |
*ppInterface = static_cast<IRawElementProviderSimple *>(this); | |
} else if (riid == __uuidof(IRawElementProviderSimple)) { | |
*ppInterface = static_cast<IRawElementProviderSimple *>(this); | |
} else { | |
*ppInterface = nullptr; | |
return E_NOINTERFACE; | |
} | |
(static_cast<IUnknown *>(*ppInterface))->AddRef(); | |
return S_OK; | |
} | |
HRESULT STDMETHODCALLTYPE | |
get_ProviderOptions(_Out_ ProviderOptions *pRetVal) { | |
if (!pRetVal) { | |
return E_INVALIDARG; | |
} | |
*pRetVal = static_cast<ProviderOptions>(ProviderOptions_ServerSideProvider | | |
ProviderOptions_UseComThreading); | |
return S_OK; | |
} | |
HRESULT STDMETHODCALLTYPE GetPatternProvider( | |
PATTERNID patternId, _Outptr_result_maybenull_ IUnknown **pRetVal) { | |
qDebug() << "UIA requested patternId " << patternId; | |
*pRetVal = NULL; | |
return S_OK; | |
} | |
HRESULT STDMETHODCALLTYPE GetPropertyValue(PROPERTYID propertyId, | |
_Out_ VARIANT *pRetVal) { | |
switch (propertyId) { | |
case UIA_ControlTypePropertyId: | |
// Stop Narrator from ever speaking this as a window | |
pRetVal->vt = VT_I4; | |
pRetVal->lVal = UIA_CustomControlTypeId; | |
break; | |
case UIA_IsControlElementPropertyId: | |
case UIA_IsContentElementPropertyId: | |
case UIA_IsKeyboardFocusablePropertyId: | |
pRetVal->vt = VT_BOOL; | |
pRetVal->boolVal = VARIANT_FALSE; | |
break; | |
case UIA_ProviderDescriptionPropertyId: | |
pRetVal->vt = VT_BSTR; | |
pRetVal->bstrVal = SysAllocString(L"Mudlet"); | |
break; | |
default: | |
pRetVal->vt = VT_EMPTY; | |
} | |
return S_OK; | |
} | |
HRESULT STDMETHODCALLTYPE | |
get_HostRawElementProvider(IRawElementProviderSimple **pRetVal) { | |
return UiaWrapper::self()->hostProviderFromHwnd(controlHWnd, pRetVal); | |
} | |
private: | |
virtual ~UiaProvider() {} | |
ULONG refCount; | |
HWND controlHWnd; | |
}; | |
bool Announcer::initializeUia() { | |
// Constructor initializes refcount to 0, assignment to a CComPtr | |
// takes it to 1. | |
uiaProvider = new UiaProvider((HWND)this->winId()); | |
// as we are not using CComPtr, ensure refcount is incremented to prevent the provider from being deleted early | |
uiaProvider->AddRef(); | |
return true; | |
} | |
Announcer::Announcer(QWidget *parent) : QWidget{parent} { initializeUia(); } | |
BSTR bStrFromQString(const QString &value) { | |
return SysAllocString(reinterpret_cast<const wchar_t *>(value.utf16())); | |
} | |
void Announcer::announce(const QString text) { | |
qDebug() << "announce" << text; | |
BSTR displayString = bStrFromQString(text); | |
BSTR activityId = bStrFromQString(qsl("Mudlet")); | |
UiaWrapper::self()->raiseNotificationEvent( | |
uiaProvider, NotificationKind_ItemAdded, NotificationProcessing_All, | |
displayString, activityId); | |
::SysFreeString(displayString); | |
::SysFreeString(activityId); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*************************************************************************** | |
* Copyright 2019-2022 Leonard de Ruijter, James Teh - OSARA * | |
* Copyright 2017 The Qt Company Ltd. * | |
* Copyright (C) 2022 by Vadim Peretokin - vadim.peretokin@mudlet.org * | |
* * | |
* This program is free software; you can redistribute it and/or modify * | |
* it under the terms of the GNU General Public License as published by * | |
* the Free Software Foundation; either version 2 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 General Public License for more details. * | |
* * | |
* You should have received a copy of the GNU General Public License * | |
* along with this program; if not, write to the * | |
* Free Software Foundation, Inc., * | |
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | |
***************************************************************************/ | |
#include "uiawrapper.h" | |
#include "utils.h" | |
#include <QDebug> | |
#include <QLibrary> | |
// this class is largely inspired from Qt's QWindowsUiaWrapper: | |
// https://github.com/qt/qtbase/blob/dev/src/gui/accessible/windows/apisupport/qwindowsuiawrapper.cpp | |
UiaWrapper::UiaWrapper() { | |
QLibrary uiaLibrary(qsl("UIAutomationCore")); | |
if (uiaLibrary.load()) { | |
m_pUiaRaiseNotificationEvent = | |
reinterpret_cast<PtrUiaRaiseNotificationEvent>( | |
uiaLibrary.resolve("UiaRaiseNotificationEvent")); | |
m_pUiaDisconnectAllProviders = | |
reinterpret_cast<PtrUiaDisconnectAllProviders>( | |
uiaLibrary.resolve("UiaDisconnectAllProviders")); | |
m_pUiaDisconnectProvider = reinterpret_cast<PtrUiaDisconnectProvider>( | |
uiaLibrary.resolve("UiaDisconnectProvider")); | |
m_pUiaHostProviderFromHwnd = reinterpret_cast<PtrUiaHostProviderFromHwnd>( | |
uiaLibrary.resolve("UiaHostProviderFromHwnd")); | |
m_pUiaClientsAreListening = reinterpret_cast<PtrUiaClientsAreListening>( | |
uiaLibrary.resolve("UiaClientsAreListening")); | |
} | |
} | |
UiaWrapper::~UiaWrapper(){}; | |
UiaWrapper *UiaWrapper::self() { | |
static UiaWrapper wrapper; | |
return &wrapper; | |
} | |
BOOL UiaWrapper::ready() { | |
return m_pUiaRaiseNotificationEvent && m_pUiaDisconnectAllProviders && | |
m_pUiaDisconnectProvider && m_pUiaHostProviderFromHwnd && | |
m_pUiaClientsAreListening; | |
} | |
HRESULT UiaWrapper::raiseNotificationEvent( | |
IRawElementProviderSimple *provider, NotificationKind notificationKind, | |
NotificationProcessing notificationProcessing, BSTR displayString, | |
BSTR activityId) { | |
if (!m_pUiaRaiseNotificationEvent) { | |
return UIA_E_NOTSUPPORTED; | |
} | |
qDebug() << "calling m_pUiaRaiseNotificationEvent" << provider; | |
return m_pUiaRaiseNotificationEvent(provider, notificationKind, | |
notificationProcessing, displayString, | |
activityId); | |
} | |
HRESULT UiaWrapper::disconnectAllProviders() { | |
if (!m_pUiaDisconnectAllProviders) { | |
return UIA_E_NOTSUPPORTED; | |
} | |
return m_pUiaDisconnectAllProviders(); | |
} | |
HRESULT UiaWrapper::disconnectProvider(IRawElementProviderSimple *pProvider) { | |
if (!m_pUiaDisconnectProvider) { | |
return UIA_E_NOTSUPPORTED; | |
} | |
return m_pUiaDisconnectProvider(pProvider); | |
} | |
BOOL UiaWrapper::clientsAreListening() { | |
if (!m_pUiaClientsAreListening) { | |
return FALSE; | |
} | |
return m_pUiaClientsAreListening(); | |
} | |
HRESULT | |
UiaWrapper::hostProviderFromHwnd(HWND hwnd, | |
IRawElementProviderSimple **ppProvider) { | |
if (!m_pUiaHostProviderFromHwnd) { | |
return UIA_E_NOTSUPPORTED; | |
} | |
return m_pUiaHostProviderFromHwnd(hwnd, ppProvider); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*************************************************************************** | |
* Copyright 2019-2022 Leonard de Ruijter, James Teh - OSARA * | |
* Copyright 2017 The Qt Company Ltd. * | |
* Copyright (C) 2022 by Vadim Peretokin - vadim.peretokin@mudlet.org * | |
* * | |
* This program is free software; you can redistribute it and/or modify * | |
* it under the terms of the GNU General Public License as published by * | |
* the Free Software Foundation; either version 2 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 General Public License for more details. * | |
* * | |
* You should have received a copy of the GNU General Public License * | |
* along with this program; if not, write to the * | |
* Free Software Foundation, Inc., * | |
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | |
***************************************************************************/ | |
#ifndef UIAWRAPPER_H | |
#define UIAWRAPPER_H | |
#include <memory> | |
#include <ole2.h> | |
#include <tlhelp32.h> | |
#include <uiautomation.h> | |
#include <utility> | |
#include <oleacc.h> | |
#include <uiautomationclient.h> | |
#include <uiautomationcore.h> | |
#include <uiautomationcoreapi.h> | |
enum NotificationProcessing { | |
NotificationProcessing_ImportantAll = 0, | |
NotificationProcessing_ImportantMostRecent = 1, | |
NotificationProcessing_All = 2, | |
NotificationProcessing_MostRecent = 3, | |
NotificationProcessing_CurrentThenMostRecent = 4 | |
}; | |
enum NotificationKind { | |
NotificationKind_ItemAdded = 0, | |
NotificationKind_ItemRemoved = 1, | |
NotificationKind_ActionCompleted = 2, | |
NotificationKind_ActionAborted = 3, | |
NotificationKind_Other = 4 | |
}; | |
class UiaWrapper { | |
UiaWrapper(); | |
virtual ~UiaWrapper(); | |
public: | |
static UiaWrapper *self(); | |
BOOL ready(); | |
LRESULT returnRawElementProvider(HWND hwnd, WPARAM wParam, LPARAM lParam, | |
IRawElementProviderSimple *el); | |
HRESULT raiseNotificationEvent(IRawElementProviderSimple *provider, | |
NotificationKind notificationKind, | |
NotificationProcessing notificationProcessing, | |
BSTR displayString, BSTR activityId); | |
HRESULT disconnectAllProviders(); | |
HRESULT disconnectProvider(IRawElementProviderSimple *pProvider); | |
HRESULT hostProviderFromHwnd(HWND hwnd, | |
IRawElementProviderSimple **ppProvider); | |
BOOL clientsAreListening(); | |
private: | |
typedef HRESULT(WINAPI *PtrUiaRaiseNotificationEvent)( | |
IRawElementProviderSimple *, NotificationKind, NotificationProcessing, | |
BSTR, BSTR); | |
typedef HRESULT(WINAPI *PtrUiaDisconnectAllProviders)(); | |
typedef HRESULT(WINAPI *PtrUiaDisconnectProvider)( | |
IRawElementProviderSimple *); | |
typedef HRESULT(WINAPI *PtrUiaHostProviderFromHwnd)( | |
HWND, IRawElementProviderSimple **); | |
typedef BOOL(WINAPI *PtrUiaClientsAreListening)(); | |
PtrUiaRaiseNotificationEvent m_pUiaRaiseNotificationEvent = nullptr; | |
PtrUiaDisconnectAllProviders m_pUiaDisconnectAllProviders = nullptr; | |
PtrUiaDisconnectProvider m_pUiaDisconnectProvider = nullptr; | |
PtrUiaHostProviderFromHwnd m_pUiaHostProviderFromHwnd = nullptr; | |
PtrUiaClientsAreListening m_pUiaClientsAreListening = nullptr; | |
}; | |
#endif // UIAWRAPPER_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment