1
0
forked from drath/libratbag

Compare commits

..

144 Commits

Author SHA1 Message Date
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
82 changed files with 2735 additions and 1260 deletions

View File

@@ -13,6 +13,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,7 +46,7 @@ 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
@@ -66,7 +78,7 @@ fedora_prep_cache: &fedora_prep_cache
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
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 libunistring-devel dbus-daemon json-glib-devel
- persist_to_workspace:
root: /var/cache/
paths:
@@ -82,8 +94,8 @@ fedora_install: &fedora_install
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 tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml python3-devel swig libunistring-devel dbus-daemon json-glib-devel
sed -i 's/systemd//' /etc/nsswitch.conf
fedora_settings: &fedora_settings
<<: *default_settings
@@ -96,6 +108,8 @@ fedora_settings: &fedora_settings
- *install
- *check_install
- *check_uninstall
- *build_buildtype_plain
- *build_buildtype_release
- *export_logs
@@ -107,15 +121,18 @@ ubuntu_settings: &ubuntu_settings
command: |
apt-get update
apt-get install -y software-properties-common
apt-get remove -y libnss-systemd
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 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 libunistring-dev libjson-glib-dev
- checkout
- *start_dbus
- *build_and_test
- *install
- *check_install
- *check_uninstall
- *build_buildtype_plain
- *build_buildtype_release
- *export_logs
doc_build: &doc_build
@@ -183,23 +200,23 @@ jobs:
fedora_cache:
<<: *fedora_prep_cache
docker:
- image: fedora:28
- image: fedora:30
fedora_latest:
<<: *fedora_settings
docker:
- image: fedora:28
ubuntu_17_10:
- image: fedora:30
ubuntu_19_04:
<<: *ubuntu_settings
docker:
- image: ubuntu:artful
- image: ubuntu:19.04
doc_build:
<<: *doc_build
docker:
- image: fedora:28
- image: fedora:30
doc_deploy:
<<: *docs_deploy
docker:
- image: fedora:28
- image: fedora:30
workflows:
@@ -208,7 +225,7 @@ workflows:
jobs:
# - fedora_rawhide
- fedora_cache
- ubuntu_17_10
- ubuntu_19_04
- fedora_latest:
requires:
- fedora_cache

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

@@ -26,9 +26,9 @@
#
import argparse
import os
import configparser
def assertIn(element, l):
if element not in l:
raise AssertionError('{} must be in {}'.format(element, l))
@@ -61,7 +61,7 @@ def check_ledtypes_str(string):
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))
@@ -169,7 +169,7 @@ def check_section_hidpp10(section):
def check_section_hidpp20(section):
permitted = ['DeviceIndex']
permitted = ['DeviceIndex', 'Quirk']
for key in section.keys():
assertIn(key, permitted)

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

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# 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

@@ -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
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=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

@@ -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

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

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

@@ -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

@@ -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

@@ -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

@@ -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,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.11',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.40.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',
@@ -181,6 +191,7 @@ deps_libratbag = [
dep_udev,
dep_libevdev,
dep_glib,
dep_json_glib,
dep_libutil,
dep_libhidpp,
]
@@ -241,6 +252,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,
@@ -269,8 +282,11 @@ data_files = files(
'data/devices/logitech-M325.device',
'data/devices/logitech-M570.device',
'data/devices/logitech-M705.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-pro-wireless.device',
@@ -279,35 +295,42 @@ data_files = files(
'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-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-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-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',
@@ -316,11 +339,14 @@ data_files = files(
)
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)
#### tests ####
enable_tests = get_option('tests')
if enable_tests
@@ -365,33 +391,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 +410,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 +433,34 @@ 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.
#
config_ratbagd_devel = configuration_data()
dbus_devel_policy = configure_file(input : 'dbus/org.freedesktop.ratbag_devel1.conf.in',
output : 'org.freedesktop.ratbag_devel1.conf',
configuration : config_ratbagd_devel)
# 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 +499,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',
@@ -512,15 +534,22 @@ else
py3 = pymod.find_installation()
endif
# ratbagctl calls ratbagd over DBus
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 +562,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',
@@ -566,8 +595,14 @@ swig_gen = generator(
'@INPUT@'],
)
# From python 3.8 we neeed python3-embed
dep_python3 = dependency('python3-embed', required: false)
if not dep_python3.found()
dep_python3 = dependency('python3')
endif
wrapper_deps = [
dependency('python3'),
dep_python3,
dep_libratbag,
dep_libshared,
]
@@ -585,20 +620,36 @@ shared_library(
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'],
extra_files: [ i_source ] + src_libratbag ,
dependencies: deps_libratbag + wrapper_deps,
include_directories : include_directories('src', 'tools'),
install: false,
)
# ratbagc is the layer that maps ratbagctl to the swig bindings
ratbagc_py_conf = configuration_data()
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 ####

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;
@@ -172,6 +174,7 @@ static void ratbagd_device_commit_pending(void *data)
r = ratbag_device_commit(device->lib_device);
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 +185,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 +233,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 +243,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 +286,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 +305,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)

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

@@ -0,0 +1,562 @@
/*
* 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_WHEEL)
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;
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;
g_autoptr(GList) 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[=raw]|\-\-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=raw
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

@@ -166,9 +166,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 +201,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 +284,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 +346,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)
@@ -521,6 +523,83 @@ static int ratbagd_run(struct ratbagd *ctx)
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;
}
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;
@@ -550,6 +629,9 @@ int main(int argc, char *argv[])
}
}
if (!install_ratbagd_devel_dbus_policy())
goto exit;
r = ratbagd_new(&ctx);
if (r < 0)
goto exit;
@@ -558,6 +640,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

@@ -412,9 +412,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)

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;
@@ -335,7 +339,7 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
struct hidpp20_color_led_info info;
int rc;
led_info = &drv_data->led_infos.color_leds[led->index];
led_info = &drv_data->led_infos.color_leds_8070[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
@@ -356,7 +360,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;
@@ -402,6 +406,81 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
}
}
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_bug_libratbag(led->profile->device->ratbag,
"%s: Unknown effect id %d\n",
led->profile->device->name,
ei.effect_id);
break;
}
}
}
static void
hidpp20drv_read_led(struct ratbag_led *led)
{
@@ -410,6 +489,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);
}
@@ -606,8 +687,8 @@ 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;
@@ -649,8 +730,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);
@@ -777,7 +859,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 +929,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;
}
@@ -897,6 +983,34 @@ out:
return rc;
}
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);
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)
return -ENOTSUP;
return -ENOTSUP;
}
static int
hidpp20drv_read_special_key_mouse(struct ratbag_device *device)
{
@@ -940,19 +1054,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 +1072,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)
{
@@ -1078,6 +1220,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 +1267,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,6 +1306,11 @@ 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;
@@ -1155,6 +1322,18 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
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;
/* 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_LED_SW_CONTROL: {
log_debug(ratbag, "device has non-rgb leds\n");
drv_data->capabilities |= HIDPP_CAP_LED_SW_CONTROL_1300;
@@ -1213,9 +1392,15 @@ hidpp20drv_commit(struct ratbag_device *device)
if (!profile->dirty)
continue;
rc = hidpp20drv_update_report_rate(profile, profile->hz);
if (rc)
return RATBAG_ERROR_DEVICE;
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->is_active)
if (resolution->is_active) {
log_raw(device->ratbag, "dpi index: %d, profile %d\n", resolution->index, profile->index);
dpi_index = resolution->index;
}
rc = hidpp20drv_update_resolution_dpi(resolution,
resolution->dpi_x,
@@ -1254,9 +1439,14 @@ hidpp20drv_commit(struct ratbag_device *device)
return RATBAG_ERROR_DEVICE;
list_for_each(profile, &device->profiles, link) {
if (profile->is_active)
if (profile->is_active) {
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->is_active)
dpi_index = resolution->index;
}
hidpp20_onboard_profiles_set_current_dpi_index(drv_data->dev,
dpi_index);
}
}
}
@@ -1318,7 +1508,8 @@ 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->led_infos.color_leds_8071);
free(drv_data->controls);
free(drv_data->sensors);
if (drv_data->dev)
@@ -1331,6 +1522,7 @@ hidpp20drv_init_device(struct ratbag_device *device,
struct hidpp20drv_data *drv_data)
{
struct ratbag_profile *profile;
bool active_profile = false;
ratbag_device_init_profiles(device,
drv_data->num_profiles,
@@ -1340,6 +1532,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
@@ -1378,10 +1582,15 @@ hidpp20drv_probe(struct ratbag_device *device)
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;

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

@@ -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;
@@ -322,6 +322,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 +344,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);
}

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
@@ -118,10 +120,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);
@@ -145,6 +147,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 +168,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,7 +803,8 @@ 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)
@@ -811,6 +833,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 +865,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 +1001,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 +1025,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;
@@ -1161,7 +1198,7 @@ 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)
return rc;
count = rc;
@@ -1532,57 +1569,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 +1799,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 +1893,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 +1920,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 +1972,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 +1998,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 +2038,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 +2062,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 +2096,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 +2135,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 +2144,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 +2168,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 +2210,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 +2232,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 +2538,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 +2628,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 +2668,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 +2720,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,8 +2751,7 @@ 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*

View File

@@ -48,7 +48,6 @@ hidpp20_feature_get_name(uint16_t feature)
{
static char numeric[8];
const char *str;
#define CASE_RETURN_STRING(a) case a: return #a; break
switch(feature)
{
@@ -58,6 +57,7 @@ hidpp20_feature_get_name(uint16_t feature)
CASE_RETURN_STRING(HIDPP_PAGE_DEVICE_NAME);
CASE_RETURN_STRING(HIDPP_PAGE_RESET);
CASE_RETURN_STRING(HIDPP_PAGE_BATTERY_LEVEL_STATUS);
CASE_RETURN_STRING(HIDPP_PAGE_BATTERY_VOLTAGE);
CASE_RETURN_STRING(HIDPP_PAGE_KBD_REPROGRAMMABLE_KEYS);
CASE_RETURN_STRING(HIDPP_PAGE_SPECIAL_KEYS_BUTTONS);
CASE_RETURN_STRING(HIDPP_PAGE_WIRELESS_DEVICE_STATUS);
@@ -65,6 +65,7 @@ hidpp20_feature_get_name(uint16_t feature)
CASE_RETURN_STRING(HIDPP_PAGE_ADJUSTABLE_DPI);
CASE_RETURN_STRING(HIDPP_PAGE_ADJUSTABLE_REPORT_RATE);
CASE_RETURN_STRING(HIDPP_PAGE_COLOR_LED_EFFECTS);
CASE_RETURN_STRING(HIDPP_PAGE_RGB_EFFECTS);
CASE_RETURN_STRING(HIDPP_PAGE_ONBOARD_PROFILES);
CASE_RETURN_STRING(HIDPP_PAGE_MOUSE_BUTTON_SPY);
default:
@@ -97,11 +98,22 @@ hidpp20_sw_led_control_get_mode_string(const enum hidpp20_led_sw_ctrl_led_mode m
str = numeric;
break;
}
#undef CASE_RETURN_STRING
return str;
}
const char*
hidpp20_get_quirk_string(enum hidpp20_quirk quirk)
{
switch (quirk) {
CASE_RETURN_STRING(HIDPP20_QUIRK_NONE);
CASE_RETURN_STRING(HIDPP20_QUIRK_G305);
CASE_RETURN_STRING(HIDPP20_QUIRK_G602);
}
abort();
}
static int
hidpp20_request_command_allow_error(struct hidpp20_device *device, union hidpp20_message *msg,
bool allow_error)
@@ -244,7 +256,7 @@ hidpp_root_get_feature(struct hidpp20_device *device,
.msg.address = CMD_ROOT_GET_FEATURE,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], feature);
set_unaligned_be_u16(&msg.msg.parameters[0], feature);
rc = hidpp20_request_command(device, &msg);
if (rc)
@@ -332,7 +344,7 @@ hidpp20_feature_set_get_feature_id(struct hidpp20_device *device,
if (rc)
return rc;
*feature = hidpp_get_unaligned_be_u16(msg.msg.parameters);
*feature = get_unaligned_be_u16(msg.msg.parameters);
*type = msg.msg.parameters[2];
return 0;
@@ -430,6 +442,41 @@ hidpp20_batterylevel_get_battery_level(struct hidpp20_device *device,
return msg.msg.parameters[2];
}
/* -------------------------------------------------------------------------- */
/* 0x1001: Battery voltage */
/* -------------------------------------------------------------------------- */
#define CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE 0x00
#define CMD_BATTERY_VOLTAGE_GET_SHOW_BATTERY_STATUS 0x10
int
hidpp20_batteryvoltage_get_battery_voltage(struct hidpp20_device *device,
uint16_t *voltage)
{
uint8_t feature_index;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE,
};
int rc;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_BATTERY_VOLTAGE);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
*voltage = get_unaligned_be_u16(msg.msg.parameters);
return msg.msg.parameters[2];
}
/* -------------------------------------------------------------------------- */
/* 0x1300: Non-RGB led support */
/* -------------------------------------------------------------------------- */
@@ -685,10 +732,10 @@ int hidpp20_led_sw_control_set_led_state(struct hidpp20_device* device,
return -EINVAL;
msg.msg.parameters[0] = state->index;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[1], state->mode);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[3], state->blink.index);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[5], state->blink.on_time);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[7], state->blink.off_time);
set_unaligned_be_u16(&msg.msg.parameters[1], state->mode);
set_unaligned_be_u16(&msg.msg.parameters[3], state->blink.index);
set_unaligned_be_u16(&msg.msg.parameters[5], state->blink.on_time);
set_unaligned_be_u16(&msg.msg.parameters[7], state->blink.off_time);
rc = hidpp20_request_command(device, &msg);
@@ -742,8 +789,8 @@ hidpp20_kbd_reprogrammable_keys_get_info(struct hidpp20_device *device,
if (rc)
return rc;
control->control_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[2]);
control->control_id = get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
control->flags = msg.msg.parameters[4];
return 0;
@@ -906,7 +953,7 @@ hidpp20_color_led_effects_get_zone_infos(struct hidpp20_device *device,
"led_info %d: location: %d type %s num_effects: %d persistency_caps: 0x%02x\n",
info->index,
info->location,
hidpp20_8070_get_location_mapping_name(info->location),
hidpp20_led_get_location_mapping_name(info->location),
info->num_effects,
info->persistency_caps);
}
@@ -948,9 +995,179 @@ hidpp20_color_led_effect_get_zone_effect_info(struct hidpp20_device *device,
info->zone_index = msg.msg.parameters[0];
info->zone_effect_index = msg.msg.parameters[1];
info->effect_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[2]);
info->effect_caps = hidpp_get_unaligned_be_u16(&msg.msg.parameters[4]);
info->effect_period = hidpp_get_unaligned_be_u16(&msg.msg.parameters[6]);
info->effect_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->effect_caps = get_unaligned_be_u16(&msg.msg.parameters[4]);
info->effect_period = get_unaligned_be_u16(&msg.msg.parameters[6]);
return 0;
}
/* -------------------------------------------------------------------------- */
/* 0x8071: RGB Effects */
/* -------------------------------------------------------------------------- */
#define CMD_RGB_EFFECTS_GET_INFO 0x00
#define CMD_RGB_EFFECTS_SET_RGB_CLUSTER_EFFECT 0x10
#define CMD_RGB_EFFECTS_SET_MULTI_LED_RGB_CLUSTER_PATTERN 0x20
#define CMD_RGB_EFFECTS_MANAGE_NV_CONFIG 0x30
#define CMD_RGB_EFFECTS_MANAGE_RGB_LED_BIN_INFO 0x40
#define CMD_RGB_EFFECTS_MANAGE_SW_CONTROL 0x50
#define CMD_RGB_EFFECTS_SET_EFFECT_SYNC_CORRECTION 0x60
#define CMD_RGB_EFFECTS_MANAGE_RGB_POWER_MODE_CONFIG 0x70
#define CMD_RGB_EFFECTS_MANAGE_RGB_POWER_MODE 0x80
int
hidpp20_rgb_effects_get_device_info(struct hidpp20_device *device,
struct hidpp20_rgb_device_info *info)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[1] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->cluster_index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->cluster_count = msg.msg.parameters[2];
info->nv_caps = get_unaligned_be_u16(&msg.msg.parameters[3]);
info->ext_caps = get_unaligned_be_u16(&msg.msg.parameters[4]);
return 0;
}
int
hidpp20_rgb_effects_get_cluster_info(struct hidpp20_device *device,
uint8_t cluster_index,
struct hidpp20_rgb_cluster_info *info)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = cluster_index,
.msg.parameters[1] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->location = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->num_effects = msg.msg.parameters[4];
info->persistency_caps = msg.msg.parameters[5];
return 0;
}
int
hidpp20_rgb_effects_get_cluster_infos(struct hidpp20_device *device,
struct hidpp20_rgb_cluster_info **infos_list)
{
struct hidpp20_rgb_cluster_info *i_list, *info;
struct hidpp20_rgb_device_info device_info = {0};
uint8_t num_infos;
unsigned i;
int rc;
rc = hidpp20_rgb_effects_get_device_info(device, &device_info);
if (rc < 0)
return rc;
num_infos = device_info.cluster_count;
if (num_infos == 0) {
*infos_list = NULL;
return 0;
}
i_list = zalloc(num_infos * sizeof(struct hidpp20_rgb_cluster_info));
for (i = 0; i < num_infos; i++) {
info = &i_list[i];
info->index = i;
rc = hidpp20_rgb_effects_get_cluster_info(device, i, info);
if (rc)
goto err;
hidpp_log_raw(&device->base,
"cluster_info %d: location: %d type %s num_effects: %d persistency_caps: 0x%02x\n",
info->index,
info->location,
hidpp20_led_get_location_mapping_name(info->location),
info->num_effects,
info->persistency_caps);
}
*infos_list = i_list;
return num_infos;
err:
free(i_list);
return rc;
}
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)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = cluster_index,
.msg.parameters[1] = effect_index,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->cluster_index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->effect_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->capabilities = get_unaligned_be_u16(&msg.msg.parameters[4]);
info->effect_period = get_unaligned_be_u16(&msg.msg.parameters[6]);
return 0;
}
@@ -1000,8 +1217,8 @@ hidpp20_special_keys_buttons_get_info(struct hidpp20_device *device,
if (rc)
return rc;
control->control_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[2]);
control->control_id = get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
control->flags = msg.msg.parameters[4];
control->position = msg.msg.parameters[5];
control->group = msg.msg.parameters[6];
@@ -1025,13 +1242,13 @@ hidpp20_special_keys_buttons_get_reporting(struct hidpp20_device *device,
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_REPORTING,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
control->reporting.remapped = hidpp_get_unaligned_be_u16(&msg.msg.parameters[3]);
control->reporting.remapped = get_unaligned_be_u16(&msg.msg.parameters[3]);
control->reporting.raw_XY = !!(msg.msg.parameters[2] & 0x10);
control->reporting.persist = !!(msg.msg.parameters[2] & 0x04);
control->reporting.divert = !!(msg.msg.parameters[2] & 0x01);
@@ -1125,8 +1342,8 @@ hidpp20_special_key_mouse_set_control(struct hidpp20_device *device,
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_SET_REPORTING,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[3], control->reporting.remapped);
set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
set_unaligned_be_u16(&msg.msg.parameters[3], control->reporting.remapped);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_SPECIAL_KEYS_BUTTONS);
@@ -1177,7 +1394,7 @@ hidpp20_mousepointer_get_mousepointer_info(struct hidpp20_device *device,
if (rc)
return rc;
*resolution = hidpp_get_unaligned_be_u16(msg.msg.parameters);
*resolution = get_unaligned_be_u16(msg.msg.parameters);
*flags = msg.msg.parameters[2];
return 0;
@@ -1225,6 +1442,11 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602) {
msg.msg.parameters[0] = 1;
i = 0;
}
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
@@ -1233,8 +1455,11 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
sensor->index = msg.msg.parameters[0];
while (i < LONG_MESSAGE_LENGTH - 4U &&
hidpp_get_unaligned_be_u16(&msg.msg.parameters[i]) != 0) {
uint16_t value = hidpp_get_unaligned_be_u16(&msg.msg.parameters[i]);
get_unaligned_be_u16(&msg.msg.parameters[i]) != 0) {
uint16_t value = get_unaligned_be_u16(&msg.msg.parameters[i]);
if (device->quirk == HIDPP20_QUIRK_G602 && i == 2)
value += 0xe000;
if (value > 0xe000) {
sensor->dpi_steps = value - 0xe000;
@@ -1265,12 +1490,15 @@ hidpp20_adjustable_dpi_get_dpi(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602)
msg.msg.parameters[0] = 1;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
sensor->dpi = hidpp_get_unaligned_be_u16(&msg.msg.parameters[1]);
sensor->default_dpi = hidpp_get_unaligned_be_u16(&msg.msg.parameters[3]);
sensor->dpi = get_unaligned_be_u16(&msg.msg.parameters[1]);
sensor->default_dpi = get_unaligned_be_u16(&msg.msg.parameters[3]);
return 0;
}
@@ -1344,7 +1572,10 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[1], dpi);
if (device->quirk == HIDPP20_QUIRK_G602)
msg.msg.parameters[0] = 1;
set_unaligned_be_u16(&msg.msg.parameters[1], dpi);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ADJUSTABLE_DPI);
@@ -1357,7 +1588,7 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
if (rc)
return rc;
returned_parameters = hidpp_get_unaligned_be_u16(&msg.msg.parameters[1]);
returned_parameters = get_unaligned_be_u16(&msg.msg.parameters[1]);
/* version 0 of the protocol does not echo the parameters */
if (returned_parameters != dpi && returned_parameters)
@@ -1427,8 +1658,15 @@ int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *d
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G402 0x01
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303 0x02
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900 0x03
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G915 0x04
#define HIDPP20_ONBOARD_PROFILES_MACRO_TYPE_G402 0x01
#define HIDPP20_USER_PROFILES_G402 0x0000
#define HIDPP20_ROM_PROFILES_G402 0x0100
#define HIDPP20_PROFILE_DIR_END 0xFFFF
#define HIDPP20_PROFILE_DIR_ENABLED 2
union hidpp20_internal_profile {
uint8_t data[HIDPP20_PROFILE_SIZE];
struct {
@@ -1439,7 +1677,9 @@ union hidpp20_internal_profile {
struct hidpp20_color profile_color;
uint8_t power_mode;
uint8_t angle_snapping;
uint8_t reserved[14];
uint8_t reserved[10];
uint16_t powersave_timeout;
uint16_t poweroff_timeout;
union hidpp20_button_binding buttons[16];
union hidpp20_button_binding alternate_buttons[16];
union {
@@ -1447,7 +1687,8 @@ union hidpp20_internal_profile {
uint8_t raw[16 * 3];
} name;
struct hidpp20_internal_led leds[2]; /* G303, g502, g900 only */
uint8_t free[24];
struct hidpp20_internal_led alt_leds[2];
uint8_t free[2];
uint16_t crc;
} __attribute__((packed)) profile;
};
@@ -1499,13 +1740,15 @@ hidpp20_onboard_profiles_read_sector(struct hidpp20_device *device,
.msg.address = CMD_ONBOARD_PROFILES_MEMORY_READ,
};
hidpp_log_debug(&device->base, "Reading sector 0x%04x\n", sector);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], sector);
set_unaligned_be_u16(&msg.msg.parameters[0], sector);
count = sector_size;
@@ -1516,14 +1759,14 @@ hidpp20_onboard_profiles_read_sector(struct hidpp20_device *device,
* less than 16 bytes to read we need to read from sector_size - 16
*/
offset = (sector_size - offset < 16) ? sector_size - 16 : offset;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[2], offset);
set_unaligned_be_u16(&msg.msg.parameters[2], offset);
buf = msg;
rc = hidpp20_request_command(device, &buf);
if (rc)
return rc;
/* msg.msg.parameters is guaranteed to have a size >= 16 */
memcpy(data + offset, buf.msg.parameters, min(count, 16));
memcpy(data + offset, buf.msg.parameters, 16);
/*
* no need to check for count >= 0:
@@ -1544,7 +1787,10 @@ hidpp20_onboard_profiles_is_sector_valid(struct hidpp20_device *device,
uint16_t crc, read_crc;
crc = hidpp_crc_ccitt(data, sector_size - 2);
read_crc = hidpp_get_unaligned_be_u16(&data[sector_size - 2]);
read_crc = get_unaligned_be_u16(&data[sector_size - 2]);
if (crc != read_crc)
hidpp_log_debug(&device->base, "Invalid CRC (%04x != %04x)\n", read_crc, crc);
return crc == read_crc;
}
@@ -1564,9 +1810,9 @@ hidpp20_onboard_profiles_write_start(struct hidpp20_device *device,
.msg.address = CMD_ONBOARD_PROFILES_MEMORY_ADDR_WRITE,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], sector);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[2], sub_address);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[4], count);
set_unaligned_be_u16(&msg.msg.parameters[0], sector);
set_unaligned_be_u16(&msg.msg.parameters[2], sub_address);
set_unaligned_be_u16(&msg.msg.parameters[4], count);
rc = hidpp20_request_command(device, &msg);
if (rc)
@@ -1634,7 +1880,7 @@ hidpp20_onboard_profiles_write_sector(struct hidpp20_device *device,
if (write_crc) {
crc = hidpp_crc_ccitt(data, sector_size - 2);
hidpp_set_unaligned_be_u16(&data[sector_size - 2], crc);
set_unaligned_be_u16(&data[sector_size - 2], crc);
}
rc = hidpp20_onboard_profiles_write_start(device,
@@ -1831,7 +2077,8 @@ hidpp20_onboard_profiles_validate(struct hidpp20_device *device,
if ((info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G402) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900)) {
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G915)) {
hidpp_log_error(&device->base,
"Profile layout not supported: 0x%02x.\n",
info->profile_format_id);
@@ -2325,12 +2572,18 @@ hidpp20_onboard_profiles_parse_profile(struct hidpp20_device *device,
profile->default_dpi = pdata->profile.default_dpi;
profile->switched_dpi = pdata->profile.switched_dpi;
profile->powersave_timeout = pdata->profile.powersave_timeout;
profile->poweroff_timeout = pdata->profile.poweroff_timeout;
for (i = 0; i < 5; i++) {
profile->dpi[i] = hidpp_get_unaligned_le_u16(&data[2 * i + 3]);
profile->dpi[i] = get_unaligned_le_u16(&data[2 * i + 3]);
}
for (i = 0; i < profiles_list->num_leds; i++)
{
hidpp20_onboard_profiles_read_led(&profile->leds[i], pdata->profile.leds[i]);
hidpp20_onboard_profiles_read_led(&profile->alt_leds[i], pdata->profile.alt_leds[i]);
}
hidpp20_buttons_to_cpu(device, profiles_list, profile, pdata->profile.buttons, profiles_list->num_buttons);
@@ -2349,16 +2602,18 @@ hidpp20_onboard_profiles_parse_profile(struct hidpp20_device *device,
return 0;
}
static int
hidpp20_onboard_profiles_parse(struct hidpp20_device *device,
struct hidpp20_profiles *profiles,
uint16_t dict_address,
bool check_crc)
int
hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
struct hidpp20_profiles *profiles)
{
_cleanup_free_ uint8_t *data = NULL;
int rc;
unsigned i;
uint16_t addr;
bool crc_valid;
bool read_userdata = true;
assert(profiles);
for (i = 0; i < profiles->num_profiles; i++) {
profiles->profiles[i].address = 0;
@@ -2368,101 +2623,83 @@ hidpp20_onboard_profiles_parse(struct hidpp20_device *device,
data = hidpp20_onboard_profiles_allocate_sector(profiles);
rc = hidpp20_onboard_profiles_read_sector(device,
dict_address,
HIDPP20_USER_PROFILES_G402,
profiles->sector_size,
data);
if (rc && device->quirk != HIDPP20_QUIRK_G305) {
/* The G305 has a bug where it throws an ERR_INVALID_ARGUMENT
if the sector has not been written to yet. If this happens
we will read the ROM profiles.*/
read_userdata = false;
goto read_profiles;
}
if (rc < 0)
return rc; // ignore_clang_sa_mem_leak
crc_valid = hidpp20_onboard_profiles_is_sector_valid(device,
profiles->sector_size,
data);
if (crc_valid || !check_crc) {
if (crc_valid) {
for (i = 0; i < profiles->num_profiles; i++) {
uint8_t *d = data + 4 * i;
if (d[0] == 0xFF && d[1] == 0xFF)
addr = get_unaligned_be_u16(d);
if(addr == HIDPP20_PROFILE_DIR_END)
break;
profiles->profiles[i].address = hidpp_get_unaligned_be_u16(d);
if (profiles->profiles[i].address != (dict_address | (i + 1)))
profiles->profiles[i].address = addr;
/* profile address sanity check */
if (profiles->profiles[i].address != (HIDPP20_USER_PROFILES_G402 | (i + 1)))
hidpp_log_info(&device->base,
"profile %d: error in the address: 0x%04x instead of 0x%04x\n",
i + 1,
profiles->profiles[i].address,
dict_address | (i + 1));
profiles->profiles[i].enabled = !!d[2];
"profile %d: error in the address: 0x%04x instead of 0x%04x\n",
i + 1,
profiles->profiles[i].address,
HIDPP20_USER_PROFILES_G402 | (i + 1));
profiles->profiles[i].enabled = !!d[HIDPP20_PROFILE_DIR_ENABLED];
}
} else {
hidpp_log_debug(&device->base, "Profile directory has an invalid CRC... Reading ROM profiles.\n");
read_userdata = false;
}
/*
* Even if the crc is wrong, we still fetch all the profiles, so as
* to have the current stored values if some profiles are legitimate.
*/
for (i = 0; i < profiles->num_profiles; i++) {
if (profiles->profiles[i].address == 0x0000) {
switch (dict_address) {
case 0x0000:
profiles->profiles[i].address = i + 1;
break;
case 0x0100:
if (i >= profiles->num_rom_profiles) {
profiles->profiles[i].address = dict_address | profiles->num_rom_profiles;
} else {
profiles->profiles[i].address = dict_address | (i + 1);
}
break;
default:
hidpp_log_error(&device->base,
"this should never happen: dict address is 0x%04x\n",
dict_address);
return -EINVAL;
}
read_profiles:
for (i = 0; i < profiles->num_profiles; i++) {
if (read_userdata) {
hidpp_log_debug(&device->base, "Parsing profile %u\n", i);
rc = hidpp20_onboard_profiles_parse_profile(device,
profiles,
i,
true);
/* on fail to read the user profile fallback to the default profile */
if (rc)
hidpp_log_debug(&device->base, "Profile %u is bad. Falling back to the ROM settings.\n", i);
else
continue;
}
/* the number of rom profiles can be different than the number of user profiles
so we if there are not enough rom profiles to populate all the user profiles
we just use the first rom profile */
if (i + 1 > profiles->num_rom_profiles)
profiles->profiles[i].address = HIDPP20_ROM_PROFILES_G402 + 1;
else
profiles->profiles[i].address = HIDPP20_ROM_PROFILES_G402 + i + 1;
rc = hidpp20_onboard_profiles_parse_profile(device,
profiles,
i,
check_crc);
if (rc == -EAGAIN)
crc_valid = false;
profiles,
i,
false);
if (rc < 0)
return rc;
}
if (check_crc && !crc_valid)
return -EAGAIN;
return 0;
}
int
hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
struct hidpp20_profiles *profiles)
{
int rc;
assert(profiles);
rc = hidpp20_onboard_profiles_parse(device, profiles, 0x0000, true);
if (rc == 0)
return profiles->num_profiles;
if (rc != -EAGAIN) {
/* something went wrong */
return rc;
}
/*
* One of the used sector has a bad crc, the device is using the ROM
* profiles.
*/
hidpp_log_debug(&device->base, "device is using ROM settings\n");
rc = hidpp20_onboard_profiles_parse(device, profiles, 0x0100, false);
if (rc < 0)
return rc;
hidpp_log_debug(&device->base, "ROM settings loaded\n");
return profiles->num_profiles;
}
@@ -2531,12 +2768,19 @@ hidpp20_onboard_profiles_write_profile(struct hidpp20_device *device,
pdata->profile.default_dpi = profile->default_dpi;
pdata->profile.switched_dpi = profile->switched_dpi;
pdata->profile.powersave_timeout = profile->powersave_timeout;
pdata->profile.poweroff_timeout = profile->poweroff_timeout;
for (i = 0; i < 5; i++) {
pdata->profile.dpi[i] = hidpp_cpu_to_le_u16(profile->dpi[i]);
}
for (i = 0; i < profiles_list->num_leds; i++)
{
hidpp20_onboard_profiles_write_led(&pdata->profile.leds[i], &profile->leds[i]);
/* we write the current led instead of the stored value */
hidpp20_onboard_profiles_write_led(&pdata->profile.alt_leds[i], &profile->leds[i]);
}
hidpp20_buttons_from_cpu(profile, pdata->profile.buttons, profiles_list->num_buttons);

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,12 +64,17 @@ struct hidpp20_device {
unsigned proto_minor;
unsigned feature_count;
struct hidpp20_feature *feature_list;
enum hidpp20_quirk quirk;
};
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 */
/* -------------------------------------------------------------------------- */
@@ -138,6 +149,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 */
/* -------------------------------------------------------------------------- */
@@ -439,6 +469,31 @@ 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);
enum hidpp20_color_led_location {
HIDPP20_COLOR_LED_LOCATION_UNDEFINED = 0,
HIDPP20_COLOR_LED_LOCATION_PRIMARY,
@@ -472,8 +527,30 @@ 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 */
/* -------------------------------------------------------------------------- */
@@ -559,11 +636,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 +645,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;
@@ -673,6 +780,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 +790,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 {

View File

@@ -33,6 +33,7 @@
#include "libratbag.h"
#include "libratbag-private.h"
#include "libratbag-data.h"
#include "hidpp20.h"
#define GROUP_DEVICE "Device"
@@ -54,6 +55,7 @@ enum driver {
struct data_hidpp20 {
int index;
enum hidpp20_quirk quirk;
};
struct data_hidpp10 {
@@ -152,6 +154,7 @@ init_data_hidpp20(struct ratbag *ratbag,
const char *group = "Driver/hidpp20";
GError *error = NULL;
int num;
char *str;
data->hidpp20.index = -1;
@@ -160,6 +163,15 @@ init_data_hidpp20(struct ratbag *ratbag,
data->hidpp20.index = 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
@@ -465,18 +477,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 +495,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 +574,14 @@ ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data)
return data->hidpp20.index;
}
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

View File

@@ -80,6 +80,9 @@ 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);
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data);
/* SteelSeries */
/**

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
*

View File

@@ -32,6 +32,10 @@
#include "libratbag-util.h"
#include "libratbag-hidraw.h"
#ifdef NDEBUG
#error "libratbag relies on assert(). Do not define NDEBUG"
#endif
#define RATBAG_LED_TYPE_UNKNOWN 0
void
@@ -567,7 +571,7 @@ ratbag_resolution_set_cap(struct ratbag_resolution *res,
{
assert(cap <= RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
res->capabilities = (1 << cap);
res->capabilities |= (1 << cap);
}
static inline void

View File

@@ -53,7 +53,7 @@ struct ratbag_test_resolution {
int xres, yres;
bool active;
bool dflt;
uint32_t caps;
uint32_t caps[10];
int dpi_min, dpi_max;
};
@@ -73,7 +73,7 @@ struct ratbag_test_led {
};
struct ratbag_test_profile {
const char *name;
char *name;
struct ratbag_test_button buttons[RATBAG_TEST_MAX_BUTTONS];
struct ratbag_test_resolution resolutions[RATBAG_TEST_MAX_RESOLUTIONS];
struct ratbag_test_led leds[RATBAG_TEST_MAX_LEDS];

View File

@@ -328,3 +328,35 @@ err:
dpi_list_free(list);
return NULL;
}
static inline uint16_t
get_unaligned_be_u16(uint8_t *buf)
{
return (buf[0] << 8) | buf[1];
}
static inline void
set_unaligned_be_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value >> 8;
buf[1] = value & 0xFF;
}
static inline uint16_t
get_unaligned_le_u16(uint8_t *buf)
{
return (buf[1] << 8) | buf[0];
}
static inline void
set_unaligned_le_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value & 0xFF;
buf[1] = value >> 8;
}
static inline uint32_t
get_unaligned_be_u32(uint8_t *buf)
{
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}

View File

@@ -322,7 +322,7 @@ out:
}
static inline bool
ratbag_test_driver(struct ratbag_device *device,
ratbag_try_driver(struct ratbag_device *device,
const struct input_id *dev_id,
const char *driver_name,
const struct ratbag_test_device *test_device)
@@ -377,9 +377,9 @@ ratbag_driver_fallback_logitech(struct ratbag_device *device,
if (dev_id->vendor != USB_VENDOR_ID_LOGITECH)
return false;
rc = ratbag_test_driver(device, dev_id, "hidpp20", NULL);
rc = ratbag_try_driver(device, dev_id, "hidpp20", NULL);
if (!rc)
rc = ratbag_test_driver(device, dev_id, "hidpp10", NULL);
rc = ratbag_try_driver(device, dev_id, "hidpp10", NULL);
return rc;
}
@@ -395,11 +395,13 @@ ratbag_assign_driver(struct ratbag_device *device,
if (!test_device) {
driver_name = ratbag_device_data_get_driver(device->data);
} else {
log_debug(device->ratbag, "This is a test device\n");
driver_name = "test_driver";
}
if (driver_name) {
rc = ratbag_test_driver(device, dev_id, driver_name, test_device);
log_debug(device->ratbag, "device assigned driver %s\n", driver_name);
rc = ratbag_try_driver(device, dev_id, driver_name, test_device);
} else {
rc = ratbag_driver_fallback_logitech(device, dev_id);
}
@@ -456,6 +458,8 @@ ratbag_device_new_from_udev_device(struct ratbag *ratbag,
if ((name = get_device_name(udev_device)) == 0)
goto out_err;
log_debug(ratbag, "New device: %s\n", name);
device = ratbag_device_new(ratbag, udev_device, name, &id);
if (!device || !device->data)
goto out_err;
@@ -624,7 +628,7 @@ ratbag_create_button(struct ratbag_profile *profile, unsigned int index)
button->index = index;
button->type = RATBAG_BUTTON_TYPE_UNKNOWN;
list_insert(&profile->buttons, &button->link);
list_append(&profile->buttons, &button->link);
return button;
}
@@ -644,7 +648,7 @@ ratbag_create_led(struct ratbag_profile *profile, unsigned int index)
if (device->data)
led->type = ratbag_device_data_get_led_type(device->data, led->index);
list_insert(&profile->leds, &led->link);
list_append(&profile->leds, &led->link);
return led;
}
@@ -669,7 +673,7 @@ ratbag_create_resolution(struct ratbag_profile *profile, int index)
res->profile = profile;
res->index = index;
list_insert(&profile->resolutions, &res->link);
list_append(&profile->resolutions, &res->link);
profile->num_resolutions++;
}
@@ -693,7 +697,7 @@ ratbag_create_profile(struct ratbag_device *device,
profile->is_enabled = true;
profile->name = NULL;
list_insert(&device->profiles, &profile->link);
list_append(&device->profiles, &profile->link);
list_init(&profile->buttons);
list_init(&profile->leds);
list_init(&profile->resolutions);

View File

@@ -1,11 +0,0 @@
#include <libratbag.h>
/* This is a build-test only */
using namespace std;
int
main(int argc, char **argv)
{
return 0;
}

View File

@@ -1,9 +0,0 @@
#include <libratbag.h>
/* This is a build-test only */
int
main(void)
{
return 0;
}

View File

@@ -31,8 +31,8 @@ import sys
def print_ratbagctl(ratbagctl_path, ratbagd_path, version_string):
with open(ratbagctl_path, 'r', encoding='utf-8') as ratbagctl, open(ratbagd_path, 'r', encoding='utf-8') as ratbagd:
for l in ratbagctl.readlines():
if l.startswith("from ratbagd import "):
for line in ratbagctl.readlines():
if line.startswith("from ratbagd import "):
headers = True
for r in ratbagd.readlines():
if not r.startswith('#') and r.strip():
@@ -40,9 +40,9 @@ def print_ratbagctl(ratbagctl_path, ratbagd_path, version_string):
if not headers:
print(r.rstrip('\n'))
else:
if '@version@' in l:
l = l.replace('@version@', version_string)
print(l.rstrip('\n'))
if '@version@' in line:
line = line.replace('@version@', version_string)
print(line.rstrip('\n'))
def main(argv):

View File

@@ -1,10 +0,0 @@
#!/bin/sh
GIT_DIR="$MESON_SOURCE_ROOT/.git"
export GIT_DIR
git log --format="format:%h" -1 2>/dev/null
if test $? -ne 0; then
echo "unknown"
fi

View File

@@ -24,6 +24,7 @@
import libratbag
import os
import sys
from enum import IntEnum
from evdev import ecodes
@@ -215,11 +216,11 @@ class Ratbagd(object):
def get_capabilities(type, object):
capabilities = []
for k in libratbag.__dict__.keys():
if k.startswith("RATBAG_{}_CAP_".format(type.upper())) and "CAP_NONE" not in k:
cap = getattr(libratbag, k)
func = getattr(libratbag, "ratbag_{}_has_capability".format(type.lower()))
if func(object, cap):
capabilities.append(cap)
if k.startswith("RATBAG_{}_CAP_".format(type.upper())) and "CAP_NONE" not in k:
cap = getattr(libratbag, k)
func = getattr(libratbag, "ratbag_{}_has_capability".format(type.lower()))
if func(object, cap):
capabilities.append(cap)
return capabilities
@@ -497,9 +498,39 @@ class RatbagdButton(metaclass=MetaRatbag):
_PREFIX = "RATBAG_BUTTON_"
MACRO_KEY_PRESS = libratbag.RATBAG_MACRO_EVENT_KEY_PRESSED
MACRO_KEY_RELEASE = libratbag.RATBAG_MACRO_EVENT_KEY_RELEASED
MACRO_WAIT = libratbag.RATBAG_MACRO_EVENT_WAIT
class ActionType(IntEnum):
NONE = libratbag.RATBAG_BUTTON_ACTION_TYPE_NONE
BUTTON = libratbag.RATBAG_BUTTON_ACTION_TYPE_BUTTON
SPECIAL = libratbag.RATBAG_BUTTON_ACTION_TYPE_SPECIAL
MACRO = libratbag.RATBAG_BUTTON_ACTION_TYPE_MACRO
class ActionSpecial(IntEnum):
INVALID = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_INVALID
UNKNOWN = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_UNKNOWN
DOUBLECLICK = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK
WHEEL_LEFT = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT
WHEEL_RIGHT = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT
WHEEL_UP = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP
WHEEL_DOWN = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN
RATCHET_MODE_SWITCH = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH
RESOLUTION_CYCLE_UP = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP
RESOLUTION_CYCLE_DOWN = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN
RESOLUTION_UP = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP
RESOLUTION_DOWN = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN
RESOLUTION_ALTERNATE = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE
RESOLUTION_DEFAULT = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT
PROFILE_CYCLE_UP = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP
PROFILE_CYCLE_DOWN = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_DOWN
PROFILE_UP = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP
PROFILE_DOWN = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN
SECOND_MODE = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE
BATTERY_LEVEL = libratbag.RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL
class Macro(IntEnum):
NONE = libratbag.RATBAG_MACRO_EVENT_NONE
KEY_PRESS = libratbag.RATBAG_MACRO_EVENT_KEY_PRESSED
KEY_RELEASE = libratbag.RATBAG_MACRO_EVENT_KEY_RELEASED
WAIT = libratbag.RATBAG_MACRO_EVENT_WAIT
"""A table mapping a button's index to its usual function as defined by X
and the common desktop environments."""
@@ -517,25 +548,25 @@ class RatbagdButton(metaclass=MetaRatbag):
@classmethod
def __late_init__(cls):
cls.SPECIAL_DESCRIPTION = {
cls.ACTION_SPECIAL_UNKNOWN: N_("Unknown"),
cls.ACTION_SPECIAL_DOUBLECLICK: N_("Doubleclick"),
cls.ACTION_SPECIAL_WHEEL_LEFT: N_("Wheel Left"),
cls.ACTION_SPECIAL_WHEEL_RIGHT: N_("Wheel Right"),
cls.ACTION_SPECIAL_WHEEL_UP: N_("Wheel Up"),
cls.ACTION_SPECIAL_WHEEL_DOWN: N_("Wheel Down"),
cls.ACTION_SPECIAL_RATCHET_MODE_SWITCH: N_("Ratchet Mode"),
cls.ACTION_SPECIAL_RESOLUTION_CYCLE_UP: N_("Cycle Resolution Up"),
cls.ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN: N_("Cycle Resolution Down"),
cls.ACTION_SPECIAL_RESOLUTION_UP: N_("Resolution Up"),
cls.ACTION_SPECIAL_RESOLUTION_DOWN: N_("Resolution Down"),
cls.ACTION_SPECIAL_RESOLUTION_ALTERNATE: N_("Resolution Switch"),
cls.ACTION_SPECIAL_RESOLUTION_DEFAULT: N_("Default Resolution"),
cls.ACTION_SPECIAL_PROFILE_CYCLE_UP: N_("Cycle Profile Up"),
cls.ACTION_SPECIAL_PROFILE_CYCLE_DOWN: N_("Cycle Profile Down"),
cls.ACTION_SPECIAL_PROFILE_UP: N_("Profile Up"),
cls.ACTION_SPECIAL_PROFILE_DOWN: N_("Profile Down"),
cls.ACTION_SPECIAL_SECOND_MODE: N_("Second Mode"),
cls.ACTION_SPECIAL_BATTERY_LEVEL: N_("Battery Level"),
cls.ActionSpecial.UNKNOWN: N_("Unknown"),
cls.ActionSpecial.DOUBLECLICK: N_("Doubleclick"),
cls.ActionSpecial.WHEEL_LEFT: N_("Wheel Left"),
cls.ActionSpecial.WHEEL_RIGHT: N_("Wheel Right"),
cls.ActionSpecial.WHEEL_UP: N_("Wheel Up"),
cls.ActionSpecial.WHEEL_DOWN: N_("Wheel Down"),
cls.ActionSpecial.RATCHET_MODE_SWITCH: N_("Ratchet Mode"),
cls.ActionSpecial.RESOLUTION_CYCLE_UP: N_("Cycle Resolution Up"),
cls.ActionSpecial.RESOLUTION_CYCLE_DOWN: N_("Cycle Resolution Down"),
cls.ActionSpecial.RESOLUTION_UP: N_("Resolution Up"),
cls.ActionSpecial.RESOLUTION_DOWN: N_("Resolution Down"),
cls.ActionSpecial.RESOLUTION_ALTERNATE: N_("Resolution Switch"),
cls.ActionSpecial.RESOLUTION_DEFAULT: N_("Default Resolution"),
cls.ActionSpecial.PROFILE_CYCLE_UP: N_("Cycle Profile Up"),
cls.ActionSpecial.PROFILE_CYCLE_DOWN: N_("Cycle Profile Down"),
cls.ActionSpecial.PROFILE_UP: N_("Profile Up"),
cls.ActionSpecial.PROFILE_DOWN: N_("Profile Down"),
cls.ActionSpecial.SECOND_MODE: N_("Second Mode"),
cls.ActionSpecial.BATTERY_LEVEL: N_("Battery Level"),
}
def __init__(self, profile, id):
@@ -594,15 +625,15 @@ class RatbagdButton(metaclass=MetaRatbag):
def special(self, special):
"""Set the button mapping to the given special entry.
@param special The special entry, as one of RatbagdButton.ACTION_SPECIAL_*
@param special The special entry, as one of RatbagdButton.ActionSpecial
"""
libratbag.ratbag_button_set_special(self._button, special)
@property
def action_type(self):
"""An enum describing the action type of the button. One of
ACTION_TYPE_NONE, ACTION_TYPE_BUTTON, ACTION_TYPE_SPECIAL,
ACTION_TYPE_MACRO. This decides which
ActionType.NONE, ActionType.BUTTON, ActionType.SPECIAL,
ActionType.MACRO. This decides which
*Mapping property has a value.
"""
return libratbag.ratbag_button_get_action_type(self._button)
@@ -610,7 +641,7 @@ class RatbagdButton(metaclass=MetaRatbag):
@property
def action_types(self):
"""An array of possible values for ActionType."""
return [t for t in (RatbagdButton.ACTION_TYPE_BUTTON, RatbagdButton.ACTION_TYPE_SPECIAL, RatbagdButton.ACTION_TYPE_MACRO)
return [t for t in (RatbagdButton.ActionType.BUTTON, RatbagdButton.ActionType.SPECIAL, RatbagdButton.ActionType.MACRO)
if libratbag.ratbag_button_has_action_type(self._button, t)]
def disable(self):
@@ -630,11 +661,13 @@ class RatbagdMacro(metaclass=MetaRatbag):
_MACRO_KEY = 1000
_MACRO_DESCRIPTION = {
RatbagdButton.MACRO_KEY_PRESS: lambda key:
RatbagdButton.Macro.NONE: lambda key:
".",
RatbagdButton.Macro.KEY_PRESS: lambda key:
"↓{}".format(ecodes.KEY[key][RatbagdMacro._PREFIX_LEN:]),
RatbagdButton.MACRO_KEY_RELEASE: lambda key:
RatbagdButton.Macro.KEY_RELEASE: lambda key:
"↑{}".format(ecodes.KEY[key][RatbagdMacro._PREFIX_LEN:]),
RatbagdButton.MACRO_WAIT: lambda val:
RatbagdButton.Macro.WAIT: lambda val:
"{}ms".format(val),
_MACRO_KEY: lambda key:
"↕{}".format(ecodes.KEY[key][RatbagdMacro._PREFIX_LEN:]),
@@ -652,10 +685,10 @@ class RatbagdMacro(metaclass=MetaRatbag):
while idx < len(self._macro):
t, v = self._macro[idx]
try:
if t == RatbagdButton.MACRO_KEY_PRESS:
if t == RatbagdButton.Macro.KEY_PRESS:
# Check for a paired press/release event
t2, v2 = self._macro[idx + 1]
if t2 == RatbagdButton.MACRO_KEY_RELEASE and v == v2:
if t2 == RatbagdButton.Macro.KEY_RELEASE and v == v2:
t = self._MACRO_KEY
idx += 1
except IndexError:
@@ -683,7 +716,7 @@ class RatbagdMacro(metaclass=MetaRatbag):
for i in range(libratbag.ratbag_button_macro_get_num_events(macro_object)):
type = libratbag.ratbag_button_macro_get_event_type(macro_object, i)
value = None
if type == RatbagdButton.MACRO_WAIT:
if type == RatbagdButton.Macro.WAIT:
value = libratbag.ratbag_button_macro_get_event_timeout(macro_object, i)
else:
value = libratbag.ratbag_button_macro_get_event_key(macro_object, i)
@@ -714,21 +747,27 @@ class RatbagdLed(metaclass=MetaRatbag):
_PREFIX = "RATBAG_LED_"
MODE_OFF = libratbag.RATBAG_LED_OFF
MODE_ON = libratbag.RATBAG_LED_ON
MODE_CYCLE = libratbag.RATBAG_LED_CYCLE
MODE_BREATHING = libratbag.RATBAG_LED_BREATHING
class Mode(IntEnum):
OFF = libratbag.RATBAG_LED_OFF
ON = libratbag.RATBAG_LED_ON
CYCLE = libratbag.RATBAG_LED_CYCLE
BREATHING = libratbag.RATBAG_LED_BREATHING
class ColorDepth(IntEnum):
MONOCHROME = libratbag.RATBAG_LED_COLORDEPTH_MONOCHROME
RGB_888 = libratbag.RATBAG_LED_COLORDEPTH_RGB_888
RGB_111 = libratbag.RATBAG_LED_COLORDEPTH_RGB_111
LED_DESCRIPTION = {
# Translators: the LED is off.
MODE_OFF: N_("Off"),
Mode.OFF: N_("Off"),
# Translators: the LED has a single, solid color.
MODE_ON: N_("Solid"),
Mode.ON: N_("Solid"),
# Translators: the LED is cycling between red, green and blue.
MODE_CYCLE: N_("Cycle"),
Mode.CYCLE: N_("Cycle"),
# Translators: the LED's is pulsating a single color on different
# brightnesses.
MODE_BREATHING: N_("Breathing"),
Mode.BREATHING: N_("Breathing"),
}
def __init__(self, profile, id):
@@ -746,16 +785,16 @@ class RatbagdLed(metaclass=MetaRatbag):
@property
def mode(self):
"""This led's mode, one of MODE_OFF, MODE_ON, MODE_CYCLE and
MODE_BREATHING."""
"""This led's mode, one of Mode.OFF, Mode.ON, Mode.CYCLE and
Mode.BREATHING."""
return libratbag.ratbag_led_get_mode(self._led)
@mode.setter
def mode(self, mode):
"""Set the led's mode to the given mode.
@param mode The new mode, as one of MODE_OFF, MODE_ON, MODE_CYCLE and
MODE_BREATHING.
@param mode The new mode, as one of Mode.OFF, Mode.ON, Mode.CYCLE and
Mode.BREATHING.
"""
libratbag.ratbag_led_set_mode(self._led, mode)
@@ -776,7 +815,7 @@ class RatbagdLed(metaclass=MetaRatbag):
@property
def colordepth(self):
"""An enum describing this led's colordepth, one of
RatbagdLed.COLORDEPTH_MONOCHROME, RatbagdLed.COLORDEPTH_RGB"""
RatbagdLed.ColorDepth.MONOCHROME, RatbagdLed.ColorDepth.RGB"""
return libratbag.ratbag_led_get_colordepth(self._led)
@property

View File

@@ -25,6 +25,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
import argparse
import os
import time
import toolbox
@@ -33,11 +34,13 @@ import sys
from ratbagctl import RatbagErrorCapability # NOQA
def main(argv):
def main(argv, start_ratbagd=True):
if not os.geteuid() == 0:
sys.exit('Script must be run as root')
# replaced by meson
os.environ['LIBRATBAG_DATA_DIR'] = '@LIBRATBAG_DATA_DIR@'
os.environ['RATBAG_TEST'] = "1"
if not argv:
argv = ["list"]
@@ -52,9 +55,10 @@ def main(argv):
parser.print_help()
return
ratbagd_process = toolbox.start_ratbagd(verbosity=cmd.verbose)
if ratbagd_process is None:
sys.exit("Failed to start or connect to ratbagd")
if start_ratbagd:
ratbagd_process = toolbox.start_ratbagd(verbosity=cmd.verbose)
if ratbagd_process is None:
sys.exit("Failed to start or connect to ratbagd")
_ratbagd = toolbox.open_ratbagd()
if _ratbagd is not None:
@@ -75,14 +79,20 @@ def main(argv):
finally:
try:
if cmd.keepalive:
print('\nexport RATBAGCTL_DEVEL="{}"\n'.format(os.environ['RATBAGCTL_DEVEL']))
signal.pause()
except KeyboardInterrupt:
pass
except AttributeError: # in case the parsing failed and cmd is None
pass
toolbox.terminate_ratbagd(ratbagd_process)
if ratbagd_process:
toolbox.terminate_ratbagd(ratbagd_process)
if __name__ == "__main__":
main(sys.argv[1:])
parser = argparse.ArgumentParser(description='commandline tool to access the ratbagd.devel debug server')
parser.add_argument('--use-existing-ratbagd', dest='use_existing', action='store_true', default=False,
help='Don\'t start up ratbagd.devel, connect to the already running one')
args, remainder = parser.parse_known_args()
main(remainder, not args.use_existing)

View File

@@ -31,29 +31,17 @@ import re
from ratbagd import Ratbagd, RatbagdDevice, RatbagdProfile, RatbagdMacro, RatbagdResolution, RatbagdButton, RatbagdLed, RatbagdUnavailable, RatbagError, RatbagErrorCode, RatbagErrorCapability # NOQA
def humanize(string):
return string.lower().replace('_', '-')
button_special_names = [humanize(e.name) for e in RatbagdButton.ActionSpecial if e.name != "INVALID"]
led_mode_names = [humanize(e.name) for e in RatbagdLed.Mode]
button_specials_strmap = {
RatbagdButton.ACTION_SPECIAL_UNKNOWN: "unknown",
RatbagdButton.ACTION_SPECIAL_DOUBLECLICK: "doubleclick",
RatbagdButton.ACTION_SPECIAL_WHEEL_LEFT: "wheel-left",
RatbagdButton.ACTION_SPECIAL_WHEEL_RIGHT: "wheel-right",
RatbagdButton.ACTION_SPECIAL_WHEEL_UP: "wheel-up",
RatbagdButton.ACTION_SPECIAL_WHEEL_DOWN: "wheel-down",
RatbagdButton.ACTION_SPECIAL_RATCHET_MODE_SWITCH: "ratchet-mode-switch",
RatbagdButton.ACTION_SPECIAL_RESOLUTION_CYCLE_UP: "resolution-cycle-up",
RatbagdButton.ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN: "resolution-cycle-down",
RatbagdButton.ACTION_SPECIAL_RESOLUTION_UP: "resolution-up",
RatbagdButton.ACTION_SPECIAL_RESOLUTION_DOWN: "resolution-down",
RatbagdButton.ACTION_SPECIAL_RESOLUTION_ALTERNATE: "resolution-alternate",
RatbagdButton.ACTION_SPECIAL_RESOLUTION_DEFAULT: "resolution-default",
RatbagdButton.ACTION_SPECIAL_PROFILE_CYCLE_UP: "profile-cycle-up",
RatbagdButton.ACTION_SPECIAL_PROFILE_CYCLE_DOWN: "profile-cycle-down",
RatbagdButton.ACTION_SPECIAL_PROFILE_UP: "profile-up",
RatbagdButton.ACTION_SPECIAL_PROFILE_DOWN: "profile-down",
RatbagdButton.ACTION_SPECIAL_SECOND_MODE: "second-mode",
RatbagdButton.ACTION_SPECIAL_BATTERY_LEVEL: "battery-level",
**{e: e.name.lower().replace("_", "-") for e in RatbagdButton.ActionSpecial},
**{e.name.lower().replace("_", "-"): e for e in RatbagdButton.ActionSpecial}
}
for k, v in [(v, k) for k, v in button_specials_strmap.items()]:
button_specials_strmap[k] = v
def list_devices(r, args):
@@ -121,34 +109,34 @@ def find_led(r, args):
def print_led(d, p, l, level):
leds = {
RatbagdLed.MODE_BREATHING: "breathing",
RatbagdLed.MODE_CYCLE: "cycle",
RatbagdLed.MODE_OFF: "off",
RatbagdLed.MODE_ON: "on",
RatbagdLed.Mode.BREATHING: "breathing",
RatbagdLed.Mode.CYCLE: "cycle",
RatbagdLed.Mode.OFF: "off",
RatbagdLed.Mode.ON: "on",
}
depths = {
RatbagdLed.COLORDEPTH_MONOCHROME: "monochrome",
RatbagdLed.COLORDEPTH_RGB_888: "rgb",
RatbagdLed.COLORDEPTH_RGB_111: "rgb111",
RatbagdLed.ColorDepth.MONOCHROME: "monochrome",
RatbagdLed.ColorDepth.RGB_888: "rgb",
RatbagdLed.ColorDepth.RGB_111: "rgb111",
}
if l.mode == RatbagdLed.MODE_OFF:
if l.mode == RatbagdLed.Mode.OFF:
print(" " * level + "LED: {}, depth: {}, mode: {}".format(l.index,
depths[l.colordepth],
leds[l.mode]))
elif l.mode == RatbagdLed.MODE_ON:
elif l.mode == RatbagdLed.Mode.ON:
print(" " * level + "LED: {}, depth: {}, mode: {}, color: {:02x}{:02x}{:02x}".format(l.index,
depths[l.colordepth],
leds[l.mode],
l.color[0],
l.color[1],
l.color[2]))
elif l.mode == RatbagdLed.MODE_CYCLE:
elif l.mode == RatbagdLed.Mode.CYCLE:
print(" " * level + "LED: {}, depth: {}, mode: {}, duration: {}, brightness: {}".format(l.index,
depths[l.colordepth],
leds[l.mode],
l.effect_duration,
l.brightness))
elif l.mode == RatbagdLed.MODE_BREATHING:
elif l.mode == RatbagdLed.Mode.BREATHING:
print(" " * level + "LED: {}, depth: {}, mode: {}, color: {:02x}{:02x}{:02x}, duration: {}, brightness: {}".format(l.index,
depths[l.colordepth],
leds[l.mode],
@@ -161,10 +149,10 @@ def print_led(d, p, l, level):
def print_led_caps(d, p, l, level):
leds = {
RatbagdLed.MODE_BREATHING: "breathing",
RatbagdLed.MODE_CYCLE: "cycle",
RatbagdLed.MODE_OFF: "off",
RatbagdLed.MODE_ON: "on",
RatbagdLed.Mode.BREATHING: "breathing",
RatbagdLed.Mode.CYCLE: "cycle",
RatbagdLed.Mode.OFF: "off",
RatbagdLed.Mode.ON: "on",
}
supported = sorted([v for k, v in leds.items() if k in l.modes])
print(" " * level + "Modes: {}".format(", ".join(supported)))
@@ -173,13 +161,13 @@ def print_led_caps(d, p, l, level):
def print_button(d, p, b, level):
header = " " * level + "Button: {} is mapped to ".format(b.index)
if b.action_type == RatbagdButton.ACTION_TYPE_BUTTON:
if b.action_type == RatbagdButton.ActionType.BUTTON:
print("{}'button {}'".format(header, b.mapping))
elif b.action_type == RatbagdButton.ACTION_TYPE_SPECIAL:
elif b.action_type == RatbagdButton.ActionType.SPECIAL:
print("{}'{}'".format(header, button_specials_strmap[b.special]))
elif b.action_type == RatbagdButton.ACTION_TYPE_MACRO:
elif b.action_type == RatbagdButton.ActionType.MACRO:
print("{}macro '{}'".format(header, str(b.macro)))
elif b.action_type == RatbagdButton.ACTION_TYPE_NONE:
elif b.action_type == RatbagdButton.ActionType.NONE:
print("{}none".format(header))
else:
print("{}UNKNOWN".format(header))
@@ -221,6 +209,7 @@ def print_device(d, level):
p = d.profiles[0] # there should be always one
print(" " * level + "{} - {}".format(d.id, d.name))
print(" " * level + " Model: {}".format(d.model))
print(" " * level + " Number of Buttons: {}".format(len(p.buttons)))
print(" " * level + " Number of Leds: {}".format(len(p.leds)))
print(" " * level + "Number of Profiles: {}".format(len(d.profiles)))
@@ -279,10 +268,10 @@ def func_led_set(r, args):
pass
else:
leds = {
"breathing": RatbagdLed.MODE_BREATHING,
"cycle": RatbagdLed.MODE_CYCLE,
"off": RatbagdLed.MODE_OFF,
"on": RatbagdLed.MODE_ON,
"breathing": RatbagdLed.Mode.BREATHING,
"cycle": RatbagdLed.Mode.CYCLE,
"off": RatbagdLed.Mode.OFF,
"on": RatbagdLed.Mode.ON,
}
l.mode = leds[mode]
try:
@@ -336,7 +325,7 @@ def func_button_action_set_special(r, args):
def func_button_action_set_macro(r, args):
b, p, d = find_button(r, args)
if not b.ACTION_TYPE_MACRO in b.action_types:
if not b.ActionType.MACRO in b.action_types:
raise RatbagErrorCapability("assigning a macro is not supported on this device")
macro_keys = args.target_macro
@@ -360,7 +349,7 @@ def func_button_action_set_macro(r, args):
if is_timeout:
t = int(s[1:])
macro.append(RatbagdButton.MACRO_WAIT, t)
macro.append(RatbagdButton.Macro.WAIT, t)
else:
if not s.startswith("KEY_") and not s.startswith("BTN_"):
msg = "Don't know how to convert {}".format(s)
@@ -368,9 +357,9 @@ def func_button_action_set_macro(r, args):
code = evdev.ecodes.ecodes[s]
if is_press:
macro.append(RatbagdButton.MACRO_KEY_PRESS, code)
macro.append(RatbagdButton.Macro.KEY_PRESS, code)
if is_release:
macro.append(RatbagdButton.MACRO_KEY_RELEASE, code)
macro.append(RatbagdButton.Macro.KEY_RELEASE, code)
b.macro = macro
commit(d, args)
@@ -418,6 +407,7 @@ def func_report_rate_get_all(r, args):
def func_report_rate_set(r, args):
p, d = find_profile(r, args)
p.report_rate = args.rate_n
commit(d, args)
def func_resolution_get(r, args):
@@ -967,27 +957,7 @@ active profile if none is given.""",
name: 'target_special',
metavar: 'S',
help_str: 'The new special value to assign',
choices: [
"unknown",
"doubleclick",
"wheel-left",
"wheel-right",
"wheel-up",
"wheel-down",
"ratchet-mode-switch",
"resolution-cycle-up",
"resolution-cycle-down",
"resolution-up",
"resolution-down",
"resolution-alternate",
"resolution-default",
"profile-cycle-up",
"profile-cycle-down",
"profile-up",
"profile-down",
"second-mode",
"battery-level",
]
choices: button_special_names
},
],
func: func_button_action_set_special,
@@ -1072,7 +1042,7 @@ active profile if none is given.""",
name: 'mode',
metavar: 'mode',
help_str: 'The mode to set as current',
choices: ['on', 'off', 'cycle', 'breathing'],
choices: led_mode_names,
},
],
},
@@ -1182,7 +1152,7 @@ class RatbagParser(object):
class RatbagParserSwitch(RatbagParser):
def __init__(self, type, name, group=None, switch=[], N_access=None, tag=None, func=None, help=None):
super(RatbagParserSwitch, self).__init__(type, name, group, tag, func, help)
super().__init__(type, name, group, tag, func, help)
self.switch = [classes[obj[of_type]](**obj) for obj in switch]
if N_access is not None:
self.N_access = RatbagParserNAccess(**N_access)
@@ -1236,7 +1206,7 @@ class RatbagParserSwitch(RatbagParser):
class RatbagParserNAccess(RatbagParserSwitch):
def __init__(self, type, name, group=None, switch=[], metavar=None, tag=None, func=None, help=None):
super(RatbagParserNAccess, self).__init__(type, name, group, switch, None, tag, func, help)
super().__init__(type, name, group, switch, None, tag, func, help)
self.metavar = metavar
def _sub_parse(self, parent, input_string, ns):
@@ -1262,7 +1232,7 @@ class RatbagParserNAccess(RatbagParserSwitch):
class RatbagParserSet(RatbagParserSwitch):
def __init__(self, type, name, group=None, switch=[], N_access=None, tag=None, func=None, help=None):
super(RatbagParserSet, self).__init__(type, name, group, switch, N_access, tag, func, help)
super().__init__(type, name, group, switch, N_access, tag, func, help)
def _add_to_subparsers(self, parent):
parser = parent.add_parser(self.name, help=self.help)
@@ -1294,7 +1264,7 @@ class RatbagParserSet(RatbagParserSwitch):
class RatbagParserCommand(RatbagParser):
def __init__(self, type, name, group=None, pos_args=[], tag=None, func=None, help=None):
super(RatbagParserCommand, self).__init__(type, name, group, tag, func, help)
super().__init__(type, name, group, tag, func, help)
self.pos_args = [classes[obj[of_type]](**obj) for obj in pos_args]
def _add_to_subparsers(self, parent):
@@ -1319,7 +1289,7 @@ class RatbagParserCommand(RatbagParser):
class RatbagParserArgument(RatbagParser):
def __init__(self, type, name, group=None, arg_type=None, metavar=None, nargs=None, choices=None, tag=None, func=None, help=None):
super(RatbagParserArgument, self).__init__(type, name, group, tag, func, help)
super().__init__(type, name, group, tag, func, help)
self.arg_type = arg_type
self.metavar = metavar
self.nargs = nargs
@@ -1331,7 +1301,7 @@ class RatbagParserArgument(RatbagParser):
class RatbagParserLink(RatbagParser):
def __init__(self, type, dest=None, group=None, tag=None, func=None, help=None):
super(RatbagParserLink, self).__init__(type, dest, group, tag, func, help)
super().__init__(type, dest, group, tag, func, help)
self.dest = dest
def get_dest(self):
@@ -1495,7 +1465,7 @@ def on_device_added(ratbagd, device):
def open_ratbagd(ratbagd_process=None, verbose=0):
try:
r = Ratbagd()
r = Ratbagd(@RATBAGD_API_VERSION@)
r.verbose = verbose
try:
r.connect('device-added', on_device_added)

View File

@@ -38,10 +38,20 @@ run_ratbagctl_in_subprocess = False
class TestRatbagCtl(unittest.TestCase):
json = None
@classmethod
def setUpClass(cls):
super(TestRatbagCtl, cls).setUpClass()
cls.reset_test_device()
super().setUpClass()
# it's a lot easier to debug a validataion failure here than
# with ratbagd failing
if cls.json is not None:
import json
json.loads(cls.json)
if cls.json is not None:
cls.load_test_device(cls.json)
cls.find_test_device()
@classmethod
@@ -50,7 +60,7 @@ class TestRatbagCtl(unittest.TestCase):
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True,
env={"RATBAGCTL_DEVEL": "org.freedesktop.ratbag_devel1_@ratbagd_sha@"})
env={"RATBAG_TEST": "1"})
(stdout, stderr) = ratbagctl.communicate()
return (ratbagctl.returncode,
@@ -71,9 +81,9 @@ class TestRatbagCtl(unittest.TestCase):
cmd.func(ratbagd, cmd)
except SystemExit as e:
returncode = e.code
except AttributeError as e:
except AttributeError:
returncode = 2
except argparse.ArgumentTypeError as e:
except argparse.ArgumentTypeError:
returncode = 2
output = sys.stdout.getvalue()
error = sys.stderr.getvalue()
@@ -95,6 +105,13 @@ class TestRatbagCtl(unittest.TestCase):
ratbagd._dbus_call("ResetTestDevice", "")
toolbox.sync_dbus()
@classmethod
def load_test_device(cls, json):
global ratbagd
rc = ratbagd._dbus_call("LoadTestDevice", "s", json)
assert(rc == 0)
toolbox.sync_dbus()
@classmethod
def find_test_device(cls):
rc, stdout, stderr = cls.run_ratbagctl_subprocess('list')
@@ -153,24 +170,42 @@ class TestRatbagCtlName(TestRatbagCtl):
class TestRatbagCtlProfile(TestRatbagCtl):
json = '''
{
"profiles": [
{
"is_active": false
},
{
"name": "test profile x2",
"is_active": false
},
{
"is_active": true
},
{
"is_active": false,
"is_disabled": true
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlProfile, cls).setUpClass()
cls.setProfile(0)
cls.setProfile(1)
cls.setProfile(0)
super().setUpClass()
def test_profile_name_get(self):
command = "profile 1 name get"
r = self.launch_good_test("test_device " + command)
self.assertEqual(r, 'test profile 2')
self.assertEqual(r, 'test profile x2')
self.launch_fail_test(command)
self.launch_fail_test("test_device " + command + " X")
self.launch_fail_test("test_device profile name get")
def test_profile_name_set(self):
r = self.launch_good_test("test_device profile 1 name get")
self.assertEqual(r, 'test profile 2')
self.assertEqual(r, 'test profile x2')
self.launch_good_test("test_device profile 1 name set banana")
r = self.launch_good_test("test_device profile 1 name get")
self.assertEqual(r, 'banana')
@@ -186,7 +221,7 @@ class TestRatbagCtlProfile(TestRatbagCtl):
def test_profile_active_get(self):
command = "profile active get"
r = self.launch_good_test("test_device " + command)
self.assertEqual(int(r), 0)
self.assertEqual(int(r), 2)
self.launch_fail_test(command)
self.launch_fail_test("test_device " + command + " X")
@@ -235,10 +270,41 @@ class TestRatbagCtlProfile(TestRatbagCtl):
class TestRatbagCtlResolution(TestRatbagCtl):
json = '''
{
"profiles": [
{
"is_active": false
},
{
"is_active": false
},
{
"is_active": true,
"resolutions": [
{ "is_active": false,
"is_default": true },
{ "xres": 1200, "yres": 1300,
"capabilities": [1],
"is_default": false,
"is_active": false },
{ "xres": 2300, "yres": 2400,
"capabilities": [1],
"is_active": true,
"is_default": false }
]
},
{
"is_active": false,
"is_disabled": true
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlResolution, cls).setUpClass()
cls.setProfile(1)
super().setUpClass()
def test_resolution_active_get(self):
command = "resolution active get"
@@ -267,13 +333,13 @@ class TestRatbagCtlResolution(TestRatbagCtl):
self.launch_fail_test("resolution 1 get X")
def test_profile_resolution(self):
r = self.launch_good_test("test_device profile 2 resolution 2 get")
self.assertEqual(r, "2: 2300x2400dpi")
r = self.launch_good_test("test_device profile 2 resolution 1 get")
self.assertEqual(r, "1: 1200x1300dpi")
def test_resolution_default_get(self):
command = "resolution default get"
r = self.launch_good_test("test_device " + command)
self.assertEqual(int(r), 1)
self.assertEqual(int(r), 0)
self.launch_fail_test("test_device " + command + " X")
self.launch_fail_test("test_device " + command + " 10")
@@ -290,10 +356,46 @@ class TestRatbagCtlResolution(TestRatbagCtl):
class TestRatbagCtlDPI(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"resolutions": [
{ "xres": 200,
"is_active": true,
"dpi_min": 50,
"dpi_max": 5000 },
{ "xres": 250,
"is_active": false },
{ "xres": 300,
"is_active": false }
]
},
{ "is_active": false,
"resolutions": [
{ "xres": 1000, "yres": 1100,
"is_active": false },
{ "xres": 1300, "yres": 1400,
"capabilities": [1],
"is_active": true }
]
},
{ "is_active": false,
"resolutions": [
{ "xres": 2100, "yres": 2200,
"capabilities": [1],
"is_active": true},
{ "xres": 2200, "yres": 2300,
"capabilities": [1],
"is_active": false }
]
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlDPI, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_dpi_get(self):
command = "dpi get"
@@ -351,8 +453,8 @@ class TestRatbagCtlDPI(TestRatbagCtl):
self.setProfile(0)
r = self.launch_good_test("test_device profile 1 dpi get")
self.assertEqual(r, "1300x1400dpi")
r = self.launch_good_test("test_device profile 1 resolution 1 dpi get")
self.assertEqual(r, "1200x1300dpi")
r = self.launch_good_test("test_device profile 2 resolution 1 dpi get")
self.assertEqual(r, "2200x2300dpi")
r = self.launch_good_test("test_device resolution 2 dpi get")
self.assertEqual(int(r[:-3]), 300) # drop 'dpi' suffix
self.launch_fail_test("test_device profile 1 profile 1 dpi get")
@@ -360,10 +462,25 @@ class TestRatbagCtlDPI(TestRatbagCtl):
class TestRatbagCtlReportRate(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"rate": 1000,
"report_rates": [500, 1000]
},
{ "is_active": false,
"rate": 2000
},
{ "is_active": false,
"rate": 3000
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlReportRate, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_rate_get(self):
command = "rate get"
@@ -407,10 +524,38 @@ class TestRatbagCtlReportRate(TestRatbagCtl):
class TestRatbagCtlButton(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"buttons": [
{ "button": 0 },
{ "action_type": "none" },
{ "action_type": "special",
"special": "profile-cycle-up" },
{ "action_type": "macro",
"macro": [ "+B", "-B", "t300" ] }
]
},
{ "is_active": false
},
{ "is_active": false,
"buttons": [
{ "action_type": "special",
"special": "profile-cycle-up" },
{ "action_type": "special" },
{ "button": 2,
"action_type": "button" },
{ "button": 3,
"action_type": "button" }
]
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlButton, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_button_count(self):
command = "button count"
@@ -493,10 +638,57 @@ class TestRatbagCtlButton(TestRatbagCtl):
class TestRatbagCtlLED(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"leds": [
{ "type": 1,
"mode": 0,
"duration": 1000,
"brightness": 20 },
{ "type": 1,
"mode": 1,
"duration": 333,
"brightness": 40,
"color": [255, 0, 0] },
{ "type": 1,
"mode": 2,
"duration": 333,
"brightness": 40 }
]
},
{
"leds": [
{ "type": 1,
"mode": 1 },
{ "type": 1,
"mode": 0 },
{ "type": 1,
"mode": 1 }
]
},
{
"leds": [
{ "type": 1,
"mode": 1,
"color": [255, 0, 0],
"duration": 1000 },
{ "type": 1,
"mode": 2,
"brightness": 40,
"duration": 333 },
{ "type": 1,
"mode": 1 }
]
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlLED, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_led_n_get(self):
self.setProfile(0)
@@ -603,36 +795,51 @@ class TestRatbagCtlLED(TestRatbagCtl):
def setUpModule():
global ratbagd_process, ratbagd, parser
ratbagd_process = toolbox.start_ratbagd()
ratbagd_process = None
if start_ratbagd:
ratbagd_process = toolbox.start_ratbagd()
assert ratbagd_process is not None
assert ratbagd_process is not None
ratbagd = toolbox.open_ratbagd(ratbagd_process)
assert ratbagd is not None
try:
ratbagd = toolbox.open_ratbagd(ratbagd_process)
assert ratbagd is not None
except Exception as e:
if ratbagd_process is not None:
toolbox.terminate_ratbagd(ratbagd_process)
raise e
parser = toolbox.get_parser()
def tearDownModule():
global ratbagd_process
toolbox.terminate_ratbagd(ratbagd_process)
if start_ratbagd:
toolbox.terminate_ratbagd(ratbagd_process)
def parse(input_string):
global run_ratbagctl_in_subprocess
global run_ratbagctl_in_subprocess, start_ratbagd
parser_test = argparse.ArgumentParser(description="Testsuite for ratbagd/ratbagctl", add_help=False)
parser_test.add_argument("--subprocess", action='store_true', default=False)
parser_test.add_argument("--use-existing-ratbagd", dest='use_existing',
action='store_true', default=False,
help='Don\'t start up ratbagd.devel, connect to the already running one')
ns, rest = parser_test.parse_known_args(input_string)
run_ratbagctl_in_subprocess = ns.subprocess
start_ratbagd = not ns.use_existing
if start_ratbagd and not os.geteuid() == 0:
print('Script must be run as root', file=sys.stderr)
sys.exit(77)
return rest
def main(argv):
if not os.geteuid() == 0:
print('Script must be run as root', file=sys.stderr)
sys.exit(77)
os.environ['RATBAG_TEST'] = '1'
os.environ['LIBRATBAG_DATA_DIR'] = '@LIBRATBAG_DATA_DIR@'
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
args = parse(argv)

View File

@@ -62,6 +62,18 @@ class RatbagErrorCode(IntEnum):
IMPLEMENTATION = -1004
class RatbagdIncompatible(Exception):
"""ratbagd is incompatible with this client"""
def __init__(self, ratbagd_version, required_version):
super().__init__()
self.ratbagd_version = ratbagd_version
self.required_version = required_version
self.message = "ratbagd API version is {} but we require {}".format(ratbagd_version, required_version)
def __str__(self):
return self.message
class RatbagdUnavailable(Exception):
"""Signals DBus is unavailable or the ratbagd daemon is not available."""
pass
@@ -116,7 +128,7 @@ class _RatbagdDBus(GObject.GObject):
_dbus = None
def __init__(self, interface, object_path):
GObject.GObject.__init__(self)
super().__init__()
if _RatbagdDBus._dbus is None:
try:
@@ -125,8 +137,8 @@ class _RatbagdDBus(GObject.GObject):
raise RatbagdUnavailable(e.message)
ratbag1 = "org.freedesktop.ratbag1"
if os.environ.get('RATBAGCTL_DEVEL'):
ratbag1 = os.environ['RATBAGCTL_DEVEL']
if os.environ.get('RATBAG_TEST'):
ratbag1 = "org.freedesktop.ratbag_devel1"
if object_path is None:
object_path = "/" + ratbag1.replace('.', '/')
@@ -196,7 +208,7 @@ class _RatbagdDBus(GObject.GObject):
# Calls a method synchronously on the bus, using the given method name,
# type signature and values.
#
# It the result is valid, it is returned. Invalid results raise the
# If the result is valid, it is returned. Invalid results raise the
# appropriate RatbagError* or RatbagdDBus* exception, or GLib.Error if
# it is an unexpected exception that probably shouldn't be passed up to
# the UI.
@@ -238,11 +250,13 @@ class Ratbagd(_RatbagdDBus):
(GObject.SignalFlags.RUN_FIRST, None, ()),
}
def __init__(self):
_RatbagdDBus.__init__(self, "Manager", None)
def __init__(self, api_version):
super().__init__("Manager", None)
result = self._get_dbus_property("Devices") or []
self._devices = [RatbagdDevice(objpath) for objpath in result]
self._proxy.connect("notify::g-name-owner", self._on_name_owner_changed)
if self.api_version != api_version:
raise RatbagdIncompatible(self.api_version or -1, api_version)
def _on_name_owner_changed(self, *kwargs):
self.emit("daemon-disappeared")
@@ -261,6 +275,10 @@ class Ratbagd(_RatbagdDBus):
self.emit("device-removed", device)
self.notify("devices")
@GObject.Property
def api_version(self):
return self._get_dbus_property("APIVersion")
@GObject.Property
def devices(self):
"""A list of RatbagdDevice objects supported by ratbagd."""
@@ -291,7 +309,7 @@ class RatbagdDevice(_RatbagdDBus):
}
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Device", object_path)
super().__init__("Device", object_path)
# FIXME: if we start adding and removing objects from this list,
# things will break!
@@ -370,7 +388,7 @@ class RatbagdProfile(_RatbagdDBus):
CAP_WRITE_ONLY = 103
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Profile", object_path)
super().__init__("Profile", object_path)
self._dirty = False
self._active = self._get_dbus_property("IsActive")
@@ -502,7 +520,7 @@ class RatbagdProfile(_RatbagdDBus):
@GObject.Property
def is_active(self):
"""Returns True if the profile is currenly active, false otherwise."""
"""Returns True if the profile is currently active, false otherwise."""
return self._active
def set_active(self):
@@ -516,7 +534,7 @@ class RatbagdResolution(_RatbagdDBus):
"""Represents a ratbagd resolution."""
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Resolution", object_path)
super().__init__("Resolution", object_path)
self._active = self._get_dbus_property("IsActive")
self._default = self._get_dbus_property("IsDefault")
@@ -595,35 +613,39 @@ class RatbagdResolution(_RatbagdDBus):
class RatbagdButton(_RatbagdDBus):
"""Represents a ratbagd button."""
ACTION_TYPE_NONE = 0
ACTION_TYPE_BUTTON = 1
ACTION_TYPE_SPECIAL = 2
ACTION_TYPE_MACRO = 4
class ActionType(IntEnum):
NONE = 0
BUTTON = 1
SPECIAL = 2
MACRO = 4
ACTION_SPECIAL_INVALID = -1
ACTION_SPECIAL_UNKNOWN = (1 << 30)
ACTION_SPECIAL_DOUBLECLICK = (1 << 30) + 1
ACTION_SPECIAL_WHEEL_LEFT = (1 << 30) + 2
ACTION_SPECIAL_WHEEL_RIGHT = (1 << 30) + 3
ACTION_SPECIAL_WHEEL_UP = (1 << 30) + 4
ACTION_SPECIAL_WHEEL_DOWN = (1 << 30) + 5
ACTION_SPECIAL_RATCHET_MODE_SWITCH = (1 << 30) + 6
ACTION_SPECIAL_RESOLUTION_CYCLE_UP = (1 << 30) + 7
ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN = (1 << 30) + 8
ACTION_SPECIAL_RESOLUTION_UP = (1 << 30) + 9
ACTION_SPECIAL_RESOLUTION_DOWN = (1 << 30) + 10
ACTION_SPECIAL_RESOLUTION_ALTERNATE = (1 << 30) + 11
ACTION_SPECIAL_RESOLUTION_DEFAULT = (1 << 30) + 12
ACTION_SPECIAL_PROFILE_CYCLE_UP = (1 << 30) + 13
ACTION_SPECIAL_PROFILE_CYCLE_DOWN = (1 << 30) + 14
ACTION_SPECIAL_PROFILE_UP = (1 << 30) + 15
ACTION_SPECIAL_PROFILE_DOWN = (1 << 30) + 16
ACTION_SPECIAL_SECOND_MODE = (1 << 30) + 17
ACTION_SPECIAL_BATTERY_LEVEL = (1 << 30) + 18
class ActionSpecial(IntEnum):
INVALID = -1
UNKNOWN = (1 << 30)
DOUBLECLICK = (1 << 30) + 1
WHEEL_LEFT = (1 << 30) + 2
WHEEL_RIGHT = (1 << 30) + 3
WHEEL_UP = (1 << 30) + 4
WHEEL_DOWN = (1 << 30) + 5
RATCHET_MODE_SWITCH = (1 << 30) + 6
RESOLUTION_CYCLE_UP = (1 << 30) + 7
RESOLUTION_CYCLE_DOWN = (1 << 30) + 8
RESOLUTION_UP = (1 << 30) + 9
RESOLUTION_DOWN = (1 << 30) + 10
RESOLUTION_ALTERNATE = (1 << 30) + 11
RESOLUTION_DEFAULT = (1 << 30) + 12
PROFILE_CYCLE_UP = (1 << 30) + 13
PROFILE_CYCLE_DOWN = (1 << 30) + 14
PROFILE_UP = (1 << 30) + 15
PROFILE_DOWN = (1 << 30) + 16
SECOND_MODE = (1 << 30) + 17
BATTERY_LEVEL = (1 << 30) + 18
MACRO_KEY_PRESS = 1
MACRO_KEY_RELEASE = 2
MACRO_WAIT = 3
class Macro(IntEnum):
NONE = 0
KEY_PRESS = 1
KEY_RELEASE = 2
WAIT = 3
"""A table mapping a button's index to its usual function as defined by X
and the common desktop environments."""
@@ -637,33 +659,33 @@ class RatbagdButton(_RatbagdDBus):
"""A table mapping a special function to its human-readable description."""
SPECIAL_DESCRIPTION = {
ACTION_SPECIAL_INVALID: N_("Invalid"),
ACTION_SPECIAL_UNKNOWN: N_("Unknown"),
ACTION_SPECIAL_DOUBLECLICK: N_("Doubleclick"),
ACTION_SPECIAL_WHEEL_LEFT: N_("Wheel Left"),
ACTION_SPECIAL_WHEEL_RIGHT: N_("Wheel Right"),
ACTION_SPECIAL_WHEEL_UP: N_("Wheel Up"),
ACTION_SPECIAL_WHEEL_DOWN: N_("Wheel Down"),
ACTION_SPECIAL_RATCHET_MODE_SWITCH: N_("Ratchet Mode"),
ACTION_SPECIAL_RESOLUTION_CYCLE_UP: N_("Cycle Resolution Up"),
ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN: N_("Cycle Resolution Down"),
ACTION_SPECIAL_RESOLUTION_UP: N_("Resolution Up"),
ACTION_SPECIAL_RESOLUTION_DOWN: N_("Resolution Down"),
ACTION_SPECIAL_RESOLUTION_ALTERNATE: N_("Resolution Switch"),
ACTION_SPECIAL_RESOLUTION_DEFAULT: N_("Default Resolution"),
ACTION_SPECIAL_PROFILE_CYCLE_UP: N_("Cycle Profile Up"),
ACTION_SPECIAL_PROFILE_CYCLE_DOWN: N_("Cycle Profile Down"),
ACTION_SPECIAL_PROFILE_UP: N_("Profile Up"),
ACTION_SPECIAL_PROFILE_DOWN: N_("Profile Down"),
ACTION_SPECIAL_SECOND_MODE: N_("Second Mode"),
ACTION_SPECIAL_BATTERY_LEVEL: N_("Battery Level"),
ActionSpecial.INVALID: N_("Invalid"),
ActionSpecial.UNKNOWN: N_("Unknown"),
ActionSpecial.DOUBLECLICK: N_("Doubleclick"),
ActionSpecial.WHEEL_LEFT: N_("Wheel Left"),
ActionSpecial.WHEEL_RIGHT: N_("Wheel Right"),
ActionSpecial.WHEEL_UP: N_("Wheel Up"),
ActionSpecial.WHEEL_DOWN: N_("Wheel Down"),
ActionSpecial.RATCHET_MODE_SWITCH: N_("Ratchet Mode"),
ActionSpecial.RESOLUTION_CYCLE_UP: N_("Cycle Resolution Up"),
ActionSpecial.RESOLUTION_CYCLE_DOWN: N_("Cycle Resolution Down"),
ActionSpecial.RESOLUTION_UP: N_("Resolution Up"),
ActionSpecial.RESOLUTION_DOWN: N_("Resolution Down"),
ActionSpecial.RESOLUTION_ALTERNATE: N_("Resolution Switch"),
ActionSpecial.RESOLUTION_DEFAULT: N_("Default Resolution"),
ActionSpecial.PROFILE_CYCLE_UP: N_("Cycle Profile Up"),
ActionSpecial.PROFILE_CYCLE_DOWN: N_("Cycle Profile Down"),
ActionSpecial.PROFILE_UP: N_("Profile Up"),
ActionSpecial.PROFILE_DOWN: N_("Profile Down"),
ActionSpecial.SECOND_MODE: N_("Second Mode"),
ActionSpecial.BATTERY_LEVEL: N_("Battery Level"),
}
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Button", object_path)
super().__init__("Button", object_path)
def _on_properties_changed(self, proxy, changed_props, invalidated_props):
if "ActionType" in changed_props.keys():
if "Mapping" in changed_props.keys():
self.notify("action-type")
def _mapping(self):
@@ -679,7 +701,7 @@ class RatbagdButton(_RatbagdDBus):
"""An integer of the current button mapping, if mapping to a button
or None otherwise."""
type, button = self._mapping()
if type != RatbagdButton.ACTION_TYPE_BUTTON:
if type != RatbagdButton.ActionType.BUTTON:
return None
return button
@@ -691,14 +713,14 @@ class RatbagdButton(_RatbagdDBus):
"""
button = GLib.Variant("u", button)
self._set_dbus_property("Mapping", "(uv)",
(RatbagdButton.ACTION_TYPE_BUTTON, button))
(RatbagdButton.ActionType.BUTTON, button))
@GObject.Property
def macro(self):
"""A RatbagdMacro object representing the currently set macro or
None otherwise."""
type, macro = self._mapping()
if type != RatbagdButton.ACTION_TYPE_MACRO:
if type != RatbagdButton.ActionType.MACRO:
return None
return RatbagdMacro.from_ratbag(macro)
@@ -712,14 +734,14 @@ class RatbagdButton(_RatbagdDBus):
"""
macro = GLib.Variant("a(uu)", macro.keys)
self._set_dbus_property("Mapping", "(uv)",
(RatbagdButton.ACTION_TYPE_MACRO, macro))
(RatbagdButton.ActionType.MACRO, macro))
@GObject.Property
def special(self):
"""An enum describing the current special mapping, if mapped to
special or None otherwise."""
type, special = self._mapping()
if type != RatbagdButton.ACTION_TYPE_SPECIAL:
if type != RatbagdButton.ActionType.SPECIAL:
return None
return special
@@ -727,17 +749,17 @@ class RatbagdButton(_RatbagdDBus):
def special(self, special):
"""Set the button mapping to the given special entry.
@param special The special entry, as one of RatbagdButton.ACTION_SPECIAL_*
@param special The special entry, as one of RatbagdButton.ActionSpecial
"""
special = GLib.Variant("u", special)
self._set_dbus_property("Mapping", "(uv)",
(RatbagdButton.ACTION_TYPE_SPECIAL, special))
(RatbagdButton.ActionType.SPECIAL, special))
@GObject.Property
def action_type(self):
"""An enum describing the action type of the button. One of
ACTION_TYPE_NONE, ACTION_TYPE_BUTTON, ACTION_TYPE_SPECIAL,
ACTION_TYPE_MACRO. This decides which
ActionType.NONE, ActionType.BUTTON, ActionType.SPECIAL,
ActionType.MACRO. This decides which
*Mapping property has a value.
"""
type, mapping = self._mapping()
@@ -765,11 +787,11 @@ class RatbagdMacro(GObject.Object):
_MACRO_KEY = 1000
_MACRO_DESCRIPTION = {
RatbagdButton.MACRO_KEY_PRESS: lambda key:
RatbagdButton.Macro.KEY_PRESS: lambda key:
"↓{}".format(ecodes.KEY[key][RatbagdMacro._PREFIX_LEN:]),
RatbagdButton.MACRO_KEY_RELEASE: lambda key:
RatbagdButton.Macro.KEY_RELEASE: lambda key:
"↑{}".format(ecodes.KEY[key][RatbagdMacro._PREFIX_LEN:]),
RatbagdButton.MACRO_WAIT: lambda val:
RatbagdButton.Macro.WAIT: lambda val:
"{}ms".format(val),
_MACRO_KEY: lambda key:
"↕{}".format(ecodes.KEY[key][RatbagdMacro._PREFIX_LEN:]),
@@ -780,7 +802,7 @@ class RatbagdMacro(GObject.Object):
}
def __init__(self, **kwargs):
GObject.Object.__init__(self, **kwargs)
super().__init__(**kwargs)
self._macro = []
def __str__(self):
@@ -793,10 +815,10 @@ class RatbagdMacro(GObject.Object):
while idx < len(self._macro):
t, v = self._macro[idx]
try:
if t == RatbagdButton.MACRO_KEY_PRESS:
if t == RatbagdButton.Macro.KEY_PRESS:
# Check for a paired press/release event
t2, v2 = self._macro[idx + 1]
if t2 == RatbagdButton.MACRO_KEY_RELEASE and v == v2:
if t2 == RatbagdButton.Macro.KEY_RELEASE and v == v2:
t = self._MACRO_KEY
idx += 1
except IndexError:
@@ -807,7 +829,7 @@ class RatbagdMacro(GObject.Object):
@GObject.Property
def keys(self):
"""A list of (RatbagdButton.MACRO_*, value) tuples representing the
"""A list of (RatbagdButton.Macro.*, value) tuples representing the
current macro."""
return self._macro
@@ -817,7 +839,7 @@ class RatbagdMacro(GObject.Object):
libratbag format.
@param macro The macro in libratbag format, as
[(RatbagdButton.MACRO_*, value)].
[(RatbagdButton.Macro.*, value)].
"""
ratbagd_macro = RatbagdMacro()
@@ -834,7 +856,7 @@ class RatbagdMacro(GObject.Object):
def append(self, type, value):
"""Appends the given event to the current macro.
@param type The type of event, as one of RatbagdButton.MACRO_*.
@param type The type of event, as one of RatbagdButton.Macro.*.
@param value If the type denotes a key event, the X.Org or Gdk keycode
of the event, as int. Otherwise, the value of the timeout
in milliseconds, as int.
@@ -855,29 +877,31 @@ class RatbagdLed(_RatbagdDBus):
TYPE_DPI = 4
TYPE_WHEEL = 5
MODE_OFF = 0
MODE_ON = 1
MODE_CYCLE = 2
MODE_BREATHING = 3
class Mode(IntEnum):
OFF = 0
ON = 1
CYCLE = 2
BREATHING = 3
COLORDEPTH_MONOCHROME = 0
COLORDEPTH_RGB_888 = 1
COLORDEPTH_RGB_111 = 2
class ColorDepth(IntEnum):
MONOCHROME = 0
RGB_888 = 1
RGB_111 = 2
LED_DESCRIPTION = {
# Translators: the LED is off.
MODE_OFF: N_("Off"),
Mode.OFF: N_("Off"),
# Translators: the LED has a single, solid color.
MODE_ON: N_("Solid"),
Mode.ON: N_("Solid"),
# Translators: the LED is cycling between red, green and blue.
MODE_CYCLE: N_("Cycle"),
Mode.CYCLE: N_("Cycle"),
# Translators: the LED's is pulsating a single color on different
# brightnesses.
MODE_BREATHING: N_("Breathing"),
Mode.BREATHING: N_("Breathing"),
}
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Led", object_path)
super().__init__("Led", object_path)
@GObject.Property
def index(self):
@@ -886,16 +910,16 @@ class RatbagdLed(_RatbagdDBus):
@GObject.Property
def mode(self):
"""This led's mode, one of MODE_OFF, MODE_ON, MODE_CYCLE and
MODE_BREATHING."""
"""This led's mode, one of Mode.OFF, Mode.ON, Mode.CYCLE and
Mode.BREATHING."""
return self._get_dbus_property("Mode")
@mode.setter
def mode(self, mode):
"""Set the led's mode to the given mode.
@param mode The new mode, as one of MODE_OFF, MODE_ON, MODE_CYCLE and
MODE_BREATHING.
@param mode The new mode, as one of Mode.OFF, Mode.ON, Mode.CYCLE and
Mode.BREATHING.
"""
self._set_dbus_property("Mode", "u", mode)
@@ -920,7 +944,7 @@ class RatbagdLed(_RatbagdDBus):
@GObject.Property
def colordepth(self):
"""An enum describing this led's colordepth, one of
RatbagdLed.COLORDEPTH_MONOCHROME, RatbagdLed.COLORDEPTH_RGB"""
RatbagdLed.ColorDepth.MONOCHROME, RatbagdLed.ColorDepth.RGB"""
return self._get_dbus_property("ColorDepth")
@GObject.Property

View File

@@ -25,7 +25,6 @@
import imp
import os
import shutil
import subprocess
import sys
@@ -36,9 +35,6 @@ RATBAGCTL_NAME = 'ratbagctl'
RATBAGCTL_PATH = os.path.join('@MESON_BUILD_ROOT@', RATBAGCTL_NAME)
RATBAGCTL_DEVEL_NAME = 'ratbagctl.devel'
RATBAGCTL_DEVEL_PATH = os.path.join('@MESON_BUILD_ROOT@', RATBAGCTL_DEVEL_NAME)
DBUS_CONF_DIR = '/etc/dbus-1/system.d'
DBUS_CONF_NAME = 'org.freedesktop.ratbag_devel1.conf'
DBUS_CONF_PATH = os.path.join(DBUS_CONF_DIR, DBUS_CONF_NAME)
def import_non_standard_path(name, path):
@@ -61,12 +57,6 @@ def start_ratbagd(verbosity=0):
from gi.repository import Gio
import time
os.makedirs(DBUS_CONF_DIR, exist_ok=True)
# first copy the policy for the ratbagd daemon to be allowed to run
shutil.copy(os.path.join('@MESON_BUILD_ROOT@', DBUS_CONF_NAME),
DBUS_CONF_PATH)
# FIXME: kill any running ratbagd.devel
args = [os.path.join('@MESON_BUILD_ROOT@', "ratbagd.devel")]
@@ -88,15 +78,15 @@ def start_ratbagd(verbosity=0):
proxy = Gio.DBusProxy.new_sync(dbus,
Gio.DBusProxyFlags.NONE,
None,
"org.freedesktop.ratbag_devel1_@ratbagd_sha@",
"/org/freedesktop/ratbag_devel1_@ratbagd_sha@",
"org.freedesktop.ratbag_devel1_@ratbagd_sha@.Manager",
"org.freedesktop.ratbag_devel1",
"/org/freedesktop/ratbag_devel1",
"org.freedesktop.ratbag_devel1.Manager",
None)
name_owner = proxy.get_name_owner()
if name_owner is None:
time.sleep(0.2)
os.environ['RATBAGCTL_DEVEL'] = "org.freedesktop.ratbag_devel1_@ratbagd_sha@"
os.environ['RATBAG_TEST'] = "1"
if name_owner is None or ratbagd_process.poll() is not None:
return None
@@ -111,10 +101,6 @@ def terminate_ratbagd(ratbagd):
ratbagd.wait(5)
except subprocess.TimeoutExpired:
ratbagd.kill()
try:
os.unlink(DBUS_CONF_PATH)
except FileNotFoundError:
pass
def sync_dbus():
@@ -125,14 +111,11 @@ def sync_dbus():
ratbagctl = import_non_standard_path(RATBAGCTL_NAME, RATBAGCTL_PATH)
from ratbagctl import open_ratbagd, get_parser, RatbagError, RatbagErrorCapability
from ratbagctl import open_ratbagd, get_parser, RatbagError, RatbagErrorCapability # NOQA
__all__ = [
RATBAGCTL_NAME,
RATBAGCTL_PATH,
DBUS_CONF_DIR,
DBUS_CONF_NAME,
DBUS_CONF_PATH,
start_ratbagd,
terminate_ratbagd,
open_ratbagd,