1#!/usr/bin/env python3

2# Copyright 2018 Canonical Ltd.

3# Written by:

4# Maciej Kisielewski <maciej.kisielewski@canonical.com>

5

6"""

7 Run `xinput test-xi2` and monitor it for multifinger tap on a touchscreen.

8"""

9

10 import re

11 import subprocess

12 import sys

13

14 EVENT_HEADER_RE = re.compile(r"^EVENT type (\d+) \((\w+)\)")

15

16

17 class Xi2Parser:

18 def __init__(self):

19 self._current_payload = ""

20 self._current_event = ()

21 self._callbacks = dict()

22

23 def register_callback(self, event_type, fn):

24 self._callbacks[event_type] = fn

25

26 def parse_line(self, line):

27 if line.startswith(" "):

28 self._current_payload += line

29 else:

30 matches = EVENT_HEADER_RE.match(line)

31 if not matches:

32 return

33 if self._current_event:

34 self._emit_event()

35 self._current_event = matches.groups()

36

37 def eof(self):

38 if self._current_event:

39 self._emit_event()

40

41 def _emit_event(self):

42 event_data = dict()

43 for line in self._current_payload.split("\n"):

44 if ":" in line:

45 k, v = line.strip().split(":", 1)

46 event_data[k] = v

47 cb = self._callbacks.get(self._current_event[1])

48 if cb:

49 cb(event_data)

50 self._current_payload = ""

51 self._current_event = ()

52

53

54 def main():

55 if len(sys.argv) != 2 or not sys.argv[1].isnumeric():

56 raise SystemExit("Usage {} FINGER_COUNT".format(sys.argv[0]))

57 print("Waiting for {}-finger tap".format(sys.argv[1]))

58

59 parser = Xi2Parser()

60 fingers = 0

61

62 def begin(ev):

63 nonlocal fingers

64 fingers += 1

65 if fingers == int(sys.argv[1]):

66 print("SUCCESS! {}-finger tap detected!".format(sys.argv[1]))

67 raise SystemExit(0)

68

69 def end(ev):

70 nonlocal fingers

71 fingers -= 1

72 if fingers < 0:

73 # it may happen if the program started with finger already touching

74 fingers = 0

75

76 parser.register_callback("TouchBegin", begin)

77 parser.register_callback("TouchEnd", end)

78 proc = subprocess.Popen(

79 ["xinput", "test-xi2", "--root"], stdout=subprocess.PIPE

80 )

81 while not proc.stdout.closed:

82 line = proc.stdout.readline().decode(sys.stdout.encoding)

83 if not line:

84 break

85 parser.parse_line(line)

86 parser.eof()

87

88

89 if __name__ == "__main__":

90 main()