Compare commits

..

28 Commits

Author SHA1 Message Date
87fe2e31c3 Delete libratbag.spec 2026-11-05 14:23:38 +01:00
ff7ec2a1e4 spec for copr 2026-11-05 14:23:30 +01:00
96c7310a6d add spec 2026-11-05 14:14:46 +01:00
villamorrd
8235b5bb60 data: add ASUS TUF Gaming M3 Gen II
Signed-off-by: villamorrd <git@villamor.ph>
2026-09-17 20:56:39 +08:00
haytham
b8d4d3ca1f data: add ASUS ROG Strix Impact III Wireless
The device enumerates as usb 0b05:1ad7 behind the Omni receiver
(0b05:1ace). The signature prefix 20, OmniSignature=20, resolves to this
file during probe. Verified on hardware: 5 profiles, 8 buttons, 1 LED,
DPI presets 400/800/1600/3200, report rate 1000 Hz, debounce 12 ms, LED
brightness 127 (50%).
2026-08-18 20:11:11 +03:00
haytham
e07d72e2a7 data: add ASUS ROG Strix Impact III
Wired model with its own usb id. The values follow the official specs
and the wireless model's protocol, but are not tested on hardware:
Leds=2 (wheel and logo) and DpiRange=100:12000@50 are inferred.
2026-08-18 20:11:11 +03:00
haytham
d93a8bc474 asus: add support for the Omni receiver and new data layouts
Mice behind an ASUS Omni receiver all enumerate with the same usb id
(0b05:1ace), so the regular bus:vid:pid DeviceMatch cannot tell models
apart. Identify the model during probe instead:

- asus-rog-omni-receiver.device is a minimal bootstrap file that only
  declares Quirks=OMNI_RECEIVER, so the asus driver gets probed on the
  receiver's hidraw nodes
- the probe queries the serial number signature (03 12 12 02) and
  matches its prefix against the OmniSignature field of the .device
  files, then re-resolves the device data for the identified usb id
- an unknown signature fails the probe with -ENODEV, which also drops
  the receiver's keyboard and media control nodes

The Strix Impact III family answers with data two bytes earlier than the
original protocol (code plus params echo instead of code plus pad), so
two quirks cover the new layouts:

- LED_V2: per-zone LED responses with data at offset 4, brightness in
  0-100
- SETTINGS_V2: settings and DPI responses with data at offset 4,
  rate/debounce/snapping as single bytes

Also write DPI values with two bytes so presets above 12850 dpi work.

Verified on a ROG Strix Impact III Wireless: identification on hidraw4,
all 5 profiles with 8 buttons, 1 LED and DPI presets 400/800/1600/3200,
report rate 1000 Hz, debounce 12 ms, LED brightness 127 (50%). Writes
round-trip and survive a ratbagd restart.
2026-08-18 20:11:11 +03:00
Ayoze Torres
1f4efda955 swig: use the Python 3 C API instead of the removed PyInt_* aliases (#1878)
src/libratbag.i calls PyInt_Check(), PyInt_AsLong() and PyInt_FromLong().
Those are Python 2 functions; they only ever worked because SWIG emitted
compatibility macros mapping them onto their PyLong_* counterparts in the
generated wrapper. SWIG 4.5.0 dropped those macros, so the generated
libratbag.c no longer compiles:

  libratbag.c: In function '_wrap_ratbag_device_get_vendor_id':
  libratbag.c:4583:17: error: implicit declaration of function
    'PyInt_FromLong'; did you mean 'PyLong_FromLong'?
  libratbag.c:4583:15: error: assignment to 'PyObject *' from 'int' makes
    pointer from integer without a cast [-Wint-conversion]

Call the PyLong_* functions directly. The bindings are generated with
swig -py3, so there is no Python 2 build to keep working, and the macros
they replace expanded to exactly these functions.

Built and verified on Arch Linux with swig 4.5.0, Python 3.14, meson 1.12.0, gcc 15: `_libratbag.so` and `_hidpp.so` link cleanly with the change and fail without it.

Co-authored-by: Jitka Plesnikova <jplesnik@redhat.com>
Signed-off-by: Stephen Kitt <skitt@debian.org>
2026-08-12 18:07:56 +00:00
John Schoenick
03afbe49f3 data: enable INDEX_OFFSET on G502 X and G502 X Wireless
f9d1574 enabled this for the G502 X PLUS, which is the RGB variant.
I can confirm the normal G502 X Wireless has this quirk.  I do not
have a base model G502 X, but comments on issue #1606 as well as logic
suggest it very likely does unless proven otherwise.
2026-07-19 16:31:01 +03:00
perfectra1n
f9d1574841 data: enable INDEX_OFFSET on the G502 X PLUS
The G502 X PLUS reports a 1-indexed active profile, so without
INDEX_OFFSET libratbag targets the wrong profile and DPI/button changes
never reach the device (the mouse boots on its first profile, reported
as index 1). It also needs the existing G502X_PLUS LED quirk, so list
both now that hidpp20 supports multiple quirks.

Verified on hardware (usb:046d:4099 via the Lightspeed receiver, Linux
7.x): the device reports active profile index 1, INDEX_OFFSET maps it to
0, and the LED still reads correctly via the G502X_PLUS slot.
2026-07-15 20:41:56 +03:00
perfectra1n
b0f2779270 hidpp20: support multiple quirks per device
The hidpp20 quirk was a single enum value, so a device could carry only
one quirk. Some devices need several at once. For example the G502 X
PLUS needs G502X_PLUS (LED in the 2nd slot) and INDEX_OFFSET (1-indexed
active profile) together, which a single field cannot express. #1799
noted this was worth adding.

Make the quirk enum combinable bit flags stored as a bitmask. The
"Quirk" key now takes a ;-separated list (like the asus driver's
"Quirks"); existing single-value device files parse the same as before.
2026-07-15 20:41:56 +03:00
thejobbitt
43f76eee67 data: add Logitech G502 X Wireless through LS dongle (046d:409f)
Add USB ID 046d:409f, the ID when connected through the LS dongle. USB ID 046d:c098 is the ID when wired.
2026-07-10 16:57:22 +00:00
Tyler
54edb7e0a2 fix: clear stale modifiers when binding keys (#1843)
Device: Logitech G502 X PLUS

Encountered an issue where re-binding keys which previously had modifiers assigned (e.g. ALT+KEY_LEFT), resulted in the modifier being retained. This small change clears modifiers before assigning new keys or macros. Keys are still able to be assigned modifiers, as assignment hasn't changed.

I didn't see a PR template, so I'm just doing my best here...

- Assigned the back button the default setting
  - Changed back button to send comma key
    - before: sent `ALT+,`
    - after: sent `,` ✔️ 
- Assigned back button to send `ALT+LEFT_ARROW`
  - Changed back button to send comma key
    - before: sent `ALT+,`
    - after: sent `,` ✔️ 
- Assigned back button to send comma key
  - Changed back button to send `ALT+,`
    - before: sent `ALT+,`
    - after: sent `ALT+,` ✔️

Closes https://github.com/libratbag/libratbag/issues/1853
Closes https://github.com/libratbag/piper/issues/835
2026-07-10 16:52:10 +00:00
winter
2fb9a701e8 data: add Glorious Model O Eternal (usb:3794:a000)
Co-authored-by: Merikei <73759842+Merikei@users.noreply.github.com>
Co-authored-by: winter <96156057+wintervoid@users.noreply.github.com>
2026-05-31 19:00:25 +03:00
rc-05
805e7fb77f feature: Add support for ASUS TUF GAMING M4 AIR
This commit adds support for the TUF GAMING M4 AIR mouse manufactured by ASUS/ASUSTek.
2026-04-25 21:27:34 +03:00
Kyoken
1e20248b6d data: add ASUS TUF Mini WL 2026-04-25 21:26:44 +03:00
Yaroslav Chvanov
d7bbed2eb1 ci: pin black and ruff versions
Avoid CI breaking on their updates.
2026-04-25 21:20:52 +03:00
Patrick Klose
874c01732a hidpp20: add quirk to fix active profile index on some devices (#1799)
* Added Quirk for G502 offset to resolve incorrect active profile being applied. Testing on G502- Hero Wireless. May be worth adding handling for multiple quirks.

* Added quirk in device files to correct offset for G502 Hero, G502-Proteus Spectrum, and G604.

* Refactored G502 Offset Quirk naming to be more broadly applicable as index offset.

* Correct typo in g502-proteus spectrum quirk value.

---------

Co-authored-by: Patrick Klose <pjklose@outlook.com>
2026-01-11 16:51:40 +00:00
Denis
c5214d2633 data: add G502 X Plus in wireless mode (usb:046d:4099) (#1728) 2025-12-14 07:10:47 +00:00
Adam Spiers
b61da30205 data: add Logitech MX Master 4 2025-11-19 22:23:19 +03:00
Kritphong Mongkhonvanit
e4184c2227 data: add ASUS ROG Keris (Wired) 2025-11-19 22:10:26 +03:00
lat9nq
78d1124c3e data: add Logitech G705 Mouse
Based on https://github.com/Nadeflore work. Thank you!
2025-05-11 12:50:04 +03:00
Sewer.
65d07ae4d1 hidpp20: add G502X Plus (Wired) Support (#1693)
* Recognize the Logitech G502X PLUS (Wired)

* Fixed: LED write functionality for G502X PLUS

Inspired by 94bd172a19

* Changed: Number of LEDs is implied by quirk set.

* Added: Extra note about single LED
2025-05-10 10:59:41 +00:00
bliepp
ca94a8a111 Additional ID for ASUS ROG Keris Wireless AimPoint (#1679) 2025-05-10 10:41:05 +00:00
Kyoken
7bdca904f5 asus: new quirks for Chakram X and other mice 2025-05-10 13:38:06 +03:00
Yaroslav Chvanov
ad6540e39b ci: upgrade Ubuntu 20 -> 24
Fixes https://github.com/libratbag/libratbag/issues/1701
2025-05-08 09:52:30 +03:00
AFCMS
df3c73e958 Bump CI Actions 2024-10-28 23:36:03 +01:00
Sebastian Fleer
dcc5def3d6 data: add an additional USB ID for Logitech MX Vertical (#1652) 2024-10-16 19:24:00 +00:00
35 changed files with 1083 additions and 141 deletions

12
.copr/Makefile Normal file
View File

@@ -0,0 +1,12 @@
outdir ?= .
spec ?= libratbag.spec
srpm:
dnf -y install git-core rpm-build
git archive --format=tar.gz --prefix=libratbag-snapshot/ \
-o $(outdir)/libratbag-snapshot.tar.gz HEAD
rpmbuild -bs $(spec) \
--define "_sourcedir $(outdir)" \
--define "_srcrpmdir $(outdir)" \
--define "gitdate $$(date +%Y%m%d)" \
--define "shortcommit $$(git rev-parse --short=7 HEAD)"

259
.copr/libratbag.spec Normal file
View File

@@ -0,0 +1,259 @@
%global udevdir %(pkg-config --variable=udevdir udev)
%global commit <full 40-char hash>
%global shortcommit %(c=%{commit}; echo ${c:0:7})
%global gitdate %(date +%%Y%%m%%d)
Name: libratbag
Version: 0.18^%{gitdate}git%{shortcommit}
Release: 1%{?dist}
Summary: Programmable input device library
License: MIT
URL: https://github.com/libratbag/libratbag
Source0: libratbag-snapshot.tar.gz
BuildRequires: git gcc gcc-c++
BuildRequires: meson pkgconfig
BuildRequires: libevdev-devel
BuildRequires: libudev-devel
BuildRequires: pkgconfig(udev) pkgconfig(glib-2.0) pkgconfig(json-glib-1.0)
BuildRequires: check-devel
%ifarch %{valgrind_arches}
BuildRequires: valgrind
%endif
BuildRequires: systemd
BuildRequires: python3 python3-devel python3-gobject
BuildRequires: python3-lxml python3-evdev swig
BuildRequires: libunistring-devel
%description
libratbag is a library that allows to configure programmable
mice.
%package ratbagd
Summary: DBus daemon to access programmable input devices
Obsoletes: libratbag < 0.9.900
Requires: python3-evdev python3-gobject
%description ratbagd
The ratbagd package contains a dbus daemon to access and configure
programmable input devices, primarily gaming mice.
%package -n liblur
Summary: Logitech Unifying Receiver library
%description -n liblur
The liblur package contains libraries and tools to access and
configure the Logitech Unifying Receivers. The functionality
are mainly listing, pairing and un-pairing Logitech devices
attached to a receiver.
%package -n liblur-devel
Summary: Development files for liblur
Requires: liblur%{?_isa} = %{version}-%{release}
%description -n liblur-devel
The liblur-devel package contains libraries and header files for
developing applications that use liblur.
%prep
%autosetup -n libratbag-snapshot
# hack until rhbz#1409661 gets fixed
%{!?__global_cxxflags: %define __global_cxxflags %{optflags}}
%build
# s390x builds sometimes fails during the tests, let just disable those
%ifarch s390x
%meson -Dudev-dir=%{udevdir} -Ddocumentation=false -Dtests=false
%else
%meson -Dudev-dir=%{udevdir} -Ddocumentation=false
%endif
%meson_build
%check
%meson_test
%install
%meson_install
%ldconfig_scriptlets -n liblur
%files ratbagd
%license COPYING
%{_bindir}/ratbagctl
%{_bindir}/ratbagd
%dir %{_datadir}/libratbag
%{_datadir}/libratbag/*.device
%{_mandir}/man1/ratbagctl.1*
%{_mandir}/man8/ratbagd.8*
%{_datadir}/dbus-1/system.d/org.freedesktop.ratbag1.conf
%{_datadir}/dbus-1/system-services/org.freedesktop.ratbag1.service
%{_unitdir}/ratbagd.service
%files -n liblur
%license COPYING
%{_libdir}/liblur.so.*
%{_bindir}/lur-command
%{_mandir}/man1/lur-command.1*
%files -n liblur-devel
%{_includedir}/liblur.h
%{_libdir}/liblur.so
%{_libdir}/pkgconfig/liblur.pc
%changelog
%changelog
* Thu Nov 05 2026 Drath <you@example.com> - 0.18^20261105git<shortcommit>-1
- Git snapshot with G502 X Plus device file
* Thu Jul 16 2026 Fedora Release Engineering <releng@fedoraproject.org> - 0.18-6
- Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild
* Fri Jan 16 2026 Fedora Release Engineering <releng@fedoraproject.org> - 0.18-5
- Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild
* Thu Jul 24 2025 Fedora Release Engineering <releng@fedoraproject.org> - 0.18-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild
* Thu Mar 06 2025 David Abdurachmanov <davidlt@rivosinc.com> 0.18-3
- Properly check valgrind arches
* Mon Jan 20 2025 Fedora Release Engineering <releng@fedoraproject.org> - 0.18-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild
* Fri Jan 03 2025 Peter Hutterer <peter.hutterer@redhat.com> - 0.18-1
- libratbag 0.18
- Revert upstream change to /sbin
* Thu Jul 18 2024 Fedora Release Engineering <releng@fedoraproject.org> - 0.17-7
- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild
* Wed Jun 26 2024 Marcin Juszkiewicz <marcin@juszkiewicz.com.pl> - 0.17-6
- Backport remove of imp Python module to fix FTFBS on F40.
* Thu Jan 25 2024 Fedora Release Engineering <releng@fedoraproject.org> - 0.17-5
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
* Sun Jan 21 2024 Fedora Release Engineering <releng@fedoraproject.org> - 0.17-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
* Fri Sep 08 2023 Peter Hutterer <peter.hutterer@redhat.com>
- SPDX migration: license is MIT, we're good
* Thu Jul 20 2023 Fedora Release Engineering <releng@fedoraproject.org> - 0.17-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
* Thu Jan 19 2023 Fedora Release Engineering <releng@fedoraproject.org> - 0.17-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
* Wed Nov 30 2022 Peter Hutterer <peter.hutterer@redhat.com> - 0.17-1
- libratbag 0.17
* Thu Jul 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 0.16-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
* Thu Jan 20 2022 Fedora Release Engineering <releng@fedoraproject.org> - 0.16-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
* Thu Jul 22 2021 Fedora Release Engineering <releng@fedoraproject.org> - 0.16-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
* Fri Jun 25 2021 Peter Hutterer <peter.hutterer@redhat.com> 0.16-1
- libratbag 0.16
* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 0.15-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
* Mon Sep 28 2020 Peter Hutterer <peter.hutterer@redhat.com> 0.15-1
- libratbag 0.15
* Tue Jul 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.14-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
* Thu May 21 2020 Peter Hutterer <peter.hutterer@redhat.com> 0.14-1
- libratbag 0.14
* Tue Feb 11 2020 Peter Hutterer <peter.hutterer@redhat.com> 0.13-1
- libratbag 0.13
* Wed Jan 29 2020 Peter Hutterer <peter.hutterer@redhat.com> 0.12-1
- libratbag 0.12
* Tue Nov 05 2019 Peter Hutterer <peter.hutterer@redhat.com> 0.11-1
- libratbag 0.11
* Mon Sep 16 2019 Peter Hutterer <peter.hutterer@redhat.com> 0.10-2
- Add missing Requires for ratbagctl
* Fri Aug 02 2019 Peter Hutterer <peter.hutterer@redhat.com> 0.10-1
- libratbag 0.10
* Fri Jul 26 2019 Peter Hutterer <peter.hutterer@redhat.com> 0.9.906-1
- libratbag 0.9.906
* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.9.905-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
* Wed Jul 17 2019 Peter Hutterer <peter.hutterer@redhat.com> 0.9.905-3
- Use python3-embed as dependency where available. Fixes FTBFS with python
3.8 (#1718290)
* Thu Feb 28 2019 Peter Hutterer <peter.hutterer@redhat.com> 0.9.905-2
- Fix meson options
* Thu Feb 28 2019 Peter Hutterer <peter.hutterer@redhat.com> 0.9.905-1
- libratbag 0.9.905
* Fri Feb 01 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.9.904-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Thu Jan 24 2019 Peter Hutterer <peter.hutterer@redhat.com> 0.9.904-1
- libratbag 0.9.904
* Wed Sep 05 2018 Peter Hutterer <peter.hutterer@redhat.com> 0.9.903-1
- libratbag 0.9.903
* Fri Jul 20 2018 Peter Hutterer <peter.hutterer@redhat.com> 0.9.902-4
- Add gcc to BuildRequires (#1604654)
- Change to use the correct -Ddocumentation=false
* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.9.902-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Tue Jun 19 2018 Miro Hrončok <mhroncok@redhat.com> - 0.9.902-2
- Rebuilt for Python 3.7
* Mon May 21 2018 Peter Hutterer <peter.hutterer@redhat.com> 0.9.902-1
- libratbag 0.9.902
* Fri Mar 23 2018 Peter Hutterer <peter.hutterer@redhat.com> 0.9.901-1
- libratbag 0.9.901
* Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.9-5
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
* Thu Aug 03 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.9-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
* Wed Jul 26 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.9-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
* Wed Jun 07 2017 Benjamin Tissoires <benjamin.tissoires@redhat.com> 0.9-2
- disable tests on s390x because they seem to fail without good reasons
* Tue Jun 06 2017 Benjamin Tissoires <benjamin.tissoires@redhat.com> 0.9-1
- libratbag v0.9
- new manpage for lur-command
* Tue May 09 2017 Benjamin Tissoires <benjamin.tissoires@redhat.com> 0.8-1
- libratbag v0.8
* Tue May 09 2017 Benjamin Tissoires <benjamin.tissoires@redhat.com> 0.7-3
- add a hack for F24 and F25 to compile
* Fri May 05 2017 Benjamin Tissoires <benjamin.tissoires@redhat.com> 0.7-2
- Remove the generation of the documentation, we don't ship it
* Thu May 04 2017 Benjamin Tissoires <benjamin.tissoires@redhat.com> 0.7-1
- Initial Fedora packaging (rhbz#1309703)

View File

@@ -8,20 +8,24 @@ env:
libjson-glib-dev libsystemd-dev libudev-dev libunistring-dev
python3-dev python3-evdev swig python3-sphinx
UBUNTU_DEP_TEST: check python3-gi python3-lxml valgrind
PIP_PACKAGES: meson ninja libevdev pyudev pytest sphinx_rtd_theme black ruff
PIP_PACKAGES: meson ninja libevdev pyudev pytest sphinx_rtd_theme black==25.12.0 ruff==0.15.12
jobs:
build-and-dist:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
strategy:
matrix:
meson_args:
- ''
- '-Dbuildtype=plain'
- '-Dbuildtype=release'
- '-Ddocumentation=true'
include:
- name: default
meson_args: ''
- name: plain
meson_args: '-Dbuildtype=plain'
- name: release
meson_args: '-Dbuildtype=release'
- name: documentation
meson_args: '-Ddocumentation=true'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/pkginstall
with:
apt: $UBUNTU_DEP_BUILD $UBUNTU_DEP_TEST
@@ -41,10 +45,10 @@ jobs:
- name: check if any files are left after uninstall
run: (test -d _instdir && tree _instdir && exit 1) || exit 0
# Capture all the meson logs, even if we failed
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: ${{ always() }} # even if we fail
with:
name: meson test logs
name: 'meson test logs ${{ matrix.name }}'
path: |
builddir/meson-logs/testlog*.txt
builddir/meson-logs/meson-log.txt

View File

@@ -11,7 +11,7 @@ env:
jobs:
deploy:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/pkginstall

View File

@@ -1,14 +1,16 @@
[Device]
Name=ASUS ROG Chakram X
DeviceMatch=usb:0b05:1a18
DeviceMatch=usb:0b05:1a18;usb:0b05:1a1a
DeviceType=mouse
Driver=asus
[Driver/asus]
Profiles=3
Buttons=5
Profiles=5
Buttons=14
Leds=3
Dpis=4
Wireless=1
DpiRange=100:36000@100
Quirks=DOUBLE_DPI
Quirks=DOUBLE_DPI;RAW_BRIGHTNESS;SEPARATE_XY_DPI;SEPARATE_LEDS;BUTTONS_SECONDARY
ButtonMapping=f0;f1;f2;0;0;e6;e8;e9;d0;d1;d2;d3
ButtonMappingSecondary=0;0;ea;eb;ec;ed

View File

@@ -1,6 +1,6 @@
[Device]
Name=ASUS ROG Keris Wireless AimPoint
DeviceMatch=usb:0b05:1a68
DeviceMatch=usb:0b05:1a66;usb:0b05:1a68
DeviceType=mouse
Driver=asus

View File

@@ -0,0 +1,13 @@
[Device]
Name=ASUS ROG Keris
DeviceMatch=usb:0b05:195c
DeviceType=mouse
Driver=asus
[Driver/asus]
Profiles=3
Buttons=8
Leds=2
Dpis=4
DpiRange=100:16000@100
Quirks=DOUBLE_DPI

View File

@@ -0,0 +1,12 @@
[Device]
Name=ASUS ROG Omni Receiver
DeviceMatch=usb:0b05:1ace
DeviceType=mouse
Driver=asus
# The Omni receiver is a shared USB device for multiple mice (all of them
# enumerate with the same usb id), so the actual model is identified during
# probe and resolved to the model-specific .device data. This file only
# exists to get the asus driver probed on the receiver's hidraw nodes.
[Driver/asus]
Quirks=OMNI_RECEIVER

View File

@@ -11,3 +11,4 @@ Leds=3
Dpis=2
DpiRange=100:7200@100
ButtonMapping=f0;f1;f2;e4;e5;e6;0;e8;e9;e1;e2
LedModes=ON;BREATHING;ON;CYCLE

View File

@@ -0,0 +1,19 @@
[Device]
Name=ASUS ROG Strix Impact III Wireless
DeviceMatch=usb:0b05:1ad7
DeviceType=mouse
Driver=asus
# This device enumerates as usb 0b05:1ad7 behind the ASUS Omni receiver
# (0b05:1ace). The usb id is resolved during probe: the receiver is
# identified first (asus-rog-omni-receiver.device) and the serial number
# signature prefix (OmniSignature) is matched against this file.
[Driver/asus]
Profiles=5
Buttons=8
Leds=1
Dpis=4
Wireless=1
DpiRange=100:36000@50
Quirks=OMNI_RECEIVER;SEPARATE_XY_DPI;LED_V2;SETTINGS_V2
OmniSignature=20

View File

@@ -0,0 +1,13 @@
[Device]
Name=ASUS ROG Strix Impact III
DeviceMatch=usb:0b05:1a88
DeviceType=mouse
Driver=asus
[Driver/asus]
Profiles=3
Buttons=8
Leds=2
Dpis=4
DpiRange=100:12000@50
Quirks=LED_V2;SETTINGS_V2

View File

@@ -0,0 +1,12 @@
[Device]
Name=ASUSTeK TUF GAMING M3 GEN II
DeviceMatch=usb:0b05:1a9b
DeviceType=mouse
Driver=asus
[Driver/asus]
Profiles=3
Buttons=8
Leds=1
Dpis=4
DpiRange=100:8000@100

View File

@@ -0,0 +1,12 @@
[Device]
Name=ASUS TUF GAMING M4 AIR
DeviceMatch=usb:0b05:1a03
DeviceType=mouse
Driver=asus
[Driver/asus]
Profiles=3
Buttons=6
Leds=0
Dpis=4
DpiRange=100:16000@100

View File

@@ -0,0 +1,14 @@
[Device]
Name=ASUS TUF GAMING MINI WL MOUSE MIKU
DeviceMatch=usb:0b05:1c56;usb:0b05:1c57
DeviceType=mouse
Driver=asus
[Driver/asus]
Profiles=5
Buttons=8
Leds=1
Dpis=4
Wireless=1
DpiRange=100:12000@100
Quirks=SEPARATE_XY_DPI;RAW_BRIGHTNESS

View File

@@ -0,0 +1,11 @@
[Device]
Name=Glorious Model O Eternal
DeviceMatch=usb:3794:a000
Driver=sinowealth
DeviceType=mouse
[Driver/sinowealth/devices/1009]
Buttons=6
LedType=RBG
DeviceName=Glorious Model O Eternal
SensorType=PMW3360

View File

@@ -0,0 +1,5 @@
[Device]
DeviceMatch=bluetooth:046d:b042
DeviceType=mouse
Driver=hidpp20
Name=Logitech MX Master 4

View File

@@ -1,5 +1,5 @@
[Device]
Name=Logitech MX Vertical
DeviceMatch=bluetooth:046d:b020;usb:046d:407b;usb:046d:c08a
DeviceMatch=bluetooth:046d:b020;usb:046d:407b;usb:046d:c08a;usb:046d:c077
Driver=hidpp20
DeviceType=mouse

View File

@@ -3,3 +3,6 @@ Name=Logitech G502 Hero Wireless
DeviceMatch=usb:046d:407f;usb:046d:c08d
Driver=hidpp20
DeviceType=mouse
[Driver/hidpp20]
Quirk=INDEX_OFFSET

View File

@@ -3,3 +3,6 @@ Name=Logitech G502 Hero
DeviceMatch=usb:046d:c08b
Driver=hidpp20
DeviceType=mouse
[Driver/hidpp20]
Quirk=INDEX_OFFSET

View File

@@ -3,3 +3,6 @@ Name=Logitech G502 Proteus Spectrum
DeviceMatch=usb:046d:c332
Driver=hidpp20
DeviceType=mouse
[Driver/hidpp20]
Quirk=INDEX_OFFSET

View File

@@ -0,0 +1,9 @@
[Device]
Name=Logitech G502 X PLUS
DeviceMatch=usb:046d:4099;usb:046d:c095
DeviceType=mouse
Driver=hidpp20
[Driver/hidpp20]
# G502X_PLUS: LED is in the 2nd slot. INDEX_OFFSET: profile index is 1-indexed.
Quirk=G502X_PLUS;INDEX_OFFSET

View File

@@ -1,5 +1,8 @@
[Device]
Name=Logitech G502 X Wireless
DeviceMatch=usb:046d:c098
DeviceMatch=usb:046d:c098;usb:046d:409f
DeviceType=mouse
Driver=hidpp20
[Driver/hidpp20]
Quirk=INDEX_OFFSET

View File

@@ -3,3 +3,6 @@ Name=Logitech G502 X
DeviceMatch=usb:046d:c099
DeviceType=mouse
Driver=hidpp20
[Driver/hidpp20]
Quirk=INDEX_OFFSET

View File

@@ -3,3 +3,6 @@ Name=Logitech G604
DeviceMatch=usb:046d:4085;bluetooth:046d:b024
Driver=hidpp20
DeviceType=mouse
[Driver/hidpp20]
Quirk=INDEX_OFFSET

View File

@@ -0,0 +1,8 @@
[Device]
Name=Logitech G705
DeviceMatch=usb:046d:c096;usb:046d:409d;bluetooth:046d:b02e
Driver=hidpp20
DeviceType=mouse
[Driver/hidpp20]
Quirk=G502X_PLUS

View File

@@ -26,6 +26,7 @@
#include "asus.h"
#include <assert.h>
#include <string.h>
#include "libratbag-data.h"
#include "libratbag-private.h"
@@ -75,15 +76,18 @@ static const unsigned char ASUS_KEY_MAPPING[] = {
/* 5E */ KEY_KP8, KEY_KP9, 0,
};
static unsigned int ASUS_POLLING_RATES[] = { 125, 250, 500, 1000 };
static unsigned int ASUS_DEBOUNCE_TIMES[] = { 4, 8, 12, 16, 20, 24, 28, 32 };
static const uint8_t ASUS_JOYSTICK_CODES[] = { 0xd0, 0xd1, 0xd2, 0xd3, 0xd7, 0xd8, 0xda, 0xdb };
static const unsigned int ASUS_POLLING_RATES[] = { 125, 250, 500, 1000 };
static const unsigned int ASUS_DEBOUNCE_TIMES[] = { 4, 8, 12, 16, 20, 24, 28, 32 };
/* search for ASUS button by ratbag types */
const struct asus_button *
asus_find_button_by_action(struct ratbag_button_action action)
asus_find_button_by_action(struct ratbag_button_action action, bool is_joystick)
{
const struct asus_button *asus_button;
ARRAY_FOR_EACH(ASUS_BUTTON_MAPPING, asus_button) {
if (is_joystick != asus_code_is_joystick(asus_button->asus_code))
continue;
if ((action.type == RATBAG_BUTTON_ACTION_TYPE_BUTTON && asus_button->button == action.action.button) ||
(action.type == RATBAG_BUTTON_ACTION_TYPE_SPECIAL && asus_button->special == action.action.special))
return asus_button;
@@ -113,6 +117,15 @@ asus_find_key_code(unsigned int linux_code)
return -1;
}
bool
asus_code_is_joystick(uint8_t asus_code) {
for (unsigned int i = 0; i < ARRAY_LENGTH(ASUS_JOYSTICK_CODES); i++) {
if (ASUS_JOYSTICK_CODES[i] == asus_code)
return true;
}
return false;
}
int
asus_get_linux_key_code(uint8_t asus_code) {
if (asus_code > ARRAY_LENGTH(ASUS_KEY_MAPPING)) {
@@ -126,15 +139,37 @@ asus_query(struct ratbag_device *device,
union asus_request *request, union asus_response *response)
{
int rc;
uint32_t quirks = ratbag_device_data_asus_get_quirks(device->data);
uint8_t omni_tx_buf[ASUS_PACKET_SIZE];
uint8_t omni_rx_buf[ASUS_PACKET_SIZE];
rc = ratbag_hidraw_output_report(device, request->raw, ASUS_PACKET_SIZE);
if (rc < 0)
return rc;
if (quirks & ASUS_QUIRK_OMNI_RECEIVER) {
/* devices behind the Omni receiver use report ID 0x03 and a
* 64-byte report, the payload is shifted by one byte */
memset(omni_tx_buf, 0, sizeof(omni_tx_buf));
omni_tx_buf[0] = 0x03;
memcpy(omni_tx_buf + 1, request->raw, ASUS_PACKET_SIZE - 1);
memset(response, 0, sizeof(union asus_response));
rc = ratbag_hidraw_read_input_report(device, response->raw, ASUS_PACKET_SIZE, NULL);
if (rc < 0)
return rc;
rc = ratbag_hidraw_output_report(device, omni_tx_buf, ASUS_PACKET_SIZE);
if (rc < 0)
return rc;
memset(response, 0, sizeof(union asus_response));
rc = ratbag_hidraw_read_input_report(device, omni_rx_buf, ASUS_PACKET_SIZE, NULL);
if (rc < 0)
return rc;
memcpy(response->raw, omni_rx_buf + 1, ASUS_PACKET_SIZE - 1);
} else {
rc = ratbag_hidraw_output_report(device, request->raw, ASUS_PACKET_SIZE);
if (rc < 0)
return rc;
memset(response, 0, sizeof(union asus_response));
rc = ratbag_hidraw_read_input_report(device, response->raw, ASUS_PACKET_SIZE, NULL);
if (rc < 0)
return rc;
}
/* invalid state, disconnected or sleeping */
if (response->data.code == ASUS_STATUS_ERROR) {
@@ -216,6 +251,11 @@ asus_get_profile_data(struct ratbag_device *device, struct asus_profile_data *da
else
data->profile_id = response.data.results[8];
if (response.data.results[9])
data->dpi_preset = response.data.results[9] - 1;
else
data->dpi_preset = -1;
data->version_primary_major = response.data.results[13];
data->version_primary_minor = response.data.results[12];
data->version_primary_build = response.data.results[11];
@@ -246,12 +286,13 @@ asus_set_profile(struct ratbag_device *device, unsigned int index)
/* read button bindings */
int
asus_get_binding_data(struct ratbag_device *device, union asus_binding_data *data)
asus_get_binding_data(struct ratbag_device *device, union asus_binding_data *data, unsigned int group)
{
int rc;
union asus_response response;
union asus_request request = {
.data.cmd = ASUS_CMD_GET_BUTTON_DATA,
.data.params[0] = (uint8_t)group,
};
rc = asus_query(device, &request, &response);
@@ -289,7 +330,7 @@ asus_set_button_action(struct ratbag_device *device, uint8_t asus_code_src,
}
int
asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_data *data)
asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_data *data, bool sep_xy_dpi)
{
int rc;
uint32_t quirks = ratbag_device_data_asus_get_quirks(device->data);
@@ -298,6 +339,7 @@ asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_dat
unsigned int i;
union asus_request request = {
.data.cmd = ASUS_CMD_GET_SETTINGS,
.data.params[0] = sep_xy_dpi ? 2 : 0,
};
rc = asus_query(device, &request, &response);
@@ -319,13 +361,51 @@ asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_dat
break;
case 4: /* 4 DPI presets */
for (i = 0; i < dpi_count; i++) {
data->data4.dpi[i] = data->data4.dpi[i] * 50 + 50;
if (quirks & ASUS_QUIRK_DOUBLE_DPI)
data->data4.dpi[i] *= 2;
if (sep_xy_dpi) { /* separate X & Y values */
if (quirks & ASUS_QUIRK_SETTINGS_V2) {
/* new layout: data starts at offset 4, each preset
* is x lo/hi, y lo/hi */
for (i = 0; i < dpi_count; i++) {
uint16_t x = data->raw[4 + i * 4] |
(data->raw[5 + i * 4] << 8);
uint16_t y = data->raw[6 + i * 4] |
(data->raw[7 + i * 4] << 8);
data->data_xy.dpi[i].x = x * 50 + 50;
data->data_xy.dpi[i].y = y * 50 + 50;
}
} else {
for (i = 0; i < dpi_count; i++) {
data->data_xy.dpi[i].x = data->data_xy.dpi[i].x * 50 + 50;
data->data_xy.dpi[i].y = data->data_xy.dpi[i].y * 50 + 50;
if (quirks & ASUS_QUIRK_DOUBLE_DPI) {
data->data_xy.dpi[i].x *= 2;
data->data_xy.dpi[i].y *= 2;
}
}
}
} else {
if (quirks & ASUS_QUIRK_SETTINGS_V2) {
/* new layout: data starts at offset 4, dpi presets
* are 2 bytes each, rate/debounce/snapping are
* single bytes at offset 12/14/16 */
for (i = 0; i < dpi_count; i++) {
uint16_t dpi = data->raw[4 + i * 2] |
(data->raw[5 + i * 2] << 8);
data->data4.dpi[i] = dpi * 50 + 50;
}
data->data4.rate = ASUS_POLLING_RATES[data->raw[12] & 0x07];
data->data4.response = ASUS_DEBOUNCE_TIMES[data->raw[14]];
data->data4.snapping = data->raw[16];
} else {
for (i = 0; i < dpi_count; i++) {
data->data4.dpi[i] = data->data4.dpi[i] * 50 + 50;
if (quirks & ASUS_QUIRK_DOUBLE_DPI)
data->data4.dpi[i] *= 2;
}
data->data4.rate = ASUS_POLLING_RATES[data->data4.rate];
data->data4.response = ASUS_DEBOUNCE_TIMES[data->data4.response];
}
}
data->data4.rate = ASUS_POLLING_RATES[data->data4.rate];
data->data4.response = ASUS_DEBOUNCE_TIMES[data->data4.response];
break;
default:
@@ -348,10 +428,12 @@ asus_set_dpi(struct ratbag_device *device, unsigned int index, unsigned int dpi)
if (quirks & ASUS_QUIRK_DOUBLE_DPI)
idpi /= 2;
idpi = (idpi - 50) / 50;
union asus_request request = {
.data.cmd = ASUS_CMD_SET_SETTING,
.data.params[0] = index,
.data.params[2] = (idpi - 50) / 50,
.data.params[2] = idpi & 0xff,
.data.params[3] = (idpi >> 8) & 0xff,
};
rc = asus_query(device, &request, &response);
@@ -440,12 +522,13 @@ asus_set_angle_snapping(struct ratbag_device *device, bool is_enabled)
}
int
asus_get_led_data(struct ratbag_device *device, union asus_led_data *data)
asus_get_led_data(struct ratbag_device *device, union asus_led_data *data, unsigned int led)
{
int rc;
union asus_response response;
union asus_request request = {
.data.cmd = ASUS_CMD_GET_LED_DATA,
.data.params[0] = led,
};
rc = asus_query(device, &request, &response);
@@ -480,3 +563,49 @@ asus_set_led(struct ratbag_device *device,
return 0;
}
/* ASUS Omni receiver: identify the mouse behind the shared receiver.
*
* All mice connected to an Omni receiver share the same USB id
* (0b05:1ace), so the model is queried with a signature request
* (03 12 12 02). The response carries a 12 byte ASCII serial number at
* offset 5 and the model is derived from its prefix.
*
* The serial number is matched against the OmniSignature field of the
* .device files, so supporting a new model does not require any code
* changes. On success the usb id of the identified model is returned in
* id, so that the caller can resolve the mouse-specific .device data.
*/
int
asus_omni_identify(struct ratbag_device *device, struct input_id *id)
{
int rc;
union asus_request request = {
.data.cmd = 0x1212,
.data.params[0] = 0x02,
};
union asus_response response;
char signature[13];
rc = asus_query(device, &request, &response);
if (rc)
return rc;
/* signature is 12 ASCII bytes at offset 5 of the raw report,
* which is offset 4 after the report id is stripped */
memcpy(signature, response.raw + 4, 12);
signature[12] = '\0';
rc = ratbag_device_data_omni_signature_match(device->ratbag, signature, id);
if (rc) {
log_debug(device->ratbag,
"Unknown ASUS Omni device serial '%s'. Please add an "
"OmniSignature entry to the .device file of the model.\n",
signature);
return rc;
}
log_info(device->ratbag,
"ASUS Omni device identified (serial %s)\n", signature);
return 0;
}

View File

@@ -9,6 +9,13 @@
#define ASUS_QUIRK_DOUBLE_DPI 1 << 0
#define ASUS_QUIRK_STRIX_PROFILE 1 << 1
#define ASUS_QUIRK_BATTERY_V2 1 << 2
#define ASUS_QUIRK_RAW_BRIGHTNESS 1 << 3
#define ASUS_QUIRK_SEPARATE_XY_DPI 1 << 4
#define ASUS_QUIRK_SEPARATE_LEDS 1 << 5
#define ASUS_QUIRK_BUTTONS_SECONDARY 1 << 6
#define ASUS_QUIRK_OMNI_RECEIVER 1 << 7 /* device is behind an ASUS Omni receiver, transport uses report ID 0x03 */
#define ASUS_QUIRK_LED_V2 1 << 8 /* LED response is per-zone with data at offset 4, brightness is 0-100 */
#define ASUS_QUIRK_SETTINGS_V2 1 << 9 /* settings/DPI response data starts at offset 4, rate/debounce/snapping are single bytes */
#define ASUS_PACKET_SIZE 64
#define ASUS_BUTTON_ACTION_TYPE_KEY 0 /* keyboard key */
@@ -18,6 +25,7 @@
/* maximum number of buttons across all ASUS devices */
#define ASUS_MAX_NUM_BUTTON 17
#define ASUS_MAX_NUM_BUTTON_GROUP 2
/* maximum number of DPI presets across all ASUS devices */
/* for 4 DPI devices: 0 - red, 1 - purple, 2 - blue (default), 3 - green */
@@ -26,6 +34,7 @@
/* maximum number of LEDs across all ASUS devices */
#define ASUS_MAX_NUM_LED 3
#define ASUS_MAX_NUM_LED_MODES 7
/* base request */
@@ -57,6 +66,7 @@ union asus_response {
struct asus_profile_data {
unsigned int profile_id;
int dpi_preset;
uint8_t version_primary_major;
uint8_t version_primary_minor;
uint8_t version_primary_build;
@@ -90,6 +100,7 @@ struct _asus_dpi2_data {
uint16_t rate; /* polling rate */
uint16_t response; /* button response */
uint16_t snapping; /* angle snapping (on/off) */
uint16_t pad2;
} __attribute__((packed)); /* struct for storing 2 DPI presets and extra settings */
struct _asus_dpi4_data {
@@ -98,11 +109,23 @@ struct _asus_dpi4_data {
uint16_t rate;
uint16_t response;
uint16_t snapping;
uint16_t pad2;
} __attribute__((packed)); /* struct for storing 4 DPI presets and extra settings */
struct _asus_dpi_xy {
uint16_t x;
uint16_t y;
};
struct _asus_dpi_xy_data {
uint32_t pad;
struct _asus_dpi_xy dpi[4];
} __attribute__((packed)); /* struct for storing 4 DPI presets with separate X & Y */
union asus_resolution_data {
struct _asus_dpi2_data data2; /* data for 2 DPI presets */
struct _asus_dpi4_data data4; /* data for 4 DPI presets */
struct _asus_dpi_xy_data data_xy; /* data for 4 DPI presets with separate X & Y */
uint8_t raw[sizeof(struct _asus_dpi4_data)];
};
@@ -153,10 +176,18 @@ static const struct asus_button ASUS_BUTTON_MAPPING[] = {
{ 0xed, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* side button D */
{ 0xee, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* side button E */
{ 0xef, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* side button F */
{ 0xd0, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* joystick up */
{ 0xd1, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* joystick down */
{ 0xd2, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* joystick forward */
{ 0xd3, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* joystick backward */
{ 0xd7, RATBAG_BUTTON_ACTION_TYPE_SPECIAL, 0, RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN }, /* joystick axis -Y */
{ 0xd8, RATBAG_BUTTON_ACTION_TYPE_SPECIAL, 0, RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP }, /* joystick axis +Y */
{ 0xda, RATBAG_BUTTON_ACTION_TYPE_SPECIAL, 0, RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT }, /* joystick axis -X */
{ 0xdb, RATBAG_BUTTON_ACTION_TYPE_SPECIAL, 0, RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT }, /* joystick axis +X */
};
const struct asus_button *
asus_find_button_by_action(struct ratbag_button_action action);
asus_find_button_by_action(struct ratbag_button_action action, bool is_joystick);
const struct asus_button *
asus_find_button_by_code(uint8_t asus_code);
@@ -164,6 +195,9 @@ asus_find_button_by_code(uint8_t asus_code);
int
asus_find_key_code(unsigned int linux_code);
bool
asus_code_is_joystick(uint8_t asus_code);
/*
* @param asus_code ASUS key code.
* @return Linux key code (as defined defined in linux/input-event-codes.h) or
@@ -191,6 +225,11 @@ asus_setup_led(struct ratbag_device *device, struct ratbag_led *led);
int
asus_query(struct ratbag_device *device, union asus_request *request, union asus_response *response);
/* ASUS Omni receiver: identify the mouse behind the shared receiver and
* return the usb id that matches its own .device file. */
int
asus_omni_identify(struct ratbag_device *device, struct input_id *id);
/* commit */
int
@@ -207,7 +246,7 @@ asus_set_profile(struct ratbag_device *device, unsigned int index);
/* button bindings */
int
asus_get_binding_data(struct ratbag_device *device, union asus_binding_data *data);
asus_get_binding_data(struct ratbag_device *device, union asus_binding_data *data, unsigned int group);
int
asus_set_button_action(struct ratbag_device *device, uint8_t asus_code_src,
@@ -216,7 +255,7 @@ asus_set_button_action(struct ratbag_device *device, uint8_t asus_code_src,
/* resolution settings */
int
asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_data *data);
asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_data *data, bool sep_xy_dpi);
int
asus_set_dpi(struct ratbag_device *device, unsigned int index, unsigned int dpi);
@@ -233,7 +272,7 @@ asus_set_angle_snapping(struct ratbag_device *device, bool is_enabled);
/* LED settings */
int
asus_get_led_data(struct ratbag_device *device, union asus_led_data *data);
asus_get_led_data(struct ratbag_device *device, union asus_led_data *data, unsigned int led);
int
asus_set_led(struct ratbag_device *device,

View File

@@ -46,17 +46,9 @@ static int ASUS_CONFIG_BUTTON_MAPPING[] = {
0xe6, /* DPI */
0xe8, /* wheel up */
0xe9, /* wheel down */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
};
/* LedModes configuration property defaults */
static unsigned int ASUS_LED_MODE[] = {
RATBAG_LED_ON,
RATBAG_LED_BREATHING,
@@ -69,12 +61,13 @@ static unsigned int ASUS_LED_MODE[] = {
struct asus_data {
uint8_t is_ready;
int button_mapping[ASUS_MAX_NUM_BUTTON];
int button_indices[ASUS_MAX_NUM_BUTTON];
int button_mapping[ASUS_MAX_NUM_BUTTON * ASUS_MAX_NUM_BUTTON_GROUP];
int button_indices[ASUS_MAX_NUM_BUTTON * ASUS_MAX_NUM_BUTTON_GROUP];
int led_modes[ASUS_MAX_NUM_LED_MODES];
};
static int
asus_driver_load_profile(struct ratbag_device *device, struct ratbag_profile *profile)
asus_driver_load_profile(struct ratbag_device *device, struct ratbag_profile *profile, int dpi_preset)
{
int rc;
const struct _asus_binding *asus_binding;
@@ -84,18 +77,28 @@ asus_driver_load_profile(struct ratbag_device *device, struct ratbag_profile *pr
struct ratbag_led *led;
struct ratbag_resolution *resolution;
union asus_binding_data binding_data;
union asus_binding_data binding_data_secondary;
union asus_led_data led_data;
union asus_resolution_data resolution_data;
union asus_resolution_data xy_resolution_data;
unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions;
unsigned int led_count = ratbag_device_get_num_leds(device);
uint32_t quirks = ratbag_device_data_asus_get_quirks(device->data);
struct asus_data *drv_data = ratbag_get_drv_data(device);
/* get buttons */
log_debug(device->ratbag, "Loading buttons data\n");
rc = asus_get_binding_data(device, &binding_data);
rc = asus_get_binding_data(device, &binding_data, 0);
if (rc)
return rc;
if (quirks & ASUS_QUIRK_BUTTONS_SECONDARY) {
rc = asus_get_binding_data(device, &binding_data_secondary, 1);
if (rc)
return rc;
}
ratbag_profile_for_each_button(profile, button) {
int asus_index = drv_data->button_indices[button->index];
if (asus_index == -1) {
@@ -103,7 +106,11 @@ asus_driver_load_profile(struct ratbag_device *device, struct ratbag_profile *pr
continue;
}
asus_binding = &binding_data.data.binding[asus_index];
if (asus_index < ASUS_MAX_NUM_BUTTON) {
asus_binding = &binding_data.data.binding[asus_index];
} else {
asus_binding = &binding_data_secondary.data.binding[asus_index % ASUS_MAX_NUM_BUTTON];
}
/* disabled */
if (asus_binding->action == ASUS_BUTTON_CODE_DISABLED) {
@@ -144,31 +151,59 @@ asus_driver_load_profile(struct ratbag_device *device, struct ratbag_profile *pr
/* get DPIs */
log_debug(device->ratbag, "Loading resolutions data\n");
rc = asus_get_resolution_data(device, &resolution_data);
rc = asus_get_resolution_data(device, &resolution_data, false);
if (rc)
return rc;
if (quirks & ASUS_QUIRK_SEPARATE_XY_DPI) {
rc = asus_get_resolution_data(device, &xy_resolution_data, true);
if (rc)
return rc;
}
switch (dpi_count) {
case 2: /* 2 DPI presets */
profile->hz = resolution_data.data2.rate;
profile->angle_snapping = resolution_data.data2.snapping;
profile->debounce = resolution_data.data2.response;
ratbag_profile_for_each_resolution(profile, resolution)
ratbag_resolution_set_resolution(
resolution,
resolution_data.data2.dpi[resolution->index],
resolution_data.data2.dpi[resolution->index]);
ratbag_profile_for_each_resolution(profile, resolution) {
if (quirks & ASUS_QUIRK_SEPARATE_XY_DPI) {
ratbag_resolution_set_cap(resolution, RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
ratbag_resolution_set_resolution(
resolution,
xy_resolution_data.data_xy.dpi[resolution->index].x,
xy_resolution_data.data_xy.dpi[resolution->index].y);
} else {
ratbag_resolution_set_resolution(
resolution,
resolution_data.data2.dpi[resolution->index],
resolution_data.data2.dpi[resolution->index]);
}
if (dpi_preset != -1 && (unsigned int)dpi_preset == resolution->index)
resolution->is_active = true;
}
break;
case 4: /* 4 DPI presets */
profile->hz = resolution_data.data4.rate;
profile->angle_snapping = resolution_data.data4.snapping;
profile->debounce = resolution_data.data4.response;
ratbag_profile_for_each_resolution(profile, resolution)
ratbag_resolution_set_resolution(
resolution,
resolution_data.data4.dpi[resolution->index],
resolution_data.data4.dpi[resolution->index]);
ratbag_profile_for_each_resolution(profile, resolution) {
if (quirks & ASUS_QUIRK_SEPARATE_XY_DPI) {
ratbag_resolution_set_cap(resolution, RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
ratbag_resolution_set_resolution(
resolution,
xy_resolution_data.data_xy.dpi[resolution->index].x,
xy_resolution_data.data_xy.dpi[resolution->index].y);
} else {
ratbag_resolution_set_resolution(
resolution,
resolution_data.data4.dpi[resolution->index],
resolution_data.data4.dpi[resolution->index]);
}
if (dpi_preset != -1 && (unsigned int)dpi_preset == resolution->index)
resolution->is_active = true;
}
break;
default:
@@ -177,16 +212,44 @@ asus_driver_load_profile(struct ratbag_device *device, struct ratbag_profile *pr
/* get LEDs */
log_debug(device->ratbag, "Loading LEDs data\n");
rc = asus_get_led_data(device, &led_data);
if (rc)
return rc;
if (!(quirks & (ASUS_QUIRK_SEPARATE_LEDS | ASUS_QUIRK_LED_V2)) && led_count) {
log_debug(device->ratbag, "Loading LEDs data\n");
rc = asus_get_led_data(device, &led_data, 0);
if (rc)
return rc;
}
ratbag_profile_for_each_led(profile, led) {
asus_led = &led_data.data.led[led->index];
led->mode = ASUS_LED_MODE[asus_led->mode];
/* convert brightness from 0-4 to 0-256 */
led->brightness = asus_led->brightness * 64;
if (quirks & (ASUS_QUIRK_SEPARATE_LEDS | ASUS_QUIRK_LED_V2)) {
log_debug(device->ratbag, "Loading LED %d data\n", led->index);
rc = asus_get_led_data(device, &led_data, led->index);
if (rc)
return rc;
}
if (quirks & ASUS_QUIRK_LED_V2) {
/* new layout: a single zone per response with data at
* offset 4, brightness is 0-100 percent */
led->mode = drv_data->led_modes[led_data.raw[4]];
led->brightness = led_data.raw[5] * 255 / 100;
led->color.red = led_data.raw[6];
led->color.green = led_data.raw[7];
led->color.blue = led_data.raw[8];
continue;
}
if (quirks & ASUS_QUIRK_SEPARATE_LEDS)
asus_led = &led_data.data.led[0];
else
asus_led = &led_data.data.led[led->index];
led->mode = drv_data->led_modes[asus_led->mode];
if (quirks & ASUS_QUIRK_RAW_BRIGHTNESS) {
led->brightness = asus_led->brightness;
} else {
/* convert brightness from 0-4 to 0-256 */
led->brightness = asus_led->brightness * 64;
}
led->color.red = asus_led->r;
led->color.green = asus_led->g;
led->color.blue = asus_led->b;
@@ -203,6 +266,7 @@ asus_driver_save_profile(struct ratbag_device *device, struct ratbag_profile *pr
struct ratbag_led *led;
struct ratbag_resolution *resolution;
struct asus_data *drv_data = ratbag_get_drv_data(device);
uint32_t quirks = ratbag_device_data_asus_get_quirks(device->data);
/* set buttons */
ratbag_profile_for_each_button(profile, button) {
@@ -218,6 +282,7 @@ asus_driver_save_profile(struct ratbag_device *device, struct ratbag_profile *pr
const struct asus_button *asus_button;
uint8_t asus_code_src;
uint8_t asus_code_dst;
bool is_joystick;
rc = drv_data->button_mapping[asus_index];
if (rc == -1) {
@@ -250,7 +315,18 @@ asus_driver_save_profile(struct ratbag_device *device, struct ratbag_profile *pr
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
/* ratbag action to ASUS code */
asus_button = asus_find_button_by_action(button->action);
is_joystick = asus_code_is_joystick(asus_code_src);
if (is_joystick) {
asus_button = asus_find_button_by_action(button->action, true);
if (asus_button != NULL) { /* found button to bind to */
rc = asus_set_button_action(
device, asus_code_src, asus_button->asus_code,
ASUS_BUTTON_ACTION_TYPE_BUTTON);
break;
}
}
asus_button = asus_find_button_by_action(button->action, false);
if (asus_button != NULL) /* found button to bind to */
rc = asus_set_button_action(
device, asus_code_src, asus_button->asus_code,
@@ -306,15 +382,23 @@ asus_driver_save_profile(struct ratbag_device *device, struct ratbag_profile *pr
log_debug(device->ratbag, "LED %d changed\n", led->index);
uint8_t led_mode = 0;
for (unsigned int i = 0; i < ARRAY_LENGTH(ASUS_LED_MODE); i++) {
if (ASUS_LED_MODE[i] == led->mode) {
for (unsigned int i = 0; i < ASUS_MAX_NUM_LED_MODES; i++) {
if (drv_data->led_modes[i] == (int) led->mode) {
led_mode = i;
break;
}
}
/* convert brightness from 0-256 to 0-4 */
uint8_t led_brightness = (uint8_t)round((double)led->brightness / 64.0);
uint8_t led_brightness;
if (quirks & ASUS_QUIRK_RAW_BRIGHTNESS) {
led_brightness = led->brightness;
} else if (quirks & ASUS_QUIRK_LED_V2) {
/* convert brightness from 0-255 to 0-100 percent */
led_brightness = (uint8_t)round((double)led->brightness * 100.0 / 255.0);
} else {
/* convert brightness from 0-256 to 0-4 */
led_brightness = (uint8_t)round((double)led->brightness / 64.0);
}
rc = asus_set_led(device, led->index, led_mode, led_brightness, led->color);
if (rc)
return rc;
@@ -365,7 +449,7 @@ asus_driver_load_profiles(struct ratbag_device *device)
return rc;
}
rc = asus_driver_load_profile(device, profile);
rc = asus_driver_load_profile(device, profile, profile_data.dpi_preset);
if (rc)
return rc;
}
@@ -447,17 +531,51 @@ asus_driver_probe(struct ratbag_device *device)
struct ratbag_button *button;
struct ratbag_resolution *resolution;
struct ratbag_led *led;
uint32_t quirks = ratbag_device_data_asus_get_quirks(device->data);
rc = ratbag_open_hidraw(device);
if (rc)
return rc;
rc = asus_get_profile_data(device, &profile_data);
if (rc) {
ratbag_close_hidraw(device);
return -ENODEV;
/* The Omni receiver is a shared USB device for multiple mice, so the
* actual model has to be identified and resolved to its own .device
* data before anything else. Wrong hidraw nodes (keyboard, media
* control, etc.) fail the identification and are rejected here. */
if (quirks & ASUS_QUIRK_OMNI_RECEIVER) {
struct ratbag_device_data *data;
struct input_id id = device->ids;
rc = asus_omni_identify(device, &id);
if (rc) {
log_debug(device->ratbag,
"Omni device is not a supported mouse\n");
ratbag_close_hidraw(device);
return -ENODEV;
}
data = ratbag_device_data_new_for_id(device->ratbag, &id);
if (!data) {
log_error(device->ratbag,
"No device data for Omni mouse %04x:%04x\n",
id.vendor, id.product);
ratbag_close_hidraw(device);
return -ENODEV;
}
ratbag_device_data_unref(device->data);
device->data = data;
device->devicetype = ratbag_device_data_get_device_type(device->data);
free(device->name);
device->name = strdup_safe(ratbag_device_data_get_name(device->data));
log_info(device->ratbag,
"ASUS Omni receiver identified device: %s\n",
device->name);
quirks = ratbag_device_data_asus_get_quirks(device->data);
}
rc = asus_get_profile_data(device, &profile_data);
/* create device state data */
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
@@ -469,23 +587,37 @@ asus_driver_probe(struct ratbag_device *device)
button_count = ratbag_device_data_asus_get_button_count(device->data);
led_count = ratbag_device_data_asus_get_led_count(device->data);
const int *bm = ratbag_device_data_asus_get_button_mapping(device->data);
const int *led_modes = ratbag_device_data_asus_get_led_modes(device->data);
/* merge ButtonMapping configuration property with defaults */
for (unsigned int i = 0; i < ASUS_MAX_NUM_BUTTON; i++) {
drv_data->button_mapping[i] = bm[i] != -1 ? bm[i] : ASUS_CONFIG_BUTTON_MAPPING[i];
for (unsigned int i = 0; i < ASUS_MAX_NUM_BUTTON * ASUS_MAX_NUM_BUTTON_GROUP; i++) {
if (bm[i] != -1) {
drv_data->button_mapping[i] = bm[i];
} else {
if (i < ARRAY_LENGTH(ASUS_CONFIG_BUTTON_MAPPING)) {
drv_data->button_mapping[i] = ASUS_CONFIG_BUTTON_MAPPING[i];
} else {
drv_data->button_mapping[i] = -1;
}
}
drv_data->button_indices[i] = -1;
}
/* merge LedModes configuration property with defaults */
for (unsigned int i = 0; i < ASUS_MAX_NUM_LED_MODES; i++)
drv_data->led_modes[i] = (led_modes[i] != -1) ? led_modes[i] : (int) ASUS_LED_MODE[i];
/* setup a lookup table for all defined buttons */
unsigned int button_index = 0;
ARRAY_FOR_EACH(ASUS_BUTTON_MAPPING, asus_button) {
/* search for this button in the ButtonMapping by it's ASUS code */
for (unsigned int i = 0; i < ASUS_MAX_NUM_BUTTON; i++) {
for (unsigned int i = 0; i < ASUS_MAX_NUM_BUTTON * ASUS_MAX_NUM_BUTTON_GROUP; i++) {
if (drv_data->button_mapping[i] == (int)asus_button->asus_code) {
/* add button to indices array */
drv_data->button_indices[button_index] = (int)i;
log_debug(device->ratbag, "Button %d is mapped to 0x%02x\n",
button_index, (uint8_t)drv_data->button_mapping[i]);
log_debug(device->ratbag, "Button %d is mapped to 0x%02x at position %d group %d\n",
button_index, (uint8_t)drv_data->button_mapping[i],
i % ASUS_MAX_NUM_BUTTON, i / ASUS_MAX_NUM_BUTTON);
button_index++;
break;
}

View File

@@ -411,7 +411,13 @@ hidpp20drv_read_led_8071(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
hidpp20_rgb_effects_get_device_info(drv_data->dev, &device_info);
cluster_info = drv_data->led_infos.color_leds_8071[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
if (drv_data->dev->quirks & HIDPP20_QUIRK_G502X_PLUS) {
// On G502X+ the second slot controls the user configurable LED.
// (Note: There is only 1 reported LED, it just happens to use 2nd index though)
h_led = &profile->leds[1];
} else {
h_led = &profile->leds[led->index];
}
switch (h_led->mode) {
case HIDPP20_LED_ON:
@@ -579,10 +585,12 @@ hidpp20drv_update_button_8100(struct ratbag_button *button)
}
profile->buttons[button->index].subany.type = type;
profile->buttons[button->index].subany.subtype = subtype;
if (subtype == HIDPP20_BUTTON_HID_TYPE_KEYBOARD)
if (subtype == HIDPP20_BUTTON_HID_TYPE_KEYBOARD) {
profile->buttons[button->index].keyboard_keys.modifier_flags = 0;
profile->buttons[button->index].keyboard_keys.key = code;
else
} else {
profile->buttons[button->index].consumer_control.consumer_control = code;
}
break;
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
code = hidpp20_onboard_profiles_get_code_from_special(action->action.special);
@@ -689,7 +697,13 @@ hidpp20drv_update_led_8070_8071(struct ratbag_led *led, struct ratbag_profile* p
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
h_profile = &drv_data->profiles->profiles[profile->index];
h_led = &(h_profile->leds[led->index]);
if (drv_data->dev->quirks & HIDPP20_QUIRK_G502X_PLUS) {
// On G502X+ the second slot controls the user configurable LED.
// (Note: There is only 1 reported LED, it just happens to use 2nd index though)
h_led = &(h_profile->leds[1]);
} else {
h_led = &(h_profile->leds[led->index]);
}
}
if (!h_led)
@@ -1420,7 +1434,7 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
case HIDPP_PAGE_COLOR_LED_EFFECTS: {
/* The 8070 feature implemented in the G602 doesn't follow the spec,
* so we ignore it */
if (ratbag_device_data_hidpp20_get_quirk(device->data) == HIDPP20_QUIRK_G602)
if (ratbag_device_data_hidpp20_get_quirks(device->data) & HIDPP20_QUIRK_G602)
break;
log_debug(ratbag, "device has color effects\n");
@@ -1694,14 +1708,15 @@ hidpp20drv_probe(struct ratbag_device *device)
goto err;
}
dev->quirk = ratbag_device_data_hidpp20_get_quirk(device->data);
dev->quirks = ratbag_device_data_hidpp20_get_quirks(device->data);
drv_data->dev = dev;
log_debug(device->ratbag, "'%s' is using protocol v%d.%d\n", ratbag_device_get_name(device), dev->proto_major, dev->proto_minor);
if(dev->quirk != HIDPP20_QUIRK_NONE)
log_debug(device->ratbag, "'%s' is quirked (%s)\n", ratbag_device_get_name(device), hidpp20_get_quirk_string(dev->quirk));
/* quirks is a bitmask; log each flag that is set */
for (uint32_t q = dev->quirks; q != 0; q &= q - 1)
log_debug(device->ratbag, "'%s' is quirked (%s)\n", ratbag_device_get_name(device), hidpp20_get_quirk_string(q & ~(q - 1)));
/* add some defaults that will be overwritten by the device */
drv_data->num_profiles = 1;

View File

@@ -110,6 +110,8 @@ hidpp20_get_quirk_string(enum hidpp20_quirk quirk)
CASE_RETURN_STRING(HIDPP20_QUIRK_NONE);
CASE_RETURN_STRING(HIDPP20_QUIRK_G305);
CASE_RETURN_STRING(HIDPP20_QUIRK_G602);
CASE_RETURN_STRING(HIDPP20_QUIRK_G502X_PLUS);
CASE_RETURN_STRING(HIDPP20_QUIRK_INDEX_OFFSET);
}
abort();
@@ -1530,7 +1532,7 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602) {
if (device->quirks & HIDPP20_QUIRK_G602) {
msg.msg.parameters[0] = 1;
i = 0;
}
@@ -1546,7 +1548,7 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
get_unaligned_be_u16(&msg.msg.parameters[i]) != 0) {
uint16_t value = get_unaligned_be_u16(&msg.msg.parameters[i]);
if (device->quirk == HIDPP20_QUIRK_G602 && i == 2)
if ((device->quirks & HIDPP20_QUIRK_G602) && i == 2)
value += 0xe000;
if (value > 0xe000) {
@@ -1578,7 +1580,7 @@ hidpp20_adjustable_dpi_get_dpi(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602)
if (device->quirks & HIDPP20_QUIRK_G602)
msg.msg.parameters[0] = 1;
rc = hidpp20_request_command(device, &msg);
@@ -1660,7 +1662,7 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602)
if (device->quirks & HIDPP20_QUIRK_G602)
msg.msg.parameters[0] = 1;
set_unaligned_be_u16(&msg.msg.parameters[1], dpi);
@@ -1802,6 +1804,7 @@ int hidpp20_adjustable_report_rate_set_report_rate(struct hidpp20_device *device
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303 0x02
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900 0x03
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G915 0x04
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G502X 0x05
#define HIDPP20_ONBOARD_PROFILES_MACRO_TYPE_G402 0x01
#define HIDPP20_USER_PROFILES_G402 0x0000
@@ -1831,7 +1834,8 @@ union hidpp20_internal_profile {
} name;
struct hidpp20_internal_led leds[2]; /* G303, g502, g900 only */
struct hidpp20_internal_led alt_leds[2];
uint8_t free[2];
uint8_t custom_animation_index; // G502X Plus, G705
uint8_t free; // unused
uint16_t crc;
} __attribute__((packed)) profile;
};
@@ -2220,7 +2224,8 @@ hidpp20_onboard_profiles_validate(struct hidpp20_device *device,
if ((info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G402) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G915)) {
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G915) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G502X)) {
hidpp_log_error(&device->base,
"Profile layout not supported: 0x%02x.\n",
info->profile_format_id);
@@ -2273,6 +2278,10 @@ hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
active_profile_index = rc;
/* Apply quirk for devices that return 1-indexed profile */
if ((device->quirks & HIDPP20_QUIRK_INDEX_OFFSET) && active_profile_index > 0)
active_profile_index--;
profiles = zalloc(sizeof(struct hidpp20_profiles));
profiles->profiles = zalloc(info.profile_count * sizeof(struct hidpp20_profile));
profiles->sector_size = info.sector_size;
@@ -2793,7 +2802,7 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
profiles->sector_size,
data);
if (rc && device->quirk == HIDPP20_QUIRK_G305) {
if (rc && (device->quirks & HIDPP20_QUIRK_G305)) {
/* The G305 has a bug where it throws an ERR_INVALID_ARGUMENT
if the sector has not been written to yet. If this happens
we will read the ROM profiles.*/
@@ -2959,6 +2968,18 @@ hidpp20_onboard_profiles_write_profile(struct hidpp20_device *device,
memcpy(pdata->profile.name.txt, profile->name, sizeof(profile->name));
// Mice like G502X can store custom animations onboard.
// ratbag does not support this, so we set it to disable it to ensure
// that the actual lighting matches the user's settings.
if (device->quirks & HIDPP20_QUIRK_G502X_PLUS)
{
// Note(sewer56): There are two known mice which use this field;
// G502X PLUS and G705. [And I only own one of these.] But it's not known how
// prevalent this is elsewhere, so in the interest of defensive programming;
// we're not setting this on unknown mice.
pdata->profile.custom_animation_index = 0x00;
}
rc = hidpp20_onboard_profiles_write_sector(device, sector, sector_size, data, true);
if (rc < 0) {
hidpp_log_error(&device->base, "failed to write profile\n");

View File

@@ -51,10 +51,13 @@ struct hidpp20_feature {
uint8_t type;
};
/* Combinable flags so a device can have several at once (stored as a bitmask). */
enum hidpp20_quirk {
HIDPP20_QUIRK_NONE,
HIDPP20_QUIRK_G305,
HIDPP20_QUIRK_G602,
HIDPP20_QUIRK_NONE = 0,
HIDPP20_QUIRK_G305 = (1 << 0),
HIDPP20_QUIRK_G602 = (1 << 1),
HIDPP20_QUIRK_G502X_PLUS = (1 << 2), // G502X+ uses 2nd LED slot instead of 1st.
HIDPP20_QUIRK_INDEX_OFFSET = (1 << 3), // Device returns 1-indexed profile, decrement by 1.
};
struct hidpp20_device {
@@ -64,7 +67,7 @@ struct hidpp20_device {
unsigned proto_minor;
unsigned feature_count;
struct hidpp20_feature *feature_list;
enum hidpp20_quirk quirk;
uint32_t quirks; /* bitmask of enum hidpp20_quirk */
unsigned int led_ext_caps;
};

View File

@@ -67,7 +67,7 @@ enum driver {
struct data_hidpp20 {
int index;
enum hidpp20_quirk quirk;
uint32_t quirks; /* bitmask of enum hidpp20_quirk */
int led_count;
int report_rate;
int button_count;
@@ -100,12 +100,14 @@ struct data_steelseries {
struct data_asus {
int profile_count;
int button_count;
int button_mapping[ASUS_MAX_NUM_BUTTON];
int button_mapping[ASUS_MAX_NUM_BUTTON * ASUS_MAX_NUM_BUTTON_GROUP];
int led_count;
int dpi_count;
int is_wireless;
struct dpi_range *dpi_range;
uint32_t quirks;
int led_modes[ASUS_MAX_NUM_LED_MODES];
char *omni_signature; /* serial number prefix of mice behind an Omni receiver */
};
struct ratbag_device_data {
@@ -175,6 +177,20 @@ init_data_hidpp10(struct ratbag *ratbag,
}
}
static uint32_t
hidpp20_quirk_from_string(const char *str)
{
if (streq(str, "G305"))
return HIDPP20_QUIRK_G305;
if (streq(str, "G602"))
return HIDPP20_QUIRK_G602;
if (streq(str, "G502X_PLUS"))
return HIDPP20_QUIRK_G502X_PLUS;
if (streq(str, "INDEX_OFFSET"))
return HIDPP20_QUIRK_INDEX_OFFSET;
return HIDPP20_QUIRK_NONE;
}
static void
init_data_hidpp20(struct ratbag *ratbag,
GKeyFile *keyfile,
@@ -183,7 +199,6 @@ init_data_hidpp20(struct ratbag *ratbag,
const char *group = "Driver/hidpp20";
GError *error = NULL;
int num;
char *str;
data->hidpp20.button_count = -1;
data->hidpp20.index = -1;
@@ -217,14 +232,13 @@ init_data_hidpp20(struct ratbag *ratbag,
if (error)
g_error_free(error);
str = g_key_file_get_string(keyfile, group, "Quirk", NULL);
data->hidpp20.quirk = HIDPP20_QUIRK_NONE;
if (str) {
if (streq(str, "G305"))
data->hidpp20.quirk = HIDPP20_QUIRK_G305;
else if(streq(str, "G602"))
data->hidpp20.quirk = HIDPP20_QUIRK_G602;
}
/* Quirks are a bitmask: "Quirk" takes a ;-separated list. */
data->hidpp20.quirks = HIDPP20_QUIRK_NONE;
gsize quirks_count = 0;
_cleanup_(g_strfreevp) char **quirks = g_key_file_get_string_list(keyfile, group, "Quirk", &quirks_count, NULL);
for (gsize i = 0; quirks && i < quirks_count; i++)
data->hidpp20.quirks |= hidpp20_quirk_from_string(quirks[i]);
}
static void
@@ -375,8 +389,10 @@ init_data_asus(struct ratbag *ratbag,
data->asus.dpi_range = NULL;
data->asus.is_wireless = -1;
data->asus.quirks = 0;
for (unsigned int i = 0; i < ASUS_MAX_NUM_BUTTON; i++)
for (unsigned int i = 0; i < ASUS_MAX_NUM_BUTTON * ASUS_MAX_NUM_BUTTON_GROUP; i++)
data->asus.button_mapping[i] = -1;
for (unsigned int i = 0; i < ASUS_MAX_NUM_LED_MODES; i++)
data->asus.led_modes[i] = -1;
int profiles = g_key_file_get_integer(keyfile, group, "Profiles", &error);
if (!error && profiles >= 0)
@@ -388,11 +404,20 @@ init_data_asus(struct ratbag *ratbag,
data->asus.button_count = buttons;
g_clear_error(&error);
gsize button_mapping_count = 0;
_cleanup_(g_strfreevp) char **button_mapping = g_key_file_get_string_list(keyfile, group, "ButtonMapping", &button_mapping_count, &error);
if (!error && button_mapping) {
for (unsigned int i = 0; i < button_mapping_count; i++) {
data->asus.button_mapping[i] = strtoul(button_mapping[i], NULL, 16);
gsize btn_map_count = 0;
_cleanup_(g_strfreevp) char **btn_map = g_key_file_get_string_list(keyfile, group, "ButtonMapping", &btn_map_count, &error);
if (!error && btn_map) {
for (unsigned int i = 0; i < btn_map_count; i++) {
data->asus.button_mapping[i] = strtoul(btn_map[i], NULL, 16);
}
}
g_clear_error(&error);
gsize btn_map_2nd_count = 0;
_cleanup_(g_strfreevp) char **btn_map_2nd = g_key_file_get_string_list(keyfile, group, "ButtonMappingSecondary", &btn_map_2nd_count, &error);
if (!error && btn_map_2nd) {
for (unsigned int i = 0; i < btn_map_2nd_count; i++) {
data->asus.button_mapping[i + ASUS_MAX_NUM_BUTTON] = strtoul(btn_map_2nd[i], NULL, 16);
}
}
g_clear_error(&error);
@@ -417,6 +442,9 @@ init_data_asus(struct ratbag *ratbag,
data->asus.is_wireless = wireless;
g_clear_error(&error);
data->asus.omni_signature = g_key_file_get_string(keyfile, group, "OmniSignature", &error);
g_clear_error(&error);
gsize quirks_count = 0;
_cleanup_(g_strfreevp) char **quirks = g_key_file_get_string_list(keyfile, group, "Quirks", &quirks_count, &error);
if (!error && quirks) {
@@ -425,8 +453,39 @@ init_data_asus(struct ratbag *ratbag,
data->asus.quirks |= ASUS_QUIRK_DOUBLE_DPI;
} else if (streq(quirks[i], "STRIX_PROFILE")) {
data->asus.quirks |= ASUS_QUIRK_STRIX_PROFILE;
} else if (streq(quirks[i], "RAW_BRIGHTNESS")) {
data->asus.quirks |= ASUS_QUIRK_RAW_BRIGHTNESS;
} else if (streq(quirks[i], "SEPARATE_XY_DPI")) {
data->asus.quirks |= ASUS_QUIRK_SEPARATE_XY_DPI;
} else if (streq(quirks[i], "SEPARATE_LEDS")) {
data->asus.quirks |= ASUS_QUIRK_SEPARATE_LEDS;
} else if (streq(quirks[i], "BUTTONS_SECONDARY")) {
data->asus.quirks |= ASUS_QUIRK_BUTTONS_SECONDARY;
} else if (streq(quirks[i], "OMNI_RECEIVER")) {
data->asus.quirks |= ASUS_QUIRK_OMNI_RECEIVER;
} else if (streq(quirks[i], "LED_V2")) {
data->asus.quirks |= ASUS_QUIRK_LED_V2;
} else if (streq(quirks[i], "SETTINGS_V2")) {
data->asus.quirks |= ASUS_QUIRK_SETTINGS_V2;
} else {
log_debug(ratbag, "%s is invalid quirk. Ignoring...\n", quirks[i]);
log_error(ratbag, "%s is invalid quirk. Ignoring...\n", quirks[i]);
}
}
}
g_clear_error(&error);
gsize led_modes_count = 0;
_cleanup_(g_strfreevp) char **led_modes = g_key_file_get_string_list(keyfile, group, "LedModes", &led_modes_count, &error);
if (!error && led_modes) {
for (unsigned int i = 0; i < led_modes_count; i++) {
if (streq(led_modes[i], "ON")) {
data->asus.led_modes[i] = RATBAG_LED_ON;
} else if (streq(led_modes[i], "BREATHING")) {
data->asus.led_modes[i] = RATBAG_LED_BREATHING;
} else if (streq(led_modes[i], "CYCLE")) {
data->asus.led_modes[i] = RATBAG_LED_CYCLE;
} else {
log_error(ratbag, "%s is invalid LED mode. Ignoring...\n", led_modes[i]);
}
}
}
@@ -704,7 +763,79 @@ out:
return data;
}
int
ratbag_device_data_omni_signature_match(struct ratbag *ratbag,
const char *signature,
struct input_id *id)
{
struct dirent **files;
const char *datadir;
int n, nfiles;
size_t best_len = 0;
int rc = -ENODEV;
datadir = getenv("LIBRATBAG_DATA_DIR");
if (!datadir)
datadir = LIBRATBAG_DATA_DIR;
n = scandir(datadir, &files, filter_device_files, alphasort);
if (n <= 0) {
log_error(ratbag, "Unable to locate device files in %s: %s\n",
datadir, n == 0 ? "No files found" : strerror(errno));
return rc;
}
nfiles = n;
while (n--) {
_cleanup_(g_key_file_freep) GKeyFile *keyfile = NULL;
_cleanup_(g_error_freep) GError *error = NULL;
_cleanup_(g_strfreevp) char **match_strv = NULL;
_cleanup_free_ char *omni_signature = NULL;
_cleanup_(freep) char *file = NULL;
size_t len;
unsigned int vendor, product;
if (xasprintf(&file, "%s/%s", datadir, files[n]->d_name) == -1)
goto out;
keyfile = g_key_file_new();
if (!g_key_file_load_from_file(keyfile, file, G_KEY_FILE_NONE, &error)) {
log_error(ratbag, "Failed to parse keyfile %s: %s\n",
file, error->message);
continue;
}
omni_signature = g_key_file_get_string(keyfile, "Driver/asus",
"OmniSignature", NULL);
if (!omni_signature)
continue;
len = strlen(omni_signature);
if (len <= best_len || strncmp(signature, omni_signature, len) != 0)
continue;
match_strv = g_key_file_get_string_list(keyfile, GROUP_DEVICE,
"DeviceMatch", NULL, NULL);
if (!match_strv)
continue;
/* the device must have a usb id to be resolvable */
if (sscanf(match_strv[0], "usb:%x:%x", &vendor, &product) != 2)
continue;
id->vendor = vendor;
id->product = product;
best_len = len;
rc = 0;
}
out:
while (nfiles--)
free(files[nfiles]);
free(files);
return rc;
}
/* HID++ 1.0 */
int
@@ -789,12 +920,12 @@ ratbag_device_data_hidpp20_get_report_rate(const struct ratbag_device_data *data
return data->hidpp20.report_rate;
}
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data)
uint32_t
ratbag_device_data_hidpp20_get_quirks(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP20);
return data->hidpp20.quirk;
return data->hidpp20.quirks;
}
/* SinoWealth */
@@ -895,6 +1026,13 @@ ratbag_device_data_asus_get_led_count(const struct ratbag_device_data *data)
return data->asus.led_count;
}
const int *
ratbag_device_data_asus_get_led_modes(const struct ratbag_device_data *data)
{
assert(data->drivertype == ASUS);
return data->asus.led_modes;
}
int
ratbag_device_data_asus_get_dpi_count(const struct ratbag_device_data *data)
{

View File

@@ -90,8 +90,8 @@ ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data);
int
ratbag_device_data_hidpp20_get_report_rate(const struct ratbag_device_data *data);
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data);
uint32_t
ratbag_device_data_hidpp20_get_quirks(const struct ratbag_device_data *data);
/* SinoWealth */
@@ -140,6 +140,19 @@ ratbag_device_data_steelseries_get_quirk(const struct ratbag_device_data *data);
/* ASUS */
/**
* Find the usb id of the device whose OmniSignature matches the serial
* number of a mouse behind an Omni receiver. The longest matching prefix
* wins. The bustype of id is left untouched, only vendor and product are
* set.
*
* @return 0 on success or -ENODEV if no .device file matches
*/
int
ratbag_device_data_omni_signature_match(struct ratbag *ratbag,
const char *signature,
struct input_id *id);
/**
* @return Number of profiles
*/
@@ -164,6 +177,12 @@ ratbag_device_data_asus_get_button_mapping(const struct ratbag_device_data *data
int
ratbag_device_data_asus_get_led_count(const struct ratbag_device_data *data);
/**
* @return Array of LED modes
*/
const int *
ratbag_device_data_asus_get_led_modes(const struct ratbag_device_data *data);
/**
* @return Number of DPI presets
*/

View File

@@ -21,12 +21,12 @@
$1 = (unsigned int *) malloc(($2 + 1) * sizeof(unsigned int));
for (i = 0; i < $2; i++) {
PyObject *s = PyList_GetItem($input, i);
if (!PyInt_Check(s)) {
if (!PyLong_Check(s)) {
free($1);
PyErr_SetString(PyExc_ValueError, "List items must be integers");
return NULL;
}
$1[i] = PyInt_AsLong(s);
$1[i] = PyLong_AsLong(s);
}
$1[i] = 0;
}
@@ -34,7 +34,7 @@
%typemap(argout) (unsigned int *resolutions, size_t nres) {
unsigned int i;
for (i = 0; i < $2; i++) {
PyList_SetItem($input, i, PyInt_FromLong($1[i]));
PyList_SetItem($input, i, PyLong_FromLong($1[i]));
}
}
@@ -61,24 +61,24 @@
/* uintXX_t mapping: Python -> C */
%typemap(in) uint8_t {
$1 = (uint8_t) PyInt_AsLong($input);
$1 = (uint8_t) PyLong_AsLong($input);
}
%typemap(in) uint16_t {
$1 = (uint16_t) PyInt_AsLong($input);
$1 = (uint16_t) PyLong_AsLong($input);
}
%typemap(in) uint32_t {
$1 = (uint32_t) PyInt_AsLong($input);
$1 = (uint32_t) PyLong_AsLong($input);
}
/* uintXX_t mapping: C -> Python */
%typemap(out) uint8_t {
$result = PyInt_FromLong((long) $1);
$result = PyLong_FromLong((long) $1);
}
%typemap(out) uint16_t {
$result = PyInt_FromLong((long) $1);
$result = PyLong_FromLong((long) $1);
}
%typemap(out) uint32_t {
$result = PyInt_FromLong((long) $1);
$result = PyLong_FromLong((long) $1);
}
/* Parse the header file to generate wrappers */

View File

@@ -108,11 +108,14 @@ def check_profile_type_str(string: str):
def check_section_asus(section: configparser.SectionProxy):
permitted_keys = (
"ButtonMapping",
"ButtonMappingSecondary",
"Buttons",
"DpiRange",
"Dpis",
"Leds",
"LedModes",
"Profiles",
"OmniSignature",
"Quirks",
"Wireless",
)
@@ -129,6 +132,13 @@ def check_section_asus(section: configparser.SectionProxy):
quirks = (
"DOUBLE_DPI",
"STRIX_PROFILE",
"RAW_BRIGHTNESS",
"SEPARATE_XY_DPI",
"SEPARATE_LEDS",
"BUTTONS_SECONDARY",
"OMNI_RECEIVER",
"LED_V2",
"SETTINGS_V2",
)
for quirk in section["Quirks"].split(";"):
assert quirk in quirks
@@ -136,6 +146,18 @@ def check_section_asus(section: configparser.SectionProxy):
# No such section - not an error.
pass
try:
led_modes = (
"ON",
"BREATHING",
"CYCLE",
)
for led_mode in section["LedModes"].split(";"):
assert led_mode in led_modes
except KeyError:
# No such section - not an error.
pass
def check_section_hidpp10(section: configparser.SectionProxy):
permitted = [