Tk Source Code

Check-in [3542beab]
Login

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

Overview
Comment:Simplify threading; re-work name, description, value cache updating
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | tka11y
Files: files | file ages | folders
SHA3-256: 3542beabac2a312795d82fd9bf93d7b252836362fe009106e8233be215c62b88
User & Date: kevin_walzer 2025-08-15 18:14:09.592
Context
2025-08-15
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
01:40
Fix typo check-in: c22c6911 user: kevin_walzer tags: tka11y
Changes
Unified Diff Ignore Whitespace Patch
Changes to unix/tkUnixAccessibility.c.
150
151
152
153
154
155
156
157
158
159
160
161
162



163
164
165
166
167
168
169
static GMainContext *glib_context = NULL;
static gboolean in_process_pending = FALSE;
static GHashTable *creation_in_progress = NULL;  /* Track windows being created */


/* Thread management functions. */
gpointer RunOnMainThread(gpointer (*func)(gpointer user_data), gpointer user_data);
void RunOnMainThreadAsync(GSourceFunc func, gpointer user_data);
void EmitBoundsChanged(AtkObject *obj, gint x, gint y, gint width, gint height);
static gboolean run_main_thread_callback(gpointer data);

/* Helper functions for main thread operations. */
static gpointer get_atk_role_for_widget_main(gpointer data);



static gpointer get_window_name_main(gpointer data);
static gpointer get_window_geometry_main(gpointer data);
static gpointer is_window_mapped_main(gpointer data);
static gpointer get_focus_window_main(gpointer data);
static gpointer get_window_parent_main(gpointer data);
static gpointer make_window_exist_main(gpointer data);
static gpointer map_window_main(gpointer data);







<





>
>
>







150
151
152
153
154
155
156

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
static GMainContext *glib_context = NULL;
static gboolean in_process_pending = FALSE;
static GHashTable *creation_in_progress = NULL;  /* Track windows being created */


/* Thread management functions. */
gpointer RunOnMainThread(gpointer (*func)(gpointer user_data), gpointer user_data);

void EmitBoundsChanged(AtkObject *obj, gint x, gint y, gint width, gint height);
static gboolean run_main_thread_callback(gpointer data);

/* Helper functions for main thread operations. */
static gpointer get_atk_role_for_widget_main(gpointer data);
static gpointer get_atk_name_for_widget_main(gpointer data);
static gpointer get_atk_description_for_widget_main(gpointer data);
static gpointer get_atk_value_for_widget_main(gpointer data);
static gpointer get_window_name_main(gpointer data);
static gpointer get_window_geometry_main(gpointer data);
static gpointer is_window_mapped_main(gpointer data);
static gpointer get_focus_window_main(gpointer data);
static gpointer get_window_parent_main(gpointer data);
static gpointer make_window_exist_main(gpointer data);
static gpointer map_window_main(gpointer data);
191
192
193
194
195
196
197

198
199

200

201
202
203
204
205
206
207
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 const gchar *tk_get_name(AtkObject *obj);
static void tk_set_name(AtkObject *obj, const gchar *name);

static const gchar *tk_get_description(AtkObject *obj);

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);







>


>

>







193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
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);
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355

    g_mutex_clear(&call.mutex);
    g_cond_clear(&call.cond);

    return call.result;
}

/* Run a function on the main thread asynchronously (no wait) */
void RunOnMainThreadAsync(GSourceFunc func, gpointer user_data)
{
    if (!glib_context) {
        func(user_data);
        return;
    }

    if (g_main_context_is_owner(glib_context)) {
        func(user_data);
    } else {
        g_main_context_invoke(glib_context, func, user_data);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * GLib-Tcl event loop integration
 *
 *----------------------------------------------------------------------
 */







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







332
333
334
335
336
337
338















339
340
341
342
343
344
345

    g_mutex_clear(&call.mutex);
    g_cond_clear(&call.cond);

    return call.result;
}
















/*
 *----------------------------------------------------------------------
 *
 * GLib-Tcl event loop integration
 *
 *----------------------------------------------------------------------
 */
451
452
453
454
455
456
457















458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
/* Helper function to integrate strings. */         
static gchar *sanitize_utf8(const gchar *str) 
{
    if (!str) return NULL;
    return g_utf8_make_valid(str, -1);
}

















/* 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);
}

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

/* Get window geometry. */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








|







441
442
443
444
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
470
471
472
473
474
475
476
477
478
/* Helper function to integrate strings. */         
static gchar *sanitize_utf8(const gchar *str) 
{
    if (!str) return NULL;
    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);
}

/* Get accessible main window name. */
static gpointer get_window_name_main(gpointer data) 
{
    MainThreadData *mt_data = (MainThreadData *)data;
    return (gpointer)Tk_PathName(mt_data->tkwin);
}

/* Get window geometry. */
878
879
880
881
882
883
884























885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904























905
906
907
908
909
910
911
912
913
914
915
916
























917
918
919
920
921
922
923
    return acc->cached_role;
}

/*
 * Name and description getters
 * for Tk-ATK objects.
 */
























static const gchar *tk_get_name(AtkObject *obj)
{
    TkAtkAccessible *acc = (TkAtkAccessible *)obj;
    return acc->cached_name;
}

static void tk_set_name(AtkObject *obj, const gchar *name)
{
    TkAtkAccessible *acc = (TkAtkAccessible *)obj;

    if (!acc) return; 

    if (obj == tk_root_accessible) {
	/* Free old cached name.  */
	g_free(acc->cached_name);
	acc->cached_name = sanitize_utf8(name); 
    }
    atk_object_set_name(obj, name);
}
























static const gchar *tk_get_description(AtkObject *obj)
{
    TkAtkAccessible *acc = (TkAtkAccessible *)obj;
    return acc->cached_description;
}


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
























 
static void tk_get_current_value(AtkValue *obj, GValue *value)
{
    TkAtkAccessible *acc = (TkAtkAccessible *)ATK_OBJECT(obj);
    g_value_init(value, G_TYPE_STRING);
    g_value_set_string(value, acc->cached_value);
}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>












>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
    return acc->cached_role;
}

/*
 * Name and description getters
 * for Tk-ATK objects.
 */

static gchar *GetAtkNameForWidget(Tk_Window win)
{
    if (!win) return NULL;
    
    gchar *name = NULL;

    MainThreadData data = {win, NULL, NULL, NULL, "name"};
    Tcl_HashEntry *hPtr = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_main, &data);
    if (hPtr) {
	Tcl_HashTable *AccessibleAttributes = (Tcl_HashTable *)RunOnMainThread(get_hash_value_main, &data);
	if (AccessibleAttributes) {
	    Tcl_HashEntry *hPtr2 = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_by_key_main, &data);
	    if (hPtr2) {
		const char *result = (const char *)RunOnMainThread(get_hash_string_value_main, &data);
		if (result) {
		    name = sanitize_utf8(result);
		}
	    }
	}
    }
    return name;
}

static const gchar *tk_get_name(AtkObject *obj)
{
    TkAtkAccessible *acc = (TkAtkAccessible *)obj;
    return acc->cached_name;
}

static void tk_set_name(AtkObject *obj, const gchar *name)
{
    TkAtkAccessible *acc = (TkAtkAccessible *)obj;

    if (!acc) return; 

    if (obj == tk_root_accessible) {
	/* Free old cached name.  */
	g_free(acc->cached_name);
	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);
	if (AccessibleAttributes) {
	    Tcl_HashEntry *hPtr2 = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_by_key_main, &data);
	    if (hPtr2) {
		const char *result = (const char *)RunOnMainThread(get_hash_string_value_main, &data);
		if (result) {
		    description = sanitize_utf8(result);
		}
	    }
	}
    }
    return description;
}

static const gchar *tk_get_description(AtkObject *obj)
{
    TkAtkAccessible *acc = (TkAtkAccessible *)obj;
    return acc->cached_description;
}


/*
 * 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);
	if (AccessibleAttributes) {
	    Tcl_HashEntry *hPtr2 = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_by_key_main, &data);
	    if (hPtr2) {
		const char *result = (const char *)RunOnMainThread(get_hash_string_value_main, &data);
		if (result) {
		    value = sanitize_utf8(result);
		}
	    }
	}
    }
    return value;
}

 
static void tk_get_current_value(AtkValue *obj, GValue *value)
{
    TkAtkAccessible *acc = (TkAtkAccessible *)ATK_OBJECT(obj);
    g_value_init(value, G_TYPE_STRING);
    g_value_set_string(value, acc->cached_value);
}
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
        AddChildToParent((TkAtkAccessible *)parent_obj, obj);
        
        /* Emit child addition signal */
        ChildrenChangedAddData *cad = g_new0(ChildrenChangedAddData, 1);
        cad->parent = g_object_ref(parent_obj);
        cad->index = g_list_length(((TkAtkAccessible *)parent_obj)->children) - 1;
        cad->child = g_object_ref(obj);
        RunOnMainThreadAsync(emit_children_changed_add, cad);
    } else {
        g_warning("TkCreateAccessibleAtkObject: Invalid parent for %s", path);
        g_hash_table_remove(creation_in_progress, tkwin);  /* Clean up tracking */
        g_object_unref(obj);
        return NULL;
    }








|







1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
        AddChildToParent((TkAtkAccessible *)parent_obj, obj);
        
        /* Emit child addition signal */
        ChildrenChangedAddData *cad = g_new0(ChildrenChangedAddData, 1);
        cad->parent = g_object_ref(parent_obj);
        cad->index = g_list_length(((TkAtkAccessible *)parent_obj)->children) - 1;
        cad->child = g_object_ref(obj);
        RunOnMainThread(emit_children_changed_add, cad);
    } else {
        g_warning("TkCreateAccessibleAtkObject: Invalid parent for %s", path);
        g_hash_table_remove(creation_in_progress, tkwin);  /* Clean up tracking */
        g_object_unref(obj);
        return NULL;
    }

1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
        }
        g_free(geometry);
    }
}

static void UpdateNameCache(TkAtkAccessible *acc)
{
    if (!acc || !acc->tkwin || !acc->interp) return;

    g_free(acc->cached_name);
    acc->cached_name = NULL;

    MainThreadData data = {acc->tkwin, acc->interp, NULL, NULL, "name"};
    Tcl_HashEntry *hPtr = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_main, &data);
    if (hPtr) {
	Tcl_HashTable *AccessibleAttributes = (Tcl_HashTable *)RunOnMainThread(get_hash_value_main, &data);
	if (AccessibleAttributes) {
	    Tcl_HashEntry *hPtr2 = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_by_key_main, &data);
	    if (hPtr2) {
		const char *result = (const char *)RunOnMainThread(get_hash_string_value_main, &data);
		if (result) {
		    acc->cached_name = sanitize_utf8(result);
		}
	    }
	}
    }

    if (!acc->cached_name) {
	acc->cached_name = sanitize_utf8((const char *)RunOnMainThread(get_window_name_main, &data));
    }
}

static void UpdateDescriptionCache(TkAtkAccessible *acc)
{
    if (!acc || !acc->tkwin) return;

    g_free(acc->cached_description);
    acc->cached_description = NULL;

    MainThreadData data = {acc->tkwin, acc->interp, 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);
	if (AccessibleAttributes) {
	    Tcl_HashEntry *hPtr2 = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_by_key_main, &data);
	    if (hPtr2) {
		const char *result = (const char *)RunOnMainThread(get_hash_string_value_main, &data);
		if (result) {
		    acc->cached_description = sanitize_utf8(result);
		}
	    }
	}
    }
}

static void UpdateValueCache(TkAtkAccessible *acc)
{
    if (!acc || !acc->tkwin) return;

    g_free(acc->cached_value);
    acc->cached_value = NULL;

    MainThreadData data = {acc->tkwin, acc->interp, 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);
	if (AccessibleAttributes) {
	    Tcl_HashEntry *hPtr2 = (Tcl_HashEntry *)RunOnMainThread(find_hash_entry_by_key_main, &data);
	    if (hPtr2) {
		const char *result = (const char *)RunOnMainThread(get_hash_string_value_main, &data);
		if (result) {
		    acc->cached_value = sanitize_utf8(result);
		}
	    }
	}
    }

    if (!acc->cached_value) {
	acc->cached_value = sanitize_utf8("");
    }
}

static void UpdateRoleCache(TkAtkAccessible *acc)
{
    if (!acc || !acc->tkwin) return;
    MainThreadData data = {acc->tkwin, acc->interp, NULL, NULL, "role"};
    acc->cached_role = (AtkRole)(uintptr_t)RunOnMainThread(get_atk_role_for_widget_main, &data);







|
<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<





<
<
<
<

<
<
<
<
<
<
|
<
<
<
<
<
<





<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<







1638
1639
1640
1641
1642
1643
1644
1645




1646













1647



1648
1649
1650
1651
1652




1653






1654






1655
1656
1657
1658
1659




1660















1661

1662
1663
1664
1665
1666
1667
1668
        }
        g_free(geometry);
    }
}

static void UpdateNameCache(TkAtkAccessible *acc)
{
    if (!acc || !acc->tkwin) return;




    MainThreadData data = {acc->tkwin, acc->interp, NULL, NULL, "name"};













    acc->cached_name = (gchar*)RunOnMainThread(get_atk_name_for_widget_main, &data);



}

static void UpdateDescriptionCache(TkAtkAccessible *acc)
{
    if (!acc || !acc->tkwin) return;




    MainThreadData data = {acc->tkwin, acc->interp, NULL, NULL, "description"};






    acc->cached_value = (gchar*)RunOnMainThread(get_atk_description_for_widget_main, &data);






}

static void UpdateValueCache(TkAtkAccessible *acc)
{
    if (!acc || !acc->tkwin) return;




    MainThreadData data = {acc->tkwin, acc->interp, NULL, NULL, "value"};















    acc->cached_value = (gchar*)RunOnMainThread(get_atk_value_for_widget_main, &data);

}

static void UpdateRoleCache(TkAtkAccessible *acc)
{
    if (!acc || !acc->tkwin) return;
    MainThreadData data = {acc->tkwin, acc->interp, NULL, NULL, "role"};
    acc->cached_role = (AtkRole)(uintptr_t)RunOnMainThread(get_atk_role_for_widget_main, &data);
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
        if (old_child && G_IS_OBJECT(old_child)) {
            if (!g_list_find(new_children, old_child)) {
                gint old_index = g_list_index(old_children, old_child);
                ChildrenChangedRemoveData *crd = g_new0(ChildrenChangedRemoveData, 1);
                crd->parent = g_object_ref(ATK_OBJECT(acc));
                crd->index = old_index;
                crd->child = g_object_ref(old_child);
                RunOnMainThreadAsync(emit_children_changed_remove, crd);
            }
            g_object_unref(old_child);
        }
        iter = g_list_next(iter);
    }
    g_list_free(old_children);

    /* Emit addition signals for genuinely new children. */
    iter = new_children;
    gint index = 0;
    while (iter) {
        AtkObject *new_child = (AtkObject *)iter->data;
        if (new_child && !g_list_find(old_children, new_child)) {
            ChildrenChangedAddData *cad = g_new0(ChildrenChangedAddData, 1);
            cad->parent = g_object_ref(ATK_OBJECT(acc));
            cad->index = index;
            cad->child = g_object_ref(new_child);
            RunOnMainThreadAsync(emit_children_changed_add, cad);
        }
        iter = g_list_next(iter);
        index++;
    }

    g_hash_table_remove(updating_objects, acc);
}







|

















|







1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
        if (old_child && G_IS_OBJECT(old_child)) {
            if (!g_list_find(new_children, old_child)) {
                gint old_index = g_list_index(old_children, old_child);
                ChildrenChangedRemoveData *crd = g_new0(ChildrenChangedRemoveData, 1);
                crd->parent = g_object_ref(ATK_OBJECT(acc));
                crd->index = old_index;
                crd->child = g_object_ref(old_child);
                RunOnMainThread(emit_children_changed_remove, crd);
            }
            g_object_unref(old_child);
        }
        iter = g_list_next(iter);
    }
    g_list_free(old_children);

    /* Emit addition signals for genuinely new children. */
    iter = new_children;
    gint index = 0;
    while (iter) {
        AtkObject *new_child = (AtkObject *)iter->data;
        if (new_child && !g_list_find(old_children, new_child)) {
            ChildrenChangedAddData *cad = g_new0(ChildrenChangedAddData, 1);
            cad->parent = g_object_ref(ATK_OBJECT(acc));
            cad->index = index;
            cad->child = g_object_ref(new_child);
            RunOnMainThread(emit_children_changed_add, cad);
        }
        iter = g_list_next(iter);
        index++;
    }

    g_hash_table_remove(updating_objects, acc);
}
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
            RemoveChildFromParent(parent_acc, ATK_OBJECT(tkAccessible));
            
            /* Emit signal safely. */
            ChildrenChangedRemoveData *crd = g_new0(ChildrenChangedRemoveData, 1);
            crd->parent = g_object_ref(parent);  /* Take explicit ref */
            crd->index = index;
            crd->child = g_object_ref(ATK_OBJECT(tkAccessible));  /* Take explicit ref. */
            RunOnMainThreadAsync(emit_children_changed_remove, crd);
        }
        g_mutex_unlock(&parent_acc->cleanup_mutex);
    }

    /* Unregister from mapping - this is safe.*/
    if (tkAccessible->tkwin) {
        UnregisterAtkObjectForTkWindow(tkAccessible->tkwin);







|







1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
            RemoveChildFromParent(parent_acc, ATK_OBJECT(tkAccessible));
            
            /* Emit signal safely. */
            ChildrenChangedRemoveData *crd = g_new0(ChildrenChangedRemoveData, 1);
            crd->parent = g_object_ref(parent);  /* Take explicit ref */
            crd->index = index;
            crd->child = g_object_ref(ATK_OBJECT(tkAccessible));  /* Take explicit ref. */
            RunOnMainThread(emit_children_changed_remove, crd);
        }
        g_mutex_unlock(&parent_acc->cleanup_mutex);
    }

    /* Unregister from mapping - this is safe.*/
    if (tkAccessible->tkwin) {
        UnregisterAtkObjectForTkWindow(tkAccessible->tkwin);
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
    if (!acc->is_being_destroyed && acc->width > 0 && acc->height > 0 && acc->is_mapped) {
        BoundsChangedData *bcd = g_new0(BoundsChangedData, 1);
        bcd->obj = ATK_OBJECT(acc);
        bcd->rect.x = acc->x;
        bcd->rect.y = acc->y;
        bcd->rect.width = acc->width;
        bcd->rect.height = acc->height;
        RunOnMainThreadAsync(emit_bounds_changed, bcd);
    }
}

/* Respond to <Map> event. */
static void TkAtkAccessible_MapHandler(ClientData clientData, XEvent *eventPtr)
{
    if (eventPtr->type != MapNotify) return;







|







1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
    if (!acc->is_being_destroyed && acc->width > 0 && acc->height > 0 && acc->is_mapped) {
        BoundsChangedData *bcd = g_new0(BoundsChangedData, 1);
        bcd->obj = ATK_OBJECT(acc);
        bcd->rect.x = acc->x;
        bcd->rect.y = acc->y;
        bcd->rect.width = acc->width;
        bcd->rect.height = acc->height;
        RunOnMainThread(emit_bounds_changed, bcd);
    }
}

/* Respond to <Map> event. */
static void TkAtkAccessible_MapHandler(ClientData clientData, XEvent *eventPtr)
{
    if (eventPtr->type != MapNotify) return;
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
    g_debug("TkAtkAccessible_FocusHandler: Object %s focus state: %s",
            Tk_PathName(acc->tkwin), eventPtr->type == FocusIn ? "FocusIn" : "FocusOut");

    if (eventPtr->type == FocusIn) {
        FocusEventData *fed = g_new0(FocusEventData, 1);
        fed->obj = atk_obj;
        fed->state = TRUE;
        RunOnMainThreadAsync(emit_focus_event, fed);

        StateChangeData *scd = g_new0(StateChangeData, 1);
        scd->obj = atk_obj;
        scd->name = g_strdup("focused");
        scd->state = TRUE;
        RunOnMainThreadAsync(emit_state_change, scd);
    } else if (eventPtr->type == FocusOut) {
        FocusEventData *fed = g_new0(FocusEventData, 1);
        fed->obj = atk_obj;
        fed->state = FALSE;
        RunOnMainThreadAsync(emit_focus_event, fed);

        StateChangeData *scd = g_new0(StateChangeData, 1);
        scd->obj = atk_obj;
        scd->name = g_strdup("focused");
        scd->state = FALSE;
        RunOnMainThreadAsync(emit_state_change, scd);
    }

    g_object_unref(state_set);
}

/* Respond to <CreateNotify> events. */
static void TkAtkAccessible_CreateHandler(ClientData clientData, XEvent *eventPtr) 
{
	
    if (eventPtr->type != CreateNotify) return;

    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. */
    RunOnMainThreadAsync((GSourceFunc)UpdateChildrenCache, acc);
}



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







|





|




|





|


















|







2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
    g_debug("TkAtkAccessible_FocusHandler: Object %s focus state: %s",
            Tk_PathName(acc->tkwin), eventPtr->type == FocusIn ? "FocusIn" : "FocusOut");

    if (eventPtr->type == FocusIn) {
        FocusEventData *fed = g_new0(FocusEventData, 1);
        fed->obj = atk_obj;
        fed->state = TRUE;
        RunOnMainThread(emit_focus_event, fed);

        StateChangeData *scd = g_new0(StateChangeData, 1);
        scd->obj = atk_obj;
        scd->name = g_strdup("focused");
        scd->state = TRUE;
        RunOnMainThread(emit_state_change, scd);
    } else if (eventPtr->type == FocusOut) {
        FocusEventData *fed = g_new0(FocusEventData, 1);
        fed->obj = atk_obj;
        fed->state = FALSE;
        RunOnMainThread(emit_focus_event, fed);

        StateChangeData *scd = g_new0(StateChangeData, 1);
        scd->obj = atk_obj;
        scd->name = g_strdup("focused");
        scd->state = FALSE;
        RunOnMainThread(emit_state_change, scd);
    }

    g_object_unref(state_set);
}

/* Respond to <CreateNotify> events. */
static void TkAtkAccessible_CreateHandler(ClientData clientData, XEvent *eventPtr) 
{
	
    if (eventPtr->type != CreateNotify) return;

    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);
}



/*
 *----------------------------------------------------------------------
 *
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
    
    ValueChangedData *vcd = g_new0(ValueChangedData, 1);
    vcd->obj = acc;
    g_value_init(&vcd->value, G_TYPE_STRING);
    g_value_set_string(&vcd->value, g_value_get_string(&gval));
    g_value_unset(&gval);
    
    RunOnMainThreadAsync(emit_value_changed, vcd);

    if (role == ATK_ROLE_TEXT || role == ATK_ROLE_ENTRY) {
        RunOnMainThreadAsync(emit_text_selection_changed, acc);
    }
   
    return TCL_OK;
}

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







|


|







2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
    
    ValueChangedData *vcd = g_new0(ValueChangedData, 1);
    vcd->obj = acc;
    g_value_init(&vcd->value, G_TYPE_STRING);
    g_value_set_string(&vcd->value, g_value_get_string(&gval));
    g_value_unset(&gval);
    
    RunOnMainThread(emit_value_changed, vcd);

    if (role == ATK_ROLE_TEXT || role == ATK_ROLE_ENTRY) {
        RunOnMainThread(emit_text_selection_changed, acc);
    }
   
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204

    TkAtkAccessible *tk_acc = (TkAtkAccessible *)acc;
    UpdateStateCache(tk_acc);

    FocusEventData *fed = g_new0(FocusEventData, 1);
    fed->obj = acc;
    fed->state = TRUE;
    RunOnMainThreadAsync(emit_focus_event, fed);

    StateChangeData *scd = g_new0(StateChangeData, 1);
    scd->obj = acc;
    scd->name = g_strdup("focused");
    scd->state = TRUE;
    RunOnMainThreadAsync(emit_state_change, scd);

    return TCL_OK;
}


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







|





|







2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223

    TkAtkAccessible *tk_acc = (TkAtkAccessible *)acc;
    UpdateStateCache(tk_acc);

    FocusEventData *fed = g_new0(FocusEventData, 1);
    fed->obj = acc;
    fed->state = TRUE;
    RunOnMainThread(emit_focus_event, fed);

    StateChangeData *scd = g_new0(StateChangeData, 1);
    scd->obj = acc;
    scd->name = g_strdup("focused");
    scd->state = TRUE;
    RunOnMainThread(emit_state_change, scd);

    return TCL_OK;
}


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