feat: wire event relevance gate
This commit is contained in:
@@ -102,7 +102,7 @@ class EventManager:
|
|||||||
self._churnSuppressed: bool = cthulhu_state.pauseAtspiChurn
|
self._churnSuppressed: bool = cthulhu_state.pauseAtspiChurn
|
||||||
self._prioritizedContextToken: Optional[str] = cthulhu_state.prioritizedDesktopContextToken
|
self._prioritizedContextToken: Optional[str] = cthulhu_state.prioritizedDesktopContextToken
|
||||||
self._relevanceBurstWindow: float = 0.15
|
self._relevanceBurstWindow: float = 0.15
|
||||||
self._relevanceBurstHistory: Dict[Tuple[str, str], float] = {}
|
self._relevanceBurstHistory: Dict[Tuple[str, str, str], float] = {}
|
||||||
|
|
||||||
cthulhu_state.device = None
|
cthulhu_state.device = None
|
||||||
self._keyHandlingActive: bool = False
|
self._keyHandlingActive: bool = False
|
||||||
@@ -238,14 +238,14 @@ class EventManager:
|
|||||||
return self._isLiveOrNotificationEvent(event)
|
return self._isLiveOrNotificationEvent(event)
|
||||||
|
|
||||||
def _isLiveOrNotificationObject(self, obj: Any) -> bool:
|
def _isLiveOrNotificationObject(self, obj: Any) -> bool:
|
||||||
if obj is None:
|
if obj is None or isinstance(obj, (str, bytes, int, float, bool)):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if AXUtilities.is_notification(obj) or AXUtilities.is_alert(obj):
|
if AXUtilities.is_notification(obj) or AXUtilities.is_alert(obj):
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
liveAttr = AXObject.get_attribute(obj, 'live')
|
liveAttr = AXObject.get_attribute(obj, 'live')
|
||||||
@@ -329,14 +329,11 @@ class EventManager:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def _classifyRelevance(self, event: Atspi.Event, app: Atspi.Accessible) -> str:
|
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):
|
if self._shouldPreserveForRelevanceGate(event):
|
||||||
return self.RELEVANCE_KEEP
|
return self.RELEVANCE_KEEP
|
||||||
|
|
||||||
if event.type.startswith("object:state-changed:focused"):
|
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)
|
role = self._eventRoleForRelevance(event)
|
||||||
eventGroup = self._relevanceEventGroup(event, role)
|
eventGroup = self._relevanceEventGroup(event, role)
|
||||||
@@ -374,7 +371,12 @@ class EventManager:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
appName = self._appNameForRelevance(app) or "unknown-app"
|
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()
|
now = time.monotonic()
|
||||||
lastSeen = self._relevanceBurstHistory.get(burstKey)
|
lastSeen = self._relevanceBurstHistory.get(burstKey)
|
||||||
self._relevanceBurstHistory[burstKey] = now
|
self._relevanceBurstHistory[burstKey] = now
|
||||||
@@ -468,6 +470,12 @@ class EventManager:
|
|||||||
else:
|
else:
|
||||||
return _ignore_with_reason("inactive-app", "event not from active app")
|
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):
|
if self._isSteamBurstChurnEvent(event, app):
|
||||||
return _ignore_with_reason("steam-burst-churn", "event is low-value Steam churn")
|
return _ignore_with_reason("steam-burst-churn", "event is low-value Steam churn")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user