Files

73 lines
3.1 KiB
C

// SPDX-License-Identifier: LGPL-2.1-or-later
#include "menu_bridge.h"
#include "edit_hook.h"
#include "notification_hook.h"
static void menu_text(WCHAR *output, UINT capacity, const WCHAR *input) {
UINT used = 0;
while (*input && *input != '\t' && used + 1 < capacity) {
if (*input == '&') {
++input;
if (*input != '&') continue;
}
output[used++] = *input++;
}
output[used] = 0;
}
// This runs in the application's process: Wine does not expose another
// process's HMENU contents to the standalone reader.
__declspec(dllexport) LRESULT CALLBACK WineAccessMenuHook(int code, WPARAM wp,
LPARAM lp) {
if (code >= 0) {
wine_access_watch_notifications();
const CWPSTRUCT *message = (const CWPSTRUCT *)lp;
if (message->message == WM_SETFOCUS) wine_access_watch_edit(message->hwnd);
if (message->message == WM_MENUSELECT || message->message == WM_EXITMENULOOP) {
HWND bridge = FindWindowW(WINE_ACCESS_MENU_CLASS, NULL);
HWND owner = GetAncestor(message->hwnd, GA_ROOT);
BOOL closed = message->message == WM_EXITMENULOOP ||
(!message->lParam && HIWORD(message->wParam) == 0xffff);
if (bridge && (closed || owner == GetAncestor(GetForegroundWindow(), GA_ROOT))) {
WineAccessMenuEvent event = {0};
UINT flags = HIWORD(message->wParam);
HMENU menu = (HMENU)message->lParam;
event.owner = (ULONGLONG)(UINT_PTR)owner;
event.menu = (ULONGLONG)(UINT_PTR)menu;
event.item = LOWORD(message->wParam);
if (closed) {
event.flags = WINE_ACCESS_MENU_CLOSED;
} else {
WCHAR text[640] = {0};
MENUITEMINFOW info = {0};
info.cbSize = sizeof(info);
info.fMask = MIIM_STRING | MIIM_STATE | MIIM_FTYPE | MIIM_SUBMENU;
info.dwTypeData = text;
info.cch = sizeof(text) / sizeof(text[0]);
if (!GetMenuItemInfoW(menu, event.item, (flags & MF_POPUP) != 0, &info) ||
(info.fType & MFT_SEPARATOR))
return CallNextHookEx(NULL, code, wp, lp);
menu_text(event.name, 512, text);
for (UINT i = 0; text[i]; ++i) {
if (text[i] == '\t') {
lstrcpynW(event.shortcut, text + i + 1, 128);
break;
}
}
if (!event.name[0]) return CallNextHookEx(NULL, code, wp, lp);
if (info.hSubMenu) event.flags |= WINE_ACCESS_MENU_SUBMENU;
if (info.fState & MFS_CHECKED) event.flags |= WINE_ACCESS_MENU_CHECKED;
if (info.fState & MFS_DISABLED) event.flags |= WINE_ACCESS_MENU_DISABLED;
GetWindowTextW(owner, event.title, 256);
}
COPYDATASTRUCT data = {WINE_ACCESS_MENU_TAG, sizeof(event), &event};
DWORD_PTR result;
// The bridge only queues the payload; speech never runs in this hook.
SendMessageTimeoutW(bridge, WM_COPYDATA, (WPARAM)message->hwnd,
(LPARAM)&data, SMTO_ABORTIFHUNG | SMTO_BLOCK, 50, &result);
}
}
}
return CallNextHookEx(NULL, code, wp, lp);
}