310 lines
11 KiB
Python
Executable File
310 lines
11 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
import evdev
|
|
import subprocess
|
|
from decimal import Decimal
|
|
import time
|
|
import os
|
|
import ConfigParser
|
|
from collections import OrderedDict
|
|
from threading import Timer, Thread
|
|
from pymouse import PyMouse
|
|
import argparse
|
|
import sys
|
|
import math
|
|
|
|
# todo
|
|
# long press should not work on two finger long press
|
|
# implement three finger tap
|
|
# implement swipes
|
|
|
|
mouse = PyMouse()
|
|
app_name = "tapioca"
|
|
config_folder = os.path.join(os.path.expanduser("~"), '.config', app_name)
|
|
config_file = app_name + ".conf"
|
|
full_config_file_path = os.path.join(config_folder, config_file)
|
|
Config = ConfigParser.ConfigParser(allow_no_value=True)
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description=app_name + " enables right click on linux touchscreen devices.")
|
|
parser.add_argument(
|
|
"--device", help="Specify name of your touch panel make" \
|
|
" (can be found out by running xinput command).")
|
|
|
|
args = parser.parse_args()
|
|
# print(args.device)
|
|
|
|
# do not edit these settings, all settings should be changed in .conf file
|
|
# in user section
|
|
|
|
default_section = "Default Settings"
|
|
user_section = "User Settings"
|
|
default_settings = OrderedDict([
|
|
('; Touch panel make (run xinput command in terminal)', None),
|
|
('touch_panel', ""),
|
|
('; Minimum long press time in seconds, e.g - 0.5, 1.2', None),
|
|
('minimum_long_press_time', 1.5),
|
|
('; Right click methods, possible values - xdotool, pymouse', None),
|
|
('right_click_method', 'xdotool'),
|
|
('; Long press behavior, possible values - pc (menu appears after finger lift),' \
|
|
' smartphone (no finger lift required, menu flickers once)', None),
|
|
('long_press_behavior', 'pc'),
|
|
('; Long press, possible values - 1 (True), 0 (False)', None),
|
|
('enable_long_press', 1),
|
|
('; Two finger tap, possible values - 1 (True), 0 (False)', None),
|
|
('enable_two_finger_tap', 1)
|
|
])
|
|
|
|
def setconfig():
|
|
try:
|
|
if os.stat(full_config_file_path).st_size > 0:
|
|
file_empty = False
|
|
else:
|
|
file_empty = True
|
|
except OSError:
|
|
pass
|
|
|
|
if not os.path.exists(config_folder):
|
|
os.makedirs(config_folder)
|
|
if not os.path.exists(full_config_file_path) or file_empty == True:
|
|
with open(full_config_file_path, "w+") as f:
|
|
Config.add_section(default_section)
|
|
Config.set(
|
|
default_section, '; !!!! do not edit, change settings in user section !!!!')
|
|
for k, v in default_settings.items():
|
|
if v == None:
|
|
Config.set(default_section, k)
|
|
else:
|
|
Config.set(default_section, k, v)
|
|
Config.add_section(user_section)
|
|
Config.set(user_section, '; change settings below')
|
|
for k, v in default_settings.items():
|
|
if v == None:
|
|
Config.set(user_section, k)
|
|
else:
|
|
Config.set(user_section, k, v)
|
|
Config.write(f)
|
|
|
|
if args.device != None:
|
|
with open(full_config_file_path, "w") as b:
|
|
Config.set(user_section, "touch_panel", args.device)
|
|
Config.write(b)
|
|
|
|
try:
|
|
setconfig()
|
|
except KeyboardInterrupt:
|
|
sys.exit(1)
|
|
|
|
Config.read(full_config_file_path)
|
|
|
|
try:
|
|
touch_panel = Config.get(user_section, "touch_panel")
|
|
except:
|
|
try:
|
|
touch_panel = Config.get(default_section, "touch_panel")
|
|
except:
|
|
touch_panel = default_settings['touch_panel']
|
|
|
|
try:
|
|
minimum_long_press_time = float(Config.get(
|
|
user_section, "minimum_long_press_time"))
|
|
except:
|
|
try:
|
|
minimum_long_press_time = float(Config.get(
|
|
default_section, "minimum_long_press_time"))
|
|
except:
|
|
minimum_long_press_time = default_settings['minimum_long_press_time']
|
|
|
|
try:
|
|
right_click_method = Config.get(user_section, "right_click_method")
|
|
except:
|
|
try:
|
|
right_click_method = Config.get(default_section, "right_click_method")
|
|
except:
|
|
right_click_method = default_settings['right_click_method']
|
|
|
|
try:
|
|
long_press_behavior = Config.get(user_section, "long_press_behavior")
|
|
except:
|
|
try:
|
|
long_press_behavior = Config.get(
|
|
default_section, "long_press_behavior")
|
|
except:
|
|
long_press_behavior = default_settings['long_press_behavior']
|
|
|
|
try:
|
|
enable_long_press = Config.getboolean(user_section, "enable_long_press")
|
|
except:
|
|
try:
|
|
enable_long_press = Config.getboolean(
|
|
default_section, "enable_long_press")
|
|
except:
|
|
enable_long_press = default_settings['enable_long_press']
|
|
|
|
try:
|
|
enable_two_finger_tap = Config.getboolean(
|
|
user_section, "enable_two_finger_tap")
|
|
except:
|
|
try:
|
|
enable_two_finger_tap = Config.getboolean(
|
|
default_section, "enable_two_finger_tap")
|
|
except:
|
|
enable_two_finger_tap = default_settings['enable_two_finger_tap']
|
|
|
|
lift_time = None
|
|
touch_time = None
|
|
touch_x = 0
|
|
touch_y = 0
|
|
finger0_time = None
|
|
finger1_time = None
|
|
finger2_time = None
|
|
abs_touch_time = None
|
|
abs_lift_time = None
|
|
no_device_found = True
|
|
long_press_done = False
|
|
|
|
dev_abs_x = 0
|
|
dev_abs_y = 0
|
|
|
|
def rclick():
|
|
if right_click_method == "xdotool":
|
|
# proc = subprocess.Popen("eval $(xdotool getmouselocation --shell) && echo $X $Y", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = True ).communicate()
|
|
# print proc[0]
|
|
# p = proc[0].strip('\n')
|
|
# xc = int(p.partition(' ')[0])
|
|
# yc = int(p.partition(' ')[2])
|
|
# print xc, yc
|
|
# subprocess.Popen(["xdotool", "mousemove", "--sync", str(xc), str(yc)], shell = False)
|
|
subprocess.Popen(["xdotool", "click", "3"], stdout=subprocess.PIPE)
|
|
elif right_click_method == "pymouse":
|
|
mouse.click(mouse.position()[0], mouse.position()[1], 2)
|
|
|
|
def dummy(time):
|
|
pass
|
|
|
|
|
|
while True:
|
|
try:
|
|
devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()]
|
|
|
|
for device in devices:
|
|
# print(device.fn, device.name, device.phys)
|
|
# print device.name
|
|
if (device.name == touch_panel):
|
|
input_node = device.fn
|
|
no_device_found = False
|
|
|
|
if no_device_found == True:
|
|
print "No touch input detected."
|
|
print "Please make sure you have entered correct touch panel name in user settings."
|
|
print "Quitting in 10 seconds."
|
|
time.sleep(10)
|
|
quit()
|
|
|
|
device = evdev.InputDevice(input_node)
|
|
# print device
|
|
# print device.capabilities(verbose=True)
|
|
# print device.active_keys(verbose=True)
|
|
|
|
for event in device.read_loop():
|
|
try:
|
|
# print(evdev.util.categorize(event))
|
|
if event.type == 3: # absolute axis event
|
|
if event.code == 0:
|
|
dev_abs_x = event.value
|
|
elif event.code == 1:
|
|
dev_abs_y = event.value
|
|
elif event.type == 1: # key event
|
|
#print(evdev.util.categorize(event))
|
|
#print event.code
|
|
pass
|
|
else:
|
|
#print(evdev.util.categorize(event))
|
|
#print event.type
|
|
pass
|
|
|
|
if event.code == 321 and event.value == 0:
|
|
print "stylus tool rubber click"
|
|
rclick()
|
|
|
|
if event.code == 330 and event.value == 1:
|
|
touch_time = Decimal(str(event.sec) + "." + str(event.usec))
|
|
touch_x = dev_abs_x
|
|
touch_y = dev_abs_y
|
|
# print touch_time, " (", touch_x, ", ", touch_y, ")"
|
|
t = Timer(minimum_long_press_time, dummy, [touch_time])
|
|
t.daemon = True
|
|
t.start()
|
|
|
|
if event.code == 330 and event.value == 0:
|
|
lift_time = Decimal(str(event.sec) + "." + str(event.usec))
|
|
# print lift_time
|
|
else:
|
|
lift_time = None
|
|
|
|
if event.code == 47 and event.value == 0:
|
|
finger0_time = Decimal(str(event.sec) + "." + str(event.usec))
|
|
# print finger0_time
|
|
|
|
if event.code == 47 and event.value == 1:
|
|
finger1_time = Decimal(str(event.sec) + "." + str(event.usec))
|
|
# print finger1_time
|
|
|
|
if event.code == 47 and event.value == 2:
|
|
finger2_time = Decimal(str(event.sec) + "." + str(event.usec))
|
|
# print finger2_time
|
|
|
|
if event.code == 57 and event.value > -1:
|
|
abs_touch_time = Decimal(str(event.sec) + "." + str(event.usec))
|
|
# print abs_touch_time
|
|
t = Timer(minimum_long_press_time, dummy, [abs_touch_time])
|
|
t.daemon = True
|
|
t.start()
|
|
|
|
if event.code == 57 and event.value == -1:
|
|
abs_lift_time = Decimal(str(event.sec) + "." + str(event.usec))
|
|
# print abs_lift_time
|
|
else:
|
|
abs_lift_time = None
|
|
|
|
if touch_time != None and lift_time == None and long_press_behavior == "smartphone":
|
|
if t.is_alive() == False and long_press_done == False:
|
|
if enable_long_press == True:
|
|
touch_distance = math.sqrt((touch_x-dev_abs_x)**2+(touch_y-dev_abs_y)**2)
|
|
# print "touch distance: ", touch_distance
|
|
if touch_distance < 200:
|
|
print "smartphone long press"
|
|
rclick()
|
|
long_press_done = True
|
|
t.cancel()
|
|
t.join()
|
|
|
|
if touch_time != None and lift_time != None and enable_long_press == True:
|
|
diff = lift_time - touch_time
|
|
# print diff
|
|
if diff >= minimum_long_press_time and long_press_behavior == "pc":
|
|
print "pc long press"
|
|
rclick()
|
|
|
|
if event.code == 330 and event.value == 0:
|
|
if long_press_done == True and long_press_behavior == "smartphone":
|
|
rclick()
|
|
long_press_done = False
|
|
touch_time = None
|
|
|
|
if finger0_time != None and finger1_time != None and enable_two_finger_tap == True:
|
|
if event.code == 330 and event.value == 0:
|
|
if finger0_time == finger1_time:
|
|
print "Two finger tap"
|
|
rclick()
|
|
except (KeyboardInterrupt, SystemExit):
|
|
raise
|
|
except:
|
|
pass
|
|
|
|
except (KeyboardInterrupt, SystemExit):
|
|
raise
|
|
except:
|
|
pass
|
|
|