Files
wine-access/tests/notification_fixture.c

116 lines
4.6 KiB
C

// 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;
}