#!/usr/bin/python
#
# Dead simple, Orca-free accessibility event logger designed to help answer
# the question "Why is Orca doing or not doing foo and bar in unity-greeter?".
#
# The quick-and-dirty way to use it is:
#   1. mv /usr/bin/orca /usr/bin/orca-real
#   2. cp listener.py /usr/bin/orca
#   3. log out, enable screen reader, interact with unity-greeter
#   4. log in as guest and view /tmp/a11yListener.out
#
# Note that key press and release events are logged for the purpose of
# debugging. That does mean, however, that what you type in a password
# field will wind up in the output. Hence step 4's suggestion to log in
# as guest.

import os
import pyatspi
import sys
import time

class A11yListener:

    def __init__(self, logFile):
        self.logFile = logFile
        self.log('================ %s ================' % time.ctime())

    def log(self, toLog):
        try:
            logfile = open(self.logFile, 'a')
            logfile.writelines('%s\n' % toLog)
            logfile.close()
        except:
            pass

    def onEvent(self, e):
        # We get a TON of state-changed events. Which ones do we care about
        # for the bug we are trying to reproduce?
        ignore = ['object:state-changed:showing',
                  'object:state-changed:visible',
                  'object:state-changed:checked',
                  'object:state-changed:armed',
                  'object:state-changed:enabled',
                  'object:state-changed:sensitive',
                  'object:state-changed:defunct']

        # W.R.T. selection and focus changes, Orca typically should present
        # the widget which just gained focus or became selected; NOT the one
        # which just gave up focus or just became unselected. For state-changed
        # events, the event's detail1 represents a boolean. (Note that does NOT
        # apply to other types of events.)
        if e.detail1 == 0:
            ignore.extend(['object:state-changed:focused',
                           'object:state-changed:selected',
                           'object:state-changed:active'])

        if not e.type in ignore:
            self.log(e)

    def onKey(self, e):
        if e.type == pyatspi.KEY_PRESSED_EVENT:
            typeString = '>>> KEY PRESSED'
        elif e.type == pyatspi.KEY_RELEASED_EVENT:
            typeString = '<<< KEY RELEASED'
        self.log('%s: %s' % (typeString, e.event_string))

    def start(self):
        pyatspi.Registry.registerKeystrokeListener(
            self.onKey,
            mask=range(256),
            kind=(pyatspi.KEY_PRESSED_EVENT, pyatspi.KEY_RELEASED_EVENT))
        pyatspi.Registry.registerEventListener(
            self.onEvent, 'object:state-changed', 'window:')
        pyatspi.Registry.start()

def main():
    a11yListener = A11yListener('/tmp/a11yListener.out')
    a11yListener.start()
    return 0

if __name__ == "__main__":
    sys.exit(main())
