73 lines
3.1 KiB
C
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);
|
|
}
|