#!/usr/bin/python3

# Copyright (c) 2019 Samuel Thibault <samuel.thibault@ens-lyon.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 
# THIS SOFTWARE IS PROVIDED BY Samuel Thibault ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
# EVENT SHALL Samuel Thibault BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
# OF SUCH DAMAGE.

import pyatspi
import time
import sys

events = [
    "document:load-complete",
    "document:load-stopped",
    "document:reload",
    "focus",
    "mouse:button",
    "object:active-descendant-changed",
    "object:children-changed",
    "object:column-reordered",
    "object:property-change",
    "object:row-reordered",
    "object:selection-changed",
    "object:state-changed",
    "object:text-attributes-changed",
    "object:text-caret-moved",
    "object:text-changed",
    "object:text-selection-changed",
    "object:value-changed",
    "window:activate",
    "window:create",
    "window:deactivate",
]

refresh_time = 3

apps = { }
last = time.time()

def clear():
    print("\033[H\033[2J", end='')
    sys.stdout.flush()

def handler(event, obj=None, extents=None):
    global apps, last

    obj = obj or event.source
    if obj:
        try:
            while obj and obj.getRole() != pyatspi.ROLE_APPLICATION:
                obj = obj.parent
            a = obj

            # Add app or event name if not already present
            if a not in apps:
                apps[a] = [ 0, {} ]
            if event.type not in apps[a][1]:
                apps[a][1][event.type] = 0

            # Count one for this event
            apps[a][0] += 1
            apps[a][1][event.type] += 1
        except:
            pass

    now = time.time()
    if last + refresh_time < now:
        delta = now - last
        last = now
        l = []

        # sort list by activity
        for a in apps.keys():
            if apps[a][1] != 0:
                try:
                    l.append((a.name,apps[a]))
                    apps[a] = [ 0, {} ]
                except:
                    pass
        l.sort(key = lambda x: x[1][0], reverse=True)

        clear()
        for (name, data) in l:
            print("%s: %.2f/s" % (name, data[0] / delta))
            for (t, n) in sorted(data[1].items(), key = lambda x: x[1], reverse=True):
                if n >= data[0] / 10:
                    print("  %s: %.2f/s" % (t, n / delta))


clear()
for e in events:
    pyatspi.Registry.registerEventListener(handler, e)
pyatspi.Registry.start()
