forked from drath/libratbag
Switch to a .ini-file based device database
The hwdb was a quick and easy way to add entries for new devices but we need more and more info about the various types of devices. It's better to move this to a proper database. Side-effect: this also gives us an easy way to point to the list of devices supported and makes checking for e.g. missing svgs easier. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
committed by
Benjamin Tissoires
parent
be624fe943
commit
340e5b7d86
33
README.md
33
README.md
@@ -80,33 +80,16 @@ library in the future, if other projects require it.
|
||||
Adding Devices to libratbag
|
||||
---------------------------
|
||||
|
||||
As of commit 3605bede4 libratbag now uses a hwdb entry to match the device
|
||||
with the drivers. To access a device through libratbag, the udev property
|
||||
RATBAG_DRIVER must be set for a device's event node. Check with
|
||||
libratbag relies on a device database to match a device with the drivers.
|
||||
See the data/devices/ directory for the set of known devices. These files
|
||||
are usually installed into $prefix/$datadir (e.g. /usr/share/libratbag/).
|
||||
|
||||
sudo udevadm info /sys/class/input/eventX | grep RATBAG_DRIVER
|
||||
To add a new device to libratbag, simply drop a new device file into that
|
||||
directory and restart ratbagd. See the data/devices/device.example file for
|
||||
guidance on what information must be set.
|
||||
|
||||
If your device is not yet assigned the property, it is not in the hwdb. It
|
||||
may however be supported by one of our existing drivers. Try enabling it by
|
||||
adding the device vendor-id/product-id to the hwdb file in
|
||||
`hwdb/70-libratbag-mouse.hwdb`. For example, for a HID++ 1.0 device, edit
|
||||
the 70-libratbag-mouse.hwdb file and add an entry with `RATBAG_DRIVER=hidpp10`.
|
||||
For the other drivers, look for the id of the driver in driver-{drivername}.c
|
||||
file and do the same.
|
||||
|
||||
Once your device is added to the hwdb, install libratbag and trigger a hwdb
|
||||
update:
|
||||
|
||||
sudo udevadm hwdb --update
|
||||
sudo udevadm control --reload
|
||||
|
||||
Then unplug/replug your mouse. `RATBAG_DRIVER` should appear in the udev
|
||||
properties of your device with the value you previously set. If the property
|
||||
is not assigned, the hwdb entry does not correctly match your device or the
|
||||
installed udev rules/hwdb entries are not picked up by udev.
|
||||
|
||||
If the device doesn't work, you'll have to start reverse-engineering the
|
||||
device-specific protocol. Good luck :)
|
||||
If the device doesn't work after adding the device file, you'll have to
|
||||
start reverse-engineering the device-specific protocol. Good luck :)
|
||||
|
||||
Source
|
||||
------
|
||||
|
||||
@@ -56,7 +56,7 @@ libratbag_references:
|
||||
fedora_install: &fedora_install
|
||||
run:
|
||||
name: Install prerequisites
|
||||
command: dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel doxygen graphviz valgrind python3-gobject python3-evdev
|
||||
command: dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel doxygen graphviz valgrind python3-gobject python3-evdev glib2-devel
|
||||
|
||||
|
||||
fedora_settings: &fedora_settings
|
||||
@@ -81,7 +81,7 @@ ubuntu_settings: &ubuntu_settings
|
||||
apt-get install -y software-properties-common
|
||||
add-apt-repository universe
|
||||
apt-get update
|
||||
apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev doxygen graphviz valgrind python3-gi python3-evdev
|
||||
apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev doxygen graphviz valgrind python3-gi python3-evdev libglib2.0-dev:
|
||||
- checkout
|
||||
- *start_dbus
|
||||
- *build_and_test
|
||||
|
||||
211
data/devices/data-parse-test.py
Executable file
211
data/devices/data-parse-test.py
Executable file
@@ -0,0 +1,211 @@
|
||||
#!/usr/bin/env python3
|
||||
# vim: set expandtab shiftwidth=4 tabstop=4:
|
||||
#
|
||||
# Copyright © 2017 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.
|
||||
#
|
||||
# Device data verification script
|
||||
#
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import configparser
|
||||
|
||||
# Set on commandline with --svg-dir
|
||||
svg_dirs = []
|
||||
|
||||
|
||||
def check_svg_str(string):
|
||||
assert(string.endswith('.svg'))
|
||||
|
||||
svg_file_found = False
|
||||
for svg_dir in svg_dirs:
|
||||
files = os.listdir(svg_dir)
|
||||
if string in files:
|
||||
svg_file_found = True
|
||||
break
|
||||
assert(svg_file_found)
|
||||
|
||||
|
||||
def check_match_str(string):
|
||||
bustypes = ['usb', 'bluetooth']
|
||||
|
||||
matches = string.split(';')
|
||||
for match in matches:
|
||||
if not match: # empty string if trailing ;
|
||||
continue
|
||||
|
||||
parts = match.split(':')
|
||||
assert(len(parts) == 3)
|
||||
assert(parts[0] in bustypes)
|
||||
vid = parts[1]
|
||||
assert(vid == '{:04x}'.format(int(vid, 16)))
|
||||
pid = parts[2]
|
||||
assert(pid == '{:04x}'.format(int(pid, 16)))
|
||||
|
||||
|
||||
def check_section_device(section):
|
||||
required_keys = ['Name', 'Driver', 'DeviceMatch']
|
||||
permitted_keys = required_keys + ['Svg']
|
||||
|
||||
for key in section.keys():
|
||||
assert(key in permitted_keys)
|
||||
|
||||
for r in required_keys:
|
||||
assert(r in section)
|
||||
|
||||
try:
|
||||
check_svg_str(section['Svg'])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
check_match_str(section['DeviceMatch'])
|
||||
|
||||
|
||||
def check_dpi_range_str(string):
|
||||
import re
|
||||
|
||||
m = re.search('^([0-9]+):([0-9]+)@([0-9\.]+)$', string)
|
||||
assert(m is not None)
|
||||
min = int(m.group(1))
|
||||
max = int(m.group(2))
|
||||
steps = float(m.group(3))
|
||||
|
||||
assert(min >= 0 and min <= 400)
|
||||
assert(max >= 2000 and max <= 12000)
|
||||
assert(steps > 0 and steps <= 100)
|
||||
|
||||
if int(steps) == steps:
|
||||
steps = int(steps)
|
||||
|
||||
assert(string == '{}:{}@{}'.format(min, max, steps))
|
||||
|
||||
|
||||
def check_dpi_list_str(string):
|
||||
entries = string.split(';')
|
||||
# Remove possible empty last entry if trailing with a ;
|
||||
if not entries[len(entries) - 1]:
|
||||
entries = entries[:-1]
|
||||
|
||||
for idx, entry in enumerate(entries):
|
||||
dpi = int(entry)
|
||||
assert(dpi >= 0 and dpi <= 12000)
|
||||
if idx > 0:
|
||||
prev = entries[idx - 1]
|
||||
prev_dpi = int(prev)
|
||||
assert(dpi > prev_dpi)
|
||||
|
||||
|
||||
def check_profile_type_str(string):
|
||||
types = ['G9', 'G500', 'G700']
|
||||
assert(string in types)
|
||||
|
||||
|
||||
def check_section_hidpp10(section):
|
||||
permitted = ['Profiles', 'ProfileType', 'DpiRange', 'DpiList', 'DeviceIndex']
|
||||
for key in section.keys():
|
||||
assert(key in permitted)
|
||||
|
||||
try:
|
||||
nprofiles = int(section['Profiles'])
|
||||
# 10 is arbitrarily chosen
|
||||
assert(nprofiles > 0 and nprofiles < 10)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
index = int(section['DeviceIndex'])
|
||||
# 10 is arbitrarily chosen
|
||||
assert(index > 0 and index < 10)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
check_dpi_range_str(section['DpiRange'])
|
||||
assert('DpiList' not in section.keys())
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
check_dpi_list_str(section['DpiList'])
|
||||
assert('DpiRange' not in section.keys())
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
check_profile_type_str(section['ProfileType'])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
def check_section_hidpp20(section):
|
||||
permitted = ['DeviceIndex']
|
||||
for key in section.keys():
|
||||
assert(key in permitted)
|
||||
|
||||
try:
|
||||
index = int(section['DeviceIndex'])
|
||||
# 10 is arbitrarily chosen
|
||||
assert(index > 0 and index < 10)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
def check_section_driver(driver, section):
|
||||
if driver == 'hidpp10':
|
||||
check_section_hidpp10(section)
|
||||
elif driver == 'hidpp20':
|
||||
check_section_hidpp20(section)
|
||||
else:
|
||||
assert('Unsupported driver section {}'.format(driver))
|
||||
|
||||
|
||||
def parse_data_file(path):
|
||||
print('Parsing file {}'.format(path))
|
||||
data = configparser.ConfigParser(strict=True)
|
||||
# Don't convert to lowercase
|
||||
data.optionxform = lambda option: option
|
||||
data.read(path)
|
||||
|
||||
assert('Device' in data.sections())
|
||||
check_section_device(data['Device'])
|
||||
|
||||
driver = data['Device']['Driver']
|
||||
driver_section = 'Driver/{}'.format(driver)
|
||||
|
||||
permitted_sections = ['Device', driver_section]
|
||||
for s in data.sections():
|
||||
assert(s in permitted_sections)
|
||||
|
||||
if data.has_section(driver_section):
|
||||
check_section_driver(driver, data[driver_section])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Device data-file checker")
|
||||
parser.add_argument('file', nargs='+')
|
||||
parser.add_argument('--svg-dir', metavar='dir', action='append',
|
||||
type=str,
|
||||
help='Directory to check for SVG files (may be given multiple times)')
|
||||
args = parser.parse_args()
|
||||
svg_dirs = args.svg_dir
|
||||
for path in args.file:
|
||||
parse_data_file(path)
|
||||
49
data/devices/device.example
Normal file
49
data/devices/device.example
Normal file
@@ -0,0 +1,49 @@
|
||||
# Example description file for a libratbag device
|
||||
#
|
||||
# Make sure the file has a recognizable name with a '.device' suffix
|
||||
|
||||
[Device]
|
||||
# The kernel name of the device. This name is not used by libratbag, it is
|
||||
# used by debugging tools.
|
||||
Name=Logitech Example Device
|
||||
|
||||
# DeviceMatch is a triplet of bus:vid:pid, where bus is 'usb' or bluetooth'
|
||||
# and pid/vid are lowercase 4-digit hexadecimal numbers with preceding
|
||||
# zeroes.
|
||||
DeviceMatch=usb:12ab:00f2
|
||||
|
||||
# The file name of the SVG file representing this device
|
||||
Svg=logitech-example.svg
|
||||
|
||||
# The libratbag driver backend
|
||||
Driver=hidpp10
|
||||
|
||||
# Only one group of driver-specific properties is permitted and it must be
|
||||
# [Driver/<drivername>]. It is a bug do have any other [Driver/foo] group in
|
||||
# a device file, but this example file lists all.
|
||||
|
||||
[Driver/hidpp10]
|
||||
# The number of profiles, must be greater than 0
|
||||
Profiles=1
|
||||
|
||||
# The type of the profile
|
||||
ProfileType=G9
|
||||
|
||||
# The range of available resolutions in the form min:max@step
|
||||
# Mutually exclusive with DpiList
|
||||
DpiRange=50:5700@50
|
||||
|
||||
# The list of available resolutions, separated by semicolons.
|
||||
# Mutually exclusive with DpiRange
|
||||
# Where possible, DpiRange should be preferred
|
||||
DpiList=50;200;400;800
|
||||
|
||||
# The HID++ 1.0 device index
|
||||
# Should be unset unless required
|
||||
# DeviceIndex=1
|
||||
|
||||
|
||||
[Driver/hidpp20]
|
||||
# The HID++ 2.0 device index
|
||||
# Should be unset unless required
|
||||
# DeviceIndex=1
|
||||
6
data/devices/etekcity-fixme.device
Normal file
6
data/devices/etekcity-fixme.device
Normal file
@@ -0,0 +1,6 @@
|
||||
# FIXME: which device is this exactly?
|
||||
[Device]
|
||||
Name=Etekcity FIXME
|
||||
DeviceMatch=usb:1ea7:4011
|
||||
Driver=etekcity
|
||||
Svg=etekcity.svg
|
||||
5
data/devices/gskill-MX-780.device
Normal file
5
data/devices/gskill-MX-780.device
Normal file
@@ -0,0 +1,5 @@
|
||||
# G.Skill MX-780
|
||||
[Device]
|
||||
Name=G.Skill MX-780
|
||||
DeviceMatch=usb:28da:3101
|
||||
Driver=gskill
|
||||
5
data/devices/logitech-M325.device
Normal file
5
data/devices/logitech-M325.device
Normal file
@@ -0,0 +1,5 @@
|
||||
# Logitech M325 over unifying
|
||||
[Device]
|
||||
Name=Logitech M325
|
||||
DeviceMatch=usb:046d:400a
|
||||
Driver=hidpp20
|
||||
5
data/devices/logitech-M570.device
Normal file
5
data/devices/logitech-M570.device
Normal file
@@ -0,0 +1,5 @@
|
||||
# Logitech M570
|
||||
[Device]
|
||||
Name=Logitech M570
|
||||
DeviceMatch=usb:046d:1028
|
||||
Driver=hidpp10
|
||||
5
data/devices/logitech-M705.device
Normal file
5
data/devices/logitech-M705.device
Normal file
@@ -0,0 +1,5 @@
|
||||
# Logitech M705
|
||||
[Device]
|
||||
Name=Logitech M705
|
||||
DeviceMatch=usb:046d:101b
|
||||
Driver=hidpp10
|
||||
6
data/devices/logitech-MX-Master.device
Normal file
6
data/devices/logitech-MX-Master.device
Normal file
@@ -0,0 +1,6 @@
|
||||
# Logitech MX Master
|
||||
[Device]
|
||||
Name=Logitech MX Master
|
||||
DeviceMatch=usb:046d:4041;bluetooth:046d:b012
|
||||
Driver=hidpp20
|
||||
Svg=logitech-mx_master.svg
|
||||
5
data/devices/logitech-T650.device
Normal file
5
data/devices/logitech-T650.device
Normal file
@@ -0,0 +1,5 @@
|
||||
# Logitech T650 over unifying
|
||||
[Device]
|
||||
Name=Logitech T650
|
||||
DeviceMatch=usb:046d:4101
|
||||
Driver=hidpp20
|
||||
5
data/devices/logitech-Wireless-Touchpad.device
Normal file
5
data/devices/logitech-Wireless-Touchpad.device
Normal file
@@ -0,0 +1,5 @@
|
||||
# Logitech Wireless Touchpad (Unifying)
|
||||
[Device]
|
||||
Name=Logitech Wireless Touchpad
|
||||
DeviceMatch=usb:046d:4011
|
||||
Driver=hidpp20
|
||||
6
data/devices/logitech-g300.device
Normal file
6
data/devices/logitech-g300.device
Normal file
@@ -0,0 +1,6 @@
|
||||
# WARNING: this file was automatically converted and has not yet been vetted
|
||||
[Device]
|
||||
Name=Logitech G300
|
||||
DeviceMatch=usb:046d:c246
|
||||
Driver=logitech_g300
|
||||
Svg=logitech-g300.svg
|
||||
6
data/devices/logitech-g303.device
Normal file
6
data/devices/logitech-g303.device
Normal file
@@ -0,0 +1,6 @@
|
||||
# G303 over USB
|
||||
[Device]
|
||||
Name=Logitech Gaming Mouse G303
|
||||
DeviceMatch=usb:046d:c080
|
||||
Svg=logitech-g303.svg
|
||||
Driver=hidpp20
|
||||
6
data/devices/logitech-g403.device
Normal file
6
data/devices/logitech-g403.device
Normal file
@@ -0,0 +1,6 @@
|
||||
# Logitech G403 over USB
|
||||
[Device]
|
||||
Name=Logitech G403
|
||||
DeviceMatch=usb:046d:c082
|
||||
Driver=hidpp20
|
||||
Svg=logitech-g403.svg
|
||||
9
data/devices/logitech-g5-2007.device
Normal file
9
data/devices/logitech-g5-2007.device
Normal file
@@ -0,0 +1,9 @@
|
||||
# Name=Logitech G5 2007
|
||||
[Device]
|
||||
Name=Logitech G5
|
||||
DeviceMatch=usb:046d:c049
|
||||
Driver=hidpp10
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiList=400;800;1600;2000
|
||||
Profiles=1
|
||||
7
data/devices/logitech-g5.device
Normal file
7
data/devices/logitech-g5.device
Normal file
@@ -0,0 +1,7 @@
|
||||
[Device]
|
||||
Name=G5
|
||||
DeviceMatch=usb:046d:c041
|
||||
Driver=hidpp10
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiList=400;800;1600;2000
|
||||
10
data/devices/logitech-g500.device
Normal file
10
data/devices/logitech-g500.device
Normal file
@@ -0,0 +1,10 @@
|
||||
[Device]
|
||||
Name=Logitech G500
|
||||
DeviceMatch=usb:046d:c068
|
||||
Driver=hidpp10
|
||||
Svg=logitech-g500.svg
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiRange=0:5700@23.53
|
||||
ProfileType=G500
|
||||
Profiles=1
|
||||
10
data/devices/logitech-g500s.device
Normal file
10
data/devices/logitech-g500s.device
Normal file
@@ -0,0 +1,10 @@
|
||||
[Device]
|
||||
Name=Logitech G500s
|
||||
DeviceMatch=usb:046d:c24e
|
||||
Driver=hidpp10
|
||||
Svg=logitech-g500s.svg
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiRange=0:8200@50
|
||||
ProfileType=G500
|
||||
Profiles=1
|
||||
6
data/devices/logitech-g502-Proteus-Core-over-USB.device
Normal file
6
data/devices/logitech-g502-Proteus-Core-over-USB.device
Normal file
@@ -0,0 +1,6 @@
|
||||
# Logitech G502 Proteus Core over USB
|
||||
[Device]
|
||||
Name=Logitech G502 Proteus Core
|
||||
DeviceMatch=usb:046d:c07d
|
||||
Driver=hidpp20
|
||||
Svg=logitech-g502.svg
|
||||
@@ -0,0 +1,6 @@
|
||||
# Logitech G502 Proteus Spectrum over USB
|
||||
[Device]
|
||||
Name=Logitech G502 Proteus Spectrum
|
||||
DeviceMatch=usb:046d:c332
|
||||
Driver=hidpp20
|
||||
Svg=logitech-g502.svg
|
||||
8
data/devices/logitech-g602.device
Normal file
8
data/devices/logitech-g602.device
Normal file
@@ -0,0 +1,8 @@
|
||||
# Logitech G602 over wireless USB
|
||||
[Device]
|
||||
Name=Logitech G602
|
||||
DeviceMatch=usb:046d:c537
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
DeviceIndex=1
|
||||
9
data/devices/logitech-g7.device
Normal file
9
data/devices/logitech-g7.device
Normal file
@@ -0,0 +1,9 @@
|
||||
# Logitech G7
|
||||
# http://support.logitech.com/en_us/product/g7-laser-cordless-mouse/specs
|
||||
[Device]
|
||||
Name=Logitech G7
|
||||
DeviceMatch=usb:046d:c51a
|
||||
Driver=hidpp10
|
||||
|
||||
[Driver/hidpp10]
|
||||
Profiles=1
|
||||
12
data/devices/logitech-g700-wireless.device
Normal file
12
data/devices/logitech-g700-wireless.device
Normal file
@@ -0,0 +1,12 @@
|
||||
# Logitech G700 over wireless USB
|
||||
# http://support.logitech.com/en_us/product/wireless-gaming-mouse-g700/specs
|
||||
[Device]
|
||||
Name=Logitech G700
|
||||
DeviceMatch=usb:046d:c531
|
||||
Driver=hidpp10
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiRange=0:5700@23.53
|
||||
ProfileType=G700
|
||||
DeviceIndex=1
|
||||
Profiles=5
|
||||
12
data/devices/logitech-g700.device
Normal file
12
data/devices/logitech-g700.device
Normal file
@@ -0,0 +1,12 @@
|
||||
# Logitech G700 over USB
|
||||
# http://support.logitech.com/en_us/product/wireless-gaming-mouse-g700/specs
|
||||
[Device]
|
||||
Name=Logitech G700
|
||||
DeviceMatch=usb:046d:c06b
|
||||
Driver=hidpp10
|
||||
Svg=logitech-g700.svg
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiRange=0:5700@23.53
|
||||
ProfileType=G700
|
||||
Profiles=5
|
||||
13
data/devices/logitech-g700s-wireless.device
Normal file
13
data/devices/logitech-g700s-wireless.device
Normal file
@@ -0,0 +1,13 @@
|
||||
# Logitech G700s over wireless USB
|
||||
# http://support.logitech.com/en_us/product/g700s-rechargable-wireless-gaming-mouse/specs
|
||||
[Device]
|
||||
Name=Logitech G700s
|
||||
DeviceMatch=usb:046d:c531
|
||||
Driver=hidpp10
|
||||
Svg=logitech-g700.svg
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiRange=0:8200@50
|
||||
ProfileType=G700
|
||||
DeviceIndex=1
|
||||
Profiles=5
|
||||
12
data/devices/logitech-g700s.device
Normal file
12
data/devices/logitech-g700s.device
Normal file
@@ -0,0 +1,12 @@
|
||||
# Logitech G700s over USB
|
||||
# http://support.logitech.com/en_us/product/g700s-rechargable-wireless-gaming-mouse/specs
|
||||
[Device]
|
||||
Name=Logitech G700s
|
||||
DeviceMatch=usb:046d:c07c
|
||||
Driver=hidpp10
|
||||
Svg=logitech-g700.svg
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiRange=0:8200@50
|
||||
ProfileType=G700
|
||||
Profiles=5
|
||||
11
data/devices/logitech-g9.device
Normal file
11
data/devices/logitech-g9.device
Normal file
@@ -0,0 +1,11 @@
|
||||
# Logitech G9
|
||||
# http://support.logitech.com/en_us/product/g9-laser-mouse/specs
|
||||
[Device]
|
||||
Name=Logitech G9
|
||||
DeviceMatch=usb:046d:c048
|
||||
Driver=hidpp10
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiList=0;200;400;600;800;1000;1200;1400;1600;1800;2000;2200;2400;2600;2800;3000;3200
|
||||
ProfileType=G9
|
||||
Profiles=5
|
||||
9
data/devices/logitech-g900-wireless.device
Normal file
9
data/devices/logitech-g900-wireless.device
Normal file
@@ -0,0 +1,9 @@
|
||||
# Logitech G900 and G403 over wireless USB
|
||||
[Device]
|
||||
Name=Logitech G900, G403
|
||||
DeviceMatch=usb:046d:c539
|
||||
Driver=hidpp20
|
||||
Svg=logitech-g900.svg
|
||||
|
||||
[Driver/hidpp20]
|
||||
DeviceIndex=1
|
||||
6
data/devices/logitech-g900.device
Normal file
6
data/devices/logitech-g900.device
Normal file
@@ -0,0 +1,6 @@
|
||||
# Logitech G900 over USB
|
||||
[Device]
|
||||
Name=Logitech G900
|
||||
DeviceMatch=usb:046d:c081
|
||||
Driver=hidpp20
|
||||
Svg=logitech-g900.svg
|
||||
11
data/devices/logitech-g9x-[Call-of-Duty-MW3-Edition].device
Normal file
11
data/devices/logitech-g9x-[Call-of-Duty-MW3-Edition].device
Normal file
@@ -0,0 +1,11 @@
|
||||
# Logitech G9x [Call of Duty MW3 Edition]
|
||||
# http://support.logitech.com/en_us/product/laser-mouse-g9x/specs
|
||||
[Device]
|
||||
Name=Logitech G9x [Call of Duty MW3 Edition]
|
||||
DeviceMatch=usb:046d:c249
|
||||
Driver=hidpp10
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiRange=0:5700@23.53
|
||||
ProfileType=G500
|
||||
Profiles=5
|
||||
11
data/devices/logitech-g9x-[Original].device
Normal file
11
data/devices/logitech-g9x-[Original].device
Normal file
@@ -0,0 +1,11 @@
|
||||
# Logitech G9x [Original]
|
||||
# http://support.logitech.com/en_us/product/g9x-laser-mouse/specs
|
||||
[Device]
|
||||
Name=G9x [Original]
|
||||
DeviceMatch=usb:046d:c066
|
||||
Driver=hidpp10
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiRange=0:5700@23.53
|
||||
ProfileType=G500
|
||||
Profiles=5
|
||||
5
data/devices/roccat-kone-xtd.device
Normal file
5
data/devices/roccat-kone-xtd.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Roccat Kone XTD
|
||||
DeviceMatch=usb:1e7d:2e22
|
||||
Driver=roccat
|
||||
Svg=roccat-kone-xtd.svg
|
||||
54
meson.build
54
meson.build
@@ -48,6 +48,7 @@ dep_udev = dependency('libudev')
|
||||
dep_libevdev = dependency('libevdev')
|
||||
dep_libsystemd = dependency('libsystemd', version : '>=227')
|
||||
dep_systemd = dependency('systemd')
|
||||
dep_glib = dependency('glib-2.0')
|
||||
dep_lm = cc.find_library('m')
|
||||
|
||||
#### libutil.a ####
|
||||
@@ -131,6 +132,8 @@ src_libratbag = [
|
||||
'src/driver-test.c',
|
||||
'src/libratbag.c',
|
||||
'src/libratbag.h',
|
||||
'src/libratbag-data.c',
|
||||
'src/libratbag-data.h',
|
||||
'src/libratbag-hidraw.c',
|
||||
'src/libratbag-hidraw.h',
|
||||
'src/libratbag-private.h',
|
||||
@@ -142,6 +145,7 @@ src_libratbag = [
|
||||
deps_libratbag = [
|
||||
dep_udev,
|
||||
dep_libevdev,
|
||||
dep_glib,
|
||||
dep_libutil,
|
||||
dep_libhidpp,
|
||||
]
|
||||
@@ -245,18 +249,46 @@ man_lur_command = configure_file (
|
||||
install_dir : join_paths(get_option('mandir'), 'man1')
|
||||
)
|
||||
|
||||
#### hwdb ####
|
||||
udev_dir = get_option('udev-dir')
|
||||
if udev_dir == ''
|
||||
udev_dir = '@0@/lib/udev'.format(get_option('prefix'))
|
||||
endif
|
||||
udev_rules_dir = '@0@/rules.d'.format(udev_dir)
|
||||
udev_hwdb_dir = '@0@/hwdb.d'.format(udev_dir)
|
||||
#### data files ####
|
||||
data_files = files(
|
||||
'data/devices/etekcity-fixme.device',
|
||||
'data/devices/gskill-MX-780.device',
|
||||
'data/devices/logitech-g300.device',
|
||||
'data/devices/logitech-g303.device',
|
||||
'data/devices/logitech-g403.device',
|
||||
'data/devices/logitech-g500.device',
|
||||
'data/devices/logitech-g500s.device',
|
||||
'data/devices/logitech-g502-Proteus-Core-over-USB.device',
|
||||
'data/devices/logitech-g502-Proteus-Spectrum-over-USB.device',
|
||||
'data/devices/logitech-g5-2007.device',
|
||||
'data/devices/logitech-g5.device',
|
||||
'data/devices/logitech-g602.device',
|
||||
'data/devices/logitech-g700.device',
|
||||
'data/devices/logitech-g700s.device',
|
||||
'data/devices/logitech-g700s-wireless.device',
|
||||
'data/devices/logitech-g700-wireless.device',
|
||||
'data/devices/logitech-g7.device',
|
||||
'data/devices/logitech-g900.device',
|
||||
'data/devices/logitech-g900-wireless.device',
|
||||
'data/devices/logitech-g9.device',
|
||||
'data/devices/logitech-g9x-[Call-of-Duty-MW3-Edition].device',
|
||||
'data/devices/logitech-g9x-[Original].device',
|
||||
'data/devices/logitech-M325.device',
|
||||
'data/devices/logitech-M570.device',
|
||||
'data/devices/logitech-M705.device',
|
||||
'data/devices/logitech-MX-Master.device',
|
||||
'data/devices/logitech-T650.device',
|
||||
'data/devices/logitech-Wireless-Touchpad.device',
|
||||
'data/devices/roccat-kone-xtd.device',
|
||||
)
|
||||
|
||||
install_data('hwdb/70-libratbag-mouse.rules',
|
||||
install_dir : udev_rules_dir)
|
||||
install_data('hwdb/70-libratbag-mouse.hwdb',
|
||||
install_dir : udev_hwdb_dir)
|
||||
install_data(data_files,
|
||||
install_dir : join_paths(get_option('datadir'), 'libratbag'))
|
||||
|
||||
data_parse_test = find_program(join_paths(meson.source_root(), 'data/devices/data-parse-test.py'))
|
||||
test('data-parse-test', data_parse_test,
|
||||
args : [ '--svg-dir', join_paths(meson.source_root(), 'data/default'),
|
||||
'--svg-dir', join_paths(meson.source_root(), 'data/gnome') ] + data_files)
|
||||
|
||||
#### svg files ####
|
||||
default_svg_files = [
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
|
||||
#include "libratbag-private.h"
|
||||
#include "libratbag-hidraw.h"
|
||||
#include "libratbag-data.h"
|
||||
|
||||
struct hidpp10drv_data {
|
||||
struct hidpp10_device *dev;
|
||||
@@ -555,9 +556,8 @@ hidpp10drv_probe(struct ratbag_device *device)
|
||||
struct hidpp10_device *dev = NULL;
|
||||
struct hidpp_device base;
|
||||
enum hidpp10_profile_type type = HIDPP10_PROFILE_UNKNOWN;
|
||||
const char *prop;
|
||||
const char *profile;
|
||||
int device_idx = HIDPP_WIRED_DEVICE_IDX;
|
||||
int nread = 0;
|
||||
unsigned int profile_count = 1;
|
||||
|
||||
rc = ratbag_find_hidraw(device, hidpp10drv_test_hidraw);
|
||||
@@ -568,31 +568,22 @@ hidpp10drv_probe(struct ratbag_device *device)
|
||||
hidpp_device_init(&base, device->hidraw.fd);
|
||||
hidpp_device_set_log_handler(&base, hidpp10_log, HIDPP_LOG_PRIORITY_RAW, device);
|
||||
|
||||
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_PROFILE");
|
||||
if (prop) {
|
||||
if (strcasecmp("G500", prop) == 0)
|
||||
profile = ratbag_device_data_hidpp10_get_profile_type(device->data);
|
||||
if (profile) {
|
||||
if (strcasecmp("G500", profile) == 0)
|
||||
type = HIDPP10_PROFILE_G500;
|
||||
else if (strcasecmp("G700", prop) == 0)
|
||||
else if (strcasecmp("G700", profile) == 0)
|
||||
type = HIDPP10_PROFILE_G700;
|
||||
else if (strcasecmp("G9", prop) == 0)
|
||||
else if (strcasecmp("G9", profile) == 0)
|
||||
type = HIDPP10_PROFILE_G9;
|
||||
|
||||
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_PROFILE_COUNT");
|
||||
if (prop)
|
||||
profile_count = atoi(prop);
|
||||
profile_count = ratbag_device_data_hidpp10_get_profile_count(device->data);
|
||||
}
|
||||
|
||||
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_INDEX");
|
||||
if (prop) {
|
||||
sscanf(prop, "%d%n", &device_idx, &nread);
|
||||
if (!nread || (prop[nread]) != '\0' || device_idx < 0) {
|
||||
log_error(device->ratbag,
|
||||
"Error parsing RATBAG_HIDPP10_INDEX: '%s' for %s\n",
|
||||
prop,
|
||||
device->name);
|
||||
device_idx = HIDPP_WIRED_DEVICE_IDX;
|
||||
}
|
||||
}
|
||||
device_idx = ratbag_device_data_hidpp10_get_index(device->data);
|
||||
if (device_idx == -1)
|
||||
device_idx = HIDPP_WIRED_DEVICE_IDX;
|
||||
|
||||
/* In the general case, we can treat all devices as wired devices
|
||||
* here. If we talk to the correct hidraw device the kernel adjusts
|
||||
* the device index for us, so even for unifying receiver devices
|
||||
@@ -611,23 +602,24 @@ hidpp10drv_probe(struct ratbag_device *device)
|
||||
}
|
||||
|
||||
if (type != HIDPP10_PROFILE_UNKNOWN) {
|
||||
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_DPI");
|
||||
if (prop) {
|
||||
rc = hidpp10_build_dpi_table_from_dpi_info(dev, prop);
|
||||
_cleanup_(dpi_list_freep) struct dpi_list *list = NULL;
|
||||
_cleanup_(freep) struct dpi_range *range = NULL;
|
||||
|
||||
range = ratbag_device_data_hidpp10_get_dpi_range(device->data);
|
||||
if (range) {
|
||||
rc = hidpp10_build_dpi_table_from_dpi_info(dev, range);
|
||||
if (rc)
|
||||
log_error(device->ratbag,
|
||||
"Error parsing RATBAG_HIDPP10_DPI: '%s' for %s\n",
|
||||
prop,
|
||||
"Error parsing DpiRange for %s\n",
|
||||
device->name);
|
||||
}
|
||||
|
||||
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_DPI_LIST");
|
||||
if (prop) {
|
||||
rc = hidpp10_build_dpi_table_from_list(dev, prop);
|
||||
list = ratbag_device_data_hidpp10_get_dpi_list(device->data);
|
||||
if (list) {
|
||||
rc = hidpp10_build_dpi_table_from_list(dev, list);
|
||||
if (rc)
|
||||
log_error(device->ratbag,
|
||||
"Error parsing RATBAG_HIDPP10_DPI_LIST: '%s' for %s\n",
|
||||
prop,
|
||||
"Error parsing DpiList for %s\n",
|
||||
device->name);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
|
||||
#include "libratbag-private.h"
|
||||
#include "libratbag-hidraw.h"
|
||||
#include "libratbag-data.h"
|
||||
|
||||
#define HIDPP_CAP_RESOLUTION_2200 (1 << 0)
|
||||
#define HIDPP_CAP_SWITCHABLE_RESOLUTION_2201 (1 << 1)
|
||||
@@ -1014,9 +1015,7 @@ hidpp20drv_probe(struct ratbag_device *device)
|
||||
struct hidpp20drv_data *drv_data;
|
||||
struct hidpp_device base;
|
||||
struct hidpp20_device *dev;
|
||||
const char *prop;
|
||||
int device_idx = HIDPP_RECEIVER_IDX;
|
||||
int nread = 0;
|
||||
|
||||
rc = ratbag_find_hidraw(device, hidpp20drv_test_hidraw);
|
||||
if (rc)
|
||||
@@ -1027,25 +1026,17 @@ hidpp20drv_probe(struct ratbag_device *device)
|
||||
hidpp_device_init(&base, device->hidraw.fd);
|
||||
hidpp_device_set_log_handler(&base, hidpp20_log, HIDPP_LOG_PRIORITY_RAW, device);
|
||||
|
||||
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP20_INDEX");
|
||||
if (prop) {
|
||||
sscanf(prop, "%d%n", &device_idx, &nread);
|
||||
if (!nread || (prop[nread]) != '\0' || device_idx < 0) {
|
||||
log_error(device->ratbag,
|
||||
"Error parsing RATBAG_HIDPP20_INDEX: '%s' for %s\n",
|
||||
prop,
|
||||
device->name);
|
||||
device_idx = HIDPP_RECEIVER_IDX;
|
||||
}
|
||||
}
|
||||
device_idx = ratbag_device_data_hidpp20_get_index(device->data);
|
||||
if (device_idx == -1)
|
||||
device_idx = HIDPP_RECEIVER_IDX;
|
||||
|
||||
/* In the general case, we can treat all devices as wired devices
|
||||
* here. If we talk to the correct hidraw device the kernel adjusts
|
||||
* the device index for us, so even for unifying receiver devices
|
||||
* we can just use 0xff as device index.
|
||||
*
|
||||
* If there is a special need like for G900, we can pass a
|
||||
* udev prop RATBAG_HIDPP20_INDEX.
|
||||
* If there is a special need like for G900, we can add this in the
|
||||
* device data file.
|
||||
*/
|
||||
dev = hidpp20_device_new(&base, device_idx);
|
||||
if (!dev) {
|
||||
|
||||
@@ -114,20 +114,10 @@ hidpp10_dpi_table_get_min_dpi(struct hidpp10_device *dev)
|
||||
|
||||
int
|
||||
hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev,
|
||||
const char *str_list)
|
||||
const struct dpi_list *list)
|
||||
{
|
||||
_cleanup_(dpi_list_freep) struct dpi_list *list;
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* str_list is in the form:
|
||||
* "0;200;400;600;800;1000;1200"
|
||||
*/
|
||||
|
||||
list = dpi_list_from_string(str_list);
|
||||
if (!list)
|
||||
goto err;
|
||||
|
||||
if (list->nentries + 0x80 - 1> 0xff)
|
||||
goto err;
|
||||
|
||||
@@ -149,19 +139,10 @@ err:
|
||||
|
||||
int
|
||||
hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev,
|
||||
const char *str_dpi)
|
||||
const struct dpi_range *range)
|
||||
{
|
||||
unsigned raw_max, i;
|
||||
_cleanup_(freep) struct dpi_range *range = NULL;
|
||||
|
||||
/*
|
||||
* str_list is in the form:
|
||||
* "MIN:MAX@STEP"
|
||||
*/
|
||||
|
||||
range = dpi_range_from_string(str_dpi);
|
||||
if (!range)
|
||||
return -EINVAL;
|
||||
|
||||
raw_max = (range->max - range->min) / range->step;
|
||||
if (raw_max > 0xff)
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
|
||||
#include "hidpp-generic.h"
|
||||
|
||||
struct dpi_list;
|
||||
struct dpi_range;
|
||||
|
||||
/* FIXME: that's what my G500s supports, but only pages 3-5 are valid.
|
||||
* 0 is zeroed, 1 and 2 are garbage, all above 6 is garbage */
|
||||
#define HIDPP10_NUM_PROFILES 5
|
||||
@@ -88,7 +91,7 @@ hidpp10_device_destroy(struct hidpp10_device *dev);
|
||||
*/
|
||||
int
|
||||
hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev,
|
||||
const char *str_list);
|
||||
const struct dpi_list *list);
|
||||
|
||||
/**
|
||||
* Builds the table of dpi for the device from the given dpi description.
|
||||
@@ -103,7 +106,7 @@ hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev,
|
||||
*/
|
||||
int
|
||||
hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev,
|
||||
const char *str_dpi);
|
||||
const struct dpi_range *range);
|
||||
|
||||
/**
|
||||
* Returns the lowest dpi stored in the dpi mapping table generated by
|
||||
|
||||
405
src/libratbag-data.c
Normal file
405
src/libratbag-data.c
Normal file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* Copyright © 2017 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 <linux/input.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "libratbag.h"
|
||||
#include "libratbag-private.h"
|
||||
#include "libratbag-data.h"
|
||||
|
||||
#define GROUP_DEVICE "Device"
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(GKeyFile *, g_key_file_free);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(GError *, g_error_free);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char **, g_strfreev);
|
||||
|
||||
enum driver {
|
||||
NONE = 0,
|
||||
HIDPP10,
|
||||
HIDPP20,
|
||||
ROCCAT,
|
||||
ETEKCITY,
|
||||
GSKILL,
|
||||
LOGITECH_G300
|
||||
};
|
||||
|
||||
struct data_hidpp20 {
|
||||
int index;
|
||||
};
|
||||
|
||||
struct data_hidpp10 {
|
||||
int index;
|
||||
int profile_count;
|
||||
char *profile_type;
|
||||
|
||||
struct dpi_list *dpi_list;
|
||||
struct dpi_range *dpi_range;
|
||||
};
|
||||
|
||||
struct ratbag_device_data {
|
||||
int refcount;
|
||||
char *name;
|
||||
char *driver;
|
||||
char *svg;
|
||||
|
||||
enum driver drivertype;
|
||||
|
||||
union {
|
||||
struct data_hidpp20 hidpp20;
|
||||
struct data_hidpp10 hidpp10;
|
||||
};
|
||||
};
|
||||
|
||||
static void
|
||||
init_data_hidpp10(struct ratbag *ratbag,
|
||||
GKeyFile *keyfile,
|
||||
struct ratbag_device_data *data)
|
||||
{
|
||||
const char *group = "Driver/hidpp10";
|
||||
char *profile_type;
|
||||
GError *error = NULL;
|
||||
_cleanup_(freep) char *str = NULL;
|
||||
int num;
|
||||
|
||||
data->hidpp10.index = -1;
|
||||
data->hidpp10.profile_count = -1;
|
||||
data->hidpp10.profile_type = NULL;
|
||||
|
||||
num = g_key_file_get_integer(keyfile, group, "DeviceIndex", &error);
|
||||
if (num != 0 || !error)
|
||||
data->hidpp10.index = num;
|
||||
if (error)
|
||||
g_error_free(error);
|
||||
|
||||
num = g_key_file_get_integer(keyfile, group, "Profiles", &error);
|
||||
if (num > 0 || !error)
|
||||
data->hidpp10.profile_count = num;
|
||||
if (error)
|
||||
g_error_free(error);
|
||||
|
||||
profile_type = g_key_file_get_string(keyfile, group, "ProfileType", NULL);
|
||||
if (profile_type)
|
||||
data->hidpp10.profile_type = profile_type;
|
||||
|
||||
str = g_key_file_get_string(keyfile, group, "DpiRange", NULL);
|
||||
if (str) {
|
||||
data->hidpp10.dpi_range = dpi_range_from_string(str);
|
||||
} else {
|
||||
str = g_key_file_get_string(keyfile, group, "DpiList", NULL);
|
||||
if (str)
|
||||
data->hidpp10.dpi_list = dpi_list_from_string(str);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
init_data_hidpp20(struct ratbag *ratbag,
|
||||
GKeyFile *keyfile,
|
||||
struct ratbag_device_data *data)
|
||||
{
|
||||
const char *group = "Driver/hidpp20";
|
||||
GError *error = NULL;
|
||||
int num;
|
||||
|
||||
data->hidpp20.index = -1;
|
||||
|
||||
num = g_key_file_get_integer(keyfile, group, "DeviceIndex", &error);
|
||||
if (num != 0 || !error)
|
||||
data->hidpp20.index = num;
|
||||
if (error)
|
||||
g_error_free(error);
|
||||
}
|
||||
|
||||
static const struct driver_map {
|
||||
enum driver map;
|
||||
const char *driver;
|
||||
void (*init)(struct ratbag *ratbag,
|
||||
GKeyFile *keyfile,
|
||||
struct ratbag_device_data *data);
|
||||
} driver_map[] = {
|
||||
{ HIDPP10, "hidpp10", init_data_hidpp10 },
|
||||
{ HIDPP20, "hidpp20", init_data_hidpp20 },
|
||||
{ ROCCAT, "roccat", NULL },
|
||||
{ ETEKCITY, "etekcity", NULL},
|
||||
{ GSKILL, "gskill", NULL },
|
||||
{ LOGITECH_G300, "logitech_g300", NULL},
|
||||
};
|
||||
|
||||
const char *
|
||||
ratbag_device_data_get_driver(const struct ratbag_device_data *data)
|
||||
{
|
||||
return data->driver;
|
||||
}
|
||||
|
||||
const char *
|
||||
ratbag_device_data_get_name(const struct ratbag_device_data *data)
|
||||
{
|
||||
return data->name;
|
||||
}
|
||||
|
||||
const char *
|
||||
ratbag_device_data_get_svg(const struct ratbag_device_data *data)
|
||||
{
|
||||
return data->svg;
|
||||
}
|
||||
|
||||
struct ratbag_device_data *
|
||||
ratbag_device_data_ref(struct ratbag_device_data *data)
|
||||
{
|
||||
data->refcount++;
|
||||
return data;
|
||||
}
|
||||
|
||||
static void
|
||||
ratbag_device_data_destroy(struct ratbag_device_data *data)
|
||||
{
|
||||
switch (data->drivertype) {
|
||||
case HIDPP10:
|
||||
free(data->hidpp10.profile_type);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
free(data->name);
|
||||
free(data->driver);
|
||||
free(data->svg);
|
||||
free(data);
|
||||
}
|
||||
|
||||
struct ratbag_device_data *
|
||||
ratbag_device_data_unref(struct ratbag_device_data *data)
|
||||
{
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
assert(data->refcount > 0);
|
||||
data->refcount--;
|
||||
|
||||
if (data->refcount == 0)
|
||||
ratbag_device_data_destroy(data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbag_device_data *, ratbag_device_data_unref);
|
||||
|
||||
static bool
|
||||
match(const struct input_id *id, char **strv)
|
||||
{
|
||||
const char *bus;
|
||||
char str[64];
|
||||
int i = 0;
|
||||
|
||||
switch (id->bustype) {
|
||||
case BUS_USB: bus = "usb"; break;
|
||||
case BUS_BLUETOOTH: bus = "bluetooth"; break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
snprintf(str, sizeof(str), "%s:%04x:%04x", bus, id->vendor, id->product);
|
||||
|
||||
while (strv[i]) {
|
||||
if (streq(strv[i], str))
|
||||
return true;
|
||||
i++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
file_data_matches(struct ratbag *ratbag,
|
||||
const char *path, const struct input_id *id,
|
||||
struct ratbag_device_data **data_out)
|
||||
{
|
||||
_cleanup_(g_key_file_freep) GKeyFile *keyfile = NULL;
|
||||
_cleanup_(g_error_freep) GError *error = NULL;
|
||||
_cleanup_(g_strfreevp) char **strv = NULL;
|
||||
_cleanup_(ratbag_device_data_unrefp) struct ratbag_device_data *data = NULL;
|
||||
int rc;
|
||||
|
||||
keyfile = g_key_file_new();
|
||||
rc = g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, &error);
|
||||
if (!rc) {
|
||||
log_error(ratbag, "Failed to parse keyfile %s: %s\n", path, error->message);
|
||||
return false;
|
||||
}
|
||||
|
||||
strv = g_key_file_get_string_list(keyfile, GROUP_DEVICE, "DeviceMatch", NULL, NULL);
|
||||
if (!strv) {
|
||||
log_error(ratbag, "Missing DeviceMatch in %s\n", basename(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!match(id, strv))
|
||||
return false;
|
||||
|
||||
data = zalloc(sizeof(*data));
|
||||
data->refcount = 1;
|
||||
data->name = g_key_file_get_string(keyfile, GROUP_DEVICE, "Name", NULL);
|
||||
if (!data->name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data->driver = g_key_file_get_string(keyfile, GROUP_DEVICE, "Driver", NULL);
|
||||
if (!data->driver) {
|
||||
log_error(ratbag, "Missing Driver in %s\n", basename(path));
|
||||
return false;
|
||||
} else {
|
||||
const struct driver_map *map;
|
||||
|
||||
data->drivertype = NONE;
|
||||
ARRAY_FOR_EACH(driver_map, map) {
|
||||
if (streq(map->driver, data->driver)) {
|
||||
data->drivertype = map->map;
|
||||
if (map->init)
|
||||
map->init(ratbag, keyfile, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->drivertype == NONE) {
|
||||
log_error(ratbag, "Unknown driver %s in %s\n",
|
||||
data->driver, basename(path));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
data->svg = g_key_file_get_string(keyfile, GROUP_DEVICE, "Svg", NULL);
|
||||
|
||||
*data_out = data;
|
||||
data = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
filter_device_files(const struct dirent *entry)
|
||||
{
|
||||
const char *SUFFIX = ".device";
|
||||
const char *name = entry->d_name;
|
||||
int len, slen;
|
||||
|
||||
if (!name || name[0] == '.')
|
||||
return 0;
|
||||
|
||||
len = strlen(name);
|
||||
slen = strlen(SUFFIX);
|
||||
if (len <= slen)
|
||||
return 0;
|
||||
|
||||
return streq(&name[len - slen], SUFFIX);
|
||||
}
|
||||
|
||||
struct ratbag_device_data *
|
||||
ratbag_device_data_new_for_id(struct ratbag *ratbag, const struct input_id *id)
|
||||
{
|
||||
struct ratbag_device_data *data = NULL;
|
||||
struct dirent **files;
|
||||
int n, nfiles;
|
||||
|
||||
n = scandir(LIBRATBAG_DATA_DIR, &files, filter_device_files, alphasort);
|
||||
if (n <= 0) {
|
||||
log_error(ratbag, "Unable to locate device files: %s\n",
|
||||
n == 0 ? "No files found" : strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nfiles = n;
|
||||
while(n--) {
|
||||
_cleanup_(freep) char *file = NULL;
|
||||
int rc;
|
||||
|
||||
rc = xasprintf(&file, "%s/%s", LIBRATBAG_DATA_DIR, files[n]->d_name);
|
||||
if (rc == -1)
|
||||
goto out;
|
||||
if (file_data_matches(ratbag, file, id, &data))
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
while(nfiles--)
|
||||
free(files[nfiles]);
|
||||
free(files);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/* HID++ 1.0 */
|
||||
|
||||
int
|
||||
ratbag_device_data_hidpp10_get_index(const struct ratbag_device_data *data)
|
||||
{
|
||||
assert(data->drivertype == HIDPP10);
|
||||
|
||||
return data->hidpp10.index;
|
||||
}
|
||||
|
||||
int
|
||||
ratbag_device_data_hidpp10_get_profile_count(const struct ratbag_device_data *data)
|
||||
{
|
||||
assert(data->drivertype == HIDPP10);
|
||||
|
||||
return data->hidpp10.profile_count;
|
||||
}
|
||||
|
||||
const char *
|
||||
ratbag_device_data_hidpp10_get_profile_type(const struct ratbag_device_data *data)
|
||||
{
|
||||
assert(data->drivertype == HIDPP10);
|
||||
|
||||
return data->hidpp10.profile_type;
|
||||
}
|
||||
|
||||
struct dpi_list *
|
||||
ratbag_device_data_hidpp10_get_dpi_list(const struct ratbag_device_data *data)
|
||||
{
|
||||
assert(data->drivertype == HIDPP10);
|
||||
|
||||
return data->hidpp10.dpi_list;
|
||||
}
|
||||
|
||||
struct dpi_range *
|
||||
ratbag_device_data_hidpp10_get_dpi_range(const struct ratbag_device_data *data)
|
||||
{
|
||||
assert(data->drivertype == HIDPP10);
|
||||
|
||||
return data->hidpp10.dpi_range;
|
||||
}
|
||||
|
||||
/* HID++ 2.0 */
|
||||
|
||||
int
|
||||
ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data)
|
||||
{
|
||||
assert(data->drivertype == HIDPP20);
|
||||
|
||||
return data->hidpp20.index;
|
||||
}
|
||||
74
src/libratbag-data.h
Normal file
74
src/libratbag-data.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright © 2017 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
|
||||
|
||||
struct ratbag_device_data;
|
||||
|
||||
struct ratbag_device_data *
|
||||
ratbag_device_data_new_for_id(struct ratbag *ratbag, const struct input_id *id);
|
||||
|
||||
|
||||
struct ratbag_device_data *
|
||||
ratbag_device_data_unref(struct ratbag_device_data *data);
|
||||
|
||||
struct ratbag_device_data *
|
||||
ratbag_device_data_ref(struct ratbag_device_data *data);
|
||||
|
||||
|
||||
const char *
|
||||
ratbag_device_data_get_driver(const struct ratbag_device_data *data);
|
||||
const char *
|
||||
ratbag_device_data_get_name(const struct ratbag_device_data *data);
|
||||
const char *
|
||||
ratbag_device_data_get_svg(const struct ratbag_device_data *data);
|
||||
|
||||
/* HID++ 1.0 */
|
||||
/**
|
||||
* @return The device index or -1 if not set
|
||||
*/
|
||||
int
|
||||
ratbag_device_data_hidpp10_get_index(const struct ratbag_device_data *data);
|
||||
|
||||
const char *
|
||||
ratbag_device_data_hidpp10_get_profile_type(const struct ratbag_device_data *data);
|
||||
|
||||
/**
|
||||
* @return The profile count index or -1 if not set
|
||||
*/
|
||||
int
|
||||
ratbag_device_data_hidpp10_get_profile_count(const struct ratbag_device_data *data);
|
||||
|
||||
struct dpi_list *
|
||||
ratbag_device_data_hidpp10_get_dpi_list(const struct ratbag_device_data *data);
|
||||
|
||||
struct dpi_range *
|
||||
ratbag_device_data_hidpp10_get_dpi_range(const struct ratbag_device_data *data);
|
||||
|
||||
/* HID++ 2.0 */
|
||||
|
||||
/**
|
||||
* @return The device index or -1 if not set
|
||||
*/
|
||||
int
|
||||
ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data);
|
||||
@@ -90,6 +90,7 @@ struct ratbag_device {
|
||||
struct input_id ids;
|
||||
struct ratbag_driver *driver;
|
||||
struct ratbag *ratbag;
|
||||
struct ratbag_device_data *data;
|
||||
unsigned long capabilities[NLONGS(MAX_CAP)];
|
||||
|
||||
unsigned num_profiles;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "usb-ids.h"
|
||||
#include "libratbag-private.h"
|
||||
#include "libratbag-util.h"
|
||||
#include "libratbag-data.h"
|
||||
|
||||
static enum ratbag_error_code
|
||||
error_code(enum ratbag_error_code code)
|
||||
@@ -180,6 +181,7 @@ ratbag_device_new(struct ratbag *ratbag, struct udev_device *udev_device,
|
||||
device->refcount = 1;
|
||||
device->udev_device = udev_device_ref(udev_device);
|
||||
device->ids = *id;
|
||||
device->data = ratbag_device_data_new_for_id(ratbag, id);
|
||||
list_init(&device->profiles);
|
||||
|
||||
list_insert(&ratbag->devices, &device->link);
|
||||
@@ -217,6 +219,7 @@ ratbag_device_destroy(struct ratbag_device *device)
|
||||
list_remove(&device->link);
|
||||
|
||||
ratbag_unref(device->ratbag);
|
||||
ratbag_device_data_unref(device->data);
|
||||
free(device->name);
|
||||
free(device);
|
||||
}
|
||||
@@ -358,7 +361,7 @@ ratbag_assign_driver(struct ratbag_device *device,
|
||||
const char *driver_name;
|
||||
|
||||
if (!test_device) {
|
||||
driver_name = udev_prop_value(device->udev_device, "RATBAG_DRIVER");
|
||||
driver_name = ratbag_device_data_get_driver(device->data);
|
||||
} else {
|
||||
driver_name = "test_driver";
|
||||
}
|
||||
@@ -433,7 +436,7 @@ ratbag_device_new_from_udev_device(struct ratbag *ratbag,
|
||||
goto out_err;
|
||||
|
||||
device = ratbag_device_new(ratbag, udev_device, name, &id);
|
||||
if (!device)
|
||||
if (!device || !device->data)
|
||||
goto out_err;
|
||||
|
||||
if (!ratbag_assign_driver(device, &device->ids, NULL))
|
||||
@@ -483,9 +486,10 @@ ratbag_device_get_name(const struct ratbag_device* device)
|
||||
LIBRATBAG_EXPORT const char *
|
||||
ratbag_device_get_svg_name(const struct ratbag_device* device)
|
||||
{
|
||||
const char *name;
|
||||
const char *name = NULL;
|
||||
|
||||
name = udev_prop_value(device->udev_device, "RATBAG_SVG");
|
||||
if (device->data)
|
||||
name = ratbag_device_data_get_svg(device->data);
|
||||
if (!name && device->driver->get_svg_name)
|
||||
name = device->driver->get_svg_name(device);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user