#!/usr/bin/python
# vim: set fileencoding=utf-8
import Xlib.display
import ctypes
import os
import signal
import time
import sys
import datetime
import re

logfile = os.environ.get("HOME") + "/x11-time-tracker.new.log"
mark_away_time = 60 * 1  # 1 minute

################################################################################
# all credit for the X11 code to
# http://thpinfo.com/2007/09/x11-idle-time-and-focused-window-in.html

class XScreenSaverInfo( ctypes.Structure):
  """ typedef struct { ... } XScreenSaverInfo; """
  _fields_ = [('window',      ctypes.c_ulong), # screen saver window
	      ('state',       ctypes.c_int),   # off,on,disabled
	      ('kind',        ctypes.c_int),   # blanked,internal,external
	      ('since',       ctypes.c_ulong), # milliseconds
	      ('idle',        ctypes.c_ulong), # milliseconds
	      ('event_mask',  ctypes.c_ulong)] # events

xlib = ctypes.cdll.LoadLibrary('libX11.so')
dpy = xlib.XOpenDisplay(0)
root = xlib.XDefaultRootWindow(dpy)
xss = ctypes.cdll.LoadLibrary('libXss.so.1')
xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo)
xss_info = xss.XScreenSaverAllocInfo()

def x_idle_time():
  xss.XScreenSaverQueryInfo( dpy, root, xss_info)
  return xss_info.contents.idle

display = Xlib.display.Display()

################################################################################

signalled_to_exit = False
now = time.time()
LOG = open(logfile, "a", 0)

def handler(sig, frame):
  global signalled_to_exit
  signalled_to_exit = True

signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)

class wm_info:
  time = 0.0
  idle = 0
  wname = ""
  def __init__(self):
    self.time = time.time()
    self.idle = x_idle_time() / 1000.0
    focus = display.get_input_focus()
    self.wname = str(focus.focus.get_wm_class()) + " " + str(focus.focus.get_wm_name())
  def ctime(self):
    return time.ctime(self.time)

def timestring(secs):
  r = str(datetime.timedelta(seconds=secs))
  r = re.sub(r'^(0+:)+', r'', r)
  r = re.sub(r'^0+(\d)', r'\1', r)
  r = re.sub(r'\.\d+$',  r's', r)
  return r

before = False
away = False
using = False
while True:

  # TODO: filter out "USED --- for 0s" and make this not mark unaway.

  now = wm_info();
  
  do_mark_away = not away and now.idle >= mark_away_time
  do_mark_unaway = away and now.idle < mark_away_time

  if not before:
    LOG.write("%s --- LOG OPENED --- with %s idle: «%s»\n" %
	      (now.ctime(), timestring(now.idle), now.wname))
    using = now

  else:

    window_changed = now.wname != before.wname

    if window_changed:

      if not away:
	LOG.write("%s --- USED --- for %s --- «%s»\n" %
		  (now.ctime(), timestring(now.time - using.time), using.wname))
	using = now

    if do_mark_away:
      if not window_changed:
	LOG.write("%s --- USED --- for %s --- «%s»\n" %
		  (now.ctime(), timestring(now.time - using.time - mark_away_time), using.wname))
      LOG.write("%s --- AWAY --- after %s idle\n" % (now.ctime(), timestring(now.idle)))
      away = now.time
      using = False

    if do_mark_unaway:
      using = now
      LOG.write("%s --- BACK --- after %s away\n" %
		(now.ctime(), timestring(now.time - away)))
      away = False

  if signalled_to_exit:
    if not away and not window_changed:
      LOG.write("%s --- USED --- for %s --- «%s»\n" %
		(now.ctime(), timestring(now.time - using.time), using.wname))
    LOG.write("%s --- LOG CLOSED --- with %.3fs idle: «%s»\n" %
	      (now.ctime(), now.idle, now.wname))
    LOG.close()
    sys.exit()

  before = now
  time.sleep(1)

