Merge branch 'wip/config-files'

This commit is contained in:
Peter Hutterer 2018-06-11 13:45:50 +10:00
commit 000ac14c27
56 changed files with 4413 additions and 305 deletions

View file

@ -0,0 +1,4 @@
[Serial Keyboards]
MatchUdevType=keyboard
MatchBus=ps2
AttrKeyboardIntegration=internal

View file

@ -0,0 +1,9 @@
[Lid Switch Ct9]
MatchName=*Lid Switch*
MatchDMIModalias=dmi:*:ct9:*
AttrLidSwitchReliability=reliable
[Lid Switch Ct10]
MatchName=*Lid Switch*
MatchDMIModalias=dmi:*:ct10:*
AttrLidSwitchReliability=reliable

View file

@ -0,0 +1,3 @@
[Trackball]
MatchName=*Trackball*
ModelTrackball=1

View file

@ -0,0 +1,5 @@
[Aiptek No Tilt Tablet]
MatchUdevType=tablet
MatchBus=usb
MatchVendor=0x08CA
ModelTabletNoTilt=1

View file

@ -0,0 +1,9 @@
[AlpsTouchpadDualPoint]
MatchUdevType=touchpad
MatchName=*AlpsPS/2 ALPS DualPoint TouchPad
ModelALPSTouchpad=1
[AlpsTouchpadGlidePoint]
MatchUdevType=touchpad
MatchName=*AlpsPS/2 ALPS GlidePoint
ModelALPSTouchpad=1

View file

@ -0,0 +1,3 @@
[Cyapa Touchpads]
MatchName=*Cypress APA Trackpad ?cyapa?
AttrPressureRange=10:8

View file

@ -0,0 +1,4 @@
[Elantech Touchpads]
MatchName=*Elantech Touchpad*
AttrResolutionHint=31x31
AttrPressureRange=10:8

View file

@ -0,0 +1,12 @@
# HUION PenTablet device. Some of these devices send a BTN_TOOL_PEN event
# with value 1 on the first event received by the device but never send the
# matching BTN_TOOL_PEN value 0 event. The device appears as if it was
# permanently in proximity.
#
# HUION re-uses USB IDs for its devices, not every HUION tablet is
# affected by this bug, libinput will auto-disable this feature
[HUION PenTablet]
MatchUdevType=tablet
MatchBus=usb
MatchVendor=0x256C
ModelTabletNoProximityOut=1

39
data/30-vendor-ibm.quirks Normal file
View file

@ -0,0 +1,39 @@
# IBM/Lenovo Scrollpoint mouse. Instead of a scroll wheel these mice
# feature trackpoint-like sticks which generate a huge amount of scroll
# events that need to be handled differently than scroll wheel events
[IBM ScrollPoint Mouse 3100]
MatchUdevType=mouse
MatchVendor=0x04B3
MatchProduct=0x3100
ModelLenovoScrollPoint=1
[IBM ScrollPoint Mouse 3103]
MatchUdevType=mouse
MatchVendor=0x04B3
MatchProduct=0x3103
ModelLenovoScrollPoint=1
[IBM ScrollPoint Mouse 3105]
MatchUdevType=mouse
MatchVendor=0x04B3
MatchProduct=0x3105
ModelLenovoScrollPoint=1
[IBM ScrollPoint Mouse 3108]
MatchUdevType=mouse
MatchVendor=0x04B3
MatchProduct=0x3108
ModelLenovoScrollPoint=1
[IBM ScrollPoint Mouse 3109]
MatchUdevType=mouse
MatchVendor=0x04B3
MatchProduct=0x3109
ModelLenovoScrollPoint=1
[IBM ScrollPoint Mouse 6049]
MatchUdevType=mouse
MatchVendor=0x17EF
MatchProduct=0x6049
ModelLenovoScrollPoint=1

View file

@ -0,0 +1,44 @@
[Logitech M570]
MatchName=*Logitech M570*
ModelTrackball=1
[Logitech Marble Mouse Trackball]
MatchUdevType=mouse
MatchBus=usb
MatchVendor=0x46D
MatchProduct=0xC408
ModelLogitechMarbleMouse=1
[Logitech K400]
MatchUdevType=mouse
MatchBus=usb
MatchVendor=0x046D
MatchProduct=0x4024
ModelBouncingKeys=1
[Logitech K400r]
MatchUdevType=mouse
MatchBus=usb
MatchVendor=0x046D
MatchProduct=0x404B
ModelBouncingKeys=1
[Logitech K830]
MatchUdevType=mouse
MatchBus=usb
MatchVendor=0x046D
MatchProduct=0x404C
ModelBouncingKeys=1
[Logitech K400Plus]
MatchUdevType=mouse
MatchBus=usb
MatchVendor=0x046D
MatchProduct=0x404D
ModelBouncingKeys=1
[Logitech Wireless Touchpad]
MatchBus=usb
MatchVendor=0x046D
MatchProduct=0x4011
AttrPalmPressureThreshold=400

View file

@ -0,0 +1,16 @@
[Microsoft Surface 3 Lid Switch]
MatchName=*Lid Switch*
MatchDMIModalias=dmi:*svnMicrosoftCorporation:pnSurface3:*
AttrLidSwitchReliability=write_open
[Microsoft Surface 3 Type Cover Keyboard]
MatchName=*Microsoft Surface Type Cover Keyboard*
MatchDMIModalias=dmi:*svnMicrosoftCorporation:pnSurface3:*
AttrKeyboardIntegration=internal
[Microsoft Nano Transceiver v2.0]
MatchUdevType=mouse
MatchBus=usb
MatchVendor=0x045E
MatchProduct=0x8000
ModelBouncingKeys=1

View file

@ -0,0 +1,11 @@
[Razer Blade Keyboard]
MatchUdevType=keyboard
MatchBus=usb
MatchVendor=0x1532
MatchProduct=0x0220
AttrKeyboardIntegration=internal
[Razer Blade Lid Switch]
MatchName=*Lid Switch*
MatchDMIModalias=dmi:*svnRazer:pnBlade*
AttrLidSwitchReliability=write_open

View file

@ -0,0 +1,6 @@
[Synaptics Serial Touchpads]
MatchUdevType=touchpad
MatchBus=ps2
MatchVendor=0x0002
MatchProduct=0x0007
ModelSynapticsSerialTouchpad=1

View file

@ -0,0 +1,12 @@
[Wacom Touchpads]
MatchUdevType=touchpad
MatchBus=usb
MatchVendor=0x056A
ModelWacomTouchpad=1
[Wacom Intuos Pro PTH660]
MatchUdevType=touchpad
MatchBus=usb
MatchVendor=0x056A
MatchProduct=0x0357
AttrPalmSizeThreshold=1

View file

@ -0,0 +1,48 @@
[Apple Touchpads USB]
MatchVendor=0x05AC
MatchBus=usb
MatchUdevType=touchpad
ModelAppleTouchpad=1
AttrSizeHint=104x75
AttrTouchSizeRange=150:130
AttrPalmSizeThreshold=800
[Apple Touchpads Bluetooth]
MatchVendor=0x05AC
MatchBus=bluetooth
MatchUdevType=touchpad
ModelAppleTouchpad=1
[Apple Internal Keyboard]
MatchName=*Apple Inc. Apple Internal Keyboard*
AttrKeyboardIntegration=internal
[Apple MagicMouse]
MatchUdevType=mouse
MatchBus=bluetooth
MatchVendor=0x05AC
MatchProduct=0x030D
ModelAppleMagicMouse=1
[Apple Magic Trackpad v1 (2010, clickpad)]
MatchUdevType=touchpad
MatchBus=bluetooth
MatchVendor=0x5AC
MatchProduct=0x030E
AttrSizeHint=130x110
AttrTouchSizeRange=20:10
AttrPalmSizeThreshold=900
[Apple Touchpad OneButton]
MatchUdevType=touchpad
MatchBus=usb
MatchVendor=0x5AC
MatchProduct=0x021A
ModelAppleTouchpadOneButton=1
[Apple Touchpad MacbookPro5,5]
MatchUdevType=touchpad
MatchBus=usb
MatchVendor=0x05AC
MatchProduct=0x0237
AttrPalmSizeThreshold=1000

View file

@ -0,0 +1,9 @@
[Asus X555LAB]
MatchName=*ETPS/2 Elantech Touchpad*
MatchDMIModalias=dmi:*svnASUSTeKCOMPUTERINC.:pnX555LAB:*
ModelTouchpadVisibleMarker=1
[Asus UX21E]
MatchName=*ETPS/2 Elantech Touchpad*
MatchDMIModalias=dmi:*svnASUSTeKComputerInc.:pnUX21E:*
AttrPressureRange=24:10

View file

@ -0,0 +1,7 @@
# Acer Hawaii Keyboard, uses Chicony VID
[Acer Hawaii Keyboard]
MatchUdevType=touchpad
MatchBus=usb
MatchVendor=0x4F2
MatchProduct=0x1558
AttrTPKComboLayout=below

View file

@ -0,0 +1,6 @@
[Saitek Cyborg RAT5]
MatchUdevType=mouse
MatchBus=usb
MatchVendor=0x06A3
MatchProduct=0x0CD5
ModelCyborgRat=1

View file

@ -0,0 +1,15 @@
[Dell Touchpads]
MatchName=* Touchpad
MatchDMIModalias=dmi:*svnDellInc.:*
ModelTouchpadVisibleMarker=1
[Dell Lattitude E6220]
MatchName=*AlpsPS/2 ALPS GlidePoint
MatchDMIModalias=dmi:*svnDellInc.:pnLatitudeE6220:*
AttrPressureRange=100:90
[Dell XPS L322X]
MatchName=*CyPS/2 Cypress Trackpad
MatchDMIModalias=dmi:*svnDell*:XPSL322X*
AttrPressureRange=32:20
AttrPalmPressureThreshold=254

View file

@ -0,0 +1,86 @@
[Google Chromebook R13 CB5-312T]
MatchName=*Elan Touchpad*
MatchDeviceTree=*Chromebook R13 CB5-312T*
AttrPressureRange=6:4
[Google Chromebook CB5-312T]
MatchName=*Elan Touchpad*
MatchDeviceTree=*CB5-312T*
AttrPressureRange=6:4
[Google Chromebook Elm]
MatchName=*Elan Touchpad*
MatchDeviceTree=*Elm*
AttrPressureRange=6:4
[Google Chromebook Falco]
MatchUdevType=touchpad
MatchName=Cypress APA Trackpad ?cyapa?
MatchDMIModalias=dmi:*pn*Falco*
ModelChromebook=1
[Google Chromebook Mario]
MatchUdevType=touchpad
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*pn*Mario*
ModelChromebook=1
[Google Chromebook Butterfly]
MatchUdevType=touchpad
MatchName=Cypress APA Trackpad ?cyapa?
MatchDMIModalias=dmi:*pn*Butterfly*
ModelChromebook=1
[Google Chromebook Peppy]
MatchUdevType=touchpad
MatchName=Cypress APA Trackpad ?cyapa?
MatchDMIModalias=dmi:*pn*Peppy*
ModelChromebook=1
[Google Chromebook ZGB]
MatchUdevType=touchpad
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*pn*ZGB*
ModelChromebook=1
[Google Chromebook Parrot]
MatchUdevType=touchpad
MatchName=Cypress APA Trackpad ?cyapa?
MatchDMIModalias=dmi:*pn*Parrot*
ModelChromebook=1
[Google Chromebook Leon]
MatchUdevType=touchpad
MatchName=Cypress APA Trackpad ?cyapa?
MatchDMIModalias=dmi:*bvn*coreboot*:pn*Leon*
ModelChromebook=1
[Google Chromebook Wolf]
MatchUdevType=touchpad
MatchName=Cypress APA Trackpad ?cyapa?
MatchDMIModalias=dmi:*bvn*coreboot*:pn*Wolf*
ModelChromebook=1
[Google Chromebook Link]
MatchUdevType=touchpad
MatchName=Cypress APA Trackpad ?cyapa?
MatchDMIModalias=dmi:*svn*GOOGLE*:pn*Link*
ModelChromebook=1
[Google Chromebook Alex]
MatchUdevType=touchpad
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*pn*Alex*
ModelChromebook=1
[Google Chromebook Lumpy]
MatchUdevType=touchpad
MatchName=Cypress APA Trackpad ?cyapa?
MatchDMIModalias=dmi:*svn*SAMSUNG*:pn*Lumpy*
ModelChromebook=1
[Google Chromebook Samus]
MatchUdevType=touchpad
MatchName=Atmel maXTouch Touchpad
MatchDMIModalias=dmi:*svn*GOOGLE*:pn*Samus*
ModelChromebook=1

24
data/50-system-hp.quirks Normal file
View file

@ -0,0 +1,24 @@
[HP Compaq 6910p]
MatchName=*SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnHewlett-Packard:*pnHPCompaq6910p*
ModelHP6910Touchpad=1
[HP Compaq 8510w]
MatchName=*SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnHewlett-Packard:*pnHPCompaq8510w*
ModelHP8510Touchpad=1
[HP Pavillion dmi4]
MatchName=*SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnHewlett-Packard:*pnHPPaviliondm4NotebookPC*
ModelHPPavilionDM4Touchpad=1
[HP Stream 11]
MatchName=SYN1EDE:00 06CB:7442
MatchDMIModalias=dmi:*svnHewlett-Packard:pnHPStreamNotebookPC11*
ModelHPStream11Touchpad=1
[HP ZBook Studio G3]
MatchName=AlpsPS/2 ALPS GlidePoint
MatchDMIModalias=dmi:*svnHP:pnHPZBookStudioG3:*
ModelHPZBookStudioG3=1

View file

@ -0,0 +1,78 @@
[Lenovo Thinkpad Touchpad]
MatchName=*Synaptics*
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPad*:*
AttrThumbPressureThreshold=100
[Lenovo x230 Touchpad]
MatchName=*SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadX230*
ModelLenovoX230=1
[Lenovo T440p Touchpad PS/2]
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadT440p*
ModelLenovoT450Touchpad=1
[Lenovo T440p Touchpad RMI4]
MatchName=Synaptics tm2964-001
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadT440p*
ModelLenovoT450Touchpad=1
[Lenovo T440s Trackpoint]
MatchName=TPPS/2 IBM TrackPoint
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadT440s*
AttrTrackpointRange=30
[Lenovo T440s Trackpoint]
MatchName=TPPS/2 IBM TrackPoint
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadT450s*
AttrTrackpointRange=50
[Lenovo P50 Touchpad]
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadP50*:
ModelLenovoT450Touchpad=1
AttrPalmPressureThreshold=150
[Lenovo *50 Touchpad]
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPad??50*:
ModelLenovoT450Touchpad=1
AttrPalmPressureThreshold=150
[Lenovo *60 Touchpad]
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPad??60*:
ModelLenovoT450Touchpad=1
AttrPalmPressureThreshold=150
[Lenovo X1 Carbon 3rd Touchpad]
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadX1Carbon3rd:*
ModelLenovoT450Touchpad=1
AttrPalmPressureThreshold=150
[Lenovo ThinkPad Compact USB Keyboard with TrackPoint]
MatchUdevType=keyboard
MatchBus=usb
MatchVendor=0x17EF
MatchProduct=0x6047
AttrKeyboardIntegration=external
[Lenovo X280 Trackpoint]
MatchName=*ALPS TrackPoint*
MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadX280:*
AttrTrackpointRange=70
# Lenovo Thinkpad X1 Yoga disables the keyboard anyway but has the same device
# use a windows key on the screen and volume rocker on the side (#103749)
[Lenovo Thinkpad X1 Yoga]
MatchName=AT Translated Set 2 keyboard
MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkPadX1Yoga1st:*
ModelTabletModeNoSuspend=1
# Lenovo Carbon X1 6th gen (RMI4 only, PS/2 is broken on this device)
[Lenovo Carbon X1 6th gen]
MatchName=Synaptics TM3288-010
MatchDMIModalias=dmi:*svnLenovo:*pvrThinkPadX1Carbon6th:*
ModelLenovoCarbonX16th=1

View file

@ -0,0 +1,19 @@
[System76 Bonobo Professional]
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnSystem76*pvrbonp5*
ModelSystem76Bonobo=1
[System76 Clevo]
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*pnW740SU*rnW740SU*
ModelClevoW740SU=1
[System76 Galago Ultra Pro]
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnSystem76*pvrgalu1*
ModelSystem76Galago=1
[System76 Kudu Professional]
MatchName=SynPS/2 Synaptics TouchPad
MatchDMIModalias=dmi:*svnSystem76*pvrkudp1*
ModelSystem76Kudu=1

77
data/README.md Normal file
View file

@ -0,0 +1,77 @@
= libinput data file format =
This directory contains hardware quirks used by libinput to work around bugs
in the hardware, device behavior and to supply information not obtained
through the kernel device.
**THIS IS NOT STABLE API**
The data format may change at any time. If your data file is not part of the
libinput git tree, do not expect it to work after an update. Absolutely no
guarantees are made for backwards-compatibility.
**THIS IS NOT A CONFIGURATION API**
Use the `libinput_device_config_foo()` functions for device configuration.
The quirks are hardware quirks only.
== Data file naming ==
Data files are read in versionsort order, read order determines how values
override each other. A values read later override previously values. The
current structure is 10-generic-foo.quirks for generic settings,
30-vendor-foo.quirks for vendor-specific settings and 50-system-foo.quirks
for system vendors. This is not a fixed naming scheme and may change at any
time. It's an approximation only because some vendors are also system
vendors, e.g. Microsoft makes devices and laptops.
Laptop-specific quirks should always go into the laptop vendor's file.
== Sections, matches and values ==
A data file must contain at least one section, each section must have at
least one `Match` tag and at least one of either `Attr` or `Model`. Section
names are free-form and may contain spaces.
```
# This is a comment
[Some touchpad]
MatchBus=usb
# No quotes around strings
MatchName=*Synaptics Touchpad*
AttrSizeHint=50x50
ModelSynapticsTouchpad=1
[Apple touchpad]
MatchVendor=0x5AC
MatchProduct=0x123
ModelAppleTouchpad=1
```
Comments are lines starting with `#`.
All `Model` tags take a value of either `1` or `0`.
All `Attr` tag values are specific to that attribute.
== Parser errors ==
The following will cause parser errors and are considered invalid data
files:
* Whitespace at the beginning of the line
* Sections without at least one `Match*` entry
* Sections with the same `Match*` entry repeated
* Sections without at least one of `Model*` or `Attr` entries
* A `Model` tag with a value other than `1` or `0`
* A string property with enclosing quotes
== Debugging ==
When modifying a data file, use the `libinput list-quirks` tool to
verify the changes. The tool can be pointed at the data directory to
analyse, use `--verbose` to get more info. For example:
```
libinput list-quirks --data-dir /path/to/git/repo/data/ --verbose /dev/input/event0
```

102
doc/device-quirks.dox Normal file
View file

@ -0,0 +1,102 @@
/**
@page device-quirks Device quirks
libinput requires extra information from devices that is not always readily
available. For example, some touchpads are known to have jumping cursors
under specific conditions. libinput ships a set of files containting the
so-called model quirks to provide that information. Model quirks are usually
installed under `/usr/share/libinput/<filename>.quirks` and are standard
`.ini` files. A file may contain multiple section headers (`[some
identifier]`) followed by one or more `MatchFoo=Bar` directives, followed by
at least one of `ModelFoo=1` or `AttrFoo=bar` directive. See the
`data/README.md` file in the libinput source repository for more details on
their contents.
@note Model quirks are internal API and may change at any time. No
backwards-compatibility is guaranteed.
For example, a quirks file may have this content to label all keyboards on
the serial bus (PS/2) as internal keyboards:
@verbatim
[Serial Keyboards]
MatchUdevType=keyboard
MatchBus=serial
AttrKeyboardIntegration=internal
@endverbatim
The model quirks are part of the source distribution and should never be
modified locally. Updates to libinput may overwrite modifications or even
stop parsing any property. For temporary local workarounds, see @ref
device-quirks-local.
Device quirks are parsed on libinput initialization. A parsing error in the
device quirks disables **all** device quirks and may negatively impact
device behavior on the host. If the quirks cannot be loaded, an error
message is posted to the log and users should use the information in @ref
device-quirks-debugging to verify their quirks files.
@section device-quirks-local Installing temporary local device quirks
The model quirks are part of the source distribution and should never be
modified. For temporary local workarounds, libinput reads the
`/etc/libinput/local-overrides.quirks` file. Users may add a sections to
this file to add a device quirk for a local device but beware that **any
modification must be upstreamed** or it may cease to work at any time.
@note Model quirks are internal API and may change at any time. No
backwards-compatibility is guaranteed. Local overrides should only be used
until the distribution updates the libinput packages.
The `local-overrides.quirks` file usually needs to be created by the user.
Once the required section has been added, use the information from section
@ref device-quirks-debugging to validate and test the quirks.
@section device-quirks-debugging Debugging device quirks
libinput provides the `libinput list-quirks` tool to list and debug model
quirks that apply to one or more local devices.
@verbatim
$ libinput list-quirks /dev/input/event19
Device has no quirks defined
$ libinput list-quirks /dev/input/event0
AttrLidSwitchReliability
@endverbatim
When called with the `--verbose` argument, `libinput list-quirks` prints
information about all files and its attempts to match the device:
@verbatim
$ libinput list-quirks --verbose /dev/input/event0
quirks debug: /usr/share/share/libinput is data root
quirks debug: /usr/share/share/libinput/10-generic-keyboard.quirks
quirks debug: /usr/share/share/libinput/10-generic-lid.quirks
[...]
quirks debug: /usr/share/etc/libinput/local-overrides.quirks
quirks debug: /dev/input/event0: fetching quirks
quirks debug: [Serial Keyboards] (10-generic-keyboard.quirks) wants MatchBus but we don't have that
quirks debug: [Lid Switch Ct9] (10-generic-lid.quirks) matches for MatchName
quirks debug: [Lid Switch Ct10] (10-generic-lid.quirks) matches for MatchName
quirks debug: [Lid Switch Ct10] (10-generic-lid.quirks) matches for MatchDMIModalias
quirks debug: [Lid Switch Ct10] (10-generic-lid.quirks) is full match
quirks debug: property added: AttrLidSwitchReliability from [Lid Switch Ct10] (10-generic-lid.quirks)
quirks debug: [Aiptek No Tilt Tablet] (30-vendor-aiptek.quirks) wants MatchBus but we don't have that
[...]
quirks debug: [HUION PenTablet] (30-vendor-huion.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech Marble Mouse Trackball] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech K400] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech K400r] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech K830] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech K400Plus] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech Wireless Touchpad] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Microsoft Surface 3 Lid Switch] (30-vendor-microsoft.quirks) matches for MatchName
[...]
AttrLidSwitchReliability
@endverbatim
Note that this is an example only, the output may change over time. The tool
uses the same parser as libinput and any parsing errors will show up in the
output.
*/

View file

@ -37,6 +37,7 @@
@page general General
- @subpage device-quirks
- @subpage udev_config
- @subpage seats
- @subpage timestamps

View file

@ -181,6 +181,57 @@ libfilter = static_library('filter', src_libfilter,
include_directories : includes_include)
dep_libfilter = declare_dependency(link_with : libfilter)
############ libquirks.a #############
libinput_data_path = join_paths(get_option('prefix'), get_option('datadir'), 'libinput')
libinput_data_override_path = join_paths(get_option('prefix'),
get_option('sysconfdir'),
'libinput',
'local-overrides.quirks')
config_h.set_quoted('LIBINPUT_DATA_DIR', libinput_data_path)
config_h.set_quoted('LIBINPUT_DATA_OVERRIDE_FILE', libinput_data_override_path)
quirks_data = [
'data/10-generic-keyboard.quirks',
'data/10-generic-lid.quirks',
'data/10-generic-trackball.quirks',
'data/30-vendor-aiptek.quirks',
'data/30-vendor-alps.quirks',
'data/30-vendor-cyapa.quirks',
'data/30-vendor-elantech.quirks',
'data/30-vendor-huion.quirks',
'data/30-vendor-ibm.quirks',
'data/30-vendor-logitech.quirks',
'data/30-vendor-microsoft.quirks',
'data/30-vendor-razer.quirks',
'data/30-vendor-synaptics.quirks',
'data/30-vendor-wacom.quirks',
'data/50-system-apple.quirks',
'data/50-system-asus.quirks',
'data/50-system-chicony.quirks',
'data/50-system-cyborg.quirks',
'data/50-system-dell.quirks',
'data/50-system-google.quirks',
'data/50-system-hp.quirks',
'data/50-system-lenovo.quirks',
'data/50-system-system76.quirks',
]
config_h.set_quoted('LIBINPUT_DATA_FILES', ':'.join(quirks_data))
config_h.set_quoted('LIBINPUT_DATA_SRCDIR', join_paths(meson.source_root(), 'data'))
install_data(quirks_data, install_dir : libinput_data_path)
src_libquirks = [
'src/quirks.c',
'src/quirks.h',
]
deps_libquirks = [dep_udev, dep_libinput_util]
libquirks = static_library('quirks', src_libquirks,
dependencies : deps_libquirks,
include_directories : includes_include)
dep_libquirks = declare_dependency(link_with : libquirks)
############ libinput.so ############
install_headers('src/libinput.h')
src_libinput = src_libfilter + [
@ -220,7 +271,8 @@ deps_libinput = [
dep_lm,
dep_rt,
dep_libwacom,
dep_libinput_util
dep_libinput_util,
dep_libquirks
]
libinput_version_h_config = configuration_data()
@ -317,6 +369,7 @@ if get_option('documentation')
meson.source_root() + '/doc/clickpad-softbuttons.dox',
meson.source_root() + '/doc/contributing.dox',
meson.source_root() + '/doc/device-configuration-via-udev.dox',
meson.source_root() + '/doc/device-quirks.dox',
meson.source_root() + '/doc/faqs.dox',
meson.source_root() + '/doc/gestures.dox',
meson.source_root() + '/doc/middle-button-emulation.dox',
@ -435,6 +488,26 @@ configure_file(input : 'tools/libinput-debug-events.man',
install_dir : join_paths(get_option('mandir'), 'man1')
)
libinput_list_quirks_sources = [ 'tools/libinput-list-quirks.c' ]
libinput_list_quirks = executable('libinput-list-quirks',
libinput_list_quirks_sources,
dependencies : [dep_libquirks, dep_libinput],
include_directories : [includes_src, includes_include],
install_dir : libinput_tool_path,
install : true
)
test('validate-quirks',
libinput_list_quirks,
args: ['--validate-only', '--data-dir=@0@'.format(join_paths(meson.source_root(), 'data'))]
)
configure_file(input : 'tools/libinput-list-quirks.man',
output : 'libinput-list-quirks.1',
configuration : man_config,
install : true,
install_dir : join_paths(get_option('mandir'), 'man1')
)
libinput_list_devices_sources = [ 'tools/libinput-list-devices.c' ]
executable('libinput-list-devices',
libinput_list_devices_sources,
@ -697,6 +770,7 @@ if get_option('tests')
dep_dl,
dep_lm,
dep_libsystemd,
dep_libquirks,
]
configure_file(input : 'udev/80-libinput-test-device.rules',
@ -763,7 +837,8 @@ if get_option('tests')
'test/test-keyboard.c',
'test/test-device.c',
'test/test-gestures.c',
'test/test-switch.c'
'test/test-switch.c',
'test/test-quirks.c',
]
def_LT_VERSION = '-DLIBINPUT_LT_VERSION="@0@:@1@:@2@"'.format(libinput_lt_c, libinput_lt_r, libinput_lt_a)
libinput_test_runner = executable('libinput-test-suite-runner',

View file

@ -28,6 +28,7 @@
#include <stdbool.h>
#include <limits.h>
#include "quirks.h"
#include "evdev-mt-touchpad.h"
#define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT ms2us(300)
@ -2841,18 +2842,27 @@ tp_dwt_config_get_default(struct libinput_device *device)
static inline bool
tp_is_tpkb_combo_below(struct evdev_device *device)
{
const char *prop;
struct quirks_context *quirks;
struct quirks *q;
char *prop;
enum tpkbcombo_layout layout = TPKBCOMBO_LAYOUT_UNKNOWN;
int rc = false;
prop = udev_device_get_property_value(device->udev_device,
"LIBINPUT_ATTR_TPKBCOMBO_LAYOUT");
if (!prop)
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
if (!q)
return false;
return parse_tpkbcombo_layout_poperty(prop, &layout) &&
if (quirks_get_string(q, QUIRK_ATTR_TPKBCOMBO_LAYOUT, &prop)) {
rc = parse_tpkbcombo_layout_poperty(prop, &layout) &&
layout == TPKBCOMBO_LAYOUT_BELOW;
}
quirks_unref(q);
return rc;
}
static inline bool
tp_is_tablet(struct evdev_device *device)
{
@ -2917,19 +2927,20 @@ static int
tp_read_palm_pressure_prop(struct tp_dispatch *tp,
const struct evdev_device *device)
{
struct udev_device *udev_device = device->udev_device;
const char *prop;
int threshold;
const int default_palm_threshold = 130;
uint32_t threshold = default_palm_threshold;
struct quirks_context *quirks;
struct quirks *q;
prop = udev_device_get_property_value(udev_device,
"LIBINPUT_ATTR_PALM_PRESSURE_THRESHOLD");
if (!prop)
return default_palm_threshold;
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
if (!q)
return threshold;
threshold = parse_palm_pressure_property(prop);
quirks_get_uint32(q, QUIRK_ATTR_PALM_PRESSURE_THRESHOLD, &threshold);
quirks_unref(q);
return threshold > 0 ? threshold : default_palm_threshold;
return threshold;
}
static inline void
@ -2953,25 +2964,27 @@ static inline void
tp_init_palmdetect_size(struct tp_dispatch *tp,
struct evdev_device *device)
{
const char *prop;
int threshold;
struct quirks_context *quirks;
struct quirks *q;
uint32_t threshold;
prop = udev_device_get_property_value(device->udev_device,
"LIBINPUT_ATTR_PALM_SIZE_THRESHOLD");
if (!prop)
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
if (!q)
return;
threshold = parse_palm_size_property(prop);
if (quirks_get_uint32(q, QUIRK_ATTR_PALM_SIZE_THRESHOLD, &threshold)) {
if (threshold == 0) {
evdev_log_bug_client(device,
"palm: ignoring invalid threshold %s\n",
prop);
return;
}
"palm: ignoring invalid threshold %d\n",
threshold);
} else {
tp->palm.use_size = true;
tp->palm.size_threshold = threshold;
}
}
quirks_unref(q);
}
static inline void
tp_init_palmdetect_arbitration(struct tp_dispatch *tp,
@ -3045,25 +3058,6 @@ tp_init_sendevents(struct tp_dispatch *tp,
tp_keyboard_timeout, tp);
}
static int
tp_read_thumb_pressure_prop(struct tp_dispatch *tp,
const struct evdev_device *device)
{
struct udev_device *udev_device = device->udev_device;
const char *prop;
int threshold;
const int default_thumb_threshold = 0;
prop = udev_device_get_property_value(udev_device,
"LIBINPUT_ATTR_THUMB_PRESSURE_THRESHOLD");
if (!prop)
return default_thumb_threshold;
threshold = parse_thumb_pressure_property(prop);
return threshold > 0 ? threshold : default_thumb_threshold;
}
static void
tp_init_thumb(struct tp_dispatch *tp)
{
@ -3072,7 +3066,9 @@ tp_init_thumb(struct tp_dispatch *tp)
double w = 0.0, h = 0.0;
struct device_coords edges;
struct phys_coords mm = { 0.0, 0.0 };
int threshold;
uint32_t threshold;
struct quirks_context *quirks;
struct quirks *q;
if (!tp->buttons.is_clickpad)
return;
@ -3101,11 +3097,13 @@ tp_init_thumb(struct tp_dispatch *tp)
if (!abs)
goto out;
threshold = tp_read_thumb_pressure_prop(tp, device);
if (threshold == 0)
goto out;
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
if (quirks_get_uint32(q,
QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD,
&threshold))
tp->thumb.threshold = threshold;
quirks_unref(q);
out:
evdev_log_debug(device,
@ -3203,7 +3201,9 @@ tp_init_pressure(struct tp_dispatch *tp,
{
const struct input_absinfo *abs;
unsigned int code;
const char *prop;
struct quirks_context *quirks;
struct quirks *q;
struct quirk_range r;
int hi, lo;
code = tp->has_mt ? ABS_MT_PRESSURE : ABS_PRESSURE;
@ -3215,20 +3215,16 @@ tp_init_pressure(struct tp_dispatch *tp,
abs = libevdev_get_abs_info(device->evdev, code);
assert(abs);
prop = udev_device_get_property_value(device->udev_device,
"LIBINPUT_ATTR_PRESSURE_RANGE");
if (prop) {
if (!parse_range_property(prop, &hi, &lo)) {
evdev_log_bug_client(device,
"discarding invalid pressure range '%s'\n",
prop);
return;
}
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
if (q && quirks_get_range(q, QUIRK_ATTR_PRESSURE_RANGE, &r)) {
hi = r.upper;
lo = r.lower;
if (hi == 0 && lo == 0) {
evdev_log_info(device,
"pressure-based touch detection disabled\n");
return;
goto out;
}
} else {
unsigned int range = abs->maximum - abs->minimum;
@ -3238,12 +3234,13 @@ tp_init_pressure(struct tp_dispatch *tp,
lo = abs->minimum + 0.10 * range;
}
if (hi > abs->maximum || hi < abs->minimum ||
lo > abs->maximum || lo < abs->minimum) {
evdev_log_bug_libinput(device,
"discarding out-of-bounds pressure range %d:%d\n",
hi, lo);
return;
goto out;
}
tp->pressure.use_pressure = true;
@ -3254,14 +3251,19 @@ tp_init_pressure(struct tp_dispatch *tp,
"using pressure-based touch detection (%d:%d)\n",
lo,
hi);
out:
quirks_unref(q);
}
static bool
tp_init_touch_size(struct tp_dispatch *tp,
struct evdev_device *device)
{
const char *prop;
struct quirks_context *quirks;
struct quirks *q;
struct quirk_range r;
int lo, hi;
int rc = false;
if (!libevdev_has_event_code(device->evdev,
EV_ABS,
@ -3269,28 +3271,25 @@ tp_init_touch_size(struct tp_dispatch *tp,
return false;
}
prop = udev_device_get_property_value(device->udev_device,
"LIBINPUT_ATTR_TOUCH_SIZE_RANGE");
if (!prop)
return false;
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
if (q && quirks_get_range(q, QUIRK_ATTR_TOUCH_SIZE_RANGE, &r)) {
hi = r.upper;
lo = r.lower;
} else {
goto out;
}
if (libevdev_get_num_slots(device->evdev) < 5) {
evdev_log_bug_libinput(device,
"Expected 5+ slots for touch size detection\n");
return false;
}
if (!parse_range_property(prop, &hi, &lo)) {
evdev_log_bug_client(device,
"discarding invalid touch size range '%s'\n",
prop);
return false;
goto out;
}
if (hi == 0 && lo == 0) {
evdev_log_info(device,
"touch size based touch detection disabled\n");
return false;
goto out;
}
/* Thresholds apply for both major or minor */
@ -3302,7 +3301,10 @@ tp_init_touch_size(struct tp_dispatch *tp,
"using size-based touch detection (%d:%d)\n",
hi, lo);
return true;
rc = true;
out:
quirks_unref(q);
return rc;
}
static int

View file

@ -43,6 +43,7 @@
#include "evdev.h"
#include "filter.h"
#include "libinput-private.h"
#include "quirks.h"
#if HAVE_LIBWACOM
#include <libwacom/libwacom.h>
@ -410,7 +411,9 @@ static void
evdev_tag_keyboard(struct evdev_device *device,
struct udev_device *udev_device)
{
const char *prop;
struct quirks_context *quirks;
struct quirks *q;
char *prop;
int code;
if (!libevdev_has_event_type(device->evdev, EV_KEY))
@ -423,10 +426,9 @@ evdev_tag_keyboard(struct evdev_device *device,
return;
}
/* This should eventually become ID_INPUT_KEYBOARD_INTEGRATION */
prop = udev_device_get_property_value(udev_device,
"LIBINPUT_ATTR_KEYBOARD_INTEGRATION");
if (prop) {
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
if (q && quirks_get_string(q, QUIRK_ATTR_KEYBOARD_INTEGRATION, &prop)) {
if (streq(prop, "internal")) {
evdev_tag_keyboard_internal(device);
} else if (streq(prop, "external")) {
@ -438,6 +440,8 @@ evdev_tag_keyboard(struct evdev_device *device,
}
}
quirks_unref(q);
device->tags |= EVDEV_TAG_KEYBOARD;
}
@ -796,12 +800,16 @@ evdev_is_fake_mt_device(struct evdev_device *device)
enum switch_reliability
evdev_read_switch_reliability_prop(struct evdev_device *device)
{
const char *prop;
enum switch_reliability r;
struct quirks_context *quirks;
struct quirks *q;
char *prop;
prop = udev_device_get_property_value(device->udev_device,
"LIBINPUT_ATTR_LID_SWITCH_RELIABILITY");
if (!parse_switch_reliability_property(prop, &r)) {
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
if (!q || !quirks_get_string(q, QUIRK_ATTR_LID_SWITCH_RELIABILITY, &prop)) {
r = RELIABILITY_UNKNOWN;
} else if (!parse_switch_reliability_property(prop, &r)) {
evdev_log_error(device,
"%s: switch reliability set to unknown value '%s'\n",
device->devname,
@ -811,6 +819,8 @@ evdev_read_switch_reliability_prop(struct evdev_device *device)
evdev_log_info(device, "will write switch open events\n");
}
quirks_unref(q);
return r;
}
@ -1169,22 +1179,17 @@ evdev_read_wheel_tilt_props(struct evdev_device *device)
static inline int
evdev_get_trackpoint_range(struct evdev_device *device)
{
struct quirks_context *quirks;
struct quirks *q;
const char *prop;
int range = DEFAULT_TRACKPOINT_RANGE;
uint32_t range = DEFAULT_TRACKPOINT_RANGE;
if (!(device->tags & EVDEV_TAG_TRACKPOINT))
return DEFAULT_TRACKPOINT_RANGE;
prop = udev_device_get_property_value(device->udev_device,
"LIBINPUT_ATTR_TRACKPOINT_RANGE");
if (prop) {
if (!safe_atoi(prop, &range) || range < 0.0) {
evdev_log_error(device,
"trackpoint range property is present but invalid, "
"using %d instead\n",
DEFAULT_TRACKPOINT_RANGE);
range = DEFAULT_TRACKPOINT_RANGE;
}
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
if (q && quirks_get_uint32(q, QUIRK_ATTR_TRACKPOINT_RANGE, &range)) {
goto out;
}
@ -1215,6 +1220,8 @@ evdev_get_trackpoint_range(struct evdev_device *device)
}
out:
quirks_unref(q);
if (range == 0) {
evdev_log_bug_libinput(device, "trackpoint range is zero\n");
range = DEFAULT_TRACKPOINT_RANGE;
@ -1256,12 +1263,11 @@ static inline uint32_t
evdev_read_model_flags(struct evdev_device *device)
{
const struct model_map {
const char *property;
enum quirk quirk;
enum evdev_device_model model;
} model_map[] = {
#define MODEL(name) { "LIBINPUT_MODEL_" #name, EVDEV_MODEL_##name }
#define MODEL(name) { QUIRK_MODEL_##name, EVDEV_MODEL_##name }
MODEL(LENOVO_X230),
MODEL(LENOVO_X220_TOUCHPAD_FW81),
MODEL(CHROMEBOOK),
MODEL(SYSTEM76_BONOBO),
MODEL(SYSTEM76_GALAGO),
@ -1290,27 +1296,60 @@ evdev_read_model_flags(struct evdev_device *device)
MODEL(LENOVO_CARBON_X1_6TH),
MODEL(LENOVO_SCROLLPOINT),
#undef MODEL
{ "ID_INPUT_TRACKBALL", EVDEV_MODEL_TRACKBALL },
{ NULL, EVDEV_MODEL_DEFAULT },
{ 0, 0 },
};
const struct model_map *m = model_map;
uint32_t model_flags = 0;
uint32_t all_model_flags = 0;
struct quirks_context *quirks;
struct quirks *q;
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
while (q && m->quirk) {
bool is_set;
while (m->property) {
/* Check for flag re-use */
if (strneq("LIBINPUT_MODEL_", m->property, 15)) {
assert((all_model_flags & m->model) == 0);
all_model_flags |= m->model;
if (quirks_get_bool(q, m->quirk, &is_set)) {
if (is_set) {
evdev_log_debug(device,
"tagged as %s\n",
quirk_get_name(m->quirk));
model_flags |= m->model;
} else {
evdev_log_debug(device,
"untagged as %s\n",
quirk_get_name(m->quirk));
model_flags &= ~m->model;
}
}
m++;
}
quirks_unref(q);
if (parse_udev_flag(device,
device->udev_device,
m->property)) {
evdev_log_debug(device, "tagged as %s\n", m->property);
model_flags |= m->model;
"ID_INPUT_TRACKBALL")) {
evdev_log_debug(device, "tagged as trackball\n");
model_flags |= EVDEV_MODEL_TRACKBALL;
}
m++;
/**
* Device is 6 years old at the time of writing this and this was
* one of the few udev properties that wasn't reserved for private
* usage, so we need to keep this for backwards compat.
*/
if (parse_udev_flag(device,
device->udev_device,
"LIBINPUT_MODEL_LENOVO_X220_TOUCHPAD_FW81")) {
evdev_log_debug(device, "tagged as trackball\n");
model_flags |= EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81;
}
return model_flags;
@ -1321,16 +1360,25 @@ evdev_read_attr_res_prop(struct evdev_device *device,
size_t *xres,
size_t *yres)
{
struct udev_device *udev;
const char *res_prop;
struct quirks_context *quirks;
struct quirks *q;
struct quirk_dimensions dim;
bool rc = false;
udev = device->udev_device;
res_prop = udev_device_get_property_value(udev,
"LIBINPUT_ATTR_RESOLUTION_HINT");
if (!res_prop)
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
if (!q)
return false;
return parse_dimension_property(res_prop, xres, yres);
rc = quirks_get_dimensions(q, QUIRK_ATTR_RESOLUTION_HINT, &dim);
if (rc) {
*xres = dim.x;
*yres = dim.y;
}
quirks_unref(q);
return rc;
}
static inline bool
@ -1338,16 +1386,25 @@ evdev_read_attr_size_prop(struct evdev_device *device,
size_t *size_x,
size_t *size_y)
{
struct udev_device *udev;
const char *size_prop;
struct quirks_context *quirks;
struct quirks *q;
struct quirk_dimensions dim;
bool rc = false;
udev = device->udev_device;
size_prop = udev_device_get_property_value(udev,
"LIBINPUT_ATTR_SIZE_HINT");
if (!size_prop)
quirks = evdev_libinput_context(device)->quirks;
q = quirks_fetch_for_device(quirks, device->udev_device);
if (!q)
return false;
return parse_dimension_property(size_prop, size_x, size_y);
rc = quirks_get_dimensions(q, QUIRK_ATTR_SIZE_HINT, &dim);
if (rc) {
*size_x = dim.x;
*size_y = dim.y;
}
quirks_unref(q);
return rc;
}
/* Return 1 if the device is set to the fake resolution or 0 otherwise */

View file

@ -140,6 +140,9 @@ struct libinput {
struct list device_group_list;
uint64_t last_event_time;
bool quirks_initialized;
struct quirks_context *quirks;
};
typedef void (*libinput_seat_destroy_func) (struct libinput_seat *seat);
@ -427,6 +430,9 @@ libinput_init(struct libinput *libinput,
const struct libinput_interface_backend *interface_backend,
void *user_data);
void
libinput_init_quirks(struct libinput *libinput);
struct libinput_source *
libinput_add_fd(struct libinput *libinput,
int fd,

View file

@ -61,6 +61,20 @@ list_insert(struct list *list, struct list *elm)
elm->next->prev = elm;
}
void
list_append(struct list *list, struct list *elm)
{
assert((list->next != NULL && list->prev != NULL) ||
!"list->next|prev is NULL, possibly missing list_init()");
assert(((elm->next == NULL && elm->prev == NULL) || list_empty(elm)) ||
!"elm->next|prev is not NULL, list node used twice?");
elm->next = list;
elm->prev = list->prev;
list->prev = elm;
elm->prev->next = elm;
}
void
list_remove(struct list *elm)
{

View file

@ -86,6 +86,7 @@ struct list {
void list_init(struct list *list);
void list_insert(struct list *list, struct list *elm);
void list_append(struct list *list, struct list *elm);
void list_remove(struct list *elm);
bool list_empty(const struct list *list);
@ -518,6 +519,36 @@ safe_atoi(const char *str, int *val)
return safe_atoi_base(str, val, 10);
}
static inline bool
safe_atou_base(const char *str, unsigned int *val, int base)
{
char *endptr;
unsigned long v;
assert(base == 10 || base == 16 || base == 8);
errno = 0;
v = strtoul(str, &endptr, base);
if (errno > 0)
return false;
if (str == endptr)
return false;
if (*str != '\0' && *endptr != '\0')
return false;
if (v > UINT_MAX)
return false;
*val = v;
return true;
}
static inline bool
safe_atou(const char *str, unsigned int *val)
{
return safe_atou_base(str, val, 10);
}
static inline bool
safe_atod(const char *str, double *val)
{

View file

@ -38,6 +38,7 @@
#include "libinput-private.h"
#include "evdev.h"
#include "timer.h"
#include "quirks.h"
#define require_event_type(li_, type_, retval_, ...) \
if (type_ == LIBINPUT_EVENT_NONE) abort(); \
@ -1720,6 +1721,46 @@ libinput_init(struct libinput *libinput,
return 0;
}
void
libinput_init_quirks(struct libinput *libinput)
{
const char *data_path,
*override_file = NULL;
struct quirks_context *quirks;
if (libinput->quirks_initialized)
return;
/* If we fail, we'll fail next time too */
libinput->quirks_initialized = true;
data_path = getenv("LIBINPUT_DATA_DIR");
if (!data_path) {
data_path = LIBINPUT_DATA_DIR;
override_file = LIBINPUT_DATA_OVERRIDE_FILE;
}
quirks = quirks_init_subsystem(data_path,
override_file,
log_msg_va,
libinput,
QLOG_LIBINPUT_LOGGING);
if (!quirks) {
log_error(libinput,
"Failed to load the device quirks from %s%s%s. "
"This will negatively affect device behavior. "
"See %sdevice-quirks.html for details.\n",
data_path,
override_file ? " and " : "",
override_file ? override_file : "",
HTTP_DOC_LINK
);
return;
}
libinput->quirks = quirks;
}
static void
libinput_device_destroy(struct libinput_device *device);
@ -1791,6 +1832,7 @@ libinput_unref(struct libinput *libinput)
libinput_timer_subsys_destroy(libinput);
libinput_drop_destroyed_sources(libinput);
quirks_context_unref(libinput->quirks);
close(libinput->epoll_fd);
free(libinput);

View file

@ -337,6 +337,13 @@ libinput_path_add_device(struct libinput *libinput,
return NULL;
}
/* We cannot do this during path_create_context because the log
* handler isn't set up there but we really want to log to the right
* place if the quirks run into parser errors. So we have to do it
* on the first call to add_device.
*/
libinput_init_quirks(libinput);
udev_device = udev_device_from_devnode(libinput, udev, path);
if (!udev_device) {
log_bug_client(libinput, "Invalid path %s\n", path);

1494
src/quirks.c Normal file

File diff suppressed because it is too large Load diff

273
src/quirks.h Normal file
View file

@ -0,0 +1,273 @@
/*
* Copyright © 2018 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include "config.h"
#include <stdbool.h>
#include <stdint.h>
#include <libudev.h>
#include "libinput.h"
/**
* Handle to the quirks context.
*/
struct quirks_context;
/**
* Contains all quirks set for a single device.
*/
struct quirks;
struct quirk_dimensions {
size_t x, y;
};
struct quirk_range {
int lower, upper;
};
/**
* Quirks known to libinput
*/
enum quirk {
QUIRK_MODEL_ALPS_TOUCHPAD = 100,
QUIRK_MODEL_APPLE_TOUCHPAD,
QUIRK_MODEL_APPLE_MAGICMOUSE,
QUIRK_MODEL_TABLET_NO_TILT,
QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON,
QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER,
QUIRK_MODEL_CYBORG_RAT,
QUIRK_MODEL_CHROMEBOOK,
QUIRK_MODEL_HP6910_TOUCHPAD,
QUIRK_MODEL_HP8510_TOUCHPAD,
QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD,
QUIRK_MODEL_HP_STREAM11_TOUCHPAD,
QUIRK_MODEL_HP_ZBOOK_STUDIO_G3,
QUIRK_MODEL_TABLET_NO_PROXIMITY_OUT,
QUIRK_MODEL_LENOVO_SCROLLPOINT,
QUIRK_MODEL_LENOVO_X230,
QUIRK_MODEL_LENOVO_T450_TOUCHPAD,
QUIRK_MODEL_TABLET_MODE_NO_SUSPEND,
QUIRK_MODEL_LENOVO_CARBON_X1_6TH,
QUIRK_MODEL_TRACKBALL,
QUIRK_MODEL_LOGITECH_MARBLE_MOUSE,
QUIRK_MODEL_BOUNCING_KEYS,
QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD,
QUIRK_MODEL_SYSTEM76_BONOBO,
QUIRK_MODEL_CLEVO_W740SU,
QUIRK_MODEL_SYSTEM76_GALAGO,
QUIRK_MODEL_SYSTEM76_KUDU,
QUIRK_MODEL_WACOM_TOUCHPAD,
QUIRK_MODEL_JUMPING_SEMI_MT,
QUIRK_ATTR_SIZE_HINT = 300,
QUIRK_ATTR_TOUCH_SIZE_RANGE,
QUIRK_ATTR_PALM_SIZE_THRESHOLD,
QUIRK_ATTR_LID_SWITCH_RELIABILITY,
QUIRK_ATTR_KEYBOARD_INTEGRATION,
QUIRK_ATTR_TPKBCOMBO_LAYOUT,
QUIRK_ATTR_PRESSURE_RANGE,
QUIRK_ATTR_PALM_PRESSURE_THRESHOLD,
QUIRK_ATTR_RESOLUTION_HINT,
QUIRK_ATTR_TRACKPOINT_RANGE,
QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD,
};
/**
* Returns a printable name for the quirk. This name is for developer
* tools, not user consumption. Do not display this in a GUI.
*/
const char*
quirk_get_name(enum quirk which);
/**
* Log priorities used if custom logging is enabled.
*/
enum quirks_log_priorities {
QLOG_NOISE,
QLOG_DEBUG = LIBINPUT_LOG_PRIORITY_DEBUG,
QLOG_INFO = LIBINPUT_LOG_PRIORITY_INFO,
QLOG_ERROR = LIBINPUT_LOG_PRIORITY_ERROR,
QLOG_PARSER_ERROR,
};
/**
* Log type to be used for logging. Use the libinput logging to hook up a
* libinput log handler. This will cause the quirks to reduce the noise and
* only provide useful messages.
*
* QLOG_CUSTOM_LOG_PRIORITIES enables more fine-grained and verbose logging,
* allowing debugging tools to be more useful.
*/
enum quirks_log_type {
QLOG_LIBINPUT_LOGGING,
QLOG_CUSTOM_LOG_PRIORITIES,
};
/**
* Initialize the quirks subsystem. This function must be called
* before anything else.
*
* If log_type is QLOG_CUSTOM_LOG_PRIORITIES, the log handler is called with
* the custom QLOG_* log priorities. Otherwise, the log handler only uses
* the libinput log priorities.
*
* @param data_path The directory containing the various data files
* @param override_file A file path containing custom overrides
* @param log_handler The libinput log handler called for debugging output
* @param libinput The libinput struct passed to the log handler
*
* @return an opaque handle to the context
*/
struct quirks_context *
quirks_init_subsystem(const char *data_path,
const char *override_file,
libinput_log_handler log_handler,
struct libinput *libinput,
enum quirks_log_type log_type);
/**
* Clean up after ourselves. This function must be called
* as the last call to the quirks subsystem.
*
* All quirks returned to the caller in quirks_fetch_for_device() must be
* unref'd before this call.
*
* @return Always NULL
*/
struct quirks_context *
quirks_context_unref(struct quirks_context *ctx);
struct quirks_context *
quirks_context_ref(struct quirks_context *ctx);
/**
* Fetch the quirks for a given device. If no quirks are defined, this
* function returns NULL.
*
* @return A new quirks struct, use quirks_unref() to release
*/
struct quirks *
quirks_fetch_for_device(struct quirks_context *ctx,
struct udev_device *device);
/**
* Reduce the refcount by one. When the refcount reaches zero, the
* associated struct is released.
*
* @return Always NULL
*/
struct quirks *
quirks_unref(struct quirks *q);
/**
* Returns true if the given quirk applies is in this quirk list.
*/
bool
quirks_has_quirk(struct quirks *q, enum quirk which);
/**
* Get the value of the given quirk, as unsigned integer.
* This function will assert if the quirk type does not match the
* requested type. If the quirk is not set for this device, val is
* unchanged.
*
* @return true if the quirk value is valid, false otherwise.
*/
bool
quirks_get_uint32(struct quirks *q,
enum quirk which,
uint32_t *val);
/**
* Get the value of the given quirk, as signed integer.
* This function will assert if the quirk type does not match the
* requested type. If the quirk is not set for this device, val is
* unchanged.
*
* @return true if the quirk value is valid, false otherwise.
*/
bool
quirks_get_int32(struct quirks *q,
enum quirk which,
int32_t *val);
/**
* Get the value of the given quirk, as string.
* This function will assert if the quirk type does not match the
* requested type. If the quirk is not set for this device, val is
* unchanged.
*
* val is set to the string, do not modify or free it. The lifetime of the
* returned string is bound to the lifetime of the quirk.
*
* @return true if the quirk value is valid, false otherwise.
*/
bool
quirks_get_string(struct quirks *q,
enum quirk which,
char **val);
/**
* Get the value of the given quirk, as bool.
* This function will assert if the quirk type does not match the
* requested type. If the quirk is not set for this device, val is
* unchanged.
*
* @return true if the quirk value is valid, false otherwise.
*/
bool
quirks_get_bool(struct quirks *q,
enum quirk which,
bool *val);
/**
* Get the value of the given quirk, as dimension.
* This function will assert if the quirk type does not match the
* requested type. If the quirk is not set for this device, val is
* unchanged.
*
* @return true if the quirk value is valid, false otherwise.
*/
bool
quirks_get_dimensions(struct quirks *q,
enum quirk which,
struct quirk_dimensions *val);
/**
* Get the value of the given quirk, as range.
* This function will assert if the quirk type does not match the
* requested type. If the quirk is not set for this device, val is
* unchanged.
*
* @return true if the quirk value is valid, false otherwise.
*/
bool
quirks_get_range(struct quirks *q,
enum quirk which,
struct quirk_range *val);

View file

@ -382,6 +382,13 @@ libinput_udev_assign_seat(struct libinput *libinput,
{
struct udev_input *input = (struct udev_input*)libinput;
/* We cannot do this during udev_create_context because the log
* handler isn't set up there but we really want to log to the right
* place if the quirks run into parser errors. So we have to do it
* here since we can expect the log handler to be set up by now.
*/
libinput_init_quirks(libinput);
if (!seat_id)
return -1;

View file

@ -103,15 +103,10 @@ static struct input_absinfo absinfo[] = {
{ .value = -1 }
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"touchpad_end\"\n"
"KERNEL!=\"event*\", GOTO=\"touchpad_end\"\n"
"ENV{ID_INPUT_TOUCHPAD}==\"\", GOTO=\"touchpad_end\"\n"
"\n"
"ATTRS{name}==\"litest AlpsPS/2 ALPS DualPoint TouchPad\","
" ENV{LIBINPUT_MODEL_TOUCHPAD_VISIBLE_MARKER}=\"1\"\n"
"\n"
"LABEL=\"touchpad_end\"";
static const char quirk_file[] =
"[litest ALPS Touchpad]\n"
"MatchName=litest AlpsPS/2 ALPS DualPoint TouchPad\n"
"ModelTouchpadVisibleMarker=1\n";
TEST_DEVICE("alps-dualpoint",
.type = LITEST_ALPS_DUALPOINT,
@ -122,5 +117,5 @@ TEST_DEVICE("alps-dualpoint",
.id = &input_id,
.events = events,
.absinfo = absinfo,
.udev_rule = udev_rule,
.quirk_file = quirk_file,
)

View file

@ -83,15 +83,10 @@ static struct input_absinfo absinfo[] = {
{ .value = -1 }
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"touchpad_end\"\n"
"KERNEL!=\"event*\", GOTO=\"touchpad_end\"\n"
"ENV{ID_INPUT_TOUCHPAD}==\"\", GOTO=\"touchpad_end\"\n"
"\n"
"ATTRS{name}==\"litest appletouch\","
" ENV{LIBINPUT_MODEL_APPLE_TOUCHPAD_ONEBUTTON}=\"1\"\n"
"\n"
"LABEL=\"touchpad_end\"";
static const char quirk_file[] =
"[litest ALPS Touchpad]\n"
"MatchName=litest appletouch\n"
"ModelAppleTouchpadOneButton=1\n";
TEST_DEVICE("appletouch",
.type = LITEST_APPLETOUCH,
@ -102,5 +97,5 @@ TEST_DEVICE("appletouch",
.id = &input_id,
.events = events,
.absinfo = absinfo,
.udev_rule = udev_rule,
.quirk_file = quirk_file,
)

View file

@ -47,11 +47,15 @@ static const char udev_rule[] =
"KERNEL!=\"event*\", GOTO=\"switch_end\"\n"
"\n"
"ATTRS{name}==\"litest gpio-keys*\",\\\n"
" ENV{ID_INPUT_SWITCH}=\"1\",\\\n"
" ENV{LIBINPUT_ATTR_LID_SWITCH_RELIABILITY}=\"reliable\"\n"
" ENV{ID_INPUT_SWITCH}=\"1\"\n"
"\n"
"LABEL=\"switch_end\"";
static const char quirk_file[] =
"[litest gpio quirk]\n"
"MatchName=litest gpio-keys\n"
"AttrLidSwitchReliability=reliable\n";
TEST_DEVICE("gpio-keys",
.type = LITEST_GPIO_KEYS,
.features = LITEST_SWITCH,
@ -63,4 +67,5 @@ TEST_DEVICE("gpio-keys",
.absinfo = NULL,
.udev_rule = udev_rule,
.quirk_file = quirk_file,
)

View file

@ -88,15 +88,10 @@ static int events[] = {
-1, -1,
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"huion_end\"\n"
"KERNEL!=\"event*\", GOTO=\"huion_end\"\n"
"ENV{ID_INPUT_TABLET}==\"\", GOTO=\"huion_end\"\n"
"\n"
"ATTRS{name}==\"litest HUION PenTablet Pen\","
" ENV{LIBINPUT_MODEL_TABLET_NO_PROXIMITY_OUT}=\"1\"\n"
"\n"
"LABEL=\"huion_end\"";
static const char quirk_file[] =
"[litest HUION tablet]\n"
"MatchName=litest HUION PenTablet Pen\n"
"ModelTabletNoProximityOut=1\n";
TEST_DEVICE("huion-tablet",
.type = LITEST_HUION_TABLET,
@ -107,5 +102,5 @@ TEST_DEVICE("huion-tablet",
.id = &input_id,
.events = events,
.absinfo = absinfo,
.udev_rule = udev_rule,
.quirk_file = quirk_file,
)

View file

@ -43,11 +43,15 @@ static const char udev_rule[] =
"KERNEL!=\"event*\", GOTO=\"switch_end\"\n"
"\n"
"ATTRS{name}==\"litest Lid Switch Surface3*\",\\\n"
" ENV{ID_INPUT_SWITCH}=\"1\",\\\n"
" ENV{LIBINPUT_ATTR_LID_SWITCH_RELIABILITY}=\"write_open\"\n"
" ENV{ID_INPUT_SWITCH}=\"1\"\n"
"\n"
"LABEL=\"switch_end\"";
static const char quirk_file[] =
"[litest Surface Lid]\n"
"MatchName=litest Lid Switch Surface3\n"
"AttrLidSwitchReliability=write_open\n";
TEST_DEVICE("lid-switch-surface3",
.type = LITEST_LID_SWITCH_SURFACE3,
.features = LITEST_SWITCH,
@ -59,4 +63,5 @@ TEST_DEVICE("lid-switch-surface3",
.absinfo = NULL,
.udev_rule = udev_rule,
.quirk_file = quirk_file,
)

View file

@ -42,11 +42,15 @@ static const char udev_rule[] =
"KERNEL!=\"event*\", GOTO=\"switch_end\"\n"
"\n"
"ATTRS{name}==\"litest Lid Switch\",\\\n"
" ENV{ID_INPUT_SWITCH}=\"1\",\\\n"
" ENV{LIBINPUT_ATTR_LID_SWITCH_RELIABILITY}=\"reliable\"\n"
" ENV{ID_INPUT_SWITCH}=\"1\"\n"
"\n"
"LABEL=\"switch_end\"";
static const char quirk_file[] =
"[litest Lid Switch]\n"
"MatchName=litest Lid Switch\n"
"AttrLidSwitchReliability=reliable\n";
TEST_DEVICE("lid-switch",
.type = LITEST_LID_SWITCH,
.features = LITEST_SWITCH,
@ -58,4 +62,5 @@ TEST_DEVICE("lid-switch",
.absinfo = NULL,
.udev_rule = udev_rule,
.quirk_file = quirk_file,
)

View file

@ -79,15 +79,10 @@ static struct input_absinfo absinfo[] = {
{ .value = -1 }
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"touchpad_end\"\n"
"KERNEL!=\"event*\", GOTO=\"touchpad_end\"\n"
"ENV{ID_INPUT_TOUCHPAD}==\"\", GOTO=\"touchpad_end\"\n"
"\n"
"ATTRS{name}==\"litest DLL0704:01 06CB:76AD Touchpad\","
" ENV{LIBINPUT_MODEL_TOUCHPAD_VISIBLE_MARKER}=\"1\"\n"
"\n"
"LABEL=\"touchpad_end\"";
static const char quirk_file[] =
"[litest Synaptics i2c Touchpad]\n"
"MatchName=litest DLL0704:01 06CB:76AD Touchpad\n"
"ModelTouchpadVisibleMarker=1\n";
TEST_DEVICE("synaptics-i2c",
.type = LITEST_SYNAPTICS_I2C,
@ -98,5 +93,5 @@ TEST_DEVICE("synaptics-i2c",
.id = &input_id,
.events = events,
.absinfo = absinfo,
.udev_rule = udev_rule,
.quirk_file = quirk_file,
)

View file

@ -105,15 +105,10 @@ static struct input_absinfo absinfo[] = {
{ .value = -1 }
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"touchpad_end\"\n"
"KERNEL!=\"event*\", GOTO=\"touchpad_end\"\n"
"ENV{ID_INPUT_TOUCHPAD}==\"\", GOTO=\"touchpad_end\"\n"
"\n"
"ATTRS{name}==\"litest SynPS/2 Synaptics TouchPad X1C3rd\","
" ENV{LIBINPUT_MODEL_LENOVO_T450_TOUCHPAD}=\"1\"\n"
"\n"
"LABEL=\"touchpad_end\"";
static const char quirk_file[] =
"[litest Synaptics X1 Carbon 3rd Touchpad]\n"
"MatchName=litest SynPS/2 Synaptics TouchPad X1C3rd\n"
"ModelLenovoT450Touchpad=1\n";
TEST_DEVICE("synaptics-carbon3rd",
.type = LITEST_SYNAPTICS_TRACKPOINT_BUTTONS,
@ -124,5 +119,5 @@ TEST_DEVICE("synaptics-carbon3rd",
.id = &input_id,
.events = events,
.absinfo = absinfo,
.udev_rule = udev_rule,
.quirk_file = quirk_file,
)

View file

@ -220,15 +220,10 @@ static int events[] = {
-1, -1,
};
static const char udev_rule[] =
"ACTION==\"remove\", GOTO=\"waltop_end\"\n"
"KERNEL!=\"event*\", GOTO=\"waltop_end\"\n"
"ENV{ID_INPUT_TABLET}==\"\", GOTO=\"waltop_end\"\n"
"\n"
"ATTRS{name}==\"litest WALTOP Batteryless Tablet*\",\\\n"
" ENV{LIBINPUT_ATTR_SIZE_HINT}=\"200x200\"\n"
"\n"
"LABEL=\"waltop_end\"";
static const char quirk_file[] =
"[litest Waltop Tablet]\n"
"MatchName=litest WALTOP Batteryless Tablet*\n"
"AttrSizeHint=200x200\n";
TEST_DEVICE("waltop-tablet",
.type = LITEST_WALTOP,
@ -239,5 +234,5 @@ TEST_DEVICE("waltop-tablet",
.id = &input_id,
.events = events,
.absinfo = absinfo,
.udev_rule = udev_rule,
.quirk_file = quirk_file,
)

View file

@ -70,6 +70,7 @@ struct litest_test_device {
struct litest_device_interface *interface;
const char *udev_rule;
const char *quirk_file;
};
struct litest_device_interface {

View file

@ -55,6 +55,7 @@
#include "litest.h"
#include "litest-int.h"
#include "libinput-util.h"
#include "quirks.h"
#include <linux/kd.h>
@ -76,6 +77,7 @@ static int verbose = 0;
const char *filter_test = NULL;
const char *filter_device = NULL;
const char *filter_group = NULL;
static struct quirks_context *quirks_context;
struct created_file {
struct list link;
@ -87,6 +89,7 @@ struct list created_files_list; /* list of all files to remove at the end of
static void litest_init_udev_rules(struct list *created_files_list);
static void litest_remove_udev_rules(struct list *created_files_list);
static char *litest_install_quirks(struct list *created_files_list);
/* defined for the litest selftest */
#ifndef LITEST_DISABLE_BACKTRACE_LOGGING
@ -741,6 +744,7 @@ litest_signal(int sig)
list_for_each_safe(f, tmp, &created_files_list, link) {
list_remove(&f->link);
unlink(f->path);
rmdir(f->path);
/* in the sighandler, we can't free */
}
@ -788,6 +792,19 @@ litest_free_test_list(struct list *tests)
}
}
LIBINPUT_ATTRIBUTE_PRINTF(3, 0)
static inline void
quirk_log_handler(struct libinput *unused,
enum libinput_log_priority priority,
const char *format,
va_list args)
{
if (priority < LIBINPUT_LOG_PRIORITY_ERROR)
return;
vfprintf(stderr, format, args);
}
static int
litest_run_suite(struct list *tests, int which, int max, int error_fd)
{
@ -803,6 +820,12 @@ litest_run_suite(struct list *tests, int which, int max, int error_fd)
struct name *n, *tmp;
struct list testnames;
quirks_context = quirks_init_subsystem(getenv("LIBINPUT_DATA_DIR"),
NULL,
quirk_log_handler,
NULL,
QLOG_LIBINPUT_LOGGING);
/* Check just takes the suite/test name pointers but doesn't strdup
* them - we have to keep them around */
list_init(&testnames);
@ -887,6 +910,8 @@ out:
free(n);
}
quirks_context_unref(quirks_context);
return failed;
}
@ -998,6 +1023,7 @@ litest_run(int argc, char **argv)
{
int failed = 0;
int inhibit_lock_fd;
char *quirks_dir;
list_init(&created_files_list);
@ -1011,6 +1037,10 @@ litest_run(int argc, char **argv)
verbose = 1;
litest_init_udev_rules(&created_files_list);
quirks_dir = litest_install_quirks(&created_files_list);
setenv("LIBINPUT_DATA_DIR", quirks_dir, 1);
free(quirks_dir);
litest_setup_sighandler(SIGINT);
@ -1104,13 +1134,20 @@ litest_copy_file(const char *dest, const char *src, const char *header)
{
int in, out, length;
struct created_file *file;
int suffixlen;
file = zalloc(sizeof(*file));
file->path = safe_strdup(dest);
suffixlen = file->path + strlen(file->path) - rindex(file->path, '.');
if (strstr(dest, "XXXXXX")) {
int suffixlen;
suffixlen = file->path +
strlen(file->path) -
rindex(file->path, '.');
out = mkstemps(file->path, suffixlen);
} else {
out = open(file->path, O_CREAT|O_WRONLY);
}
if (out == -1)
litest_abort_msg("Failed to write to file %s (%s)\n",
file->path,
@ -1169,6 +1206,86 @@ litest_install_model_quirks(struct list *created_files_list)
list_insert(created_files_list, &file->link);
}
static char *
litest_init_device_quirk_file(const char *data_dir,
struct litest_test_device *dev)
{
int fd;
FILE *f;
char path[PATH_MAX];
static int count;
if (!dev->quirk_file)
return NULL;
snprintf(path, sizeof(path),
"%s/99-%03d-%s.quirks",
data_dir,
++count,
dev->shortname);
fd = open(path, O_CREAT|O_WRONLY, 0644);
litest_assert_int_ne(fd, -1);
f = fdopen(fd, "w");
litest_assert_notnull(f);
litest_assert_int_ge(fputs(dev->quirk_file, f), 0);
fclose(f);
return safe_strdup(path);
}
static char *
litest_install_quirks(struct list *created_files_list)
{
struct litest_test_device **dev = devices;
struct created_file *file;
char dirname[PATH_MAX] = "/run/litest-XXXXXX";
char **quirks, **q;
litest_assert_notnull(mkdtemp(dirname));
litest_assert_int_ne(chmod(dirname, 0755), -1);
quirks = strv_from_string(LIBINPUT_DATA_FILES, ":");
litest_assert(quirks);
q = quirks;
while (*q) {
char *filename;
char dest[PATH_MAX];
char src[PATH_MAX];
litest_assert(strneq(*q, "data/", 5));
filename = &(*q)[5];
snprintf(src, sizeof(src), "%s/%s", LIBINPUT_DATA_SRCDIR, filename);
snprintf(dest, sizeof(dest), "%s/%s", dirname, filename);
file = litest_copy_file(dest, src, NULL);
list_append(created_files_list, &file->link);
q++;
}
strv_free(quirks);
/* Now add the per-device special config files */
while (*dev) {
char *path;
path = litest_init_device_quirk_file(dirname, *dev);
if (path) {
struct created_file *file = zalloc(sizeof(*file));
file->path = path;
list_insert(created_files_list, &file->link);
}
dev++;
}
file = zalloc(sizeof *file);
file->path = safe_strdup(dirname);
list_append(created_files_list, &file->link);
return safe_strdup(dirname);
}
static inline void
mkdir_p(const char *dir)
{
@ -1212,6 +1329,7 @@ litest_remove_udev_rules(struct list *created_files_list)
list_for_each_safe(f, tmp, created_files_list, link) {
list_remove(&f->link);
unlink(f->path);
rmdir(f->path);
free(f->path);
free(f);
}
@ -1399,6 +1517,9 @@ litest_add_device_with_overrides(struct libinput *libinput,
d->libinput = libinput;
d->libinput_device = libinput_path_add_device(d->libinput, path);
d->quirks = quirks_fetch_for_device(quirks_context,
libinput_device_get_udev_device(d->libinput_device));
litest_assert(d->libinput_device != NULL);
libinput_device_ref(d->libinput_device);
@ -1522,6 +1643,8 @@ litest_delete_device(struct litest_device *d)
litest_assert_int_eq(d->skip_ev_syn, 0);
quirks_unref(d->quirks);
if (d->libinput_device) {
libinput_path_remove_device(d->libinput_device);
libinput_device_unref(d->libinput_device);

View file

@ -35,6 +35,7 @@
#include <math.h>
#include "libinput-util.h"
#include "quirks.h"
struct test_device {
const char *name;
@ -345,6 +346,7 @@ struct litest_device {
struct libevdev *evdev;
struct libevdev_uinput *uinput;
struct libinput *libinput;
struct quirks *quirks;
bool owns_context;
struct libinput_device *libinput_device;
struct litest_device_interface *interface;

View file

@ -1141,87 +1141,6 @@ START_TEST(device_accelerometer)
}
END_TEST
START_TEST(device_udev_tag_alps)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
struct udev_device *d;
const char *prop;
d = libinput_device_get_udev_device(device);
prop = udev_device_get_property_value(d,
"LIBINPUT_MODEL_ALPS_TOUCHPAD");
if (strstr(libinput_device_get_name(device), "ALPS"))
ck_assert_notnull(prop);
else
ck_assert(prop == NULL);
udev_device_unref(d);
}
END_TEST
START_TEST(device_udev_tag_wacom)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
struct udev_device *d;
const char *prop;
d = libinput_device_get_udev_device(device);
prop = udev_device_get_property_value(d,
"LIBINPUT_MODEL_WACOM_TOUCHPAD");
if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_WACOM)
ck_assert_notnull(prop);
else
ck_assert(prop == NULL);
udev_device_unref(d);
}
END_TEST
START_TEST(device_udev_tag_apple)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
struct udev_device *d;
const char *prop;
d = libinput_device_get_udev_device(device);
prop = udev_device_get_property_value(d,
"LIBINPUT_MODEL_APPLE_TOUCHPAD");
if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_APPLE)
ck_assert_notnull(prop);
else
ck_assert(prop == NULL);
udev_device_unref(d);
}
END_TEST
START_TEST(device_udev_tag_synaptics_serial)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
struct udev_device *d;
const char *prop;
d = libinput_device_get_udev_device(device);
prop = udev_device_get_property_value(d,
"LIBINPUT_MODEL_SYNAPTICS_SERIAL_TOUCHPAD");
if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_SYNAPTICS_SERIAL &&
libevdev_get_id_product(dev->evdev) == PRODUCT_ID_SYNAPTICS_SERIAL)
ck_assert_notnull(prop);
else
ck_assert(prop == NULL);
udev_device_unref(d);
}
END_TEST
START_TEST(device_udev_tag_wacom_tablet)
{
struct litest_device *dev = litest_current_device();
@ -1627,10 +1546,6 @@ TEST_COLLECTION(device)
litest_add("device:wheel", device_wheel_only, LITEST_WHEEL, LITEST_RELATIVE|LITEST_ABSOLUTE|LITEST_TABLET);
litest_add_no_device("device:accelerometer", device_accelerometer);
litest_add("device:udev tags", device_udev_tag_alps, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("device:udev tags", device_udev_tag_wacom, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("device:udev tags", device_udev_tag_apple, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("device:udev tags", device_udev_tag_synaptics_serial, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("device:udev tags", device_udev_tag_wacom_tablet, LITEST_TABLET, LITEST_ANY);
litest_add_no_device("device:invalid rel events", device_nonpointer_rel);

View file

@ -1198,6 +1198,118 @@ START_TEST(safe_atoi_base_8_test)
}
END_TEST
struct atou_test {
char *str;
bool success;
unsigned int val;
};
START_TEST(safe_atou_test)
{
struct atou_test tests[] = {
{ "10", true, 10 },
{ "20", true, 20 },
{ "-1", false, 0 },
{ "2147483647", true, 2147483647 },
{ "-2147483648", false, 0},
{ "4294967295", true, 4294967295 },
{ "0x0", false, 0 },
{ "-10x10", false, 0 },
{ "1x-99", false, 0 },
{ "", false, 0 },
{ "abd", false, 0 },
{ "xabd", false, 0 },
{ "0xaf", false, 0 },
{ "0x0x", false, 0 },
{ "x10", false, 0 },
{ NULL, false, 0 }
};
unsigned int v;
bool success;
for (int i = 0; tests[i].str != NULL; i++) {
v = 0xad;
success = safe_atou(tests[i].str, &v);
ck_assert(success == tests[i].success);
if (success)
ck_assert_int_eq(v, tests[i].val);
else
ck_assert_int_eq(v, 0xad);
}
}
END_TEST
START_TEST(safe_atou_base_16_test)
{
struct atou_test tests[] = {
{ "10", true, 0x10 },
{ "20", true, 0x20 },
{ "-1", false, 0 },
{ "0x10", true, 0x10 },
{ "0xff", true, 0xff },
{ "abc", true, 0xabc },
{ "-10", false, 0 },
{ "0x0", true, 0 },
{ "0", true, 0 },
{ "0x-99", false, 0 },
{ "0xak", false, 0 },
{ "0x", false, 0 },
{ "x10", false, 0 },
{ NULL, false, 0 }
};
unsigned int v;
bool success;
for (int i = 0; tests[i].str != NULL; i++) {
v = 0xad;
success = safe_atou_base(tests[i].str, &v, 16);
ck_assert(success == tests[i].success);
if (success)
ck_assert_int_eq(v, tests[i].val);
else
ck_assert_int_eq(v, 0xad);
}
}
END_TEST
START_TEST(safe_atou_base_8_test)
{
struct atou_test tests[] = {
{ "7", true, 07 },
{ "10", true, 010 },
{ "20", true, 020 },
{ "-1", false, 0 },
{ "010", true, 010 },
{ "0ff", false, 0 },
{ "abc", false, 0},
{ "0xabc", false, 0},
{ "-10", false, 0 },
{ "0", true, 0 },
{ "00", true, 0 },
{ "0x0", false, 0 },
{ "0x-99", false, 0 },
{ "0xak", false, 0 },
{ "0x", false, 0 },
{ "x10", false, 0 },
{ NULL, false, 0 }
};
unsigned int v;
bool success;
for (int i = 0; tests[i].str != NULL; i++) {
v = 0xad;
success = safe_atou_base(tests[i].str, &v, 8);
ck_assert(success == tests[i].success);
if (success)
ck_assert_int_eq(v, tests[i].val);
else
ck_assert_int_eq(v, 0xad);
}
}
END_TEST
struct atod_test {
char *str;
bool success;
@ -1583,6 +1695,67 @@ START_TEST(timer_flush)
}
END_TEST
START_TEST(list_test_insert)
{
struct list_test {
int val;
struct list node;
} tests[] = {
{ .val = 1 },
{ .val = 2 },
{ .val = 3 },
{ .val = 4 },
};
struct list_test *t;
struct list head;
int val;
list_init(&head);
ARRAY_FOR_EACH(tests, t) {
list_insert(&head, &t->node);
}
val = 4;
list_for_each(t, &head, node) {
ck_assert_int_eq(t->val, val);
val--;
}
ck_assert_int_eq(val, 0);
}
END_TEST
START_TEST(list_test_append)
{
struct list_test {
int val;
struct list node;
} tests[] = {
{ .val = 1 },
{ .val = 2 },
{ .val = 3 },
{ .val = 4 },
};
struct list_test *t;
struct list head;
int val;
list_init(&head);
ARRAY_FOR_EACH(tests, t) {
list_append(&head, &t->node);
}
val = 1;
list_for_each(t, &head, node) {
ck_assert_int_eq(t->val, val);
val++;
}
ck_assert_int_eq(val, 5);
}
END_TEST
TEST_COLLECTION(misc)
{
litest_add_no_device("events:conversion", event_conversion_device_notify);
@ -1617,6 +1790,9 @@ TEST_COLLECTION(misc)
litest_add_no_device("misc:parser", safe_atoi_test);
litest_add_no_device("misc:parser", safe_atoi_base_16_test);
litest_add_no_device("misc:parser", safe_atoi_base_8_test);
litest_add_no_device("misc:parser", safe_atou_test);
litest_add_no_device("misc:parser", safe_atou_base_16_test);
litest_add_no_device("misc:parser", safe_atou_base_8_test);
litest_add_no_device("misc:parser", safe_atod_test);
litest_add_no_device("misc:parser", strsplit_test);
litest_add_no_device("misc:parser", kvsplit_double_test);
@ -1626,4 +1802,7 @@ TEST_COLLECTION(misc)
litest_add_no_device("misc:fd", fd_no_event_leak);
litest_add_no_device("misc:library_version", library_version);
litest_add_no_device("misc:list", list_test_insert);
litest_add_no_device("misc:list", list_test_append);
}

956
test/test-quirks.c Normal file
View file

@ -0,0 +1,956 @@
/*
* Copyright © 2018 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <config.h>
#include <check.h>
#include <libinput.h>
#include "libinput-util.h"
#include "litest.h"
#include "quirks.h"
static void
log_handler(struct libinput *this_is_null,
enum libinput_log_priority priority,
const char *format,
va_list args)
{
#if 0
vprintf(format, args);
#endif
}
struct data_dir {
char *dirname;
char *filename;
};
static struct data_dir
make_data_dir(const char *file_content)
{
struct data_dir dir = {0};
char dirname[PATH_MAX] = "/run/litest-quirk-test-XXXXXX";
char *filename;
FILE *fp;
int rc;
litest_assert_notnull(mkdtemp(dirname));
dir.dirname = safe_strdup(dirname);
if (file_content) {
rc = xasprintf(&filename, "%s/testfile.quirks", dirname);
litest_assert_int_eq(rc, (int)(strlen(dirname) + 16));
fp = fopen(filename, "w+");
rc = fputs(file_content, fp);
fclose(fp);
litest_assert_int_ge(rc, 0);
dir.filename = filename;
}
return dir;
}
static void
cleanup_data_dir(struct data_dir dd)
{
if (dd.filename) {
unlink(dd.filename);
free(dd.filename);
}
if (dd.dirname) {
rmdir(dd.dirname);
free(dd.dirname);
}
}
START_TEST(quirks_invalid_dir)
{
struct quirks_context *ctx;
ctx = quirks_init_subsystem("/does-not-exist",
NULL,
log_handler,
NULL,
QLOG_LIBINPUT_LOGGING);
ck_assert(ctx == NULL);
}
END_TEST
START_TEST(quirks_empty_dir)
{
struct quirks_context *ctx;
struct data_dir dd = make_data_dir(NULL);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_LIBINPUT_LOGGING);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_empty)
{
struct quirks_context *ctx;
const char quirks_file[] = "[Empty Section]";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_double)
{
struct quirks_context *ctx;
const char quirks_file[] = "[Section name]";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_missing_match)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_missing_attr)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_match_after_attr)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"AttrSizeHint=10x10\n"
"MatchName=mouse\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_duplicate_match)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"MatchUdevType=mouse\n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_section_duplicate_attr)
{
/* This shouldn't be allowed but the current parser
is happy with it */
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"AttrSizeHint=10x10\n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_section)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section Missing Bracket\n"
"MatchUdevType=mouse\n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_trailing_whitespace)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse \n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_unknown_match)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"Matchblahblah=mouse\n"
"AttrSizeHint=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_unknown_attr)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"Attrblahblah=10x10\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_unknown_model)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"Modelblahblah=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_error_model_not_one)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"ModelAppleTouchpad=true\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_comment_inline)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name] # some inline comment\n"
"MatchUdevType=mouse\t # another inline comment\n"
"ModelAppleTouchpad=1#\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_comment_empty)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"#\n"
" #\n"
"MatchUdevType=mouse\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_bustype)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchBus=usb\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchBus=bluetooth\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchBus=i2c\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchBus=rmi\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchBus=ps2\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_bustype_invalid)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchBustype=venga\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_vendor)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchVendor=0x0000\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchVendor=0x0001\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchVendor=0x2343\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_vendor_invalid)
{
struct quirks_context *ctx;
const char *quirks_file[] = {
"[Section name]\n"
"MatchVendor=-1\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchVendor=abc\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchVendor=0xFFFFF\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchVendor=123\n"
"ModelAppleTouchpad=1\n",
};
const char **qf;
ARRAY_FOR_EACH(quirks_file, qf) {
struct data_dir dd = make_data_dir(*qf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
}
END_TEST
START_TEST(quirks_parse_product)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchProduct=0x0000\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchProduct=0x0001\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchProduct=0x2343\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_product_invalid)
{
struct quirks_context *ctx;
const char *quirks_file[] = {
"[Section name]\n"
"MatchProduct=-1\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchProduct=abc\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchProduct=0xFFFFF\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchProduct=123\n"
"ModelAppleTouchpad=1\n",
};
const char **qf;
ARRAY_FOR_EACH(quirks_file, qf) {
struct data_dir dd = make_data_dir(*qf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
}
END_TEST
START_TEST(quirks_parse_name)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchName=1235\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchName=abc\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchName=*foo\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchName=foo*\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchName=foo[]\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchName=*foo*\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_name_invalid)
{
struct quirks_context *ctx;
const char *quirks_file[] = {
"[Section name]\n"
"MatchName=\n"
"ModelAppleTouchpad=1\n",
};
const char **qf;
ARRAY_FOR_EACH(quirks_file, qf) {
struct data_dir dd = make_data_dir(*qf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
}
END_TEST
START_TEST(quirks_parse_udev)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=touchpad\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchUdevType=mouse\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchUdevType=pointingstick\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchUdevType=tablet\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchUdevType=tablet-pad\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchUdevType=keyboard\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_udev_invalid)
{
struct quirks_context *ctx;
const char *quirks_file[] = {
"[Section name]\n"
"MatchUdevType=blah\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchUdevType=\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchUdevType=123\n"
"ModelAppleTouchpad=1\n",
};
const char **qf;
ARRAY_FOR_EACH(quirks_file, qf) {
struct data_dir dd = make_data_dir(*qf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
}
END_TEST
START_TEST(quirks_parse_dmi)
{
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchDMIModalias=dmi:*\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchDMIModalias=dmi:*svn*pn*:\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_dmi_invalid)
{
struct quirks_context *ctx;
const char *quirks_file[] = {
"[Section name]\n"
"MatchDMIModalias=\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchDMIModalias=*pn*\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchDMIModalias=dmi*pn*\n"
"ModelAppleTouchpad=1\n",
"[Section name]\n"
"MatchDMIModalias=foo\n"
"ModelAppleTouchpad=1\n",
};
const char **qf;
ARRAY_FOR_EACH(quirks_file, qf) {
struct data_dir dd = make_data_dir(*qf);
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
}
END_TEST
START_TEST(quirks_model_one)
{
struct litest_device *dev = litest_current_device();
struct udev_device *ud = libinput_device_get_udev_device(dev->libinput_device);
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"ModelAppleTouchpad=1\n";
struct data_dir dd = make_data_dir(quirks_file);
struct quirks *q;
bool isset;
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
q = quirks_fetch_for_device(ctx, ud);
ck_assert_notnull(q);
ck_assert(quirks_get_bool(q, QUIRK_MODEL_APPLE_TOUCHPAD, &isset));
ck_assert(isset == true);
quirks_unref(q);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_model_zero)
{
struct litest_device *dev = litest_current_device();
struct udev_device *ud = libinput_device_get_udev_device(dev->libinput_device);
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchUdevType=mouse\n"
"ModelAppleTouchpad=0\n";
struct data_dir dd = make_data_dir(quirks_file);
struct quirks *q;
bool isset;
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert_notnull(ctx);
q = quirks_fetch_for_device(ctx, ud);
ck_assert_notnull(q);
ck_assert(quirks_get_bool(q, QUIRK_MODEL_APPLE_TOUCHPAD, &isset));
ck_assert(isset == false);
quirks_unref(q);
quirks_context_unref(ctx);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_model_alps)
{
struct litest_device *dev = litest_current_device();
struct libinput_device *device = dev->libinput_device;
struct quirks *q;
bool exists, value;
q = dev->quirks;
exists = quirks_get_bool(q, QUIRK_MODEL_ALPS_TOUCHPAD, &value);
if (strstr(libinput_device_get_name(device), "ALPS")) {
ck_assert(exists);
ck_assert(value);
} else {
ck_assert(!exists);
ck_assert(!value);
}
}
END_TEST
START_TEST(quirks_model_wacom)
{
struct litest_device *dev = litest_current_device();
struct quirks *q;
bool exists, value;
q = dev->quirks;
exists = quirks_get_bool(q, QUIRK_MODEL_WACOM_TOUCHPAD, &value);
if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_WACOM) {
ck_assert(exists);
ck_assert(value);
} else {
ck_assert(!exists);
ck_assert(!value);
}
}
END_TEST
START_TEST(quirks_model_apple)
{
struct litest_device *dev = litest_current_device();
struct quirks *q;
bool exists, value;
q = dev->quirks;
exists = quirks_get_bool(q, QUIRK_MODEL_APPLE_TOUCHPAD, &value);
if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_APPLE) {
ck_assert(exists);
ck_assert(value);
} else {
ck_assert(!exists);
ck_assert(!value);
}
}
END_TEST
START_TEST(quirks_model_synaptics_serial)
{
struct litest_device *dev = litest_current_device();
struct quirks *q;
bool exists, value;
q = dev->quirks;
exists = quirks_get_bool(q, QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD, &value);
if (libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_SYNAPTICS_SERIAL &&
libevdev_get_id_product(dev->evdev) == PRODUCT_ID_SYNAPTICS_SERIAL) {
ck_assert(exists);
ck_assert(value);
} else {
ck_assert(!exists);
ck_assert(!value);
}
}
END_TEST
TEST_COLLECTION(quirks)
{
litest_add_for_device("quirks:datadir", quirks_invalid_dir, LITEST_MOUSE);
litest_add_for_device("quirks:datadir", quirks_empty_dir, LITEST_MOUSE);
litest_add_for_device("quirks:structure", quirks_section_empty, LITEST_MOUSE);
litest_add_for_device("quirks:structure", quirks_section_double, LITEST_MOUSE);
litest_add_for_device("quirks:structure", quirks_section_missing_match, LITEST_MOUSE);
litest_add_for_device("quirks:structure", quirks_section_missing_attr, LITEST_MOUSE);
litest_add_for_device("quirks:structure", quirks_section_match_after_attr, LITEST_MOUSE);
litest_add_for_device("quirks:structure", quirks_section_duplicate_match, LITEST_MOUSE);
litest_add_for_device("quirks:structure", quirks_section_duplicate_attr, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_error_section, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_error_trailing_whitespace, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_error_unknown_match, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_error_unknown_attr, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_error_unknown_model, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_error_model_not_one, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_comment_inline, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_comment_empty, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_bustype, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_bustype_invalid, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_vendor, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_vendor_invalid, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_product, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_product_invalid, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_name, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_name_invalid, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_udev, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_udev_invalid, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_dmi, LITEST_MOUSE);
litest_add_for_device("quirks:parsing", quirks_parse_dmi_invalid, LITEST_MOUSE);
litest_add_for_device("quirks:model", quirks_model_one, LITEST_MOUSE);
litest_add_for_device("quirks:model", quirks_model_zero, LITEST_MOUSE);
litest_add("quirks:devices", quirks_model_alps, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("quirks:devices", quirks_model_wacom, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("quirks:devices", quirks_model_apple, LITEST_TOUCHPAD, LITEST_ANY);
litest_add("quirks:devices", quirks_model_synaptics_serial, LITEST_TOUCHPAD, LITEST_ANY);
}

View file

@ -143,16 +143,15 @@ END_TEST
static bool
lid_switch_is_reliable(struct litest_device *dev)
{
struct udev_device *udev_device;
const char *prop;
char *prop;
bool is_reliable = false;
udev_device = libinput_device_get_udev_device(dev->libinput_device);
prop = udev_device_get_property_value(udev_device,
"LIBINPUT_ATTR_LID_SWITCH_RELIABILITY");
if (quirks_get_string(dev->quirks,
QUIRK_ATTR_LID_SWITCH_RELIABILITY,
&prop)) {
is_reliable = streq(prop, "reliable");
}
is_reliable = prop && streq(prop, "reliable");
udev_device_unref(udev_device);
return is_reliable;
}

View file

@ -293,19 +293,13 @@ END_TEST
static inline bool
tablet_has_proxout_quirk(struct litest_device *dev)
{
struct udev_device *udev_device;
bool has_quirk;
bool is_set = false;
if (!quirks_get_bool(dev->quirks,
QUIRK_MODEL_TABLET_NO_PROXIMITY_OUT,
&is_set))
return false;
udev_device = libinput_device_get_udev_device(dev->libinput_device);
has_quirk = !!udev_device_get_property_value(udev_device,
"LIBINPUT_MODEL_TABLET_NO_PROXIMITY_OUT");
if (!has_quirk)
has_quirk = !libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_PEN);
udev_device_unref(udev_device);
return has_quirk;
return is_set;
}
START_TEST(tip_up_prox_out)

View file

@ -0,0 +1,261 @@
/*
* Copyright © 2018 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <sys/stat.h>
#include "libinput-util.h"
#include "quirks.h"
static bool verbose = false;
static void
log_handler(struct libinput *this_is_null,
enum libinput_log_priority priority,
const char *format,
va_list args)
{
FILE *out = stdout;
enum quirks_log_priorities p = priority;
char buf[256] = {0};
const char *prefix = "";
switch (p) {
case QLOG_NOISE:
case QLOG_DEBUG:
if (!verbose)
return;
prefix = "quirks debug";
break;
case QLOG_INFO:
prefix = "quirks info";
break;
case QLOG_ERROR:
out = stderr;
prefix = "quirks error";
break;
case QLOG_PARSER_ERROR:
out = stderr;
prefix = "quirks parser error";
break;
}
snprintf(buf, sizeof(buf), "%s: %s", prefix, format);
vfprintf(out, buf, args);
}
static void
list_device_quirks(struct quirks_context *ctx, struct udev_device *device)
{
struct quirks *quirks;
quirks = quirks_fetch_for_device(ctx, device);
if (!quirks) {
printf("Device has no quirks defined\n");
} else {
enum quirk qlist[] = {
QUIRK_MODEL_ALPS_TOUCHPAD,
QUIRK_MODEL_APPLE_TOUCHPAD,
QUIRK_MODEL_APPLE_MAGICMOUSE,
QUIRK_MODEL_TABLET_NO_TILT,
QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON,
QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER,
QUIRK_MODEL_CYBORG_RAT,
QUIRK_MODEL_CHROMEBOOK,
QUIRK_MODEL_HP6910_TOUCHPAD,
QUIRK_MODEL_HP8510_TOUCHPAD,
QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD,
QUIRK_MODEL_HP_STREAM11_TOUCHPAD,
QUIRK_MODEL_HP_ZBOOK_STUDIO_G3,
QUIRK_MODEL_TABLET_NO_PROXIMITY_OUT,
QUIRK_MODEL_LENOVO_SCROLLPOINT,
QUIRK_MODEL_LENOVO_X230,
QUIRK_MODEL_LENOVO_T450_TOUCHPAD,
QUIRK_MODEL_TABLET_MODE_NO_SUSPEND,
QUIRK_MODEL_LENOVO_CARBON_X1_6TH,
QUIRK_MODEL_TRACKBALL,
QUIRK_MODEL_LOGITECH_MARBLE_MOUSE,
QUIRK_MODEL_BOUNCING_KEYS,
QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD,
QUIRK_MODEL_SYSTEM76_BONOBO,
QUIRK_MODEL_CLEVO_W740SU,
QUIRK_MODEL_SYSTEM76_GALAGO,
QUIRK_MODEL_SYSTEM76_KUDU,
QUIRK_MODEL_WACOM_TOUCHPAD,
QUIRK_ATTR_SIZE_HINT,
QUIRK_ATTR_TOUCH_SIZE_RANGE,
QUIRK_ATTR_PALM_SIZE_THRESHOLD,
QUIRK_ATTR_LID_SWITCH_RELIABILITY,
QUIRK_ATTR_KEYBOARD_INTEGRATION,
QUIRK_ATTR_TPKBCOMBO_LAYOUT,
QUIRK_ATTR_PRESSURE_RANGE,
QUIRK_ATTR_PALM_PRESSURE_THRESHOLD,
QUIRK_ATTR_RESOLUTION_HINT,
QUIRK_ATTR_TRACKPOINT_RANGE,
};
enum quirk *q;
ARRAY_FOR_EACH(qlist, q) {
if (!quirks_has_quirk(quirks, *q))
continue;
printf("%s\n", quirk_get_name(*q));
}
}
quirks_unref(quirks);
}
static void
usage(void)
{
printf("Usage:\n"
" %s [--data-dir /path/to/data/dir] /dev/input/event0\n"
" Print the quirks for the given device\n"
"\n",
program_invocation_short_name);
printf(" %s [--data-dir /path/to/data/dir] --validate-only\n"
" Validate the database\n",
program_invocation_short_name);
}
int
main(int argc, char **argv)
{
struct udev *udev = NULL;
struct udev_device *device = NULL;
const char *path;
const char *data_path = NULL,
*override_file = NULL;
int rc = 1;
struct quirks_context *quirks;
bool validate = false;
while (1) {
int c;
int option_index = 0;
enum {
OPT_VERBOSE,
OPT_DATADIR,
OPT_VALIDATE,
};
static struct option opts[] = {
{ "help", no_argument, 0, 'h' },
{ "verbose", no_argument, 0, OPT_VERBOSE },
{ "data-dir", required_argument, 0, OPT_DATADIR },
{ "validate-only", no_argument, 0, OPT_VALIDATE },
{ 0, 0, 0, 0}
};
c = getopt_long(argc, argv, "h", opts, &option_index);
if (c == -1)
break;
switch(c) {
case '?':
exit(1);
break;
case 'h':
usage();
exit(0);
break;
case OPT_VERBOSE:
verbose = true;
break;
case OPT_DATADIR:
data_path = optarg;
break;
case OPT_VALIDATE:
validate = true;
break;
default:
usage();
return 1;
}
}
if (optind >= argc && !validate) {
usage();
return 1;
}
/* Overriding the data dir means no custom override file */
if (!data_path) {
data_path = LIBINPUT_DATA_DIR;
override_file = LIBINPUT_DATA_OVERRIDE_FILE;
}
quirks = quirks_init_subsystem(data_path,
override_file,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
if (!quirks) {
fprintf(stderr,
"Failed to initialize the device quirks. "
"Please see the above errors "
"and/or re-run with --verbose for more details\n");
return 1;
}
if (validate) {
rc = 0;
goto out;
}
udev = udev_new();
path = argv[optind];
if (strneq(path, "/sys/", 5)) {
device = udev_device_new_from_syspath(udev, path);
} else {
struct stat st;
if (stat(path, &st) < 0) {
fprintf(stderr, "Error: %s: %m\n", path);
goto out;
}
device = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
}
if (device) {
list_device_quirks(quirks, device);
rc = 0;
} else {
usage();
rc = 1;
}
udev_device_unref(device);
out:
udev_unref(udev);
quirks_context_unref(quirks);
return rc;
}

View file

@ -0,0 +1,36 @@
.TH libinput-list-quirks "1" "" "libinput @LIBINPUT_VERSION@" "libinput Manual"
.SH NAME
libinput\-list\-quirks \- quirk debug helper for libinput
.SH SYNOPSIS
.B libinput list\-quirks [\-\-data\-dir /path/to/dir] [\-\-verbose\fB] \fI/dev/input/event0\fB
.br
.B libinput list\-quirks [\-\-data\-dir /path/to/dir] [\-\-verbose\fB] \-\-validate\-only
.br
.B libinput list\-quirks [\-\-help]
.SH DESCRIPTION
.PP
The
.B "libinput list\-quirks"
tool parses the quirks file in \fIdata\-dir\fR and prints all quirks applied
to the given device.
.PP
This is a debugging tool only, its output and behavior may change at any
time. Do not rely on the output.
.SH OPTIONS
.TP 8
.B \-\-data\-dir \fI/path/to/dir\fR
Use the given directory as data directory for quirks files.
.TP 8
.B \-\-help
Print help
.TP 8
.B \-\-validate\-only
Only validate that the quirks files can be parsed. When this option is
given, no device file should be supplied.
.TP 8
.B \-\-verbose
Use verbose output, useful for debugging.
.SH LIBINPUT
Part of the
.B libinput(1)
suite