'''
Created on 02.12.2011

@author: Hannes Eilers
@summary: Small tuio based python mouse using xautomation to generate X-Server Inputs
'''
import tuio
import Tkinter
import time
import math
import os
import sys



if __name__ == '__main__':

	'''Ich
	---------------------- CONFIG ----------------------
	'''

	RCLICK_TIMEOUT = 2  		# timeout in seconds bevor a hold is a right click

	COUNT_COURSORS = 5			# times a cursor have to be recognized to do something

	TH_CUR_POS = 5				# threshold around the last found position of the cursor
								# if the new position differs more than this amount of pixels move

	TH_MOV_CUR_POS = 0.08		# threshold for distance between two gesture cursors
								# if the distance between two cursors is higher than this value (0.0 - 1.0)
								# the don't belong together
	TH_MOV_VEC = 0.3			# difference between the length of the two movement vectors
								# of wo cursors if the difference is higher than this
								# value (0.0-1.0) the vectors don't belong together
	TH_MOV_ANG = 0.1			# difference of the angle (radiant) between the movement vectors
								# if the difference is higher than this value
								# the vectors don't belong together

	SCROLL_MULT = 1				# number of key down events should be done for every scroll event

	SCREEN = 0

	'''
	---------------------- CONFIG END ----------------------
	'''

	tracking = tuio.Tracking()

	mySessionID = None
	lastX = 0
	lastY = 0
	startTime = 0
	dRClick = False

	# get screen dimensions
	window = Tkinter.Tk()
	screen = [window.winfo_screenwidth(), window.winfo_screenheight()]

	print "TUIO MOUSE"
	print "close window to quit"
	print "Screen dimensions: " + str(screen) + "\n"
	print "tap to click"
	print "tap, move and release to drag and drop"
	print "tap an hold for " + str(RCLICK_TIMEOUT) + " seconds to do a right click"
	print "use two parallel fingers to scroll up/down and left/right"

	'''check if to set the x screen'''
	try:
		if sys.argv[1] == '-screen' or sys.argv[1] == '-s':
			SCREEN = sys.argv[2]
	except:
		pass

	'''main loop'''
	try:
		while 1:

			# update tracking data
			lCursors = []
			tracking.update()

			# add every new tracked cursor to list
			i = 0
			for cur in tracking.cursors():
				lCursors.append(cur)
				i = i + 1


			'''
			handle gestures
			'''
			# if there're two or more cursors
			if( lCursors and i > 1 ):

				# create vectors
				cur1 = [lCursors[0].xpos, lCursors[0].ypos]
				cur2 = [lCursors[1].xpos, lCursors[1].ypos]

				# calculate distance between cursors
				dVec = [cur1[0]-cur2[0], cur1[1]-cur2[1]]
				distance = math.sqrt(dVec[0]*dVec[0] + dVec[1]*dVec[1])

				# check if cursors belong together
				if(abs(distance) <= TH_MOV_CUR_POS):
					# cursors belong together
					# create movement vectors
					mov1 = [lCursors[0].xmot, lCursors[0].ymot, lCursors[0].mot_accel]
					mov2 = [lCursors[1].xmot, lCursors[1].ymot, lCursors[1].mot_accel]
					mov = [(mov1[0]+mov2[0])/2.0, (mov1[1]+mov2[1])/2.0, (mov1[2]+mov2[2])/2.0]

					# calculate length of movement vectors
					try:
						lmov1 = math.sqrt(mov1[0]*mov1[0] + mov1[1]*mov1[1])
						lmov2 = math.sqrt(mov2[0]*mov2[0] + mov2[1]*mov2[1])
					except:
						lmov1 = 0.0
						lmov2 = 0.0

					lmov = (lmov1 + lmov2)/2.0


					'''
					handle scrolling
					'''
					# calculate angle between movement vectors
					try:
						ang = math.acos( (mov1[0]*mov2[0] + mov1[1]*mov2[1])/(lmov1 * lmov2) )
					except:
						ang = TH_MOV_ANG + 1

					# check if angle between movement vectors and length of the movement vectors is ok
					if( (ang <= TH_MOV_ANG) and (abs(lmov1-lmov2) <= TH_MOV_VEC) ):
						# angle and length are ok

						# scroll right/left (x-axis)
						if( abs(round(mov[0])) > 0 ):
							if( mov[0] > 0 ):
								os.system( "xdotool key Right" )
							else:
								os.system( "xdotool key Left" )

						# scroll up/down (y-axis)
						if( abs(round(mov[1])) > 0 ):
							if( mov[1] > 0):
								os.system( "xdotool click 5" )
							else:
								os.system( "xdotool click 4" )

						i = 0		# block handle clicking
						pass


			'''
			handle clicking
			'''
			# if there's a cursor
			if( lCursors and i > 0 ):

				# calculate absolute screen position of first found cursor
				x = int(lCursors[0].xpos * screen[0])
				y = int(lCursors[0].ypos * screen[1])

				# if first cursor has same id like last time just move
				if( lCursors[0].sessionid == mySessionID ):

					if not startTime:
						startTime = time.time()

					# check if position differs from last position
					if( abs(lastX - x) > TH_CUR_POS ) or ( abs(lastY - y ) > TH_CUR_POS ):
						# position changed
						os.system( "xdotool mousemove --screen" + str(SCREEN) + " " + str(x) + " " + str(y) )
						lastX = x
						lastY = y
					else:
						# position didn't changed
						# check for hold
						if( (time.time() >= startTime + RCLICK_TIMEOUT) and (not dRClick) ):
							# check found do right click
							os.system( "xdotool click 3" )
							dRClick = True

				else:
					# else release mouse button, move mouse cursor an press mouse button again
					startTime = 0
					dRClick = False
					os.system( "xdotool mouseup 3" )
					os.system( "xdotool mouseup 1" )
					os.system( "xdotool mousemove --screen" + str(SCREEN) + " Hi" + str(x) + " " + str(y) )
					os.system( "xdotool mousedown 1" )
					mySessionID = lCursors[0].sessionid
					countCursors = 0

			# if there's no cursor release mouse button an clear last session ID
			else:
				os.system( "xdotool mouseup 1" )
				dRClick = False
				mySessionID = None

	except KeyboardInterrupt:
		os.system( "xdotool mouseup 1" )
		os.system( "xdotool mouseup 3" )
		tracking.stop()
