#!/usr/bin/python3

import threading
import time
import os
import struct
import fcntl
import subprocess

# ioctl to get the device name of /dev/input/event*
EVIOCGNAME = 0x82004506
EVIOCGBIT = 0x81fe4520

# Format for the linux /dev/input/event* events.
#   tv_sec, tv_usec, type, code, value
EVENT_FMT = "llHHi"

EV_SYN = 0x00
EV_KEY = 0x01
EV_REL = 0x02
EV_ABS = 0x03
EV_MSC = 0x04
EV_SW  = 0x05
EV_LED = 0x11
EV_SND = 0x12
EV_REP = 0x14
EV_FF  = 0x15
EV_PWR = 0x16
EV_FF_STATUS = 0x17

KEY_RESERVED = 0
KEY_ESC = 1
KEY_1 = 2
KEY_2 = 3
KEY_3 = 4
KEY_4 = 5
KEY_5 = 6
KEY_6 = 7
KEY_7 = 8
KEY_8 = 9
KEY_9 = 10
KEY_0 = 11
KEY_MINUS = 12
KEY_EQUAL = 13
KEY_BACKSPACE = 14
KEY_TAB = 15
KEY_Q = 16
KEY_W = 17
KEY_E = 18
KEY_R = 19
KEY_T = 20
KEY_Y = 21
KEY_U = 22
KEY_I = 23
KEY_O = 24
KEY_P = 25
KEY_LEFTBRACE = 26
KEY_RIGHTBRACE = 27
KEY_ENTER = 28
KEY_LEFTCTRL = 29
KEY_A = 30
KEY_S = 31
KEY_D = 32
KEY_F = 33
KEY_G = 34
KEY_H = 35
KEY_J = 36
KEY_K = 37
KEY_L = 38
KEY_SEMICOLON = 39
KEY_APOSTROPHE = 40
KEY_GRAVE = 41
KEY_LEFTSHIFT = 42
KEY_BACKSLASH = 43
KEY_Z = 44
KEY_X = 45
KEY_C = 46
KEY_V = 47
KEY_B = 48
KEY_N = 49
KEY_M = 50
KEY_COMMA = 51
KEY_DOT = 52
KEY_SLASH = 53
KEY_RIGHTSHIFT = 54
KEY_KPASTERISK = 55
KEY_LEFTALT = 56
KEY_SPACE = 57
KEY_CAPSLOCK = 58
KEY_F1 = 59
KEY_F2 = 60
KEY_F3 = 61
KEY_F4 = 62
KEY_F5 = 63
KEY_F6 = 64
KEY_F7 = 65
KEY_F8 = 66
KEY_F9 = 67
KEY_F10 = 68
KEY_NUMLOCK = 69
KEY_SCROLLLOCK = 70
KEY_KP7 = 71
KEY_KP8 = 72
KEY_KP9 = 73
KEY_KPMINUS = 74
KEY_KP4 = 75
KEY_KP5 = 76
KEY_KP6 = 77
KEY_KPPLUS = 78
KEY_KP1 = 79
KEY_KP2 = 80
KEY_KP3 = 81
KEY_KP0 = 82
KEY_KPDOT = 83

KEY_ZENKAKUHANKAKU = 85
KEY_102ND = 86
KEY_F11 = 87
KEY_F12 = 88
KEY_RO = 89
KEY_KATAKANA = 90
KEY_HIRAGANA = 91
KEY_HENKAN = 92
KEY_KATAKANAHIRAGANA = 93
KEY_MUHENKAN = 94
KEY_KPJPCOMMA = 95
KEY_KPENTER = 96
KEY_RIGHTCTRL = 97
KEY_KPSLASH = 98
KEY_SYSRQ = 99
KEY_RIGHTALT = 100
KEY_LINEFEED = 101
KEY_HOME = 102
KEY_UP = 103
KEY_PAGEUP = 104
KEY_LEFT = 105
KEY_RIGHT = 106
KEY_END = 107
KEY_DOWN = 108
KEY_PAGEDOWN = 109
KEY_INSERT = 110
KEY_DELETE = 111
KEY_MACRO = 112
KEY_MUTE = 113
KEY_VOLUMEDOWN = 114
KEY_VOLUMEUP = 115
KEY_POWER = 116 # SC System Power Down
KEY_KPEQUAL = 117
KEY_KPPLUSMINUS = 118
KEY_PAUSE = 119
KEY_SCALE = 120 # AL Compiz Scale (Expose)

KEY_KPCOMMA = 121
KEY_HANGEUL = 122
KEY_HANGUEL = KEY_HANGEUL
KEY_HANJA = 123
KEY_YEN = 124
KEY_LEFTMETA = 125
KEY_RIGHTMETA = 126
KEY_COMPOSE = 127

KEY_STOP = 128 # AC Stop
KEY_AGAIN = 129
KEY_PROPS = 130 # AC Properties
KEY_UNDO = 131 # AC Undo
KEY_FRONT = 132
KEY_COPY = 133 # AC Copy
KEY_OPEN = 134 # AC Open
KEY_PASTE = 135 # AC Paste
KEY_FIND = 136 # AC Search
KEY_CUT = 137 # AC Cut
KEY_HELP = 138 # AL Integrated Help Center
KEY_MENU = 139 # Menu (show menu)
KEY_CALC = 140 # AL Calculator
KEY_SETUP = 141
KEY_SLEEP = 142 # SC System Sleep
KEY_WAKEUP = 143 # System Wake Up
KEY_FILE = 144 # AL Local Machine Browser
KEY_SENDFILE = 145
KEY_DELETEFILE = 146
KEY_XFER = 147
KEY_PROG1 = 148
KEY_PROG2 = 149
KEY_WWW = 150 # AL Internet Browser
KEY_MSDOS = 151
KEY_COFFEE = 152 # AL Terminal Lock/Screensaver
KEY_SCREENLOCK = KEY_COFFEE
KEY_ROTATE_DISPLAY = 153 # Display orientation for e.g. tablets
KEY_DIRECTION = KEY_ROTATE_DISPLAY
KEY_CYCLEWINDOWS = 154
KEY_MAIL = 155
KEY_BOOKMARKS = 156 # AC Bookmarks
KEY_COMPUTER = 157
KEY_BACK = 158 # AC Back
KEY_FORWARD = 159 # AC Forward
KEY_CLOSECD = 160
KEY_EJECTCD = 161
KEY_EJECTCLOSECD = 162
KEY_NEXTSONG = 163
KEY_PLAYPAUSE = 164
KEY_PREVIOUSSONG = 165
KEY_STOPCD = 166
KEY_RECORD = 167
KEY_REWIND = 168
KEY_PHONE = 169 # Media Select Telephone
KEY_ISO = 170
KEY_CONFIG = 171 # AL Consumer Control Configuration
KEY_HOMEPAGE = 172 # AC Home
KEY_REFRESH = 173 # AC Refresh
KEY_EXIT = 174 # AC Exit
KEY_MOVE = 175
KEY_EDIT = 176
KEY_SCROLLUP = 177
KEY_SCROLLDOWN = 178
KEY_KPLEFTPAREN = 179
KEY_KPRIGHTPAREN = 180
KEY_NEW = 181 # AC New
KEY_REDO = 182 # AC Redo/Repeat

KEY_F13 = 183
KEY_F14 = 184
KEY_F15 = 185
KEY_F16 = 186
KEY_F17 = 187
KEY_F18 = 188
KEY_F19 = 189
KEY_F20 = 190
KEY_F21 = 191
KEY_F22 = 192
KEY_F23 = 193
KEY_F24 = 194

KEY_PLAYCD = 200
KEY_PAUSECD = 201
KEY_PROG3 = 202
KEY_PROG4 = 203
KEY_DASHBOARD = 204 # AL Dashboard
KEY_SUSPEND = 205
KEY_CLOSE = 206 # AC Close
KEY_PLAY = 207
KEY_FASTFORWARD = 208
KEY_BASSBOOST = 209
KEY_PRINT = 210 # AC Print
KEY_HP = 211
KEY_CAMERA = 212
KEY_SOUND = 213
KEY_QUESTION = 214
KEY_EMAIL = 215
KEY_CHAT = 216
KEY_SEARCH = 217
KEY_CONNECT = 218
KEY_FINANCE = 219 # AL Checkbook/Finance
KEY_SPORT = 220
KEY_SHOP = 221
KEY_ALTERASE = 222
KEY_CANCEL = 223 # AC Cancel
KEY_BRIGHTNESSDOWN = 224
KEY_BRIGHTNESSUP = 225
KEY_MEDIA = 226

KEY_SWITCHVIDEOMODE = 227 # Cycle between available video outputs (Monitor/LCD/TV-out/etc)
KEY_KBDILLUMTOGGLE = 228
KEY_KBDILLUMDOWN = 229
KEY_KBDILLUMUP = 230

KEY_SEND = 231 # AC Send
KEY_REPLY = 232 # AC Reply
KEY_FORWARDMAIL = 233 # AC Forward Msg
KEY_SAVE = 234 # AC Save
KEY_DOCUMENTS = 235

KEY_BATTERY = 236

KEY_BLUETOOTH = 237
KEY_WLAN = 238
KEY_UWB = 239

KEY_UNKNOWN = 240

KEY_VIDEO_NEXT = 241 # drive next video source
KEY_VIDEO_PREV = 242 # drive previous video source
KEY_BRIGHTNESS_CYCLE = 243 # brightness up, after max is min
KEY_BRIGHTNESS_AUTO = 244 # Set Auto Brightness: manual, brightness control is off, rely on ambient
KEY_BRIGHTNESS_ZERO = KEY_BRIGHTNESS_AUTO
KEY_DISPLAY_OFF = 245 # display device to off state

KEY_WWAN = 246 # Wireless WAN (LTE, UMTS, GSM, etc.)
KEY_WIMAX = KEY_WWAN
KEY_RFKILL = 247 # Key that controls all radios

KEY_MICMUTE = 248 # Mute / unmute the microphone


class TouchpadHack:
   def __init__(self):
       self.__disabled = False
       self.__condition = threading.Condition()
       self.__disabled_until_time = time.monotonic()
       self.__running = True
       
       self.__disable_timeout = 0.5
       self.__timeout_thread = threading.Thread(target=self.timeoutThread, daemon=True)

   def findXInputDeviceNumber(self):
       res = subprocess.run(["/usr/bin/xinput", "list"], stdout=subprocess.PIPE).stdout
       for line in res.split(b"\n"):
           if b"pointer" in line and b"HAILUCK" in line:
               line = line[line.find(b"id=")+3:]
               line = line[:line.find(b"\t")]
               return int(line)
       return None
   
   def timeoutThread(self):
       device_number = self.findXInputDeviceNumber()
       while self.__running:
           with self.__condition:
               self.__condition.wait()
           os.system("xinput disable %d" % (device_number))
           print("disable")
           while time.monotonic() < self.__disabled_until_time and self.__running:
               time.sleep(self.__disabled_until_time - time.monotonic())
           os.system("xinput enable %d" % (device_number))
           print("enable")

   def inputReadThread(self):
       self.__timeout_thread.start()

       # Search for the first Pinebook keyboard entry. The first seems to be the keyboard, the 2nd the mouse.
       for event_path in sorted(os.listdir("/dev/input")):
           if not event_path.startswith("event"):
               continue
           event_path = os.path.join("/dev/input", event_path)
           f = open(event_path, "rb")
           input_device_name = bytearray(512)
           fcntl.ioctl(f, EVIOCGNAME, input_device_name, True)
           input_device_name = input_device_name[:input_device_name.find(b"\0")].decode("utf-8")
           if input_device_name == "HAILUCK CO.,LTD USB KEYBOARD":
               break
           f.close()

       try:
           while True:
               # We read events here from the keyboard and then just ignore them.
               # The only thing we care about is that an event happens.
               event = f.read(struct.calcsize(EVENT_FMT))
               tv_sec, tv_usec, etype, code, value = struct.unpack(EVENT_FMT, event)
               if etype == EV_KEY and self.isDisableKey(code):
                   self.initiateOrUpdateDisable()
       except KeyboardInterrupt:
           self.__running = False
           print("Shutting down")
       with self.__condition:
           self.__condition.notify()
       self.__timeout_thread.join()

   def isDisableKey(self, code):
       # Don't react to modifier keys
       if code == KEY_LEFTALT or code == KEY_RIGHTALT:
           return False
       if code == KEY_LEFTCTRL or code == KEY_RIGHTCTRL:
           return False
       if code == KEY_LEFTSHIFT or code == KEY_RIGHTSHIFT:
           return False
       if code == KEY_LEFTMETA or code == KEY_RIGHTMETA:
           return False
       return True

   def initiateOrUpdateDisable(self):
       with self.__condition:
           self.__condition.notify()
           self.__disabled_until_time = time.monotonic() + self.__disable_timeout

TouchpadHack().inputReadThread()
