Code review and cleanup.
This commit is contained in:
1 parent
bbe931335f
commit
92d6ad3cc4
23 files changed
+1497
-886
No files matched your search
@@ -507,69 +507,13 @@ func cthulhuAtspiEventCallback(event *C.AtspiEvent, userData unsafe.Pointer) {
|
||||
return
|
||||
}
|
||||
snapshotStarted := time.Now()
|
||||
target := returnedMenuItem
|
||||
ownedTarget := false
|
||||
if target == nil {
|
||||
target = adapter.libreOfficeFocusTarget(event, eventType)
|
||||
ownedTarget = target != nil
|
||||
}
|
||||
if target == nil {
|
||||
target, ownedTarget = selectNativeEventTarget(event, eventType)
|
||||
}
|
||||
target, ownedTarget := adapter.resolveNativeEventTarget(event, eventType, returnedMenuItem)
|
||||
if target == nil {
|
||||
return
|
||||
}
|
||||
if (eventType == FocusEvent || eventType == FocusedStateEvent) &&
|
||||
C.atspi_accessible_get_role(target, nil) == C.ATSPI_ROLE_MENU_ITEM {
|
||||
// Qt menus can change their checked state without a state-change
|
||||
// event. Reopening the menu must capture the current provider state.
|
||||
C.atspi_accessible_clear_cache_single(target)
|
||||
}
|
||||
if (eventType == SelectedStateEvent || eventType == SelectionChangedEvent) &&
|
||||
C.atspi_accessible_get_role(target, nil) == C.ATSPI_ROLE_PAGE_TAB {
|
||||
// Firefox can emit selected-state events from the previous tab;
|
||||
// query the current state/name before using either event path.
|
||||
C.atspi_accessible_clear_cache_single(target)
|
||||
}
|
||||
if eventType == WindowDeactivatedEvent {
|
||||
// Match Python's key-grab churn check against fresh window state rather
|
||||
// than an AT-SPI cache entry from before the deactivation event.
|
||||
C.atspi_accessible_clear_cache_single(event.source)
|
||||
}
|
||||
payload := C.cthulhu_event_payload(event)
|
||||
snapshot, popupFor := adapter.captureNativeEventSnapshot(event, target, eventType, originID, live)
|
||||
if completeLive {
|
||||
text := C.cthulhu_live_content(event.source)
|
||||
snapshot.Text = sanitizeSnapshotField(C.GoString(text))
|
||||
snapshot.LiveAtomic = true
|
||||
C.g_free(C.gpointer(payload))
|
||||
payload = text
|
||||
}
|
||||
// Shared Qt actions can report focus through their original, hidden menu
|
||||
// even while another menu emits focus-gained for that very accessible.
|
||||
// The explicit event establishes focus; the queried state may disagree.
|
||||
// Do not transfer this evidence to a redirected descendant or a dead item.
|
||||
if snapshot.ID() == originID && !snapshot.Defunct &&
|
||||
(eventType == FocusEvent || eventType == FocusedStateEvent && C.cthulhu_event_detail1(event) == 1) {
|
||||
switch snapshot.Role {
|
||||
case "menu item", "check menu item", "radio menu item":
|
||||
snapshot.Focused = true
|
||||
}
|
||||
}
|
||||
var inlineMenuID ObjectID
|
||||
if eventType == SelectedStateEvent && C.cthulhu_event_detail1(event) == 1 && !snapshot.Defunct &&
|
||||
(snapshot.Role == "button" || snapshot.Role == "push button" || snapshot.Role == "toggle button" ||
|
||||
snapshot.Role == "menu item" || snapshot.Role == "check menu item" || snapshot.Role == "radio menu item") {
|
||||
if menu := C.cthulhu_inline_menu_container(target); menu != nil {
|
||||
inlineMenuID = nativeObjectID(menu)
|
||||
C.g_object_unref(C.gpointer(menu))
|
||||
if inlineMenuID.Valid() && inlineMenuID.BusName == snapshot.BusName {
|
||||
// The event's detail is authoritative even when libatspi still
|
||||
// caches the previous SELECTED state on the first visit.
|
||||
eventType, snapshot.Selected = ActiveDescendantEvent, true
|
||||
}
|
||||
}
|
||||
}
|
||||
refreshNativeEventTarget(event, target, eventType)
|
||||
snapshot, popupFor, payload := adapter.captureNativeEventContent(event, target, eventType, originID, live, completeLive)
|
||||
eventType, inlineMenuID := normalizeNativeMenuFocus(event, target, eventType, originID, &snapshot)
|
||||
// Chromium can drop ACTIVE from its visible main frame while a tiny
|
||||
// same-process sibling claims it. On X11, the correlated foreground
|
||||
// window is the stronger ownership check performed below. Keep the
|
||||
@@ -579,7 +523,6 @@ func cthulhuAtspiEventCallback(event *C.AtspiEvent, userData unsafe.Pointer) {
|
||||
if webNotification == nil && !progressUpdate && !adapter.Foreground().Client.Known && eventType != WindowCreateEvent && eventType != WindowActivatedEvent &&
|
||||
eventType != WindowDeactivatedEvent && eventType != WindowDestroyedEvent && eventType != AnnouncementEvent && !snapshot.AutocompletePopup &&
|
||||
C.cthulhu_in_inactive_window(event.source) != 0 {
|
||||
C.g_free(C.gpointer(payload))
|
||||
if ownedTarget {
|
||||
C.g_object_unref(C.gpointer(target))
|
||||
}
|
||||
@@ -593,7 +536,7 @@ func cthulhuAtspiEventCallback(event *C.AtspiEvent, userData unsafe.Pointer) {
|
||||
focusEvent := NewEventWithPayload(
|
||||
eventType,
|
||||
snapshot,
|
||||
C.GoString(payload),
|
||||
payload,
|
||||
)
|
||||
if eventType != nativeEventType {
|
||||
focusEvent.NativeType = nativeEventType
|
||||
@@ -636,7 +579,6 @@ func cthulhuAtspiEventCallback(event *C.AtspiEvent, userData unsafe.Pointer) {
|
||||
if eventType == ActiveDescendantEvent && inlineMenuID.Valid() {
|
||||
focusEvent.OriginPath = inlineMenuID.Path
|
||||
}
|
||||
C.g_free(C.gpointer(payload))
|
||||
if ownedTarget {
|
||||
C.g_object_unref(C.gpointer(target))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// Authors: Storm Dragon, Angus Kola
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package atspi
|
||||
|
||||
/*
|
||||
#include "adapter_native.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// resolveNativeEventTarget runs on the GLib owner. The returned menu item is
|
||||
// borrowed from the callback; LibreOffice and selected descendants can return
|
||||
// owned references. The caller releases an owned target after event capture.
|
||||
func (adapter *Adapter) resolveNativeEventTarget(event *C.AtspiEvent, eventType string, returnedMenuItem *C.AtspiAccessible) (*C.AtspiAccessible, bool) {
|
||||
target := returnedMenuItem
|
||||
ownedTarget := false
|
||||
if target == nil {
|
||||
target = adapter.libreOfficeFocusTarget(event, eventType)
|
||||
ownedTarget = target != nil
|
||||
}
|
||||
if target == nil {
|
||||
target, ownedTarget = selectNativeEventTarget(event, eventType)
|
||||
}
|
||||
return target, ownedTarget
|
||||
}
|
||||
|
||||
// Refresh only the established provider caches, in query order, before capture.
|
||||
// Both accessibles are borrowed and stay on the GLib owner thread.
|
||||
func refreshNativeEventTarget(event *C.AtspiEvent, target *C.AtspiAccessible, eventType string) {
|
||||
if (eventType == FocusEvent || eventType == FocusedStateEvent) &&
|
||||
C.atspi_accessible_get_role(target, nil) == C.ATSPI_ROLE_MENU_ITEM {
|
||||
// Qt menus can change their checked state without a state-change
|
||||
// event. Reopening the menu must capture the current provider state.
|
||||
C.atspi_accessible_clear_cache_single(target)
|
||||
}
|
||||
if (eventType == SelectedStateEvent || eventType == SelectionChangedEvent) &&
|
||||
C.atspi_accessible_get_role(target, nil) == C.ATSPI_ROLE_PAGE_TAB {
|
||||
// Firefox can emit selected-state events from the previous tab;
|
||||
// query the current state/name before using either event path.
|
||||
C.atspi_accessible_clear_cache_single(target)
|
||||
}
|
||||
if eventType == WindowDeactivatedEvent {
|
||||
// Match Python's key-grab churn check against fresh window state rather
|
||||
// than an AT-SPI cache entry from before the deactivation event.
|
||||
C.atspi_accessible_clear_cache_single(event.source)
|
||||
}
|
||||
}
|
||||
|
||||
// captureNativeEventContent borrows event/target and owns all payload buffers.
|
||||
// A completed atomic live region replaces the event payload before copying it.
|
||||
// No native references escape this helper; all calls remain on the GLib owner.
|
||||
func (adapter *Adapter) captureNativeEventContent(event *C.AtspiEvent, target *C.AtspiAccessible, eventType string, originID ObjectID, live nativeLiveProperties, completeLive bool) (Snapshot, ObjectID, string) {
|
||||
payload := C.cthulhu_event_payload(event)
|
||||
defer func() { C.g_free(C.gpointer(payload)) }()
|
||||
snapshot, popupFor := adapter.captureNativeEventSnapshot(event, target, eventType, originID, live)
|
||||
if completeLive {
|
||||
text := C.cthulhu_live_content(event.source)
|
||||
snapshot.Text = sanitizeSnapshotField(C.GoString(text))
|
||||
snapshot.LiveAtomic = true
|
||||
C.g_free(C.gpointer(payload))
|
||||
payload = text
|
||||
}
|
||||
return snapshot, popupFor, C.GoString(payload)
|
||||
}
|
||||
|
||||
// normalizeNativeMenuFocus applies provider evidence to this event's snapshot.
|
||||
// It borrows target and releases the temporary inline-menu reference locally.
|
||||
func normalizeNativeMenuFocus(event *C.AtspiEvent, target *C.AtspiAccessible, eventType string, originID ObjectID, snapshot *Snapshot) (string, ObjectID) {
|
||||
// Shared Qt actions can report focus through their original, hidden menu
|
||||
// even while another menu emits focus-gained for that very accessible.
|
||||
// The explicit event establishes focus; the queried state may disagree.
|
||||
// Do not transfer this evidence to a redirected descendant or a dead item.
|
||||
if snapshot.ID() == originID && !snapshot.Defunct &&
|
||||
(eventType == FocusEvent || eventType == FocusedStateEvent && C.cthulhu_event_detail1(event) == 1) {
|
||||
switch snapshot.Role {
|
||||
case "menu item", "check menu item", "radio menu item":
|
||||
snapshot.Focused = true
|
||||
}
|
||||
}
|
||||
var inlineMenuID ObjectID
|
||||
if eventType == SelectedStateEvent && C.cthulhu_event_detail1(event) == 1 && !snapshot.Defunct &&
|
||||
(snapshot.Role == "button" || snapshot.Role == "push button" || snapshot.Role == "toggle button" ||
|
||||
snapshot.Role == "menu item" || snapshot.Role == "check menu item" || snapshot.Role == "radio menu item") {
|
||||
if menu := C.cthulhu_inline_menu_container(target); menu != nil {
|
||||
inlineMenuID = nativeObjectID(menu)
|
||||
C.g_object_unref(C.gpointer(menu))
|
||||
if inlineMenuID.Valid() && inlineMenuID.BusName == snapshot.BusName {
|
||||
// The event's detail is authoritative even when libatspi still
|
||||
// caches the previous SELECTED state on the first visit.
|
||||
eventType, snapshot.Selected = ActiveDescendantEvent, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return eventType, inlineMenuID
|
||||
}
|
||||
Reference in new issue
Block a user