76 lines
3.5 KiB
C
76 lines
3.5 KiB
C
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#include "adapter_native.h"
|
|
|
|
// Owned by the native accessible, on the AT-SPI GLib thread. No accessible
|
|
// references or application labels are retained. Destruction frees the data;
|
|
// semantic identity changes and explicit capability removal retire it too.
|
|
static const gchar *menu_checkability_key = "cthulhu-menu-checkability";
|
|
enum { CHECKABILITY_INFERRED = 1, CHECKABILITY_EXPLICIT = 2 };
|
|
|
|
// Qt can hide a submenu on Left/Escape without emitting focus-gained for
|
|
// its parent action. Require fresh provider focus and visibility on that
|
|
// exact parent; closing an entire menu or switching away grants no focus.
|
|
AtspiAccessible *cthulhu_returned_menu_item(AtspiAccessible *menu) {
|
|
atspi_accessible_clear_cache_single(menu);
|
|
AtspiStateSet *states = atspi_accessible_get_state_set(menu);
|
|
gboolean hidden = states != NULL &&
|
|
!atspi_state_set_contains(states, ATSPI_STATE_SHOWING) &&
|
|
!atspi_state_set_contains(states, ATSPI_STATE_DEFUNCT);
|
|
if (states != NULL) g_object_unref(states);
|
|
if (!hidden) return NULL;
|
|
AtspiAccessible *parent = atspi_accessible_get_parent(menu, NULL);
|
|
if (parent == NULL) return NULL;
|
|
atspi_accessible_clear_cache_single(parent);
|
|
AtspiRole role = atspi_accessible_get_role(parent, NULL);
|
|
states = atspi_accessible_get_state_set(parent);
|
|
gboolean focused = states != NULL &&
|
|
atspi_state_set_contains(states, ATSPI_STATE_FOCUSED) &&
|
|
atspi_state_set_contains(states, ATSPI_STATE_SHOWING) &&
|
|
!atspi_state_set_contains(states, ATSPI_STATE_DEFUNCT);
|
|
if (states != NULL) g_object_unref(states);
|
|
if (focused && (role == ATSPI_ROLE_MENU_ITEM || role == ATSPI_ROLE_CHECK_MENU_ITEM ||
|
|
role == ATSPI_ROLE_RADIO_MENU_ITEM)) return parent;
|
|
g_object_unref(parent);
|
|
return NULL;
|
|
}
|
|
|
|
void cthulhu_forget_menu_checkability(AtspiAccessible *source) {
|
|
if (source != NULL)
|
|
g_object_set_data(G_OBJECT(source), menu_checkability_key, NULL);
|
|
}
|
|
|
|
void cthulhu_note_menu_checkability(AtspiAccessible *source) {
|
|
if (source != NULL &&
|
|
GPOINTER_TO_INT(g_object_get_data(G_OBJECT(source), menu_checkability_key)) != CHECKABILITY_EXPLICIT)
|
|
g_object_set_data(G_OBJECT(source), menu_checkability_key,
|
|
GINT_TO_POINTER(CHECKABILITY_INFERRED));
|
|
}
|
|
|
|
void cthulhu_capture_menu_checkability(AtspiAccessible *source, cthulhu_snapshot *snapshot) {
|
|
if (snapshot->defunct || g_strcmp0(snapshot->role, "menu item") != 0) {
|
|
cthulhu_forget_menu_checkability(source);
|
|
return;
|
|
}
|
|
if (!snapshot->statesKnown) return;
|
|
|
|
gint evidence = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(source), menu_checkability_key));
|
|
if (snapshot->checkable) {
|
|
g_object_set_data(G_OBJECT(source), menu_checkability_key,
|
|
GINT_TO_POINTER(CHECKABILITY_EXPLICIT));
|
|
} else if (snapshot->checked || snapshot->indeterminate) {
|
|
cthulhu_note_menu_checkability(source);
|
|
snapshot->checkable = TRUE;
|
|
} else if (evidence == CHECKABILITY_INFERRED) {
|
|
// Providers such as Qt 5 omit CHECKABLE in both states. Once CHECKED
|
|
// or a checked-state event proves this object's capability, retain it
|
|
// when the value becomes false. Absence alone proves nothing.
|
|
snapshot->checkable = TRUE;
|
|
} else {
|
|
// A provider which supplied CHECKABLE can explicitly withdraw it.
|
|
cthulhu_forget_menu_checkability(source);
|
|
}
|
|
}
|