]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - hwdb/parse_hwdb.py
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / hwdb / parse_hwdb.py
index e163edbc51d11f0627effd5d01885d29b63ac364..d84fba2221ade7a8ac3049a16a9224636ed18dca 100755 (executable)
@@ -1,10 +1,7 @@
-#!/usr/bin/python3
-# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
 #
-# This file is part of systemd. It is distrubuted under the MIT license, see
-# below.
-#
-# Copyright 2016 Zbigniew Jędrzejewski-Szmek
+# This file is distributed under the MIT license, see below.
 #
 # The MIT License (MIT)
 #
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
-import functools
 import glob
 import string
 import sys
 import os
 
 try:
-    from pyparsing import (Word, White, Literal, ParserElement, Regex,
-                           LineStart, LineEnd,
-                           ZeroOrMore, OneOrMore, Combine, Or, Optional, Suppress, Group,
+    from pyparsing import (Word, White, Literal, ParserElement, Regex, LineEnd,
+                           OneOrMore, Combine, Or, Optional, Suppress, Group,
                            nums, alphanums, printables,
-                           stringEnd, pythonStyleComment,
+                           stringEnd, pythonStyleComment, QuotedString,
                            ParseBaseException)
 except ImportError:
     print('pyparsing is not available')
@@ -49,20 +44,31 @@ except ImportError:
     ecodes = None
     print('WARNING: evdev is not available')
 
+try:
+    from functools import lru_cache
+except ImportError:
+    # don't do caching on old python
+    lru_cache = lambda: (lambda f: f)
+
 EOL = LineEnd().suppress()
-EMPTYLINE = LineStart() + LineEnd()
+EMPTYLINE = LineEnd()
 COMMENTLINE = pythonStyleComment + EOL
 INTEGER = Word(nums)
+STRING =  QuotedString('"')
 REAL = Combine((INTEGER + Optional('.' + Optional(INTEGER))) ^ ('.' + INTEGER))
+SIGNED_REAL = Combine(Optional(Word('-+')) + REAL)
 UDEV_TAG = Word(string.ascii_uppercase, alphanums + '_')
 
 TYPES = {'mouse':    ('usb', 'bluetooth', 'ps2', '*'),
          'evdev':    ('name', 'atkbd', 'input'),
+         'id-input': ('modalias'),
          'touchpad': ('i8042', 'rmi', 'bluetooth', 'usb'),
+         'joystick': ('i8042', 'rmi', 'bluetooth', 'usb'),
          'keyboard': ('name', ),
-         }
+         'sensor':   ('modalias', ),
+        }
 
-@functools.lru_cache()
+@lru_cache()
 def hwdb_grammar():
     ParserElement.setDefaultWhitespaceChars('')
 
@@ -70,44 +76,69 @@ def hwdb_grammar():
                 for category, conn in TYPES.items())
     matchline = Combine(prefix + Word(printables + ' ' + '®')) + EOL
     propertyline = (White(' ', exact=1).suppress() +
-                    Combine(UDEV_TAG - '=' - Word(alphanums + '_=:@*.! ') - Optional(pythonStyleComment)) +
+                    Combine(UDEV_TAG - '=' - Word(alphanums + '_=:@*.!-;, "') - Optional(pythonStyleComment)) +
                     EOL)
     propertycomment = White(' ', exact=1) + pythonStyleComment + EOL
 
     group = (OneOrMore(matchline('MATCHES*') ^ COMMENTLINE.suppress()) -
              OneOrMore(propertyline('PROPERTIES*') ^ propertycomment.suppress()) -
-             (EMPTYLINE ^ stringEnd()).suppress() )
+             (EMPTYLINE ^ stringEnd()).suppress())
     commentgroup = OneOrMore(COMMENTLINE).suppress() - EMPTYLINE.suppress()
 
     grammar = OneOrMore(group('GROUPS*') ^ commentgroup) + stringEnd()
 
     return grammar
 
-@functools.lru_cache()
+@lru_cache()
 def property_grammar():
     ParserElement.setDefaultWhitespaceChars(' ')
 
-    setting = Optional('*')('DEFAULT') + INTEGER('DPI') + Suppress('@') + INTEGER('HZ')
-    props = (('MOUSE_DPI', Group(OneOrMore(setting('SETTINGS*')))),
+    dpi_setting = (Optional('*')('DEFAULT') + INTEGER('DPI') + Suppress('@') + INTEGER('HZ'))('SETTINGS*')
+    mount_matrix_row = SIGNED_REAL + ',' + SIGNED_REAL + ',' + SIGNED_REAL
+    mount_matrix = (mount_matrix_row + ';' + mount_matrix_row + ';' + mount_matrix_row)('MOUNT_MATRIX')
+
+    props = (('MOUSE_DPI', Group(OneOrMore(dpi_setting))),
              ('MOUSE_WHEEL_CLICK_ANGLE', INTEGER),
              ('MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL', INTEGER),
+             ('MOUSE_WHEEL_CLICK_COUNT', INTEGER),
+             ('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', INTEGER),
+             ('ID_INPUT', Literal('1')),
+             ('ID_INPUT_ACCELEROMETER', Literal('1')),
+             ('ID_INPUT_JOYSTICK', Literal('1')),
+             ('ID_INPUT_KEY', Literal('1')),
+             ('ID_INPUT_KEYBOARD', Literal('1')),
+             ('ID_INPUT_MOUSE', Literal('1')),
+             ('ID_INPUT_POINTINGSTICK', Literal('1')),
+             ('ID_INPUT_SWITCH', Literal('1')),
+             ('ID_INPUT_TABLET', Literal('1')),
+             ('ID_INPUT_TABLET_PAD', Literal('1')),
+             ('ID_INPUT_TOUCHPAD', Literal('1')),
+             ('ID_INPUT_TOUCHSCREEN', Literal('1')),
              ('ID_INPUT_TRACKBALL', Literal('1')),
+             ('MOUSE_WHEEL_TILT_HORIZONTAL', Literal('1')),
+             ('MOUSE_WHEEL_TILT_VERTICAL', Literal('1')),
              ('POINTINGSTICK_SENSITIVITY', INTEGER),
              ('POINTINGSTICK_CONST_ACCEL', REAL),
+             ('ID_INPUT_JOYSTICK_INTEGRATION', Or(('internal', 'external'))),
              ('ID_INPUT_TOUCHPAD_INTEGRATION', Or(('internal', 'external'))),
-    )
+             ('XKB_FIXED_LAYOUT', STRING),
+             ('XKB_FIXED_VARIANT', STRING),
+             ('KEYBOARD_LED_NUMLOCK', Literal('0')),
+             ('KEYBOARD_LED_CAPSLOCK', Literal('0')),
+             ('ACCEL_MOUNT_MATRIX', mount_matrix),
+            )
     fixed_props = [Literal(name)('NAME') - Suppress('=') - val('VALUE')
                    for name, val in props]
     kbd_props = [Regex(r'KEYBOARD_KEY_[0-9a-f]+')('NAME')
                  - Suppress('=') -
                  ('!' ^ (Optional('!') - Word(alphanums + '_')))('VALUE')
-                 ]
+                ]
     abs_props = [Regex(r'EVDEV_ABS_[0-9a-f]{2}')('NAME')
                  - Suppress('=') -
                  Word(nums + ':')('VALUE')
-                 ]
+                ]
 
-    grammar = Or(fixed_props + kbd_props + abs_props)
+    grammar = Or(fixed_props + kbd_props + abs_props) + EOL
 
     return grammar
 
@@ -125,7 +156,8 @@ def convert_properties(group):
 def parse(fname):
     grammar = hwdb_grammar()
     try:
-        parsed = grammar.parseFile(fname)
+        with open(fname, 'r', encoding='UTF-8') as f:
+            parsed = grammar.parseFile(f)
     except ParseBaseException as e:
         error('Cannot parse {}: {}', fname, e)
         return []
@@ -145,11 +177,27 @@ def check_one_default(prop, settings):
     if len(defaults) > 1:
         error('More than one star entry: {!r}', prop)
 
+def check_one_mount_matrix(prop, value):
+    numbers = [s for s in value if s not in {';', ','}]
+    if len(numbers) != 9:
+        error('Wrong accel matrix: {!r}', prop)
+    try:
+        numbers = [abs(float(number)) for number in numbers]
+    except ValueError:
+        error('Wrong accel matrix: {!r}', prop)
+    bad_x, bad_y, bad_z = max(numbers[0:3]) == 0, max(numbers[3:6]) == 0, max(numbers[6:9]) == 0
+    if bad_x or bad_y or bad_z:
+        error('Mount matrix is all zero in {} row: {!r}',
+              'x' if bad_x else ('y' if bad_y else 'z'),
+              prop)
+
 def check_one_keycode(prop, value):
     if value != '!' and ecodes is not None:
         key = 'KEY_' + value.upper()
         if key not in ecodes:
-            error('Keycode {} unknown', key)
+            key = value.upper()
+            if key not in ecodes:
+                error('Keycode {} unknown', key)
 
 def check_properties(groups):
     grammar = property_grammar()
@@ -169,6 +217,8 @@ def check_properties(groups):
             prop_names.add(parsed.NAME)
             if parsed.NAME == 'MOUSE_DPI':
                 check_one_default(prop, parsed.VALUE.SETTINGS)
+            elif parsed.NAME == 'ACCEL_MOUNT_MATRIX':
+                check_one_mount_matrix(prop, parsed.VALUE)
             elif parsed.NAME.startswith('KEYBOARD_KEY_'):
                 check_one_keycode(prop, parsed.VALUE)
 
@@ -177,8 +227,7 @@ def print_summary(fname, groups):
           .format(fname,
                   len(groups),
                   sum(len(matches) for matches, props in groups),
-                  sum(len(props) for matches, props in groups),
-          ))
+                  sum(len(props) for matches, props in groups)))
 
 if __name__ == '__main__':
     args = sys.argv[1:] or glob.glob(os.path.dirname(sys.argv[0]) + '/[67]0-*.hwdb')