// SPDX-License-Identifier: LGPL-2.1-or-later #define COBJMACROS #include #include #include static HMENU bar, file, edit, nested, context; static int step; static HWND marshaledControl; static HWND button; static LRESULT CALLBACK provider_proc(HWND window, UINT message, WPARAM wp, LPARAM lp) { if (message == WM_GETOBJECT && (LONG)lp == OBJID_CLIENT) { IAccessible *accessible = NULL; if (SUCCEEDED(CreateStdAccessibleObject(window, OBJID_CLIENT, &IID_IAccessible, (void **)&accessible))) { // Returning a marshaled object forces the separate helper to use COM, // rather than Wine's directly constructed native-control proxy. LRESULT result = LresultFromObject(&IID_IAccessible, wp, (IUnknown *)accessible); IAccessible_Release(accessible); return result; } } return DefWindowProcW(window, message, wp, lp); } static LRESULT CALLBACK window_proc(HWND window, UINT message, WPARAM wp, LPARAM lp) { if (message == WM_TIMER) { switch (++step) { case 1: SetFocus(button); // Enter File through the application's actual native menu loop. PostMessageW(window, WM_SYSCOMMAND, SC_KEYMENU, 'f'); break; case 2: case 3: PostMessageW(window, WM_KEYDOWN, VK_DOWN, 1); break; case 4: PostMessageW(window, WM_KEYDOWN, VK_RIGHT, 1); break; case 5: PostMessageW(window, WM_KEYDOWN, VK_ESCAPE, 1); break; case 6: PostMessageW(window, WM_KEYDOWN, VK_ESCAPE, 1); break; case 7: // Context menus use a separate HMENU, unrelated to the menu bar. TrackPopupMenu(context, TPM_LEFTALIGN, 200, 200, 0, window, NULL); break; case 8: PostMessageW(window, WM_KEYDOWN, VK_DOWN, 1); break; case 9: PostMessageW(window, WM_KEYDOWN, VK_ESCAPE, 1); break; case 10: AppendMenuW(file, MF_STRING | MF_CHECKED | MF_GRAYED, 104, L"Pinned && &offline\tCtrl+P"); /* fall through */ case 11: // Also exercise checked/disabled items and duplicate notifications. SendMessageW(window, WM_MENUSELECT, MAKEWPARAM(104, MF_CHECKED | MF_GRAYED), (LPARAM)file); break; case 12: SendMessageW(window, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0); break; case 13: SendMessageW(window, WM_MENUSELECT, MAKEWPARAM(104, MF_CHECKED | MF_GRAYED), (LPARAM)file); break; case 14: PostMessageW(window, WM_SYSCOMMAND, SC_KEYMENU, 'f'); break; case 15: // Move from Open to the submenu using ordinary menu navigation. PostMessageW(window, WM_KEYDOWN, VK_DOWN, 1); PostMessageW(window, WM_KEYDOWN, VK_DOWN, 1); PostMessageW(window, WM_KEYDOWN, VK_DOWN, 1); break; case 16: PostMessageW(window, WM_KEYDOWN, VK_RIGHT, 1); break; case 17: case 18: case 19: PostMessageW(window, WM_KEYDOWN, VK_ESCAPE, 1); break; default: DestroyWindow(window); break; } 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)args; (void)show; if (FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) return 1; WNDCLASSW cls = {0}; cls.hInstance = instance; cls.lpszClassName = L"WineAccessMenuFixture"; cls.lpfnWndProc = window_proc; RegisterClassW(&cls); bar = CreateMenu(); file = CreatePopupMenu(); edit = CreatePopupMenu(); nested = CreatePopupMenu(); context = CreatePopupMenu(); AppendMenuW(file, MF_STRING, 101, L"&Open\tCtrl+O"); AppendMenuW(file, MF_STRING | MF_CHECKED, 102, L"&Word wrap"); AppendMenuW(file, MF_STRING | MF_CHECKED | MF_GRAYED, 103, L"Save && &close\tCtrl+S"); AppendMenuW(nested, MF_STRING, 401, L"&Nested action"); AppendMenuW(file, MF_POPUP, (UINT_PTR)nested, L"&Favorites"); AppendMenuW(edit, MF_STRING, 302, L"&Cut\tCtrl+X"); AppendMenuW(context, MF_STRING, 201, L"&Context action"); AppendMenuW(bar, MF_POPUP, (UINT_PTR)file, L"&File"); AppendMenuW(bar, MF_POPUP, (UINT_PTR)edit, L"&Edit"); HWND window = CreateWindowW(cls.lpszClassName, L"Wine menu fixture", WS_OVERLAPPEDWINDOW, 120, 120, 400, 240, NULL, bar, instance, NULL); ShowWindow(window, SW_SHOW); button = CreateWindowW(L"Button", L"Return to control", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 10, 10, 200, 30, window, NULL, instance, NULL); cls.lpszClassName = L"WineAccessMarshaledFixture"; cls.lpfnWndProc = provider_proc; RegisterClassW(&cls); marshaledControl = CreateWindowW(cls.lpszClassName, L"Application-owned accessibility", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 10, 60, 250, 30, window, NULL, instance, NULL); SetForegroundWindow(window); SetFocus(button); SetFocus(marshaledControl); NotifyWinEvent(EVENT_OBJECT_FOCUS, marshaledControl, OBJID_CLIENT, CHILDID_SELF); SetTimer(window, 1, 400, NULL); MSG message; while (GetMessageW(&message, NULL, 0, 0) > 0) { TranslateMessage(&message); DispatchMessageW(&message); } DestroyMenu(context); CoUninitialize(); return 0; }