#!/usr/bin/python
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Explain device capabilities from /proc/bus/input/devices.

This tool processes the contents of /proc/bus/input/devices, expanding bitfields
of event codes into lists of names for those events.
"""

import argparse
import evdev
import sys


def parse_bitfield(bitfield, word_bits):
  """Parse a serialized bitfield from /proc/bus/input/devices.

  Returns a list of bits in the set.

  Example: parse_bitfield('10 3', 64) == [0, 1, 68]
  """
  groups = bitfield.split(' ')
  group_count = len(groups)
  result_bits = []

  for group_index in xrange(group_count):
    group_val = int(groups[group_count - 1 - group_index], 16)

    for group_bit in xrange(word_bits):
      if group_val & (1 << group_bit):
        result_bits.append(group_index * word_bits + group_bit)

  return result_bits


PROP_NAMES = {
    0x0: 'INPUT_PROP_POINTER',
    0x1: 'INPUT_PROP_DIRECT',
    0x2: 'INPUT_PROP_BUTTONPAD',
    0x3: 'INPUT_PROP_SEMI_MT',
    0x4: 'INPUT_PROP_TOPBUTTONPAD',
}


KEY_NAMES = evdev.ecodes.bytype[evdev.ecodes.EV_KEY].copy()

# Fix keys with multiple names.
KEY_NAMES[evdev.ecodes.KEY_MUTE] = 'KEY_MUTE'
KEY_NAMES[evdev.ecodes.KEY_SCREENLOCK] = 'KEY_SCREENLOCK'
KEY_NAMES[evdev.ecodes.KEY_HANGEUL] = 'KEY_HANGEUL'
KEY_NAMES[evdev.ecodes.BTN_LEFT] = 'BTN_LEFT'
KEY_NAMES[evdev.ecodes.BTN_TRIGGER] = 'BTN_TRIGGER'
KEY_NAMES[evdev.ecodes.BTN_0] = 'BTN_0'
KEY_NAMES[evdev.ecodes.BTN_TOOL_PEN] = 'BTN_TOOL_PEN'


BITFIELDS = [
  ('B: EV=', evdev.ecodes.EV),
  ('B: PROP=', PROP_NAMES),
  ('B: KEY=', KEY_NAMES),
  ('B: REL=', evdev.ecodes.bytype[evdev.ecodes.EV_REL]),
  ('B: MSC=', evdev.ecodes.bytype[evdev.ecodes.EV_MSC]),
  ('B: LED=', evdev.ecodes.bytype[evdev.ecodes.EV_LED]),
  ('B: ABS=', evdev.ecodes.bytype[evdev.ecodes.EV_ABS]),
  ('B: SW=', evdev.ecodes.bytype[evdev.ecodes.EV_SW]),
  ('B: REP=', evdev.ecodes.bytype[evdev.ecodes.EV_REP]),
  ('B: SND=', evdev.ecodes.bytype[evdev.ecodes.EV_SND]),
]


def explain_bitfield(serialized_bits, bit_names, word_size, output_file):
  """Annotate a bitfield using the provided symbolic names for each bit."""

  bits = parse_bitfield(serialized_bits, word_size)
  for bit in bits:
    if bit in bit_names:
      output_file.write('    %s\n' % bit_names[bit])
    else:
      output_file.write('    0x%x\n' % bit)


def explain(input_file, output_file, word_size):
  """Annotate an input file formatted like /proc/bus/input/devices."""

  for line in input_file:
    output_file.write(line)
    for prefix, bit_names in BITFIELDS:
      if line.startswith(prefix):
        explain_bitfield(line[len(prefix):], bit_names, word_size, output_file)


def main(argv):
  parser = argparse.ArgumentParser()
  parser.add_argument('input_file', nargs='?',
      default='/proc/bus/input/devices',
      help="filename to read input from")
  parser.add_argument('output_file', nargs='?',
      help="filename to write output to")
  parser.add_argument('--word_size', type=int, default=64,
      help="word size of the system that generated the input file")
  args = parser.parse_args(argv)

  input_file = sys.stdin
  if args.input_file:
    input_file = open(args.input_file, 'r')

  output_file = sys.stdout
  if args.output_file:
    output_file = open(args.output_file, 'w')

  explain(input_file, output_file, args.word_size)

  return 0


if __name__ == '__main__':
  sys.exit(main(sys.argv[1:]))
