|
#!/usr/bin/env python3 |
|
|
|
# Workaround for: Drop-down menus from menu bar don't react to touchscreen taps |
|
# https://bugzilla.gnome.org/show_bug.cgi?id=789041 |
|
# Works on X only (no Wayland support) |
|
|
|
# sudo ./gtk_touchscreen_fix.py (root required to recognize touch events) |
|
|
|
# credits to niteshgupta16 (https://bugzilla.gnome.org/show_bug.cgi?id=789041#c16) |
|
|
|
########## Follow Setup Instructions below ######## |
|
|
|
# Run command below to install dependencies on Ubuntu. |
|
# You can adapt it for your distro. |
|
# sudo apt install python3-evdev xdotool x11-utils python3-xlib |
|
|
|
# Enter correct touch panel name below (as shown by 'xinput' command). |
|
# It is important that you enter correct touch panel name in the variable below. |
|
# Wrong touch panel name can lead to unintended behaviors and crashes. |
|
|
|
TOUCH_PANEL_NAME = "ipts 045E:001F Touchscreen" # This is my tablet's touch panel according to xinput. Edit it. |
|
|
|
# This script uses two different methods for a workaround fix for this bug. |
|
# It is possible that default method below may not work on your distro window manager. |
|
# If one method is not working for you, try toggling the value below to True or False. |
|
# These two methods are python3-xlib (True) and xdotool (False). |
|
|
|
USE_XLIB = True |
|
|
|
########## End of Setup Instructions ######## |
|
|
|
|
|
########## Do not edit below unless you know what you are doing. ########## |
|
|
|
import subprocess, time |
|
import evdev |
|
from Xlib import X |
|
from Xlib.display import Display |
|
from Xlib.ext.xtest import fake_input |
|
|
|
VALID_WINDOW_TYPES = ["_NET_WM_WINDOW_TYPE_POPUP_MENU"] |
|
display = Display() |
|
screen = display.screen() |
|
no_device_found = True |
|
touch_event_code = 330 |
|
left_click_button = 1 |
|
upto_ten_fingers_touch_time = [None, None, None, None, None, None, None, None, None, None] |
|
devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()] |
|
|
|
def quit_script(): |
|
print ("Quitting in 10 seconds.") |
|
time.sleep(10) |
|
quit() |
|
|
|
for device in devices: |
|
if (device.name == TOUCH_PANEL_NAME): |
|
input_node = device.fn |
|
no_device_found = False |
|
|
|
if no_device_found == True: |
|
print ("Touch input device not found.") |
|
print ("Make sure you have entered correct touch panel name at the top of the script.") |
|
quit_script() |
|
|
|
device = evdev.InputDevice(input_node) |
|
|
|
def is_multitouch(event): |
|
if event.code == 47: |
|
upto_ten_fingers_touch_time[event.value] = event.timestamp() |
|
if upto_ten_fingers_touch_time[0] and upto_ten_fingers_touch_time[0] in upto_ten_fingers_touch_time[1:]: |
|
return True |
|
return False |
|
|
|
for event in device.read_loop(): |
|
# print(evdev.util.categorize(event)) |
|
if USE_XLIB and not is_multitouch(event): |
|
if event.code == touch_event_code and event.value == 1: |
|
touch_window_type, touch_pos = None, None |
|
window = screen.root |
|
window_details = window.query_pointer() |
|
touch_pos = window_details._data["root_x"], window_details._data["root_y"] |
|
touch_window_type = window_details.child.get_full_property(display.intern_atom('_NET_WM_WINDOW_TYPE'), 0) |
|
|
|
if touch_window_type: |
|
try: |
|
touch_window_type = display.get_atom_name(touch_window_type.value[0]) |
|
except IndexError: |
|
print("Looks like this method doesn't work on your window manager.") |
|
print("Change the method at the top of the script and try again.") |
|
quit_script() |
|
|
|
if event.code == touch_event_code and event.value == 0 and touch_pos and touch_window_type in VALID_WINDOW_TYPES: |
|
window = screen.root |
|
window_details = window.query_pointer() |
|
lift_pos = window_details._data["root_x"], window_details._data["root_y"] |
|
lift_window_type = window_details.child.get_full_property(display.intern_atom('_NET_WM_WINDOW_TYPE'), 0) |
|
|
|
if lift_window_type: |
|
try: |
|
lift_window_type = display.get_atom_name(lift_window_type.value[0]) |
|
except IndexError: |
|
print("Looks like this method doesn't work on your window manager.") |
|
print("Change the method at the top of the script and try again.") |
|
quit_script() |
|
|
|
if touch_pos == lift_pos or lift_window_type == touch_window_type: |
|
# print("Tap") |
|
fake_input(display, X.ButtonPress, left_click_button) |
|
fake_input(display, X.ButtonRelease, left_click_button) |
|
display.sync() |
|
|
|
if not USE_XLIB and not is_multitouch(event): |
|
if event.code == touch_event_code and event.value == 1: |
|
touch_window_type, touch_pos = None, None |
|
command = "xprop -id $(xdotool getmouselocation --shell | grep WINDOW | cut -d \"=\" -f 2) | grep _NET_WM_WINDOW_TYPE | cut -d \"=\" -f 2" |
|
touch_window_type = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = True ).communicate() |
|
touch_window_type = touch_window_type[0].decode('UTF-8').strip('\n').strip(' ') |
|
if touch_window_type == "": |
|
print("Looks like this method doesn't work on your window manager.") |
|
print("Change the method at the top of the script and try again.") |
|
quit_script() |
|
|
|
command = "eval $(xdotool getmouselocation --shell) && echo \"$X, $Y\"" |
|
touch_pos = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = True ).communicate() |
|
touch_pos = touch_pos[0].decode('UTF-8').strip('\n') |
|
touch_pos = int((touch_pos.partition(',')[0])), int((touch_pos.partition(',')[2])) |
|
|
|
if event.code == touch_event_code and event.value == 0 and touch_pos and touch_window_type in VALID_WINDOW_TYPES: |
|
command = "xprop -id $(xdotool getmouselocation --shell | grep WINDOW | cut -d \"=\" -f 2) | grep _NET_WM_WINDOW_TYPE | cut -d \"=\" -f 2" |
|
lift_window_type = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = True ).communicate() |
|
lift_window_type = lift_window_type[0].decode('UTF-8').strip('\n').strip(' ') |
|
if lift_window_type == "": |
|
print("Looks like this method doesn't work on your window manager.") |
|
print("Change the method at the top of the script and try again.") |
|
quit_script() |
|
|
|
command = "eval $(xdotool getmouselocation --shell) && echo \"$X, $Y\"" |
|
lift_pos = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = True ).communicate() |
|
lift_pos = lift_pos[0].decode('UTF-8').strip('\n') |
|
lift_pos = int((lift_pos.partition(',')[0])), int((lift_pos.partition(',')[2])) |
|
|
|
if touch_pos == lift_pos or lift_window_type == touch_window_type: |
|
# print("Tap") |
|
command = "xdotool click %s" % left_click_button |
|
subprocess.Popen(command, shell=True) |