Tk Source Code

Check-in [a6258251]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Additional cleanup
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | tka11y
Files: files | file ages | folders
SHA3-256: a6258251a5f1210704c76d305c1fa2ea963059da86b090a80e963c9d4b6d126d
User & Date: kevin_walzer 2025-08-15 19:13:19.078
Context
2025-08-15
21:44
Accessibility names/descriptions now read by Orca check-in: 6a72df82 user: kevin_walzer tags: tka11y
19:13
Additional cleanup check-in: a6258251 user: kevin_walzer tags: tka11y
18:14
Simplify threading; re-work name, description, value cache updating check-in: 3542beab user: kevin_walzer tags: tka11y
Changes
Unified Diff Ignore Whitespace Patch
Changes to unix/tkUnixAccessibility.c.
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
static gpointer get_window_handle_main(gpointer data);
/* Added: New helper for root/screen coordinates to fix extents */
static gpointer get_root_coords_main(gpointer data);

/* Signal emission helpers. */
static gpointer emit_children_changed_add(gpointer data);
static gpointer emit_children_changed_remove(gpointer data);
static gboolean emit_value_changed(gpointer data);
static gboolean emit_text_selection_changed(gpointer data);
static gboolean emit_focus_event(gpointer data);
static gboolean emit_state_change(gpointer data);
static gboolean emit_bounds_changed(gpointer data);

/* ATK interface implementations. */
static void tk_get_extents(AtkComponent *component, gint *x, gint *y, gint *width, gint *height, AtkCoordType coord_type);
static gboolean tk_contains(AtkComponent *component, gint x, gint y, AtkCoordType coord_type);
static void tk_atk_component_interface_init(AtkComponentIface *iface);
static gint tk_get_n_children(AtkObject *obj);
static AtkObject *tk_ref_child(AtkObject *obj, gint i);
static AtkRole GetAtkRoleForWidget(Tk_Window win);
static AtkRole tk_get_role(AtkObject *obj);
static gchar *GetAtkNameForWidget(Tk_Window win);
static const gchar *tk_get_name(AtkObject *obj);
static void tk_set_name(AtkObject *obj, const gchar *name);
static gchar *GetAtkDescriptionForWidget(Tk_Window win);
static const gchar *tk_get_description(AtkObject *obj);
static gchar *GetAtkValueforTkWidget(Tk_Window win);
static void tk_get_current_value(AtkValue *obj, GValue *value);
static void tk_atk_value_interface_init(AtkValueIface *iface);
static AtkStateSet *tk_ref_state_set(AtkObject *obj);
static gboolean tk_action_do_action(AtkAction *action, gint i);
static gint tk_action_get_n_actions(AtkAction *action);
static const gchar *tk_action_get_name(AtkAction *action, gint i);
static void tk_atk_action_interface_init(AtkActionIface *iface);







|
|
|
|
|














|







179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
static gpointer get_window_handle_main(gpointer data);
/* Added: New helper for root/screen coordinates to fix extents */
static gpointer get_root_coords_main(gpointer data);

/* Signal emission helpers. */
static gpointer emit_children_changed_add(gpointer data);
static gpointer emit_children_changed_remove(gpointer data);
static gpointer emit_value_changed(gpointer data);
static gpointer emit_text_selection_changed(gpointer data);
static gpointer emit_focus_event(gpointer data);
static gpointer emit_state_change(gpointer data);
static gpointer emit_bounds_changed(gpointer data);

/* ATK interface implementations. */
static void tk_get_extents(AtkComponent *component, gint *x, gint *y, gint *width, gint *height, AtkCoordType coord_type);
static gboolean tk_contains(AtkComponent *component, gint x, gint y, AtkCoordType coord_type);
static void tk_atk_component_interface_init(AtkComponentIface *iface);
static gint tk_get_n_children(AtkObject *obj);
static AtkObject *tk_ref_child(AtkObject *obj, gint i);
static AtkRole GetAtkRoleForWidget(Tk_Window win);
static AtkRole tk_get_role(AtkObject *obj);
static gchar *GetAtkNameForWidget(Tk_Window win);
static const gchar *tk_get_name(AtkObject *obj);
static void tk_set_name(AtkObject *obj, const gchar *name);
static gchar *GetAtkDescriptionForWidget(Tk_Window win);
static const gchar *tk_get_description(AtkObject *obj);
static gchar *GetAtkValueForWidget(Tk_Window win);
static void tk_get_current_value(AtkValue *obj, GValue *value);
static void tk_atk_value_interface_init(AtkValueIface *iface);
static AtkStateSet *tk_ref_state_set(AtkObject *obj);
static gboolean tk_action_do_action(AtkAction *action, gint i);
static gint tk_action_get_n_actions(AtkAction *action);
static const gchar *tk_action_get_name(AtkAction *action, gint i);
static void tk_atk_action_interface_init(AtkActionIface *iface);
233
234
235
236
237
238
239
240
241

242
243
244
245
246
247
248
/* Cache update functions. */
static void UpdateGeometryCache(TkAtkAccessible *acc);
static void UpdateNameCache(TkAtkAccessible *acc);
static void UpdateDescriptionCache(TkAtkAccessible *acc);
static void UpdateValueCache(TkAtkAccessible *acc);
static void UpdateRoleCache(TkAtkAccessible *acc);
static void UpdateStateCache(TkAtkAccessible *acc);
static void UpdateChildrenCache(TkAtkAccessible *acc);
static gboolean DeferredChildrenUpdate(gpointer user_data);


/* Event handlers. */
void TkAtkAccessible_RegisterEventHandlers(Tk_Window tkwin, void *tkAccessible);
static void TkAtkAccessible_DestroyHandler(ClientData clientData, XEvent *eventPtr);
static void TkAtkAccessible_ConfigureHandler(ClientData clientData, XEvent *eventPtr);
static void TkAtkAccessible_MapHandler(ClientData clientData, XEvent *eventPtr);
static void TkAtkAccessible_UnmapHandler(ClientData clientData, XEvent *eventPtr);







|

>







233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/* Cache update functions. */
static void UpdateGeometryCache(TkAtkAccessible *acc);
static void UpdateNameCache(TkAtkAccessible *acc);
static void UpdateDescriptionCache(TkAtkAccessible *acc);
static void UpdateValueCache(TkAtkAccessible *acc);
static void UpdateRoleCache(TkAtkAccessible *acc);
static void UpdateStateCache(TkAtkAccessible *acc);
static void UpdateChildrenCache(ClientData object);
static gboolean DeferredChildrenUpdate(gpointer user_data);
static void DeferredChildrenUpdateTcl(ClientData clientData);

/* Event handlers. */
void TkAtkAccessible_RegisterEventHandlers(Tk_Window tkwin, void *tkAccessible);
static void TkAtkAccessible_DestroyHandler(ClientData clientData, XEvent *eventPtr);
static void TkAtkAccessible_ConfigureHandler(ClientData clientData, XEvent *eventPtr);
static void TkAtkAccessible_MapHandler(ClientData clientData, XEvent *eventPtr);
static void TkAtkAccessible_UnmapHandler(ClientData clientData, XEvent *eventPtr);
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461


462



463
464
465
466
467
468
469
    return g_utf8_make_valid(str, -1);
}

/* Get accessible value. */
static gpointer get_atk_value_for_widget_main(gpointer data) 
{
    MainThreadData *mt_data = (MainThreadData *)data;
    return (gpointer)GetAtkValueForWidget(mt_data->tkwin);
}

/* Get accessible name. */
static gpointer get_atk_name_for_widget_main(gpointer data) 
{
    MainThreadData *mt_data = (MainThreadData *)data;
    return (gpointer)(GetAtkNameForWidget(mt_data->tkwin));
}








/* Get accessible role. */
static gpointer get_atk_role_for_widget_main(gpointer data) 
{
    MainThreadData *mt_data = (MainThreadData *)data;
    return (gpointer)(uintptr_t)GetAtkRoleForWidget(mt_data->tkwin);
}







|






|


>
>
|
>
>
>







446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
    return g_utf8_make_valid(str, -1);
}

/* Get accessible value. */
static gpointer get_atk_value_for_widget_main(gpointer data) 
{
    MainThreadData *mt_data = (MainThreadData *)data;
    return (gchar*)GetAtkValueForWidget(mt_data->tkwin);
}

/* Get accessible name. */
static gpointer get_atk_name_for_widget_main(gpointer data) 
{
    MainThreadData *mt_data = (MainThreadData *)data;
    return (gchar*)(GetAtkNameForWidget(mt_data->tkwin));
}

/* Get accessible description. */
static gpointer get_atk_description_for_widget_main(gpointer data) 
{
    MainThreadData *mt_data = (MainThreadData *)data;
    return (gchar*)(GetAtkDescriptionForWidget(mt_data->tkwin));
}

/* Get accessible role. */
static gpointer get_atk_role_for_widget_main(gpointer data) 
{
    MainThreadData *mt_data = (MainThreadData *)data;
    return (gpointer)(uintptr_t)GetAtkRoleForWidget(mt_data->tkwin);
}
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
    g_free(crd);
    
    return G_SOURCE_REMOVE;
}


/* Emit value-changed signal. */
static gboolean emit_value_changed(gpointer data)
{
    if (data) {
        ValueChangedData *vcd = (ValueChangedData *)data;
        g_signal_emit_by_name(vcd->obj, "value-changed", &vcd->value);
        g_value_unset(&vcd->value);
        g_free(vcd);
    }
    return G_SOURCE_REMOVE;
}

/* Emit text-selection-changed signal. */
static gboolean emit_text_selection_changed(gpointer data)
{
    if (data) {
        g_signal_emit_by_name(data, "text-selection-changed");
    }
    return G_SOURCE_REMOVE;
}

/* Emit focus-event signal. */
static gboolean emit_focus_event(gpointer data)
{
    if (data) {
        FocusEventData *fed = (FocusEventData *)data;
        g_signal_emit_by_name(fed->obj, "focus-event", fed->state);
        g_free(fed);
    }
    return G_SOURCE_REMOVE;
}

/* Emit state-change signal. */
static gboolean emit_state_change(gpointer data)
{
    if (data) {
        StateChangeData *scd = (StateChangeData *)data;
        g_signal_emit_by_name(scd->obj, "state-change", scd->name, scd->state);
        g_free(scd->name);
        g_free(scd);
    }
    return G_SOURCE_REMOVE;
}

/* Emit bounds-changed signal. */
static gboolean emit_bounds_changed(gpointer data)
{
    if (data) {
        BoundsChangedData *bcd = (BoundsChangedData *)data;
        g_signal_emit_by_name(bcd->obj, "bounds-changed", &bcd->rect);
        g_free(bcd);
    }
    return G_SOURCE_REMOVE;







|











|








|










|











|







689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
    g_free(crd);
    
    return G_SOURCE_REMOVE;
}


/* Emit value-changed signal. */
static gpointer emit_value_changed(gpointer data)
{
    if (data) {
        ValueChangedData *vcd = (ValueChangedData *)data;
        g_signal_emit_by_name(vcd->obj, "value-changed", &vcd->value);
        g_value_unset(&vcd->value);
        g_free(vcd);
    }
    return G_SOURCE_REMOVE;
}

/* Emit text-selection-changed signal. */
static gpointer emit_text_selection_changed(gpointer data)
{
    if (data) {
        g_signal_emit_by_name(data, "text-selection-changed");
    }
    return G_SOURCE_REMOVE;
}

/* Emit focus-event signal. */
static gpointer emit_focus_event(gpointer data)
{
    if (data) {
        FocusEventData *fed = (FocusEventData *)data;
        g_signal_emit_by_name(fed->obj, "focus-event", fed->state);
        g_free(fed);
    }
    return G_SOURCE_REMOVE;
}

/* Emit state-change signal. */
static gpointer emit_state_change(gpointer data)
{
    if (data) {
        StateChangeData *scd = (StateChangeData *)data;
        g_signal_emit_by_name(scd->obj, "state-change", scd->name, scd->state);
        g_free(scd->name);
        g_free(scd);
    }
    return G_SOURCE_REMOVE;
}

/* Emit bounds-changed signal. */
static gpointer emit_bounds_changed(gpointer data)
{
    if (data) {
        BoundsChangedData *bcd = (BoundsChangedData *)data;
        g_signal_emit_by_name(bcd->obj, "bounds-changed", &bcd->rect);
        g_free(bcd);
    }
    return G_SOURCE_REMOVE;
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
	acc->cached_name = sanitize_utf8(name); 
    }
    atk_object_set_name(obj, name);
}

static gchar *GetAtkDescriptionForWidget(Tk_Window win)
{
    if (!win) return;

    gchar *description = NULL;

    MainThreadData data = {win, NULL, NULL, NULL, "description"};
    Tcl_HashEntry *hPtr = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_main, &data);
    if (hPtr) {
	Tcl_HashTable *AccessibleAttributes = (Tcl_HashTable *)RunOnMainThread(get_hash_value_main, &data);







|







935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
	acc->cached_name = sanitize_utf8(name); 
    }
    atk_object_set_name(obj, name);
}

static gchar *GetAtkDescriptionForWidget(Tk_Window win)
{
    if (!win) return NULL;

    gchar *description = NULL;

    MainThreadData data = {win, NULL, NULL, NULL, "description"};
    Tcl_HashEntry *hPtr = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_main, &data);
    if (hPtr) {
	Tcl_HashTable *AccessibleAttributes = (Tcl_HashTable *)RunOnMainThread(get_hash_value_main, &data);
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978


/*
 * Functions to map accessible value to ATK using
 * AtkValue interface.
 */

static gchar *GetAtkValueforTkWidget(Tk_Window win)
{
    if (!win) return;
    
    gchar *value = NULL;

    MainThreadData data = {win, NULL, NULL, NULL, "value"};
    Tcl_HashEntry *hPtr = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_main, &data);
    if (hPtr) {
	Tcl_HashTable *AccessibleAttributes = (Tcl_HashTable *)RunOnMainThread(get_hash_value_main, &data);







|

|







968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984


/*
 * Functions to map accessible value to ATK using
 * AtkValue interface.
 */

static gchar *GetAtkValueForWidget(Tk_Window win)
{
    if (!win) return NULL;
    
    gchar *value = NULL;

    MainThreadData data = {win, NULL, NULL, NULL, "value"};
    Tcl_HashEntry *hPtr = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_main, &data);
    if (hPtr) {
	Tcl_HashTable *AccessibleAttributes = (Tcl_HashTable *)RunOnMainThread(get_hash_value_main, &data);
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
    
    if (!tkwin || !parent_obj || !G_IS_OBJECT(parent_obj)) {
	g_warning("RegisterChildWidgets: Invalid tkwin or parent_obj");
	return;
    }

    TkAtkAccessible *acc = (TkAtkAccessible *)parent_obj;
    /* Update children cache asynchronously. */
    UpdateChildrenCache(acc);
}


/*
 * Root window setup. These are the foundation of the
 * accessibility object system in ATK. atk_get_root() is the
 * critical link to at-spi - it is called by the ATK system







<
|







1338
1339
1340
1341
1342
1343
1344

1345
1346
1347
1348
1349
1350
1351
1352
    
    if (!tkwin || !parent_obj || !G_IS_OBJECT(parent_obj)) {
	g_warning("RegisterChildWidgets: Invalid tkwin or parent_obj");
	return;
    }

    TkAtkAccessible *acc = (TkAtkAccessible *)parent_obj;

    Tcl_DoWhenIdle(UpdateChildrenCache, (ClientData) acc);
}


/*
 * Root window setup. These are the foundation of the
 * accessibility object system in ATK. atk_get_root() is the
 * critical link to at-spi - it is called by the ATK system
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
    /* Mark creation as complete. */
    acc->is_being_created = FALSE;
    
    /* Remove from creation tracking. */
    g_hash_table_remove(creation_in_progress, tkwin);

    /* Shedule children update for LATER, not now. This breaks the recursion cycle. */
    g_idle_add_full(G_PRIORITY_LOW, (GSourceFunc)DeferredChildrenUpdate, acc, NULL);

    return obj;
}

/*
 * Functions to map Tk window to its corresponding Atk object.
 */







|







1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
    /* Mark creation as complete. */
    acc->is_being_created = FALSE;
    
    /* Remove from creation tracking. */
    g_hash_table_remove(creation_in_progress, tkwin);

    /* Shedule children update for LATER, not now. This breaks the recursion cycle. */
	Tcl_DoWhenIdle(DeferredChildrenUpdateTcl, acc);

    return obj;
}

/*
 * Functions to map Tk window to its corresponding Atk object.
 */
1687
1688
1689
1690
1691
1692
1693
1694
1695

1696
1697
1698
1699
1700
1701
1702
		break;
	    }
	    parent = (Tk_Window)RunOnMainThread(get_window_parent_main, &(MainThreadData){parent, NULL, NULL, NULL, NULL});
	}
    }
}

static void UpdateChildrenCache(TkAtkAccessible *acc)
{

    if (!acc || !acc->tkwin || !acc->interp) return;
    if (acc->is_being_destroyed || acc->is_being_created) return;

    /* Prevent recursive calls. */
    static GHashTable *updating_objects = NULL;
    if (!updating_objects) {
        updating_objects = g_hash_table_new(g_direct_hash, g_direct_equal);







|

>







1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
		break;
	    }
	    parent = (Tk_Window)RunOnMainThread(get_window_parent_main, &(MainThreadData){parent, NULL, NULL, NULL, NULL});
	}
    }
}

static void UpdateChildrenCache(ClientData object)
{
	TkAtkAccessible *acc = (TkAtkAccessible*) object;
    if (!acc || !acc->tkwin || !acc->interp) return;
    if (acc->is_being_destroyed || acc->is_being_created) return;

    /* Prevent recursive calls. */
    static GHashTable *updating_objects = NULL;
    if (!updating_objects) {
        updating_objects = g_hash_table_new(g_direct_hash, g_direct_equal);
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820


1821


1822
1823
1824
1825
1826
1827
1828
1829
                    if (child_obj) {
                        TkAtkAccessible_RegisterEventHandlers(child, (TkAtkAccessible *)child_obj);
                    }
                }
            }
            
            /* Update the children cache now that all objects exist. */
            UpdateChildrenCache(acc);
        }
    }

    return G_SOURCE_REMOVE;
}








/*
 *----------------------------------------------------------------------
 *
 * Event handlers - update Tk and ATK in response to various X events. 
 *
 *----------------------------------------------------------------------







|






>
>
|
>
>
|







1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
                    if (child_obj) {
                        TkAtkAccessible_RegisterEventHandlers(child, (TkAtkAccessible *)child_obj);
                    }
                }
            }
            
            /* Update the children cache now that all objects exist. */
            UpdateChildrenCache((ClientData)acc);
        }
    }

    return G_SOURCE_REMOVE;
}

/* Run DeferredChildrenUpdate on Tcl's event loop. */
static void DeferredChildrenUpdateTcl(ClientData clientData) 
{
    TkAtkAccessible *acc = (TkAtkAccessible *)clientData;
    DeferredChildrenUpdate(acc);
}

/*
 *----------------------------------------------------------------------
 *
 * Event handlers - update Tk and ATK in response to various X events. 
 *
 *----------------------------------------------------------------------
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
        UpdateNameCache(acc);
        UpdateDescriptionCache(acc);
        UpdateValueCache(acc);
        UpdateRoleCache(acc);
        UpdateStateCache(acc);
        
        /* Update children cache asynchronously to avoid recursion */
        g_idle_add((GSourceFunc)UpdateChildrenCache, acc);
    }

    updating = FALSE;

    /* Emit signals for valid objects only */
    if (!acc->is_being_destroyed && acc->width > 0 && acc->height > 0 && acc->is_mapped) {
        BoundsChangedData *bcd = g_new0(BoundsChangedData, 1);







|







1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
        UpdateNameCache(acc);
        UpdateDescriptionCache(acc);
        UpdateValueCache(acc);
        UpdateRoleCache(acc);
        UpdateStateCache(acc);
        
        /* Update children cache asynchronously to avoid recursion */
        Tcl_DoWhenIdle(UpdateChildrenCache, (ClientData) acc);
    }

    updating = FALSE;

    /* Emit signals for valid objects only */
    if (!acc->is_being_destroyed && acc->width > 0 && acc->height > 0 && acc->is_mapped) {
        BoundsChangedData *bcd = g_new0(BoundsChangedData, 1);
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
    TkAtkAccessible *acc = (TkAtkAccessible *)clientData;
    if (!acc || !acc->tkwin || !acc->interp) {
	g_warning("TkAtkAccessible_CreateHandler: Invalid or null acc/tkwin/interp");
	return;
    }

    /* Update children cache asynchronously. */
    RunOnMainThread((GSourceFunc)UpdateChildrenCache, acc);
}



/*
 *----------------------------------------------------------------------
 *







|







2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
    TkAtkAccessible *acc = (TkAtkAccessible *)clientData;
    if (!acc || !acc->tkwin || !acc->interp) {
	g_warning("TkAtkAccessible_CreateHandler: Invalid or null acc/tkwin/interp");
	return;
    }

    /* Update children cache asynchronously. */
    Tcl_DoWhenIdle(UpdateChildrenCache, (ClientData) acc);
}



/*
 *----------------------------------------------------------------------
 *