|
From: Kevin W. <kw...@co...> - 2025-10-18 21:07:18
|
Hello all, This is a CFV warning for TIP 733, screen reader/accessibility support for Tk. I consider the project feature complete and as stable as I can make it at this point. I'd appreciate final reviews/comments, and then I will call for a formal vote in the next couple of weeks. My goal is to include TIP 733 in 9.1.a1, which I understand is by the end of November. I've received detailed feedback from Ashok, Jan, and Nico. I'd like to address each point: 1. Nico reported a crash on startup. I am not able to reproduce this crash. 2. Jan noted that the test suite failed on all three platforms during automated Github runs. I added code to accessibility.tcl to return an empty namespace if a screen reader isn't running. That way there will be no unintended side effects from the accessibility commands conflicting with the test suite, which now runs normally on all three platforms. Note: I am NOT including automated testing of accessibility within the test suite itself. Accessibility is best tested through interactive use of a screen reader. 3. Toggleswitch - Added and made accessible on all platforms. This implementation may be refined in the future as the widget is brand new, but for now I just wanted to ensure the widget's inclusion in accessibility. 4. "Set/get" balance in commands - complete. 5. Language issues/macOS - I have not received further feedback on my fix. This is not a showstopper for approving the TIP, in any case, but can be addressed later through bug reporting if needed. 6. Progress bars - I have opted for a simple implementation that returns a verbalized "busy" if the widget is running. This means that if a progress bar is visible, it will report "busy" even if it is not running - the best solution here would be to hide the widget when not active. (That's what I do in my apps.) There is no easy way to track the animation state of a running progress bar in real time, and I judged the complexity of supporting this to be not worth the effort. If anyone disagrees, they are welcome to submit a patch after the TIP is finalized. 7. Echoing keypresses and the prior word in text and entry widgets - done, on all three platforms. I think that is the most significant addition based on the prior feedback and brings Tk accessibility much more in line with expectations - thanks for that suggestion, Ashok. 8. Microsoft Narrator support - rudimentary. Ashok, I have been able to implement all your suggestions - but they only work well with NVDA. (I also assume they work with JAWS, but as it is an expensive proprietary product with a subscription licensing model similar to Adobe, I have not attempted to use it.) UI Automation is a significantly more complex API than Microsoft Active Accessibility, which is no lightweight API in itself. I have spent a great deal of time trying to make UIA and MSAA work together, but have not made any progress. The LegacyIAccessible pattern that I've implemented for UIA support does not do much, and attempting to develop a complete UIA implementation has risked turning the Windows portion of this project into a mess. Unfortunately, the accessibility environment on Windows is quite fragmented, and requires the developer to make some choices. My choice has been to focus on MSAA, which lines up very well with Tk's widget set and overall UI design, and NVDA, which is an extremely robust, FOSS screen reader. Ashok, if you install NVDA and run the widget demo, you will see a significant difference. My decisions here also mean that we have to document that Narrator is not well-supported, which I have noted in the TIP. I am not happy with this, but those are the choices Windows presents. A proper UIA implementation is a project for another day, and given I have spent 18 months on just this, I may opt to leave that for someone else to implement. Looking forward to any further comments. Thanks to all. --Kevin |
|
From: Jan N. <jan...@gm...> - 2025-10-21 07:45:27
|
Op za 18 okt 2025 om 23:07 schreef Kevin Walzer:
> 2. Jan noted that the test suite failed on all three platforms during
> automated Github runs. I added code to accessibility.tcl to return an
> empty namespace if a screen reader isn't running. That way there will be
> no unintended side effects from the accessibility commands conflicting
> with the test suite, which now runs normally on all three platforms.
> Note: I am NOT including automated testing of accessibility within the
> test suite itself. Accessibility is best tested through interactive use
> of a screen reader.
Unfortunately, testcases are still failing when there is no
screen-reader. e.g.:
==== font-1.1 TkFontPkgInit FAILED
==== Contents of test case:
interp create foo
foo eval {
load {} Tk
....
invalid command name "::tk::accessible::check_screenreader"
while executing
"::tk::accessible::check_screenreader"
invoked from within
"if {([::tk::accessible::check_screenreader] eq 0 ||
[::tk::accessible::check_screenreader] eq "")} {
# Do not load if screen reader is not running..."
(file "/Users/runner/work/tk/tk/tk/library/accessibility.tcl" line 14)
invoked from within
<https://github.com/tcltk/tk/actions/runs/18675529789/job/5324461262>
This is on Linux. but Windows and MacOS fail too
:-(
Jan Nijtmans
|
|
From: Kevin W. <kw...@co...> - 2025-10-21 10:50:36
|
This is where I am unsure how to proceed. The check_screenreader command is either a stub (if Tk is built without accessibility on Linux) or a full command, but in either case it should be present. This looks instead like some kind of timing issue instead with the test suite. I can’t load accessibility any earlier. Can anyone suggest a solution?
> On Oct 21, 2025, at 3:45 AM, Jan Nijtmans <jan...@gm...> wrote:
>
> Op za 18 okt 2025 om 23:07 schreef Kevin Walzer:
>> 2. Jan noted that the test suite failed on all three platforms during
>> automated Github runs. I added code to accessibility.tcl to return an
>> empty namespace if a screen reader isn't running. That way there will be
>> no unintended side effects from the accessibility commands conflicting
>> with the test suite, which now runs normally on all three platforms.
>> Note: I am NOT including automated testing of accessibility within the
>> test suite itself. Accessibility is best tested through interactive use
>> of a screen reader.
>
> Unfortunately, testcases are still failing when there is no
> screen-reader. e.g.:
> ==== font-1.1 TkFontPkgInit FAILED
> ==== Contents of test case:
> interp create foo
> foo eval {
> load {} Tk
> ....
> invalid command name "::tk::accessible::check_screenreader"
> while executing
> "::tk::accessible::check_screenreader"
> invoked from within
> "if {([::tk::accessible::check_screenreader] eq 0 ||
> [::tk::accessible::check_screenreader] eq "")} {
> # Do not load if screen reader is not running..."
> (file "/Users/runner/work/tk/tk/tk/library/accessibility.tcl" line 14)
> invoked from within
>
> <https://github.com/tcltk/tk/actions/runs/18675529789/job/5324461262>
>
> This is on Linux. but Windows and MacOS fail too
>
> :-(
> Jan Nijtmans
|
|
From: Kevin W. <kw...@co...> - 2025-10-21 11:47:22
|
I was able to reproduce this just interactively in Wish. The failure
occurs when the "interp create" command is run:
% interp create foo
foo
% foo eval {
load {} Tk
}
Can't find a usable tk.tcl in the following directories:
/Library/Frameworks/Tk.framework/Versions/9.1/Resources/Scripts
/Library/Frameworks/Tk.framework/Versions/9.1/Resources/Scripts/tk.tcl:
invalid command name "::tk::accessible::check_screenreader"
invalid command name "::tk::accessible::check_screenreader"
while executing
"::tk::accessible::check_screenreader"
...
This is an area I don't really work with, so I don't understand what is
going on. Why wouldn't a command that is available in the main interp
fail to be available in a child interp that was not called with the
-safe flag? I understand there's probably some machinery here I'm not
wiring up - any suggestions?
On 10/21/25 6:50 AM, Kevin Walzer wrote:
> This is where I am unsure how to proceed. The check_screenreader command is either a stub (if Tk is built without accessibility on Linux) or a full command, but in either case it should be present. This looks instead like some kind of timing issue instead with the test suite. I can’t load accessibility any earlier. Can anyone suggest a solution?
>
>> On Oct 21, 2025, at 3:45 AM, Jan Nijtmans <jan...@gm...> wrote:
>>
>> Op za 18 okt 2025 om 23:07 schreef Kevin Walzer:
>>> 2. Jan noted that the test suite failed on all three platforms during
>>> automated Github runs. I added code to accessibility.tcl to return an
>>> empty namespace if a screen reader isn't running. That way there will be
>>> no unintended side effects from the accessibility commands conflicting
>>> with the test suite, which now runs normally on all three platforms.
>>> Note: I am NOT including automated testing of accessibility within the
>>> test suite itself. Accessibility is best tested through interactive use
>>> of a screen reader.
>> Unfortunately, testcases are still failing when there is no
>> screen-reader. e.g.:
>> ==== font-1.1 TkFontPkgInit FAILED
>> ==== Contents of test case:
>> interp create foo
>> foo eval {
>> load {} Tk
>> ....
>> invalid command name "::tk::accessible::check_screenreader"
>> while executing
>> "::tk::accessible::check_screenreader"
>> invoked from within
>> "if {([::tk::accessible::check_screenreader] eq 0 ||
>> [::tk::accessible::check_screenreader] eq "")} {
>> # Do not load if screen reader is not running..."
>> (file "/Users/runner/work/tk/tk/tk/library/accessibility.tcl" line 14)
>> invoked from within
>>
>> <https://github.com/tcltk/tk/actions/runs/18675529789/job/5324461262>
>>
>> This is on Linux. but Windows and MacOS fail too
>>
>> :-(
>> Jan Nijtmans
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
|
|
From: Kevin W. <kw...@co...> - 2025-10-21 12:33:58
|
Checking for the existence of the check_screenreader command via "info commands" allowed a child interp to load cleanly. I did not see a failure on this issue in the test suite, so I've committed the update with the expectation that the test suite will now pass. This also appears to mean that accessibility cannot run in a child interp, because the absence of the command check_screenreader command means accessibility will not load. Is that a problem? I'm fine documenting it in the TIP and/or man page. Please advise. On 10/21/25 7:47 AM, Kevin Walzer wrote: > This is an area I don't really work with, so I don't understand what > is going on. Why wouldn't a command that is available in the main > interp fail to be available in a child interp that was not called with > the -safe flag? I understand there's probably some machinery here I'm > not wiring up - any suggestions? |
|
From: Emiliano <emi...@gm...> - 2025-10-21 18:52:53
|
On Sat, 18 Oct 2025 17:07:03 -0400
Kevin Walzer <kw...@co...> wrote:
Hi Kevin
This is an amazing work, providing a long awaited feature. Thanks !!
I started testing tka11y branch on X11 (linux, x86_64). So far, I've been able
to find these issues
* The following line in the configure script is not required to build with
atk support. Gtk is not needed.
&& pkg-config --exists gtk+-3.0 \
* Starting wish shows a short but visible delay in which the default
root window is mapped on screen (200x200). This is most noticeable
starting applications such as tkcon, in which the root window is
displayed and then withdrawn.
This delay is in Tk initialization, not process initialization;
loading Tk in a child interpreter shows the same delay.
* Test textWind-17.10 (peer widget window configuration test) hangs, and
closing the tktest window afterwards segfaults.
I was unable to isolate this hang completely. Simply running the text
tests with TESTFLAGS="-file text*" doesn't hang, so it must depend on
something that runs earlier in the test suite.
Also, can't say anything about any further tests in the test suite.
* Widgets must be mapped before using [tk accessible get_acc_*]
with them. Example
# ---- unmapped.tcl
pack [entry .e]
tk accessible add_acc_object .e
puts [tk accessible get_acc_role .e]
# ----
$ ./wish9.1 unmapped.tcl
No table found. You must set the accessibility role first.
# ---- mapped.tcl
pack [entry .e]
tk accessible add_acc_object .e
update
puts [tk accessible get_acc_role .e]
# ----
$ ./wish9.1 mapped.tcl
Entry
* The data in commands [tk accessible {get|set}_acc_*] is not multi
interp safe. Running the folowing code
# ---- childinterp.tcl
pack [entry .e]
tk accessible add_acc_object .e
update
after 2000 {
puts "before: [tk accessible get_acc_role .e]"
# create a new Tk application and then delete it
interp create i
i eval {package require Tk}
interp delete i
# we can no longer access the accessibility data tables
catch {tk accessible get_acc_role .e} err od
puts "after: $err\n$od"
exit
}
# ----
results in (return options dictionary formatted)
$ ./wish9.1 childinterp.tcl
before: Entry
after: No table found. You must set the accessibility role first.
-code 1 -level 0 -errorstack {INNER {invokeStk tk accessible get_acc_role .e}}
-errorcode NONE -errorinfo {No table found. You must set the accessibility role first.
while executing
"tk accessible get_acc_role .e"} -errorline 1
The solution here is simple: declare and initialize TkAccessibilityObject
as NULL
Tcl_HashTable *TkAccessibilityObject = NULL;
and only allocate and initialize as hash table if NULL in the
TkAccessibility_Init function
if (!TkAccessibilityObject) {
TkAccessibilityObject = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
Tcl_InitHashTable(TkAccessibilityObject, TCL_ONE_WORD_KEYS);
}
* When widgets are destroyed, the hash tables are not cleaned up, at least
the TkAccessibilityObject hash table from generic/tkAccessibility.c .
Even recreating a widget with the same pathname and class will,
most likely, result in a different tkwin pointer, resulting in an ever
growing table with lots of unused entries.
Can't say anything about the GLib based hash tables as I don't know how
they work.
I see there's an event handler for widget deletion in
unix/tkUnixAccessibility.c , but there's no cleanup on the above mentioned
hash table there. I guess the same applies to both Win and Mac specific
code.
Regards
--
Emiliano
|
|
From: Kevin W. <kw...@co...> - 2025-10-21 19:25:30
|
Hi Emiliano,
Thank you for the feedback. I will review it carefully.
Thanks,
Kevin
> On Oct 21, 2025, at 2:53 PM, Emiliano <emi...@gm...> wrote:
>
> On Sat, 18 Oct 2025 17:07:03 -0400
> Kevin Walzer <kw...@co...> wrote:
>
> Hi Kevin
>
> This is an amazing work, providing a long awaited feature. Thanks !!
>
> I started testing tka11y branch on X11 (linux, x86_64). So far, I've been able
> to find these issues
>
> * The following line in the configure script is not required to build with
> atk support. Gtk is not needed.
>
> && pkg-config --exists gtk+-3.0 \
>
> * Starting wish shows a short but visible delay in which the default
> root window is mapped on screen (200x200). This is most noticeable
> starting applications such as tkcon, in which the root window is
> displayed and then withdrawn.
> This delay is in Tk initialization, not process initialization;
> loading Tk in a child interpreter shows the same delay.
>
> * Test textWind-17.10 (peer widget window configuration test) hangs, and
> closing the tktest window afterwards segfaults.
> I was unable to isolate this hang completely. Simply running the text
> tests with TESTFLAGS="-file text*" doesn't hang, so it must depend on
> something that runs earlier in the test suite.
> Also, can't say anything about any further tests in the test suite.
>
> * Widgets must be mapped before using [tk accessible get_acc_*]
> with them. Example
>
> # ---- unmapped.tcl
> pack [entry .e]
> tk accessible add_acc_object .e
> puts [tk accessible get_acc_role .e]
> # ----
> $ ./wish9.1 unmapped.tcl
> No table found. You must set the accessibility role first.
>
> # ---- mapped.tcl
> pack [entry .e]
> tk accessible add_acc_object .e
> update
> puts [tk accessible get_acc_role .e]
> # ----
> $ ./wish9.1 mapped.tcl
> Entry
>
> * The data in commands [tk accessible {get|set}_acc_*] is not multi
> interp safe. Running the folowing code
>
> # ---- childinterp.tcl
> pack [entry .e]
> tk accessible add_acc_object .e
> update
> after 2000 {
> puts "before: [tk accessible get_acc_role .e]"
> # create a new Tk application and then delete it
> interp create i
> i eval {package require Tk}
> interp delete i
> # we can no longer access the accessibility data tables
> catch {tk accessible get_acc_role .e} err od
> puts "after: $err\n$od"
> exit
> }
> # ----
>
> results in (return options dictionary formatted)
>
> $ ./wish9.1 childinterp.tcl
> before: Entry
> after: No table found. You must set the accessibility role first.
> -code 1 -level 0 -errorstack {INNER {invokeStk tk accessible get_acc_role .e}}
> -errorcode NONE -errorinfo {No table found. You must set the accessibility role first.
> while executing
> "tk accessible get_acc_role .e"} -errorline 1
>
> The solution here is simple: declare and initialize TkAccessibilityObject
> as NULL
>
> Tcl_HashTable *TkAccessibilityObject = NULL;
>
> and only allocate and initialize as hash table if NULL in the
> TkAccessibility_Init function
>
> if (!TkAccessibilityObject) {
> TkAccessibilityObject = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
> Tcl_InitHashTable(TkAccessibilityObject, TCL_ONE_WORD_KEYS);
> }
>
> * When widgets are destroyed, the hash tables are not cleaned up, at least
> the TkAccessibilityObject hash table from generic/tkAccessibility.c .
> Even recreating a widget with the same pathname and class will,
> most likely, result in a different tkwin pointer, resulting in an ever
> growing table with lots of unused entries.
> Can't say anything about the GLib based hash tables as I don't know how
> they work.
> I see there's an event handler for widget deletion in
> unix/tkUnixAccessibility.c , but there's no cleanup on the above mentioned
> hash table there. I guess the same applies to both Win and Mac specific
> code.
>
> Regards
>
> --
> Emiliano
>
>
> _______________________________________________
> Tcl-Core mailing list
> Tcl...@li...
> https://lists.sourceforge.net/lists/listinfo/tcl-core
|
|
From: Kevin W. <kw...@co...> - 2025-10-21 23:05:29
|
Hi Emiliano,
Thanks for your input. Please see my responses below:
On 10/21/25 2:52 PM, Emiliano wrote:
> On Sat, 18 Oct 2025 17:07:03 -0400
> Kevin Walzer <kw...@co...> wrote:
>
> Hi Kevin
>
> This is an amazing work, providing a long awaited feature. Thanks !!
I appreciate your good words.
>
> I started testing tka11y branch on X11 (linux, x86_64). So far, I've been able
> to find these issues
>
> * The following line in the configure script is not required to build with
> atk support. Gtk is not needed.
>
> && pkg-config --exists gtk+-3.0 \
I've removed this.
>
> * Starting wish shows a short but visible delay in which the default
> root window is mapped on screen (200x200). This is most noticeable
> starting applications such as tkcon, in which the root window is
> displayed and then withdrawn.
> This delay is in Tk initialization, not process initialization;
> loading Tk in a child interpreter shows the same delay.
There is a fair amount of initialization going on with accessibility on
Wish startup, so this slight delay is probably unavoidable. In any event
I do not see it as a serious issue.
>
> * Test textWind-17.10 (peer widget window configuration test) hangs, and
> closing the tktest window afterwards segfaults.
> I was unable to isolate this hang completely. Simply running the text
> tests with TESTFLAGS="-file text*" doesn't hang, so it must depend on
> something that runs earlier in the test suite.
> Also, can't say anything about any further tests in the test suite.
I do not intend to have the test suite run with a screen reader active -
there is too much overhead with accessibility that may cause conflicts
and failures with the test suite. In fact, some of my recent commits
have been intended to ensure that the accessibility commands do not leak
into the test suite when the screen reader is inactive. Is the above
hang occurring with, or without, a screen reader?
>
> * Widgets must be mapped before using [tk accessible get_acc_*]
> with them. Example
>
> # ---- unmapped.tcl
> pack [entry .e]
> tk accessible add_acc_object .e
> puts [tk accessible get_acc_role .e]
> # ----
> $ ./wish9.1 unmapped.tcl
> No table found. You must set the accessibility role first.
>
> # ---- mapped.tcl
> pack [entry .e]
> tk accessible add_acc_object .e
> update
> puts [tk accessible get_acc_role .e]
> # ----
> $ ./wish9.1 mapped.tcl
> Entry
Yes, this is the intended design. The accessibility bindings are bound
to <Map> events - it's the only way I've found to get them initialized
in a clean manner.
>
> * The data in commands [tk accessible {get|set}_acc_*] is not multi
> interp safe. Running the folowing code
>
> # ---- childinterp.tcl
> pack [entry .e]
> tk accessible add_acc_object .e
> update
> after 2000 {
> puts "before: [tk accessible get_acc_role .e]"
> # create a new Tk application and then delete it
> interp create i
> i eval {package require Tk}
> interp delete i
> # we can no longer access the accessibility data tables
> catch {tk accessible get_acc_role .e} err od
> puts "after: $err\n$od"
> exit
> }
> # ----
>
> results in (return options dictionary formatted)
>
> $ ./wish9.1 childinterp.tcl
> before: Entry
> after: No table found. You must set the accessibility role first.
> -code 1 -level 0 -errorstack {INNER {invokeStk tk accessible get_acc_role .e}}
> -errorcode NONE -errorinfo {No table found. You must set the accessibility role first.
> while executing
> "tk accessible get_acc_role .e"} -errorline 1
To avoid test suite failures, accessibility does not load in child
interpreters. I am documenting this in the man page.
>
> The solution here is simple: declare and initialize TkAccessibilityObject
> as NULL
>
> Tcl_HashTable *TkAccessibilityObject = NULL;
>
> and only allocate and initialize as hash table if NULL in the
> TkAccessibility_Init function
>
> if (!TkAccessibilityObject) {
> TkAccessibilityObject = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
> Tcl_InitHashTable(TkAccessibilityObject, TCL_ONE_WORD_KEYS);
> }
I've added this change because it seems to be make sense, but I am not
changing functionality in child interpreters.
>
> * When widgets are destroyed, the hash tables are not cleaned up, at least
> the TkAccessibilityObject hash table from generic/tkAccessibility.c .
> Even recreating a widget with the same pathname and class will,
> most likely, result in a different tkwin pointer, resulting in an ever
> growing table with lots of unused entries.
> Can't say anything about the GLib based hash tables as I don't know how
> they work.
> I see there's an event handler for widget deletion in
> unix/tkUnixAccessibility.c , but there's no cleanup on the above mentioned
> hash table there. I guess the same applies to both Win and Mac specific
> code.
I've added a cleanup handler for the global TkAccessibilityObject for
app exit.
Thanks,
Kevin
|
|
From: Emiliano <emi...@gm...> - 2025-10-22 01:13:11
Attachments:
cleanup.diff
|
Hi Kevin Se comments below > I do not intend to have the test suite run with a screen reader active - > there is too much overhead with accessibility that may cause conflicts > and failures with the test suite. In fact, some of my recent commits > have been intended to ensure that the accessibility commands do not leak > into the test suite when the screen reader is inactive. Is the above > hang occurring with, or without, a screen reader? The hang occurs without the screen reader active. > I've added this change because it seems to be make sense, but I am not > changing functionality in child interpreters. The problem is that TkAccessibility_Init is run on every child interpreter that loads Tk, but the TkAccessibilityObject hash table should be initialized just once. > I've added a cleanup handler for the global TkAccessibilityObject for > app exit. This is desirable, yes. But the cleanup I had in mind is when widgets are destroyed; if not cleaned up, the entries will still be there even when they are not needed anymore. See attached patch for a simple solution. Regards. -- Emiliano |
|
From: Kevin W. <kw...@co...> - 2025-10-22 12:48:22
|
Hi Emiliano, I committed your patch, thank you. Re-running the test suite on X11 shows a few failures, but none that are related to accessibility, as far as I can tell - and no hangs or segfaults. Regards, Kevin On 10/21/25 9:12 PM, Emiliano wrote: > Hi Kevin > > Se comments below > >> I do not intend to have the test suite run with a screen reader active - >> there is too much overhead with accessibility that may cause conflicts >> and failures with the test suite. In fact, some of my recent commits >> have been intended to ensure that the accessibility commands do not leak >> into the test suite when the screen reader is inactive. Is the above >> hang occurring with, or without, a screen reader? > The hang occurs without the screen reader active. > >> I've added this change because it seems to be make sense, but I am not >> changing functionality in child interpreters. > The problem is that TkAccessibility_Init is run on every child interpreter > that loads Tk, but the TkAccessibilityObject hash table should be initialized > just once. > >> I've added a cleanup handler for the global TkAccessibilityObject for >> app exit. > This is desirable, yes. But the cleanup I had in mind is when widgets are > destroyed; if not cleaned up, the entries will still be there even when > they are not needed anymore. See attached patch for a simple solution. > > Regards. > > > > _______________________________________________ > Tcl-Core mailing list > Tcl...@li... > https://lists.sourceforge.net/lists/listinfo/tcl-core |