diff --git a/src/cthulhu/event_manager.py b/src/cthulhu/event_manager.py index d1be833..321690a 100644 --- a/src/cthulhu/event_manager.py +++ b/src/cthulhu/event_manager.py @@ -102,7 +102,7 @@ class EventManager: self._churnSuppressed: bool = cthulhu_state.pauseAtspiChurn self._prioritizedContextToken: Optional[str] = cthulhu_state.prioritizedDesktopContextToken self._relevanceBurstWindow: float = 0.15 - self._relevanceBurstHistory: Dict[Tuple[str, str], float] = {} + self._relevanceBurstHistory: Dict[Tuple[str, str, str], float] = {} cthulhu_state.device = None self._keyHandlingActive: bool = False @@ -238,14 +238,14 @@ class EventManager: return self._isLiveOrNotificationEvent(event) def _isLiveOrNotificationObject(self, obj: Any) -> bool: - if obj is None: + if obj is None or isinstance(obj, (str, bytes, int, float, bool)): return False try: if AXUtilities.is_notification(obj) or AXUtilities.is_alert(obj): return True except Exception: - return False + pass try: liveAttr = AXObject.get_attribute(obj, 'live') @@ -329,14 +329,11 @@ class EventManager: return None def _classifyRelevance(self, event: Atspi.Event, app: Atspi.Accessible) -> str: - if self._is_obsolete_by_context(event): - return self.RELEVANCE_DROP - if self._shouldPreserveForRelevanceGate(event): return self.RELEVANCE_KEEP if event.type.startswith("object:state-changed:focused"): - return self.RELEVANCE_KEEP if event.detail1 else self.RELEVANCE_DROP + return self.RELEVANCE_KEEP role = self._eventRoleForRelevance(event) eventGroup = self._relevanceEventGroup(event, role) @@ -374,7 +371,12 @@ class EventManager: return False appName = self._appNameForRelevance(app) or "unknown-app" - burstKey = (appName, eventGroup) + try: + contextKey = self._context_token_for_event(event) + except Exception: + contextKey = None + contextKey = contextKey or f"source:{id(event.source)}" + burstKey = (appName, eventGroup, contextKey) now = time.monotonic() lastSeen = self._relevanceBurstHistory.get(burstKey) self._relevanceBurstHistory[burstKey] = now @@ -468,6 +470,12 @@ class EventManager: else: return _ignore_with_reason("inactive-app", "event not from active app") + relevance = self._classifyRelevance(event, app) + if relevance == self.RELEVANCE_DROP: + return _ignore_with_reason("event-relevance-drop", "event is background churn") + if relevance == self.RELEVANCE_COLLAPSE: + return _ignore_with_reason("event-relevance-collapse", "event was collapsed by relevance gate") + if self._isSteamBurstChurnEvent(event, app): return _ignore_with_reason("steam-burst-churn", "event is low-value Steam churn")