// SPDX-License-Identifier: LGPL-2.1-or-later #define COBJMACROS #include #include #include #include #include #include #ifdef WINE_ACCESS_PRISM_FIXTURE #include #define PRISM_STATIC #include #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; }