Fixes for edit boxes in wine.
This commit is contained in:
1 parent
c5657ee5d4
commit
39b7488aea
7 files changed
+112
-7
No files matched your search
@@ -42,6 +42,16 @@ target_link_options(cthulhu-wine-access PRIVATE -mwindows)
|
||||
target_compile_options(cthulhu-wine-access PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wall -Wextra>)
|
||||
|
||||
include(CTest)
|
||||
if(BUILD_TESTING)
|
||||
add_executable(msaa-states-test tests/msaa_states_test.cpp)
|
||||
target_compile_features(msaa-states-test PRIVATE cxx_std_20)
|
||||
target_compile_options(msaa-states-test PRIVATE -Wall -Wextra)
|
||||
add_test(NAME msaa-states COMMAND "${CMAKE_CURRENT_BINARY_DIR}/msaa-states-test.exe")
|
||||
set_tests_properties(msaa-states PROPERTIES ENVIRONMENT
|
||||
"WINEPREFIX=${CMAKE_CURRENT_BINARY_DIR}/test-prefix;CTHULHU_WINE_ACCESS_MANAGED=1;WINEDEBUG=-all;WINEDLLOVERRIDES=mscoree,mshtml=")
|
||||
endif()
|
||||
|
||||
# Winegcc emits a launcher and a Winelib sidecar. CMake's normal target
|
||||
# installation looks for the Linux target name, so install both emitted
|
||||
# artifacts explicitly and keep them together for Wine to resolve.
|
||||
|
||||
@@ -102,6 +102,11 @@ offsets and the action kind travel in the otherwise non-spoken `states` field
|
||||
for debug diagnostics. Older readers that only accept focus/value/selection
|
||||
events still receive focus and menus, but need updating for editor actions.
|
||||
|
||||
MSAA `name` and `state` events report changes to controls. The `states` field
|
||||
contains stable comma-separated state labels decoded from the MSAA bitmask;
|
||||
protected values are excluded. Readers should filter name/state changes to
|
||||
their focused source and distinguish these labels from editor diagnostics.
|
||||
|
||||
This is editing feedback, not a full text-review provider: Say All, review-cursor
|
||||
commands and braille routing into Wine text are not implemented. Typing echo
|
||||
remains the reader's responsibility. RichEdit and 32-bit editor hooks are not
|
||||
@@ -138,3 +143,23 @@ application exits.
|
||||
|
||||
The helper source is available under the terms of LGPL-2.1-or-later. See
|
||||
`LICENSE`.
|
||||
|
||||
## AI-assisted contributions
|
||||
|
||||
AI-assisted contributions are welcome. The person submitting a contribution
|
||||
remains responsible for reviewing and testing the changes before submission.
|
||||
Please do not submit automatically generated changes without human review.
|
||||
|
||||
Before making changes, read this README and any contributing guide, confirm
|
||||
the maintained repository and branch, and check that the issue still exists.
|
||||
Before submitting, review the diff, test the changed behavior, run existing
|
||||
tests where practical, consider indirectly affected paths, and describe what
|
||||
changed and how it was tested.
|
||||
|
||||
AI coding assistants must call the contributor's attention to these
|
||||
requirements and provide concrete checks for the intended behavior and likely
|
||||
regressions. Newly added tests alone do not establish acceptance. Leave
|
||||
submission to the contributor after they can review and test. Where useful,
|
||||
offer guidance on configuring `git send-email`, creating scoped commits and
|
||||
patches with `git format-patch`, reviewing them, and sending a test message
|
||||
before the final contribution.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Maintainer: Storm Dragon <storm_dragon@stormux.org>
|
||||
pkgname=wine-access-git
|
||||
pkgver=r6.g0992ffe
|
||||
pkgver=r8.gc5657ee
|
||||
pkgrel=1
|
||||
pkgdesc='Wine accessibility helper with MSAA, NVDA Controller, and Tolk compatibility for Cthulhu'
|
||||
arch=('x86_64')
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "nvdaController.h"
|
||||
#include "menu_bridge.h"
|
||||
#include "edit_hook.h"
|
||||
#include "msaa_states.h"
|
||||
|
||||
#include <commctrl.h>
|
||||
#include <gio/gio.h>
|
||||
@@ -522,14 +523,19 @@ void CALLBACK winevent_callback(HWINEVENTHOOK, DWORD event, HWND window,
|
||||
VARIANT role{};
|
||||
VariantInit(&role);
|
||||
std::string roleText;
|
||||
LONG roleId = 0;
|
||||
if (SUCCEEDED(accessible->get_accRole(child, &role)))
|
||||
roleText = variant_text(role);
|
||||
if (role.vt == VT_I4) roleId = role.lVal;
|
||||
VariantClear(&role);
|
||||
VARIANT state{};
|
||||
VariantInit(&state);
|
||||
std::string stateText;
|
||||
if (SUCCEEDED(accessible->get_accState(child, &state)))
|
||||
stateText = variant_text(state);
|
||||
bool password = false;
|
||||
if (SUCCEEDED(accessible->get_accState(child, &state)) && state.vt == VT_I4) {
|
||||
stateText = msaa_states(static_cast<DWORD>(state.lVal), roleId);
|
||||
password = state.lVal & STATE_SYSTEM_PROTECTED;
|
||||
}
|
||||
VariantClear(&state);
|
||||
LONG childCount = 0;
|
||||
accessible->get_accChildCount(&childCount);
|
||||
@@ -568,7 +574,7 @@ void CALLBACK winevent_callback(HWINEVENTHOOK, DWORD event, HWND window,
|
||||
ControlDescription control;
|
||||
control.name = name;
|
||||
control.role = roleText;
|
||||
control.value = value;
|
||||
control.value = password ? "" : value;
|
||||
control.states = stateText;
|
||||
control.description = description;
|
||||
control.count = static_cast<int>(childCount);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#pragma once
|
||||
#include <oleacc.h>
|
||||
#include <string>
|
||||
|
||||
// accState is a bitmask, not a role identifier. Keep protocol values stable
|
||||
// and omit focus/visibility bookkeeping from spoken state descriptions.
|
||||
inline std::string msaa_states(DWORD states, LONG role) {
|
||||
std::string result;
|
||||
const auto append = [&result](const char *text) {
|
||||
if (!result.empty()) result += ", ";
|
||||
result += text;
|
||||
};
|
||||
if (states & STATE_SYSTEM_MIXED) append("half checked");
|
||||
else if (states & STATE_SYSTEM_CHECKED) append("checked");
|
||||
else if (role == ROLE_SYSTEM_CHECKBUTTON || role == ROLE_SYSTEM_RADIOBUTTON)
|
||||
append("not checked");
|
||||
if (states & STATE_SYSTEM_SELECTED) append("selected");
|
||||
if (states & STATE_SYSTEM_PRESSED) append("pressed");
|
||||
if (states & STATE_SYSTEM_EXPANDED) append("expanded");
|
||||
if (states & STATE_SYSTEM_COLLAPSED) append("collapsed");
|
||||
if (states & STATE_SYSTEM_UNAVAILABLE) append("unavailable");
|
||||
if (states & STATE_SYSTEM_READONLY) append("read only");
|
||||
if (states & STATE_SYSTEM_PROTECTED) append("password");
|
||||
if (states & STATE_SYSTEM_BUSY) append("busy");
|
||||
return result;
|
||||
}
|
||||
+14
-3
@@ -34,6 +34,13 @@ object review are outside the current implementation.
|
||||
|
||||
## Editor and actual Notepad checks
|
||||
|
||||
The CMake build also includes a small MSAA state decoder check. Run it with
|
||||
`xvfb-run -a ctest --test-dir build --output-on-failure`. It uses its own prefix
|
||||
under the build directory, marks it as a managed test process and never sends
|
||||
reader output. It verifies combined bitmasks, unchecked radio buttons, mixed
|
||||
checkboxes, focus-only state suppression and password flags. It does not
|
||||
establish that a particular application's MSAA provider emits those states.
|
||||
|
||||
Run these against the built helper, optionally prefixed with `xvfb-run -a` for
|
||||
a private X display:
|
||||
|
||||
@@ -58,9 +65,13 @@ checks resulting document text as well as recorded accessibility output. These
|
||||
tests use a recording D-Bus service; they do not run the full reader or prove
|
||||
audible speech, typing echo, physical keys, or a physical braille display.
|
||||
|
||||
On 2026-10-10, the build, isolated editor/Notepad/menu checks, staged launcher
|
||||
permissions, and Cthulhu-Go app/remote/wine race tests passed. See the sibling
|
||||
reader's `docs/testing/wine-accessibility.md` for the matching Go behavior.
|
||||
The earlier 2026-10-10 helper fixtures and Go package tests did not establish
|
||||
that Go accepted editor `text` events. The reader follow-up adds that support,
|
||||
focused name/state handling, repeated-action and password/focus tests, and a
|
||||
real private-session D-Bus test through the Go presenter. See the sibling
|
||||
reader's `docs/testing/wine-accessibility.md` for current evidence and live
|
||||
acceptance gates. Run Xvfb fixtures sequentially; concurrent automatic display
|
||||
allocation can collide.
|
||||
|
||||
## Live Notepad acceptance
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#include "../msaa_states.h"
|
||||
#include <cstdio>
|
||||
|
||||
int main() {
|
||||
const struct { DWORD states; LONG role; const char *expected; } cases[] = {
|
||||
{STATE_SYSTEM_CHECKED | STATE_SYSTEM_UNAVAILABLE, ROLE_SYSTEM_CHECKBUTTON, "checked, unavailable"},
|
||||
{STATE_SYSTEM_MIXED | STATE_SYSTEM_CHECKED, ROLE_SYSTEM_CHECKBUTTON, "half checked"},
|
||||
{0, ROLE_SYSTEM_RADIOBUTTON, "not checked"},
|
||||
{STATE_SYSTEM_FOCUSED | STATE_SYSTEM_FOCUSABLE, ROLE_SYSTEM_PUSHBUTTON, ""},
|
||||
{STATE_SYSTEM_SELECTED | STATE_SYSTEM_EXPANDED, ROLE_SYSTEM_OUTLINEITEM, "selected, expanded"},
|
||||
{STATE_SYSTEM_PROTECTED | STATE_SYSTEM_READONLY, ROLE_SYSTEM_TEXT, "read only, password"},
|
||||
{STATE_SYSTEM_COLLAPSED | STATE_SYSTEM_BUSY, ROLE_SYSTEM_OUTLINEITEM, "collapsed, busy"},
|
||||
{STATE_SYSTEM_PRESSED, ROLE_SYSTEM_PUSHBUTTON, "pressed"},
|
||||
};
|
||||
for (const auto &testCase : cases) {
|
||||
const auto actual = msaa_states(testCase.states, testCase.role);
|
||||
if (actual != testCase.expected) {
|
||||
std::fprintf(stderr, "MSAA state %lu: got '%s', expected '%s'\n",
|
||||
static_cast<unsigned long>(testCase.states), actual.c_str(), testCase.expected);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
std::puts("PASS: MSAA bitmask states, unchecked roles and password flags");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user