-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathRightClick-v2.py
More file actions
executable file
·228 lines (201 loc) · 10.1 KB
/
RightClick-v2.py
File metadata and controls
executable file
·228 lines (201 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
"""
Python Script for adding 1 & 2 finger multitouch gestures to implement
a right click option with Touchscreens in the Ubuntu unity environment.
This is implemented with the evdev Python library on an ELAN touchscreen.
Currently implements 2 types of right click options:
1 finger long touch: Timeout of 1.7 seconds, movement cancels action
2 finger tap: movement cancels action
GV:
For troubleshooting you can use xinput to see a list of all registered input devices.
With xinput --list-props id you see properties of the device.
With $evtest (and in some case xev) you can verify the events captured by a particular device.
Tip: Virtual Devices created with python uinput will also be listed in the end of evtest devices list.
To find info about your screen and your Xserver you can call xdpyinfo (using |less). The first lines give usefull info about characteristics of your system/screen
Also i think it is better instead of libinput driver to use evdev.
Using evdev as a driver, you are allowed to use xinput to set properties dynamically
(before to make them permanent in conf files under /etc/X11/xorg.conf.d/ or /usr/share/X11/xorg.conf.d/ folder)
To dynamically set evdev properties first go at $xinput --list-props deviceid , identify the property id number you want to change
and then $xinput set-prop 8 280 1 (8= device id, 280 = parameter id , 1 = parameter value = true).
Alternative: xinput set-prop 8 'Evdev Third Button Emulation' 1)
Tip: You can record the events using evemu-record (see http://linuxwacom.sourceforge.net/wiki/index.php/Analysing_kernel_events)
You can see the handlers of the event by cat /proc/bus/input/devices , line Handler=mouse 0 event 1 (or combine |grep hanler in cat)
"""
from evdev import InputDevice, ecodes, UInput, list_devices
from threading import Timer
from pymouse import PyMouse
import sys
from datetime import datetime
class TrackedEvent(object):
"""
Class for multitouch event tracking.
Track position, movement, slots used (total number of fingers in gesture),
timing of long presses, and event completion.
"""
def __init__(self, dev, abilities, var_x, var_y):
""" Initialize tracking attributes. """
self.dev = dev
self.abilities = abilities
self.vars = {'ABS_X': var_x, 'ABS_Y': var_y}
self.position = {'ABS_X': None, 'ABS_Y': None}
self.fingers = 0
self.total_event_fingers = 0
self.discard = 0
self.moved = 0
self.track_start = None
self.click_delay = 0.7
self.grabbed=False
# def stamp(self):
# self.stamp=datetime.now().time()
# return self.stamp;
def add_finger(self):
""" Add a detected finger. """
self.fingers += 1
self.total_event_fingers = self.fingers
def remove_fingers(self):
""" Remove detected finger upon release. """
if self.fingers == 1:
print datetime.now(),':[Remove_Fingers]: Total Fingers used= ', self.total_event_fingers
self.fingers -= 1
if (self.fingers == 0 and
self.total_event_fingers == 2 and
self.moved == 0):
self.total_event_fingers = 0
self._initiate_right_click()
elif (self.fingers == 0 and
self.total_event_fingers == 1 and
self.moved == 0):
self.total_event_fingers = 0
try:
self.track_start.cancel()
self.track_start.join()
except AttributeError: # capture Nonetype track_start
if dbg: print datetime.now(),':[Remove_Fingers]: Attribute Error = ', AttributeError
pass
#try:
#self.dev.ungrab()
#if dbg: print datetime.now(),':[Remove_Fingers]: Device UnGrab'
#except (OSError,IOError): # capture case where grab was never initiated
# if dbg: print datetime.now(),':[Remove_Fingers]: OS/IO Error Exception'
# pass
if self.fingers == 0:
self.discard = 1
if dbg: print datetime.now(),':[Remove_Fingers]: Fingers 0 (Discard)'
def position_event(self, event_code, value):
""" tracks position to track movement of fingers """
if self.position[event_code] is None:
self.position[event_code] = value
else:
if dbg: print datetime.now(),':[Position_Event]: Touch offset:', abs(self.position[event_code] - value)
if abs(self.position[event_code] - value) > self.vars[event_code]:
self._moved_event()
if (self.fingers == 1 and self.position['ABS_X'] and
self.position['ABS_Y'] and self.track_start is None):
self._trackit()
def _trackit(self):
""" start timing for long press """
self.track_start = Timer(self.click_delay, self._long_press)
self.track_start.start()
def _long_press(self):
if self.fingers == 1 and self.moved == 0:
self._initiate_right_click()
#if dbg: print datetime.now(),':[_Long_Press]: Device Grab'
#self.dev.grab()
def _moved_event(self):
""" movement detected. """
self.moved = 1
if dbg: print datetime.now(),':[Moved_Event]: Moved over the limit (self.moved=1)'
def _initiate_right_click(self):
""" Internal method for initiating a right click at touch point. """
if dbg: print datetime.now(),':[Initiate_Right_Click]: Right Click will be injected now'
'''
with UInput(self.abilities) as ui:
ui.write(ecodes.EV_ABS, ecodes.ABS_X, 0)
ui.write(ecodes.EV_ABS, ecodes.ABS_Y, 0)
ui.write(ecodes.EV_KEY, ecodes.BTN_RIGHT, 1)
ui.write(ecodes.EV_KEY, ecodes.BTN_RIGHT, 0)
ui.syn()
'''
m = PyMouse()
x , y = m.position() # gets mouse current position coordinates
m.click(x, y, 1) # the third argument represents the mouse button (1 left click,2 right click,3 middle click)
m.click(x, y, 2) # the third argument represents the mouse button (1 left click,2 right click,3 middle click)
if dbg: print datetime.now(),':[Initiate_Right_Click]: Right Click injected at X,Y:', m.position()
def initiate_gesture_find():
"""
This function will scan all input devices until it finds an
ELAN touchscreen. It will then enter a loop to monitor this device
without blocking its usage by the system.
"""
for device in list_devices():
dev = InputDevice(device)
if (dev.name == 'ELAN Touchscreen' or dev.name=='VirtualBox USB Tablet' or dev.name == 'Atmel Atmel maXTouch Digitizer'):
if dbg: print datetime.now(),':[Gesture_Find]: Device found=', dev.name, '-',dev, "\n"
break
Abs_events = {}
abilities = {ecodes.EV_ABS: [ecodes.ABS_X, ecodes.ABS_Y],
ecodes.EV_KEY: (ecodes.BTN_LEFT, ecodes.BTN_RIGHT)}
# Assuming QHD screen on my Yoga 2 Pro as default for resolution measures
res_x = 13 # touch unit resolution # units/mm in x direction
res_y = 13 # touch unit resolution # units/mm in y direction
# would be weird if above resolutions differed, but will treat generically
codes = dev.capabilities()
if dbg: print datetime.now(),':[Gesture Find]: Device capabilities= ',dev.capabilities(verbose=True,absinfo=True)
for code in codes:
if code == 3:
for type_code in codes[code]:
human_code = ecodes.ABS[type_code[0]]
if human_code == 'ABS_X':
vals = type_code[1]
abilities[ecodes.EV_ABS][0] = (ecodes.ABS_X, vals)
res_x = vals[-1]
elif human_code == 'ABS_Y':
vals = type_code[1]
abilities[ecodes.EV_ABS][1] = (ecodes.ABS_Y, vals)
res_y = vals[-1]
Abs_events[type_code[0]] = human_code
# Average index finger width is 16-20 mm, assume 20 mm
# touch resolution noise assumed at 10% (5% radius), so 1.0 mm by default
# this seemed resonable from my own trial tests
var_x = 4.0 * res_x # variablity in movement allowed in x direction
var_y = 4.0 * res_y # variablity in movement allowed in y direction
'''
var_x = 1.0 * res_x # variablity in movement allowed in x direction
var_y = 1.0 * res_y # variablity in movement allowed in y direction
'''
MT_event = None
for event in dev.read_loop():
if event.type == ecodes.EV_ABS:
if MT_event is None:
MT_event = TrackedEvent(dev, abilities, var_x, var_y)
event_code = Abs_events[event.code]
if event_code == 'ABS_X' or event_code == 'ABS_Y':
MT_event.position_event(event_code, event.value)
elif event_code == 'ABS_MT_TRACKING_ID':
if event.value == -1:
MT_event.remove_fingers()
if MT_event.discard == 1:
MT_event = None
else:
MT_event.add_finger()
if __name__ == '__main__':
dbg = True #applied for testing. Normally this should be False
#dbg = False
if len(sys.argv)>1 and sys.argv[1]=="--debug":
print 'Debug option selected'
dbg = True
else:
print 'Run with --debug to get detailed info'
print('debug level:',dbg)
initiate_gesture_find()
'''
We can add more options like:
argv --calibrate -> call a calibrate procedure to get correct options for res-x , res-y, var-x, var-y
argv --icon -> display a tray icon (or --noicon if icon is going to be displayed by default)
argv --lowres -> apply a lower value in valx-valy (i.e value of 1.0 as per Zyell initial scrip)
function screencapabilities detection : to ensure that touchscreen comply with the method described in script
Nautilus is buggy. If you click once at whitespace, then you can not bring context menu in files/folders.
Actually you can not even open them with double click , either by touch or by trackpad (!)
You can select a folder , but you can not even do a right click.
All right clicks after right click at whitespace, bring the whitespace menu and not the folder menu.
And all this stuff with left click first.
'''