1
0
forked from drath/libratbag

Compare commits

...

282 Commits

Author SHA1 Message Date
Thomas H.P. Andersen
e28a6a92d6 sinowealth: add definition for repeated clicks 2020-10-05 01:49:34 +02:00
Thomas H.P. Andersen
381de107be sinowealth: add .device option for led RGB vs RBG 2020-10-05 01:49:34 +02:00
Marian Beermann
f474776a4d sinowealth: working button remapping 2020-10-05 01:40:36 +02:00
Marian Beermann
fd99aa29a7 sinowealth: SINOWEALTH_BUTTON_TPYE_SWITCH_DPI 2020-10-05 01:40:36 +02:00
Marian Beermann
53bc81f6fe sinowealth: use macro action with keycode_from_macro() 2020-10-05 01:40:36 +02:00
Marian Beermann
ff215126b2 sinowealth: fix button_data being discarded 2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
78793296a2 WIP: sinowealth: button write support
Seems like it should work - but it does not...
2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
a6cb7f8301 sinowealth: revamp button reading 2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
9cb444bb07 sinowealth: use enum for sensor 2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
121a843071 WIP: sinowealth: read button mapping 2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
62a620c056 sinowealth: use commonly available breathing mode
The DM5 does not have the RGB_BREATHING1 mode, but
does have the RGB_BREATHING7 mode. This changes the
driver to map to the commonly available mode.
2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
9d30ec5437 sinowealth: read device-specific from .device file
This moves device-specific specifications to the
.device files. The number of LEDs and buttons varies
as well as sensor (dpi specs)
2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
e570e29a1e sinowealth: parse led color as RGB
This was parsed as RBG but this is not the layout used on the
DM5. The RBG-parsing seems intentional, so this might need to
flip on either device or firmware version.
2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
1f79d8e6e2 add device file for Dream Machines DM5 Blink 2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
e753db72f9 meson: bump required meson version
Bump the required meson version to match the features that we make
use of.

We now also make use of the 'copy:' keyword in configure_file that
is available in version 0.47.
2020-10-05 00:39:43 +01:00
Thomas H.P. Andersen
e1f4fac9a2 ratbagd-json: fix compilation with clang
Compilation on clang failed as we would try to jump to
the 'out' label and then jump over the initialization
of a variable with __attribute__((cleanup)).

We move the initaliztion up to make sure it happens before
the goto.
2020-09-29 02:11:09 +01:00
Thomas H.P. Andersen
8f981c8b87 circleci: bump to fedora 32
Bump to fedora 32 to updated meson version
2020-09-29 02:08:17 +01:00
Thomas H.P. Andersen
01d2cf8477 sinowealth: silence warning 2020-09-29 01:07:12 +02:00
Filipe Laíns
f7c22ed88c libratbag 0.15
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-09-25 21:38:01 +01:00
Thomas H.P. Andersen
aeda65cbfa sinowealth: remove per-resolution led
The glorious mice uses a single profile. To differentiate between
resolutions each one has customizable color. A led on the bottom
of the mouse shows the active resolution color.

Libratbag does not support a per-resolution color.

To allow adjusting these colors the sinowealth driver defines a led
for each resolution. This is a misuse of the model and will cause
problems when the mouse is added to Piper.

This patch removes the per-resolution leds and hardcodes the led
count to one.
2020-09-25 20:51:45 +01:00
Pipat Saengow
ed2439e5e2 sinowealthnubwo: more reasonable default and refactor 2020-09-21 17:39:28 +02:00
Pipat Saengow
f407521e8f Add driver for Nubwo X7 Spectrum
Nubwo X7 Spectrum is a sinowealth-based gaming mouse.
The sinowealth-nubwo driver included in this commit provides

- LED support (except color mode that are not available in libratbag)
- DPI and reporting support
- NO button mapping/macros support
2020-09-21 17:39:28 +02:00
heimer16
3c4fe23f70 data: update Logitech g9x (#913)
* add LED for Logitech g9x
* update min dpi range and step for Logitech g9x

Co-authored-by: Erin Heimer <heimer16@gmail.com>
2020-09-11 22:28:57 +02:00
Mike Crowe
9e2ac55dbc libratbag-private.h: Ensure that _GNU_SOURCE is defined early enough
When compiling on Ubuntu 20.04 with GCC 9.3.0 and glibc 2.31, I get:

In file included from ../src/libratbag-private.h:32,
                 from ../src/driver-sinowealth.c:24:
../src/libratbag-util.h: In function ‘asprintf_safe’:
../src/libratbag-util.h:154:7: warning: implicit declaration of function ‘vasprintf’; did you mean ‘vsprintf’? [-Wimplicit-function-declaration]
  154 |  rc = vasprintf(&result, fmt, args);
      |       ^~~~~~~~~
      |       vsprintf

This appears to be because <features.h> is being included by another
header before config.h has been included. If _GNU_SOURCE has not been
defined at that point, then the other macros that control whether
vasprintf is declared are not set correctly.

Include config.h right at the top of libratbag-private.h to ensure
that _GNU_SOURCE is defined before any system headers are included to
avoid this problem.
2020-08-30 19:16:51 +02:00
Nguyễn Chính Hữu
1e72ebaa7b data: add the G203 name to the .device file 2020-08-29 22:23:35 +01:00
peelz
920bc2b993 driver-hidpp10: fix segfault when committing unnamed profiles 2020-08-23 16:21:05 +01:00
TurkeyTheGobbler
20b573668e g300: use the correct step size 2020-07-25 17:26:37 +01:00
Daniel Lehmann
49acc11db3 Fix enumeration of Logitech G5 (2007)
Before this patch, ratbagd would abort initialization of the device because
no profiles are returned.

Once that was handled more gracefully, ratbagd would fail because it doesn't
read the DpiList from its own config file, as no ProfileType is specified.
Specifying G500 as the closest match.
2020-07-23 01:04:14 +01:00
Stephan Lachnit
364e3fd965 data: add additional USB-ID for the M720 Thriatlon
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-07-23 01:00:33 +01:00
Marian Beermann
bb230d390d sinowealth: fix reading RGB brightness from device 2020-07-23 00:45:29 +01:00
Marian Beermann
93a076bed9 sinowealth: code style changes 2020-07-23 00:45:29 +01:00
Marian Beermann
e7b50c9061 sinowealth: rewrite RGB LED mode conversions 2020-07-23 00:45:29 +01:00
Marian Beermann
9d533bdc3e sinowealth: fix dpi_count not being updated
The firmware of these mice only considers the first dpi_count zero bits
in dpi_enabled.
2020-07-23 00:45:29 +01:00
Marian Beermann
d7906b54b4 sinowealth: code cleanup and style changes 2020-07-23 00:45:29 +01:00
Marian Beermann
f1eba370ed sinowealth: do not typedef structs 2020-07-23 00:45:29 +01:00
Marian Beermann
7b5ab2ed99 sinowealth: separate capability setting logic from read_profile 2020-07-23 00:45:29 +01:00
Marian Beermann
d2d5471836 sinowealth: implement brightness and speed controls 2020-07-23 00:45:29 +01:00
Marian Beermann
9cfc1e98e3 sinowealth: fixed rgb
Turns out it was caused by two mistakes:
1.) While writing the struct I overlooked a changed offset in my notes,
    so a bunch of padding was missing in the middle of the struct (unk4[33]);
    that padding is probably the set of colors (11) used by tail mode
2.) The LED bodies actually use RBG ordering instead of RGB
2020-07-23 00:45:29 +01:00
Marian Beermann
691661276f pad struct sinowealth_config_report to full size of report 2020-07-23 00:45:29 +01:00
Marian Beermann
25ed99db84 Add sinowealth driver 2020-07-23 00:45:29 +01:00
Dejan
02226d0428 data: add another device ID for G203 mouse 2020-07-09 21:54:56 +01:00
Stephan Lachnit
f822fb0fa7 remove unused string format to fix flake8 (#1024)
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-07-06 11:48:47 +02:00
Stephan Lachnit
b88be0add0 ci: bump Ubuntu to 20.04
This also removes the manual addition of the universe repository, since it's already enabled in the circleci image.
Because of this, software-properties-common doesn't need to be installed anymore, and libnss-systemd isn't going to be installed (see c425780220).
However, DEBIAN_FRONTEND=noninteractive needs to be set, else the image will loop while trying to configure tzdata (aka the timezome).

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-06-09 10:55:31 +10:00
Stephan Lachnit
9fc6d12486 data: add additional USB-ID for Roccat Kone Pure
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-06-07 20:58:41 +02:00
Stephan Lachnit
0c64dad9e1 fix wrong driver id for Roccat Kone Pure
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-06-07 20:58:41 +02:00
Farid Gh
f61485c603 data: add bluetooth ID to the MX Vertical 2020-05-29 13:57:21 +01:00
Filipe Laíns
8c295479d8 hidpp20: keep unsupported button mappings
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-05-25 18:08:40 +01:00
Dylan Baker
e55b04ec7d meson: Fix python3 version detection with python3 module
I misread the documentation about the python3 module. The newer python
module puts the language_version in the object returned by
`pymod.find_installation()`, while the python3 module has the same
method in the *module*.

Fixes #985
2020-05-19 19:47:24 +01:00
Filipe Laíns
1231415470 libratbag 0.14
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-05-17 21:23:57 +01:00
Filipe Laíns
660da27021 data: add Logitech G935
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-05-17 21:19:32 +01:00
Dylan Baker
481b6f029a meson: make finding python more robust
This code assumes that there is a python3 or python3-embed pkg-config
file on the system, which may or may not be true. On Gentoo for example,
only python-X.Y pkg-config files are available and this will fail to
build.

This makes use of the new py_installation.dependency(embed : true) if
possible, and falls back to trying to find a python that matches the
version of python found by the python3 or python module.
2020-05-17 19:19:42 +01:00
Filipe Laíns
768d93a45a github: add device info to bug report template
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-29 14:23:47 +01:00
ksv1986
1c6e039d01 hidpp10: fix handling of hidpp10_device_new() failures
Signed-off-by: ksv1986 <ksv0x07c2@gmail.com>
2020-04-26 15:07:23 +01:00
ksv1986
d16df8d100 hidpp10: fix crash if device failed to initialize properly
Signed-off-by: ksv1986 <ksv0x07c2@gmail.com>
2020-04-26 15:07:23 +01:00
Filipe Laíns
4b1e49d522 hidpp20: fix report type in ColorLedEffects (0x8070) set_zone_effect
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-26 15:07:06 +01:00
Filipe Laíns
786cebcb0b hidpp20: cleanup, use short reports when possible
libratbag has a quite communication-intensive setup routine. This patch
uses short reports when possible. Using short reports will speed up the
setup process and have a lower power consumption, which is relevant for
wireless devices.

We now check the report descriptor for the supported report IDs and
adjust the report type used based on the device capabilities. Because of
that, this patch should work without any issues as the report type will
get overwritten if it not supported.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-26 15:07:06 +01:00
Filipe Laíns
d53454bcb3 hidpp10: fix circular loop on hidpp10_onboard_profiles_read_macro
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-26 15:06:25 +01:00
Filipe Laíns
a0def65b64 hidpp10: add debug log on hidpp10_onboard_profiles_parse_macro fail
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-26 15:06:25 +01:00
Stephan Lachnit
2a40279903 doc: replace some tabs with spaces
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-04-26 15:06:08 +01:00
Filipe Laíns
02c95422f0 hidpp20: fix mapping button to consumer usages
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-18 02:31:17 +01:00
Filipe Laíns
4ce1cf2807 hidpp10: convert key actions to macro
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-18 02:31:17 +01:00
Filipe Laíns
89168f2187 hidpp10: add support from writting simple macro actions
Fixes #900

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-18 02:31:17 +01:00
Filipe Laíns
bb92337268 ci: remove dbus policy dir hack in arch
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-18 02:29:05 +01:00
Filipe Laíns
a8316c07f3 ratbagd: create path if not present before installing dbus policy dir
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-18 02:29:05 +01:00
Filipe Laíns
39769c57dd ci: save cache between jobs for fedora and arch
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-17 05:57:38 +01:00
Filipe Laíns
200a8af7a7 ci: cache packages in arch
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-17 05:57:38 +01:00
FFY00
fcf9f7745d ci: add arch
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-17 05:57:38 +01:00
Filipe Laíns
633c9fc55d libratbag-data: add missing header entry (#958)
Dicovered in #957

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-16 15:59:50 +02:00
Peter Hutterer
ee73434ead circleci: bump to use ubuntu 19.10
The 19.04 repos give us 404s, so let's update to the next version.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2020-04-16 08:01:53 +10:00
Filipe Laíns
d60b1c9d71 data: overwrite led count in G305
Fixes #949

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-14 19:00:12 +01:00
Filipe Laíns
88a60f55b4 hidpp20: allow overwritting the led count
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-14 19:00:12 +01:00
D33M0N
dba202ae8b data: added bluetooth ID to the MX Anywhere 2 2020-04-03 19:27:02 +01:00
Filipe Laíns
6cdcce9de4 hidpp20: log error number when commiting
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-03 17:02:44 +02:00
Filipe Laíns
e2c3373145 hidpp10: log errors when comitting
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-03 17:02:44 +02:00
Filipe Laíns
02575a9008 hidpp20: add button/macro opcodes
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-03-22 19:17:47 +00:00
Filipe Laíns
1097b1ba68 meson: silence swig warnings
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-03-22 19:13:58 +00:00
Filipe Laíns
77b67775da ratbagctl: don't print empty properties
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-03-22 19:08:58 +00:00
Filipe Laíns
5b23c9ebae data: add Logitech M720
Fixes #939

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-03-22 18:58:07 +00:00
Stephan Lachnit
6d0974be25 circleci: add diffutils dependency for fedora
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-02-21 15:21:42 +01:00
simrat39
ea9111a2a6 devices: Set g402 LedTypes
Signed-off-by: simrat39 <simrats169169@gmail.com>
2020-02-10 15:49:45 +00:00
Filipe Laíns
42787ee422 meson: libratbag 0.13
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:32:55 +00:00
Filipe Laíns
611dd9be0d hidpp20: keep unsupported effects
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
fd20f9e642 hidpp20: simplify hidpp20_onboard_profiles_write_led
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
9be5576967 hidpp20: add some missing led effects
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
9fe511fcc5 hidpp20: add profile field to special bindings in onboard profiles
New devices define a profile field in the button configuration. This
allows us to map a button to set the target profile as active. Even
though we don't support configuring this right now, some devices, such
as the G915/G815, come pre-configured with this action on th M-Keys.
This patch makes us able to read and write those settings, this way they
don't get lost when we write a profile to the device.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
f0b69aab19 hidpp20: only set active resolution if there are resolutions on 0x8100
This prevents us from trying to set a resolution on keyboards.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
7610b80ced hidpp20: log errors when failed to write sector
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
f0a05bb522 data: add the Logitech G Powerplay
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-10 08:24:10 +10:00
Torbjørn Fylling
2669cc28b2 data: add support for Logitech G815 keyboard 2020-01-31 10:13:37 +00:00
Peter Hutterer
302702d94a Declare a bunch of structs as extern
gcc 10 complains about multiple definitions of these when linking.

/usr/bin/ld: libhidpp.a(src_hidpp-generic.c.o):/builddir/build/BUILD/libratbag-0.12/x86_64-redhat-linux-gnu/../src/libratbag-private.h:588:
multiple definition of `etekcity_driver';
libutil.a(src_libratbag-util.c.o):/builddir/build/BUILD/libratbag-0.12/x86_64-redhat-linux-gnu/../src/libratbag-private.h:588:
first defined here

Special case here is the test_driver struct, without the extern gcc seems to
think it's a new struct with all zeros, causing a segfault when we then try to
strcmp the test driver name.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2020-01-29 02:12:28 +00:00
Filipe Laíns
e1136c4d30 hidpp20: add debug log when failed to read led (8070, getZoneEffects)
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-28 21:41:45 +01:00
Filipe Laíns
0012853a1d hidpp20: make sure getZoneEffect (8070) is supported
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-28 21:41:45 +01:00
Filipe Laíns
afb85f868c hidpp20: change unsupported effect log msg to debug verbosity
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-28 21:41:45 +01:00
Filipe Laíns
b76fcdadde hidpp20: enable 0x8070 (ColorLedEffects) standalone use
0x8071 is still missing but as of now we don't have any device that
needs it.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-28 21:41:45 +01:00
Stephan Lachnit
ba7c092ee2 driver-hidpp20: replace goto with return
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-01-08 07:36:52 +10:00
Stephan Lachnit
3483bd8c14 driver-hidpp20: minor cleanup
Removes useless setting of the dpi index when updating resolutions.
Removes weird empty line.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-01-08 07:36:52 +10:00
Filipe Laíns
0fb0eb3d42 hidpp20: log errors in hidpp20drv_commit
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:22:57 +00:00
Filipe Laíns
5fb1f4429d ratbagd: log error if failed to commit
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:22:57 +00:00
Filipe Laíns
5f5e294598 hidpp20: add support for reading/writing the report rate via 0x8060
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:18:06 +00:00
Filipe Laíns
b4218737cd hidpp20: fix bug that was unsetting some capabilities
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:18:06 +00:00
Filipe Laíns
3875bb70fd hidpp20: implement dirty flag for report rate
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:13:01 +00:00
Filipe Laíns
e9d67b7235 ratbag: add dirty flag for report rate
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:13:01 +00:00
Filipe Laíns
8a476a8724 hidpp: fix supported_report_types data type
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-03 22:40:03 +00:00
Sadika Sumanapala
d67cbec5f8 Add M590 bluetooth ID 2020-01-03 08:17:43 +10:00
Stephan Lachnit
f82c6f72b6 Use EditorConfig instead of vim comments
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-24 08:04:11 +10:00
Stephan Lachnit
af30fc28cd CircleCI: use variables for dependencies
Use separate variable lists for dependencies.
Uninstall check merged in upstream meson, so it can be simplified.
`libsolv` doesn't need to be updated any more with Fedora 31.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-24 07:34:18 +10:00
Filipe Laíns
3fb8a51456 docs: adjust description to say we support other devices than mice
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-23 06:26:55 +10:00
Matthias Bach
ceaa3c3ad5 Reorder the initialisation of the ratbagd client
When the API version mismatches we want to fail early before performing
any more complex initialisation. We do however have to check for
devices first so that we can distinguish cases where we cannot access
the daemon from those where it is too old to provide an API version.
2019-12-22 16:58:20 +00:00
Matthias Bach
551b918ba8 Give a more specific error message if user cannot reach ratbagd via D-BUS
If access failed, e.g. due to insufficient permissions, don't confuse
the user by reporting an API mismatch.

This is a minimal improvement to target issue #802. As, if access is
restricted to a group, that group is known in Meson, it would even be
possible to report that group in the error message. But, that would
require converting ratbagd.py to a template or finding some other way to
inject that information at build time.
2019-12-22 16:58:20 +00:00
Filipe Laíns
3105428ba1 tests: fix data parse for hidpp index
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-20 10:30:10 +01:00
Benjamin Tissoires
b3f2406180 data: add G910
Same situation than the G513, 0x8100 is not present.
But heh.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2019-12-20 09:15:24 +00:00
Benjamin Tissoires
902160f430 ratbag-command fixes
- File "./build/ratbag-command", line 2289, in open_ratbagd
    r = Ratbagd(1)
  TypeError: __init__() takes 1 positional argument but 2 were given
- AttributeError: module 'libratbag' has no attribute
  'ratbag_device_get_bus'
- File "./build/ratbag-command", line 267, in model
    return "{}:{04x}:{04x}:{d}".format(bus, vid, pid, version)
  KeyError: '04x'
- swig/python detected a memory leak of type 'uint32_t *', no destructor
  found.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2019-12-19 15:56:52 +01:00
Filipe Laíns
fd6d0b6198 data: add G513
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 13:58:02 +01:00
Filipe Laíns
5ed98fdb22 hidpp20: only enable 0x8070 or 0x8071 if 0x8100 is present
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 13:58:02 +01:00
Filipe Laíns
e0e73c2971 data: add G915
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 12:06:15 +00:00
Filipe Laíns
0d3b4f1b64 libratbag: add 'switches' led type
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 12:06:15 +00:00
Filipe Laíns
20b6e3deeb hidpp20: only populate resolutions if the device supports
This patch moves the 0x8100 initialization routine out of
hidpp20drv_init_feature because it needs the capabilities to be
processed first.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 12:05:54 +00:00
Filipe Laíns
ffcdee912d libratbag: allow no resolutions
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 12:05:54 +00:00
Filipe Laíns
5357d4cbc2 meson: libratbag 0.12
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-18 19:25:30 +00:00
Filipe Laíns
26c534cc74 hidpp20: check supported report types
Fixes #854

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-18 19:23:31 +00:00
Stephan Lachnit
904c7988bb hidpp20: set dictionary output to raw
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-17 15:54:42 +00:00
Stephan Lachnit
7d255a2f93 Fix missing capability in ratbagctl.test
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-17 15:54:42 +00:00
Stephan Lachnit
d0a5cb1f0e Minor ratbagd/ratbagc adjustments
Bumped copyright to 2019.
Fixed typo in profile.capabilities description.
only ratbagd.py: Removed vim helper line since the file has a proper file extension.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-17 15:54:42 +00:00
Qiu Wenbo
71b1d66287 hidpp20: fix a double free error of hidpp20
These are two fields of a union and should have the same value. What the
code doing here is trying to free a pointer twice.

Signed-off-by: Qiu Wenbo <qiuwenbo@phytium.com.cn>
2019-12-14 14:25:44 +00:00
Qiu Wenbo
32ebbca42f hidpp20: fix G305 quirk detection
Signed-off-by: Qiu Wenbo <qiuwenbo@phytium.com.cn>
2019-12-14 14:25:44 +00:00
Stephan Lachnit
a0afbfeb3f Update CircleCI config
Rename fedora_latest to fedora_31 which is more representative.
Drop the Fedora Rawhide workflow which was always disabled anyway.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-13 09:34:26 +10:00
Stephan Lachnit
9dfd4fd03a remove .gitignore
Use a personal .gitignore for your build directory instead.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-12 20:06:16 +00:00
Peter Hutterer
b0ea75979a data: add a test for unifying receiver IDs
We don't want any device files that use the receiver itself as a match so
let's test for that. In virtually all cases this means the user must update
the kernel and hope support for that receiver is already present in that
version.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-12-11 00:27:35 +00:00
Stephan Lachnit
a5c1ca202e drivers: add Roccat Kone Pure driver
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-11 00:20:02 +00:00
Peter Hutterer
8d40ad6c0c libratbag: only log to stderr for error messages
This was copied from libinput and made sense while libratbag was a library -
we want "unhandled" messages to end up somewhere and stderr is the most likely
candidate here.

Now that we always run within ratbagd and it's all effectively inseperable,
just log to stdout for anything below error.

The switch statement was reformatted for consistency, only the ERROR case has
changed code.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-12-11 09:07:10 +10:00
Peter Hutterer
72d11de597 ratbagd: print two verbose messages at startup
When no device is present, starting ratbagd even with --verbose prints exactly
nothing. Let's be more verbose here to give the user an indication that
something's happening. The user wants everything, is probably willing to
accept anything but in the end all we can say is that we have nothing.

Related to #862

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-12-10 22:48:53 +00:00
Filipe Laíns
47cc5fce92 github: add issue templates
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-05 10:01:59 +00:00
Filipe Laíns
5da791b268 ratbagd: support the old --verbose=raw syntax
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-05 09:44:35 +00:00
Filipe Laíns
e63802b01a ratbagd: make raw the default verbosity level (#857)
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-05 08:07:46 +10:00
Jeff Huang
cd89b123ce Add Logitech M585/M590 support. (#845) 2019-11-27 15:52:18 +10:00
Peter Hutterer
f6546b3222 circle CI: add diff as fedora package
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-11-18 08:50:36 +10:00
Peter Hutterer
cd8ddc8444 circle CI: switch to Fedora 31
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-11-18 07:16:22 +10:00
Luke
cdb3226ef1 meson: don't use brackets, parenthesis and braces in filenames
Renamed filenames to avoid brackets, and added a check to data-parse-test.py to
check for brackets, parentheses, and braces so they aren't shipped in filenames
in the future.
2019-11-17 00:11:42 +00:00
Peter Hutterer
ce377fc36c meson.build: libratbag 0.11
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-11-05 11:06:27 +10:00
Mateusz Mojsiejuk
fe1308fe38 data: add Steelseries Kinzu V3 2019-11-02 21:21:41 +00:00
FFY00
983d3e2a88 data: fix G502 Hero Wireless .device file
Signed-off-by: FFY00 <lains@archlinux.org>
2019-10-23 18:47:17 +01:00
Filipe Laíns
3a3d9e1148 devices: add Logitech G604
Fixes #827

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-10-23 18:46:44 +01:00
Pieter
afbba26feb data: add wireded ID to G502 Hero Wireless 2019-10-23 18:45:14 +01:00
Peter Hutterer
8cc37047fb tools: fix typo in ratbagd.py
Reported in https://github.com/libratbag/piper/issues/406

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-10-16 10:50:58 +10:00
Benjamin Tissoires
c8f56cc535 swig: generate python helpers to use hidpp C library
```
$> sudo python3
Python 3.7.1 (default, Nov 23 2018, 10:01:49)
[GCC 8.2.1 20181105 (Red Hat 8.2.1-5)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import hidpp
>>> f = open('/dev/hidraw1', 'r+b')
>>> base = hidpp.hidpp_device()
>>> hidpp.hidpp_device_init(base, f.fileno())
>>> dev = hidpp.hidpp20_device_new(base, 0xff)
>>> hidpp.hidpp20_feature_get_name(0x8100)
'HIDPP_PAGE_ONBOARD_PROFILES'
>>> hidpp.new_uint8_tp()
<Swig Object of type 'uint8_t *' at 0x7f865198fb70>
>>> feature_index = hidpp.new_uint8_tp()
>>> feature_type = hidpp.new_uint8_tp()
>>> feature_version = hidpp.new_uint8_tp()
>>> hidpp.hidpp_root_get_feature(dev, 0x8100, feature_index, feature_type, feature_version)
0
>>> hidpp.uint8_tp_value(feature_index)
14
```

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-09-26 09:12:42 +01:00
Dustin Falgout
b6936ddd3b Add Support For Logitech MX Master 3 (#819) 2019-09-20 15:32:43 +10:00
Filipe Laíns
d4d9712316 hidpp20: add suport for the G602
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-09-11 09:43:41 +01:00
Filipe Laíns
2353af214b hidpp20: fallback to 1st profile when no 8100 profile is active
When the windows driver puts a device in host mode it will mark all 8100
profiles as disabled. If no 8100 profile is enabled no libratbag profile
will get enabled. This patch prevents this by marking the first profile
as enabled if no profile is already enabled.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-09-11 09:33:45 +01:00
Filipe Laíns
a4d19efb93 hidpp20: fix reading cluster info in the 8071 feature
TBH I don't know why I wrote the original code in the first place, it
clearly doesn't follow the spec.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-09-11 15:09:43 +10:00
Filipe Laíns
cb0b458824 hidpp20: make cluster_info a single variable in hidpp20drv_read_led_8071
This was probably leftover code.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-09-11 15:09:43 +10:00
Adrian Pascu
48c476b28c Add missing MX master PID
As seen here: https://github.com/torvalds/linux/blob/master/drivers/hid/hid-logitech-hidpp.c#L3717
This mouse is a warranty replacement, shows up as MX Master in Logitech Options but has MX Master 2S Hardware.
2019-09-02 13:24:18 +10:00
Filipe Laíns
d6c3bf881d hidp20: rename profile mapping 0x04
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-30 11:11:23 +01:00
rHermes
1b000e627a Add G502 Hero Wireless to meson.build 2019-08-20 11:03:54 +02:00
rHermes
75bcf39114 Add Logitech G502 Hero Wireless device 2019-08-20 11:03:54 +02:00
Peter Hutterer
33ced3dfb7 hidpp10: if we can't fetch the current profile, error out
Right now we initialize any hidpp10 device with one profile and then go and
fetch the data. But we never check for return codes, so it always succeeds
even when we can't get anything out of the device at all. Eventually we fail
the sanity checks with "invalid dpi list" which is confusing at best.

Let's avoid this by requiring at least the current profile to work. This will
break devices that have a single emulated profile, i.e. those that don't have
profiles but rely on libratbag to emulate one based on the
current-resolution/current-... calls. Not sure we actually support any of
those, so let's see.

Closes #738 though it doesn't fix it, it just bails out better.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-19 15:49:24 +02:00
Peter Hutterer
906f17add2 Rename ratbag_test_driver to ratbag_try_driver
This is confusing because we do have a test driver and we have an optional
test_device argument here too.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-19 15:49:24 +02:00
Filipe Laíns
016de548fc hidpp20: fix setting default resolution
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-17 10:02:28 +02:00
banditopazzo
665c022382 added bluetooth device ID in profiles for Logitech MX Master AMZ (amazon) 2019-08-15 08:37:58 +10:00
Filipe Laíns
81c7b42bec hidpp20/data: add the G305
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-13 08:52:06 +01:00
Filipe Laíns
edb054c6f8 hidpp20: add quirk system
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-13 08:52:06 +01:00
Peter Hutterer
8ae995b72d hidpp10: try switching to profile 0 where the device is in factory profiles
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-04 17:17:56 +02:00
Peter Hutterer
a941b75ef2 hidpp10: if the current profile is the factory profile, pretend it's 0
Rather than bail out completely, let's pretend the factory profile is profile
0 and keep going. This gives us a pretend-function mouse, though whether this
actually works is questionable.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-04 17:17:56 +02:00
Peter Hutterer
8cb85da6be release.sh: make this script usable for piper
Remove some hardcoded names and fix the version number extraction.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-04 17:17:45 +02:00
Peter Hutterer
7ff59a636f release.sh: drop the module file and section handling
leftover from when this was the xorg release script, we need neither here

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-04 17:17:45 +02:00
Peter Hutterer
6cd050f541 release.sh: fix version number replacement
sed treats . as "any character", so this breaks if we have a 0.10 version
number.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-04 17:17:45 +02:00
Peter Hutterer
ab3f1df06e meson.build: libratbag 0.10
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-02 11:19:16 +10:00
Peter Hutterer
caa31e17cf circle CI: add meson plain and release build types
Second time lucky.

Fixes #770

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 20:02:28 +10:00
Peter Hutterer
b7e6103496 circleci: move existing config to the new location
CircleCI 2.0 requires a new config file location, otherwise it won't run. And
since 1.0 is sunset now, this means our CI wasn't getting executed.

The actual format of the config is the same, we were already using 2.0
features.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 10:24:09 +10:00
Peter Hutterer
b1b6a2761e Revert "circle CI: add meson plain and release build types"
This broke CircleCI but since it didn't run for that build, no-one noticed.

This reverts commit 3bc31edd72.
2019-08-01 10:24:09 +10:00
Peter Hutterer
11f4f32947 Revert "hidpp20: add quirk system"
This reverts commit 196d53e65f.
2019-08-01 09:47:00 +10:00
Peter Hutterer
4f5d0dffec Revert "hidpp20/data: add the G305"
This reverts commit cad05ffced.
2019-08-01 09:47:00 +10:00
Peter Hutterer
d213bdb86e data: add a test for duplicate DeviceMatch lines
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 09:22:03 +10:00
Peter Hutterer
e69e1de17b devices: drop the G700s wireless entry
The G700 and G700s both use the receiver's product id, so they both end up
having the same DeviceMatch. One of those two never gets loaded but which one
is implementation-defined and may change accidentally.

Let's drop the G700s entry because the G700 is closer to a lowest comon
denominator. Once we have kernel support for the actual product id, we can
re-instante them.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 09:22:03 +10:00
Filipe Laíns
cad05ffced hidpp20/data: add the G305
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-01 09:20:47 +10:00
Filipe Laíns
196d53e65f hidpp20: add quirk system
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-01 09:20:47 +10:00
Peter Hutterer
c606b1abe3 tools: flake8 fixes
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 09:08:06 +10:00
Peter Hutterer
b4009669b9 ratbagd: Add an APIVersion property to make version checks easier
While the DBus API is still in flux, things can break at any time.

Keeping piper and libratbag in version-step works terribly, doubly so where
piper is flatpaked but ratbagd comes from some repo somewhere. We can't check
at build-time, we can't run ratbagd --version at runtime.

so let's add a version field for now and drop it if we ever get to stable.
Meanwhile, ratbagd.py will throw an exception if the expected version doesn't
match exactly.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 09:08:06 +10:00
Peter Hutterer
d458305f93 tools: ratbagdctl.test: if an exception occurs during startup, terminate
Don't leave a ratbagd.devel process hanging if something goes wrong.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 09:08:06 +10:00
Peter Hutterer
9c11960665 ratbagd: document the various commandline options in the man page
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 08:46:19 +10:00
Peter Hutterer
e8fc8b45d1 ratbagd: drop the hardcoded test device
Force the tests to load a test device with JSON that matches their
expectations instead of having a magic device pre-setup where the tests have
to know what the values are.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 08:36:16 +10:00
Peter Hutterer
e64a121572 tools: switch the button tests over to json 2019-07-31 08:36:16 +10:00
Peter Hutterer
607e47953c tools: switch led tests to use the json parsing 2019-07-31 08:36:16 +10:00
Peter Hutterer
a5816055dc tools: switch the report rate to json 2019-07-31 08:36:16 +10:00
Peter Hutterer
62ac0772f5 tools: switch the dpi test to json 2019-07-31 08:36:16 +10:00
Peter Hutterer
fc9e63a5aa tools: switch the resolution tests to use the json loading 2019-07-31 08:36:16 +10:00
Peter Hutterer
936a8f7a92 tools: switch the ratbagctl.test to use the new json loading 2019-07-31 08:36:16 +10:00
Peter Hutterer
818e665853 ratbagd: add ability to load json for test devices
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 08:36:16 +10:00
Peter Hutterer
3a3b11ade4 ratbagctl: autogenerate the choices list for the specials and led modes
Let's not maintain that list manually if we can fetch it from the enums.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 12:39:11 +02:00
Peter Hutterer
3bc31edd72 circle CI: add meson plain and release build types
Fixes #770

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 12:31:01 +02:00
Peter Hutterer
485df20a83 tools: fix typo in ratbagd.py
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 19:30:11 +10:00
Peter Hutterer
49291c4b45 ratbagd: encode the test device path without underscores
sd-bus encodes underscores as _5f which looks like memory corruption. Let's
skip them, they don't add value anyway.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 19:28:07 +10:00
Filipe Laíns
02ec2442d3 hidpp20: add support for RGB Effects feature (0x8071)
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-30 15:12:10 +10:00
Filipe Laíns
a8b4d9c6f3 hidpp20: add new profile layout (0x04)
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-30 15:11:54 +10:00
Peter Hutterer
22a6ac1c7e hidpp10: make raw debugging messages a bit easier to read
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 12:01:42 +10:00
Peter Hutterer
42c302c860 tools: fix a few comments referring to old names
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 09:53:36 +10:00
Peter Hutterer
e2b1581aef tools: ratbagctl.test: set the data dir correctly
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 09:35:56 +10:00
Peter Hutterer
10a3e87980 meson.build: remove unused fallback SVG name
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 06:55:30 +10:00
Peter Hutterer
2d465a63e1 meson: drop the unused libratbag version define
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 06:52:14 +10:00
Eric Culp
c2315323f4 meson.build: fix builds with --buildtype=plain
When building with --buildtype=plain, the build fails with the following
error:

    cc1: error: ‘-Wformat-security’ ignored without ‘-Wformat’ [-Werror=format-security]

-Werror=format-security was recently added in 43def1b3a5 by #745
2019-07-27 09:43:49 +10:00
Peter Hutterer
05d7e9b324 ratbagd: poke DBus to reload its config after installing the devel policy
Fixes #753
2019-07-26 16:42:01 +10:00
Peter Hutterer
71a399f20f ratbagd: encode the test device iteration in the test device name
The sequence triggered by the ratbagctl.test suite is:
- DBus call Commit()
- ratbagd schedules the callback as idle func
- DBus call ResetTestDevice()
- ratbagd cleans up the test device
- callback is called on next idle
  - boom! the device is already freed

To avoid this, we need to take a ref of the device while it's being scheduled
for the commit handling. But that blocks the object paths for new test devices
because the test device name is encoded in those paths (like "hidraw" for real
devices) and it doesn't change. So let's add a count to the test device name
to make sure the new device doesn't re-use the old device's paths.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 16:41:04 +10:00
Peter Hutterer
8259c2e706 ratbagd: introduce ref/unref for ratbagd_device pointers
We'll need to keep references around to devices even when caller is done with
them.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 16:41:04 +10:00
Peter Hutterer
89980a4a72 meson: add the prefix to the dbus dir
Otherwise where the directory is used within meson.build, the prefix would be
missing. No real effect since we don't use it anywhere but let's be safe.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
79e94b4a7e ratbagd: install the devel dbus policy file on startup
Don't rely on some other tool to install the policy file, let's just do this
in ratbagd.devel directly so it's immediately executable.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
122f05c272 tools: drop the RATBAGCTL_DEVEL environ, just use RATBAG_TEST instead
Which is the same env that ratbagd uses as well. And we don't have variable
values in RATBAGCTL_DEVEL anymore, so no point in having two different
variables here.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
b06ac1035e tools: add --use-existing-ratbagd to the ratbagctl.test script as well
This makes it possible to run the script against a manually started
ratbagd.devel - with a lot less effort.

And since we're don't need to be root to do this, let's move that check too.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
647fe432b2 tools: add argument to ratbagctl.devel to connect to running ratbagd
This makes it a lot easier to start ratbagd.devel in gdb, then run
ratbagctl.devel against it.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
4e2e9f037b Drop the ratbagctl.devel sha bus name
We already have a separate name for the devel bus anyway, the git sha doesn't
give us a huge amount of benefits but makes manual debugging a major PITA.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
f92fee0b15 meson: document the various built bits a bit better
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
d7c394f379 tools: ratbactl.test: if RATBAGCTL_DEVEL is set, use the existing server
When there's a bug that's triggered by the test suite it's hard to figure out
what exactly crashes. So allow starting ratbagd.devel manually and run the
test suite against that process so we can gdb it.

The steps required for this are a bit insane, so the instructions are listed.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 21:38:12 +10:00
Peter Hutterer
7fec19f8f2 test: switch the resolution capabilities to an array
Just like the profile ones

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 09:08:55 +02:00
Peter Hutterer
11c1e2d71a Fix setting the capability flags on the resolutions
2 hours of debugging... ffs

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 09:08:55 +02:00
Peter Hutterer
e2cbaf9dea tools: print the Model in ratbagctl info
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 07:26:27 +02:00
Peter Hutterer
bf9d86e228 hidpp10: if the profile name is the empty string, make it NULL
Because our profile struct can never have an NULL name, let's assume the
empty string equals NULL.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 13:02:18 +10:00
Filipe Laíns
f24a3bd441 hidpp20: fix 0x1001 comments, add missing ASE and use get_unaligned_be_u16
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-24 19:17:31 +10:00
Filipe Laíns
df5742ebfe libratbag-util: move data manipulation functions out of hidpp-generic
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-24 19:17:21 +10:00
Peter Hutterer
0c569e0bf7 tools: use super() in the ratbagctl test
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-24 09:16:24 +02:00
Peter Hutterer
5ea1731f38 tools: use super() without arguments in ratbagctl
I *think* this is a Python2 leftover or something. Either way, let's remove
it.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-24 09:16:24 +02:00
Peter Hutterer
572064aba8 tools: use super() instead of the named parent class in ratbagd.py
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-24 09:16:24 +02:00
Peter Hutterer
da2764e78d Append the created profiles/leds/buttons/resolutions, don't insert
We're initializing them with incrementing indices, let's make sure the list is
initialized that way too. Makes things more obvious in the log when the first
profile to be queried from the device isn't profile 4.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-23 13:47:11 +02:00
Peter Hutterer
4e56afae22 roccat: use the new get/set feature helpers
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-23 13:46:23 +02:00
Peter Hutterer
d46251900c hidraw: add a feature report get/set helper functions
No real functional changes since they just forward to the existing function.
But it reduces the cognitive load a bit when reading the code.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-23 13:46:23 +02:00
Peter Hutterer
43def1b3a5 meson.build: enable -Werror=format-security
We don't want to accidentally ignore those.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-19 07:17:45 +02:00
Filipe Laíns
247ff7695d data: add MX Vertical
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-19 07:57:53 +10:00
Filipe Laíns
7a14026f2d hidpp20: fix warnings
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 15:13:51 +01:00
Filipe Laíns
55925db29e meson: bump project version to 0.9.906
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 13:29:12 +02:00
Peter Hutterer
87d4aed3ad Drop the cpp requirement and linker tests
This is a leftover from libratbag-as-a-library and obsolete now that ratbagd
is our only product. Our buildtest now is simply: "does ratbagd build?" and we
certainly shouldn't require a C++ compiler.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-17 07:20:36 +02:00
Peter Hutterer
7de944aaa0 Use python3-embed as dependency on Python 3.8
Python 3.8 no longer links to libpython, resulting in undefined references to
Python API.

But it provides a python3-embed.pc now, so let's try using that first and only
fall back on the normal case if that fails.

See https://github.com/mesonbuild/meson/issues/5629
2019-07-17 06:05:36 +02:00
Filipe Laíns
488774d3ba data: add logitech-g403-hero.device
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
Filipe Laíns
0673af2991 data: add logitech-g403-wireless.device
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
Filipe Laíns
0e84b63921 data: remove -over-USB from .device name and follow the layout of other .device files
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
Filipe Laíns
6e34f314f8 data: add logitech-g903-hero.device
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
Filipe Laíns
e3268f2ce6 data: add device PID to logitech-g903.device
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
Filipe Laíns
47623654d0 data: add logitech-g703-hero.device
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
Filipe Laíns
94423769b9 data: add device PID to logitech-g703.device
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
Filipe Laíns
4efe8f4830 data: add device PID to logitech-g603.device
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
Filipe Laíns
ac06e7d0c8 data: start using the device PID in logitech-g602.device
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
Filipe Laíns
f534cb55b0 data: remove logitech-gpowerplay-wireless.device
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
aTosser
bb3f386cca data: remove logitech-lightspeed.device
No longer required as G900 WPID already included in logitech-g900.device

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
aTosser
6393338d0f data: start using the device PID in logitech-g-pro-wireless.device
Changed PID for wireless PID. Necessary post Linux 5.2

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:49:01 +10:00
Filipe Laíns
b225e4bd80 hidpp20: simplify hidpp20_onboard_profiles_initialize
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 09:47:00 +10:00
Filipe Laíns
635257d6b8 hidpp20: improve debug messages
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-10 16:39:44 +02:00
Filipe Laíns
b31a8c5355 hidpp20: fall back to firmware profiles on a profile by profile basis
currently if any use profile is not valid we fallback to the firmware
profiles. this is quite annoying because the user might have only one
valid profile. this patch will keep valid profiles and only fallback to
firmware profiles on a profile by profile basis.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-10 16:39:44 +02:00
Ian Santopietro
412f3fef87 data: fix incorrect PID in MX Anywhere 2 2019-07-08 17:56:06 +02:00
Filipe Laíns
8f88573a08 hidpp20: fix reading onboard profiles (8100)
when we read the memory from the device we already align the offset so
that it matches the end of the sector. for ex, for a 255 sector size the
offset will be set to 255-16.
when we copy the memory (using memcpy) we just copy the bytes that will
finish filling the buffer, ignoring that the offset was aligned. we need
to copy the full 16 bytes, otherwise it will fail when sector_size does
not align with 16.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-08 10:33:53 +02:00
Filipe Laíns
c1265bc98a hidpp10: fix segfault for the c531 (and possibly other 27MHz receivers)
hidpp10_read_memory will return the HID++ error if the request fails.
right now, the error code from hidpp10_read_memory is transparently
passed through hidpp10_onboard_profiles_read_macro. This is problematic
because hidpp10_onboard_profiles_parse_macro only aborts on rc < 0 and
HID++ error are > 0.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-05 07:54:09 +10:00
Filipe Laíns
723d394c02 data: add Logitech Marathon M705
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-03 08:51:29 +10:00
Filipe Laíns
808f423600 data: add ID to MX Ergo
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-02 14:14:50 +02:00
Filipe Laíns
ae9c639a74 data: add Logitech MX518
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-02 14:14:28 +02:00
Filipe Laíns
932cbf0a5c logitech: don't show hidden features
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-02 09:55:11 +02:00
Yigit Guneli
486842b6cb data: add Logitech MX Ergo
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-06-28 18:47:08 +01:00
Ian Santopietro
2ae8233eeb data: add other Bluetooth PID to MX Anywhere 2
My MX Anywhere 2 has a different PID from the one supplied in the default .device file. This adds the PID to the device file to ensure mice with this PID get detected too.
2019-06-28 18:46:00 +01:00
Peter Hutterer
cdd70105b5 data: add a README for devices, remove the svg one
We don't ship SVGs here anymore, so let's remove that README. And instead add
one to the devices directory.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-06-17 09:44:41 +10:00
FFY00
6babb7b307 data: add other ID to MX Master 2019-05-31 18:24:25 +10:00
Pedro Vanzella
26843861c0 Add battery voltage reporting for newer mice 2019-05-29 13:50:41 +02:00
FFY00
2eed8dbb92 steelseries: only print firmware version on successful read 2019-05-28 07:57:22 +10:00
FFY00
a13ee32c61 steelseries: fix bug where we were wrongly dellocating dpi_{range,list}
_cleanup_ is deallocating the pointer returned by
ratbag_device_data_steelseries_get_dpi_{range,list}(). Deallocating
dpi_list results in a wrong step value, and a wrong step value then
results in a wrong step count when we try to set the dpi.

Fixes #436 and #702
2019-05-27 00:25:39 +01:00
Peter Hutterer
dbe98ed5b9 circleci: update to fedora 30
And install dbus-daemon manually, until we figure out how to correctly start
the new dbus-broker this will at least help us pass the tests.

And disable nss-systemd so we don't suffer from timeouts as dbus-daemon waits
for that to time out, see c425780220 for details.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-05-24 02:49:20 +01:00
Peter Hutterer
c425780220 circleci: drop libnss-systemd from Ubuntu
This is installed by software-properties-common, but since we don't
start our container with systemd, it effectively points to nowhere, adding
nothing but a delay of several minutes.

That delay affects dbus-daemon on startup and on configuration reload, it
takes several minutes to do either. ratbagctl.test installs a custom dbus
config file to be able to connect to ratbagd.devel, that config file triggers
a reload in the dbus-daemon. Since that reload takes longer than any of our
timeouts, the tests eventually fail.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-05-24 09:32:50 +10:00
Peter Hutterer
1e9839cb67 circle CI: switch to ubuntu 19.04
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-05-24 09:32:50 +10:00
FFY00
bbeaef749e meson: add 'sphinx-build' to sphinx executable name list 2019-05-24 07:16:19 +10:00
Peter Hutterer
240231b065 Error out if NDEBUG is defined
At some point meson #defined NDEBUG depending on the build type, thus
disabling asserts. libratbag relies on those, so let's error out if NDEBUG is
defined.

Related #685
2019-05-07 09:24:28 +02:00
Benjamin Tissoires
6f27053ab0 driver-hidpp20: re-enable changing the report rate
Fixes bf127d4c68

Related: #692

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2019-04-29 10:29:59 +10:00
Benjamin Tissoires
fc16a8dc7c ratabgc: handle the None value from the macro
Not sure why ratbagctl doesn't complain about it, but ratbag-command does

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2019-04-29 10:29:59 +10:00
Benjamin Tissoires
6256e9046f ratbagc: Fix a missing macro enum conversion
An overlook from 10305a123c

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2019-04-29 10:29:59 +10:00
Benjamin Tissoires
563550c473 driver-hidpp20: use the correct active dpi index
There is no guarantees the current value of dpi_index will be correct.
On my G502 the profiles are read in reverse order meaning that setting the
dpi_index can be applied to a wrong value.

Look for the correct libratbag dpi index before setting it.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2019-04-29 10:29:59 +10:00
Benjamin Tissoires
a04f2d1c24 data: update G900 and G403 for kernel v5.2+
kernel v5.2 should solve #380, finally

Fixes #380


Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2019-04-24 14:46:07 +01:00
Daniel Caujolle-Bert
d86933d746 Add MX-Anywhere-2S bluetooth IDs. 2019-04-10 12:03:45 +01:00
FFY00
55f7d75567 steelseries: fix regression in v2 led
Fixes #683
2019-04-09 12:16:48 +01:00
Rene Kjellerup
6ee1c0a591 devices: add other ID for Logitech G Pro
Fixes #684
2019-04-05 22:39:33 +01:00
Peter Hutterer
94d32930e2 ratbagd: use libunistring to convert profile names to utf-8
Where a profile name is not UTF8 compatible, let's try to convert it from
latin-1. If that fails, we just escape anything that's not ascii and move on.

Fixes #617

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-03-15 08:25:00 +01:00
Peter Hutterer
2b2d8e8980 ratbagctl: get the special name list from the enum
For two dicts x and y the merged dict can be created as {**x, **y}. We use
that to take the enum names and covert them to lowercase with hyphens instead
of underscores.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-03-12 16:33:05 +01:00
Peter Hutterer
10305a123c tools: convert our enum equivalents to python enums
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-03-12 16:33:05 +01:00
Peter Hutterer
11db35edba Fix flake8 warnings (#678)
The only notable one here is the regex that had an escape sequence

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-03-12 06:49:54 +01:00
Peter Hutterer
836922b133 ratbagd: fix property update notifications
Don't send property updates for props that don't exist anymore.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-03-08 08:39:11 +01:00
FFY00
4d00a815a4 revert "data: add Logitech G100s (#670)"
This reverts commit 0e28d82a16.
2019-03-05 22:15:42 +01:00
Filipe Laíns
0e28d82a16 data: add Logitech G100s (#670) 2019-03-05 16:57:39 +01:00
Peter Hutterer
336440cddb Install the device files in the top-level datadir
Now that the SVGs are gone, we don't need the subfolder anymore

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-03-05 09:06:05 +01:00
Peter Hutterer
b2a3d39122 libratbag: improve debugging to help with new devices
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-03-05 13:29:11 +10:00
Peter Hutterer
4b7b9d2e48 meson.build: rename the logind dep for consistency with the rest
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-03-04 20:17:06 +10:00
Andy Fitzsimon
57097ceddb updated logo look and feel
Rough update but headed in the right direction

* Simplified silhouette of bag
* Reduced number of rats inside the Rat Bag to two 
* Eyes are still small and potentially still not printable at small scale
* Introduced rough stylized logotype based on Google's Sedgwick Ave Display font
* Reduced total file size by a heap
2019-03-04 10:44:59 +10:00
FFY00
c7ce1a96a7 data: rename logitech-g900-wireless to logitech-lightspeed 2019-03-04 10:33:58 +10:00
122 changed files with 5896 additions and 1522 deletions

View File

@@ -1,5 +1,12 @@
# vim: set expandtab shiftwidth=2 tabstop=8:
libratbag_references:
build_dependencies: &build_dependencies
FEDORA_DEP_BUILD: gcc gcc-c++ meson dbus-daemon glib2-devel json-glib-devel libevdev-devel libudev-devel libunistring-devel python3-devel python3-evdev swig
FEDORA_DEP_TEST: check-devel python3-gobject python3-lxml valgrind diffutils
FEDORA_DEP_DOC: python3-sphinx python3-sphinx_rtd_theme
UBUNTU_DEP_BUILD: gcc g++ meson pkg-config systemd libevdev-dev libglib2.0-dev libjson-glib-dev libsystemd-dev libudev-dev libunistring-dev python3-dev python3-evdev swig
UBUNTU_DEP_TEST: check python3-gi python3-lxml valgrind
ARCH_DEP_BUILD: meson swig glib2 libevdev libudev.so libunistring json-glib python python-evdev python-gobject
ARCH_DEP_TEST: check valgrind python-lxml
default_settings: &default_settings
working_directory: ~/libratbag
environment:
@@ -13,6 +20,18 @@ libratbag_references:
ninja -v -C build ${NINJA_ARGS}
environment:
MESON_PARAMS: --prefix=/usr
build_buildtype_plain: &build_buildtype_plain
run:
<<: *build_default
name: Build with buildtype plain
environment:
MESON_PARAMS: --prefix=/usr -Dbuildtype=plain
build_buildtype_release: &build_buildtype_release
run:
<<: *build_default
name: Build with buildtype release
environment:
MESON_PARAMS: --prefix=/usr -Dbuildtype=release
build_and_test: &build_and_test
run:
<<: *build_default
@@ -34,16 +53,13 @@ libratbag_references:
name: Checking installation
command: |
PREFIX=/usr/local/
diff -u <(cd data/devices; ls *.device) <(cd $PREFIX/share/libratbag/devices; ls *.device)
diff -u <(cd data/devices; ls *.device) <(cd $PREFIX/share/libratbag; ls *.device)
check_uninstall: &check_uninstall
run:
name: Checking if any files are left after uninstall
command: |
PREFIX=/root/test_install
# workaround until https://github.com/mesonbuild/meson/pull/2033 is merged
# and a new release appears
git clone https://github.com/whot/meson -b wip/remove-directories-on-uninstall
./meson/meson.py build_install --prefix=$PREFIX
meson build_install --prefix=$PREFIX
ninja -C build_install install
ninja -C build_install uninstall
if [ -d $PREFIX ]
@@ -61,33 +77,34 @@ libratbag_references:
mkdir /run/dbus
/usr/bin/dbus-daemon --system --fork
fedora_prep_cache: &fedora_prep_cache
<<: *default_settings
steps:
- run:
name: Initializing Fedora dnf cache
command: dnf install -y --downloadonly libsolv tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml
- persist_to_workspace:
root: /var/cache/
paths:
- dnf/*
save_date: &save_date
run:
name: Save date
command: date +%F > /tmp/date
fedora_save_cache: &fedora_save_cache
save_cache:
key: fedora-cache-v1-{{ checksum "/tmp/date" }}
paths:
- /var/cache/dnf
fedora_fetch_cache: &fedora_fetch_cache
attach_workspace:
at: /var/cache/
restore_cache:
keys:
- fedora-cache-v1
fedora_install: &fedora_install
run:
name: Install prerequisites
command: |
echo keepcache=1 >> /etc/dnf/dnf.conf
dnf upgrade -y libsolv
dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml python3-devel swig
dnf install -y git ${FEDORA_DEP_BUILD} ${FEDORA_DEP_TEST}
sed -i 's/systemd//' /etc/nsswitch.conf
fedora_settings: &fedora_settings
<<: *default_settings
steps:
- *save_date
- *fedora_fetch_cache
- *fedora_install
- checkout
@@ -96,8 +113,42 @@ fedora_settings: &fedora_settings
- *install
- *check_install
- *check_uninstall
- *build_buildtype_plain
- *build_buildtype_release
- *export_logs
- *fedora_save_cache
environment:
*build_dependencies
arch_save_cache: &arch_save_cache
save_cache:
key: arch-cache-v1-{{ checksum "/tmp/date" }}
paths:
- /var/cache/pacman/pkg
arch_fetch_cache: &arch_fetch_cache
restore_cache:
keys:
- arch-cache-v1
arch_settings: &arch_settings
<<: *default_settings
steps:
- *save_date
- *arch_fetch_cache
- run:
name: Install prerequisites
command: pacman -Syu --noconfirm --noprogressbar --needed base-devel git tree ${ARCH_DEP_BUILD} ${ARCH_DEP_TEST}
- *start_dbus
- checkout
- *build_and_test
- *install
- *check_install
- *check_uninstall
- *export_logs
- *arch_save_cache
environment:
*build_dependencies
ubuntu_settings: &ubuntu_settings
<<: *default_settings
@@ -105,18 +156,20 @@ ubuntu_settings: &ubuntu_settings
- run:
name: Install prerequisites
command: |
export DEBIAN_FRONTEND=noninteractive
apt-get update
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 valgrind python3-gi python3-evdev libglib2.0-dev python3-lxml python3-dev swig systemd
apt-get install -y git ${UBUNTU_DEP_BUILD} ${UBUNTU_DEP_TEST}
- checkout
- *start_dbus
- *build_and_test
- *install
- *check_install
- *check_uninstall
- *build_buildtype_plain
- *build_buildtype_release
- *export_logs
environment:
*build_dependencies
doc_build: &doc_build
<<: *default_settings
@@ -125,7 +178,7 @@ doc_build: &doc_build
- *fedora_install
- run:
name: Install documentation build-deps
command: dnf install -y python3-sphinx python3-sphinx_rtd_theme
command: dnf install -y ${FEDORA_DEP_DOC}
- checkout
- *build_with_docs
- *export_logs
@@ -135,6 +188,8 @@ doc_build: &doc_build
root: build
paths:
- doc/html/*
environment:
*build_dependencies
docs_deploy: &docs_deploy
<<: *default_settings
@@ -176,48 +231,38 @@ docs_deploy: &docs_deploy
version: 2
jobs:
fedora_rawhide:
fedora_32:
<<: *fedora_settings
docker:
- image: fedora:rawhide
fedora_cache:
<<: *fedora_prep_cache
- image: fedora:32
arch:
<<: *arch_settings
docker:
- image: fedora:28
fedora_latest:
<<: *fedora_settings
docker:
- image: fedora:28
ubuntu_17_10:
- image: archlinux
ubuntu_20_04:
<<: *ubuntu_settings
docker:
- image: ubuntu:artful
- image: ubuntu:20.04
doc_build:
<<: *doc_build
docker:
- image: fedora:28
- image: fedora:32
doc_deploy:
<<: *docs_deploy
docker:
- image: fedora:28
- image: fedora:32
workflows:
version: 2
compile_and_test:
jobs:
# - fedora_rawhide
- fedora_cache
- ubuntu_17_10
- fedora_latest:
requires:
- fedora_cache
- doc_build:
requires:
- fedora_cache
- ubuntu_20_04
- arch
- fedora_32
- doc_build
- doc_deploy:
requires:
- fedora_cache
- doc_build
filters:
branches:

20
.editorconfig Normal file
View File

@@ -0,0 +1,20 @@
root = true
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.{c,h}]
indent_style = tab
tab_width = 8
trim_trailing_whitespace = true
[{*.{py,py.in},tools/ratbagctl.*.in}]
indent_style = space
indent_size = 4
[*.{yml,xsl}]
indent_style = space
indent_size = 2

42
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,42 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Information**
<!-- If your issue is related to a Logitech device please make sure you are running at least Linux 5.2 -->
- `ratbagd` version (`ratbagd --version`):
- Distribution:
- Kernel version (ex. `uname -srmo`): `KERNEL VERSION HERE`
Device info (if applicable):
```
$ ratbagctl <device> info
OUTPUT HERE
```
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Logs**
Please start the daemon with verbosity and reproduce the issue.
First make sure it isn't running already.
```
$ ratbagd --verbose=raw
OUTPUT HERE
```
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

11
.gitignore vendored
View File

@@ -1,11 +0,0 @@
*.o
*.pc
*.swp
*~
*.sig
*.tar.*
*.announce
*.patch
*.rej
*.trs
tags

View File

@@ -3,9 +3,10 @@ libratbag
<img src="https://libratbag.github.io/_images/logo.svg" alt="" width="30%" align="right">
libratbag provides **ratbagd**, a DBus daemon to configure gaming mice.
The daemon provides a generic way to access the various features exposed by
these mice and abstracts away hardware-specific and kernel-specific quirks.
libratbag provides **ratbagd**, a DBus daemon to configure input devices,
mainly gaming mice. The daemon provides a generic way to access the various
features exposed by these mice and abstracts away hardware-specific and
kernel-specific quirks.
libratbag currently supports devices from Logitech, Etekcity, GSkill,
Roccat, Steelseries. See [the device

View File

@@ -1,17 +0,0 @@
libratbag data files
====================
This directory contains the SVG files for each theme. In general, device SVGs
should approximate the device to make it immediately recognizable. Detail work
not required, the graphic is an illustration only.
For theme-specific requirements, consult the README in the subdirectories.
Technique
---------
The simplest approach is to find a photo of the device and import it into
inkscape. Put it on the lowest layer, create a new layer "Device" above it
and start tracing the outlines and edges of the device. Fill in the shapes
and your device should resemble the underlying photo. Delete the photo
layer, add leaders and button labels and you're done.

11
data/devices/README.md Normal file
View File

@@ -0,0 +1,11 @@
libratbag data files
====================
This directory contains the data files for each supported device. libratbag
reads these files to identify the backend driver for any given device. If a
device is not listed, libratbag will not support it.
See the device.example file for instructions on the various options.
See the libratbag wiki for more information:
https://github.com/libratbag/libratbag/wiki/Adding-a-device/

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env python3
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright © 2017 Red Hat, Inc.
#
@@ -26,8 +25,9 @@
#
import argparse
import os
import configparser
import re
def assertIn(element, l):
if element not in l:
@@ -57,11 +57,11 @@ def check_match_str(string):
def check_ledtypes_str(string):
permitted_types = ['logo', 'side', 'battery', 'dpi']
permitted_types = ['logo', 'side', 'battery', 'dpi', 'switches']
types = string.split(';')
for t in types:
if not t: # emtpy string if trailing ;
if not t: # emtpy string if trailing ;
continue
assertIn(t, permitted_types)
@@ -88,7 +88,7 @@ def check_section_device(section):
def check_dpi_range_str(string):
import re
m = re.search('^([0-9]+):([0-9]+)@([0-9\.]+)$', string)
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))
@@ -137,9 +137,8 @@ def check_section_hidpp10(section):
pass
try:
index = int(section['DeviceIndex'])
# 10 is arbitrarily chosen
assert(index > 0 and index < 10)
index = int(section['DeviceIndex'], 16)
assert(index > 0 and index <= 0xff)
except KeyError:
pass
@@ -169,14 +168,13 @@ def check_section_hidpp10(section):
def check_section_hidpp20(section):
permitted = ['DeviceIndex']
permitted = ['DeviceIndex', 'Leds', 'Quirk']
for key in section.keys():
assertIn(key, permitted)
try:
index = int(section['DeviceIndex'])
# 10 is arbitrarily chosen
assert(index > 0 and index < 10)
index = int(section['DeviceIndex'], 16)
assert(index > 0 and index <= 0xff)
except KeyError:
pass
@@ -190,6 +188,15 @@ def check_section_driver(driver, section):
assert('Unsupported driver section {}'.format(driver))
def validate_data_file_name(path):
# Matching any of the characters in the regular expression will throw an
# error. Currently only tests the square brackets [], parentheses, and curly
# braces.
illegal_characters_regex = '([\[\]\{\}\(\)])'
found_characters = re.findall(illegal_characters_regex, path)
if found_characters:
raise AssertionError("data file name '{}' contains illegal characters: '{}'".format(path, ''.join(found_characters)))
def parse_data_file(path):
print('Parsing file {}'.format(path))
data = configparser.ConfigParser(strict=True)
@@ -216,4 +223,5 @@ if __name__ == "__main__":
parser.add_argument('file', nargs='+')
args = parser.parse_args()
for path in args.file:
validate_data_file_name(path)
parse_data_file(path)

View File

@@ -0,0 +1,10 @@
[Device]
Name=Dream Machines DM5 Blink
DeviceMatch=usb:258a:0027
Driver=sinowealth
[Driver/sinowealth]
Buttons=8
Leds=1
LedType=RGB
DpiRange=100:16000@100

57
data/devices/duplicate-check.py Executable file
View File

@@ -0,0 +1,57 @@
#!/usr/bin/env python3
#
# Copyright © 2018 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
import argparse
import os
import sys
import configparser
def parse_data_file(path):
data = configparser.ConfigParser(strict=True)
# Don't convert to lowercase
data.optionxform = lambda option: option
data.read(path)
matches = data['Device']['DeviceMatch']
return matches.split(';')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Device duplicate match checker')
parser.add_argument('file', nargs='+')
args = parser.parse_args()
device_matches = {}
duplicates = False
for path in args.file:
matches = parse_data_file(path)
fname = os.path.basename(path)
for m in matches:
if m in device_matches:
print('Duplicate DeviceMatch={} in {} and {}'.format(m, fname, device_matches[m]))
duplicates = True
device_matches[m] = fname
if duplicates:
sys.exit(1)

View File

@@ -0,0 +1,9 @@
[Device]
Name=Glorious Model D
DeviceMatch=usb:258a:0033
Driver=sinowealth
[Driver/sinowealth]
Buttons=6
Leds=2
DpiRange=100:12000@100

View File

@@ -0,0 +1,10 @@
[Device]
Name=Glorious Model O
DeviceMatch=usb:258a:0036
Driver=sinowealth
[Driver/sinowealth]
Buttons=6
Leds=2
LedType=RBG
DpiRange=100:12000@100

View File

@@ -0,0 +1,5 @@
# Logitech M585/M590
[Device]
Name=Logitech M585/M590
DeviceMatch=usb:046d:406b;bluetooth:046d:b01b
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech M720
DeviceMatch=usb:046d:405e;bluetooth:046d:b015
Driver=hidpp20

View File

@@ -1,4 +1,4 @@
[Device]
Name=Logitech MX Anywhere 2
DeviceMatch=usb:046d:404a;bluetooth:046d:b013
DeviceMatch=usb:046d:404a;bluetooth:046d:b013;bluetooth:046d:b018;bluetooth:046d:b01f
Driver=hidpp20

View File

@@ -1,4 +1,4 @@
[Device]
Name=Logitech MX Anywhere 2S
DeviceMatch=usb:046d:406a
DeviceMatch=bluetooth:046d:b01a;usb:046d:406a
Driver=hidpp20

View File

@@ -0,0 +1,5 @@
# Logitech MX Ergo
[Device]
Name=Logitech MX Ergo
DeviceMatch=bluetooth:046d:b01d;usb:046d:406f
Driver=hidpp20

View File

@@ -0,0 +1,5 @@
# Logitech MX Master 3
[Device]
Name=Logitech MX Master 3
DeviceMatch=bluetooth:046d:b023;usb:046d:4082
Driver=hidpp20

View File

@@ -1,5 +1,5 @@
# Logitech MX Master
[Device]
Name=Logitech MX Master
DeviceMatch=usb:046d:4041;bluetooth:046d:b012;usb:046d:4060
DeviceMatch=usb:046d:4041;bluetooth:046d:b012;usb:046d:4060;bluetooth:046d:b017;bluetooth:046d:b01e;usb:046d:4071
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech MX Vertical
DeviceMatch=bluetooth:046d:b020;usb:046d:407b
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=MX518
DeviceMatch=usb:046d:c08e
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech Marathon M705
DeviceMatch=usb:046d:406d
Driver=hidpp20

View File

@@ -0,0 +1,8 @@
[Device]
Name=Logitech G Powerplay
DeviceMatch=usb:046d:405f
LedTypes=logo
Driver=hidpp20
[Driver/hidpp20]
DeviceIndex=7

View File

@@ -1,6 +1,6 @@
[Device]
Name=Logitech G Pro Wireless
DeviceMatch=usb:046d:c539;usb:046d:c088
DeviceMatch=usb:046d:4079;usb:046d:c088
LedTypes=logo;battery;dpi;
Driver=hidpp20

View File

@@ -1,5 +1,5 @@
[Device]
Name=Logitech Gaming Mouse G Pro
DeviceMatch=usb:046d:c085
DeviceMatch=usb:046d:c085;usb:046d:c08c
Driver=hidpp20
LedTypes=logo;side

View File

@@ -1,6 +1,6 @@
# G102 and G203 (USB)
# G102, G103 and G203 (USB)
[Device]
Name=Logitech Gaming Mouse G102/G103
DeviceMatch=usb:046d:c084
Name=Logitech Gaming Mouse G102/G103/G203
DeviceMatch=usb:046d:c084;usb:046d:c092
Driver=hidpp20
LedTypes=logo;side;

View File

@@ -0,0 +1,8 @@
[Device]
Name=Logitech Gaming Mouse G305
DeviceMatch=usb:046d:4074
Driver=hidpp20
[Driver/hidpp20]
Leds=0
Quirk=G305

View File

@@ -2,4 +2,5 @@
[Device]
Name=Logitech G402 Gaming Mouse
DeviceMatch=usb:046d:c07e
LedTypes=logo
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech G403 Hero
DeviceMatch=usb:046d:c08f
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech G403 Wireless
DeviceMatch=usb:046d:405d;usb:046d:c082
Driver=hidpp20

View File

@@ -1,5 +1,4 @@
# Logitech G403
[Device]
Name=Logitech G403
DeviceMatch=usb:046d:c082;usb:046d:c083
DeviceMatch=usb:046d:c083
Driver=hidpp20

View File

@@ -7,3 +7,4 @@ Driver=hidpp10
[Driver/hidpp10]
DpiList=400;800;1600;2000
Profiles=1
ProfileType=G500

View File

@@ -1,5 +0,0 @@
# Logitech G502 HERO over USB
[Device]
Name=Logitech G502 HERO
DeviceMatch=usb:046d:c08b
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech G502 Hero Wireless
DeviceMatch=usb:046d:407f;usb:046d:c08d
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech G502 Hero
DeviceMatch=usb:046d:c08b
Driver=hidpp20

View File

@@ -1,4 +1,3 @@
# Logitech G502 Proteus Core over USB
[Device]
Name=Logitech G502 Proteus Core
DeviceMatch=usb:046d:c07d

View File

@@ -1,4 +1,3 @@
# Logitech G502 Proteus Spectrum over USB
[Device]
Name=Logitech G502 Proteus Spectrum
DeviceMatch=usb:046d:c332

View File

@@ -0,0 +1,5 @@
[Device]
Name=Logitech G513
DeviceMatch=usb:046d:c33c
LedTypes=switches
Driver=hidpp20

View File

@@ -1,8 +1,9 @@
# Logitech G602 over wireless USB
[Device]
Name=Logitech G602
DeviceMatch=usb:046d:c537
DeviceMatch=usb:046d:402c
Driver=hidpp20
[Driver/hidpp20]
DeviceIndex=1
Quirk=G602

View File

@@ -1,5 +1,5 @@
# Logitech G603
[Device]
Name=Logitech G603
DeviceMatch=bluetooth:046d:b01c
DeviceMatch=usb:046d:406c;bluetooth:046d:b01c
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech G604
DeviceMatch=usb:046d:4085;bluetooth:046d:b024
Driver=hidpp20

View File

@@ -1,12 +0,0 @@
# 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
[Driver/hidpp10]
DpiRange=0:8200@50
ProfileType=G700
DeviceIndex=1
Profiles=5

View File

@@ -0,0 +1,5 @@
[Device]
Name=Logitech G703 Hero
DeviceMatch=usb:046d:4086;usb:046d:c090
Driver=hidpp20

View File

@@ -1,5 +1,5 @@
[Device]
Name=Logitech G703
DeviceMatch=usb:046d:c087
DeviceMatch=usb:046d:c087;usb:046d:4070
Driver=hidpp20

View File

@@ -0,0 +1,5 @@
[Device]
Name=Logitech G815
DeviceMatch=usb:046d:c33f
LedTypes=logo;switches
Driver=hidpp20

View File

@@ -1,8 +0,0 @@
# Logitech G900, G903, G603 and G403 over wireless USB
[Device]
Name=Logitech G900, G903, G603, G403
DeviceMatch=usb:046d:c539
Driver=hidpp20
[Driver/hidpp20]
DeviceIndex=1

View File

@@ -1,5 +1,5 @@
# Logitech G900 over USB
# Logitech G900
[Device]
Name=Logitech G900
DeviceMatch=usb:046d:c081
DeviceMatch=usb:046d:c081;usb:046d:4053
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech G903 Hero
DeviceMatch=usb:046d:c091;usb:046d:4087
Driver=hidpp20

View File

@@ -1,5 +1,4 @@
# Logitech G903 over USB
[Device]
Name=Logitech G903
DeviceMatch=usb:046d:c086
DeviceMatch=usb:046d:c086;usb:046d:4067
Driver=hidpp20

View File

@@ -0,0 +1,8 @@
[Device]
Name=Logitech G910
DeviceMatch=usb:046d:c335
LedTypes=logo;switches
Driver=hidpp20
[Driver/hidpp20]
DeviceIndex=ff

View File

@@ -0,0 +1,8 @@
[Device]
Name=Logitech G915
DeviceMatch=usb:046d:c33e
LedTypes=logo;switches
Driver=hidpp20
[Driver/hidpp20]
DeviceIndex=1

View File

@@ -0,0 +1,5 @@
[Device]
Name=Logitech G935
DeviceMatch=usb:046d:0a87
LedTypes=logo;side
Driver=hidpp20

View File

@@ -4,8 +4,10 @@
Name=G9x [Original]
DeviceMatch=usb:046d:c066
Driver=hidpp10
LedTypes=dpi
[Driver/hidpp10]
DpiRange=0:5700@23.53
DpiRange=200:5700@50
ProfileType=G500
Profiles=5
Leds=1

View File

@@ -1,8 +0,0 @@
# Logitech G PowerPlay mouse pad + wireless USB receiver.
[Device]
Name=Logitech G PowerPlay mouse pad.
DeviceMatch=usb:046d:c53a
Driver=hidpp20
[Driver/hidpp20]
DeviceIndex=1

View File

@@ -0,0 +1,5 @@
[Device]
Name=Nubwo x7 spectrum
DeviceMatch=usb:258a:0012
Driver=sinowealth_nubwo

74
data/devices/receiver-check.py Executable file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python3
#
# Copyright © 2018 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
import argparse
import os
import sys
import configparser
# see the IDs from
# https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L772
# https://github.com/torvalds/linux/blob/master/drivers/hid/hid-logitech-dj.c#L1826
logitech_receivers = [
0xc50c, # USB_DEVICE_ID_S510_RECEIVER
0xc517, # USB_DEVICE_ID_S510_RECEIVER_2
0xc512, # USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500
0xc513, # USB_DEVICE_ID_MX3000_RECEIVER
0xc51b, # USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER
0xc52b, # USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER
0xc52f, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER
0xc532, # USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2
0xc534, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2
0xc539, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1
0xc53f, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1
0xc53a, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY
]
RECEIVERS = ['usb:046d:{}'.format(r) for r in logitech_receivers]
def parse_data_file(path):
data = configparser.ConfigParser(strict=True)
# Don't convert to lowercase
data.optionxform = lambda option: option
data.read(path)
matches = data['Device']['DeviceMatch']
return matches.split(';')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Checks that no unifying receiver ID is in the device files')
parser.add_argument('file', nargs='+')
args = parser.parse_args()
receiver_found = False
for path in args.file:
matches = parse_data_file(path)
fname = os.path.basename(path)
for m in matches:
if m in RECEIVERS:
print('Receiver ID {} found in file {}'.format(m, fname))
receiver_found = True
if receiver_found:
sys.exit(1)

View File

@@ -0,0 +1,4 @@
[Device]
Name=Roccat Kone Pure
DeviceMatch=usb:1e7d:2dc2;usb:1e7d:2dbe
Driver=roccat-kone-pure

View File

@@ -0,0 +1,11 @@
[Device]
Name=SteelSeries Kinzu V3
DeviceMatch=usb:1038:1388
Driver=steelseries
[Driver/steelseries]
DeviceVersion=1
Buttons=3
Leds=0
DpiList=400;800;1600;2000
MacroLength=0

View File

@@ -1,182 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg class="ratbag-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path class="ratbag-bag" d="M38.8 22.1a48.4 48.4 0 0 1 22.7 0s9-8.9 5-11.2c-6-3.2-3.9.8-16.3 3.5 15.5-7.5 5.4-5.7-16.3-3.5-5.3.5 5 11.2 5 11.2zm20.7 2.8c5.7 17.1-.8 4.7-2-.1-4 22.1-3.7 7.7-3-.6-5-.5-10.7-.4-15.7 1.3a47 47 0 0 0-17 32.7c0 13.3 5 7.9 28.4 7.9 28.8 0 28.4 5.4 28.4-7.9 0-9.8-9-25.9-19-33.3z" />
<path class="ratbag-rats" fill="#fff" d="M35.8 62.3c3.3 3.4-1 2.4-1.9.1-5.6 1.9-16.9.7-3.8-16.3.5-.4.6.5.4 1-4 6.8-7.5 15.5.3 13.2-1-8.2 3.2-14.1 6.8-19.7 1.1-1.4 2.3-4.3 2.8-5.5-3-2.1-.8-2 1.2-1.5 1.7-1.6 3.5-2.5 6.6-3 1.7 2.5-2.7 8.2-2.7 8.2 0 2.3.1 3.7.4 4.9 3 6.5 1.2 7.3-1.4 2.9-.3.1.3 2.6-1.4 7.9.5 5.9-2.5 7-2.5 7 7.3 2.2 1 3-1 .5-.8-.5-2.9-.1-3.8.3zm15-9c.1-1.7 1.4-4 1.8-.4 2 2.1 7.5-3.4 13.3-2.2 3.7.8 5 3.3 4.5 7.4 11.7 3.8 4.3 7.9-4.6 6.9 11.8-2.5 9.3-4.1 3.5-5-.4.8-4.6 1.6-5.5 1.5-3.9 1.5-6.7 2.2-3.2-.2l-4.5-.4c-3.9 2.8-2.9 2.4-1.7-.3-2-1.4-6 1.9-6.3-.1 0-.6 1.6-5.9 2.6-7.2z"/>
<path class="ratbag-eyes" d="M51 56.9c.6.4-1 1.2-1 1.2s.1-1.6 1-1.2zm-7-22.6c-.8-.4 1-1.2 1-1.2s-.3 1.7-1 1.2z"/>
<path class="ratbag-logotype" d="M81.3 89.3c-.6.9-2.3 1.1-2.7 0 0-1.3.6-2.5.5-3.8-.5.5-1 1.6-1.6 2.3-1 1-1.8 2.4-3.4 2.3-1.3-.2-2.8-.7-3.3-2-1.3-1.8-.8-4-.5-6 .7-2.8 2.6-5.3 5.2-6.7 1.3-.4 3.2-.2 4 1.2 0 2.3-1.5 4.9-3.8 4.9-2.6-.7 1.4-3 .7-4.2-1.4-.3-1.8 1.8-2.4 2.8-.8 2-1.5 4.3-1 6.4 1.6 3.5 5-2.8 2.5-1.4-1 .7-2.9 0-1.5-1.2 1-1 2.4-1.3 3.6-2.1 5.6-2.2 3.5.3 3.7 7.5zm-18.3-6c-.1.6 2-.1 2.3.3.4.4-.4-2.2-.6-2.6zm3.6-8c1.2 4.7 2 9 3.4 13.2-.2 1-1.6 1.7-2.6 1.5-1.4-1.2-.2-2.8-1.6-4-.3-.4-3.7-.5-3.8.5l-1.4 3.2c-.6.6-2.9.9-2.5-.4.5-1.4 1-2.8 1.8-4l3.4-9.6c.5-1.3 2.3-1.2 3.3-.4zm-10.5 9.5c-.4-.8-1.3-1.5-2.3-1.4-.8-.3-.6.5-.7 1 0 1.1-.2 2.2-.4 3.3 1-.3 1.9-1 2.7-1.7.4-.2.7-.7.7-1.2zm-6.3 5.8c-1.3 0-1.2-2 0-2 0-3.5.3-7 1-10.3.6-.7 1.6-1 2.4-1.2 1 .4 1 1.4.6 2.2-.4.9 0 1.4.8.7.8-.6 1-1.6 1-2.5-.8-1.9-4.3-.3-4.8-.4-1.2-.6.3-1.5 1-1.7.8-.4 1.7-.9 2.7-.7 1.2 0 2.7-.1 3.6 1 .8 1.2.1 2.8-.6 4-.3.7-1.6 1.3-1.7 1.8 6.4 1.1.8 9.4-6 9zm-1.6-13.4c-2.1.6-.9 3.4-1.4 5-.4 2.7-.9 5.5-.8 8.3-1.3 1.3-3.5-.3-2.9-2 0-2.8.1-5.7.6-8.5.4-1.2.6-2.9-1.1-2-2 .3-2.2-2.5 0-2.2 3 .2.7-3.6 2.5-4.8 1.8-1 3.6 1 2.5 2.7.1.5-.6 1.7.4 1.5 2-.8 2.4 3 .2 2zm-12.3 5.2c-1 .6.6 1.1 1 1.5 1 1 .2-.8 0-1.2-.9-1.7-.4-2-1-.3zm3-7.2l2 9.9c.3 1.5 1.6 3.7 0 4.7-1.4.8-2.4-.5-3.3-1.4-.9-.7-1.9-1.2-2.6-2.1-1-.3-1.9 2.8-2.9 3.8-.8 0-3 .2-1.8-1 .5-1.8 1.2-3.5 1.8-5.3-1.3-1 .3-2 1-2.6a52 52 0 0 0 2.4-5.6c.7-1 2.6-1.3 3.3-.4zm-16.4 2.9v2.4c1.6-.4 4-2.4 2.2-4-1.5-1.4-2.5 0-2.2 1.6zm6.7 3.3c2 1.2.6 3.8.5 5.6-.2 2-2.5 4.1-4.3 2.3-1.2-1.1-3.7-4.3-4.1-1-.5 2.5-3.3 1.7-2.8-.6.3-2.3 1-4.6 1.2-7-.3-2.4-1-4.8-.3-7.2 1-2.7 4.4-2.5 6.4-1.3 2.3.9 3.8 3.7 2.3 6-.5 2-3.1 3.1-3.7 4.6 1.2 2 3.6 2.9 3.4-.3.3-.5.8-1 1.4-1z" />
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="180.65305mm"
height="180.65305mm"
viewBox="0 0 180.65305 180.65305"
version="1.1"
id="svg8"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="logo.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="-156.7648"
inkscape:cy="473.25323"
inkscape:document-units="mm"
inkscape:current-layer="layer2"
showgrid="false"
inkscape:measure-start="0,0"
inkscape:measure-end="0,0"
inkscape:window-width="2560"
inkscape:window-height="1376"
inkscape:window-x="2560"
inkscape:window-y="27"
inkscape:window-maximized="1"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-to-guides="true">
<inkscape:grid
type="xygrid"
id="grid1058"
originx="-15.590431"
originy="-49.255695" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="rat right"
style="display:inline;opacity:1"
transform="translate(-15.59043,-80.129979)">
<path
sodipodi:nodetypes="sssssssssssssssccssscss"
inkscape:connector-curvature="0"
id="path1039"
d="m 181.70318,102.59719 c -3.2499,1.24415 -5.70487,5.21773 -6.1731,8.64054 -0.46127,3.37194 1.06674,8.17528 5.17162,8.92716 1.88245,0.3448 5.75552,3.71382 1.56778,2.91866 -4.41092,-0.83753 -9.60561,-1.81767 -12.03434,-5.88833 -2.51189,-4.21004 -6.0824,-2.70094 -9.60946,-1.97697 -3.61649,0.74233 -3.34262,3.70961 -0.59483,4.79149 1.76651,0.69552 -4.7698,1.02182 -6.46586,-0.31589 -0.99074,-0.7814 -4.3093,0.86964 -6.03067,0.70651 -4.14705,-0.39301 -8.0988,0.0723 -12.42874,-0.59505 -2.75134,-0.42408 -6.23873,1.52773 -5.40472,-2.94355 0.41316,-2.21506 -1.53788,-7.95327 -0.25583,-12.08908 1.43935,-4.64326 2.78762,-6.554728 6.29989,-9.291599 2.96282,-2.308725 9.62529,-5.456849 13.27656,-6.171955 5.94327,-1.163997 13.69394,2.401773 15.47361,3.054918 2.51726,0.923848 7.67473,-3.278929 11.1961,-3.200515 2.07518,-1.437388 4.50939,-5.479552 6.03783,0.452639 3.26133,0.136438 5.00967,-0.06864 9.09359,2.312711 2.76398,1.611684 4.87236,1.538272 5.34391,2.873448 0.62005,1.755647 -2.69292,5.296383 -3.28547,5.288623 -0.8492,-0.0111 -0.81394,0.0819 -2.0197,0.17649 -0.32445,1.41613 -2.05245,1.97075 -5.1013,1.61048 -1.43172,-0.16918 -2.7495,0.21878 -4.05687,0.71927 z"
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path1045"
d="m 119.43296,119.1374 c -3.19754,-3.4816 -3.91548,-3.31073 -5.87228,-7.17694 -1.51881,-4.76902 -1.2579,-3.59102 -4.44899,-11.00554 2.31432,2.99141 3.84563,6.48437 5.56001,9.87162 2.19923,4.34517 4.03074,4.88595 7.22829,8.36756"
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="cccsc" />
<path
transform="translate(-4.1666665e-8,1.0416667e-6)"
style="display:inline;fill:#ffffff;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="m 183.59605,92.870943 c 0.70159,-0.65481 2.47893,-0.65481 2.89988,-0.187088 0.42095,0.467721 -1.82412,1.028987 -2.89988,0.187088 z"
id="path833"
inkscape:connector-curvature="0">
<animate
values="orange;red;orange"
dur="7s"
repeatCount="indefinite"
attributeName="fill" />
</path>
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="rat left"
style="display:inline;opacity:1"
transform="translate(-15.59043,-80.129979)">
<path
sodipodi:nodetypes="ccsccccccc"
inkscape:connector-curvature="0"
id="path1298"
d="m 86.198873,115.45536 c -0.640498,-1.90396 -0.324058,-5.87792 -2.548937,-9.26118 1.761266,-3.58236 5.461967,-1.0306 3.345989,-4.71668 -1.27974,-2.229332 -5.394303,-0.5453 -6.437336,0.67341 -2.968786,0.40847 -6.280299,-3.592034 -8.586349,-4.613758 -3.902419,-1.141693 -9.722226,-2.223874 -12.939723,-3.303186 -2.646277,1.694635 -1.446947,6.207524 -1.598912,7.088294 1.30852,3.28167 1.243822,1.80143 2.493275,5.51481 2.008262,2.23089 4.855928,4.59155 5.686271,9.4289 7.349678,-0.38854 9.558907,-0.59389 20.585722,-0.81061 z"
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccccsc"
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 108.49567,117.83325 c -0.24901,-4.72058 -0.43574,-9.98729 -1.82782,-14.09082 -4.0267,-10.295998 -4.72401,-12.496029 -0.0129,-13.556767 -2.07044,-0.256651 -5.41881,-0.421857 -5.48281,2.489527 0.517,3.746632 1.94024,7.284981 3.15986,10.88015 1.56453,4.61189 1.00424,9.35889 1.25325,14.07947"
id="path955"
inkscape:connector-curvature="0" />
<path
transform="translate(-4.1666665e-8,1.0416667e-6)"
style="display:inline;opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 71.938542,102.84868 c -0.902446,-0.17526 -1.969377,-1.68007 -1.548427,-2.12186 0.867434,-0.27642 1.714682,0.92977 1.548427,2.12186 z"
id="path833-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc">
<animate
values="green;yellow;green"
dur="3s"
repeatCount="indefinite"
attributeName="fill" />
</path>
</g>
<g
inkscape:label="sack"
inkscape:groupmode="layer"
id="layer1"
style="display:inline"
transform="translate(-15.59043,-80.129979)">
<rect
ry="0.44884709"
y="172.18915"
x="1150"
height="4.8108511"
width="6.0803819"
id="rect1075"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:3.16499996;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<g
style="display:inline;opacity:1"
id="g1316"
transform="translate(-11.599497,6.3500003)">
<path
sodipodi:nodetypes="cccccczzcczc"
inkscape:connector-curvature="0"
id="path815"
d="m 71.911001,151.04073 c -13.545552,17.75365 -48.518606,24.46129 -42.445015,52.8882 4.987974,25.22201 13.988429,29.44417 56.008367,27.90906 47.288497,0.24745 47.307997,0.7217 90.436597,0.28592 14.07598,2.60143 28.63685,-20.8018 23.94043,-35.61574 -4.77259,-20.1552 -28.81931,-24.18426 -36.25354,-41.43822 -7.52218,-23.58108 1.99039,-35.03297 22.19568,-25.38895 20.20529,9.64403 26.03755,-15.82001 -11.10418,-15.33498 -37.14173,0.48503 -66.18409,-5.57956 -84.958574,-4.07459 -15.461428,0.81842 -41.712504,-2.86861 -44.765236,21.70932 2.422297,6.71408 19.985082,-12.15526 27.605897,-5.44801 7.620815,6.70724 20.413185,4.32379 -0.660426,24.50799 z"
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.16499996;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccccc"
style="display:inline;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.16499996;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 79.716618,142.54753 c 0,0 63.912362,8.07113 81.488252,0.7006 0,0 -0.56974,-7.74657 1.60362,-10.91212 -17.57589,7.37053 -79.821755,-0.0115 -79.821755,-0.0115 3.474504,7.01583 -3.270117,10.22298 -3.270117,10.22298 z"
id="path842"
inkscape:connector-curvature="0" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer6"
inkscape:label="rat bottom"
style="display:inline;opacity:1"
transform="translate(-15.59043,-80.129979)">
<path
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccsccccccccccccccc"
inkscape:connector-curvature="0"
id="path1212"
d="m 122.70428,243.35492 c 6.05524,1.14081 7.22067,1.31431 10.02264,2.72758 0,0.38071 -0.19945,0.89381 -1.01365,1.66697 -0.8142,0.77314 -2.6542,0.92241 -3.94953,1.25266 -0.67325,0.3643 -1.36184,1.00187 -1.50199,1.72727 3.97782,-1.45575 2.82486,-1.28103 2.75389,0.45072 0.20024,0.42037 1.54248,-1.05607 1.70256,-1.07661 1.00124,-0.41482 0.93822,0.001 1.55056,0.0319 0.20389,0.64546 -0.30382,1.34832 -0.26341,1.72391 0.84425,0.0769 1.57036,-1.33585 1.5884,-2.00591 0.34832,-0.5087 0.57096,-0.30966 1.0296,0 0.83149,0.862 0.93498,0.50569 1.95428,1.01599 2.24374,0.45044 -0.93235,-2.10713 -1.49797,-2.41992 -0.0737,-0.0369 1.39328,-2.39745 1.58909,-2.64425 2.32503,-0.403 4.75988,-1.00269 6.10414,-1.57594 v -1.2e-4 c 5.97825,1.84348 14.60207,1.47181 21.66926,1.7325 8.87347,0.60502 6.80866,4.81981 -11.94665,7.57383 29.43774,3.60709 24.29174,-11.42807 13.49975,-11.41332 -4.45874,0.0577 -12.7835,-0.12064 -21.26078,-1.9791 -1.35135,-2.33359 -1.8837,-5.9598 -1.12611,-9.63435 -2.49031,-11.667 -8.91489,-30.75707 -16.82134,-42.62455 -2.9074,-3.93147 -6.12061,-11.52979 -7.33913,-14.68828 7.81948,-5.62697 0.38705,-8.64346 -3.43515,-4.04735 -4.2835,-4.24158 -9.25924,-6.58986 -17.436457,-7.91069 -4.46939,6.75139 7.201157,21.8355 7.201157,21.8355 0.27261,11.69368 -1.47411,14.8979 -4.3435,23.39568 0.0471,0.3928 -0.49786,0.52092 -0.68357,0.83384 -0.19254,0.21312 -0.924097,1.82103 -1.179137,2.01487 -0.12771,0.23832 -0.38006,0.38366 -0.60384,0.59827 -0.23775,0.19619 -0.40433,0.49501 -0.42142,0.8292 -0.13385,0.31584 -0.17018,0.488 -0.19925,0.87639 -0.15645,0.29381 0.11002,0.40231 0.35386,0.34986 0.27783,-0.0135 0.56968,0.0505 0.82065,-0.10012 0.38688,-0.0135 0.824117,-0.32301 0.901337,-0.70099 0.0571,-0.34113 0.16568,-0.80155 0.62396,-0.6509 0.27636,0.006 0.58137,-0.16307 0.77806,0.15023 0.27193,0.12771 0.77421,0.23993 0.87876,-0.14883 0.16039,-0.42589 0.57357,-0.67322 0.67434,-1.12667 0.14254,-0.12321 0.37352,-0.19611 0.30974,-0.28545 0.26187,-0.26516 0.52474,-0.58479 0.89076,-0.74525 3.05534,-1.07719 3.51973,-4.67744 3.86462,-4.59812 0.67734,0.15577 -0.84342,6.82001 3.73079,20.85428 -1.4046,15.75023 6.7093,18.67126 6.7093,18.67126 0.17258,1.22487 -3.10226,1.32242 -3.80222,1.80721 -0.96512,0.31347 -2.42142,0.5571 -3.36241,0.16943 -0.41052,-0.20344 -1.14128,-0.10911 -0.6235,0.34889 -0.6851,0.62634 -1.75451,0.42589 -2.61368,0.63307 -0.84134,0.12395 -1.06409,1.07707 -0.0627,0.80147 -0.42125,1.18238 1.04775,0.72998 1.68923,0.70002 0.53648,-0.0399 0.30954,0.7577 0.96774,0.39958 0.92805,-0.19058 1.83353,-0.32154 2.75115,-0.51031 0.98245,-0.0454 1.60905,0.10657 2.586,0.18597 0.728,0.16342 2.43322,-0.53193 2.89833,-1.06277 0.62324,-0.55427 1.57699,-1.41676 2.07353,-2.11063 0.49656,-0.69386 0.66544,-0.53528 1.31986,-1.29795 z"
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.39657527px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path850"
d="m 109.46587,169.42014 c -0.90244,-0.17526 -1.96937,-1.68007 -1.54842,-2.12186 0.86743,-0.27642 1.71468,0.92977 1.54842,2.12186 z"
style="display:inline;opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1">
<animate
values="red;blue;red"
dur="5s"
repeatCount="indefinite"
attributeName="fill" />
</path>
</g>
<!-- comment out style section to remove animated eyes-->
<style>
.ratbag-eyes{animation: color-cycle 5s linear infinite}
@keyframes color-cycle{
20%{fill: #27A269}
40%{fill: #E5A50B}
60%{fill: #EE323A}
80%{fill: #C061CA}}
</style>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -12,15 +12,15 @@
<busconfig>
<policy user="root">
<allow own="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow send_destination="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow receive_sender="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow own="org.freedesktop.ratbag_devel1"/>
<allow send_destination="org.freedesktop.ratbag_devel1"/>
<allow receive_sender="org.freedesktop.ratbag_devel1"/>
</policy>
<!-- allow everyone to talk to the daemon -->
<policy context="default">
<allow send_destination="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow receive_sender="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow send_destination="org.freedesktop.ratbag_devel1"/>
<allow receive_sender="org.freedesktop.ratbag_devel1"/>
</policy>
</busconfig>

View File

@@ -152,6 +152,3 @@ texinfo_documents = [
author, '@PACKAGE_NAME@', 'One line description of project.',
'Miscellaneous'),
]

View File

@@ -80,6 +80,19 @@ org.freedesktop.ratbag1.Manager
The **org.freedesktop.ratbag1.Manager** interface is the entry point to
interact with ratbagd.
.. attribute:: APIVersion
:type: i
:flags: read-only,constant
The DBus API version. This is a stopgap feature while the DBus API
is still in flux. The version has no backwards or
forward-compatibilty guarantees - your client must understand
**exactly** the same version, it is not enough to support an older
or a newer version.
This API will be removed once the DBus API is declared stable.
.. attribute:: Devices
:type: ao

View File

@@ -1,6 +1,6 @@
if get_option('documentation')
cmd_sphinx = find_program('sphinx-build-3', 'sphinx-build3')
cmd_sphinx = find_program('sphinx-build-3', 'sphinx-build3', 'sphinx-build')
doc_config = configuration_data()
doc_config.set('PACKAGE_NAME', meson.project_name())

View File

@@ -1,14 +1,14 @@
project('libratbag', 'c', 'cpp',
version : '0.9.905',
project('libratbag', 'c',
version : '0.15',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.40.0')
meson_version : '>= 0.53.0')
libratbag_version = meson.project_version().split('.')
git = find_program('git')
r = run_command('tools/ratbag-git-sha.sh')
ratbagd_sha = r.stdout().strip()
# The DBus API version. Increase this every time the DBus API changes.
# No backwards/forwards guarantee, clients are expected to understand
# whatever ratbagd speaks or bail out. This should be removed if we ever
# finish the API and declare it stable.
ratbagd_api_version = 1
# We use libtool-version numbers because it's easier to understand.
# Before making a release, the libratbag_so_* and liblur_so_*
@@ -28,22 +28,24 @@ liblur_so_version = '@0@.@1@.@2@'.format((liblur_so_c-liblur_so_a),
# Compiler setup
cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
cppflags = ['-Wno-unused-parameter', '-fvisibility=hidden']
cflags = cppflags + ['-Wmissing-prototypes', '-Wstrict-prototypes']
cflags = ['-Wno-unused-parameter',
'-fvisibility=hidden',
'-Wmissing-prototypes',
'-Wformat', # required by format-security
'-Werror=format-security',
'-Wstrict-prototypes']
add_project_arguments(cflags, language: 'c')
add_project_arguments(cppflags, language: 'cpp')
# Initialize config.h, to be added to in the various options below, config.h
# is generated at the end of this file
config_h = configuration_data()
config_h.set('_GNU_SOURCE', '1')
config_h.set_quoted('RATBAG_VERSION', meson.project_version())
config_h.set_quoted('FALLBACK_SVG_NAME', 'fallback.svg')
config_h.set('RATBAGD_API_VERSION', ratbagd_api_version)
libratbag_data_dir = join_paths(get_option('prefix'),
get_option('datadir'),
'libratbag')
libratbag_data_dir_devel = join_paths(meson.source_root(), 'data')
libratbag_data_dir_devel = join_paths(meson.source_root(), 'data', 'devices')
config_h.set_quoted('LIBRATBAG_DATA_DIR', libratbag_data_dir)
# Coverity breaks because it doesn't define _Float128 correctly, you'll end
@@ -70,12 +72,14 @@ pkgconfig = import('pkgconfig')
dep_udev = dependency('libudev')
dep_libevdev = dependency('libevdev')
dep_glib = dependency('glib-2.0')
dep_json_glib = dependency('json-glib-1.0')
dep_lm = cc.find_library('m')
dep_unistring = cc.find_library('unistring')
if get_option('logind-provider') == 'elogind'
logind_dep = dependency('libelogind', version : '>=227')
dep_logind = dependency('libelogind', version : '>=227')
else
logind_dep = dependency('libsystemd', version : '>=227')
dep_logind = dependency('libsystemd', version : '>=227')
endif
enable_systemd = get_option('systemd')
@@ -117,7 +121,10 @@ lib_libhidpp = static_library('hidpp',
dependencies : deps_libhidpp)
dep_libhidpp = declare_dependency(link_with: lib_libhidpp)
### liblur.a ####
### liblur ####
#
# liblur is the library to handle logitech unifying receivers.
#
install_headers('src/liblur.h')
src_liblur = [
@@ -153,6 +160,9 @@ pkgconfig.generate (
)
#### libratbag.so ####
#
# libratbag is an internal-only library and the bit that does the actual
# work talking to the mouse. it's used by ratbagd.
src_libratbag = [
'src/libratbag-enums.h',
'src/libratbag.h',
@@ -162,8 +172,11 @@ src_libratbag = [
'src/driver-logitech-g300.c',
'src/driver-logitech-g600.c',
'src/driver-roccat.c',
'src/driver-roccat-kone-pure.c',
'src/driver-gskill.c',
'src/driver-steelseries.c',
'src/driver-sinowealth.c',
'src/driver-sinowealth-nubwo.c',
'src/driver-test.c',
'src/libratbag.c',
'src/libratbag.h',
@@ -181,6 +194,7 @@ deps_libratbag = [
dep_udev,
dep_libevdev,
dep_glib,
dep_json_glib,
dep_libutil,
dep_libhidpp,
]
@@ -241,6 +255,8 @@ executable('hidpp20-reset',
)
#### lur-command ####
#
# A tool to access and manipulate logitech unifying receivers.
src_lur_command = [ 'tools/lur-command.c' ]
executable('lur-command',
src_lur_command,
@@ -268,59 +284,88 @@ data_files = files(
'data/devices/gskill-MX-780.device',
'data/devices/logitech-M325.device',
'data/devices/logitech-M570.device',
'data/devices/logitech-M585-M590.device',
'data/devices/logitech-M705.device',
'data/devices/logitech-M720.device',
'data/devices/logitech-MX-Ergo.device',
'data/devices/logitech-MX-Master.device',
'data/devices/logitech-MX-Master-2S.device',
'data/devices/logitech-MX-Master-3.device',
'data/devices/logitech-MX518.device',
'data/devices/logitech-T650.device',
'data/devices/logitech-Wireless-Touchpad.device',
'data/devices/logitech-g-powerplay.device',
'data/devices/logitech-g-pro-wireless.device',
'data/devices/logitech-g-pro.device',
'data/devices/logitech-g102-g203.device',
'data/devices/logitech-g300.device',
'data/devices/logitech-g302.device',
'data/devices/logitech-g303.device',
'data/devices/logitech-g305.device',
'data/devices/logitech-g402.device',
'data/devices/logitech-g403-hero.device',
'data/devices/logitech-g403-wireless.device',
'data/devices/logitech-g403.device',
'data/devices/logitech-g5-2007.device',
'data/devices/logitech-g5.device',
'data/devices/logitech-g500.device',
'data/devices/logitech-g500s.device',
'data/devices/logitech-g502-HERO-over-USB.device',
'data/devices/logitech-g502-Proteus-Core-over-USB.device',
'data/devices/logitech-g502-Proteus-Spectrum-over-USB.device',
'data/devices/logitech-g502-hero-wireless.device',
'data/devices/logitech-g502-hero.device',
'data/devices/logitech-g502-proteus-core.device',
'data/devices/logitech-g502-proteus-spectrum.device',
'data/devices/logitech-g513.device',
'data/devices/logitech-g600.device',
'data/devices/logitech-g602.device',
'data/devices/logitech-g603.device',
'data/devices/logitech-g604.device',
'data/devices/logitech-g7.device',
'data/devices/logitech-g700-wireless.device',
'data/devices/logitech-g700.device',
'data/devices/logitech-g700s-wireless.device',
'data/devices/logitech-g700s.device',
'data/devices/logitech-g703-hero.device',
'data/devices/logitech-g703.device',
'data/devices/logitech-g9.device',
'data/devices/logitech-g900-wireless.device',
'data/devices/logitech-g900.device',
'data/devices/logitech-g903-hero.device',
'data/devices/logitech-g903.device',
'data/devices/logitech-g9x-[Call-of-Duty-MW3-Edition].device',
'data/devices/logitech-g9x-[Original].device',
'data/devices/logitech-gpowerplay-wireless.device',
'data/devices/logitech-g815.device',
'data/devices/logitech-g910.device',
'data/devices/logitech-g915.device',
'data/devices/logitech-g935.device',
'data/devices/logitech-g9x-Call-of-Duty-MW3-Edition.device',
'data/devices/logitech-g9x-Original.device',
'data/devices/logitech-Marathon-M705.device',
'data/devices/logitech-MX-Anywhere2.device',
'data/devices/logitech-MX-Anywhere2S.device',
'data/devices/logitech-MX-Vertical.device',
'data/devices/roccat-kone-pure.device',
'data/devices/roccat-kone-xtd.device',
'data/devices/steelseries-kinzu-v2.device',
'data/devices/steelseries-kinzu-v3.device',
'data/devices/steelseries-rival-310.device',
'data/devices/steelseries-rival-600.device',
'data/devices/steelseries-rival.device',
'data/devices/steelseries-sensei-310.device',
'data/devices/steelseries-sensei-raw.device',
'data/devices/glorious-model-d.device',
'data/devices/glorious-model-o.device',
'data/devices/nubwo-x7-spectrum.device',
'data/devices/dream-machines-dm5-blink.device',
)
install_data(data_files,
install_dir : join_paths(get_option('datadir'), 'libratbag', 'devices'))
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: data_files)
duplicate_test = find_program(join_paths(meson.source_root(), 'data/devices/duplicate-check.py'))
test('duplicate-test', duplicate_test, args : data_files)
reciever_id_test = find_program(join_paths(meson.source_root(), 'data/devices/receiver-check.py'))
test('receiver-id-test', reciever_id_test, args : data_files)
#### tests ####
enable_tests = get_option('tests')
if enable_tests
@@ -365,33 +410,13 @@ if enable_tests
'--error-exitcode=3',
'--suppressions=' + valgrind_suppressions_file ],
timeout_multiplier: 5)
executable('test-build-cxx',
['test/build-cxx.cc'],
dependencies : [ dep_libratbag ],
include_directories : include_directories('src'),
cpp_args : ['-Wall', '-Wextra', '-Wno-unused-parameter'],
install : false)
executable('test-build-linker',
['test/build-pedantic.c'],
dependencies : [ dep_libratbag ],
include_directories : include_directories('src'),
install : false)
executable('test-build-gnuc90',
['test/build-pedantic.c'],
dependencies : [ dep_libratbag ],
include_directories : include_directories('src'),
c_args : ['-std=gnu90', '-Werror'],
install : false)
executable('test-build-pedantic',
['test/build-pedantic.c'],
dependencies : [ dep_libratbag ],
include_directories : include_directories('src'),
c_args : ['-std=c99', '-pedantic', '-Werror'],
install : false)
endif
#### ratbagd ####
#
# The main item of this repo, a DBus server that uses libratbag to talk to
# the mice. The DBus API is the public-facing API.
#
src_ratbagd = [
'src/shared-macro.h',
'src/shared-rbtree.h',
@@ -404,14 +429,17 @@ src_ratbagd = [
'ratbagd/ratbagd-profile.c',
'ratbagd/ratbagd-resolution.c',
'ratbagd/ratbagd-test.c',
'ratbagd/ratbagd-json.c',
'ratbagd/ratbagd-json.h',
'src/libratbag-util.h',
'src/libratbag-util.c',
]
deps_ratbagd = [
dep_udev,
logind_dep,
dep_logind,
dep_libratbag,
dep_unistring,
]
executable('ratbagd',
@@ -424,21 +452,33 @@ executable('ratbagd',
install_man('ratbagd/ratbagd.8')
#### ratbagd_devel ####
#
# A development ratbagd server that owns a different name on the bus
# (org.freedesktop.ratbag_devel1). This server is used by ratbagdctl.devel.
#
dbus_devel_policy = configure_file(input : 'dbus/org.freedesktop.ratbag_devel1.conf.in',
output : 'org.freedesktop.ratbag_devel1.conf',
copy : true)
# This is a hack. We always install the devel policy file into
# /etc/dbus-1/system.d, independent of any prefixes we use otherwise.
# This should never be used outside of developer machines anyway, but
# installations on those may use different prefixes for building.
# This is not set in stone, suggest something better if you can.
dbussystemdir = join_paths('/', get_option('sysconfdir'), 'dbus-1', 'system.d')
executable('ratbagd.devel',
src_ratbagd,
dependencies : deps_ratbagd,
include_directories : include_directories('src'),
install : false,
c_args : ['-DRATBAG_DBUS_INTERFACE="ratbag_devel1_@0@"'.format(ratbagd_sha),
c_args : ['-DRATBAG_DBUS_INTERFACE="ratbag_devel1"',
'-DDBUS_POLICY_SRC="@0@/@1@"'.format(meson.build_root(), dbus_devel_policy),
'-DDBUS_POLICY_DST="@0@/@1@"'.format(dbussystemdir, dbus_devel_policy),
'-DDISABLE_COREDUMP=1'],
)
config_ratbagd_devel = configuration_data()
config_ratbagd_devel.set('ratbagd_sha', ratbagd_sha)
configure_file(input : 'dbus/org.freedesktop.ratbag_devel1.conf.in',
output : 'org.freedesktop.ratbag_devel1.conf',
configuration : config_ratbagd_devel)
#### unit file ####
if enable_systemd
@@ -477,7 +517,7 @@ endif
dbusdir = get_option('dbus-root-dir')
if dbusdir == ''
dbusdir = join_paths(get_option('datadir'), 'dbus-1')
dbusdir = join_paths(get_option('prefix'), get_option('datadir'), 'dbus-1')
endif
configure_file(input : 'dbus/org.freedesktop.ratbag1.service.in',
@@ -504,23 +544,47 @@ configure_file(input : 'dbus/org.freedesktop.ratbag1.conf.in',
#### tools ####
# This is a special idiomatic construct in meson to get a fast dependency that
# doesn't exist and isn't logged as "not found".
dep_python3 = dependency('', required : false)
if meson.version().version_compare('<0.48.0')
python = import('python3')
py3 = python.find_python()
py_version = python.language_version()
else
pymod = import('python')
py3 = pymod.find_installation()
if meson.version().version_compare('>= 0.53.0')
dep_python3 = py3.dependency(embed : true)
endif
py_version = py3.language_version()
endif
# ratbagctl calls ratbagd over DBus
# From python 3.8 we neeed python3-embed
embed = py_version.version_compare('>= 3.8') ? '-embed' : ''
if not dep_python3.found()
dep_python3 = dependency('python-@0@@1@'.format(py_version, embed), required : false)
if not dep_python3.found()
dep_python3 = dependency('python3@0@'.format(embed))
endif
endif
config_ratbagctl = configuration_data()
config_ratbagctl.set('RATBAGD_API_VERSION', ratbagd_api_version)
config_ratbagctl.set('version', meson.project_version())
ratbagctl_in = configure_file(input : 'tools/ratbagctl.in.in',
output : 'ratbagctl.in',
configuration: config_ratbagctl)
# ratbagctl is the commandline tool to interact with ratbagd over DBus.
custom_target('ratbagctl',
output : 'ratbagctl',
input : ['tools/ratbagctl.in', 'tools/ratbagd.py'],
input : [ratbagctl_in, 'tools/ratbagd.py'],
build_by_default : true,
command : [py3, join_paths(meson.source_root(), 'tools', 'merge_ratbagd.py'),
'@INPUT@',
'--output', '@OUTPUT@',
'--version', meson.project_version()],
'--output', '@OUTPUT@'],
install: true,
install_dir: get_option('bindir'))
@@ -533,9 +597,9 @@ man_ratbagctl = configure_file (
)
config_ratbagctl_devel = configuration_data()
config_ratbagctl_devel.set('ratbagd_sha', ratbagd_sha)
config_ratbagctl_devel.set('MESON_BUILD_ROOT', meson.build_root())
config_ratbagctl_devel.set('LIBRATBAG_DATA_DIR', libratbag_data_dir_devel)
config_ratbagctl_devel.set('RATBAGD_API_VERSION', ratbagd_api_version)
configure_file(input : 'tools/toolbox.py',
output : 'toolbox.py',
@@ -567,7 +631,7 @@ swig_gen = generator(
)
wrapper_deps = [
dependency('python3'),
dep_python3,
dep_libratbag,
dep_libshared,
]
@@ -578,7 +642,25 @@ shared_library(
'libratbag',
c_source,
name_prefix: '_',
c_args : ['-Wno-missing-prototypes', '-Wno-format-nonliteral'],
c_args : ['-Wno-missing-prototypes',
'-Wno-format-nonliteral',
'-Wno-missing-field-initializers'],
extra_files: [ i_source ] + src_libratbag ,
dependencies: deps_libratbag + wrapper_deps,
include_directories : include_directories('src', 'tools'),
install: false,
)
i_source = join_paths(meson.source_root(), 'src', 'hidpp.i')
c_source = swig_gen.process(i_source)
shared_library(
'hidpp',
c_source,
name_prefix: '_',
c_args : ['-Wno-missing-prototypes',
'-Wno-format-nonliteral',
'-Wno-missing-field-initializers',
'-Wno-strict-prototypes'],
extra_files: [ i_source ] + src_libratbag ,
dependencies: deps_libratbag + wrapper_deps,
include_directories : include_directories('src', 'tools'),
@@ -591,14 +673,17 @@ ratbagc_py_conf.set('LIBRATBAG_DATA_DIR', libratbag_data_dir_devel)
ratbagc_py = configure_file(input: 'tools/ratbagc.py.in',
output: 'ratbagc.py',
configuration: ratbagc_py_conf)
# ratbag-command is a commandline tool with the UI as ratbagctl but instead
# of connecting to ratbagd over DBus, it uses libratbag directly. This is a
# development tool for protocol debugging etc.
custom_target('ratbag-command',
output : 'ratbag-command',
input : ['tools/ratbagctl.in', ratbagc_py],
input : [ratbagctl_in, ratbagc_py],
build_by_default : true,
command : [py3, join_paths(meson.source_root(), 'tools', 'merge_ratbagd.py'),
'@INPUT@',
'--output', '@OUTPUT@',
'--version', meson.project_version()],
'--output', '@OUTPUT@'],
install: false)
#### output files ####

12
ratbagd/.gitignore vendored
View File

@@ -1,12 +0,0 @@
*.a
*.log
*.o
*.stamp
*.swp
*.trs
*~
.dirstamp
.libs/
/*.tar.xz
tags
__pycache__

View File

@@ -87,17 +87,10 @@ static int ratbagd_button_set_button(sd_bus *bus,
r = ratbag_button_set_button(button->lib_button, map);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"ButtonMapping",
NULL);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"ActionType",
"Mapping",
NULL);
}
@@ -146,17 +139,10 @@ static int ratbagd_button_set_special(sd_bus *bus,
r = ratbag_button_set_special(button->lib_button, special);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"SpecialMapping",
NULL);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"ActionType",
"Mapping",
NULL);
}
@@ -261,17 +247,10 @@ static int ratbagd_button_set_macro(sd_bus *bus,
}
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"Macro",
NULL);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"ActionType",
"Mapping",
NULL);
}

View File

@@ -41,6 +41,8 @@
#include "libratbag-util.h"
struct ratbagd_device {
unsigned int refcount;
struct ratbagd *ctx;
RBNode node;
char *sysname;
@@ -170,8 +172,11 @@ static void ratbagd_device_commit_pending(void *data)
int r;
r = ratbag_device_commit(device->lib_device);
if (r)
log_error("error committing device (%d)\n", r);
if (r < 0)
ratbagd_device_resync(device, device->ctx->bus);
ratbagd_device_unref(device);
}
static int ratbagd_device_commit(sd_bus_message *m,
@@ -182,7 +187,7 @@ static int ratbagd_device_commit(sd_bus_message *m,
ratbagd_schedule_task(device->ctx,
ratbagd_device_commit_pending,
device);
ratbagd_device_ref(device));
CHECK_CALL(sd_bus_reply_method_return(m, "u", 0));
@@ -230,7 +235,7 @@ int ratbagd_device_new(struct ratbagd_device **out,
const char *sysname,
struct ratbag_device *lib_device)
{
_cleanup_(ratbagd_device_freep) struct ratbagd_device *device = NULL;
_cleanup_(ratbagd_device_unrefp) struct ratbagd_device *device = NULL;
struct ratbag_profile *profile;
unsigned int i;
int r;
@@ -240,6 +245,7 @@ int ratbagd_device_new(struct ratbagd_device **out,
assert(sysname);
device = zalloc(sizeof(*device));
device->refcount = 1;
device->ctx = ctx;
rbnode_init(&device->node);
device->lib_device = ratbag_device_ref(lib_device);
@@ -282,12 +288,12 @@ int ratbagd_device_new(struct ratbagd_device **out,
return 0;
}
struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device)
static void ratbagd_device_free(struct ratbagd_device *device)
{
unsigned int i;
if (!device)
return NULL;
return;
assert(!ratbagd_device_linked(device));
@@ -301,7 +307,26 @@ struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device)
assert(!device->lib_device); /* ratbag yields !NULL if still pinned */
return mfree(device);
mfree(device);
}
struct ratbagd_device *ratbagd_device_ref(struct ratbagd_device *device)
{
assert(device->refcount > 0);
++device->refcount;
return device;
}
struct ratbagd_device *ratbagd_device_unref(struct ratbagd_device *device)
{
assert(device->refcount > 0);
--device->refcount;
if (device->refcount == 0)
ratbagd_device_free(device);
return NULL;
}
const char *ratbagd_device_get_sysname(struct ratbagd_device *device)

563
ratbagd/ratbagd-json.c Normal file
View File

@@ -0,0 +1,563 @@
/*
* Copyright © 2019 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.
*/
/* JSON parser for ratbag test devices. The JSON format is not fixed and may
change at any time, but basically looks like this:
{
"profiles": [
{
"is_active": bool,
"is_default": bool,
"is_disabled": bool,
"rate": int,
"report_rates": [ int, ...],
"capabilities": [int, ...] # profile capabilities
"resolutions": [
{
"xres": int,
"yres": int,
"dpi_min", int,
"dpi_max", int,
"is_active": bool,
"is_default": bool,
"capabilities": [int, ...] # resolution capabilities
}
]
"buttons": [
"action_type": "<enum>" # none, button, key, special, macro, unknown
"button": int, # if button
"key": int, # if key
"special": "<enum>", # if special, for values see see special_lookup()
"macro": ["+B", "-B", "t400"] # if macro. uppercase only
]
"leds": [ ]
},
{
... next profile ...
}
]
}
ratbagd uses a minimum sane device (1 profile, 1 resolution, etc.) and any
JSON instructions get merged into that device. Which means you usually
only need to set those bits you care about being checked.
*/
#include "libratbag-util.h"
#include "ratbagd-json.h"
#include "ratbagd-test.h"
#include <json-glib/json-glib.h>
#include <libevdev/libevdev.h>
/* Hack because we don't do proper data passing */
static int num_resolutions;
static int num_buttons;
static int num_leds;
static int parse_error = 0;
#define parser_error(element_) { \
log_error("json: parser error: %s:%d: element '%s'\n", __func__, __LINE__, (element_)); \
parse_error = -EINVAL; \
goto out; \
}
static void parse_resolution_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_resolution *resolution = data;
if (streq(name, "xres")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("xres");
resolution->xres = v;
log_verbose("json: xres: %d\n", v);
} else if (streq(name, "yres")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("yres");
resolution->yres = v;
log_verbose("json: yres: %d\n", v);
} else if (streq(name, "dpi_min")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("dpi_min");
resolution->dpi_min = v;
log_verbose("json: dpi_min: %d\n", v);
} else if (streq(name, "dpi_max")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("dpi_min");
resolution->dpi_max = v;
log_verbose("json: dpi_max: %d\n", v);
} else if (streq(name, "is_active")) {
gboolean v = json_object_get_boolean_member(obj, name);
resolution->active = v;
log_verbose("json: is_active: %d\n", v);
} else if (streq(name, "is_default")) {
gboolean v = json_object_get_boolean_member(obj, name);
resolution->dflt = v;
log_verbose("json: is_default: %d\n", v);
} else if (streq(name, "capabilities")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(resolution->caps));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
if (v > RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION)
parser_error("capabilities");
resolution->caps[s] = v;
}
log_verbose("json: caps: %d %d %d %d %d...\n",
resolution->caps[0],
resolution->caps[1],
resolution->caps[2],
resolution->caps[3],
resolution->caps[4]);
} else {
log_error("json: unknown resolution key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_resolution(JsonNode *node,
struct ratbag_test_resolution *resolution)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_resolution_member, resolution);
}
static void parse_led_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_led *led = data;
if (streq(name, "type")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > RATBAG_LED_TYPE_SWITCHES)
parser_error("type");
led->type = v;
log_verbose("json: type: %d\n", v);
} else if (streq(name, "mode")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > RATBAG_LED_BREATHING)
parser_error("mode");
led->mode = v;
log_verbose("json: mode: %d\n", v);
} else if (streq(name, "duration")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 10000)
parser_error("duration");
led->ms = v;
log_verbose("json: duration: %d\n", v);
} else if (streq(name, "brightness")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 100)
parser_error("brightness");
led->brightness = v;
log_verbose("json: brightness: %d\n", v);
} else if (streq(name, "color")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) == 3);
led->color.red = json_array_get_int_element(a, 0);
led->color.green = json_array_get_int_element(a, 1);
led->color.blue = json_array_get_int_element(a, 2);
log_verbose("json: color: %02x%02x%02x\n",
led->color.red,
led->color.green,
led->color.blue);
} else {
log_error("json: unknown led key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_led(JsonNode *node, struct ratbag_test_led *led)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_led_member, led);
}
static enum ratbag_button_action_special
special_lookup(const char *string)
{
const struct {
const char *key;
enum ratbag_button_action_special special;
} lut[] = {
{ "invalid", RATBAG_BUTTON_ACTION_SPECIAL_INVALID},
{ "unknown", RATBAG_BUTTON_ACTION_SPECIAL_UNKNOWN},
{ "doubleclick", RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK},
{ "wheel-left", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT},
{ "wheel-right", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT},
{ "wheel-up", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP},
{ "wheel-down", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN},
{ "ratchet-mode-switch", RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH},
{ "resolution-cycle-up", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP},
{ "resolution-cycledown", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN},
{ "resolution-up", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP},
{ "resolution-down", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN},
{ "resolution-alternate", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE},
{ "resolution-default", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT},
{ "profile-cycle-up", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP},
{ "profile-cycle-down", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_DOWN},
{ "profile-up", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP},
{ "profile-down", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN},
{ "second-mode", RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE},
{ "battery-level", RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL},
};
for (size_t i = 0; i < ARRAY_LENGTH(lut); i++) {
if (streq(string, lut[i].key))
return lut[i].special;
}
parser_error("special");
out:
return RATBAG_BUTTON_ACTION_SPECIAL_INVALID;
}
static enum ratbag_button_action_type
action_type_lookup(const char *string)
{
const struct {
const char *key;
enum ratbag_button_action_type type;
} lut[] = {
{ "none", RATBAG_BUTTON_ACTION_TYPE_NONE},
{ "button", RATBAG_BUTTON_ACTION_TYPE_BUTTON},
{ "special", RATBAG_BUTTON_ACTION_TYPE_SPECIAL},
{ "key", RATBAG_BUTTON_ACTION_TYPE_KEY},
{ "macro", RATBAG_BUTTON_ACTION_TYPE_MACRO},
{ "unknown", RATBAG_BUTTON_ACTION_TYPE_UNKNOWN},
};
for (size_t i = 0; i < ARRAY_LENGTH(lut); i++) {
if (streq(string, lut[i].key))
return lut[i].type;
}
parser_error("action_type");
out:
return RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
}
static inline struct ratbag_test_macro_event
parse_macro(const char *m)
{
struct ratbag_test_macro_event event = {
.type = RATBAG_MACRO_EVENT_INVALID,
.value = 0,
};
char keyname[32];
if (strlen(m) < 2)
goto out;
switch(m[0]) {
case '+':
event.type = RATBAG_MACRO_EVENT_KEY_PRESSED;
snprintf(keyname, sizeof(keyname), "KEY_%s", m + 1);
event.value = libevdev_event_code_from_name(EV_KEY,
keyname);
log_verbose("json: macro: +%s\n", keyname);
break;
case '-':
event.type = RATBAG_MACRO_EVENT_KEY_RELEASED;
snprintf(keyname, sizeof(keyname), "KEY_%s", m + 1);
event.value = libevdev_event_code_from_name(EV_KEY,
keyname);
log_verbose("json: macro: -%s\n", keyname);
break;
case 't':
event.type = RATBAG_MACRO_EVENT_WAIT;
event.value = atoi(m+1);
log_verbose("json: macro: t%d\n", event.value);
break;
default:
parser_error("macro");
break;
}
out:
return event;
}
static void parse_button_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_button *button = data;
if (streq(name, "action_type")) {
const gchar* v = json_object_get_string_member(obj, name);
button->action_type = action_type_lookup(v);
log_verbose("json: action_type: %s\n", v);
} else if (streq(name, "button")) {
gint v = json_object_get_int_member(obj, name);
if (v < 0 || v > 32)
parser_error("button");
button->button = v;
log_verbose("json: button: %d\n", v);
} else if (streq(name, "key")) {
gint v = json_object_get_int_member(obj, name);
if (v < 0 || v > KEY_MAX)
parser_error("key");
button->key = v;
log_verbose("json: key: %d\n", v);
} else if (streq(name, "special")) {
const gchar *v = json_object_get_string_member(obj, name);
button->special = special_lookup(v);
log_verbose("json: special: %s\n", v);
} else if (streq(name, "macro")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(button->macro));
for (size_t s = 0; s < json_array_get_length(a); s++) {
const gchar *v = json_array_get_string_element(a, s);
button->macro[s] = parse_macro(v);
}
} else {
log_error("json: unknown button key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_button(JsonNode *node, struct ratbag_test_button *button)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_button_member, button);
}
static void parse_profile_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_profile *profile = data;
if (streq(name, "name")) {
const char *v = json_object_get_string_member(obj, name);
if (v == NULL)
parser_error("name");
free(profile->name);
profile->name = strdup_safe(v);
log_verbose("name: %s\n", v);
} else if (streq(name, "is_default")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->dflt = v;
log_verbose("json: is_default: %d\n", v);
} else if (streq(name, "is_active")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->active = v;
log_verbose("json: is_active: %d\n", v);
} else if (streq(name, "is_disabled")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->disabled = v;
log_verbose("json: is_disabled: %d\n", v);
} else if (streq(name, "rate")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("rate");
profile->hz = v;
log_verbose("json: rate: %d\n", v);
} else if (streq(name, "report_rates")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(profile->report_rates));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
if (v < 0 || v > 20000)
parser_error("report_rate");
profile->report_rates[s] = v;
}
log_verbose("json: report rates: %d %d %d %d %d\n",
profile->report_rates[0],
profile->report_rates[1],
profile->report_rates[2],
profile->report_rates[3],
profile->report_rates[4]);
} else if (streq(name, "capabilities")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(profile->caps));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
profile->caps[s] = v;
}
log_verbose("json: caps: %d %d %d %d %d...\n",
profile->caps[0],
profile->caps[1],
profile->caps[2],
profile->caps[3],
profile->caps[4]);
} else if (streq(name, "resolutions")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing resolution %d\n", idx);
parse_resolution(l->data, &profile->resolutions[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_resolutions = max(idx, num_resolutions);
} else if (streq(name, "leds")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing LED %d\n", idx);
parse_led(l->data, &profile->leds[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_leds = max(idx, num_leds);
} else if (streq(name, "buttons")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing button %d\n", idx);
parse_button(l->data, &profile->buttons[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_buttons = max(idx, num_buttons);
} else {
log_error("json: unknown profile key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_profile(JsonNode *node,
struct ratbag_test_profile *profile)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_profile_member, profile);
}
/* declared here because this isn't really public API, we just need to
* access it from the tests */
int ratbagd_parse_json(const char *data, struct ratbag_test_device *device)
{
g_autoptr(JsonParser) parser = NULL;
JsonNode *root;
JsonObject *obj;
JsonArray *arr;
int r = -EINVAL;
g_autoptr(GError) error = NULL;
g_autoptr(GList) list = NULL;
error = 0;
parser = json_parser_new();
if (!json_parser_load_from_data(parser, data, strlen(data), &error)) {
log_error("Failed to load JSON: %s\n", error->message);
r = -EINVAL;
goto out;
}
log_verbose("json: data: %s\n", data);
root = json_parser_get_root(parser);
if (JSON_NODE_TYPE(root) != JSON_NODE_OBJECT)
parser_error("root");
obj = json_node_get_object(root);
arr = json_object_get_array_member(obj, "profiles");
if (!arr)
parser_error("profiles");
/* Our test device is preloaded with sane defaults, let's keep those */
num_resolutions = device->num_resolutions;
num_buttons = device->num_buttons;
num_leds = device->num_leds;
list = json_array_get_elements(arr);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing profile %d\n", idx);
parse_profile(l->data, &device->profiles[idx]);
if (parse_error != 0)
goto out;;
l = g_list_next(l);
idx++;
}
device->num_profiles = idx;
device->num_resolutions = num_resolutions;
device->num_buttons = num_buttons;
device->num_leds = num_leds;
r = 0;
out:
return r;
}

28
ratbagd/ratbagd-json.h Normal file
View File

@@ -0,0 +1,28 @@
/*
* Copyright © 2019 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include "libratbag-test.h"
int ratbagd_parse_json(const char *data, struct ratbag_test_device *device);

View File

@@ -31,6 +31,8 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistr.h>
#include <uniconv.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include "ratbagd.h"
@@ -382,18 +384,23 @@ ratbagd_profile_get_name(sd_bus *bus,
{
struct ratbagd_profile *profile = userdata;
const char *name = ratbag_profile_get_name(profile->lib_profile);
_cleanup_free_ char *asciiname;
_cleanup_free_ char *utf8 = NULL;
if (!name)
asciiname = strdup("");
else
/* This should really be a UTF8 check and it should really be in
* libratbag, but for now this will do to at least not kick us off
* the bus. https://github.com/libratbag/libratbag/issues/617
*/
asciiname = strdup_ascii_only(name);
if (name) {
if (u8_check((const uint8_t*)name, strlen(name)) == NULL)
utf8 = strdup(name);
else
utf8 = (char*)u8_strconv_from_encoding(name,
"ISO-8859-1",
iconveh_question_mark);
if (!utf8)
utf8 = (char*)strdup_ascii_only(name);
} else {
utf8 = strdup("");
}
CHECK_CALL(sd_bus_message_append(reply, "s", asciiname));
CHECK_CALL(sd_bus_message_append(reply, "s", utf8));
return 0;
}

View File

@@ -30,159 +30,21 @@
#include <systemd/sd-event.h>
#include "libratbag-test.h"
#include "ratbagd-json.h"
/* A pre-setup sane device. Use for sanity testing by toggling the various
* error conditions.
*/
static const struct ratbag_test_device ratbagd_test_device_descr = {
.num_profiles = 4,
.num_resolutions = 3,
.num_buttons = 4,
.num_leds = 3,
.profiles = {
{
.name = NULL,
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 0 },
{ .button_type = RATBAG_BUTTON_TYPE_MIDDLE,
.action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = KEY_3 },
{ .button_type = RATBAG_BUTTON_TYPE_RIGHT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
.special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO,
.macro = {
{ .type = RATBAG_MACRO_EVENT_KEY_PRESSED,
.value = KEY_B },
{ .type = RATBAG_MACRO_EVENT_KEY_RELEASED,
.value = KEY_B },
{ .type = RATBAG_MACRO_EVENT_WAIT,
.value = 300 },
},
}
},
.resolutions = {
{ .xres = 100, .yres = 200,
.dpi_min = 50, .dpi_max = 5000},
{ .xres = 200, .yres = 300, .active = true, .dflt = true },
{ .xres = 300, .yres = 400 },
},
.active = true,
.dflt = false,
.hz = 1000,
.report_rates = {500, 1000},
.leds = {
{
.mode = RATBAG_LED_OFF,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20,
.type = RATBAG_LED_TYPE_LOGO,
},
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20,
.type = RATBAG_LED_TYPE_SIDE,
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.ms = 333,
.brightness = 40,
.type = RATBAG_LED_TYPE_SIDE,
}
},
},
{
.buttons = {
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 4 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 5 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 6 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 7 },
},
.resolutions = {
{ .xres = 1100, .yres = 1200, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 1200, .yres = 1300, .dflt = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 1300, .yres = 1400, .active = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
},
.hz = 2000,
.active = false,
.dflt = true,
.name = "test profile 2",
},
{
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
.special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO,
.macro = {
{ .type = RATBAG_MACRO_EVENT_KEY_PRESSED,
.value = KEY_A },
{ .type = RATBAG_MACRO_EVENT_KEY_RELEASED,
.value = KEY_A },
{ .type = RATBAG_MACRO_EVENT_WAIT,
.value = 150 },
}
},
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 2 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 3 },
},
.resolutions = {
{ .xres = 2100, .yres = 2200, .active = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 2200, .yres = 2300, .dflt = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 2300, .yres = 2400, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
},
.hz = 3000,
.leds = {
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.ms = 333,
.brightness = 40
}
},
.active = false,
.dflt = false,
},
{
.disabled = true,
},
},
.destroyed = NULL,
.destroyed_data = NULL,
};
int ratbagd_reset_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
static int load_test_device(sd_bus_message *m,
struct ratbagd *ctx,
const struct ratbag_test_device *source)
{
static int count;
static struct ratbagd_device *ratbagd_test_device = NULL;
struct ratbagd *ctx = userdata;
struct ratbag_device *device;
int r;
char devicename[64];
if (ratbagd_test_device) {
ratbagd_device_unlink(ratbagd_test_device);
ratbagd_device_free(ratbagd_test_device);
ratbagd_device_unref(ratbagd_test_device);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
@@ -191,9 +53,10 @@ int ratbagd_reset_test_device(sd_bus_message *m,
NULL);
}
device = ratbag_device_new_test_device(ctx->lib_ctx, &ratbagd_test_device_descr);
device = ratbag_device_new_test_device(ctx->lib_ctx, source);
r = ratbagd_device_new(&ratbagd_test_device, ctx, "test_device", device);
snprintf(devicename, sizeof(devicename), "testdevice%d", count++);
r = ratbagd_device_new(&ratbagd_test_device, ctx, devicename, device);
/* the ratbagd_device takes its own reference, drop ours */
ratbag_device_unref(device);
@@ -216,6 +79,55 @@ int ratbagd_reset_test_device(sd_bus_message *m,
return 0;
}
static const struct ratbag_test_device default_device_descr = {
.num_profiles = 1,
.num_resolutions = 1,
.num_buttons = 1,
.num_leds = 0,
.profiles = {
{
.name = NULL,
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 0 },
},
.resolutions = {
{ .xres = 1000, .yres = 1000,
.dpi_min = 1000, .dpi_max = 1000},
},
.disabled = false,
.active = true,
.dflt = true,
.hz = 1000,
.report_rates = {1000},
},
},
.destroyed = NULL,
.destroyed_data = NULL,
};
int ratbagd_load_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd *ctx = userdata;
struct ratbag_test_device td = default_device_descr;
char *data;
int r = 0;
CHECK_CALL(sd_bus_message_read(m, "s", &data));
r = ratbagd_parse_json(data, &td);
if (r != 0) {
log_error("Failed to parse JSON data\n");
} else {
r = load_test_device(m, ctx, &td);
}
return sd_bus_reply_method_return(m, "i", r);
}
#endif
void ratbagd_init_test_device(struct ratbagd *ctx)
@@ -223,7 +135,7 @@ void ratbagd_init_test_device(struct ratbagd *ctx)
#ifdef RATBAG_DEVELOPER_EDITION
setenv("RATBAG_TEST", "1", 0);
ratbagd_reset_test_device(NULL, ctx, NULL);
load_test_device(NULL, ctx, &default_device_descr);
#endif
}

View File

@@ -32,4 +32,7 @@ void ratbagd_init_test_device(struct ratbagd *ctx);
int ratbagd_reset_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error);
int ratbagd_load_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error);
#endif /* RATBAG_DEVELOPER_EDITION */

View File

@@ -3,12 +3,27 @@
ratbagd \- system daemon to introspect and modify configurable mice
.SH SYNOPSIS
.B ratbagd
.RB [ \-\-verbose ]
.RB [ \-\-verbose[=debug]|\-\-quiet|\-\-version|\-\-help]
.SH DESCRIPTION
.B ratbagd
starts the daemon. It shouldn't be invoked directly;
.B ratbagd
is normally started through DBus activation.
.SH OPTIONS
.TP 8
.B \-\-help
Print help and exit
.TP 8
.B \-\-verbose, \-\-verbose=debug
Enable verbose output, optionally including raw messages as sent and
received from the device. This should be use to debug any issues with the
device.
.TP 8
.B \-\-quiet
Disable any output but error messages.
.TP 8
.B \-\-version
Show the version number.
.SH SEE ALSO
.BR ratbagctl (1)
.SH AUTHORS

View File

@@ -29,6 +29,7 @@
#include <assert.h>
#include <errno.h>
#include <libgen.h>
#include <libratbag.h>
#include <libudev.h>
#include <stdio.h>
@@ -166,9 +167,10 @@ static int ratbagd_get_devices(sd_bus *bus,
static const sd_bus_vtable ratbagd_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("APIVersion", "i", 0, offsetof(struct ratbagd, api_version), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Devices", "ao", ratbagd_get_devices, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
#ifdef RATBAG_DEVELOPER_EDITION
SD_BUS_METHOD("ResetTestDevice", "", "u", ratbagd_reset_test_device, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("LoadTestDevice", "s", "i", ratbagd_load_test_device, SD_BUS_VTABLE_UNPRIVILEGED),
#endif /* RATBAG_DEVELOPER_EDITION */
SD_BUS_VTABLE_END,
};
@@ -200,7 +202,7 @@ static void ratbagd_process_device(struct ratbagd *ctx,
/* device was removed, unlink it and destroy our context */
if (device) {
ratbagd_device_unlink(device);
ratbagd_device_free(device);
ratbagd_device_unref(device);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
@@ -283,7 +285,7 @@ static struct ratbagd *ratbagd_free(struct ratbagd *ctx)
RATBAGD_DEVICE_FOREACH_SAFE(device, tmp, ctx) {
ratbagd_device_unlink(device);
ratbagd_device_free(device);
ratbagd_device_unref(device);
}
ctx->bus = sd_bus_flush_close_unref(ctx->bus);
@@ -345,6 +347,7 @@ static int ratbagd_new(struct ratbagd **out)
int r;
ctx = zalloc(sizeof(*ctx));
ctx->api_version = RATBAGD_API_VERSION;
r = sd_event_default(&ctx->event);
if (r < 0)
@@ -354,6 +357,7 @@ static int ratbagd_new(struct ratbagd **out)
if (r < 0)
return r;
log_verbose("Initializing libratbag\n");
ctx->lib_ctx = ratbag_create_context(&ratbagd_lib_interface, ctx);
if (!ctx->lib_ctx)
return -ENOMEM;
@@ -518,9 +522,95 @@ static int ratbagd_run(struct ratbagd *ctx)
ctx);
sd_event_add_post(ctx->event, NULL, before_idle_cb, ctx);
log_verbose("DBus server ready\n");
return sd_event_loop(ctx->event);
}
static bool install_ratbagd_devel_dbus_policy(void)
{
bool rc = true;
#ifdef RATBAG_DEVELOPER_EDITION
_cleanup_close_ int in = -1, out = -1;
int nread, nwrite;
char buf[40960];
int r;
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL;
sd_bus *bus = NULL;
rc = false;
log_verbose("Installing DBus policy file to %s\n", DBUS_POLICY_DST);
in = open(DBUS_POLICY_SRC, O_RDONLY);
if (in == -1) {
log_error("Failed to source policy file: %m\n");
goto out;
}
if (mkdir_p(dirname(strdupa(DBUS_POLICY_DST)), 0755)) {
log_error("Failed to create destination path: %m\n");
goto out;
}
out = open(DBUS_POLICY_DST, O_CREAT|O_WRONLY, 0644);
if (out == -1) {
log_error("Failed to open destination: %m\n");
goto out;
}
nread = read(in, buf, sizeof(buf));
if (nread < 0) {
log_error("Failed to read policy file: %m\n");
goto out;
}
nwrite = write(out, buf, nread);
if (nread != nwrite) {
log_error("Failed to write policy file: %m\n");
goto out;
}
/* Now poke DBus to reload itself */
r = sd_bus_open_system(&bus);
if (r < 0) {
log_error("Unable to open system bus: %s\n", strerror(-r));
goto out;
}
r = sd_bus_call_method(bus, "org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"ReloadConfig",
&error,
&m,
"");
if (r < 0) {
log_error("Failed to call DBus ReloadConfig: %s\n",
error.message);
goto out;
}
rc = true;
out:
sd_bus_error_free(&error);
sd_bus_message_unref(m);
sd_bus_unref(bus);
#endif
return rc;
}
static void remove_ratbagd_devel_dbus_policy(void)
{
#ifdef RATBAG_DEVELOPER_EDITION
unlink(DBUS_POLICY_DST);
#endif
}
int main(int argc, char *argv[])
{
struct ratbagd *ctx = NULL;
@@ -538,18 +628,21 @@ int main(int argc, char *argv[])
return 0;
} else if (streq(argv[1], "--quiet")) {
log_level = LL_QUIET;
} else if (streq(argv[1], "--verbose=raw")) {
} else if (streq(argv[1], "--verbose") || streq(argv[1], "--verbose=raw")) {
log_level = LL_RAW;
} else if (streq(argv[1], "--verbose")) {
} else if (streq(argv[1], "--verbose=debug")) {
log_level = LL_VERBOSE;
} else {
fprintf(stderr, "Usage: %s [--version | --quiet | --verbose[=raw]]\n",
fprintf(stderr, "Usage: %s [--version | --quiet | --verbose[=debug]]\n",
program_invocation_short_name);
r = -EINVAL;
goto exit;
}
}
if (!install_ratbagd_devel_dbus_policy())
goto exit;
r = ratbagd_new(&ctx);
if (r < 0)
goto exit;
@@ -558,6 +651,7 @@ int main(int argc, char *argv[])
r = ratbagd_run(ctx);
remove_ratbagd_devel_dbus_policy();
exit:
ratbagd_free(ctx);

View File

@@ -170,7 +170,8 @@ int ratbagd_device_new(struct ratbagd_device **out,
struct ratbagd *ctx,
const char *sysname,
struct ratbag_device *lib_device);
struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device);
struct ratbagd_device *ratbagd_device_ref(struct ratbagd_device *device);
struct ratbagd_device *ratbagd_device_unref(struct ratbagd_device *device);
const char *ratbagd_device_get_sysname(struct ratbagd_device *device);
const char *ratbagd_device_get_path(struct ratbagd_device *device);
unsigned int ratbagd_device_get_num_buttons(struct ratbagd_device *device);
@@ -181,7 +182,7 @@ bool ratbagd_device_linked(struct ratbagd_device *device);
void ratbagd_device_link(struct ratbagd_device *device);
void ratbagd_device_unlink(struct ratbagd_device *device);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_device *, ratbagd_device_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_device *, ratbagd_device_unref);
struct ratbagd_device *ratbagd_device_lookup(struct ratbagd *ctx,
const char *name);
@@ -217,6 +218,8 @@ struct ratbagd_device *ratbagd_device_next(struct ratbagd_device *device);
*/
struct ratbagd {
int api_version;
sd_event *event;
struct ratbag *lib_ctx;
struct udev_monitor *monitor;

View File

@@ -90,13 +90,11 @@ check_option_args() {
#
check_modules_specification() {
if [ x"$MODFILE" = x ]; then
if [ x"${INPUT_MODULES}" = x ]; then
echo ""
echo "Error: no modules specified (blank command line)."
usage
exit 1
fi
if [ x"${INPUT_MODULES}" = x ]; then
echo ""
echo "Error: no modules specified (blank command line)."
usage
exit 1
fi
}
@@ -112,7 +110,7 @@ Subject: [ANNOUNCE] $pkg_name $pkg_version
To: $list_to
Cc: $list_cc
libratbag $tag_name is out.
$pkg_name $tag_name is out.
The list of interesting changes are:
@@ -129,43 +127,12 @@ RELEASE
The libratbag project does not generate tarballs for releases, you can
grab one directly from github:
https://github.com/libratbag/libratbag/archive/$tag_name/$tarball
https://github.com/libratbag/$pkg_name/archive/$tag_name/$tarball
RELEASE
done
}
#------------------------------------------------------------------------------
# Function: read_modfile
#------------------------------------------------------------------------------
#
# Read the module names from the file and set a variable to hold them
# This will be the same interface as cmd line supplied modules
#
read_modfile() {
if [ x"$MODFILE" != x ]; then
# Make sure the file is sane
if [ ! -r "$MODFILE" ]; then
echo "Error: module file '$MODFILE' is not readable or does not exist."
exit 1
fi
# read from input file, skipping blank and comment lines
while read line; do
# skip blank lines
if [ x"$line" = x ]; then
continue
fi
# skip comment lines
if echo "$line" | $GREP -q "^#" ; then
continue;
fi
INPUT_MODULES="$INPUT_MODULES $line"
done <"$MODFILE"
fi
return 0
}
#------------------------------------------------------------------------------
# Function: print_epilog
#------------------------------------------------------------------------------
@@ -215,46 +182,6 @@ process_modules() {
done
}
#------------------------------------------------------------------------------
# Function: get_section
#------------------------------------------------------------------------------
# Code 'return 0' on success
# Code 'return 1' on error
# Sets global variable $section
get_section() {
local module_url
local full_module_url
# Obtain the git url in order to find the section to which this module belongs
full_module_url=`git config --get remote.$remote_name.url | sed 's:\.git$::'`
if [ $? -ne 0 ]; then
echo "Error: unable to obtain git url for remote \"$remote_name\"."
return 1
fi
# The last part of the git url will tell us the section. Look for xorg first
echo "$full_module_url"
module_url=`echo "$full_module_url" | $GREP -o "libratbag/.*"`
if [ $? -eq 0 ]; then
module_url=`echo $module_url | cut -d'/' -f2,3`
else
echo "Error: unable to locate a valid project url from \"$full_module_url\"."
echo "Cannot establish url as one of libratbag"
cd $top_src
return 1
fi
# Find the section (subdirs) where the tarballs are to be uploaded
# The module relative path can be app/xfs, xserver, or mesa/drm for example
section=`echo $module_url | cut -d'/' -f1`
if [ $? -ne 0 ]; then
echo "Error: unable to extract section from $module_url first field."
return 1
fi
return 0
}
#------------------------------------------------------------------------------
# Function: process_module
#------------------------------------------------------------------------------
@@ -299,26 +226,27 @@ process_module() {
current_branch=`git branch | $GREP "\*" | sed -e "s/\* //"`
remote_name=`git config --get branch.$current_branch.remote`
remote_branch=`git config --get branch.$current_branch.merge | cut -d'/' -f3,4`
remote_url_root="https://github.com/libratbag/libratbag/archive"
echo "Info: working off the \"$current_branch\" branch tracking the remote \"$remote_name/$remote_branch\"."
# Obtain the section
get_section
if [ $? -ne 0 ]; then
cd $top_src
return 1
fi
# Find out the tarname from meson.build
pkg_name=$($GREP '^project(' meson.build | sed 's|project(\([^\s,]*\).*|\1|' | sed "s/'//g")
pkg_version=$($GREP -m 1 '^ version : ' meson.build | sed 's| version : \([^\s,]*\).*|\1|' | sed "s/'//g")
pkg_version=$($GREP -m 1 '^[ ]*version \?: ' meson.build | sed 's|[ ]*version \?: \([^\s,]*\).*|\1|' | sed "s/'//g")
tar_name="$pkg_name-$pkg_version"
targz=$tar_name.tar.gz
tag_name=$(echo "v$pkg_version" | sed 's|.0$||')
# libratbag tags with v0.3 but piper user just 0.3
case `git describe` in
v*)
tag_name=$(echo "v$pkg_version" | sed 's|\.0$||')
;;
*)
tag_name=$(echo "$pkg_version" | sed 's|\.0$||')
;;
esac
remote_url_root="https://github.com/libratbag/$pkg_name/archive"
# Now get the tarballs from github directly to compute their checksum
remote_targz_url=$remote_url_root/$tag_name/$targz
echo "downloading the tarbal from $remote_targz_url"
echo "downloading the tarball from $remote_targz_url"
curl $remote_targz_url > $targz
# Obtain the top commit SHA which should be the version bump
@@ -513,13 +441,6 @@ do
exit 1
;;
*)
if [ x"${MODFILE}" != x ]; then
echo ""
echo "Error: specifying both modules and --modfile is not permitted"
echo ""
usage
exit 1
fi
INPUT_MODULES="${INPUT_MODULES} $1"
;;
esac
@@ -530,9 +451,6 @@ done
# If no modules specified (blank cmd line) display help
check_modules_specification
# Read the module file and normalize input in INPUT_MODULES
read_modfile
# Loop through each module to release
# Exit on error if --no-quit no specified
process_modules

View File

@@ -164,6 +164,7 @@ hidpp10drv_map_button(struct ratbag_device *device,
{
struct hidpp10_profile profile;
int ret;
unsigned int modifiers = 0;
ret = hidpp10_get_profile(hidpp10, button->profile->index, &profile);
if (ret)
@@ -178,6 +179,7 @@ hidpp10drv_map_button(struct ratbag_device *device,
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
button->action.action.key.key = ratbag_hidraw_get_keycode_from_keyboard_usage(device,
profile.buttons[button->index].keys.key);
modifiers = profile.buttons[button->index].keys.modifier_flags;
break;
case PROFILE_BUTTON_TYPE_CONSUMER_CONTROL:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
@@ -199,6 +201,14 @@ hidpp10drv_map_button(struct ratbag_device *device,
}
}
if (button->action.type == RATBAG_BUTTON_ACTION_TYPE_KEY) {
ret = ratbag_button_macro_new_from_keycode(button, button->action.action.key.key, modifiers);
if (ret < 0) {
log_error(device->ratbag, "hidpp10: error while reading button %d\n", button->index);
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
}
}
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
}
@@ -299,6 +309,8 @@ hidpp10drv_write_button(struct hidpp10_profile *profile,
struct hidpp10drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp10_device *hidpp10 = drv_data->dev;
uint8_t code;
unsigned int modifiers, key;
int rc;
if (hidpp10->profile_type == HIDPP10_PROFILE_UNKNOWN)
return -ENOTSUP;
@@ -330,6 +342,26 @@ hidpp10drv_write_button(struct hidpp10_profile *profile,
profile->buttons[button->index].special.special = code;
break;
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
rc = ratbag_action_keycode_from_macro(action, &key, &modifiers);
if (rc < 0) {
log_error(device->ratbag, "hidpp10: can't convert macro action to keycode in button %d\n", button->index);
return -EINVAL;
}
code = ratbag_hidraw_get_keyboard_usage_from_keycode(device, key);
if (code == 0) {
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, key);
if (code == 0)
return -EINVAL;
profile->buttons[button->index].consumer_control.type = PROFILE_BUTTON_TYPE_CONSUMER_CONTROL;
profile->buttons[button->index].consumer_control.consumer_control = code;
} else {
profile->buttons[button->index].keys.type = PROFILE_BUTTON_TYPE_KEYS;
profile->buttons[button->index].keys.key = code;
profile->buttons[button->index].keys.modifier_flags = modifiers;
}
break;
default:
return -ENOTSUP;
}
@@ -412,9 +444,12 @@ hidpp10drv_read_profile(struct ratbag_profile *profile)
profile->is_enabled = p.enabled;
if (profile->name)
if (profile->name) {
free(profile->name);
profile->name = strdup_safe((char*)p.name);
profile->name = NULL;
}
if (p.name && p.name[0] != '\0')
profile->name = strdup_safe((char*)p.name);
rc = hidpp10_get_current_profile(hidpp10, &idx);
if (rc == 0 && (unsigned int)idx == profile->index)
@@ -538,7 +573,8 @@ hidpp10drv_commit(struct ratbag_device *device)
return rc;
p.enabled = profile->is_enabled;
strncpy_safe((char*)p.name, profile->name, 24);
if (profile->name != NULL)
strncpy_safe((char*)p.name, profile->name, 24);
ratbag_profile_for_each_resolution(profile, resolution) {
p.dpi_modes[resolution->index].xres = resolution->dpi_x;
@@ -555,8 +591,10 @@ hidpp10drv_commit(struct ratbag_device *device)
continue;
rc = hidpp10drv_write_button(&p, button, &action);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp10: failed to update buttons (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
ratbag_profile_for_each_led(profile, led)
@@ -564,8 +602,10 @@ hidpp10drv_commit(struct ratbag_device *device)
if (dev->profile_type != HIDPP10_PROFILE_UNKNOWN) {
rc = hidpp10_set_profile(dev, profile->index, &p);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp10: failed to set profile (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
/* Update the current resolution in case it changed */
@@ -573,8 +613,10 @@ hidpp10drv_commit(struct ratbag_device *device)
rc = hidpp10_set_current_resolution(dev,
active_resolution->dpi_x,
active_resolution->dpi_y);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp10: failed to set active resolution (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
active_resolution = NULL;
}
@@ -628,9 +670,9 @@ hidpp10drv_probe(struct ratbag_device *device)
* If there is a special need like for G700(s), then add a DeviceIndex
* entry to the .device file.
*/
dev = hidpp10_device_new(&base, device_idx, type, profile_count);
rc = hidpp10_device_new(&base, device_idx, type, profile_count, &dev);
if (!dev) {
if (rc) {
log_error(device->ratbag,
"Failed to get HID++1.0 device for %s\n",
device->name);
@@ -711,14 +753,14 @@ static void
hidpp10drv_remove(struct ratbag_device *device)
{
struct hidpp10drv_data *drv_data;
struct hidpp10_device *dev;
ratbag_close_hidraw(device);
drv_data = ratbag_get_drv_data(device);
dev = drv_data->dev;
if (!drv_data)
return;
hidpp10_device_destroy(dev);
hidpp10_device_destroy(drv_data->dev);
free(drv_data);
}

View File

@@ -56,6 +56,10 @@
#define HIDPP_CAP_ONBOARD_PROFILES_8100 (1 << 6)
#define HIDPP_CAP_LED_SW_CONTROL_1300 (1 << 7)
#define HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060 (1 << 8)
#define HIDPP_CAP_BATTERY_VOLTAGE_1001 (1 << 9)
#define HIDPP_CAP_RGB_EFFECTS_8071 (1 << 10)
#define HIDPP_HIDDEN_FEATURE (1 << 6)
struct hidpp20drv_data {
struct hidpp20_device *dev;
@@ -65,6 +69,7 @@ struct hidpp20drv_data {
unsigned num_controls;
struct hidpp20_control_id *controls;
struct hidpp20_profiles *profiles;
struct hidpp20_led *leds;
union hidpp20_generic_led_zone_info led_infos;
unsigned int report_rates[4];
@@ -330,14 +335,24 @@ static void
hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_data)
{
struct hidpp20_profile *profile;
struct hidpp20_led *h_led;
struct hidpp20_led h_led_val;
struct hidpp20_led *h_led = &h_led_val;
struct hidpp20_color_led_zone_info* led_info;
struct hidpp20_color_led_info info;
int rc;
led_info = &drv_data->led_infos.color_leds[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
led_info = &drv_data->led_infos.color_leds_8070[led->index];
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
} else {
rc = hidpp20_color_led_effects_get_zone_effect(drv_data->dev, led->index, h_led);
if (rc) {
log_debug(led->profile->device->ratbag,
"Failed to read led settings\n");
}
}
switch (h_led->mode) {
case HIDPP20_LED_ON:
@@ -356,7 +371,7 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
/* pre-filled, only override if unknown */
if (led->type == RATBAG_LED_TYPE_UNKNOWN)
led->type = hidpp20_8070_get_location_mapping(led_info->location);
led->type = hidpp20_led_get_location_mapping(led_info->location);
led->color.red = h_led->color.red;
led->color.green = h_led->color.green;
led->color.blue = h_led->color.blue;
@@ -393,10 +408,85 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
break;
default:
log_bug_libratbag(led->profile->device->ratbag,
"%s: Unknown effect id %d\n",
led->profile->device->name,
ei.effect_id);
log_debug(led->profile->device->ratbag,
"%s: Unsupported effect (%d)\n",
led->profile->device->name,
ei.effect_id);
break;
}
}
}
static void
hidpp20drv_read_led_8071(struct ratbag_led *led, struct hidpp20drv_data* drv_data)
{
struct hidpp20_profile *profile;
struct hidpp20_led *h_led;
struct hidpp20_rgb_device_info device_info;
struct hidpp20_rgb_cluster_info cluster_info;
int rc;
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];
switch (h_led->mode) {
case HIDPP20_LED_ON:
led->mode = RATBAG_LED_ON;
break;
case HIDPP20_LED_CYCLE:
led->mode = RATBAG_LED_CYCLE;
break;
case HIDPP20_LED_BREATHING:
led->mode = RATBAG_LED_BREATHING;
break;
default:
led->mode = RATBAG_LED_OFF;
break;
}
/* pre-filled, only override if unknown */
if (led->type == RATBAG_LED_TYPE_UNKNOWN)
led->type = hidpp20_led_get_location_mapping(cluster_info.location);
led->color.red = h_led->color.red;
led->color.green = h_led->color.green;
led->color.blue = h_led->color.blue;
led->ms = h_led->period;
led->brightness = h_led->brightness * 255 / 100;
if (device_info.ext_caps & HIDPP20_COLOR_LED_INFO_EXT_CAP_MONOCHROME_ONLY)
led->colordepth = RATBAG_LED_COLORDEPTH_MONOCHROME;
else
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
for (int i = 0; i < cluster_info.num_effects; i++) {
struct hidpp20_rgb_effect_info ei;
rc = hidpp20_rgb_effects_get_effect_info(drv_data->dev,
cluster_info.index,
i, &ei);
if (rc < 0)
break;
switch (ei.effect_id) {
case HIDPP20_COLOR_LED_ZONE_EFFECT_DISABLED:
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_FIXED:
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_CYCLING:
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_WAVE:
case HIDPP20_COLOR_LED_ZONE_EFFECT_BREATHING:
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
break;
default:
log_debug(led->profile->device->ratbag,
"%s: Unsupported effect (%d)\n",
led->profile->device->name,
ei.effect_id);
break;
}
}
@@ -410,6 +500,8 @@ hidpp20drv_read_led(struct ratbag_led *led)
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
hidpp20drv_read_led_8070(led, drv_data);
else if (drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071)
hidpp20drv_read_led_8071(led, drv_data);
else if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
hidpp20drv_read_led_1300(led, drv_data);
}
@@ -483,7 +575,7 @@ hidpp20drv_update_button_8100(struct ratbag_button *button)
code = ratbag_hidraw_get_keyboard_usage_from_keycode(device, key);
if (code == 0) {
subtype = HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL;
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, action->action.key.key);
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, key);
if (code == 0)
return -EINVAL;
}
@@ -606,15 +698,17 @@ hidpp20drv_update_led_1300(struct ratbag_led *led, struct hidpp20drv_data *data)
}
static int
hidpp20drv_update_led_8070(struct ratbag_led *led, struct ratbag_profile* profile,
struct hidpp20drv_data *drv_data)
hidpp20drv_update_led_8070_8071(struct ratbag_led *led, struct ratbag_profile* profile,
struct hidpp20drv_data *drv_data)
{
struct hidpp20_profile *h_profile;
struct hidpp20_led *h_led;
struct hidpp20_led h_led_val;
struct hidpp20_led *h_led = &h_led_val;
h_profile = &drv_data->profiles->profiles[profile->index];
h_led = &(h_profile->leds[led->index]);
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 (!h_led)
return -EINVAL;
@@ -639,6 +733,13 @@ hidpp20drv_update_led_8070(struct ratbag_led *led, struct ratbag_profile* profil
h_led->period = led->ms;
h_led->brightness = led->brightness * 100 / 255;
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)) {
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
hidpp20_color_led_effects_set_zone_effect(drv_data->dev,
led->index,
h_led_val);
}
return RATBAG_SUCCESS;
}
@@ -649,8 +750,9 @@ hidpp20drv_update_led(struct ratbag_led *led)
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
return hidpp20drv_update_led_8070(led, profile, drv_data);
if ((drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070) |
(drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071))
return hidpp20drv_update_led_8070_8071(led, profile, drv_data);
if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
return hidpp20drv_update_led_1300(led, drv_data);
@@ -740,9 +842,12 @@ hidpp20drv_read_report_rate_8060(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag *ratbag = device->ratbag;
struct ratbag_profile *profile;
uint8_t bitflags_ms;
int nrates = 0;
int rc;
uint8_t rate_ms;
unsigned rate_hz;
rc = hidpp20_adjustable_report_rate_get_report_rate_list(drv_data->dev,
&bitflags_ms);
@@ -761,6 +866,37 @@ hidpp20drv_read_report_rate_8060(struct ratbag_device *device)
drv_data->num_report_rates = nrates;
if (!hidpp20_adjustable_report_rate_get_report_rate(drv_data->dev, &rate_ms)) {
switch (rate_ms)
{
case 1:
rate_hz = 1000;
break;
case 2:
case 3: /* 3ms = 333.(3)Hz, we round to 500Hz */
rate_hz = 500;
break;
case 4:
case 5: /* 5ms = 200Hz, we round to 250Hz */
rate_hz = 250;
break;
case 6: /* 6ms = 166.(6)Hz, we round to 125Hz */
case 7: /* 7ms = 142Hz, we round to 125Hz */
case 8:
rate_hz = 125;
break;
default:
rate_hz = 0;
break;
}
if (rate_hz) {
log_debug(ratbag, "report rate is %u\n", rate_hz);
ratbag_device_for_each_profile(device, profile)
profile->hz = rate_hz;
}
}
log_debug(ratbag, "device has %d report rates\n", nrates);
return 0;
@@ -777,7 +913,8 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
unsigned int default_rate = 500;
int rc;
if (drv_data->capabilities & HIDPP_CAP_RESOLUTION_2200) {
if ((drv_data->capabilities & HIDPP_CAP_RESOLUTION_2200) &&
(drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201) == 0) {
uint16_t resolution;
uint8_t flags;
@@ -846,6 +983,9 @@ hidpp20drv_update_resolution_dpi_8100(struct ratbag_resolution *resolution,
h_profile = &drv_data->profiles->profiles[profile->index];
h_profile->dpi[resolution->index] = dpi;
if (resolution->is_default)
h_profile->default_dpi = resolution->index;
return RATBAG_SUCCESS;
}
@@ -857,7 +997,7 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_sensor *sensor;
int rc, i;
int i;
int dpi = dpi_x; /* dpi_x == dpi_y if we don't have the individual resolution cap */
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
@@ -873,14 +1013,13 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
sensor = &drv_data->sensors[0];
/* validate that the sensor accepts the given DPI */
rc = -EINVAL;
if (dpi < sensor->dpi_min || dpi > sensor->dpi_max)
goto out;
return -EINVAL;
if (sensor->dpi_steps) {
for (i = sensor->dpi_min; i < dpi; i += sensor->dpi_steps) {
}
if (i != dpi)
goto out;
return -EINVAL;
} else {
i = 0;
while (sensor->dpi_list[i]) {
@@ -888,13 +1027,60 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
break;
}
if (sensor->dpi_list[i] != dpi)
goto out;
return -EINVAL;
}
rc = hidpp20_adjustable_dpi_set_sensor_dpi(drv_data->dev, sensor, dpi);
return hidpp20_adjustable_dpi_set_sensor_dpi(drv_data->dev, sensor, dpi);
}
out:
return rc;
static int
hidpp20drv_update_report_rate_8060(struct ratbag_profile *profile, int hz)
{
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
int rc;
rc = hidpp20_adjustable_report_rate_set_report_rate(drv_data->dev, 1000/hz);
if (rc)
return rc;
return RATBAG_SUCCESS;
}
static int
hidpp20drv_update_report_rate_8100(struct ratbag_profile *profile, int hz)
{
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_profile *h_profile;
h_profile = &drv_data->profiles->profiles[profile->index];
h_profile->report_rate = hz;
return RATBAG_SUCCESS;
}
static int
hidpp20drv_update_report_rate(struct ratbag_profile *profile, int hz)
{
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
int rc;
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
return hidpp20drv_update_report_rate_8100(profile, hz);
if (drv_data->capabilities & HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060) {
rc = hidpp20drv_update_report_rate_8060(profile, hz);
/* re-populate the profile with the correct value if we fail */
if (rc)
hidpp20drv_read_report_rate_8060(profile->device);
return rc;
}
return -ENOTSUP;
}
static int
@@ -940,19 +1126,15 @@ hidpp20drv_read_kbd_reprogrammable_key(struct ratbag_device *device)
}
static int
hidpp20drv_read_color_leds(struct ratbag_device *device)
hidpp20drv_read_color_leds_8070(struct hidpp20drv_data *drv_data)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
int rc;
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return 0;
free(drv_data->led_infos.color_leds);
drv_data->led_infos.color_leds = NULL;
free(drv_data->led_infos.color_leds_8070);
drv_data->led_infos.color_leds_8070 = NULL;
drv_data->num_leds = 0;
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos.color_leds);
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos.color_leds_8070);
if (rc > 0) {
drv_data->num_leds = rc;
@@ -962,6 +1144,38 @@ hidpp20drv_read_color_leds(struct ratbag_device *device)
return rc;
}
static int
hidpp20drv_read_color_leds_8071(struct hidpp20drv_data *drv_data)
{
int rc;
free(drv_data->led_infos.color_leds_8071);
drv_data->led_infos.color_leds_8071 = NULL;
drv_data->num_leds = 0;
rc = hidpp20_rgb_effects_get_cluster_infos(drv_data->dev, &drv_data->led_infos.color_leds_8071);
if (rc > 0) {
drv_data->num_leds = rc;
rc = 0;
}
return rc;
}
static int
hidpp20drv_read_color_leds(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
return hidpp20drv_read_color_leds_8070(drv_data);
else if (drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071)
return hidpp20drv_read_color_leds_8071(drv_data);
return 0;
}
static int
hidpp20drv_read_leds(struct ratbag_device *device)
{
@@ -1037,6 +1251,60 @@ hidpp20drv_read_profile_8100(struct ratbag_profile *profile)
ratbag_profile_set_report_rate(profile, p->report_rate);
}
static int
hidpp20drv_init_leds_8070_8071(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag *ratbag = device->ratbag;
/* we only support 0x8071 via 0x8100 */
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) &&
drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071) {
log_debug(ratbag, "disabling 0x8071 (RGB Effects) feature because the device doesn't have 0x8100 (Onboard Memory Profiles)\n");
drv_data->capabilities &= ~HIDPP_CAP_RGB_EFFECTS_8071;
}
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070 ||
drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071) {
/* we read the profile once to get the correct number of
* supported leds. */
if (hidpp20drv_read_color_leds(device))
return 0;
device->num_leds = drv_data->num_leds;
}
return 0;
}
static int
hidpp20drv_init_profile_8100(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag *ratbag = device->ratbag;
int rc;
log_debug(ratbag, "initializing onboard profiles\n");
rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles);
if (rc < 0)
return rc;
rc = hidpp20_onboard_profiles_initialize(drv_data->dev, drv_data->profiles);
if (rc < 0)
return rc;
drv_data->num_profiles = drv_data->profiles->num_profiles;
drv_data->num_buttons = drv_data->profiles->num_buttons;
if (drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201)
drv_data->num_resolutions = drv_data->profiles->num_modes;
/* We ignore the profile's num_leds and require
* HIDPP_PAGE_COLOR_LED_EFFECTS to succeed instead
*/
return 0;
}
static void
hidpp20drv_read_profile(struct ratbag_profile *profile)
{
@@ -1078,6 +1346,10 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
if (rc < 0)
return rc;
/* check if it is a hidden feature */
if (feature_type & HIDPP_HIDDEN_FEATURE)
return 0;
switch (feature) {
case HIDPP_PAGE_ROOT:
case HIDPP_PAGE_FEATURE_SET:
@@ -1121,6 +1393,22 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
drv_data->capabilities |= HIDPP_CAP_BATTERY_LEVEL_1000;
break;
}
case HIDPP_PAGE_BATTERY_VOLTAGE: {
uint16_t voltage;
enum hidpp20_battery_voltage_status status;
rc = hidpp20_batteryvoltage_get_battery_voltage(drv_data->dev, &voltage);
if (rc < 0)
return rc;
status = rc;
log_debug(ratbag, "device battery voltage is %dmv, status %02x \n",
voltage, status);
drv_data->capabilities |= HIDPP_CAP_BATTERY_VOLTAGE_1001;
break;
}
case HIDPP_PAGE_KBD_REPROGRAMMABLE_KEYS: {
log_debug(ratbag, "device has programmable keys/buttons\n");
drv_data->capabilities |= HIDPP_CAP_KBD_REPROGRAMMABLE_KEYS_1b00;
@@ -1144,15 +1432,18 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
break;
}
case HIDPP_PAGE_COLOR_LED_EFFECTS: {
/* The 8070 feauture 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)
break;
log_debug(ratbag, "device has color effects\n");
drv_data->capabilities |= HIDPP_CAP_COLOR_LED_EFFECTS_8070;
/* we read the profile once to get the correct number of
* supported leds. */
if (hidpp20drv_read_color_leds(device))
return 0;
device->num_leds = drv_data->num_leds;
break;
}
case HIDPP_PAGE_RGB_EFFECTS: {
log_debug(ratbag, "device has color effects\n");
drv_data->capabilities |= HIDPP_CAP_RGB_EFFECTS_8071;
break;
}
case HIDPP_PAGE_LED_SW_CONTROL: {
@@ -1170,22 +1461,6 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
case HIDPP_PAGE_ONBOARD_PROFILES: {
log_debug(ratbag, "device has onboard profiles\n");
drv_data->capabilities |= HIDPP_CAP_ONBOARD_PROFILES_8100;
rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles);
if (rc < 0)
return rc;
rc = hidpp20_onboard_profiles_initialize(drv_data->dev, drv_data->profiles);
if (rc < 0)
return rc;
drv_data->num_profiles = drv_data->profiles->num_profiles;
drv_data->num_resolutions = drv_data->profiles->num_modes;
drv_data->num_buttons = drv_data->profiles->num_buttons;
/* We ignore the profile's num_leds and require
* HIDPP_PAGE_COLOR_LED_EFFECTS to succeed instead
*/
break;
}
case HIDPP_PAGE_MOUSE_BUTTON_SPY: {
@@ -1207,21 +1482,27 @@ hidpp20drv_commit(struct ratbag_device *device)
struct ratbag_led *led;
struct ratbag_resolution *resolution;
int rc;
unsigned int dpi_index = 0;
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
continue;
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->is_active)
dpi_index = resolution->index;
if (profile->rate_dirty) {
rc = hidpp20drv_update_report_rate(profile, profile->hz);
if (rc) {
log_error(device->ratbag, "hidpp20: failed to update report rate (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
ratbag_profile_for_each_resolution(profile, resolution) {
rc = hidpp20drv_update_resolution_dpi(resolution,
resolution->dpi_x,
resolution->dpi_y);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp20: failed to update resolution (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
list_for_each(button, &profile->buttons, link) {
@@ -1229,8 +1510,10 @@ hidpp20drv_commit(struct ratbag_device *device)
continue;
rc = hidpp20drv_update_button(button);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp20: failed to update button (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
list_for_each(led, &profile->leds, link) {
@@ -1238,10 +1521,11 @@ hidpp20drv_commit(struct ratbag_device *device)
continue;
rc = hidpp20drv_update_led(led);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp20: failed to update led (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
}
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
@@ -1250,13 +1534,19 @@ hidpp20drv_commit(struct ratbag_device *device)
rc = hidpp20_onboard_profiles_commit(drv_data->dev,
drv_data->profiles);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp20: failed to commit profile (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
list_for_each(profile, &device->profiles, link) {
if (profile->is_active)
hidpp20_onboard_profiles_set_current_dpi_index(drv_data->dev,
dpi_index);
if (profile->is_active) {
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->is_active)
hidpp20_onboard_profiles_set_current_dpi_index(drv_data->dev,
resolution->index);
}
}
}
}
@@ -1286,8 +1576,21 @@ hidpp20drv_20_probe(struct ratbag_device *device)
return rc;
}
return 0;
/* initializations that depend on other features */
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070 ||
drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071) {
rc = hidpp20drv_init_leds_8070_8071(device);
if (rc < 0)
return rc;
}
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
rc = hidpp20drv_init_profile_8100(device);
if (rc < 0)
return rc;
}
return 0;
}
static int
@@ -1318,9 +1621,10 @@ hidpp20drv_remove(struct ratbag_device *device)
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(drv_data->profiles);
free(drv_data->led_infos.color_leds);
free(drv_data->led_infos.color_leds_8070);
free(drv_data->controls);
free(drv_data->sensors);
free(drv_data->leds);
if (drv_data->dev)
hidpp20_device_destroy(drv_data->dev);
free(drv_data);
@@ -1331,6 +1635,12 @@ hidpp20drv_init_device(struct ratbag_device *device,
struct hidpp20drv_data *drv_data)
{
struct ratbag_profile *profile;
bool active_profile = false;
int num;
num = ratbag_device_data_hidpp20_get_led_count(device->data);
if (num >= 0)
drv_data->num_leds = num;
ratbag_device_init_profiles(device,
drv_data->num_profiles,
@@ -1340,6 +1650,18 @@ hidpp20drv_init_device(struct ratbag_device *device,
ratbag_device_for_each_profile(device, profile)
hidpp20drv_read_profile(profile);
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
/* Fallback to the first profile if no profile is active */
list_for_each(profile, &device->profiles, link)
if (profile->is_active)
active_profile = true;
if (!active_profile && device->num_profiles >= 1) {
profile = ratbag_device_get_profile(device, 0);
profile->is_active = true;
}
}
}
static int
@@ -1372,19 +1694,24 @@ hidpp20drv_probe(struct ratbag_device *device)
* 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);
dev = hidpp20_device_new(&base, device_idx, (struct hidpp_hid_report*) device->hidraw[0].reports, device->hidraw[0].num_reports);
if (!dev) {
rc = -ENODEV;
goto err;
}
dev->quirk = ratbag_device_data_hidpp20_get_quirk(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));
/* add some defaults that will be overwritten by the device */
drv_data->num_profiles = 1;
drv_data->num_resolutions = 1;
drv_data->num_resolutions = 0;
drv_data->num_buttons = 8;
drv_data->num_leds = 0;

View File

@@ -37,7 +37,7 @@
#define LOGITECH_G300_BUTTON_MAX 8
#define LOGITECH_G300_NUM_DPI 4
#define LOGITECH_G300_NUM_LED 1
#define LOGITECH_G300_DPI_MIN 250
#define LOGITECH_G300_DPI_MIN 100
#define LOGITECH_G300_DPI_MAX 2500
#define LOGITECH_G300_REPORT_ID_GET_ACTIVE 0xF0
@@ -51,7 +51,7 @@
#define LOGITECH_G300_REPORT_SIZE_PROFILE 35
struct logitech_g300_resolution {
uint8_t dpi :7; /* Range 1-10. dpi = 250*value */
uint8_t dpi :7; /* Range 1-10. dpi = 100*value */
uint8_t is_default :1;
} __attribute__((packed));
@@ -449,11 +449,10 @@ logitech_g300_read_profile(struct ratbag_profile *profile)
ratbag_profile_for_each_resolution(profile, resolution) {
struct logitech_g300_resolution *res =
&report->dpi_levels[resolution->index];
unsigned int dpis[10] = { 250, 500, 750, 1000, 1250, 1500, 1750,
2000, 2250, 2500 };
unsigned int dpis[49] = { 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950, 2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350, 2400, 2450, 2500, };
resolution->dpi_x = res->dpi * 250;
resolution->dpi_y = res->dpi * 250;
resolution->dpi_x = res->dpi * 50;
resolution->dpi_y = res->dpi * 50;
resolution->is_default = res->is_default;
resolution->is_active = res->is_default;
@@ -546,7 +545,7 @@ logitech_g300_write_profile(struct ratbag_profile *profile)
struct logitech_g300_resolution *res =
&report->dpi_levels[resolution->index];
res->dpi = resolution->dpi_x / 250;
res->dpi = resolution->dpi_x / 50;
res->is_default = resolution->is_default;
if (profile->is_active && resolution->is_active)

View File

@@ -0,0 +1,845 @@
/*
* Copyright © 2015 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 <assert.h>
#include <errno.h>
#include <libevdev/libevdev.h>
#include <linux/input.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
#define ROCCAT_PROFILE_MAX 4
#define ROCCAT_BUTTON_MAX 17
#define ROCCAT_NUM_DPI 5
#define ROCCAT_LED_MAX 0
#define ROCCAT_MAX_RETRY_READY 10
#define ROCCAT_REPORT_ID_CONFIGURE_PROFILE 4
#define ROCCAT_REPORT_ID_PROFILE 5
#define ROCCAT_REPORT_ID_SETTINGS 6
#define ROCCAT_REPORT_ID_KEY_MAPPING 7
#define ROCCAT_REPORT_ID_MACRO 8
#define ROCCAT_REPORT_SIZE_PROFILE 59
#define ROCCAT_REPORT_SIZE_SETTINGS 31
#define ROCCAT_REPORT_SIZE_MACRO 2082
#define ROCCAT_CONFIG_SETTINGS 0x80
#define ROCCAT_CONFIG_KEY_MAPPING 0x90
#define ROCCAT_MAX_MACRO_LENGTH 500
struct roccat_settings_report {
uint8_t reportID;
uint8_t twoB;
uint8_t profileID;
uint8_t x_y_linked;
uint8_t x_sensitivity; /* 0x06 means 0 */
uint8_t y_sensitivity; /* 0x06 means 0 */
uint8_t dpi_mask;
uint8_t xres[5];
uint8_t current_dpi;
uint8_t yres[5];
uint8_t padding1;
uint8_t report_rate;
uint8_t padding2[11];
// uint16_t checksum;
// uint8_t padding2[4];
// uint8_t light;
// uint8_t light_heartbit;
// uint8_t padding3[5];
} __attribute__((packed));
struct roccat_macro {
uint8_t reportID;
uint8_t twentytwo;
uint8_t height;
uint8_t profile;
uint8_t button_index;
uint8_t active;
uint8_t padding[24];
char group[24];
char name[24];
uint16_t length;
struct {
uint8_t keycode;
uint8_t flag;
uint16_t time;
} keys[ROCCAT_MAX_MACRO_LENGTH];
uint16_t checksum;
} __attribute__((packed));
struct roccat_data {
uint8_t profiles[(ROCCAT_PROFILE_MAX + 1)][ROCCAT_REPORT_SIZE_PROFILE];
struct roccat_settings_report settings[(ROCCAT_PROFILE_MAX + 1)];
struct roccat_macro macros[(ROCCAT_PROFILE_MAX + 1)][(ROCCAT_BUTTON_MAX + 1)];
};
struct roccat_button_type_mapping {
uint8_t raw;
enum ratbag_button_type type;
};
static const struct roccat_button_type_mapping roccat_button_type_mapping[] = {
{ 0, RATBAG_BUTTON_TYPE_LEFT },
{ 1, RATBAG_BUTTON_TYPE_RIGHT },
{ 2, RATBAG_BUTTON_TYPE_MIDDLE },
{ 3, RATBAG_BUTTON_TYPE_EXTRA },
{ 4, RATBAG_BUTTON_TYPE_SIDE },
{ 5, RATBAG_BUTTON_TYPE_WHEEL_UP },
{ 6, RATBAG_BUTTON_TYPE_WHEEL_DOWN },
{ 7, RATBAG_BUTTON_TYPE_RESOLUTION_UP },
{ 8, RATBAG_BUTTON_TYPE_RESOLUTION_DOWN },
{ 9, RATBAG_BUTTON_TYPE_LEFT },
{ 10, RATBAG_BUTTON_TYPE_RIGHT },
{ 11, RATBAG_BUTTON_TYPE_MIDDLE },
{ 12, RATBAG_BUTTON_TYPE_EXTRA },
{ 13, RATBAG_BUTTON_TYPE_SIDE },
{ 14, RATBAG_BUTTON_TYPE_WHEEL_UP },
{ 15, RATBAG_BUTTON_TYPE_WHEEL_DOWN },
{ 16, RATBAG_BUTTON_TYPE_RESOLUTION_UP },
{ 17, RATBAG_BUTTON_TYPE_RESOLUTION_DOWN },
};
static enum ratbag_button_type
roccat_raw_to_button_type(uint8_t data)
{
const struct roccat_button_type_mapping *mapping;
ARRAY_FOR_EACH(roccat_button_type_mapping, mapping) {
if (mapping->raw == data)
return mapping->type;
}
return RATBAG_BUTTON_TYPE_UNKNOWN;
}
struct roccat_button_mapping {
uint8_t raw;
struct ratbag_button_action action;
};
static struct roccat_button_mapping roccat_button_mapping[] = {
/* FIXME: { 0, Disabled }, */
{ 1, BUTTON_ACTION_BUTTON(1) },
{ 2, BUTTON_ACTION_BUTTON(2) },
{ 3, BUTTON_ACTION_BUTTON(3) },
{ 4, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK) },
/* FIXME: { 5, Shortcut (modifier + key) }, */
{ 6, BUTTON_ACTION_NONE },
{ 7, BUTTON_ACTION_BUTTON(4) },
{ 8, BUTTON_ACTION_BUTTON(5) },
//{ 9, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT) },
//{ 10, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT) },
{ 13, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP) },
{ 14, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN) },
/* FIXME: { 15, quicklaunch }, -> hidraw report 03 00 60 07 01 00 00 00 */
{ 16, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP) },
{ 17, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP) },
{ 18, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN) },
{ 20, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP) },
{ 21, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP) },
{ 22, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN) },
{ 26, BUTTON_ACTION_KEY(KEY_LEFTMETA) },
/* FIXME: { 27, open driver }, -> hidraw report 02 83 01 00 00 00 00 00 */
//{ 32, BUTTON_ACTION_KEY(KEY_CONFIG) },
{ 33, BUTTON_ACTION_KEY(KEY_PREVIOUSSONG) },
{ 34, BUTTON_ACTION_KEY(KEY_NEXTSONG) },
{ 35, BUTTON_ACTION_KEY(KEY_PLAYPAUSE) },
{ 36, BUTTON_ACTION_KEY(KEY_STOPCD) },
{ 37, BUTTON_ACTION_KEY(KEY_MUTE) },
{ 38, BUTTON_ACTION_KEY(KEY_VOLUMEUP) },
{ 39, BUTTON_ACTION_KEY(KEY_VOLUMEDOWN) },
{ 48, BUTTON_ACTION_MACRO },
{ 65, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE) },
/* FIXME: { 66, Easywheel sensitivity }, */
/* FIXME: { 67, Easywheel profile }, */
/* FIXME: { 68, Easywheel CPI }, */
/* FIXME: { 81, Other Easyshift }, -> hidraw report 03 00 ff 05 01 00 00 00 */
/* FIXME: { 82, Other Easyshift Lock }, -> hidraw report 03 00 ff 05 01 00 00 00 */
/* FIXME: { 83, Both Easyshift }, -> hidraw report 03 00 ff 04 01 00 00 00 */
};
static const struct ratbag_button_action*
roccat_raw_to_button_action(uint8_t data)
{
struct roccat_button_mapping *mapping;
ARRAY_FOR_EACH(roccat_button_mapping, mapping) {
if (mapping->raw == data)
return &mapping->action;
}
return NULL;
}
static uint8_t
roccat_button_action_to_raw(const struct ratbag_button_action *action)
{
struct roccat_button_mapping *mapping;
ARRAY_FOR_EACH(roccat_button_mapping, mapping) {
if (ratbag_button_action_match(&mapping->action, action))
return mapping->raw;
}
return 0;
}
static inline uint16_t
roccat_get_unaligned_u16(uint8_t *buf)
{
return (buf[1] << 8) | buf[0];
}
static inline uint16_t
roccat_compute_crc(uint8_t *buf, unsigned int len)
{
unsigned i;
uint16_t crc = 0;
if (len < 3)
return 0;
for (i = 0; i < len - 2; i++) {
crc += buf[i];
}
return crc;
}
static inline int
roccat_crc_is_valid(struct ratbag_device *device, uint8_t *buf, unsigned int len)
{
uint16_t crc;
uint16_t given_crc;
if (len < 3)
return 0;
crc = roccat_compute_crc(buf, len);
given_crc = roccat_get_unaligned_u16(&buf[len - 2]);
log_raw(device->ratbag,
"checksum computed: 0x%04x, checksum given: 0x%04x\n",
crc,
given_crc);
return crc == given_crc;
}
static int
roccat_is_ready(struct ratbag_device *device)
{
uint8_t buf[3] = { 0 };
int rc;
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_CONFIGURE_PROFILE,
buf, sizeof(buf));
if (rc < 0)
return rc;
if (rc != sizeof(buf))
return -EIO;
if (buf[1] == 0x03)
msleep(100);
if (buf[1] == 0x02)
return 2;
return buf[1] == 0x01;
}
static int
roccat_wait_ready(struct ratbag_device *device)
{
unsigned count = 0;
int rc;
msleep(10);
while (count < ROCCAT_MAX_RETRY_READY) {
rc = roccat_is_ready(device);
if (rc < 0)
return rc;
if (rc == 1)
return 0;
if (rc == 2)
return 2;
msleep(10);
count++;
}
return -ETIMEDOUT;
}
static int
roccat_current_profile(struct ratbag_device *device)
{
uint8_t buf[3];
int ret;
ret = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_PROFILE, buf,
sizeof(buf));
if (ret < 0)
return ret;
if (ret != 3)
return -EIO;
return buf[2];
}
static int
roccat_set_current_profile(struct ratbag_device *device, unsigned int index)
{
uint8_t buf[] = {ROCCAT_REPORT_ID_PROFILE, 0x03, index};
int ret;
if (index > ROCCAT_PROFILE_MAX)
return -EINVAL;
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
sizeof(buf));
if (ret < 0)
return ret;
if (ret != sizeof(buf))
return -EIO;
ret = roccat_wait_ready(device);
if (ret)
log_error(device->ratbag,
"Error while waiting for the device to be ready: %s (%d)\n",
strerror(-ret), ret);
return ret;
}
static int
roccat_set_config_profile(struct ratbag_device *device, uint8_t profile, uint8_t type)
{
uint8_t buf[] = {ROCCAT_REPORT_ID_CONFIGURE_PROFILE, profile, type};
int ret;
if (profile > ROCCAT_PROFILE_MAX)
return -EINVAL;
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
sizeof(buf));
if (ret < 0)
return ret;
if (ret != sizeof(buf))
return -EIO;
ret = roccat_wait_ready(device);
if (ret < 0)
log_error(device->ratbag,
"Error while waiting for the device to be ready: %s (%d)\n",
strerror(-ret), ret);
return ret;
}
static const struct ratbag_button_action *
roccat_button_to_action(struct ratbag_profile *profile,
unsigned int button_index)
{
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data = ratbag_get_drv_data(device);
uint8_t data;
data = drv_data->profiles[profile->index][3 + button_index * 3];
return roccat_raw_to_button_action(data);
}
static int
roccat_write_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
unsigned int index = profile->index;
struct roccat_data *drv_data;
int rc;
uint8_t *buf;
uint16_t *crc;
assert(index <= ROCCAT_PROFILE_MAX);
drv_data = ratbag_get_drv_data(device);
buf = drv_data->profiles[index];
crc = (uint16_t *)&buf[ROCCAT_REPORT_SIZE_PROFILE - 2];
*crc = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_PROFILE);
roccat_set_config_profile(device, index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE);
if (rc < ROCCAT_REPORT_SIZE_PROFILE)
return -EIO;
log_raw(device->ratbag, "profile: %d written %s:%d\n",
buf[2],
__FILE__, __LINE__);
rc = roccat_wait_ready(device);
if (rc)
log_error(device->ratbag,
"Error while waiting for the device to be ready: %s (%d)\n",
strerror(-rc), rc);
return rc;
}
static void
roccat_read_button(struct ratbag_button *button)
{
const struct ratbag_button_action *action;
struct ratbag_device *device;
struct roccat_macro *macro;
struct roccat_data *drv_data;
uint8_t *buf;
unsigned j, time;
int rc;
device = button->profile->device;
drv_data = ratbag_get_drv_data(device);
action = roccat_button_to_action(button->profile, button->index);
if (action)
ratbag_button_set_action(button, action);
button->type = roccat_raw_to_button_type(button->index);
// if (action == NULL)
// log_error(device->ratbag, "button: %d -> %d %s:%d\n",
// button->index, drv_data->profiles[button->profile->index][3 + button->index * 3],
// __FILE__, __LINE__);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
if (action && action->type == RATBAG_BUTTON_ACTION_TYPE_MACRO) {
struct ratbag_button_macro *m = NULL;
roccat_set_config_profile(device,
button->profile->index,
0);
roccat_set_config_profile(device,
button->profile->index,
button->index);
macro = &drv_data->macros[button->profile->index][button->index];
buf = (uint8_t*)macro;
buf[0] = ROCCAT_REPORT_ID_MACRO;
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO);
if (rc != ROCCAT_REPORT_SIZE_MACRO) {
log_error(device->ratbag,
"Unable to retrieve the macro for button %d of profile %d: %s (%d)\n",
button->index, button->profile->index,
rc < 0 ? strerror(-rc) : "not read enough", rc);
} else {
if (buf[0] != ROCCAT_REPORT_ID_MACRO) {
log_error(device->ratbag,
"Error while reading the macro of button %d of profile %d.\n",
button->index,
button->profile->index);
goto out_macro;
}
if (!roccat_crc_is_valid(device, buf, ROCCAT_REPORT_SIZE_MACRO)) {
log_error(device->ratbag,
"wrong checksum while reading the macro of button %d of profile %d.\n",
button->index,
button->profile->index);
goto out_macro;
}
m = ratbag_button_macro_new(macro->name);
log_raw(device->ratbag,
"macro on button %d of profile %d is named '%s', and contains %d events:\n",
button->index, button->profile->index,
macro->name, macro->length);
for (j = 0; j < macro->length; j++) {
unsigned int keycode = ratbag_hidraw_get_keycode_from_keyboard_usage(device,
macro->keys[j].keycode);
ratbag_button_macro_set_event(m,
j * 2,
macro->keys[j].flag & 0x01 ? RATBAG_MACRO_EVENT_KEY_PRESSED : RATBAG_MACRO_EVENT_KEY_RELEASED,
keycode);
if (macro->keys[j].time)
time = macro->keys[j].time;
else
time = macro->keys[j].flag & 0x01 ? 10 : 50;
ratbag_button_macro_set_event(m,
j * 2 + 1,
RATBAG_MACRO_EVENT_WAIT,
time);
log_raw(device->ratbag,
" - %s %s\n",
libevdev_event_code_get_name(EV_KEY, keycode),
macro->keys[j].flag & 0x80 ? "released" : "pressed");
}
ratbag_button_copy_macro(button, m);
}
out_macro:
msleep(10);
ratbag_button_macro_unref(m);
}
}
static int
roccat_write_macro(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
struct ratbag_device *device;
struct roccat_macro *macro;
struct roccat_data *drv_data;
uint8_t *buf;
unsigned i, count = 0;
int rc;
if (action->type != RATBAG_BUTTON_ACTION_TYPE_MACRO)
return 0;
device = button->profile->device;
drv_data = ratbag_get_drv_data(device);
macro = &drv_data->macros[button->profile->index][button->index];
buf = (uint8_t*)macro;
memset(buf, 0, ROCCAT_REPORT_SIZE_MACRO);
for (i = 0; i < MAX_MACRO_EVENTS && count < ROCCAT_MAX_MACRO_LENGTH; i++) {
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_INVALID)
return -EINVAL; /* should not happen, ever */
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_NONE)
break;
/* ignore the first wait */
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_WAIT &&
!count)
continue;
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_KEY_PRESSED ||
action->macro->events[i].type == RATBAG_MACRO_EVENT_KEY_RELEASED) {
macro->keys[count].keycode = ratbag_hidraw_get_keyboard_usage_from_keycode(device, action->macro->events[i].event.key);
}
switch (action->macro->events[i].type) {
case RATBAG_MACRO_EVENT_KEY_PRESSED:
macro->keys[count].flag = 0x01;
break;
case RATBAG_MACRO_EVENT_KEY_RELEASED:
macro->keys[count].flag = 0x02;
break;
case RATBAG_MACRO_EVENT_WAIT:
macro->keys[--count].time = action->macro->events[i].event.timeout;
break;
case RATBAG_MACRO_EVENT_INVALID:
case RATBAG_MACRO_EVENT_NONE:
/* should not happen */
log_error(device->ratbag,
"something went wrong while writing a macro.\n");
}
count++;
}
macro->reportID = ROCCAT_REPORT_ID_MACRO;
macro->twentytwo = 0x22;
macro->height = 0x08;
macro->profile = button->profile->index;
macro->button_index = button->index;
macro->active = 0x01;
strcpy(macro->group, "g0");
strncpy(macro->name, action->macro->name, 23);
macro->length = count;
macro->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_MACRO);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO);
if (rc < 0)
return rc;
if (rc != ROCCAT_REPORT_SIZE_MACRO)
return -EIO;
rc = roccat_wait_ready(device);
if (rc)
log_error(device->ratbag,
"Error while waiting for the device to be ready: %s (%d)\n",
strerror(-rc), rc);
return rc;
}
static int
roccat_write_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
struct ratbag_profile *profile = button->profile;
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data = ratbag_get_drv_data(device);
uint8_t rc, *data;
data = &drv_data->profiles[profile->index][3 + button->index * 3];
rc = roccat_button_action_to_raw(action);
if (!rc)
return -EINVAL;
*data = rc;
rc = roccat_write_profile(button->profile);
if (rc) {
log_error(device->ratbag,
"unable to write the profile to the device: '%s' (%d)\n",
strerror(-rc), rc);
return rc;
}
rc = roccat_write_macro(button, action);
if (rc) {
log_error(device->ratbag,
"unable to write the macro to the device: '%s' (%d)\n",
strerror(-rc), rc);
return rc;
}
return rc;
}
static int
roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data = ratbag_get_drv_data(device);
struct roccat_settings_report *settings_report;
uint8_t *buf;
int rc;
if (dpi_x < 100 || dpi_x > 5000 || dpi_x % 50)
return -EINVAL;
if (dpi_y < 100 || dpi_y > 5000 || dpi_y % 50)
return -EINVAL;
settings_report = &drv_data->settings[profile->index];
settings_report->xres[resolution->index] = dpi_x / 50;
settings_report->yres[resolution->index] = dpi_y / 50;
if (resolution->is_active)
settings_report->current_dpi = resolution->index;
buf = (uint8_t*)settings_report;
// No checksum for settings report on Kone Pure.
//settings_report->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_SETTINGS);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS);
if (rc < 0)
return rc;
if (rc != ROCCAT_REPORT_SIZE_SETTINGS)
return -EIO;
rc = roccat_wait_ready(device);
if (rc)
log_error(device->ratbag,
"Error while waiting for the device to be ready: %s (%d)\n",
strerror(-rc), rc);
return rc;
}
static void
roccat_read_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data;
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct roccat_settings_report *setting_report;
uint8_t *buf;
unsigned int report_rate;
int dpi_x, dpi_y;
int rc;
unsigned int report_rates[] = { 125, 250, 500, 1000 };
assert(profile->index <= ROCCAT_PROFILE_MAX);
drv_data = ratbag_get_drv_data(device);
setting_report = &drv_data->settings[profile->index];
buf = (uint8_t*)setting_report;
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_SETTINGS);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS);
if (rc < ROCCAT_REPORT_SIZE_SETTINGS)
return;
/* first retrieve the report rate, it is set per profile */
if (setting_report->report_rate < ARRAY_LENGTH(report_rates)) {
report_rate = report_rates[setting_report->report_rate];
} else {
log_error(device->ratbag,
"error while reading the report rate of the mouse (0x%02x)\n",
buf[26]);
report_rate = 0;
}
ratbag_profile_set_report_rate_list(profile, report_rates,
ARRAY_LENGTH(report_rates));
ratbag_profile_set_report_rate(profile, report_rate);
ratbag_profile_for_each_resolution(profile, resolution) {
dpi_x = setting_report->xres[resolution->index] * 50;
dpi_y = setting_report->yres[resolution->index] * 50;
if (!(setting_report->dpi_mask & (1 << resolution->index))) {
/* the profile is disabled, overwrite it */
dpi_x = 0;
dpi_y = 0;
}
ratbag_resolution_set_resolution(resolution, dpi_x, dpi_y);
ratbag_resolution_set_cap(resolution,
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
resolution->is_active = (resolution->index == setting_report->current_dpi);
ratbag_resolution_set_dpi_list_from_range(resolution, 100, 5000);
}
buf = drv_data->profiles[profile->index];
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE);
msleep(10);
if (rc < ROCCAT_REPORT_SIZE_PROFILE)
return;
// Buttons were read from the buffer that was yet un-initialized with the device data.
ratbag_profile_for_each_button(profile, button)
roccat_read_button(button);
if (!roccat_crc_is_valid(device, buf, ROCCAT_REPORT_SIZE_PROFILE))
log_error(device->ratbag,
"Error while reading profile %d, continuing...\n",
profile->index);
log_raw(device->ratbag, "profile: %d %s:%d\n",
buf[2],
__FILE__, __LINE__);
}
static int
roccat_probe(struct ratbag_device *device)
{
int rc;
struct ratbag_profile *profile;
struct roccat_data *drv_data;
int active_idx;
rc = ratbag_open_hidraw(device);
if (rc)
return rc;
if (!ratbag_hidraw_has_report(device, ROCCAT_REPORT_ID_KEY_MAPPING)) {
ratbag_close_hidraw(device);
return -ENODEV;
}
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
/* profiles are 0-indexed */
ratbag_device_init_profiles(device,
ROCCAT_PROFILE_MAX + 1,
ROCCAT_NUM_DPI,
ROCCAT_BUTTON_MAX + 1,
ROCCAT_LED_MAX);
ratbag_device_for_each_profile(device, profile)
roccat_read_profile(profile);
active_idx = roccat_current_profile(device);
if (active_idx < 0) {
log_error(device->ratbag,
"Can't talk to the mouse: '%s' (%d)\n",
strerror(-active_idx),
active_idx);
rc = -ENODEV;
goto err;
}
list_for_each(profile, &device->profiles, link) {
if (profile->index == (unsigned int)active_idx) {
profile->is_active = true;
break;
}
}
log_raw(device->ratbag,
"'%s' is in profile %d\n",
ratbag_device_get_name(device),
profile->index);
return 0;
err:
free(drv_data);
ratbag_set_drv_data(device, NULL);
return rc;
}
static void
roccat_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
free(ratbag_get_drv_data(device));
}
struct ratbag_driver roccat_driver = {
.name = "Roccat Kone Pure",
.id = "roccat-kone-pure",
.probe = roccat_probe,
.remove = roccat_remove,
.write_profile = roccat_write_profile,
.set_active_profile = roccat_set_current_profile,
.write_button = roccat_write_button,
.write_resolution_dpi = roccat_write_resolution_dpi,
};

View File

@@ -267,9 +267,8 @@ roccat_is_ready(struct ratbag_device *device)
uint8_t buf[3] = { 0 };
int rc;
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_CONFIGURE_PROFILE,
buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_CONFIGURE_PROFILE,
buf, sizeof(buf));
if (rc < 0)
return rc;
if (rc != sizeof(buf))
@@ -315,8 +314,8 @@ roccat_current_profile(struct ratbag_device *device)
uint8_t buf[3];
int ret;
ret = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_PROFILE, buf,
sizeof(buf), HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
ret = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_PROFILE, buf,
sizeof(buf));
if (ret < 0)
return ret;
@@ -335,8 +334,8 @@ roccat_set_current_profile(struct ratbag_device *device, unsigned int index)
if (index > ROCCAT_PROFILE_MAX)
return -EINVAL;
ret = ratbag_hidraw_raw_request(device, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
sizeof(buf));
if (ret < 0)
return ret;
@@ -362,8 +361,8 @@ roccat_set_config_profile(struct ratbag_device *device, uint8_t profile, uint8_t
if (profile > ROCCAT_PROFILE_MAX)
return -EINVAL;
ret = ratbag_hidraw_raw_request(device, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
sizeof(buf));
if (ret < 0)
return ret;
@@ -409,9 +408,8 @@ roccat_write_profile(struct ratbag_profile *profile)
*crc = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_PROFILE);
roccat_set_config_profile(device, index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE);
if (rc < ROCCAT_REPORT_SIZE_PROFILE)
return -EIO;
@@ -468,9 +466,8 @@ roccat_read_button(struct ratbag_button *button)
macro = &drv_data->macros[button->profile->index][button->index];
buf = (uint8_t*)macro;
buf[0] = ROCCAT_REPORT_ID_MACRO;
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO);
if (rc != ROCCAT_REPORT_SIZE_MACRO) {
log_error(device->ratbag,
"Unable to retrieve the macro for button %d of profile %d: %s (%d)\n",
@@ -594,9 +591,8 @@ roccat_write_macro(struct ratbag_button *button,
macro->length = count;
macro->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_MACRO);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO);
if (rc < 0)
return rc;
@@ -673,9 +669,8 @@ roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
settings_report->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_SETTINGS);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS);
if (rc < 0)
return rc;
@@ -713,9 +708,8 @@ roccat_read_profile(struct ratbag_profile *profile)
setting_report = &drv_data->settings[profile->index];
buf = (uint8_t*)setting_report;
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_SETTINGS);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS);
if (rc < ROCCAT_REPORT_SIZE_SETTINGS)
return;
@@ -756,9 +750,8 @@ roccat_read_profile(struct ratbag_profile *profile)
buf = drv_data->profiles[profile->index];
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE);
msleep(10);

View File

@@ -0,0 +1,319 @@
/*
* Copyright © 2020 Pipat Saengow
*
* 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 "libratbag-enums.h"
#include "libratbag-hidraw.h"
#include "libratbag-private.h"
#include "libratbag-util.h"
#include "shared-macro.h"
#include <stdint.h>
#include <string.h>
#define SINOWEALTHNUBWO_PERF_CMD_REPORTID 0x02
#define SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID 0x03
#define SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID 0x04
#define SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE 256
#define SINOWEALTHNUBWO_GET_FIRMWARE_MSGOFFSET 48
#define SINOWEALTHNUBWO_PERF_CMD_MSGSIZE 16
// Actually more but I only implemented one
#define SINOWEALTHNUBWO_NUM_PROFILES 1
#define SINOWEALTHNUBWO_NUM_RESOLUTIONS 1
// Actually 8 but I am not going to implement macros.
#define SINOWEALTHNUBWO_NUM_BUTTONS 0
#define SINOWEALTHNUBWO_NUM_LEDS 1
//Magic set_feature that must be called before requesting firmware string
static uint8_t PREFIRMWARE_QUERY_MSG[] = {0x02, 0x01, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static unsigned int REPORT_RATES[] = { 125, 250, 333, 500, 1000 };
static uint8_t REPORT_RATES_ENCODED[] = {0x08, 0x04, 0x03, 0x02, 0x01};
static uint8_t REPORT_RATES_CMD[] = {0x02, 0x06, 0xbb, 0xaa, 0x28, 0x00, 0x01, 0x00};
static unsigned int DPILIST[] = { 1000, 2000, 3000, 5000, 15000};
static uint8_t DPI_ENCODED[] = {0x04, 0x03, 0x02, 0x01, 0x00};
static uint8_t DPI_CMD[] = { 0x02, 0x06, 0xbb, 0xaa, 0x32, 0x00, 0x01, 0x00 };
enum
sinowealthnubwo_color_mode {
SINOWEALTHNUBWO_COLOR_OFF = 0x00,
SINOWEALTHNUBWO_COLOR_ON = 0x01,
SINOWEALTHNUBWO_COLOR_BREATHING = 0x02,
SINOWEALTHNUBWO_COLOR_COLOR_SHIFT = 0x03,
SINOWEALTHNUBWO_COLOR_SPECTRUM = 0x04,
SINOWEALTHNUBWO_COLOR_MARQUEE = 0x05,
};
struct
sinowealthnubwo_aesthetic_report {
uint8_t report_id; //0x03
uint8_t cmd[7];
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t color_mode;
uint8_t padzero; // 0x00
uint8_t brightness; // 0x01 to 0x03
uint8_t tempo; // 0x05 0x03 0x01
uint8_t padzero2[16*3+1];
} __packed__;
static uint8_t AESTHETIC_CMD[] = {0x06, 0xbb, 0xaa, 0x2a, 0x00, 0x0a, 0x00};
static int
sinowealthnubwo_test_hidraw(struct ratbag_device *device)
{
return
ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID)
&& ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID)
&& ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID);
}
static int
sinowealth_get_firmware_string(struct ratbag_device *device, char **output)
{
uint8_t buff[SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE + 1] = {0}; //Purposefully overalloacate to prevent buffer overrun
int size;
size = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, PREFIRMWARE_QUERY_MSG, ARRAY_LENGTH(PREFIRMWARE_QUERY_MSG));
if (size < 0) {
log_error(device->ratbag, "Error while sending pre-firmware request message: %d\n", size);
return size;
}
size = ratbag_hidraw_get_feature_report(device, SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID, buff, SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE);
if (size != SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE) {
log_error(device->ratbag ,"Firmware report reply size mismatch expected %d got %d\n", SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE, size);
return size;
}
*output = strdup_ascii_only((char *) buff + SINOWEALTHNUBWO_GET_FIRMWARE_MSGOFFSET);
return 0;
}
static int
sinowealthnubwo_probe(struct ratbag_device *device)
{
int error;
struct ratbag_profile *profile;
struct ratbag_resolution *resolution;
struct ratbag_led *led;
error = ratbag_find_hidraw(device, sinowealthnubwo_test_hidraw);
if (error)
return error;
char *fwstr;
error = sinowealth_get_firmware_string(device, &fwstr);
if (error)
return error;
log_info(device->ratbag, "Firmware: %s\n", fwstr);
free(fwstr);
ratbag_device_init_profiles(device,
SINOWEALTHNUBWO_NUM_PROFILES,
SINOWEALTHNUBWO_NUM_RESOLUTIONS,
SINOWEALTHNUBWO_NUM_BUTTONS,
SINOWEALTHNUBWO_NUM_LEDS);
ratbag_device_for_each_profile(device, profile) {
profile->is_active = true;
ratbag_profile_set_cap(profile, RATBAG_PROFILE_CAP_WRITE_ONLY);
ratbag_profile_set_report_rate_list(profile, REPORT_RATES, ARRAY_LENGTH(REPORT_RATES));
ratbag_profile_for_each_resolution(profile, resolution) {
ratbag_resolution_set_dpi_list(resolution, DPILIST, ARRAY_LENGTH(DPILIST));
resolution->dpi_x = resolution->dpi_y = DPILIST[ARRAY_LENGTH(DPILIST)-1];
resolution->is_active = true;
resolution->is_default = true;
}
ratbag_profile_for_each_led(profile, led) {
led->mode = RATBAG_LED_OFF;
led->color.red = led->color.green = led->color.blue = 0xFF;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
led->type = RATBAG_LED_TYPE_SIDE;
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
//Actually more
}
}
return 0;
}
static uint8_t
encode_dpi(unsigned int dpi)
{
for (size_t i = 0; i < ARRAY_LENGTH(DPILIST); i++)
if (DPILIST[i] == dpi)
return DPI_ENCODED[i];
return DPI_ENCODED[0];
}
static uint8_t
encode_report_rate(unsigned int reportrate)
{
for (size_t i = 0; i < ARRAY_LENGTH(REPORT_RATES); i++)
if (REPORT_RATES[i] == reportrate)
return REPORT_RATES_ENCODED[i];
return REPORT_RATES_ENCODED[0];
}
static int
sinowealthnubwo_set_dpi(struct ratbag_device *device, int dpi)
{
uint8_t buf[SINOWEALTHNUBWO_PERF_CMD_MSGSIZE] = {0};
memcpy(buf, DPI_CMD, ARRAY_LENGTH(DPI_CMD));
buf[ARRAY_LENGTH(DPI_CMD)] = encode_dpi(dpi);
int error = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, buf, SINOWEALTHNUBWO_PERF_CMD_MSGSIZE);
if (error < 0)
return error;
return 0;
}
static int
sinowealthnubwo_set_report_rate(struct ratbag_device *device, int reportrate)
{
uint8_t buf[SINOWEALTHNUBWO_PERF_CMD_MSGSIZE] = {0};
memcpy(buf, REPORT_RATES_CMD, ARRAY_LENGTH(REPORT_RATES_CMD));
buf[ARRAY_LENGTH(REPORT_RATES_CMD)] = encode_report_rate(reportrate);
int error = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, buf, SINOWEALTHNUBWO_PERF_CMD_MSGSIZE);
if (error < 0)
return error;
return 0;
}
static enum sinowealthnubwo_color_mode encode_color(enum ratbag_led_mode mode)
{
switch (mode) {
case RATBAG_LED_OFF:
return SINOWEALTHNUBWO_COLOR_OFF;
case RATBAG_LED_ON:
return SINOWEALTHNUBWO_COLOR_ON;
case RATBAG_LED_CYCLE:
return SINOWEALTHNUBWO_COLOR_MARQUEE;
case RATBAG_LED_BREATHING:
return SINOWEALTHNUBWO_COLOR_BREATHING;
default:
return SINOWEALTHNUBWO_COLOR_OFF;
}
}
static uint8_t normalize_duration(int duration)
{
const int MAX_DURATION = 10000;
const uint8_t avail_dura[] = {0x01, 0x03, 0x05};
const int selected = (duration * ((int) ARRAY_LENGTH(avail_dura)) - 1) / MAX_DURATION;
return avail_dura[selected];
}
static uint8_t normalize_brightness(int brightness)
{
const int MAX_BRIGHTNESS = 255;
return 1 + (brightness * 3 - 1) / MAX_BRIGHTNESS;
}
static int
sinowealthnubwo_set_aesthetic(struct ratbag_device *device, struct ratbag_led *led) {
struct sinowealthnubwo_aesthetic_report report;
memset(&report, 0, sizeof(struct sinowealthnubwo_aesthetic_report));
report.report_id = SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID;
memcpy(report.cmd, AESTHETIC_CMD, ARRAY_LENGTH(AESTHETIC_CMD));
report.r = led->color.red;
report.g = led->color.green;
report.b = led->color.blue;
report.color_mode = encode_color(led->mode);
report.tempo = normalize_duration(led->ms);
report.brightness = normalize_brightness(led->brightness);
int size = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID, (uint8_t *) &report, sizeof(report));
if (size < 0)
return size;
return 0;
}
static int
sinowealthnubwo_write_profile(struct ratbag_device *device, struct ratbag_profile *profile)
{
struct ratbag_resolution *resolution;
struct ratbag_led *led;
int error;
log_debug(device->ratbag, "Writing updates\n");
log_debug(device->ratbag, "Setting report rate\n");
error = sinowealthnubwo_set_report_rate(device, profile->hz);
if (error)
return error;
ratbag_profile_for_each_resolution(profile, resolution) {
if (!resolution->dirty)
continue;
log_debug(device->ratbag, "Setting DPI\n");
error = sinowealthnubwo_set_dpi(device, resolution->dpi_x);
if (error)
return error;
}
ratbag_profile_for_each_led(profile, led) {
if (!led->dirty)
continue;
log_debug(device->ratbag, "Setting aesthetic\n");
error = sinowealthnubwo_set_aesthetic(device, led);
if (error)
return error;
}
return 0;
}
static int
sinowealthnubwo_commit(struct ratbag_device *device)
{
struct ratbag_profile *profile;
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty) continue;
int error = sinowealthnubwo_write_profile(device, profile);
if (error)
return error;
}
return 0;
}
static void
sinowealthnubwo_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
}
struct ratbag_driver sinowealth_nubwo_driver = {
.name = "Sinowealth Nubwo",
.id = "sinowealth_nubwo",
.probe = sinowealthnubwo_probe,
.remove = sinowealthnubwo_remove,
.commit = sinowealthnubwo_commit,
};

744
src/driver-sinowealth.c Normal file
View File

@@ -0,0 +1,744 @@
/*
* Copyright © 2020 Marian Beermann
*
* 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 "libratbag-private.h"
#include "libratbag-hidraw.h"
#include "libratbag-data.h"
#define SINOWEALTH_REPORT_ID_CONFIG 0x4
#define SINOWEALTH_REPORT_ID_CMD 0x5
#define SINOWEALTH_CMD_FIRMWARE_VERSION 0x1
#define SINOWEALTH_CMD_GET_CONFIG 0x11
#define SINOWEALTH_CMD_GET_BUTTONS 0x12
#define SINOWEALTH_CONFIG_SIZE 520
#define SINOWEALTH_CONFIG_SIZE_USED 131
#define SINOWEALTH_BUTTON_SIZE 88
#define SINOWEALTH_XY_INDEPENDENT 0x80
/* other models might have up to eight */
#define SINOWEALTH_NUM_DPIS 6
#define SINOWEALTH_RGB_BRIGHTNESS_BITS 0xF0
#define SINOWEALTH_RGB_SPEED_BITS 0x0F
#define SINOWEALTH_BUTTON_TYPE_BUTTON 0x11
#define SINOWEALTH_BUTTON_TYPE_KEY 0x21
#define SINOWEALTH_BUTTON_TYPE_REPEATED 0x31
#define SINOWEALTH_BUTTON_TYPE_SWITCH_DPI 0x41
#define SINOWEALTH_BUTTON_TYPE_DISABLED 0x50
struct sinowealth_color {
uint8_t data[3];
} __attribute__((packed));
_Static_assert(sizeof(struct sinowealth_color) == 3, "Invalid size");
enum rgb_effect {
RGB_OFF = 0,
RGB_GLORIOUS = 0x1, /* unicorn mode */
RGB_SINGLE = 0x2, /* single constant color */
RGB_BREATHING = 0x5, /* Full RGB breathing */
RGB_BREATHING7 = 0x3, /* breathing with seven user-defined colors */
RGB_BREATHING1 = 0xa, /* single color breathing */
RGB_TAIL = 0x4,
RGB_RAVE = 0x7,
RGB_WAVE = 0x9
};
struct sinowealth_config_report {
uint8_t report_id; /* SINOWEALTH_REPORT_ID_CONFIG */
uint8_t command_id;
uint8_t unknown1;
/* always 0 when config is read from device,
* has to be 0x7b when writing config to device
*/
uint8_t config_write;
uint8_t unknown2[6];
/* 0x80 - SINOWEALTH_XY_INDEPENDENT */
uint8_t config;
uint8_t dpi_count:4;
uint8_t active_dpi:4;
/* bit set: disabled, unset: enabled
* this structure has support for eight DPI slots,
* but the glorious software only exposes six
*/
uint8_t dpi_enabled;
/* DPI/CPI is encoded in the way the PMW3360 sensor accepts it
* value = (DPI - 100) / 100
* or the way the PMW3389 sensor accepts it
* value = DPI / 100
* If XY are identical, dpi[0-6] contain the sensitivities,
* while in XY independent mode each entry takes two chars for X and Y.
*/
uint8_t dpi[16];
struct sinowealth_color dpi_color[8];
uint8_t rgb_effect; /* see enum rgb_effect */
/* 0x40 - brightness (constant)
* 0x1/2/3 - speed
*/
uint8_t glorious_mode;
uint8_t glorious_direction;
uint8_t single_mode;
struct sinowealth_color single_color;
/* 0x40 - brightness (constant)
* 0x1/2/3 - speed
*/
uint8_t breathing7_mode;
uint8_t breathing7_colorcount; /* 7, constant */
struct sinowealth_color breathing7_colors[7];
/* 0x10/20/30/40 - brightness
* 0x1/2/3 - speed
*/
uint8_t tail_mode;
uint8_t unknown3[33];
/* 0x10/20/30/40 - brightness
* 0x1/2/3 - speed
*/
uint8_t rave_mode;
struct sinowealth_color rave_colors[2];
/* 0x10/20/30/40 - brightness
* 0x1/2/3 - speed
*/
uint8_t wave_mode;
/* 0x1/2/3 - speed */
uint8_t breathing1_mode;
struct sinowealth_color breathing1_color;
uint8_t unknown4;
/* 0x1 - 2 mm
* 0x2 - 3 mm
*/
uint8_t lift_off_distance;
uint8_t padding[SINOWEALTH_CONFIG_SIZE - SINOWEALTH_CONFIG_SIZE_USED];
} __attribute__((packed));
_Static_assert(sizeof(struct sinowealth_config_report) == SINOWEALTH_CONFIG_SIZE, "Invalid size");
struct sinowealth_button_data {
uint8_t type;
uint8_t data[3];
} __attribute__((packed));
struct sinowealth_button_report {
uint8_t report_id;
uint8_t command_id;
uint8_t unknown1[6];
struct sinowealth_button_data buttons[20];
uint8_t padding[SINOWEALTH_CONFIG_SIZE - SINOWEALTH_BUTTON_SIZE];
} __attribute__((packed));
_Static_assert(sizeof(struct sinowealth_button_report) == SINOWEALTH_CONFIG_SIZE, "Invalid size");
enum sinowealth_sensor {
PWM3360,
PWM3389,
};
enum sinowealth_led_format {
LED_RGB,
LED_RBG,
};
struct sinowealth_data {
/* this is kinda unnecessary at this time, but all the other drivers do it too ;) */
struct sinowealth_config_report config;
/* holds button data so we can send back unchanged values for the buttons we do not parse yet */
struct sinowealth_button_report buttons;
/* Specifies sensor used in the device */
enum sinowealth_sensor sensor;
/* whether the devices stores the main LED in RGB or RBG format */
enum sinowealth_led_format led_type;
};
static int
sinowealth_raw_to_dpi(struct ratbag_device *device, int raw)
{
struct sinowealth_data *drv_data = device->drv_data;
int dpi;
if (drv_data->sensor == PWM3360)
dpi = (raw + 1) * 100;
else
dpi = raw * 100;
return dpi;
}
static int
sinowealth_dpi_to_raw(struct ratbag_device *device, unsigned int dpi)
{
struct sinowealth_data *drv_data = device->drv_data;
struct dpi_range *dpirange = NULL;
int raw;
dpirange = ratbag_device_data_sinowealth_get_dpi_range(device->data);
assert(dpi >= dpirange->min && dpi <= dpirange->max);
if (drv_data->sensor == PWM3360)
raw = dpi / 100 - 1;
else
raw = dpi / 100;
return raw;
}
static struct ratbag_color
sinowealth_raw_to_color(struct sinowealth_color raw)
{
return (struct ratbag_color) {.red = raw.data[0], .green = raw.data[1], .blue = raw.data[2]};
}
static struct sinowealth_color
sinowealth_color_to_raw(struct ratbag_color color)
{
return (struct sinowealth_color) {.data[0] = color.red, .data[1] = color.green, .data[2] = color.blue};
}
static struct ratbag_color
sinowealth_rbg_to_color(struct sinowealth_color raw)
{
return (struct ratbag_color) {.red = raw.data[0], .green = raw.data[2], .blue = raw.data[1]};
}
static struct sinowealth_color
sinowealth_color_to_rbg(struct ratbag_color color)
{
return (struct sinowealth_color) {.data[0] = color.red, .data[2] = color.green, .data[1] = color.blue};
}
static int
sinowealth_rgb_mode_to_brightness(uint8_t mode)
{
/* brightness is 0-4 in the upper nibble of mode,
* while libratbag uses 0-255.
*/
return min((mode >> 4) * 64, 255);
}
static uint8_t
sinowealth_brightness_to_rgb_mode(int brightness)
{
/* convert 0-255 to 0-4 in the upper nibble of mode */
return ((brightness + 1) / 64) << 4;
}
static int
sinowealth_rgb_mode_to_duration(uint8_t mode)
{
switch (mode & SINOWEALTH_RGB_SPEED_BITS) {
case 0: return 10000; /* static: does not translate to duration */
case 1: return 1500;
case 2: return 1000;
case 3: return 500;
default: return 0;
}
}
static uint8_t
sinowealth_duration_to_rgb_mode(int duration_ms)
{
uint8_t mode = 0;
if (duration_ms <= 500) {
mode |= 3;
} else if (duration_ms <= 1000) {
mode |= 2;
} else {
mode |= 1;
}
return mode;
}
static void
sinowealth_set_led_from_rgb_mode(struct ratbag_led *led, uint8_t mode)
{
led->brightness = sinowealth_rgb_mode_to_brightness(mode);
led->ms = sinowealth_rgb_mode_to_duration(mode);
}
static uint8_t
sinowealth_led_to_rgb_mode(const struct ratbag_led *led)
{
uint8_t mode;
mode = sinowealth_brightness_to_rgb_mode(led->brightness);
mode |= sinowealth_duration_to_rgb_mode(led->ms);
return mode;
}
static int
sinowealth_read_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct sinowealth_data *drv_data = device->drv_data;
struct sinowealth_config_report *config = &drv_data->config;
struct ratbag_resolution *resolution;
struct ratbag_led *led;
unsigned int hz = 1000; /* TODO */
int rc;
uint8_t cmd[6] = {SINOWEALTH_REPORT_ID_CMD, SINOWEALTH_CMD_GET_CONFIG};
rc = ratbag_hidraw_set_feature_report(device, SINOWEALTH_REPORT_ID_CMD, cmd, sizeof(cmd));
if (rc != sizeof(cmd)) {
log_error(device->ratbag, "Error while sending read config command: %d\n", rc);
return -1;
}
rc = ratbag_hidraw_get_feature_report(device, SINOWEALTH_REPORT_ID_CONFIG,
(uint8_t*) config, SINOWEALTH_CONFIG_SIZE);
/* The GET_FEATURE report length has to be 520, but the actual data returned is less */
if (rc != SINOWEALTH_CONFIG_SIZE_USED) {
log_error(device->ratbag, "Could not read device configuration: %d\n", rc);
return -1;
}
/* TODO */
ratbag_profile_set_report_rate_list(profile, &hz, 1);
ratbag_profile_set_report_rate(profile, hz);
ratbag_profile_for_each_resolution(profile, resolution) {
if (config->config & SINOWEALTH_XY_INDEPENDENT) {
resolution->dpi_x = sinowealth_raw_to_dpi(device, config->dpi[resolution->index * 2]);
resolution->dpi_y = sinowealth_raw_to_dpi(device, config->dpi[resolution->index * 2 + 1]);
} else {
resolution->dpi_x = sinowealth_raw_to_dpi(device, config->dpi[resolution->index]);
resolution->dpi_y = resolution->dpi_x;
}
if (config->dpi_enabled & (1<<resolution->index)) {
/* DPI step is disabled, fake it by setting DPI to 0 */
resolution->dpi_x = 0;
resolution->dpi_y = 0;
}
resolution->is_active = resolution->index == config->active_dpi - 1u;
resolution->is_default = resolution->is_active;
}
/* Body lighting */
led = ratbag_profile_get_led(profile, 0);
switch (config->rgb_effect) {
case RGB_OFF:
led->mode = RATBAG_LED_OFF;
break;
case RGB_SINGLE:
led->mode = RATBAG_LED_ON;
if (drv_data->led_type == LED_RGB)
led->color = sinowealth_raw_to_color(config->single_color);
else
led->color = sinowealth_rbg_to_color(config->single_color);
led->brightness = sinowealth_rgb_mode_to_brightness(config->single_mode);
break;
case RGB_GLORIOUS:
case RGB_BREATHING:
case RGB_BREATHING7:
case RGB_TAIL:
case RGB_RAVE:
case RGB_WAVE:
led->mode = RATBAG_LED_CYCLE;
sinowealth_set_led_from_rgb_mode(led, config->glorious_mode);
break;
case RGB_BREATHING1:
led->mode = RATBAG_LED_BREATHING;
led->color = sinowealth_rbg_to_color(config->breathing1_color);
sinowealth_set_led_from_rgb_mode(led, config->breathing1_mode);
break;
}
ratbag_led_unref(led);
profile->is_active = true;
return 0;
}
struct sinowealth_button_mapping {
uint8_t raw;
struct ratbag_button_action action;
};
static struct sinowealth_button_mapping sinowealth_button_mapping[] = {
{ 0x01, BUTTON_ACTION_BUTTON(1) },
{ 0x02, BUTTON_ACTION_BUTTON(2) },
{ 0x04, BUTTON_ACTION_BUTTON(3) },
{ 0x08, BUTTON_ACTION_BUTTON(5) },
{ 0x10, BUTTON_ACTION_BUTTON(4) },
};
static const struct ratbag_button_action*
sinowealth_raw_to_button_action(uint8_t data)
{
struct sinowealth_button_mapping *mapping;
ARRAY_FOR_EACH(sinowealth_button_mapping, mapping) {
if (mapping->raw == data)
return &mapping->action;
}
return NULL;
}
static uint8_t
sinowealth_button_action_to_raw(const struct ratbag_button_action *action)
{
struct sinowealth_button_mapping *mapping;
ARRAY_FOR_EACH(sinowealth_button_mapping, mapping) {
if (ratbag_button_action_match(&mapping->action, action))
return mapping->raw;
}
return 0x00;
}
static int
sinowealth_read_buttons(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct sinowealth_data *drv_data = device->drv_data;
struct sinowealth_button_report *buf = &drv_data->buttons;
struct ratbag_button *button;
struct sinowealth_button_data button_data;
enum ratbag_button_type button_types[8] = {RATBAG_BUTTON_TYPE_UNKNOWN};
uint8_t cmd[6] = {SINOWEALTH_REPORT_ID_CMD, SINOWEALTH_CMD_GET_BUTTONS};
int rc;
rc = ratbag_hidraw_set_feature_report(device, SINOWEALTH_REPORT_ID_CMD, cmd, sizeof(cmd));
if (rc != sizeof(cmd)) {
log_error(device->ratbag, "Error while sending read config command: %d\n", rc);
return -1;
}
rc = ratbag_hidraw_get_feature_report(device, SINOWEALTH_REPORT_ID_CONFIG, (uint8_t*) buf, SINOWEALTH_CONFIG_SIZE);
if (rc != SINOWEALTH_BUTTON_SIZE) {
log_error(device->ratbag, "Could not read device button configuration: %d\n", rc);
return -1;
}
button_types[0] = RATBAG_BUTTON_TYPE_LEFT;
button_types[1] = RATBAG_BUTTON_TYPE_RIGHT;
button_types[2] = RATBAG_BUTTON_TYPE_MIDDLE;
button_types[3] = RATBAG_BUTTON_TYPE_THUMB;
button_types[4] = RATBAG_BUTTON_TYPE_THUMB2;
button_types[5] = RATBAG_BUTTON_TYPE_RESOLUTION_UP;
button_types[6] = RATBAG_BUTTON_TYPE_RESOLUTION_DOWN;
button_types[7] = RATBAG_BUTTON_TYPE_THUMB3;
ratbag_profile_for_each_button(profile, button) {
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
button->type = button_types[button->index];
button_data = buf->buttons[button->index];
if (button_data.type == SINOWEALTH_BUTTON_TYPE_BUTTON) {
const struct ratbag_button_action *action;
action = sinowealth_raw_to_button_action(button_data.data[0]);
if (action)
ratbag_button_set_action(button, action);
} else if (button_data.type == SINOWEALTH_BUTTON_TYPE_KEY) {
unsigned int key, modifiers;
int rc;
key = ratbag_hidraw_get_keycode_from_keyboard_usage(device, button_data.data[1]);
modifiers = 0;
if (button_data.data[0] & 0x01)
modifiers |= MODIFIER_LEFTCTRL;
if (button_data.data[0] & 0x02)
modifiers |= MODIFIER_LEFTSHIFT;
if (button_data.data[0] & 0x04)
modifiers |= MODIFIER_LEFTALT;
if (button_data.data[0] & 0x08)
modifiers |= MODIFIER_LEFTMETA;
rc = ratbag_button_macro_new_from_keycode(button, key, modifiers);
if (rc < 0) {
log_error(device->ratbag,
"Error while reading button %d\n",
button->index);
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
}
}
}
return 0;
}
static int
sinowealth_write_buttons(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct sinowealth_data *drv_data = device->drv_data;
struct sinowealth_button_report *buf = &drv_data->buttons;
struct ratbag_button *button;
int rc;
buf->report_id = 0x04;
buf->command_id = 0x12;
buf->unknown1[1] = 0x50;
ratbag_profile_for_each_button(profile, button) {
struct sinowealth_button_data *button_data;
struct ratbag_button_action *action = &button->action;
button_data = &buf->buttons[button->index];
if (action->type == RATBAG_BUTTON_ACTION_TYPE_BUTTON) {
button_data->type = SINOWEALTH_BUTTON_TYPE_BUTTON;
button_data->data[0] = sinowealth_button_action_to_raw(action);
} else if (action->type == RATBAG_BUTTON_ACTION_TYPE_MACRO) {
unsigned int key, modifiers;
rc = ratbag_action_keycode_from_macro(action,
&key,
&modifiers);
if (rc < 0) {
log_error(device->ratbag,
"Error while writing macro for button %d\n",
button->index);
}
button_data->type = SINOWEALTH_BUTTON_TYPE_KEY;
button_data->data[0] = modifiers;
button_data->data[1] = ratbag_hidraw_get_keyboard_usage_from_keycode(device, key);
}
}
rc = ratbag_hidraw_set_feature_report(device, SINOWEALTH_REPORT_ID_CONFIG,
(uint8_t*) buf, SINOWEALTH_CONFIG_SIZE);
if (rc != SINOWEALTH_CONFIG_SIZE) {
log_error(device->ratbag, "Error while writing buttons: %d\n", rc);
return -1;
}
return 0;
}
static void
sinowealth_init_profile(struct ratbag_device *device)
{
struct ratbag_profile *profile;
struct ratbag_resolution *resolution;
struct ratbag_led *led;
struct sinowealth_data *drv_data = device->drv_data;
struct dpi_range *dpirange = NULL;
int button_count, led_count;
const char *led_type;
dpirange = ratbag_device_data_sinowealth_get_dpi_range(device->data);
button_count = ratbag_device_data_sinowealth_get_button_count(device->data);
led_count = ratbag_device_data_sinowealth_get_led_count(device->data);
led_type = ratbag_device_data_sinowealth_get_led_type(device->data);
if (!dpirange)
{
log_error(device->ratbag, "DpiRange must be defined in .device file\n");
}
if (button_count < 0)
{
log_error(device->ratbag, "Button count must be defined in .device file\n");
button_count = 0;
}
if (led_count < 0)
{
log_error(device->ratbag, "Led count must be defined in .device file\n");
led_count = 0;
}
if (!led_type)
log_error(device->ratbag, "Led type must be defined in .device file. Defaulting to RGB.\n");
if (dpirange->max == 12000)
drv_data->sensor = PWM3360;
else
drv_data->sensor = PWM3389;
if (led_type && streq(led_type, "RBG"))
drv_data->led_type = LED_RBG;
else
drv_data->led_type = LED_RGB;
ratbag_device_init_profiles(device, 1, SINOWEALTH_NUM_DPIS, button_count, led_count);
profile = ratbag_device_get_profile(device, 0);
ratbag_profile_for_each_resolution(profile, resolution) {
ratbag_resolution_set_dpi_list_from_range(resolution,
dpirange->min,
dpirange->max);
ratbag_resolution_set_cap(resolution, RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
}
/* Set up LED capabilities */
ratbag_profile_for_each_led(profile, led) {
if (led->index == 0) {
led->type = RATBAG_LED_TYPE_SIDE;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
} else {
led->type = RATBAG_LED_TYPE_DPI;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
}
}
ratbag_profile_unref(profile);
}
static int
sinowealth_test_hidraw(struct ratbag_device *device)
{
/* Only the keyboard interface has this report */
return ratbag_hidraw_has_report(device, SINOWEALTH_REPORT_ID_CONFIG);
}
static int
sinowealth_probe(struct ratbag_device *device)
{
int rc;
struct sinowealth_data *drv_data = 0;
struct ratbag_profile *profile = 0;
rc = ratbag_find_hidraw(device, sinowealth_test_hidraw);
if (rc)
goto err;
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
sinowealth_init_profile(device);
profile = ratbag_device_get_profile(device, 0);
rc = sinowealth_read_profile(profile);
if (rc) {
rc = -ENODEV;
goto err;
}
rc = sinowealth_read_buttons(profile);
if (rc) {
rc = -ENODEV;
goto err;
}
return 0;
err:
free(drv_data);
ratbag_set_drv_data(device, 0);
return rc;
}
static int
sinowealth_commit(struct ratbag_device *device)
{
struct ratbag_profile *profile = ratbag_device_get_profile(device, 0);
struct sinowealth_data *drv_data = device->drv_data;
struct sinowealth_config_report *config = &drv_data->config;
struct ratbag_resolution *resolution;
struct ratbag_led *led;
int rc;
uint8_t dpi_enabled = 0;
/* Check if any resolution requires independent XY DPIs */
config->config &= ~SINOWEALTH_XY_INDEPENDENT;
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->dpi_x != resolution->dpi_y && resolution->dpi_x && resolution->dpi_y) {
config->config |= SINOWEALTH_XY_INDEPENDENT;
break;
}
}
config->dpi_count = 0;
ratbag_profile_for_each_resolution(profile, resolution) {
if (!resolution->dpi_x || !resolution->dpi_y)
continue;
if (config->config & SINOWEALTH_XY_INDEPENDENT) {
config->dpi[resolution->index * 2] = sinowealth_dpi_to_raw(device, resolution->dpi_x);
config->dpi[resolution->index * 2 + 1] = sinowealth_dpi_to_raw(device, resolution->dpi_y);
} else {
config->dpi[resolution->index] = sinowealth_dpi_to_raw(device, resolution->dpi_x);
}
dpi_enabled |= 1<<resolution->index;
config->dpi_count++;
}
config->dpi_enabled = ~dpi_enabled; /* config->dpi_enabled is inverted */
/* Body lighting */
led = ratbag_profile_get_led(profile, 0);
switch(led->mode) {
case RATBAG_LED_OFF:
config->rgb_effect = RGB_OFF;
break;
case RATBAG_LED_ON:
config->rgb_effect = RGB_SINGLE;
if (drv_data->led_type == LED_RGB)
config->single_color = sinowealth_color_to_raw(led->color);
else
config->single_color = sinowealth_color_to_rbg(led->color);
break;
case RATBAG_LED_CYCLE:
config->rgb_effect = RGB_GLORIOUS;
config->glorious_mode = sinowealth_led_to_rgb_mode(led);
break;
case RATBAG_LED_BREATHING:
config->rgb_effect = RGB_BREATHING7;
config->breathing1_color = sinowealth_color_to_rbg(led->color);
config->breathing1_mode = sinowealth_led_to_rgb_mode(led);
break;
}
ratbag_led_unref(led);
config->config_write = 0x7b; /* magic */
rc = ratbag_hidraw_set_feature_report(device, SINOWEALTH_REPORT_ID_CONFIG,
(uint8_t*) config, SINOWEALTH_CONFIG_SIZE);
if (rc != SINOWEALTH_CONFIG_SIZE) {
log_error(device->ratbag, "Error while writing config: %d\n", rc);
ratbag_profile_unref(profile);
return -1;
}
rc = sinowealth_write_buttons(profile);
ratbag_profile_unref(profile);
return rc;
}
static void
sinowealth_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
free(ratbag_get_drv_data(device));
}
struct ratbag_driver sinowealth_driver = {
.name = "Sinowealth",
.id = "sinowealth",
.probe = sinowealth_probe,
.remove = sinowealth_remove,
.commit = sinowealth_commit
};

View File

@@ -271,8 +271,8 @@ steelseries_probe(struct ratbag_device *device)
struct ratbag_button *button;
struct ratbag_led *led;
int rc, button_count, led_count, device_version, mono_led, short_button;
_cleanup_(dpi_list_freep) struct dpi_list *dpilist = NULL;
_cleanup_(freep) struct dpi_range *dpirange = NULL;
struct dpi_list *dpilist = NULL;
struct dpi_range *dpirange = NULL;
unsigned int report_rates[] = { 125, 250, 500, 1000 };
@@ -366,10 +366,11 @@ steelseries_probe(struct ratbag_device *device)
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
steelseries_get_firmware_version(device);
log_debug(device->ratbag, "SteelSeries firmware version %d.%d\n",
drv_data->firmware_major,
drv_data->firmware_minor);
rc = steelseries_get_firmware_version(device);
if(rc == 0)
log_debug(device->ratbag, "SteelSeries firmware version %d.%d\n",
drv_data->firmware_major,
drv_data->firmware_minor);
steelseries_read_settings(device);
@@ -572,7 +573,7 @@ steelseries_write_buttons(struct ratbag_profile *profile)
msleep(10);
if (device_version == 3)
ret = ratbag_hidraw_raw_request(device, STEELSERIES_ID_BUTTONS,
ret = ratbag_hidraw_raw_request(device, STEELSERIES_ID_BUTTONS,
buf,sizeof(buf),HID_FEATURE_REPORT,HID_REQ_SET_REPORT);
else
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
@@ -699,8 +700,8 @@ construct_cycle_buffer(struct steelseries_led_cycle *cycle,
/* this seems to be the minimum allowed */
duration = max(buf[spec->point_count_idx] * 330, cycle->duration);
hidpp_set_unaligned_le_u16(&buf[spec->duration_idx], duration);
set_unaligned_le_u16(&buf[spec->duration_idx], duration);
}
static int
@@ -709,6 +710,7 @@ steelseries_write_led_cycle(struct ratbag_led *led,
{
struct ratbag_device *device = led->profile->device;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
int ret;
struct steelseries_led_cycle cycle;
@@ -739,7 +741,7 @@ steelseries_write_led_cycle(struct ratbag_led *led,
list_append(&cycle.points, &point[0].link);
break;
case RATBAG_LED_CYCLE:
case RATBAG_LED_CYCLE:
point[0].color = red;
point[0].pos = 0x00;
@@ -768,7 +770,7 @@ steelseries_write_led_cycle(struct ratbag_led *led,
point[2].color = black;
point[2].pos = 0x7F;
list_append(&cycle.points, &point[0].link);
list_append(&cycle.points, &point[1].link);
list_append(&cycle.points, &point[2].link);
@@ -782,8 +784,12 @@ steelseries_write_led_cycle(struct ratbag_led *led,
construct_cycle_buffer(&cycle, cycle_spec, buf, sizeof(buf));
msleep(10);
ret = ratbag_hidraw_raw_request(device, cycle_spec->cmd_val, buf,
sizeof(buf), cycle_spec->hid_report_type, HID_REQ_SET_REPORT);
if (device_version == 3)
ret = ratbag_hidraw_raw_request(device, cycle_spec->cmd_val, buf,
sizeof(buf), cycle_spec->hid_report_type, HID_REQ_SET_REPORT);
else
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
if (ret < 0)
return ret;

View File

@@ -214,7 +214,10 @@ test_read_profile(struct ratbag_profile *profile)
res->is_default = r->dflt;
if (r->dflt)
default_set = true;
ratbag_resolution_set_cap(res, r->caps);
for (size_t j = 0; j < ARRAY_LENGTH(r->caps) && r->caps[j]; j++) {
ratbag_resolution_set_cap(res, r->caps[j]);
}
}
/* special case triggered by the test suite when num_resolutions is 0 */

View File

@@ -234,7 +234,7 @@ hidpp20_1b04_get_physical_mapping_name(uint16_t value)
}
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value)
hidpp20_led_get_location_mapping(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;
@@ -247,7 +247,7 @@ hidpp20_8070_get_location_mapping(uint16_t value)
}
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value)
hidpp20_led_get_location_mapping_name(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;
@@ -306,6 +306,33 @@ hidpp_read_response(struct hidpp_device *dev, uint8_t *buf, size_t size)
return rc >= 0 ? rc : -errno;
}
void
hidpp_get_supported_report_types(struct hidpp_device *dev, struct hidpp_hid_report *reports, unsigned int num_reports)
{
if (!reports) {
hidpp_log_debug(dev, "hidpp: we don't have information about the hid reports, ignoring checks\n");
dev->supported_report_types = 0xffff;
return;
}
/* reset the bits we are gonna check */
dev->supported_report_types &= (0xff & ~HIDPP_REPORT_SHORT);
dev->supported_report_types &= (0xff & ~HIDPP_REPORT_LONG);
for (unsigned i = 0; i < num_reports; i++)
if ((reports[i].usage_page & 0xff00) == 0xff00) /* vendor defined usage page (0xff00-0xffff) */
switch (reports[i].report_id) {
case REPORT_ID_SHORT:
hidpp_log_debug(dev, "hidpp: device supports short reports\n");
dev->supported_report_types |= HIDPP_REPORT_SHORT;
break;
case REPORT_ID_LONG:
hidpp_log_debug(dev, "hidpp: device supports long reports\n");
dev->supported_report_types |= HIDPP_REPORT_LONG;
break;
}
}
void
hidpp_log(struct hidpp_device *dev,
enum hidpp_log_priority priority,
@@ -322,6 +349,21 @@ hidpp_log(struct hidpp_device *dev,
va_end(args);
}
char *
hidpp_buffer_to_string(const uint8_t *buf, size_t len)
{
size_t dstlen = len * 6;
char *dst = zalloc(dstlen);
unsigned int n = 0;
char *sep = "";
for (unsigned i = 0; i < len; ++i) {
n += snprintf_safe(&dst[n], dstlen - n, "%s%02x", sep, buf[i] & 0xFF);
sep = " ";
}
return dst;
}
void
hidpp_log_buffer(struct hidpp_device *dev,
enum hidpp_log_priority priority,
@@ -329,23 +371,10 @@ hidpp_log_buffer(struct hidpp_device *dev,
uint8_t *buf, size_t len)
{
_cleanup_free_ char *output_buf = NULL;
char *sep = "";
unsigned int i, n;
unsigned int buf_len;
_cleanup_free_ char *bytes = NULL;
buf_len = header ? strlen(header) : 0;
buf_len += len * 3;
buf_len += 1; /* terminating '\0' */
output_buf = zalloc(buf_len);
n = 0;
if (header)
n += snprintf_safe(output_buf, buf_len - n, "%s", header);
for (i = 0; i < len; ++i) {
n += snprintf_safe(&output_buf[n], buf_len - n, "%s%02x", sep, buf[i] & 0xFF);
sep = " ";
}
bytes = hidpp_buffer_to_string(buf, len);
asprintf(&output_buf, "%s %s", header ? header : "", bytes);
hidpp_log(dev, priority, "%s\n", output_buf);
}
@@ -361,6 +390,7 @@ hidpp_device_init(struct hidpp_device *dev, int fd)
{
dev->hidraw_fd = fd;
hidpp_device_set_log_handler(dev, simple_log, HIDPP_LOG_PRIORITY_INFO, NULL);
dev->supported_report_types = 0;
}
void

View File

@@ -32,6 +32,8 @@
#include <stdarg.h>
#include <stddef.h>
#include "libratbag-util.h"
#define HIDPP_RECEIVER_IDX 0xFF
#define HIDPP_WIRED_DEVICE_IDX 0x00
@@ -93,13 +95,23 @@ typedef void (*hidpp_log_handler)(void *userdata,
const char *format, va_list args)
__attribute__ ((format (printf, 3, 0)));
struct hidpp_hid_report {
unsigned int report_id;
unsigned int usage_page;
unsigned int usage;
};
struct hidpp_device {
int hidraw_fd;
void *userdata;
hidpp_log_handler log_handler;
enum hidpp_log_priority log_priority;
unsigned supported_report_types;
};
#define HIDPP_REPORT_SHORT (1 << 0)
#define HIDPP_REPORT_LONG (1 << 1)
void
hidpp_device_init(struct hidpp_device *dev, int fd);
void
@@ -118,10 +130,10 @@ enum ratbag_button_type
hidpp20_1b04_get_physical_mapping(uint16_t value);
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value);
hidpp20_led_get_location_mapping(uint16_t value);
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value);
hidpp20_led_get_location_mapping_name(uint16_t value);
const char *
hidpp20_1b04_get_logical_mapping_name(uint16_t value);
@@ -138,6 +150,11 @@ hidpp_write_command(struct hidpp_device *dev, uint8_t *cmd, int size);
int
hidpp_read_response(struct hidpp_device *dev, uint8_t *buf, size_t size);
void
hidpp_get_supported_report_types(struct hidpp_device *dev,
struct hidpp_hid_report *reports,
unsigned int num_reports);
void
hidpp_log(struct hidpp_device *dev,
enum hidpp_log_priority priority,
@@ -145,6 +162,9 @@ hidpp_log(struct hidpp_device *dev,
...)
__attribute__ ((format (printf, 3, 4)));
char *
hidpp_buffer_to_string(const uint8_t *buf, size_t len);
void
hidpp_log_buffer(struct hidpp_device *dev,
enum hidpp_log_priority priority,
@@ -163,62 +183,30 @@ hidpp_log_buffer(struct hidpp_device *dev,
uint16_t hidpp_crc_ccitt(uint8_t *data, unsigned int length);
static inline uint16_t
hidpp_get_unaligned_be_u16(uint8_t *buf)
{
return (buf[0] << 8) | buf[1];
}
static inline uint16_t
hidpp_be_u16_to_cpu(uint16_t data)
{
return hidpp_get_unaligned_be_u16((uint8_t *)&data);
}
static inline void
hidpp_set_unaligned_be_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value >> 8;
buf[1] = value & 0xFF;
return get_unaligned_be_u16((uint8_t *)&data);
}
static inline uint16_t
hidpp_cpu_to_be_u16(uint16_t data)
{
uint16_t result;
hidpp_set_unaligned_be_u16((uint8_t *)&result, data);
set_unaligned_be_u16((uint8_t *)&result, data);
return result;
}
static inline uint16_t
hidpp_get_unaligned_le_u16(uint8_t *buf)
{
return (buf[1] << 8) | buf[0];
}
static inline uint16_t
hidpp_le_u16_to_cpu(uint16_t data)
{
return hidpp_get_unaligned_le_u16((uint8_t *)&data);
}
static inline void
hidpp_set_unaligned_le_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value & 0xFF;
buf[1] = value >> 8;
return get_unaligned_le_u16((uint8_t *)&data);
}
static inline uint16_t
hidpp_cpu_to_le_u16(uint16_t data)
{
uint16_t result;
hidpp_set_unaligned_le_u16((uint8_t *)&result, data);
set_unaligned_le_u16((uint8_t *)&result, data);
return result;
}
static inline uint32_t
hidpp_get_unaligned_be_u32(uint8_t *buf)
{
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}

23
src/hidpp.i Normal file
View File

@@ -0,0 +1,23 @@
/* wrapping libratbag functions from hidpp*.h using SWIG. */
%module hidpp
%{
/* the resulting C file should be built as a python extension */
#define SWIG_FILE_WITH_INIT
/* Includes the header in the wrapper code */
#include <shared.h>
#include <hidpp-generic.h>
#include <hidpp20.h>
%}
#define __attribute__(x)
#define _Static_assert(a,b)
/* Parse the header file to generate wrappers */
%include "stdint.i"
%include "cpointer.i"
%pointer_functions(int, intp);
%pointer_functions(uint8_t, uint8_tp);
%pointer_functions(uint16_t, uint16_tp);
%include "hidpp-generic.h"
%include "hidpp20.h"

View File

@@ -161,7 +161,6 @@ hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev,
return 0;
}
static int
hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
{
@@ -171,6 +170,7 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
int ret;
uint8_t hidpp_err = 0;
int command_size;
_cleanup_free_ char *rxdata = NULL, *txdata = NULL;
switch (msg->msg.report_id) {
case REPORT_ID_SHORT:
@@ -186,9 +186,19 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
/* create the expected header */
expected_header = *msg;
txdata = hidpp_buffer_to_string(&msg->data[4], command_size - 4);
hidpp_log_raw(&dev->base, "hidpp10 tx: %02x | %02x | %02x | %02x | %s\n",
msg->msg.report_id,
msg->msg.device_idx,
msg->msg.sub_id,
msg->msg.address,
txdata);
/* response message length doesn't depend on request length */
#if 0
hidpp_log_buf_raw(&dev->base, " expected_header: ?? ", &expected_header.data[1], 3);
hidpp_log_buf_raw(&dev->base, " expected_error_dev: ", expected_error_dev.data, SHORT_MESSAGE_LENGTH);
#endif
/* Send the message to the Device */
ret = hidpp_write_command(&dev->base, msg->data, command_size);
@@ -238,8 +248,15 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
goto out_err;
}
rxdata = hidpp_buffer_to_string(&read_buffer.data[4], ret - 4);
hidpp_log_raw(&dev->base, "hidpp10 rx: %02x | %02x | %02x | %02x | %s\n",
read_buffer.msg.report_id,
read_buffer.msg.device_idx,
read_buffer.msg.sub_id,
read_buffer.msg.address,
rxdata);
if (!hidpp_err) {
hidpp_log_buf_raw(&dev->base, " received: ", read_buffer.data, ret);
/* copy the answer for the caller */
*msg = read_buffer;
}
@@ -277,7 +294,8 @@ hidpp10_get_hidpp_notifications(struct hidpp10_device *dev,
union hidpp10_message notifications = CMD_HIDPP_NOTIFICATIONS(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching HID++ notifications\n");
hidpp_log_raw(&dev->base, "Fetching HID++ notifications (%#02x)\n",
__CMD_HIDPP_NOTIFICATIONS);
res = hidpp10_request_command(dev, &notifications);
if (res)
@@ -298,7 +316,8 @@ hidpp10_set_hidpp_notifications(struct hidpp10_device *dev,
union hidpp10_message notifications = CMD_HIDPP_NOTIFICATIONS(idx, SET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting HID++ notifications\n");
hidpp_log_raw(&dev->base, "Setting HID++ notifications (%#02x\n",
__CMD_HIDPP_NOTIFICATIONS);
notifications.msg.parameters[0] = reporting_flags & 0xFF;
notifications.msg.parameters[1] = (reporting_flags >> 8) & 0x1F;
@@ -333,7 +352,8 @@ hidpp10_get_individual_features(struct hidpp10_device *dev,
union hidpp10_message features = CMD_ENABLE_INDIVIDUAL_FEATURES(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching individual features\n");
hidpp_log_raw(&dev->base, "Fetching individual features (%#02x)\n",
__CMD_ENABLE_INDIVIDUAL_FEATURES);
res = hidpp10_request_command(dev, &features);
if (res)
@@ -356,7 +376,8 @@ hidpp10_set_individual_features(struct hidpp10_device *dev,
union hidpp10_message mode = CMD_ENABLE_INDIVIDUAL_FEATURES(idx, SET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting individual features\n");
hidpp_log_raw(&dev->base, "Setting individual features (%#02x)\n",
__CMD_ENABLE_INDIVIDUAL_FEATURES);
mode.msg.device_idx = dev->index;
@@ -700,7 +721,7 @@ hidpp10_write_profile_directory(struct hidpp10_device *dev)
}
crc = hidpp_crc_ccitt(bytes, HIDPP10_PAGE_SIZE - 2);
hidpp_set_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2], crc);
set_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2], crc);
res = hidpp10_send_hot_payload(dev,
0x00, 0x0000, /* destination: RAM */
@@ -782,11 +803,16 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, uint8_t *current_profile
unsigned i;
uint8_t type, page, offset;
hidpp_log_raw(&dev->base, "Fetching current profile\n");
hidpp_log_raw(&dev->base, "Fetching current profile (%#02x)\n",
__CMD_PROFILE);
res = hidpp10_request_command(dev, &profile);
if (res)
return res;
if (res) {
/* Profiles not supported */
hidpp_log_debug(&dev->base, "Profiles not supported\n");
*current_profile = 0;
return 0;
}
type = profile.msg.parameters[0];
page = profile.msg.parameters[1];
@@ -811,6 +837,20 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, uint8_t *current_profile
"unable to find the profile at (%d,%d) in the directory\n",
page, offset);
break;
case PROFILE_TYPE_FACTORY:
/* Factory profile is selected and profile switching is
* disabled. Let's switch to the first profile because the
* factory profile doesn't help anywone */
res = hidpp10_set_current_profile(dev, 0);
if (res == 0) {
hidpp_log_info(&dev->base, "switched from factory profile to 0\n");
*current_profile = 0;
return 0;
}
hidpp_log_error(&dev->base,
"current profile is factory profile but switching to 0 failed.\n");
break;
default:
hidpp_log_error(&dev->base,
"Unexpected value: %02x\n",
@@ -829,7 +869,8 @@ hidpp10_set_internal_current_profile(struct hidpp10_device *dev,
union hidpp10_message profile = CMD_PROFILE(idx, SET_REGISTER_REQ);
int8_t page, offset;
hidpp_log_raw(&dev->base, "Setting current profile\n");
hidpp_log_raw(&dev->base, "Setting current profile (%#02x)\n",
__CMD_PROFILE);
profile.msg.parameters[0] = profile_type;
@@ -964,9 +1005,9 @@ hidpp10_fill_dpi_modes_16(struct hidpp10_device *dev,
struct _hidpp10_dpi_mode_16 *dpi = &dpi_list[i];
be = (uint8_t*)&dpi->xres;
profile->dpi_modes[i].xres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_be_u16(be));
profile->dpi_modes[i].xres = hidpp10_get_dpi_value(dev, get_unaligned_be_u16(be));
be = (uint8_t*)&dpi->yres;
profile->dpi_modes[i].yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_be_u16(be));
profile->dpi_modes[i].yres = hidpp10_get_dpi_value(dev, get_unaligned_be_u16(be));
profile->dpi_modes[i].led[0] = dpi->led1 == 0x2;
profile->dpi_modes[i].led[1] = dpi->led2 == 0x2;
@@ -988,9 +1029,9 @@ hidpp10_write_dpi_modes_16(struct hidpp10_device *dev,
struct _hidpp10_dpi_mode_16 *dpi = &dpi_list[i];
be = (uint8_t*)&dpi->xres;
hidpp_set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].xres));
set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].xres));
be = (uint8_t*)&dpi->yres;
hidpp_set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].yres));
set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].yres));
dpi->led1 = profile->dpi_modes[i].led[0] ? 0x02 : 0x01;
dpi->led2 = profile->dpi_modes[i].led[1] ? 0x02 : 0x01;
@@ -1107,6 +1148,9 @@ hidpp10_onboard_profiles_read_macro(struct hidpp10_device *device,
}
if (rc == -ENOMEM) {
if ((unsigned) offset + mem_index > 0xff)
goto out_err;
offset += mem_index;
if (offset & 0x01)
offset--;
@@ -1161,8 +1205,10 @@ hidpp10_onboard_profiles_parse_macro(struct hidpp10_device *device,
hidpp_log_raw(&device->base, "*** macro starts at (0x%02x, 0x%04x) ***\n", page, offset);
rc = hidpp10_onboard_profiles_read_macro(device, page, offset, &macro);
if (rc < 0)
if (rc) {
hidpp_log_raw(&device->base, "hidpp10: failed to read macro\n");
return rc;
}
count = rc;
@@ -1532,57 +1578,62 @@ hidpp10_read_profile(struct hidpp10_device *dev, uint8_t number)
}
profile->initialized = 1;
hidpp_log_buf_raw(&dev->base,
"+++++++++++++++++++ Profile data: +++++++++++++++++ \n",
data->data, 78);
hidpp_log_raw(&dev->base,
"+++++++++++++++++++ Profile data: +++++++++++++++++ \n");
for (size_t x = 0; x < 78; x += 8) {
hidpp_log_buf_raw(&dev->base, NULL, &data->data[x], min(8, 78 - x));
}
hidpp_log_raw(&dev->base,
"+++++++++++++++++++ Profile data end +++++++++++++++++ \n");
}
hidpp_log_raw(&dev->base, "Profile %d:\n", number);
for (i = 0; i < 5; i++) {
hidpp_log_raw(&dev->base,
"DPI mode: %dx%d dpi\n",
" DPI mode: %dx%d dpi\n",
profile->dpi_modes[i].xres,
profile->dpi_modes[i].yres);
hidpp_log_raw(&dev->base,
"LED status: 1:%s 2:%s 3:%s 4:%s\n",
" LED status: 1:%s 2:%s 3:%s 4:%s\n",
profile->dpi_modes[i].led[0] ? "on" : "off",
profile->dpi_modes[i].led[1] ? "on" : "off",
profile->dpi_modes[i].led[2] ? "on" : "off",
profile->dpi_modes[i].led[3] ? "on" : "off");
}
hidpp_log_raw(&dev->base, "Angle correction: %d\n", profile->angle_correction);
hidpp_log_raw(&dev->base, "Default DPI mode: %d\n", profile->default_dpi_mode);
hidpp_log_raw(&dev->base, "Refresh rate: %d\n", profile->refresh_rate);
hidpp_log_raw(&dev->base, " Angle correction: %d\n", profile->angle_correction);
hidpp_log_raw(&dev->base, " Default DPI mode: %d\n", profile->default_dpi_mode);
hidpp_log_raw(&dev->base, " Refresh rate: %d\n", profile->refresh_rate);
for (i = 0; i < 13; i++) {
union hidpp10_button *button = &profile->buttons[i];
switch (button->any.type) {
case PROFILE_BUTTON_TYPE_BUTTON:
hidpp_log_raw(&dev->base,
"Button %zd: button %d\n",
" Button %zd: button %d\n",
i,
button->button.button);
break;
case PROFILE_BUTTON_TYPE_KEYS:
hidpp_log_raw(&dev->base,
"Button %zd: key %d modifier %x\n",
" Button %zd: key %d modifier %x\n",
i,
button->keys.key,
button->keys.modifier_flags);
break;
case PROFILE_BUTTON_TYPE_SPECIAL:
hidpp_log_raw(&dev->base,
"Button %zd: special %x\n",
" Button %zd: special %x\n",
i,
button->special.special);
break;
case PROFILE_BUTTON_TYPE_CONSUMER_CONTROL:
hidpp_log_raw(&dev->base,
"Button %zd: consumer: %x\n",
" Button %zd: consumer: %x\n",
i,
button->consumer_control.consumer_control);
break;
case PROFILE_BUTTON_TYPE_DISABLED:
hidpp_log_raw(&dev->base, "Button %zd: disabled\n", i);
hidpp_log_raw(&dev->base, " Button %zd: disabled\n", i);
break;
default:
/* FIXME: this is the page number for the macro,
@@ -1757,7 +1808,7 @@ hidpp10_set_profile(struct hidpp10_device *dev, uint8_t number, struct hidpp10_p
}
crc = hidpp_crc_ccitt(page_data, HIDPP10_PAGE_SIZE - 2);
hidpp_set_unaligned_be_u16(&page_data[HIDPP10_PAGE_SIZE - 2], crc);
set_unaligned_be_u16(&page_data[HIDPP10_PAGE_SIZE - 2], crc);
/*
* writing the data in several steps to prevent shroedinger state
@@ -1851,7 +1902,8 @@ hidpp10_get_led_status(struct hidpp10_device *dev,
uint8_t *status = led_status.msg.parameters;
int res;
hidpp_log_raw(&dev->base, "Fetching LED status\n");
hidpp_log_raw(&dev->base, "Fetching LED status (%#02x)\n",
__CMD_LED_STATUS);
res = hidpp10_request_command(dev, &led_status);
if (res)
@@ -1877,7 +1929,8 @@ hidpp10_set_led_status(struct hidpp10_device *dev,
int res;
int i;
hidpp_log_raw(&dev->base, "Setting LED status\n");
hidpp_log_raw(&dev->base, "Setting LED status (%#02x)\n",
__CMD_LED_STATUS);
for (i = 0; i < 6; i++) {
switch (led[i]) {
@@ -1928,7 +1981,8 @@ hidpp10_get_led_intensity(struct hidpp10_device *dev,
uint8_t *intensity = led_intensity.msg.parameters;
int res;
hidpp_log_raw(&dev->base, "Fetching LED intensity\n");
hidpp_log_raw(&dev->base, "Fetching LED intensity (%#02x)\n",
__CMD_LED_INTENSITY);
res = hidpp10_request_command(dev, &led_intensity);
if (res)
@@ -1953,7 +2007,8 @@ hidpp10_set_led_intensity(struct hidpp10_device *dev,
uint8_t *intensity = led_intensity.msg.parameters;
int res;
hidpp_log_raw(&dev->base, "Setting LED intensity\n");
hidpp_log_raw(&dev->base, "Setting LED intensity (%#02x)\n",
__CMD_LED_INTENSITY);
intensity[0] = led_intensity_in_percent[0]/10 & 0xF;
intensity[0] |= (led_intensity_in_percent[1]/10 & 0xF) << 4;
@@ -1992,7 +2047,8 @@ hidpp10_get_led_color(struct hidpp10_device *dev,
union hidpp10_message led_color = CMD_LED_COLOR(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching LED color\n");
hidpp_log_raw(&dev->base, "Fetching LED color (%#02x)\n",
__CMD_LED_COLOR);
res = hidpp10_request_command(dev, &led_color);
if (res)
@@ -2015,7 +2071,8 @@ hidpp10_set_led_color(struct hidpp10_device *dev,
union hidpp10_message led_color = CMD_LED_COLOR(idx, SET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting LED color\n");
hidpp_log_raw(&dev->base, "Setting LED color (%#02x)\n",
__CMD_LED_COLOR);
led_color.msg.parameters[0] = red;
led_color.msg.parameters[1] = green;
@@ -2048,7 +2105,8 @@ hidpp10_get_optical_sensor_settings(struct hidpp10_device *dev,
union hidpp10_message sensor = CMD_OPTICAL_SENSOR_SETTINGS(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching optical sensor settings\n");
hidpp_log_raw(&dev->base, "Fetching optical sensor settings (%#02x)\n",
__CMD_OPTICAL_SENSOR_SETTINGS);
res = hidpp10_request_command(dev, &sensor);
if (res)
@@ -2086,7 +2144,8 @@ hidpp10_get_current_resolution(struct hidpp10_device *dev,
union hidpp10_message resolution_long = CMD_CURRENT_RESOLUTION(REPORT_ID_SHORT, idx, GET_LONG_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching current resolution\n");
hidpp_log_raw(&dev->base, "Fetching current resolution (%#02x)\n",
__CMD_CURRENT_RESOLUTION);
switch (dev->profile_type) {
case HIDPP10_PROFILE_G9:
@@ -2094,15 +2153,15 @@ hidpp10_get_current_resolution(struct hidpp10_device *dev,
if (res)
return res;
/* resolution is in 50dpi multiples */
*xres = *yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution.data[4]));
*xres = *yres = hidpp10_get_dpi_value(dev, get_unaligned_le_u16(&resolution.data[4]));
break;
default:
res = hidpp10_request_command(dev, &resolution_long);
if (res)
return res;
/* resolution is in 50dpi multiples */
*xres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution_long.data[4]));
*yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution_long.data[6]));
*xres = hidpp10_get_dpi_value(dev, get_unaligned_le_u16(&resolution_long.data[4]));
*yres = hidpp10_get_dpi_value(dev, get_unaligned_le_u16(&resolution_long.data[6]));
}
return 0;
@@ -2118,14 +2177,17 @@ hidpp10_set_current_resolution(struct hidpp10_device *dev,
union hidpp10_message resolution_long = CMD_CURRENT_RESOLUTION(REPORT_ID_LONG, idx, SET_LONG_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting current resolution (%#02x)\n",
__CMD_CURRENT_RESOLUTION);
switch (dev->profile_type) {
case HIDPP10_PROFILE_G9:
resolution.data[4] = hidpp10_get_dpi_mapping(dev, xres);
res = hidpp10_request_command(dev, &resolution);
break;
default:
hidpp_set_unaligned_le_u16(&resolution_long.data[4], hidpp10_get_dpi_mapping(dev, xres));
hidpp_set_unaligned_le_u16(&resolution_long.data[6], hidpp10_get_dpi_mapping(dev, yres));
set_unaligned_le_u16(&resolution_long.data[4], hidpp10_get_dpi_mapping(dev, xres));
set_unaligned_le_u16(&resolution_long.data[6], hidpp10_get_dpi_mapping(dev, yres));
res = hidpp10_request_command(dev, &resolution_long);
break;
@@ -2157,7 +2219,8 @@ hidpp10_get_usb_refresh_rate(struct hidpp10_device *dev,
union hidpp10_message refresh = CMD_USB_REFRESH_RATE(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching USB refresh rate\n");
hidpp_log_raw(&dev->base, "Fetching USB refresh rate (%#02x)\n",
__CMD_USB_REFRESH_RATE);
res = hidpp10_request_command(dev, &refresh);
if (res)
@@ -2178,7 +2241,8 @@ hidpp10_set_usb_refresh_rate(struct hidpp10_device *dev,
unsigned idx = dev->index;
union hidpp10_message refresh = CMD_USB_REFRESH_RATE(idx, GET_REGISTER_REQ);
hidpp_log_raw(&dev->base, "Setting USB refresh rate\n");
hidpp_log_raw(&dev->base, "Setting USB refresh rate (%#02x)\n",
__CMD_USB_REFRESH_RATE);
refresh.msg.parameters[0] = 1000/rate;
@@ -2483,7 +2547,7 @@ hidpp10_read_page(struct hidpp10_device *dev, uint8_t page,
}
crc = hidpp_crc_ccitt(bytes, HIDPP10_PAGE_SIZE - 2);
read_crc = hidpp_get_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2]);
read_crc = get_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2]);
if (crc != read_crc)
return -EILSEQ; /* return illegal sequence */
@@ -2573,7 +2637,7 @@ hidpp10_get_pairing_information(struct hidpp10_device *dev,
return -1;
*report_interval = pairing_information.msg.string[2];
*wpid = hidpp_get_unaligned_be_u16(&pairing_information.msg.string[3]);
*wpid = get_unaligned_be_u16(&pairing_information.msg.string[3]);
*device_type = pairing_information.msg.string[7];
return 0;
@@ -2613,7 +2677,7 @@ hidpp10_get_extended_pairing_information(struct hidpp10_device *dev,
if (res)
return -1;
*serial = hidpp_get_unaligned_be_u32(&info.msg.string[1]);
*serial = get_unaligned_be_u32(&info.msg.string[1]);
return 0;
}
@@ -2665,7 +2729,7 @@ hidpp10_get_firmare_information(struct hidpp10_device *dev,
res = hidpp10_request_command(dev, &build_information);
if (res)
return res;
build = hidpp_get_unaligned_be_u16(&build_information.msg.string[1]);
build = get_unaligned_be_u16(&build_information.msg.string[1]);
*major_out = maj;
*minor_out = min;
@@ -2696,16 +2760,17 @@ hidpp10_get_device_info(struct hidpp10_device *dev)
hidpp10_get_optical_sensor_settings(dev, &reflect);
hidpp10_get_current_profile(dev, &current_profile);
return 0;
return hidpp10_get_current_profile(dev, &current_profile);
}
struct hidpp10_device*
int
hidpp10_device_new(const struct hidpp_device *base,
int idx,
enum hidpp10_profile_type type,
unsigned int profile_count)
unsigned int profile_count,
struct hidpp10_device **out)
{
int rc;
struct hidpp10_device *dev;
dev = zalloc(sizeof(*dev));
@@ -2716,12 +2781,14 @@ hidpp10_device_new(const struct hidpp_device *base,
dev->profile_count = profile_count;
dev->profiles = zalloc(dev->profile_count * sizeof(struct hidpp10_profile));
if (hidpp10_get_device_info(dev) != 0) {
if ((rc = hidpp10_get_device_info(dev)) != 0) {
hidpp10_device_destroy(dev);
dev = NULL;
}
return dev;
*out = dev;
return rc;
}
int

View File

@@ -69,11 +69,12 @@ struct hidpp10_device {
unsigned int profile_count;
};
struct hidpp10_device*
int
hidpp10_device_new(const struct hidpp_device *base,
int idx,
enum hidpp10_profile_type type,
unsigned int profile_count);
unsigned int profile_count,
struct hidpp10_device **out);
int
hidpp10_device_read_profiles(struct hidpp10_device *dev);

File diff suppressed because it is too large Load Diff

View File

@@ -51,6 +51,12 @@ struct hidpp20_feature {
uint8_t type;
};
enum hidpp20_quirk {
HIDPP20_QUIRK_NONE,
HIDPP20_QUIRK_G305,
HIDPP20_QUIRK_G602,
};
struct hidpp20_device {
struct hidpp_device base;
unsigned int index;
@@ -58,18 +64,25 @@ struct hidpp20_device {
unsigned proto_minor;
unsigned feature_count;
struct hidpp20_feature *feature_list;
enum hidpp20_quirk quirk;
unsigned int led_ext_caps;
};
int hidpp20_request_command(struct hidpp20_device *dev, union hidpp20_message *msg);
#define CASE_RETURN_STRING(a) case a: return #a; break
const char *hidpp20_feature_get_name(uint16_t feature);
const char *hidpp20_get_quirk_string(enum hidpp20_quirk quirk);
/* -------------------------------------------------------------------------- */
/* generic hidpp20 device operations */
/* -------------------------------------------------------------------------- */
struct hidpp20_device *
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx);
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx,
struct hidpp_hid_report *reports, unsigned int num_reports);
void
hidpp20_device_destroy(struct hidpp20_device *device);
@@ -138,6 +151,25 @@ int hidpp20_batterylevel_get_battery_level(struct hidpp20_device *device,
uint16_t *level,
uint16_t *next_level);
/* -------------------------------------------------------------------------- */
/* 0x1001: Battery Voltage */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_BATTERY_VOLTAGE 0x1001
enum hidpp20_battery_voltage_status {
BATTERY_VOLTAGE_STATUS_DISCHARGING = 0,
BATTERY_VOLTAGE_STATUS_WIRELESS_CHARGING = 0x10,
BATTERY_VOLTAGE_STATUS_CHARGING = 0x80,
};
/**
* Retrieves the battery voltage
*
* @return the battery status or a negative errno on error
*/
int hidpp20_batteryvoltage_get_battery_voltage(struct hidpp20_device *device,
uint16_t *voltage);
/* -------------------------------------------------------------------------- */
/* 0x1300: LED software control */
/* -------------------------------------------------------------------------- */
@@ -401,6 +433,12 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *device,
uint8_t *bitflags_ms);
int hidpp20_adjustable_report_rate_get_report_rate(struct hidpp20_device *device,
uint8_t *rate_ms);
int hidpp20_adjustable_report_rate_set_report_rate(struct hidpp20_device *device,
uint8_t rate_ms);
/* -------------------------------------------------------------------------- */
/* 0x8070v4 - Color LED effects */
/* -------------------------------------------------------------------------- */
@@ -439,6 +477,43 @@ hidpp20_color_led_effect_get_zone_effect_info(struct hidpp20_device *device,
uint8_t zone_effect_index,
struct hidpp20_color_led_zone_effect_info *info);
struct hidpp20_rgb_device_info;
int
hidpp20_rgb_effects_get_device_info(struct hidpp20_device *device,
struct hidpp20_rgb_device_info *info);
struct hidpp20_rgb_cluster_info;
int
hidpp20_rgb_effects_get_cluster_info(struct hidpp20_device *device,
uint8_t cluster_index,
struct hidpp20_rgb_cluster_info *info);
int
hidpp20_rgb_effects_get_cluster_infos(struct hidpp20_device *device,
struct hidpp20_rgb_cluster_info **infos_list);
struct hidpp20_rgb_effect_info;
int
hidpp20_rgb_effects_get_effect_info(struct hidpp20_device *device,
uint8_t cluster_index,
uint8_t effect_index,
struct hidpp20_rgb_effect_info *info);
struct hidpp20_led;
int
hidpp20_color_led_effects_set_zone_effect(struct hidpp20_device *device,
uint8_t zone_index,
struct hidpp20_led led);
int
hidpp20_color_led_effects_get_zone_effect(struct hidpp20_device *device,
uint8_t zone_index,
struct hidpp20_led *led);
enum hidpp20_color_led_location {
HIDPP20_COLOR_LED_LOCATION_UNDEFINED = 0,
HIDPP20_COLOR_LED_LOCATION_PRIMARY,
@@ -472,22 +547,72 @@ enum hidpp20_color_led_zone_effect {
HIDPP20_COLOR_LED_ZONE_EFFECT_BOOT_UP = 8,
HIDPP20_COLOR_LED_ZONE_EFFECT_DEMO_MODE = 8,
HIDPP20_COLOR_LED_ZONE_EFFECT_BREATHING = 10,
HIDPP20_COLOR_LED_ZONE_EFFECT_RIPPLE = 11,
HIDPP20_COLOR_LED_ZONE_EFFECT_CUSTOM = 12,
};
/* -------------------------------------------------------------------------- */
/* 0x8071 - RGB Effects */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_RGB_EFFECTS 0x8071
#define HIDPP20_RGB_EFFECTS_INDEX_ALL 0xFF
/* type of info */
#define HIDPP20_RGB_EFFECTS_TOI_GENERAL 0x00
#define HIDPP20_RGB_EFFECTS_TOI_EFFECT 0x01
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_STATE 0x00
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_DEFAULTS 0x01
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_UUID_0_10 0x02
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_UUID_11_16 0x03
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_0_10 0x04
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_11_21 0x05
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_21_31 0x06
/* -------------------------------------------------------------------------- */
/* 0x8100 - Onboard Profiles */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_ONBOARD_PROFILES 0x8100
/* type */
#define HIDPP20_BUTTON_HID_TYPE 0x80
#define HIDPP20_BUTTON_SPECIAL 0x90
/* standard opcodes (0x80) */
#define HIDPP20_BUTTON_HID_TYPE_NOOP 0x00
#define HIDPP20_BUTTON_HID_TYPE_MOUSE 0x01
#define HIDPP20_BUTTON_HID_TYPE_KEYBOARD 0x02
#define HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL 0x03
#define HIDPP20_BUTTON_SPECIAL 0x90
/* special opcodes (0x90) */
#define HIDPP20_BUTTON_SPECIAL_NOOP 0x00
#define HIDPP20_BUTTON_SPECIAL_TILT_LEFT 0x01
#define HIDPP20_BUTTON_SPECIAL_TILT_RIGHT 0x02
#define HIDPP20_BUTTON_SPECIAL_NEXT_DPI 0x03
#define HIDPP20_BUTTON_SPECIAL_PREV_DPI 0x04
#define HIDPP20_BUTTON_SPECIAL_CYCLE_DPI 0x05
#define HIDPP20_BUTTON_SPECIAL_DEFAULT_DPI 0x06
#define HIDPP20_BUTTON_SPECIAL_SHIFT_DPI 0x07
#define HIDPP20_BUTTON_SPECIAL_NEXT_PROFILE 0x08
#define HIDPP20_BUTTON_SPECIAL_PREV_PROFILE 0x09
#define HIDPP20_BUTTON_SPECIAL_CYCLE_PROFILE 0x0a
#define HIDPP20_BUTTON_SPECIAL_GSHIFT 0x0b
/* memory model >= 3 */
#define HIDPP20_BUTTON_SPECIAL_BATTERY_INDICATOR 0x0c
#define HIDPP20_BUTTON_SPECIAL_ENABLE_PROFILE 0x0d
#define HIDPP20_BUTTON_SPECIAL_PERFORMANCE_SWITCH 0x0e
#define HIDPP20_BUTTON_SPECIAL_HOST 0x0f
#define HIDPP20_BUTTON_SPECIAL_SCROLL_DOWN 0x10
#define HIDPP20_BUTTON_SPECIAL_SCROLL_UP 0x11
/* buttons */
#define HIDPP20_BUTTON_MACRO 0x00
#define HIDPP20_BUTTON_DISABLED 0xFF
/* modifiers */
#define HIDPP20_MODIFIER_KEY_CTRL 0x01
#define HIDPP20_MODIFIER_KEY_SHIFT 0x02
@@ -521,6 +646,8 @@ union hidpp20_button_binding {
struct {
uint8_t type; /* HIDPP20_BUTTON_SPECIAL */
uint8_t special;
uint8_t reserved;
uint8_t profile;
} __attribute__((packed)) special;
struct {
uint8_t type; /* HIDPP20_BUTTON_MACRO */
@@ -559,11 +686,6 @@ struct hidpp20_color_led_zone_info {
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_info) == 5, "Invalid size");
union hidpp20_generic_led_zone_info {
struct hidpp20_led_sw_ctrl_led_info* leds;
struct hidpp20_color_led_zone_info* color_leds;
};
struct hidpp20_color_led_zone_effect_info {
uint8_t zone_index;
uint8_t zone_effect_index;
@@ -573,6 +695,41 @@ struct hidpp20_color_led_zone_effect_info {
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_effect_info) == 8, "Invalid size");
struct hidpp20_rgb_device_info {
uint8_t cluster_index;
uint8_t effect_index;
uint8_t cluster_count;
/* we don't care about NV capabilities for libratbag, they just
* indicate sale demo effects */
uint16_t nv_caps;
uint16_t ext_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_device_info) == 7, "Invalid size");
struct hidpp20_rgb_cluster_info {
uint8_t index;
uint8_t effect_index;
uint16_t location;
uint16_t num_effects;
uint16_t persistency_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_cluster_info) == 8, "Invalid size");
struct hidpp20_rgb_effect_info {
uint8_t cluster_index;
uint8_t effect_index;
uint16_t effect_id;
uint16_t capabilities;
uint16_t effect_period;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_effect_info) == 8, "Invalid size");
union hidpp20_generic_led_zone_info {
struct hidpp20_led_sw_ctrl_led_info* leds;
struct hidpp20_color_led_zone_info* color_leds_8070;
struct hidpp20_rgb_cluster_info* color_leds_8071;
};
struct hidpp20_color {
uint8_t red;
uint8_t green;
@@ -590,7 +747,11 @@ enum hidpp20_led_mode {
HIDPP20_LED_OFF = 0x00,
HIDPP20_LED_ON = 0x01,
HIDPP20_LED_CYCLE = 0x03,
HIDPP20_LED_COLOR_WAVE = 0x04,
HIDPP20_LED_STARLIGHT = 0x05,
HIDPP20_LED_BREATHING = 0x0a,
HIDPP20_LED_RIPPLE = 0x0b,
HIDPP20_LED_CUSTOM = 0x0c,
};
enum hidpp20_led_waveform {
@@ -615,35 +776,76 @@ struct hidpp20_internal_led {
uint16_t period_or_speed; /* period in ms, speed is device dependent */
uint8_t intensity; /* 1 - 100 percent, 0 means 100 */
} __attribute__((packed)) cycle;
struct hidpp20_led_starlight {
struct hidpp20_color color_sky;
struct hidpp20_color color_star;
} __attribute__((packed)) starlight;
struct hidpp20_led_breath {
struct hidpp20_color color;
uint16_t period_or_speed; /* period in ms, speed is device dependent */
uint8_t waveform; /* enum hidpp20_led_waveform */
uint8_t intensity; /* 1 - 100 percent, 0 means 100 */
} __attribute__((packed)) breath;
struct hidpp20_led_ripple {
struct hidpp20_color color;
uint8_t reserved;
uint16_t period;
} __attribute__((packed)) ripple;
struct hidpp20_led_custom {
uint8_t slot;
uint16_t init_frame;
uint16_t lenght;
uint16_t frame_period;
uint8_t intensity;
} __attribute__((packed)) custom;
uint8_t padding[10];
} __attribute__((packed)) effect;
};
_Static_assert(sizeof(struct hidpp20_led_fixed) == 4, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_cycle) == 8, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_starlight) == 6, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_breath) == 7, "Invalid size");
_Static_assert(sizeof(struct hidpp20_internal_led) == 11, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_ripple) == 6, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_custom) == 8, "Invalid size");
typedef uint8_t percent_t;
struct hidpp20_led {
enum hidpp20_led_mode mode;
struct hidpp20_color color;
struct hidpp20_color extra_color;
uint16_t period;
percent_t brightness;
uint8_t original[sizeof(struct hidpp20_internal_led)];
};
#define HIDPP20_MACRO_NOOP 0x01
/* macro opcodes */
/* control commands */
#define HIDPP20_MACRO_WAIT_FOR_RELEASE 0x01
#define HIDPP20_MACRO_REPEAT_WHILE_PRESSED 0x02
#define HIDPP20_MACRO_REPEAT_UNTIL_CANCELED 0x03
#define HIDPP20_MACRO_END 0xff
/* delay & jump commands */
#define HIDPP20_MACRO_NOOP 0x00 /* wait the duration of a macro step */
#define HIDPP20_MACRO_DELAY 0x40
#define HIDPP20_MACRO_JUMP 0x60
/* button & key event generation */
#define HIDPP20_MACRO_BUTTON_DOWN 0x41
#define HIDPP20_MACRO_BUTTON_UP 0x42
#define HIDPP20_MACRO_KEY_PRESS 0x43
#define HIDPP20_MACRO_KEY_RELEASE 0x44
#define HIDPP20_MACRO_JUMP 0x60
#define HIDPP20_MACRO_END 0xff
#define HIDPP20_MACRO_CONS_DOWN 0x45
#define HIDPP20_MACRO_CONS_UP 0x46
/* analog commands */
#define HIDPP20_MACRO_ROLLER 0x20
#define HIDPP20_MACRO_ACPAN 0x21
#define HIDPP20_MACRO_XY 0x61
union hidpp20_macro_data {
struct {
@@ -673,6 +875,8 @@ struct hidpp20_profile {
uint16_t address;
uint8_t enabled;
char name[16 * 3];
uint16_t powersave_timeout;
uint16_t poweroff_timeout;
unsigned report_rate;
unsigned default_dpi;
unsigned switched_dpi;
@@ -681,6 +885,7 @@ struct hidpp20_profile {
union hidpp20_button_binding buttons[32];
union hidpp20_macro_data *macros[32];
struct hidpp20_led leds[HIDPP20_LED_COUNT];
struct hidpp20_led alt_leds[HIDPP20_LED_COUNT];
};
struct hidpp20_onboard_profiles_info {
@@ -808,6 +1013,14 @@ hidpp20_onboard_profiles_allocate_sector(struct hidpp20_profiles *profiles)
return zalloc(profiles->sector_size);
}
void
hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
struct hidpp20_internal_led internal_led);
void
hidpp20_onboard_profiles_write_led(struct hidpp20_internal_led *internal_led,
struct hidpp20_led *led);
/* -------------------------------------------------------------------------- */
/* 0x8110 - Mouse Button Spy */
/* -------------------------------------------------------------------------- */

View File

@@ -140,20 +140,21 @@ hidraw_is_receiver(int fd)
return true;
}
static struct hidpp10_device *
hidpp10_init(int fd)
static int
hidpp10_init(int fd, struct hidpp10_device **out)
{
struct hidpp_device base;
hidpp_device_init(&base, fd);
return hidpp10_device_new(&base, HIDPP_RECEIVER_IDX,
HIDPP10_PROFILE_UNKNOWN, 1);
HIDPP10_PROFILE_UNKNOWN, 1, out);
}
_EXPORT_ int
lur_receiver_new_from_hidraw(int fd, void *userdata, struct lur_receiver **out)
{
int rc;
struct lur_receiver *receiver;
if (!hidraw_is_receiver(fd))
@@ -165,8 +166,8 @@ lur_receiver_new_from_hidraw(int fd, void *userdata, struct lur_receiver **out)
receiver->userdata = userdata;
list_init(&receiver->devices);
receiver->hidppdev = hidpp10_init(fd);
if (!receiver->hidppdev)
rc = hidpp10_init(fd, &receiver->hidppdev);
if (rc)
goto error;
*out = receiver;
@@ -174,8 +175,7 @@ lur_receiver_new_from_hidraw(int fd, void *userdata, struct lur_receiver **out)
error:
free(receiver);
return -errno;
return rc;
}
_EXPORT_ int
@@ -203,7 +203,7 @@ lur_receiver_enumerate(struct lur_receiver *lur,
uint32_t serial;
bool is_new_device = true;
d = hidpp10_device_new(&base, i, HIDPP10_PROFILE_UNKNOWN, 1);
rc = hidpp10_device_new(&base, i, HIDPP10_PROFILE_UNKNOWN, 1, &d);
if (!d)
continue;

View File

@@ -33,6 +33,7 @@
#include "libratbag.h"
#include "libratbag-private.h"
#include "libratbag-data.h"
#include "hidpp20.h"
#define GROUP_DEVICE "Device"
@@ -50,10 +51,14 @@ enum driver {
LOGITECH_G300,
LOGITECH_G600,
STEELSERIES,
SINOWEALTH,
SINOWEALTH_NUBWO,
};
struct data_hidpp20 {
int index;
enum hidpp20_quirk quirk;
int led_count;
};
struct data_hidpp10 {
@@ -77,6 +82,13 @@ struct data_steelseries {
int short_button;
};
struct data_sinowealth {
int button_count;
int led_count;
char *led_type;
struct dpi_range *dpi_range;
};
struct ratbag_device_data {
int refcount;
char *name;
@@ -88,6 +100,7 @@ struct ratbag_device_data {
struct data_hidpp20 hidpp20;
struct data_hidpp10 hidpp10;
struct data_steelseries steelseries;
struct data_sinowealth sinowealth;
};
enum ratbag_led_type led_types[20];
@@ -152,14 +165,32 @@ init_data_hidpp20(struct ratbag *ratbag,
const char *group = "Driver/hidpp20";
GError *error = NULL;
int num;
char *str;
data->hidpp20.index = -1;
data->hidpp20.led_count = -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);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "Leds", &error);
if (!error)
data->hidpp20.led_count = num;
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;
}
}
static void
@@ -231,6 +262,50 @@ init_data_steelseries(struct ratbag *ratbag,
g_error_free(error);
}
static void
init_data_sinowealth(struct ratbag *ratbag,
GKeyFile *keyfile,
struct ratbag_device_data *data)
{
const char *group = "Driver/sinowealth";
GError *error = NULL;
_cleanup_(freep) char *str = NULL;
int num;
char *led_type;
data->sinowealth.button_count = -1;
data->sinowealth.led_count = -1;
num = g_key_file_get_integer(keyfile, group, "Buttons", &error);
if (num != 0 || !error)
data->sinowealth.button_count = num;
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "Leds", &error);
if (num > 0 || !error)
data->sinowealth.led_count = num;
if (error)
g_error_free(error);
error = NULL;
led_type = g_key_file_get_string(keyfile, group, "LedType", NULL);
if (led_type)
data->sinowealth.led_type = led_type;
if (error)
g_error_free(error);
error = NULL;
str = g_key_file_get_string(keyfile, group, "DpiRange", NULL);
if (str)
data->sinowealth.dpi_range = dpi_range_from_string(str);
error = NULL;
}
static const struct driver_map {
enum driver map;
const char *driver;
@@ -246,6 +321,8 @@ static const struct driver_map {
{ LOGITECH_G300, "logitech_g300", NULL},
{ LOGITECH_G600, "logitech_g600", NULL},
{ STEELSERIES, "steelseries", init_data_steelseries },
{ SINOWEALTH, "sinowealth", init_data_sinowealth },
{ SINOWEALTH_NUBWO, "sinowealth_nubwo", NULL},
};
const char *
@@ -334,6 +411,8 @@ parse_ledtypes(char **strv, enum ratbag_led_type *types, size_t ntypes)
types[i] = RATBAG_LED_TYPE_SIDE;
else if (streq(s, "wheel"))
types[i] = RATBAG_LED_TYPE_WHEEL;
else if (streq(s, "switches"))
types[i] = RATBAG_LED_TYPE_SWITCHES;
else
return -1;
@@ -465,18 +544,16 @@ ratbag_device_data_new_for_id(struct ratbag *ratbag, const struct input_id *id)
struct dirent **files;
int n, nfiles;
const char *datadir;
char devicedir[PATH_MAX] = {0};
datadir = getenv("LIBRATBAG_DATA_DIR");
if (!datadir)
datadir = LIBRATBAG_DATA_DIR;
log_debug(ratbag, "Using data directory '%s'\n", datadir);
snprintf(devicedir, sizeof(devicedir), "%s/devices", datadir);
n = scandir(devicedir, &files, filter_device_files, alphasort);
n = scandir(datadir, &files, filter_device_files, alphasort);
if (n <= 0) {
log_error(ratbag, "Unable to locate device files in %s: %s\n",
devicedir, n == 0 ? "No files found" : strerror(errno));
datadir, n == 0 ? "No files found" : strerror(errno));
return NULL;
}
@@ -485,13 +562,16 @@ ratbag_device_data_new_for_id(struct ratbag *ratbag, const struct input_id *id)
_cleanup_(freep) char *file = NULL;
int rc;
rc = xasprintf(&file, "%s/%s", devicedir, files[n]->d_name);
rc = xasprintf(&file, "%s/%s", datadir, files[n]->d_name);
if (rc == -1)
goto out;
if (file_data_matches(ratbag, file, id, &data))
goto out;
}
if (!data)
log_debug(ratbag, "No data file found for %04x:%04x\n", id->vendor, id->product);
out:
while(nfiles--)
free(files[nfiles]);
@@ -561,6 +641,22 @@ ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data)
return data->hidpp20.index;
}
int
ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP20);
return data->hidpp20.led_count;
}
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP20);
return data->hidpp20.quirk;
}
/* SteelSeries */
int
@@ -626,3 +722,35 @@ ratbag_device_data_steelseries_get_short_button(const struct ratbag_device_data
return data->steelseries.short_button;
}
int
ratbag_device_data_sinowealth_get_button_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == SINOWEALTH);
return data->sinowealth.button_count;
}
int
ratbag_device_data_sinowealth_get_led_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == SINOWEALTH);
return data->sinowealth.led_count;
}
struct dpi_range *
ratbag_device_data_sinowealth_get_dpi_range(const struct ratbag_device_data *data)
{
assert(data->drivertype == SINOWEALTH);
return data->sinowealth.dpi_range;
}
const char *
ratbag_device_data_sinowealth_get_led_type(const struct ratbag_device_data *data)
{
assert(data->drivertype == SINOWEALTH);
return data->sinowealth.led_type;
}

View File

@@ -80,6 +80,12 @@ ratbag_device_data_hidpp10_get_led_count(const struct ratbag_device_data *data);
int
ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data);
int
ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data);
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data);
/* SteelSeries */
/**
@@ -114,3 +120,22 @@ ratbag_device_data_steelseries_get_mono_led(const struct ratbag_device_data *dat
int
ratbag_device_data_steelseries_get_short_button(const struct ratbag_device_data *data);
/**
* @return The button count or -1 if not set
*/
int
ratbag_device_data_sinowealth_get_button_count(const struct ratbag_device_data *data);
/**
* @return The led count or -1 if not set
*/
int
ratbag_device_data_sinowealth_get_led_count(const struct ratbag_device_data *data);
struct dpi_range *
ratbag_device_data_sinowealth_get_dpi_range(const struct ratbag_device_data *data);
const char *
ratbag_device_data_sinowealth_get_led_type(const struct ratbag_device_data *data);

View File

@@ -293,6 +293,7 @@ enum ratbag_led_type {
RATBAG_LED_TYPE_BATTERY,
RATBAG_LED_TYPE_DPI,
RATBAG_LED_TYPE_WHEEL,
RATBAG_LED_TYPE_SWITCHES,
};
/**

View File

@@ -1239,6 +1239,8 @@ ratbag_hidraw_parse_report_descriptor(struct ratbag_device *device)
if (rc < 0)
return rc;
log_debug(device->ratbag, "Parsing HID report descriptor\n");
i = 0;
usage_page = 0;
usage = 0;
@@ -1260,7 +1262,7 @@ ratbag_hidraw_parse_report_descriptor(struct ratbag_device *device)
switch (hid) {
case HID_REPORT_ID:
if (hidraw->reports) {
log_debug(device->ratbag, "report ID %02x\n", content);
log_debug(device->ratbag, "- HID report ID %02x\n", content);
hidraw->reports[hidraw->num_reports].report_id = content;
hidraw->reports[hidraw->num_reports].usage_page = usage_page;
hidraw->reports[hidraw->num_reports].usage = usage;
@@ -1328,6 +1330,10 @@ ratbag_open_hidraw_node(struct ratbag_device *device, struct udev_device *hidraw
goto err;
}
log_debug(device->ratbag,
"hidraw info: bus %#04x vendor %#04x product %#04x\n",
info.bustype, info.vendor, info.product);
/* check basic matching between the hidraw node and the ratbag device */
if (info.bustype != device->ids.bustype ||
(info.vendor & 0xFFFF )!= device->ids.vendor ||
@@ -1582,6 +1588,23 @@ ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char reportnum,
return -EINVAL;
}
int
ratbag_hidraw_get_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len)
{
return ratbag_hidraw_raw_request(device, reportnum, buf, len,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
}
int
ratbag_hidraw_set_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len)
{
return ratbag_hidraw_raw_request(device, reportnum, buf, len,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
}
int
ratbag_hidraw_output_report(struct ratbag_device *device, uint8_t *buf, size_t len)
{

View File

@@ -101,7 +101,7 @@ void ratbag_close_hidraw_index(struct ratbag_device *device, int idx);
*
* @param device the ratbag device
* @param reportnum report ID
* @param buf in/out data to transfer
* @param buf[in/out] data to transfer
* @param len length of buf
* @param rtype HID report type
* @param reqtype HID_REQ_GET_REPORT or HID_REQ_SET_REPORT
@@ -114,6 +114,41 @@ int ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char report
uint8_t *buf, size_t len, unsigned char rtype,
int reqtype);
/**
* Get feature report from device.
*
* This call sends a HID_REQ_GET_REPORT to the device for the given feature
* and returns the data in the caller-allocated buffer buf of size len.
*
* @param device the ratbag device
* @param reportnum report ID
* @param buf[out] data returned from device
* @param len length of buf
*
* @return count of data transfered, or a negative errno on error
*
* Convenience wrapper around ratbag_hidraw_raw_request()
*/
int ratbag_hidraw_get_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len);
/**
* Set feature report on device.
*
* This call sends a HID_REQ_SET_REPORT to the device for the given feature
* and the buffer buf of size len.
*
* @param device the ratbag device
* @param reportnum report ID
* @param buf[in] data returned from device
* @param len length of buf
*
* @return count of data transfered, or a negative errno on error
*
* Convenience wrapper around ratbag_hidraw_raw_request()
*/
int ratbag_hidraw_set_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len);
/**
* Send output report to device
*

Some files were not shown because too many files have changed in this diff Show More