1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Keyboard device module to capture keyboard events."""
6
7import fcntl
8import os
9import re
10import sys
11
12sys.path.append('../../bin/input')
13import input_device
14
15import mtb
16
17from linux_input import EV_KEY
18
19
20class KeyboardDevice:
21    """A class about keyboard device properties."""
22    TYPE = '(keyboard|chromeos-ec-i2c|cros-ec-spi|cros-ec-i2c|cros_ec)'
23
24    def __init__(self, device_node=None):
25        self._device_info_file = '/proc/bus/input/devices'
26        self.device_node = (device_node if device_node
27                                        else self.get_device_node(self.TYPE))
28        self.system_device = self._non_blocking_open(self.device_node)
29        self._input_event = input_device.InputEvent()
30
31    def __del__(self):
32        self.system_device.close()
33
34    def get_device_node(self, device_type):
35        """Get the keyboard device node through device info file.
36
37        Example of the keyboard device information looks like
38
39        I: Bus=0011 Vendor=0001 Product=0001 Version=ab41
40        N: Name="AT Translated Set 2 keyboard"
41        P: Phys=isa0060/serio0/input0
42        S: Sysfs=/devices/platform/i8042/serio0/input/input5
43        U: Uniq=
44        H: Handlers=sysrq kbd event5
45        """
46        device_node = None
47        device_found = None
48        device_pattern = re.compile('N: Name=.*%s' % device_type, re.I)
49        event_number_pattern = re.compile('H: Handlers=.*event(\d?)', re.I)
50        with open(self._device_info_file) as info:
51            for line in info:
52                if device_found:
53                    result = event_number_pattern.search(line)
54                    if result:
55                        event_number = int(result.group(1))
56                        device_node = '/dev/input/event%d' % event_number
57                        break
58                else:
59                    device_found = device_pattern.search(line)
60        return device_node
61
62    def exists(self):
63        """Indicate whether this device exists or not."""
64        return bool(self.device_node)
65
66    def _non_blocking_open(self, filename):
67        """Open the system file in the non-blocking mode."""
68        fd = open(filename)
69        fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK)
70        return fd
71
72    def _non_blocking_read(self, fd):
73        """Non-blocking read on fd."""
74        try:
75            self._input_event.read(fd)
76            return self._input_event
77        except Exception:
78            return None
79
80    def get_key_press_event(self, fd):
81        """Read the keyboard device node to get the key press events."""
82        event = True
83        # Read the device node continuously until either a key press event
84        # is got or there is no more events to read.
85        while event:
86            event = self._non_blocking_read(fd)
87            if event and event.type == EV_KEY and event.value == 1:
88                return event.code
89        return None
90