diff --git a/src/cthulhu/event_manager.py b/src/cthulhu/event_manager.py index 26634e6..5903865 100644 --- a/src/cthulhu/event_manager.py +++ b/src/cthulhu/event_manager.py @@ -1729,6 +1729,35 @@ class EventManager: return False + def _is_notification_event_during_xterm_handoff(self, event: Atspi.Event) -> bool: + """Returns True when an event should bypass XTerm handoff suppression.""" + + for obj in (event.source, event.any_data): + if obj is None or isinstance(obj, (str, bytes, int, float, bool)): + continue + try: + if AXUtilities.is_notification(obj) or AXUtilities.is_alert(obj): + return True + except Exception: + pass + + notificationEventTypes = ( + "window:create", + "object:property-change:accessible-name", + "object:property-change:accessible-value", + ) + if not event.type.startswith(notificationEventTypes): + return False + + app = AXObject.get_application(event.source) + appName = (AXObject.get_name(app) or "").casefold() + return appName in ( + "notification-daemon", + "notify-osd", + "xfce4-notifyd", + "mate-notification-daemon", + ) + def _processObjectEvent(self, event: Atspi.Event) -> None: """Handles all object events destined for scripts. @@ -1743,7 +1772,8 @@ class EventManager: eType = event.type refreshXtermHandoff = self._event_refreshes_xterm_handoff(event) - suppressForXterm = self._inputEventManager is not None and ( + isNotification = self._is_notification_event_during_xterm_handoff(event) + suppressForXterm = not isNotification and self._inputEventManager is not None and ( self._inputEventManager.should_suppress_atspi_events_for_xterm( refresh=refreshXtermHandoff, ) diff --git a/tests/test_event_manager_xterm_handoff_regressions.py b/tests/test_event_manager_xterm_handoff_regressions.py index f949b1c..01b0a2e 100644 --- a/tests/test_event_manager_xterm_handoff_regressions.py +++ b/tests/test_event_manager_xterm_handoff_regressions.py @@ -97,6 +97,33 @@ class EventManagerXtermHandoffRegressionTests(unittest.TestCase): refresh=False, ) + def test_notification_daemon_window_create_is_processed_during_xterm_handoff(self) -> None: + event = FakeEvent("window:create", source=object()) + notificationApp = object() + self.manager._isActivatableEvent = mock.Mock(return_value=(False, "test")) + + with ( + mock.patch.object(event_manager.debug, "printObjectEvent"), + mock.patch.object(event_manager.debug, "printMessage"), + mock.patch.object( + event_manager.AXObject, + "get_application", + return_value=notificationApp, + ), + mock.patch.object( + event_manager.AXObject, + "get_name", + return_value="xfce4-notifyd", + ), + mock.patch.object(event_manager.AXObject, "is_dead", return_value=False), + mock.patch.object(event_manager.AXUtilities, "is_defunct", return_value=False), + mock.patch.object(event_manager.AXUtilities, "is_iconified", return_value=False), + ): + self.manager._processObjectEvent(event) + + self.manager._get_scriptForEvent.assert_called_once_with(event) + self.manager._inputEventManager.should_suppress_atspi_events_for_xterm.assert_not_called() + if __name__ == "__main__": unittest.main()