Added tapioca
This commit is contained in:
Executable
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPTPATH=`realpath $0`
|
||||||
|
cd `dirname $SCRIPTPATH`/tapioca
|
||||||
|
python tapioca.py --device "" > /dev/null
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 nitg16
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Tapioca
|
||||||
|
|
||||||
|
Tapioca enables right click on linux touchscreen devices using technologies like python-evedev, pymouse (PyUserInput) and xdotool.
|
||||||
|
|
||||||
|
# Available Gestures
|
||||||
|
|
||||||
|
Long press right click and two finger tap right click.
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
Tapioca has been tested to work on Ubuntu 17.04. Run commands below to install dependencies.
|
||||||
|
|
||||||
|
sudo apt install python-evdev xinput xdotool python-pip
|
||||||
|
sudo pip install PyUserInput
|
||||||
|
|
||||||
|
Download Tapioca from [here](https://github.com/nitg16/tapioca/archive/master.zip) and extract to a folder.
|
||||||
|
|
||||||
|
# Running Tapioca
|
||||||
|
|
||||||
|
Find out your touch panel make by running `xinput` command.
|
||||||
|
|
||||||
|
Now run the command below to launch Tapioca.
|
||||||
|
|
||||||
|
sudo python tapioca.py --device ""
|
||||||
|
|
||||||
|
Add the name of your touch panel make within the quotes.
|
||||||
|
|
||||||
|
# Configuring Tapioca
|
||||||
|
|
||||||
|
`tapioca.conf` file is located in `~/.config/tapioca/` folder.
|
||||||
|
|
||||||
|
It is automatically created when you run Tapioca for the first time. Your device name is also automatically added to the config file if you use the command mentioned above to run the application.
|
||||||
|
|
||||||
|
You can edit settings in `User Settings` in config file. All config options are well commented.
|
||||||
|
|
||||||
|
**It is highly recommended that you review config file at least once.**
|
||||||
|
|
||||||
|
# Wayland support?
|
||||||
|
|
||||||
|
Not possible as of now.
|
||||||
|
|
||||||
|
# Credits
|
||||||
|
|
||||||
|
* All the developers behind Linux kernel, python-evdev, PyUserInput, pymouse, xdotool and xinput.
|
||||||
|
|
||||||
|
* [Python-Touchscreen-RightClick](https://github.com/Zyell/Python-Touchscreen-RightClick)
|
||||||
Executable
+309
@@ -0,0 +1,309 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
Reference in New Issue
Block a user