[pyatspi2] Interim focus tracking for gnome-shell magnifier via D-Bus
- From: Joseph Scheuhammer <jscheuhamm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pyatspi2] Interim focus tracking for gnome-shell magnifier via D-Bus
- Date: Thu, 30 Aug 2012 20:11:08 +0000 (UTC)
commit f2e1fe655356e8ad83e7245a71bad06f2adf9d08
Author: Joseph Scheuhammer <clown alum mit edu>
Date: Fri Aug 24 15:39:43 2012 -0400
Interim focus tracking for gnome-shell magnifier via D-Bus
Proof-of-concept standalone application that shows how:
1. to track keyboard focus and the caret using AT-SPI events, and
2. use D-Bus to drive the magnifier to insure the tracked object is
within the magnified view.
https://bugzilla.gnome.org/show_bug.cgi?id=682636
examples/magFocusTracker.py | 278 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 278 insertions(+), 0 deletions(-)
---
diff --git a/examples/magFocusTracker.py b/examples/magFocusTracker.py
new file mode 100755
index 0000000..8269f1b
--- /dev/null
+++ b/examples/magFocusTracker.py
@@ -0,0 +1,278 @@
+#!/usr/bin/python
+
+# magFocusTracker
+#
+# Copyright 2009 Sun Microsystems Inc.
+# Copyright 2010 Willie Walker
+# Copyright 2011-2012 Igalia, S. L.
+# Copyright 2011-2012 Inclusive Design Research Centre, OCAD University
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA 02110-1301 USA.
+#
+# * Contributor: Joanie Diggs <diggs igalia com>
+# * Contributor: Joseph Scheuhammer <clown alum mit edu>
+
+"""Proof-of-concept standalone application that shows how:
+1. to track keyboard focus and the caret using AT-SPI events, and
+2. use D-Bus to drive the magnifier to insure the tracked object is
+ within the magnified view.
+"""
+
+__copyright__ = \
+ "Copyright (c) 2009 Sun Microsystems Inc." \
+ "Copyright (c) 2010 Willie Walker" \
+ "Copyright (c) 2011-2012 Igalia, S.L." \
+ "Copyright (c) 2011-2012 Inclusive Design Research Centre, OCAD University"
+__license__ = "LGPL"
+
+import dbus
+import pyatspi
+import sys
+from dbus.mainloop.glib import DBusGMainLoop
+from gi.repository import Gdk
+from gi.repository.Gio import Settings
+
+_screenWidth = 0
+_screenHeight = 0
+_zoomer = None
+
+class RoiHandler:
+ """For handling D-Bus calls to zoomRegion.getRoi() asynchronously"""
+
+ def __init__(self, left=0, top=0, width=0, height=0, centerX=0, centerY=0,
+ edgeMarginX=0, edgeMarginY=0):
+ self.left = left
+ self.top = top
+ self.width = width
+ self.height = height
+ self.centerX = centerX
+ self.centerY = centerY
+ self.edgeMarginX = edgeMarginX
+ self.edgeMarginY = edgeMarginY
+
+ def setRoiCenter(self, reply):
+ """Given a region of interest, put that at the center of the magnifier.
+
+ Arguments:
+ - reply: an array defining a rectangle [left, top, right, bottom]
+ """
+ roiWidth = reply[2] - reply[0]
+ roiHeight = reply[3] - reply[1]
+ if self.width > roiWidth:
+ self.centerX = self.left
+ if self.height > roiHeight:
+ self.centerY = self.top
+ _setROICenter(self.centerX, self.centerY)
+
+ def setRoiCursorPush(self, reply):
+ """Given a region of interest, nudge it if the caret or control is not
+ visible.
+
+ Arguments:
+ - reply: an array defining a rectangle [left, top, right, bottom]
+ """
+
+ roiLeft = reply[0]
+ roiTop = reply[1]
+ roiWidth = reply[2] - roiLeft
+ roiHeight = reply[3] - roiTop
+ leftOfROI = (self.left - self.edgeMarginX) <= roiLeft
+ rightOfROI = \
+ (self.left + self.width + self.edgeMarginX) >= (roiLeft + roiWidth)
+ aboveROI = (self.top - self.edgeMarginY) <= roiTop
+ belowROI = \
+ (self.top + self.height + self.edgeMarginY) >= (roiTop + roiHeight)
+
+ x1 = roiLeft
+ x2 = roiLeft + roiWidth
+ y1 = roiTop
+ y2 = roiTop + roiHeight
+
+ if leftOfROI:
+ x1 = max(0, self.left - self.edgeMarginX)
+ x2 = x1 + roiWidth
+ elif rightOfROI:
+ self.left = min(_screenWidth, self.left + self.edgeMarginX)
+ if self.width > roiWidth:
+ x1 = self.left
+ x2 = x1 + roiWidth
+ else:
+ x2 = self.left + self.width
+ x1 = x2 - roiWidth
+
+ if aboveROI:
+ y1 = max(0, self.top - self.edgeMarginY)
+ y2 = y1 + roiHeight
+ elif belowROI:
+ self.top = min(_screenHeight, self.top + self.edgeMarginY)
+ if self.height > roiHeight:
+ y1 = self.top
+ y2 = y1 + roiHeight
+ else:
+ y2 = self.top + self.height
+ y1 = y2 - roiHeight
+
+ _setROICenter((x1 + x2) / 2, (y1 + y2) / 2)
+
+ def setRoiCenterErr(self, error):
+ _dbusCallbackError('_setROICenter()', error)
+
+ def setRoiCursorPushErr(self, error):
+ _dbusCallbackError('_setROICursorPush()', error)
+
+ def magnifyAccessibleErr(self, error):
+ _dbusCallbackError('magnifyAccessible()', error)
+
+def _dbusCallbackError(funcName, error):
+ """Log D-Bus errors
+
+ Arguments:
+ - funcName: The name of the gsmag function that made the D-Bus call.
+ - error: The error that D-Bus returned.
+ """
+ logLine = funcName + ' failed: ' + str(error)
+ debug.println(debug.LEVEL_WARNING, logLine)
+
+def _setROICenter(x, y):
+ """Centers the region of interest around the given point.
+
+ Arguments:
+ - x: integer in unzoomed system coordinates representing x component
+ - y: integer in unzoomed system coordinates representing y component
+ """
+ _zoomer.shiftContentsTo(x, y, ignore_reply=True)
+
+def _setROICursorPush(x, y, width, height):
+ """Nudges the ROI if the caret or control is not visible.
+
+ Arguments:
+ - x: integer in unzoomed system coordinates representing x component
+ - y: integer in unzoomed system coordinates representing y component
+ - width: integer in unzoomed system coordinates representing the width
+ - height: integer in unzoomed system coordinates representing the height
+ """
+
+ roiPushHandler = RoiHandler(x, y, width, height)
+ _zoomer.getRoi(reply_handler=roiPushHandler.setRoiCursorPush,
+ error_handler=roiPushHandler.setRoiCursorPushErr)
+
+def magnifyAccessible(event, obj=None, extents=None):
+ """Sets the region of interest to the upper left of the given
+ accessible, if it implements the Component interface. Otherwise,
+ does nothing.
+
+ Arguments:
+ - event: the Event that caused this to be called
+ - obj: the accessible
+ """
+
+ if event.type.startswith("object:state-changed") and not event.detail1:
+ # This object just became unselected or unfocused, and we're not
+ # big on nostalgia.
+ return
+
+ obj = obj or event.source
+
+ haveSomethingToMagnify = False
+
+ if extents:
+ [x, y, width, height] = extents
+ haveSomethingToMagnify = True
+ elif event and event.type.startswith("object:text-caret-moved"):
+ try:
+ text = obj.queryText()
+ if text and (text.caretOffset >= 0):
+ offset = text.caretOffset
+ if offset == text.characterCount:
+ offset -= 1
+ [x, y, width, height] = \
+ text.getCharacterExtents(offset, 0)
+ haveSomethingToMagnify = (width + height > 0)
+ except:
+ haveSomethingToMagnify = False
+
+ if haveSomethingToMagnify:
+ _setROICursorPush(x, y, width, height)
+ return
+
+ if not haveSomethingToMagnify:
+ try:
+ extents = obj.queryComponent().getExtents(0)
+ [x, y, width, height] = \
+ [extents.x, extents.y, extents.width, extents.height]
+ haveSomethingToMagnify = True
+ except:
+ haveSomethingToMagnify = False
+
+ if haveSomethingToMagnify:
+ _setROICursorPush(x, y, width, height)
+
+def init():
+ global _zoomer
+ global _screenWidth
+ global _screenHeight
+
+ screen = Gdk.Screen.get_default()
+ _screenWidth = screen.width()
+ _screenHeight = screen.height()
+
+ _dbusLoop = DBusGMainLoop()
+ _bus = dbus.SessionBus(mainloop=_dbusLoop)
+ _proxy_obj = _bus.get_object("org.gnome.Magnifier", "/org/gnome/Magnifier")
+ _magnifier = dbus.Interface(_proxy_obj, "org.gnome.Magnifier")
+ zoomerPaths = _magnifier.getZoomRegions()
+ if not zoomerPaths:
+ return
+
+ zoomProxy = _bus.get_object('org.gnome.Magnifier', zoomerPaths[0])
+ _zoomer = dbus.Interface(
+ zoomProxy, dbus_interface='org.gnome.Magnifier.ZoomRegion')
+
+ pyatspi.Registry.registerEventListener(magnifyAccessible,
+ "object:text-caret-moved",
+ "object:state-changed:focused",
+ "object:state-changed:selected")
+ pyatspi.Registry.start()
+
+def shutdown():
+ pyatspi.Registry.deregisterEventListener(magnifyAccessible,
+ "object:text-caret-moved",
+ "object:state-changed:focused",
+ "object:state-changed:selected")
+ pyatspi.Registry.stop()
+
+def onEnabledChanged(gsetting, key):
+ if key != 'screen-magnifier-enabled':
+ return
+
+ enabled = gsetting.get_boolean(key)
+ if enabled:
+ init()
+ else:
+ shutdown()
+
+def main():
+ a11yAppSettings = Settings('org.gnome.desktop.a11y.applications')
+ if a11yAppSettings.get_boolean('screen-magnifier-enabled'):
+ a11yAppSettings.connect('changed', onEnabledChanged)
+ init()
+ else:
+ print 'Magnification is not running. Exiting.'
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]