Echo typed characters in Wine edit fields
This commit is contained in:
1 parent
d9aa40e16c
commit
a002c4cc1e
7 files changed
+154
-22
No files matched your search
@@ -193,8 +193,8 @@ document area, MUSHclient's command field and read-only output notepad, and
|
|||||||
use visible sibling labels immediately to their left, even when the labels
|
use visible sibling labels immediately to their left, even when the labels
|
||||||
were created after the fields. Protected fields report their label and
|
were created after the fields. Protected fields report their label and
|
||||||
password role without forwarding their contents.
|
password role without forwarding their contents.
|
||||||
It reports the current line on focus and menu return, characters
|
It reports accepted typed characters, the current line on focus and menu return,
|
||||||
and words on horizontal navigation, lines on vertical/page/document-boundary
|
characters and words on horizontal navigation, lines on vertical/page/document-boundary
|
||||||
navigation, changed selections, and Backspace/Delete/Cut feedback. Password
|
navigation, changed selections, and Backspace/Delete/Cut feedback. Password
|
||||||
contents are excluded, and read-only edits do not report unsuccessful deletions.
|
contents are excluded, and read-only edits do not report unsuccessful deletions.
|
||||||
Undo refreshes the text snapshot used by subsequent navigation. It can attach to
|
Undo refreshes the text snapshot used by subsequent navigation. It can attach to
|
||||||
@@ -212,11 +212,18 @@ selection feedback and Ctrl+Backspace/Delete feedback are not implemented.
|
|||||||
Backspace reports only the removed text, such as `t`. Delete and Cut retain
|
Backspace reports only the removed text, such as `t`. Delete and Cut retain
|
||||||
their deletion feedback, such as `t deleted`.
|
their deletion feedback, such as `t deleted`.
|
||||||
|
|
||||||
|
Standard Edit typing sends each accepted character as an ordered editor action,
|
||||||
|
including repeated letters, spaces, tabs, line breaks and Unicode surrogate
|
||||||
|
pairs. Typing over selected text also reports the inserted character. Password
|
||||||
|
fields, background controls and rejected input remain silent. Paste and
|
||||||
|
programmatic text changes do not generate character echo. The reader presents
|
||||||
|
these through the same feedback path as caret navigation and Backspace.
|
||||||
|
|
||||||
Editor actions use `PresentAccessibleEvent` with event type `text`; actual focus
|
Editor actions use `PresentAccessibleEvent` with event type `text`; actual focus
|
||||||
uses `focus`. Cthulhu-Go must include the corresponding editor-event support so
|
uses `focus`. Cthulhu-Go must include the corresponding editor-event support so
|
||||||
rapid actions and repeated characters bypass focus coalescing. Selection
|
rapid actions and repeated characters bypass focus coalescing. Selection
|
||||||
offsets and the action kind travel in the otherwise non-spoken `states` field
|
offsets and the action kind (8 for typed insertion) travel in the otherwise
|
||||||
for debug diagnostics. Standard edits use UTF-16 offsets; Scintilla marks its
|
non-spoken `states` field for debug diagnostics. Standard edits use UTF-16 offsets; Scintilla marks its
|
||||||
encoded byte offsets with `offset_unit=byte`. Older readers that only accept
|
encoded byte offsets with `offset_unit=byte`. Older readers that only accept
|
||||||
focus/value/selection events still receive focus and menus, but need updating
|
focus/value/selection events still receive focus and menus, but need updating
|
||||||
for editor actions.
|
for editor actions.
|
||||||
@@ -262,10 +269,10 @@ label captions decode keyboard mnemonics and escaped `&&`; static labels with
|
|||||||
|
|
||||||
These hooks provide editing feedback. Independent review-cursor commands use
|
These hooks provide editing feedback. Independent review-cursor commands use
|
||||||
the optional flat-review protocol described below; Say All and native braille
|
the optional flat-review protocol described below; Say All and native braille
|
||||||
routing remain unsupported. Typing echo
|
routing remain unsupported. Word echo and typing echo in Scintilla
|
||||||
remains the reader's responsibility. RichEdit and 32-bit Scintilla hooks are not
|
remain outside this helper's current support. RichEdit and 32-bit Scintilla hooks
|
||||||
covered. Reports are limited to 1023 UTF-16 units; documents over one million
|
are not covered. Reports are limited to 1023 UTF-16 units; documents over one million
|
||||||
units use line feedback without full selection/deletion snapshots.
|
units retain typing and line feedback without full selection/deletion snapshots.
|
||||||
Scintilla reads at most 4096 encoded bytes per report, then limits output to
|
Scintilla reads at most 4096 encoded bytes per report, then limits output to
|
||||||
1023 UTF-16 units without splitting surrogate pairs. Wrapped Scintilla lines
|
1023 UTF-16 units without splitting surrogate pairs. Wrapped Scintilla lines
|
||||||
report the underlying document line.
|
report the underlying document line.
|
||||||
|
|||||||
+36
@@ -14,6 +14,8 @@ typedef struct EditState {
|
|||||||
WCHAR *text;
|
WCHAR *text;
|
||||||
DWORD length;
|
DWORD length;
|
||||||
BOOL busy, known, destroyed;
|
BOOL busy, known, destroyed;
|
||||||
|
WCHAR pending_high;
|
||||||
|
DWORD pending_caret;
|
||||||
} EditState;
|
} EditState;
|
||||||
|
|
||||||
static void selection(HWND window, DWORD *start, DWORD *end) {
|
static void selection(HWND window, DWORD *start, DWORD *end) {
|
||||||
@@ -94,6 +96,8 @@ static LRESULT CALLBACK edit_proc(HWND window, UINT message, WPARAM wp, LPARAM l
|
|||||||
message == WM_PASTE || message == WM_CUT || message == WM_CLEAR ||
|
message == WM_PASTE || message == WM_CUT || message == WM_CLEAR ||
|
||||||
message == WM_UNDO || message == EM_UNDO || message == EM_REPLACESEL ||
|
message == WM_UNDO || message == EM_UNDO || message == EM_REPLACESEL ||
|
||||||
message == WM_SETTEXT || message == EM_SETHANDLE || message == EM_FMTLINES;
|
message == WM_SETTEXT || message == EM_SETHANDLE || message == EM_FMTLINES;
|
||||||
|
BOOL typing = message == WM_CHAR &&
|
||||||
|
((wp >= 0x20 && wp <= 0xffff && wp != 0x7f) || wp == VK_RETURN || wp == VK_TAB);
|
||||||
BOOL requested_focus = message == RegisterWindowMessageW(EDIT_FOCUS_MESSAGE);
|
BOOL requested_focus = message == RegisterWindowMessageW(EDIT_FOCUS_MESSAGE);
|
||||||
BOOL focus = message == WM_SETFOCUS || requested_focus;
|
BOOL focus = message == WM_SETFOCUS || requested_focus;
|
||||||
BOOL selected = message == EM_SETSEL;
|
BOOL selected = message == EM_SETSEL;
|
||||||
@@ -101,6 +105,9 @@ static LRESULT CALLBACK edit_proc(HWND window, UINT message, WPARAM wp, LPARAM l
|
|||||||
return DefSubclassProc(window, message, wp, lp);
|
return DefSubclassProc(window, message, wp, lp);
|
||||||
|
|
||||||
state->busy = TRUE;
|
state->busy = TRUE;
|
||||||
|
WCHAR pending_high = state->pending_high;
|
||||||
|
DWORD pending_caret = state->pending_caret;
|
||||||
|
state->pending_high = 0;
|
||||||
DWORD old_start = 0, old_end = 0;
|
DWORD old_start = 0, old_end = 0;
|
||||||
selection(window, &old_start, &old_end);
|
selection(window, &old_start, &old_end);
|
||||||
BOOL password = (GetWindowLongPtrW(window, GWL_STYLE) & ES_PASSWORD) != 0;
|
BOOL password = (GetWindowLongPtrW(window, GWL_STYLE) & ES_PASSWORD) != 0;
|
||||||
@@ -185,6 +192,35 @@ static LRESULT CALLBACK edit_proc(HWND window, UINT message, WPARAM wp, LPARAM l
|
|||||||
kind = message == WM_CHAR && wp == VK_BACK
|
kind = message == WM_CHAR && wp == VK_BACK
|
||||||
? WINE_ACCESS_TEXT_BACKSPACED : WINE_ACCESS_TEXT_DELETED;
|
? WINE_ACCESS_TEXT_BACKSPACED : WINE_ACCESS_TEXT_DELETED;
|
||||||
report = TRUE;
|
report = TRUE;
|
||||||
|
} else if (typing && !(GetWindowLongPtrW(window, GWL_STYLE) & ES_READONLY) &&
|
||||||
|
start == end && start > old_start && old_start <= old_end &&
|
||||||
|
old_end <= old_length && state->length > old_length - (old_end - old_start)) {
|
||||||
|
// Echo accepted input, including replacements that shorten a selection.
|
||||||
|
// WM_CHAR delivers supplementary characters as two UTF-16 messages.
|
||||||
|
if (wp >= 0xd800 && wp <= 0xdbff) {
|
||||||
|
state->pending_high = (WCHAR)wp;
|
||||||
|
state->pending_caret = start;
|
||||||
|
} else if (wp < 0xdc00 || wp > 0xdfff ||
|
||||||
|
(pending_high && pending_caret == old_start && old_start == old_end)) {
|
||||||
|
DWORD first = old_start;
|
||||||
|
BOOL paired = wp >= 0xdc00 && wp <= 0xdfff;
|
||||||
|
if (paired) --first;
|
||||||
|
if (state->text && start <= state->length) {
|
||||||
|
copy_range(text, state->text, first, start);
|
||||||
|
} else {
|
||||||
|
// Large documents have no full snapshot. The accepted WM_CHAR still
|
||||||
|
// supplies bounded typing feedback without copying the document.
|
||||||
|
UINT used = 0;
|
||||||
|
if (paired) text[used++] = pending_high;
|
||||||
|
text[used++] = (WCHAR)wp;
|
||||||
|
text[used] = 0;
|
||||||
|
LONG_PTR style = GetWindowLongPtrW(window, GWL_STYLE);
|
||||||
|
if (style & ES_UPPERCASE) CharUpperBuffW(text, used);
|
||||||
|
else if (style & ES_LOWERCASE) CharLowerBuffW(text, used);
|
||||||
|
}
|
||||||
|
kind = WINE_ACCESS_TEXT_INSERTED;
|
||||||
|
report = text[0] != 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (report) notify_text(window, kind, text, start, end);
|
if (report) notify_text(window, kind, text, start, end);
|
||||||
state->busy = FALSE;
|
state->busy = FALSE;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#define WINE_ACCESS_TEXT_READ 5
|
#define WINE_ACCESS_TEXT_READ 5
|
||||||
#define WINE_ACCESS_TEXT_PASSWORD 6
|
#define WINE_ACCESS_TEXT_PASSWORD 6
|
||||||
#define WINE_ACCESS_TEXT_BACKSPACED 7
|
#define WINE_ACCESS_TEXT_BACKSPACED 7
|
||||||
|
#define WINE_ACCESS_TEXT_INSERTED 8
|
||||||
#define WINE_ACCESS_TEXT_SCINTILLA 1
|
#define WINE_ACCESS_TEXT_SCINTILLA 1
|
||||||
#define WINE_ACCESS_TEXT_READONLY 2
|
#define WINE_ACCESS_TEXT_READONLY 2
|
||||||
|
|
||||||
|
|||||||
+17
-11
@@ -193,21 +193,24 @@ python3 tests/menu_integration.py --build-dir build --fixture winforms --32-bit
|
|||||||
python3 tests/menu_integration.py --build-dir build --fixture notepad
|
python3 tests/menu_integration.py --build-dir build --fixture notepad
|
||||||
```
|
```
|
||||||
|
|
||||||
The standard Edit fixture checks focus, characters/words/lines, incremental
|
The standard Edit fixture checks focus, typing echo, characters/words/lines,
|
||||||
selection and unselection, Backspace/Delete/Clear, read-only controls, password
|
incremental selection and unselection, Backspace/Delete/Clear, read-only controls, password
|
||||||
exclusion, surrogate pairs, rapid repeated letters, menu return at the caret,
|
exclusion, surrogate pairs, rapid repeated letters, menu return at the caret,
|
||||||
short replacements, CRLF deletion, long-line truncation, Undo snapshots, and
|
short replacements, CRLF deletion, long-line truncation, Undo snapshots, and
|
||||||
Ctrl+End line reading, and Up/Down navigation in a read-only multiline output
|
Ctrl+End line reading, and Up/Down navigation in a read-only multiline output
|
||||||
control. Both 32-bit and 64-bit fixtures use the same assertions. Lifecycle
|
control. Typing checks include repeated letters, spaces, Enter/Tab, accented
|
||||||
variants attach after the control has focus or
|
characters, surrogate pairs, selection replacement, documents beyond the full
|
||||||
|
snapshot limit, and silence for password/background/read-only fields and
|
||||||
|
input rejected at the text limit. Both 32-bit and 64-bit fixtures use the same
|
||||||
|
assertions. Lifecycle variants attach after the control has focus or
|
||||||
restart the helper without closing it. Every text action must have an established
|
restart the helper without closing it. Every text action must have an established
|
||||||
focus source, matching the Go reader's filtering contract.
|
focus source, matching the Go reader's filtering contract.
|
||||||
|
|
||||||
The Notepad fixture launches Wine's actual `notepad.exe` and uses `SendInput`
|
The Notepad fixture launches Wine's actual `notepad.exe` and uses `SendInput`
|
||||||
for arrows, Ctrl+Right, Shift selection, Delete, and Alt/Escape menu return. It
|
for typing, arrows, Ctrl+Right, Shift selection, Delete, and Alt/Escape menu return.
|
||||||
checks resulting document text as well as recorded accessibility output. These
|
It 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
|
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.
|
audible typing echo, physical keys, or a physical braille display.
|
||||||
|
|
||||||
For live MUSHclient acceptance, install the helper and reopen MUSHclient so the
|
For live MUSHclient acceptance, install the helper and reopen MUSHclient so the
|
||||||
updated 32-bit DLL can load. In the command field, check Left/Right and
|
updated 32-bit DLL can load. In the command field, check Left/Right and
|
||||||
@@ -408,8 +411,9 @@ helper and the application load the new DLL together. Reopen BARD in its own
|
|||||||
prefix. Tab and Shift+Tab between Email, Password, Sign in and Work Offline.
|
prefix. Tab and Shift+Tab between Email, Password, Sign in and Work Offline.
|
||||||
Expect labels and edit/password roles; navigate and select ordinary email
|
Expect labels and edit/password roles; navigate and select ordinary email
|
||||||
text, and confirm password contents stay excluded. Recheck native Notepad,
|
text, and confirm password contents stay excluded. Recheck native Notepad,
|
||||||
menus, the installer list and switching to Linux applications. Typing echo
|
menus, the installer list and switching to Linux applications. Standard Edit
|
||||||
and routable Wine review remain separate unfinished features.
|
typing echo is now forwarded through editor events; routable Wine review
|
||||||
|
remains unfinished.
|
||||||
|
|
||||||
## Live Notepad acceptance
|
## Live Notepad acceptance
|
||||||
|
|
||||||
@@ -421,8 +425,10 @@ Close existing Notepad windows after saving any work so the updated hook can loa
|
|||||||
rebuilds Cthulhu and its braille helper and opens the test terminal; it does
|
rebuilds Cthulhu and its braille helper and opens the test terminal; it does
|
||||||
not build wine-access.
|
not build wine-access.
|
||||||
3. Run `wine notepad` in the intended Wine prefix. Expect edit/blank focus
|
3. Run `wine notepad` in the intended Wine prefix. Expect edit/blank focus
|
||||||
feedback. Type `hello beta`, Enter, then `second line`. Typing echo should
|
feedback. Type `hello beta`, Enter, then `second line`. Expect each typed
|
||||||
follow the reader's configured settings.
|
character, including both `l` characters, spaces and the line break. Standard
|
||||||
|
Edit typing uses the reader's editor feedback path. Check typing over selected
|
||||||
|
text too, and confirm password fields never announce their typed contents.
|
||||||
4. Press Ctrl+Home, then Right three times. Expect the first line at Ctrl+Home,
|
4. Press Ctrl+Home, then Right three times. Expect the first line at Ctrl+Home,
|
||||||
then `e`, `l`, `l`. Both repeated letters should produce feedback. Ctrl+Right
|
then `e`, `l`, `l`. Both repeated letters should produce feedback. Ctrl+Right
|
||||||
should read `beta`; Down should read `second line`.
|
should read `beta`; Down should read `second line`.
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#ifdef WINE_ACCESS_WINFORMS_FIXTURE
|
#ifdef WINE_ACCESS_WINFORMS_FIXTURE
|
||||||
static const WCHAR *editClass = L"WindowsForms10.EDIT.app.0.fixture_r7_ad1";
|
static const WCHAR *editClass = L"WindowsForms10.EDIT.app.0.fixture_r7_ad1";
|
||||||
@@ -131,6 +133,67 @@ static LRESULT CALLBACK window_proc(HWND window, UINT message, WPARAM wp, LPARAM
|
|||||||
key(VK_DELETE, FALSE, FALSE);
|
key(VK_DELETE, FALSE, FALSE);
|
||||||
if (GetWindowTextLengthW(editor) != 31) ExitProcess(2);
|
if (GetWindowTextLengthW(editor) != 31) ExitProcess(2);
|
||||||
break;
|
break;
|
||||||
|
case 35:
|
||||||
|
SendMessageW(editor, WM_CHAR, L'!', 1);
|
||||||
|
if (GetWindowTextLengthW(editor) != 31) ExitProcess(2);
|
||||||
|
break;
|
||||||
|
case 36:
|
||||||
|
SendMessageW(editor, EM_SETREADONLY, FALSE, 0);
|
||||||
|
SetWindowTextW(editor, L"");
|
||||||
|
SendMessageW(editor, WM_CHAR, L'z', 1);
|
||||||
|
SendMessageW(editor, WM_CHAR, L'z', 1);
|
||||||
|
SendMessageW(editor, WM_CHAR, L' ', 1);
|
||||||
|
SendMessageW(editor, WM_CHAR, 0x00e9, 1);
|
||||||
|
SendMessageW(editor, WM_CHAR, 0xd83d, 1);
|
||||||
|
SendMessageW(editor, WM_CHAR, 0xde00, 1);
|
||||||
|
SendMessageW(editor, WM_CHAR, VK_RETURN, 1);
|
||||||
|
SendMessageW(editor, WM_CHAR, VK_TAB, 1);
|
||||||
|
{
|
||||||
|
WCHAR text[32];
|
||||||
|
GetWindowTextW(editor, text, 32);
|
||||||
|
if (lstrcmpW(text, L"zz \u00e9\U0001f600\r\n\t")) ExitProcess(2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 37:
|
||||||
|
// Replacing selected text is still typing, even when length shrinks.
|
||||||
|
SendMessageW(editor, EM_SETSEL, 0, 4);
|
||||||
|
SendMessageW(editor, WM_CHAR, L'q', 1);
|
||||||
|
break;
|
||||||
|
case 38:
|
||||||
|
SetFocus(password);
|
||||||
|
SendMessageW(password, WM_CHAR, L'@', 1);
|
||||||
|
// Background changes must not produce echo for the focused password.
|
||||||
|
SendMessageW(editor, WM_CHAR, L'~', 1);
|
||||||
|
break;
|
||||||
|
case 39:
|
||||||
|
SetFocus(editor);
|
||||||
|
{
|
||||||
|
DWORD length = 1024 * 1024 + 1;
|
||||||
|
WCHAR *large = calloc(length + 1, sizeof(WCHAR));
|
||||||
|
if (!large) ExitProcess(2);
|
||||||
|
for (DWORD i = 0; i < length; ++i) large[i] = L'x';
|
||||||
|
SendMessageW(editor, EM_SETLIMITTEXT, length + 3, 0);
|
||||||
|
SetWindowTextW(editor, large);
|
||||||
|
free(large);
|
||||||
|
SendMessageW(editor, EM_SETSEL, length, length);
|
||||||
|
SendMessageW(editor, WM_CHAR, L'L', 1);
|
||||||
|
SendMessageW(editor, WM_CHAR, 0xd83d, 1);
|
||||||
|
SendMessageW(editor, WM_CHAR, 0xde00, 1);
|
||||||
|
if (GetWindowTextLengthW(editor) != (int)length + 3) {
|
||||||
|
fprintf(stderr, "large typing: length=%d expected=%lu limit=%ld\n",
|
||||||
|
GetWindowTextLengthW(editor), (unsigned long)length + 3,
|
||||||
|
(long)SendMessageW(editor, EM_GETLIMITTEXT, 0, 0));
|
||||||
|
ExitProcess(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
SetWindowTextW(editor, L"full");
|
||||||
|
SendMessageW(editor, EM_SETSEL, 4, 4);
|
||||||
|
SendMessageW(editor, EM_SETLIMITTEXT, 4, 0);
|
||||||
|
SendMessageW(editor, WM_CHAR, L'%', 1);
|
||||||
|
if (GetWindowTextLengthW(editor) != 4) ExitProcess(2);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
DestroyWindow(window); break;
|
DestroyWindow(window); break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,8 +251,10 @@ with tempfile.TemporaryDirectory(prefix="wine-access-menus-") as temporary:
|
|||||||
"N unselected", "N deleted"):
|
"N unselected", "N deleted"):
|
||||||
assert expected in spoken, f"missing Notepad feedback: {expected!r}; {text_events!r}"
|
assert expected in spoken, f"missing Notepad feedback: {expected!r}; {text_events!r}"
|
||||||
assert spoken.count("l") == 2, "Notepad lost repeated characters"
|
assert spoken.count("l") == 2, "Notepad lost repeated characters"
|
||||||
|
typed = [e[2] for e in text_events if e[1] == "text" and "kind=8" in e[5]]
|
||||||
|
assert typed == ["z", "z", "space", "new line"], f"Notepad typing echo missing or incorrect: {typed!r}"
|
||||||
assert any(e[3] == "edit" and e[4] == "otepad second line" for e in text_events), "Notepad menu return missing"
|
assert any(e[3] == "edit" and e[4] == "otepad second line" for e in text_events), "Notepad menu return missing"
|
||||||
print("PASS: actual Wine Notepad keyboard characters, words, lines, selection, deletion, and menu return")
|
print("PASS: actual Wine Notepad typing echo, keyboard characters, words, lines, selection, deletion, and menu return")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
if args.fixture == "scintilla":
|
if args.fixture == "scintilla":
|
||||||
textEvents = [e for e in events if e[0].endswith(":text")]
|
textEvents = [e for e in events if e[0].endswith(":text")]
|
||||||
@@ -302,13 +304,16 @@ with tempfile.TemporaryDirectory(prefix="wine-access-menus-") as temporary:
|
|||||||
for line in ("readonly first", "readonly second"):
|
for line in ("readonly first", "readonly second"):
|
||||||
assert any(e[1] == "text" and e[2] == line for e in text_events), f"read-only arrow feedback missing: {line}"
|
assert any(e[1] == "text" and e[2] == line for e in text_events), f"read-only arrow feedback missing: {line}"
|
||||||
assert "r deleted" not in spoken, "read-only document reported an unsuccessful deletion"
|
assert "r deleted" not in spoken, "read-only document reported an unsuccessful deletion"
|
||||||
|
typed = [e[2] for e in text_events if e[1] == "text" and "kind=8" in e[5]]
|
||||||
|
assert typed == ["z", "z", "space", "\u00e9", "\U0001f600", "new line", "tab", "q", "L", "\U0001f600"], f"typing echo missing or incorrect: {typed!r}"
|
||||||
|
assert not any(e[1] == "text" and e[2] in ("!", "@", "~", "%") for e in events), "rejected, password or background typing leaked"
|
||||||
if args.fixture == "winforms":
|
if args.fixture == "winforms":
|
||||||
assert any(e[2] == "Email" and e[3] == "edit" and e[4] == "alpha beta"
|
assert any(e[2] == "Email" and e[3] == "edit" and e[4] == "alpha beta"
|
||||||
for e in text_events), "managed email label/focus missing"
|
for e in text_events), "managed email label/focus missing"
|
||||||
assert any(e[2] == "Password" and e[3] == "edit" and e[6] == "password"
|
assert any(e[2] == "Password" and e[3] == "edit" and e[6] == "password"
|
||||||
and e[4] == "" for e in text_events), "managed password label/protection missing"
|
and e[4] == "" for e in text_events), "managed password label/protection missing"
|
||||||
assert not any(e[2] == "Unrelated" for e in text_events), "unrelated label associated with field"
|
assert not any(e[2] == "Unrelated" for e in text_events), "unrelated label associated with field"
|
||||||
print("PASS: editor focus, characters, words, lines, selection, deletion, Unicode, passwords, and read-only controls")
|
print("PASS: editor focus, typing echo, characters, words, lines, selection, deletion, Unicode, passwords, and read-only controls")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
menus = [event for event in events if ":menu:" in event[0]]
|
menus = [event for event in events if ":menu:" in event[0]]
|
||||||
for event in menus:
|
for event in menus:
|
||||||
|
|||||||
+15
-1
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
// Drive Wine's actual Notepad; the standard-control fixture covers edge cases.
|
// Drive Wine's actual Notepad; the standard-control fixture covers edge cases.
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
static DWORD notepad_pid;
|
static DWORD notepad_pid;
|
||||||
static HWND notepad;
|
static HWND notepad;
|
||||||
@@ -61,6 +62,18 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous, LPSTR args, int show)
|
|||||||
if (notepad) {
|
if (notepad) {
|
||||||
HWND edit = FindWindowExW(notepad, NULL, L"Edit", NULL);
|
HWND edit = FindWindowExW(notepad, NULL, L"Edit", NULL);
|
||||||
SetForegroundWindow(notepad);
|
SetForegroundWindow(notepad);
|
||||||
|
Sleep(400);
|
||||||
|
key('Z', FALSE, FALSE);
|
||||||
|
key('Z', FALSE, FALSE);
|
||||||
|
key(VK_SPACE, FALSE, FALSE);
|
||||||
|
key(VK_RETURN, FALSE, FALSE);
|
||||||
|
WCHAR typed[16] = {0};
|
||||||
|
SendMessageW(edit, WM_GETTEXT, 16, (LPARAM)typed);
|
||||||
|
if (lstrcmpW(typed, L"zz \r\n")) {
|
||||||
|
fprintf(stderr, "Notepad typing did not reach its edit: units=%d first=%04x foreground=%p expected=%p\n",
|
||||||
|
lstrlenW(typed), (unsigned)typed[0], (void *)GetForegroundWindow(), (void *)notepad);
|
||||||
|
ExitProcess(2);
|
||||||
|
}
|
||||||
SendMessageW(edit, WM_SETTEXT, 0, (LPARAM)L"hello beta\r\nNotepad second line");
|
SendMessageW(edit, WM_SETTEXT, 0, (LPARAM)L"hello beta\r\nNotepad second line");
|
||||||
SendMessageW(edit, EM_SETSEL, 0, 0);
|
SendMessageW(edit, EM_SETSEL, 0, 0);
|
||||||
Sleep(400);
|
Sleep(400);
|
||||||
@@ -79,12 +92,13 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous, LPSTR args, int show)
|
|||||||
WCHAR text[80] = {0};
|
WCHAR text[80] = {0};
|
||||||
SendMessageW(edit, WM_GETTEXT, 80, (LPARAM)text);
|
SendMessageW(edit, WM_GETTEXT, 80, (LPARAM)text);
|
||||||
if (!lstrcmpW(text, L"hello beta\r\notepad second line")) result = 0;
|
if (!lstrcmpW(text, L"hello beta\r\notepad second line")) result = 0;
|
||||||
|
else fprintf(stderr, "Notepad final document mismatch: units=%d\n", lstrlenW(text));
|
||||||
PostMessageW(notepad, WM_CLOSE, 0, 0);
|
PostMessageW(notepad, WM_CLOSE, 0, 0);
|
||||||
for (int i = 0; i < 100 && !saveDialog; ++i) {
|
for (int i = 0; i < 100 && !saveDialog; ++i) {
|
||||||
EnumWindows(find_save_dialog, 0);
|
EnumWindows(find_save_dialog, 0);
|
||||||
Sleep(50);
|
Sleep(50);
|
||||||
}
|
}
|
||||||
if (!saveDialog) result = 2;
|
if (!saveDialog) { fprintf(stderr, "Notepad save dialog missing\n"); result = 2; }
|
||||||
else {
|
else {
|
||||||
SetForegroundWindow(saveDialog);
|
SetForegroundWindow(saveDialog);
|
||||||
Sleep(400);
|
Sleep(400);
|
||||||
|
|||||||
Reference in new issue
Block a user