// SPDX-License-Identifier: LGPL-2.1-or-later #include #include #include #include #include "../menu_bridge.h" static BOOL editor_mode, stalled, getter_mode, background_mode, popup_mode; static HWND editor, button, provider; static WNDPROC native_provider_proc; static unsigned step; static const WCHAR *cls_name = L"WineEventPopupFixture"; static void marker(const WCHAR *variable) { WCHAR path[4096]; if (!GetEnvironmentVariableW(variable, path, 4096)) return; HANDLE file = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file != INVALID_HANDLE_VALUE) CloseHandle(file); } static void stall_provider() { if (stalled) return; stalled = TRUE; marker(L"WINE_ACCESS_STALL_READY"); Sleep(8000); marker(L"WINE_ACCESS_STALL_DONE"); } static LRESULT CALLBACK property_proc(HWND window, UINT message, WPARAM wp, LPARAM lp) { if (message == WM_GETTEXT || message == WM_GETTEXTLENGTH) stall_provider(); return CallWindowProcW(native_provider_proc, window, message, wp, lp); } static LRESULT CALLBACK window_proc(HWND window, UINT message, WPARAM wp, LPARAM lp) { if (message == WM_GETOBJECT && (LONG)lp == OBJID_CLIENT && !editor_mode && !stalled) { if (getter_mode) return DefWindowProcW(window, message, wp, lp); stall_provider(); } if (message == WM_TIMER) { if (!editor_mode) { KillTimer(window, 1); if (getter_mode) { SetFocus(provider); NotifyWinEvent(EVENT_OBJECT_FOCUS, provider, OBJID_CLIENT, CHILDID_SELF); } else NotifyWinEvent(EVENT_OBJECT_FOCUS, window, OBJID_CLIENT, CHILDID_SELF); } else if (++step == 1) { SetForegroundWindow(window); SetFocus(editor); PostMessageW(editor, WM_CHAR, 'a', 0); PostMessageW(editor, WM_CHAR, 'a', 0); } else { KillTimer(window, 1); WCHAR text[16]; DWORD start, end; GetWindowTextW(editor, text, 16); SendMessageW(editor, EM_GETSEL, (WPARAM)&start, (LPARAM)&end); if (lstrcmpW(text, L"aa") || start != 2 || end != 2) ExitProcess(2); marker(L"WINE_ACCESS_EDITOR_CHECKED"); SetFocus(button); NotifyWinEvent(EVENT_OBJECT_FOCUS, button, OBJID_CLIENT, CHILDID_SELF); if (popup_mode) { HWND popup = CreateWindowExW(WS_EX_NOACTIVATE, cls_name, L"Owned popup provider", WS_POPUP | WS_VISIBLE, 200, 200, 200, 80, window, NULL, GetModuleHandleW(NULL), NULL); // A bare X server can activate a newly mapped Wine popup despite // WS_EX_NOACTIVATE. Establish the intended owner focus explicitly // before testing its independent accessible focus notification. SetForegroundWindow(window); SetFocus(button); NotifyWinEvent(EVENT_OBJECT_FOCUS, popup, OBJID_CLIENT, CHILDID_SELF); if (GetForegroundWindow() != window || GetFocus() != button) { fprintf(stderr, "popup focus changed: foreground=%p expected=%p focus=%p expected=%p\n", GetForegroundWindow(), window, GetFocus(), button); ExitProcess(4); } } } return 0; } if (message == WM_DESTROY) { PostQuitMessage(0); return 0; } return DefWindowProcW(window, message, wp, lp); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous, LPSTR args, int show) { (void)previous; (void)show; if (strstr(args, "quit-helper")) { HWND bridge = FindWindowW(WINE_ACCESS_MENU_CLASS, NULL); DWORD thread = GetWindowThreadProcessId(bridge, NULL); return thread && PostThreadMessageW(thread, WM_QUIT, 0, 0) ? 0 : 3; } if (FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) return 5; editor_mode = strstr(args, "editor") != NULL; getter_mode = strstr(args, "getter") != NULL; background_mode = strstr(args, "background") != NULL; popup_mode = strstr(args, "popup") != NULL; WNDCLASSW cls = {}; cls.hInstance = instance; cls.lpfnWndProc = window_proc; cls.lpszClassName = L"WineEventStallFixture"; RegisterClassW(&cls); HWND window = CreateWindowW(cls.lpszClassName, editor_mode ? L"Responsive editor fixture" : L"Stalled provider fixture", WS_OVERLAPPEDWINDOW, 100, 100, 500, 300, NULL, NULL, instance, NULL); if (editor_mode) { editor = CreateWindowW(L"Edit", L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 10, 10, 300, 30, window, NULL, instance, NULL); button = CreateWindowW(L"Button", L"Responsive button", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 10, 60, 200, 30, window, NULL, instance, NULL); } if (getter_mode) { provider = CreateWindowW(L"Static", L"Stalled property provider", WS_CHILD | WS_VISIBLE, 10, 10, 300, 30, window, NULL, instance, NULL); native_provider_proc = reinterpret_cast(SetWindowLongPtrW(provider, GWLP_WNDPROC, reinterpret_cast(property_proc))); } WNDCLASSW popupClass = cls; popupClass.lpszClassName = cls_name; popupClass.lpfnWndProc = DefWindowProcW; RegisterClassW(&popupClass); ShowWindow(window, background_mode ? SW_SHOWNOACTIVATE : SW_SHOW); if (!background_mode) SetForegroundWindow(window); if (editor_mode) SetFocus(editor); SetTimer(window, 1, 500, NULL); MSG message; while (GetMessageW(&message, NULL, 0, 0) > 0) { TranslateMessage(&message); DispatchMessageW(&message); } CoUninitialize(); return 0; }