[orca] Work around multiple issues related to active windows



commit 6d0dad96d957546e32d97f933ad0f238314fdea9
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Mon Nov 23 16:09:44 2015 -0500

    Work around multiple issues related to active windows
    
    * Add heuristic and additional checks to find the real active
      window because multiple windows can claim to be active and
      it's our job to figure out which is "lying" to us.
    
    * Add the ability to ask for the active window for specific
      apps, because the aforementioned heuristic is a heuristic.
    
    * Verify that the parent of the window is the app we think it
      is, and update the script if not. The underlying problem may
      be whatever's triggering the atspi_accessible_set_cache_mask
      assertion failure(accessible == accessible->parent.app->root).

 src/orca/script_utilities.py             |   88 ++++++++++++++++++++++++------
 src/orca/scripts/web/script.py           |    5 ++-
 src/orca/scripts/web/script_utilities.py |   30 ++++++----
 3 files changed, 94 insertions(+), 29 deletions(-)
---
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index 8ce4a61..198c5c8 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -100,26 +100,66 @@ class Utilities:
     #                                                                       #
     #########################################################################
 
-    @staticmethod
-    def activeWindow():
-        """Traverses the list of known apps looking for one who has an
-        immediate child (i.e., a window) whose state includes the active
-        state.
+    def _isActiveAndShowingAndNotIconified(self, obj):
+        try:
+            state = obj.getState()
+        except:
+            msg = "ERROR: Exception getting state of %s" % obj
+            debug.println(debug.LEVEL_INFO, msg)
+            return False
 
-        Returns the Python Accessible of the window that's active or None if
-        no windows are active.
-        """
+        if not state.contains(pyatspi.STATE_ACTIVE):
+            return False
+
+        if state.contains(pyatspi.STATE_ICONIFIED):
+            return False
+
+        return state.contains(pyatspi.STATE_SHOWING)
 
-        apps = Utilities.knownApplications()
+    def canBeActiveWindow(self, window):
+        if not window:
+            return False
+
+        window.clearCache()
+        if not self._isActiveAndShowingAndNotIconified(window):
+            msg = "INFO: %s is not active and showing, or is iconified" % window
+            debug.println(debug.LEVEL_INFO, msg)
+            return False
+
+        msg = "INFO: %s can be active window" % window
+        debug.println(debug.LEVEL_INFO, msg)
+        return True
+
+    def activeWindow(self, *apps):
+        """Tries to locate the active window; may or may not succeed."""
+
+        candidates = []
+        apps = apps or self.knownApplications()
         for app in apps:
-            for child in app:
-                try:
-                    if child.getState().contains(pyatspi.STATE_ACTIVE):
-                        return child
-                except:
-                    debug.printException(debug.LEVEL_FINEST)
+            candidates.extend([child for child in app if self.canBeActiveWindow(child)])
 
-        return None
+        if not candidates:
+            msg = "ERROR: Unable to find active window from %s" % list(map(str, apps))
+            debug.println(debug.LEVEL_INFO, msg)
+            return None
+
+        if len(candidates) == 1:
+            msg = "INFO: Active window is %s" % candidates[0]
+            debug.println(debug.LEVEL_INFO, msg)
+            return candidates[0]
+
+        # Sorting by size in a lame attempt to filter out the "desktop" frame of various
+        # desktop environments. App name won't work because we don't know it. Getting the
+        # screen/desktop size via Gdk risks a segfault depending on the user environment.
+        # Asking AT-SPI2 for the size seems to give us 1024x768 regardless of reality....
+        # This is why we can't have nice things.
+        candidates = sorted(candidates, key=functools.cmp_to_key(self.sizeComparison))
+        msg = "WARNING: These windows all claim to be active: %s" % list(map(str, candidates))
+        debug.println(debug.LEVEL_INFO, msg)
+
+        msg = "INFO: Active window is (hopefully) %s" % candidates[0]
+        debug.println(debug.LEVEL_INFO, msg)
+        return candidates[0]
 
     @staticmethod
     def ancestorWithRole(obj, ancestorRoles, stopRoles):
@@ -1655,6 +1695,22 @@ class Utilities:
         return min(max(rv, -1), 1)
 
     @staticmethod
+    def sizeComparison(obj1, obj2):
+        try:
+            bbox = obj1.queryComponent().getExtents(pyatspi.DESKTOP_COORDS)
+            width1, height1 = bbox.width, bbox.height
+        except:
+            width1, height1 = 0, 0
+
+        try:
+            bbox = obj2.queryComponent().getExtents(pyatspi.DESKTOP_COORDS)
+            width2, height2 = bbox.width, bbox.height
+        except:
+            width2, height2 = 0, 0
+
+        return (width1 * height1) - (width2 * height2)
+
+    @staticmethod
     def spatialComparison(obj1, obj2):
         """Compares the physical locations of obj1 and obj2 and returns -1,
         0, or 1 to indicate if obj1 physically is before, is in the same
diff --git a/src/orca/scripts/web/script.py b/src/orca/scripts/web/script.py
index 7a718f6..1de15ad 100644
--- a/src/orca/scripts/web/script.py
+++ b/src/orca/scripts/web/script.py
@@ -1719,7 +1719,10 @@ class Script(default.Script):
     def onWindowActivated(self, event):
         """Callback for window:activate accessibility events."""
 
-        return False
+        msg = "WEB: Calling default onWindowActivated"
+        debug.println(debug.LEVEL_INFO, msg)
+        super().onWindowActivated(event)
+        return True
 
     def onWindowDeactivated(self, event):
         """Callback for window:deactivate accessibility events."""
diff --git a/src/orca/scripts/web/script_utilities.py b/src/orca/scripts/web/script_utilities.py
index 9180454..e05d135 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -184,19 +184,25 @@ class Utilities(script_utilities.Utilities):
         return list(filter(self.isDocument, targets))
 
     def documentFrame(self, obj=None):
-        isShowing = lambda x: x and x.getState().contains(pyatspi.STATE_SHOWING)
-
-        try:
-            windows = [child for child in self._script.app]
-        except:
-            msg = "WEB: Exception getting children for app %s" % self._script.app
+        app = self._script.app
+        if orca_state.activeWindow in app:
+            window = orca_state.activeWindow
+        else:
+            msg = "WARNING: %s is not in %s" % (orca_state.activeWindow, app)
             debug.println(debug.LEVEL_INFO, msg)
-            windows = []
-
-        if orca_state.activeWindow in windows:
-            windows = [orca_state.activeWindow]
+            window = self.activeWindow(app)
+            try:
+                self._script.app = window.getApplication()
+                msg = "WEB: updating script's app to %s" % self._script.app
+                debug.println(debug.LEVEL_INFO, msg)
+            except:
+                msg = "ERROR: Exception getting app for %s" % window
+                debug.println(debug.LEVEL_INFO, msg)
+            else:
+                orca_state.activeWindow = window
 
-        for window in windows:
+        if window:
+            isShowing = lambda x: x and x.getState().contains(pyatspi.STATE_SHOWING)
             documents = self._getDocumentsEmbeddedBy(window)
             documents = list(filter(isShowing, documents))
             if len(documents) == 1:


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]