Updates for when prism doesn't behave.
This commit is contained in:
1 parent
0d9cc2651d
commit
6e92ad13d1
12 files changed
+415
-5
No files matched your search
@@ -0,0 +1,115 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#define COBJMACROS
|
||||
#include <windows.h>
|
||||
#include <oleauto.h>
|
||||
#include <uiautomationcore.h>
|
||||
#include <uiautomationcoreapi.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef WINE_ACCESS_PRISM_FIXTURE
|
||||
#include <shellapi.h>
|
||||
#define PRISM_STATIC
|
||||
#include <prism.h>
|
||||
#define LOAD(name) static __typeof__(&name) p_##name
|
||||
LOAD(prism_config_init); LOAD(prism_init); LOAD(prism_registry_acquire_best);
|
||||
LOAD(prism_backend_name); LOAD(prism_backend_speak); LOAD(prism_shutdown);
|
||||
LOAD(prism_backend_stop);
|
||||
static PrismContext *context;
|
||||
static PrismBackend *backend;
|
||||
#endif
|
||||
|
||||
static HWND window;
|
||||
static int step = -2;
|
||||
|
||||
static void notify(const WCHAR *text, enum NotificationProcessing processing) {
|
||||
#ifdef WINE_ACCESS_PRISM_FIXTURE
|
||||
char utf8[20000];
|
||||
WideCharToMultiByte(CP_UTF8, 0, text, -1, utf8, sizeof(utf8), NULL, NULL);
|
||||
BOOL interrupt = processing == NotificationProcessing_ImportantMostRecent ||
|
||||
processing == NotificationProcessing_ImportantAll;
|
||||
// Prism rejects empty speak text; its stop command emits an empty important
|
||||
// UIA notification instead.
|
||||
PrismError result = utf8[0] ? p_prism_backend_speak(backend, utf8, interrupt) :
|
||||
interrupt ? p_prism_backend_stop(backend) : PRISM_OK;
|
||||
if (result != PRISM_OK) ExitProcess(4);
|
||||
#else
|
||||
BSTR displayString = SysAllocString(text);
|
||||
BSTR activity = SysAllocString(L"Fixture");
|
||||
IRawElementProviderSimple *provider = NULL;
|
||||
if (FAILED(UiaHostProviderFromHwnd(window, &provider)) || !provider) ExitProcess(2);
|
||||
HRESULT result = UiaRaiseNotificationEvent(provider, NotificationKind_ActionCompleted,
|
||||
processing, displayString, activity);
|
||||
IRawElementProviderSimple_Release(provider);
|
||||
SysFreeString(displayString);
|
||||
SysFreeString(activity);
|
||||
if (FAILED(result)) ExitProcess(3);
|
||||
#endif
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK window_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) {
|
||||
if (message == WM_TIMER) {
|
||||
// Real UIA calls through normal imports, reached by the global native hook.
|
||||
switch (++step) {
|
||||
case -1: case 0: break;
|
||||
case 1: notify(L"Menu caf\u00e9 \U0001f600", NotificationProcessing_All); break;
|
||||
case 2: notify(L"Second item", NotificationProcessing_ImportantMostRecent); break;
|
||||
case 3: notify(L"", NotificationProcessing_ImportantMostRecent); break;
|
||||
case 4: notify(L"", NotificationProcessing_All); break;
|
||||
case 5: {
|
||||
WCHAR longText[4100];
|
||||
for (UINT i = 0; i < ARRAYSIZE(longText) - 1; ++i) longText[i] = L'x';
|
||||
longText[4094] = 0xd83d; longText[4095] = 0xde00;
|
||||
longText[ARRAYSIZE(longText) - 1] = 0;
|
||||
notify(longText, NotificationProcessing_All);
|
||||
break;
|
||||
}
|
||||
case 6: ShowWindow(hwnd, SW_HIDE); break;
|
||||
case 7: notify(L"Background must stay silent", NotificationProcessing_ImportantAll); break;
|
||||
default: DestroyWindow(hwnd); break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (message == WM_DESTROY) { PostQuitMessage(0); return 0; }
|
||||
return DefWindowProcW(hwnd, message, wp, lp);
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous, LPSTR args, int show) {
|
||||
(void)previous; (void)args; (void)show;
|
||||
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||||
WNDCLASSW cls = {0};
|
||||
cls.hInstance = instance; cls.lpfnWndProc = window_proc;
|
||||
cls.lpszClassName = L"WineAccessNotificationFixture";
|
||||
RegisterClassW(&cls);
|
||||
window = CreateWindowW(cls.lpszClassName, L"Notification fixture", WS_OVERLAPPEDWINDOW,
|
||||
10, 10, 300, 200, NULL, NULL, instance, NULL);
|
||||
if (!window) return 1;
|
||||
ShowWindow(window, SW_SHOW); SetForegroundWindow(window);
|
||||
#ifdef WINE_ACCESS_PRISM_FIXTURE
|
||||
int argumentCount = 0;
|
||||
WCHAR **arguments = CommandLineToArgvW(GetCommandLineW(), &argumentCount);
|
||||
HMODULE module = argumentCount == 2 ? LoadLibraryW(arguments[1]) : NULL;
|
||||
LocalFree(arguments);
|
||||
if (!module) return 5;
|
||||
#undef LOAD
|
||||
#define LOAD(name) do { FARPROC address = GetProcAddress(module, #name); \
|
||||
memcpy(&p_##name, &address, sizeof(p_##name)); if (!p_##name) return 6; } while (0)
|
||||
LOAD(prism_config_init); LOAD(prism_init); LOAD(prism_registry_acquire_best);
|
||||
LOAD(prism_backend_name); LOAD(prism_backend_speak); LOAD(prism_shutdown);
|
||||
LOAD(prism_backend_stop);
|
||||
PrismConfig config = p_prism_config_init();
|
||||
context = p_prism_init(&config);
|
||||
if (!context) return 7;
|
||||
backend = p_prism_registry_acquire_best(context);
|
||||
if (!backend || strcmp(p_prism_backend_name(backend), "UIA")) return 8;
|
||||
#endif
|
||||
SetTimer(window, 1, 500, NULL);
|
||||
MSG message;
|
||||
while (GetMessageW(&message, NULL, 0, 0) > 0) {
|
||||
TranslateMessage(&message); DispatchMessageW(&message);
|
||||
}
|
||||
#ifdef WINE_ACCESS_PRISM_FIXTURE
|
||||
p_prism_shutdown(context);
|
||||
#endif
|
||||
CoUninitialize();
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user