Compare commits

...

162 Commits
v0.4 ... v0.9

Author SHA1 Message Date
Benjamin Tissoires
7da53c61cf meson.build: libratbag 0.9
- convert Logitech devices to the commit API (those compatible)
- fixes for ratbag-command regarding LEDs (still few caveats left)
- some coverity fixes
- a brand new lur-command man page

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:26:37 +02:00
Benjamin Tissoires
714c0d5330 hidpp20: reset the LEDs before writing to it
We somehow now know how to write effects.
No need to carry around some left overs from previous configurations.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Benjamin Tissoires
dc92abb5e5 tools: replace tabs by spaces in ratbag-command usage
It's a mix of tabs and spaces. Let's use spaces to keep the formatting.

And move "switch-etekcity" back to the end, it's a special case.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Benjamin Tissoires
b0c515b59d tools: allow to set more than one setting for the LEDs
Or we will take the defaults that are not intuitive (like white color
when setting to on).

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Benjamin Tissoires
8e6c13832e hidpp20: led: document the internal structure and match the documentation
Shouldn't change the behavior, but this allows adding the other effects
found in the documentation.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Benjamin Tissoires
e33f655f61 hidpp20: do not waste memory
Currently we only support mice with 2 LEDs in the profile. No need
to have 32 placeholders.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Benjamin Tissoires
8e8b7b0611 hidpp20: make sure we read/write correct endianness to the device
rate is big endian, so we need to convert it.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Benjamin Tissoires
d28f2f0123 hidpp20: keep function names consistent
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Benjamin Tissoires
a8eea870f5 hidpp20: make sure we check all modes in the switch statement
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Benjamin Tissoires
7e12e45311 hidpp20-drv: no need to use doubles
We are using a uint in the end, so better have proper order in the
multiplications/divisions than using doubles.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Benjamin Tissoires
b31c1d5ff3 tools: ratbag-command: have only one function to dump the LED settings
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Benjamin Tissoires
f6949458b7 tools: make led a first class citizen of ratbag-command
Allows to follow the usage message: we do not need to provide the
currently active profile.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Benjamin Tissoires
15dd6b4f2b hidpp20: color effects, fix endianness
We are receiving big endian, thus the weird power of two locations.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 15:03:47 +02:00
Stephen Kitt
7afd59149c Add lur-command manpage
This is copied from the Debian package.

Signed-off-by: Stephen Kitt <steve@sk2.org>
2017-05-30 14:11:48 +10:00
Stephen Kitt
a47f506293 Fix ratbag-command manpage
* Description capitalisation
* Grammar
* Hyphen encoding
* --version

Signed-off-by: Stephen Kitt <steve@sk2.org>
2017-05-30 14:11:48 +10:00
Benjamin Tissoires
f243abb65c release.sh: remove unreachable test now that we switched to meson
and I must say, why would you not run this script in the current
directory.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-05-29 17:29:12 +10:00
Benjamin Tissoires
a4b177436d hidpp20: take into account sectors on 16bits
First part of fixing #122.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-05-29 10:33:33 +10:00
Benjamin Tissoires
8367569236 hidpp10: fix coverity issue
profile->dpi_modes[i].led[] is an array of boolean.
Given that the callers (hidpp10_fill_dpi_modes_*()) already are doing the
comparison against 0x02, I guess this is just copy/paste error.

Fixes #152

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-05-29 10:32:08 +10:00
Benjamin Tissoires
5bbb2b28ea hidpp20drv: switch to the commit API
I do not like duplicating ratbag_old_write_profile() into each driver,
but, heh.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-05-29 09:15:07 +10:00
bentiss
2b92ab6dd6 hidpp20drv: fix segfault with G502 (#163)
When using the G502 without RGB LEDs, the feature 8070 is not here.
And this segfaults :)

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-05-24 11:52:56 +03:00
Christian Kellner
c05c3dcaa3 tools: consistently call led mode "breathing"
Rename "breath" in the help text and reporting for the led mode to
"breathing". The actual command to set the mode was already called
that way.

Signed-off-by: Christian Kellner <christian@kellner.me>
2017-05-24 09:05:15 +10:00
Benjamin Tissoires
bf20845249 meson.build: libratbag 0.8
- coverity fixes
- new --version to ratbag-command
- cleaning up the meson build tools

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-05-09 14:55:15 +02:00
Peter Hutterer
eb01399f26 tools: fix OOB for LED color parsing
Let's not forget about the null-terminator, he feels lonely otherwise.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-09 10:14:47 +02:00
Peter Hutterer
c815654c94 tools: add --version to ratbag-command
Behold, the glory of our version number!

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-09 09:24:41 +02:00
Peter Hutterer
f1ce908b50 meson: quote the libratbag version properly
Otherwise ETOOMANYDECIMALSEPARATORS, confusion, mayham, anarchy, the lot.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-09 09:24:41 +02:00
Peter Hutterer
fb07204d9f meson: drop garbage argument from configure_file
Unsurprisingly, meson does not support the ratbag keyword argument. Though it
should, because we're great. But anyway, this lead to the argument being
ignored which means it didn't get an 'input' argument, which means that
libratbag-version.h.in was ignored. And since it all works anyway (Behold! The
magic! Ooooh! Aaaah!) we can just drop that file.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-09 09:24:41 +02:00
Peter Hutterer
1e5648b11d tools: terminate option list with a zero entry
Because, well, that's what we're supposed to do. Unless you want to think
outside the (array) box, but that has never helped anyone, has it?

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-09 09:24:41 +02:00
Peter Hutterer
0902e3420f tools: replace getopt magic number 2 with 'optional_argument'
So *that's* what 2 means...

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-09 09:24:41 +02:00
Peter Hutterer
698d8ab365 Replace ifndef include guards with pragma once
Now that we use meson exclusively and meson uses this for its config.h, you
wouldn't get around it anyway. Besides, the main compilers support it and
libratbag is too niche to worry about others.

This also *adds* the pragma to tools/shared.h where it was previously missing,
and changes it to be the top statement in hidpp-generic.h

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-05 09:02:15 +02:00
Peter Hutterer
ad1fa22196 hidpp10: fix out-of-bounds access
sizeof() gives us twice as many elements as we have indices for on this 16-bit
array.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:52:59 +02:00
Peter Hutterer
04ebd96bb7 roccat: fix the neverending story^Wtimeout
If we don't change count, the value of count < MAX_RETRY is always true.

Coverity is a champ, isn't it?

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:52:59 +02:00
Peter Hutterer
09b5ae14b1 liblur: fix memory leak
Rather than having a bunch of gotos just define a cleanup for this device and
let the magic do the rest.

Found by coverity

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:52:59 +02:00
Peter Hutterer
8aa127186c gskill: fix memory leak
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:52:59 +02:00
Peter Hutterer
a962082173 hidpp20: constify a helper function
Just because we can

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:52:59 +02:00
Peter Hutterer
5c0dbe4d5c hidpp20: fix dereference before null check
Found by coverity

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:52:59 +02:00
Peter Hutterer
5934816b11 tools: check for negative index coming from argv
Coverity is unhappy.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:52:59 +02:00
Peter Hutterer
28dd84713f Reset the button action struct to zero before use
A semi false-positive from Coverity, because action.macro is unset. Doesn't
matter because we never use it, but having it zeroed is the nice thing to do
(and what we do in the other functions).

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:52:59 +02:00
Peter Hutterer
11b31cf9bd Fix a comment typo
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:52:59 +02:00
Peter Hutterer
dfd2d88953 tools: fix coverity warnings
Assigned but not used.

Found by coverity

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:52:59 +02:00
Peter Hutterer
6a097bd691 meson: drop -g, the meson buildtype option takes care of that
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:49:34 +02:00
Peter Hutterer
2776d1ce8e meson: use warning_level=2 instead of manual -Wall -Wextra
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:49:34 +02:00
Peter Hutterer
0834b210ca Remove now-obsolete autogen.sh script
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-26 08:26:07 +02:00
Peter Hutterer
cc41adbbf9 gitignore: drop build/ from gitignore again
As pointed out elsewhere, the build directory is decided by the user and
should thus be a user-specific gitignore configuration.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-26 08:26:07 +02:00
Peter Hutterer
63a666025f Update gitignore for meson
Drop all automake detritus, leave only the default build directory meson
suggests

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-25 08:11:40 +02:00
Peter Hutterer
08ce9773dd Remove liblur.pc.in
Should've been removed with b8ed1ea

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-24 10:11:38 +02:00
Peter Hutterer
f745734d90 meson: generate the libratbag.pc file through configure_file
pkgconfig.generate() does not (yet) have a parameter for setting custom
variables - we need that for pkgdatadir to point to the SVG files. Swap to
using configure_file with the old .pc.in file that was forgotten in the tree
anyway.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-24 10:11:38 +02:00
Benjamin Tissoires
5443083b1d meson.build: libratbag 0.7
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-04-20 11:59:48 +02:00
Benjamin Tissoires
cc19496699 Fix release.sh for meson
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-04-20 11:59:48 +02:00
Peter Hutterer
b595691c5e meson: break up the two long error messages
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-20 11:28:05 +02:00
Peter Hutterer
7d784a1a91 doc: use USE_MDFILE_AS_MAINPAGE to include the README.md as mainpage
Supported as of doxygen 1.8.3 (Dec 2012), see
http://www.stack.nl/~dimitri/doxygen/manual/changelog.html#log_1_8_3

Fixes #145

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-20 11:28:05 +02:00
Peter Hutterer
a664042f64 meson: add doxygen input files to source list
To rebuild the documentation when either changes

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-20 11:28:05 +02:00
Benjamin Tissoires
5bc4470a8c Fix warning: ‘x’ may be used uninitialized in this function
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-04-20 11:20:07 +02:00
Peter Hutterer
b8ed1ea3a4 Remove autotools build system
And thus drop the code to parse the top_srcdir from the arguments to the
symbols-leak-test, not the env.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-19 14:40:57 +02:00
Peter Hutterer
f2ad49cf20 Add meson build file
For a builddir called 'builddir', the following commands are sufficient now:

    meson builddir
    ninja -C builddir
    sudo ninja -C builddir install

Meson only needs to be run once after checkout or after the removal of the
builddir.

Fixes https://github.com/libratbag/libratbag/issues/142

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-19 14:40:57 +02:00
Peter Hutterer
fd6a9e73ed test: always run the symbols-leak-test
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-19 14:40:57 +02:00
Peter Hutterer
db57a8e7b6 test: pass TOP_SRCDIR via the env or as $1
Prep work for meson, even though we'll eventuall drop the env bit. This just
removes the extra symbols-leak-test.in step.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-19 14:40:57 +02:00
Peter Hutterer
2a35bf009f configure.ac: drop "auto" detection of test dependencies
Always build the tests, if someone doesn't want it disable it with a configure
flag but at least be explicit about it.

Justification: any developer should build the full stack as intended by
default. Distributions may disable bits and pieces, but they shouldn't rely on
auto-detection to figure it out, they should explicity disable those bits they
don't want.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-04 10:00:38 +02:00
Peter Hutterer
42d49d74ab configure.ac: drop "auto" detection of doxygen dependencies
Always build the documentation, if someone doesn't want it disable it with a
configure flag but at least be explicit about it.

Justification: any developer should build the full stack as intended by
default. Distributions may disable bits and pieces, but they shouldn't rely on
auto-detection to figure it out, they should explicity disable those bits they
don't want.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-04 10:00:38 +02:00
Peter Hutterer
a9f34249fc configure.ac: remove an erroneous quotation mark
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-04 10:00:38 +02:00
Peter Hutterer
1ce59bcc1f doc: drop the custom INPUT to doxygen
Not necessary, we have it in the doxygen.in
2017-04-03 09:55:53 +02:00
Peter Hutterer
bb35821c45 tools: fix signed vs unsigned comparison warnings
And use this to start using C99 variable style, for pure loop variables it's
better to have them declared in the loop itself.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-03 09:55:53 +02:00
Peter Hutterer
bbec42c426 tools: include config.h for ratbag-command
And that then pulls in _GNU_SOURCE for us

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-03 09:55:53 +02:00
Peter Hutterer
cb487a6d1a tools: fix compiler warning 'no previous prototype'
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-03 09:55:53 +02:00
Peter Hutterer
7fdcf62bf2 tools: fix compiler warning
../tools/shared.c:132:1: warning: ‘static’ is not at beginning of declaration [-Wold-style-declaration]

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-03 09:55:53 +02:00
Peter Hutterer
f82c1119bf tools: fix Makefile, it's SOURCES, not SOURCeS
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-03 09:55:53 +02:00
Peter Hutterer
f60f8a12ab tools: fix string parsing for LED colors 2017-03-31 15:07:01 +10:00
Peter Hutterer
fd348d456d tools: add missing LED_OFF switch case
And add the missing break
2017-03-31 15:07:01 +10:00
Peter Hutterer
0f73a97bc8 tools: move variable declaration up
We do C99, but not this :)
2017-03-31 15:07:01 +10:00
Peter Hutterer
7783f50110 tools: fix unused variable warnings 2017-03-31 15:07:01 +10:00
Benjamin Tissoires
b85fcd0cee hwdb: add missing G900 over USB entry
Allows to have the nice picture

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-03-22 11:49:19 +01:00
Benjamin Tissoires
3c879b7389 configure.ac: libratbag 0.6
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-03-22 11:05:22 +01:00
Benjamin Tissoires
44e7bc1c23 G-Skill: shut up compiler warnings
driver-gskill.c: In function ‘gskill_read_button’:
driver-gskill.c:791:3: warning: ‘event_data’ may be used uninitialized in this function [-Wmaybe-uninitialized]
   ratbag_button_macro_set_event(macro, event_idx, type,
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            event_data);
            ~~~~~~~~~~~
driver-gskill.c:791:3: warning: ‘type’ may be used uninitialized in this function [-Wmaybe-uninitialized]
   ratbag_button_macro_set_event(macro, event_idx, type,
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            event_data);
            ~~~~~~~~~~~

Add a default case which should never get reached.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-03-22 11:05:22 +01:00
Benjamin Tissoires
0bcaeaeb33 doctext: fix various unresolved links
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-03-22 11:05:22 +01:00
Ilia guterman
da60f0748c driver-hidpp20: Add LED API (#113)
Read colors and effects from hidpp20 capable device
2017-03-22 02:25:00 +02:00
Wolf480pl
5418392143 Add more button scancodes to driver-logitech-g300
Apparently the mouse can be configured to send scancodes
for buttons 6-9.

Note: there's no meaning attached to scancode 0x118 in the kernel,
which is sent by button 9, but xev does detect it.
2017-03-21 09:15:43 +01:00
Ilia guterman
6c18b16a00 align ratbag-command usage documentation (#135) 2017-03-08 01:44:29 +02:00
Ilya Guterman
8e5ad717c4 ratbag-command: implement LED commands 2017-03-07 11:07:50 +10:00
alcomposer
1ff263f0a4 Add g300, g303, g900 SVG links to HWDB 2017-03-07 10:50:14 +10:00
alcomposer
e9f0ca4385 SVG Logitech G900 Data 2017-03-07 10:50:14 +10:00
alcomposer
079b67f111 add SVG Logitech G303 data (#131)
An SVG of the Logitech G303 mouse, can also pass as the G302 as very little difference between models
2017-03-06 16:56:03 +01:00
alcomposer
a224ef6d0b add logitech g300 svg data
Logitech g300 svg data. I don't have this mouse so not 100% sure about mappings.
2017-03-06 10:49:45 +10:00
Ilya Guterman
0c0a6d39cb Add the ability to set LED effects 2017-02-22 10:10:03 +10:00
Ilya Guterman
3439058a54 fix indentation in documentation 2017-02-17 15:02:43 +10:00
Ilya Guterman
6414f7901f hidpp20: Implement feature 0x8070: LED effects 2017-02-13 10:24:26 +10:00
mxmgh
4d6390ea66 Teach ratbag-command the "ratchet mode switch" special action name 2017-02-13 08:10:45 +10:00
Ilya Guterman
3f83d5057d libratbag api support LEDs 2017-02-10 09:48:32 +10:00
Clément Vuchener
67f1c00181 hidpp10: Fix and add some special action codes
I fixed resolution cycle up actually being resultion cycle down and added the missing cycle actions for resolutions and profiles.

Signed-off-by: Clément Vuchener <clement.vuchener@gmail.com>
2017-01-24 07:41:22 +10:00
Clément Vuchener
c4013ca4b0 Add special actions for cycling down resolutions and profiles
Signed-off-by: Clément Vuchener <clement.vuchener@gmail.com>
2017-01-24 07:41:22 +10:00
Thomas Hindoe Paaboel Andersen
25294d9a63 Add driver for the Logitech G300/G300s
Supports the common functionality exposed by libratbag:
 - selecting profile 1-3 (profiles cannot be disabled)
 - selecting resolution 1-4 (can also not be disabled)
 - report frequency
 - default resolution per profile
 - button actions: mouse button numbers or key+modifier
   (modifiers are just a bit field and can easly be set
   in the driver, but not included as libratbag does not
   support it yet)
 - LEDs are not yet hooked up but are just a table of 7
   different predifined colors.
 - Seting active profile and resolution are both implemented
   but only profile activation is in use. The resolution
   activation code is commented until supported from libratbag
   itself.

The G300 has an unusual way of numbering buttons:
Left:1, Right:2, Middle: 3

The purpose of report type 0xF2 is not known.
2017-01-09 09:33:58 +01:00
Ilya Guterman
7d241642f9 hidpp20: first parsing of feature 0x8070 - onboard profiles 2017-01-04 10:50:21 +10:00
Stephen Kitt
d1e42ac56c Copy all three bytes from parameters
In hidpp10_get_hidpp_notifications(), we want all three bytes from
parameters[], not parameters[0] twice. This matches
hidpp10_set_hidpp_notifications().

Signed-off-by: Stephen Kitt <skitt@redhat.com>
2017-01-04 07:29:13 +10:00
Ilya Guterman
45129b2bc8 hidpp20: define internal LED structure 2016-12-05 14:09:22 +10:00
Lyude
0568b4c39d libratbag-private.h: Fix space before brace
This was bugging me :(.

Signed-off-by: Lyude <lyude@redhat.com>
2016-11-22 20:54:18 -05:00
Lyude
5b152a6c3f gskill: Add some missing profile fields
Must have missed these somewhere down the line when I was reading the
specs for this…

Signed-off-by: Lyude <lyude@redhat.com>
2016-11-22 19:25:09 -05:00
Lyude
52d4e35192 Add the ability to enable or disable profiles
This is especially important for the mx780, so we can get rid of the
rude workaround we currently have in the driver for it.

Signed-off-by: Lyude <lyude@redhat.com>

Changes since v1:
- Just have profile enable/disable in ratbag-command
- Use bool instead of int
Changes since v2:
- Remove leftover function, whoops
- Fix leftover ints to bools
- Lots of style fixes
2016-11-16 09:45:01 +10:00
Lyude
a1a3750bc8 gskill: Add profile_to_pdata()
Saves me some typing :).

Changes since v1:
- Turn profile_to_pdata() into inline function
2016-11-16 09:45:01 +10:00
Thomas Hindoe Paaboel Andersen
3cfda944d0 Update report rate on all resolutions if needed
For drivers without the capability
RATBAG_RESOLUTION_CAP_INDIVIDUAL_REPORT_RATE
we should update the report rate on all resolutions.
2016-10-28 09:30:52 +10:00
Thomas Hindoe Paaboel Andersen
852fc2da2d gitignore: add test-iconv-helper 2016-10-27 11:10:59 +10:00
Thomas Hindoe Paaboel Andersen
adefb2921b unset previous default resolution
Make sure that there can only be one resolution marked
as default.

The previous code allowed to set multiple resolutions
as default. To clean up any bad state we allways loop
all other resolutions and unset the default if neccesary.
2016-10-27 11:05:50 +10:00
Thomas Hindoe Paaboel Andersen
27a141fd87 only set dirty when changed 2016-10-27 11:05:50 +10:00
Lyude
2b63ca4935 gskill: Check for ret < 0 in gskill_read_profile_name()
This was only working before because we weren't actually returning the
profile size correctly. Fix that up as well.
2016-10-27 10:05:57 +10:00
Lyude
107d7e652f gskill: Specify the correct encoding for profile names
Only managed to notice then while working on enabling/disabling
profiles. Somewhere down the line the encoding we pass to
ratbag_utf8_to_enc() managed to get typoed, causing it to actually fail
and not show anything because of the iconv helpers not returning
negative error codes.
2016-10-27 10:05:57 +10:00
Lyude
7e8d40b57a libratbag-util: Add test suite for iconv helpers
After learning that just using this mouse was not the greatest way of
figuring out whether or not the iconv helpers worked, I decided to
write a few simple tests to ensure silly things like this don't happen
again.

Changes since v1:
- fix indentation
- Fix year in copyright on test-iconv-helper.c
- Use an encoding that's definitely invalid for the
  iconv_invalid_encoding test
- Add tests for a bunch of bad utf16le strings
- Add tests for converting emojis to/from utf16le. Just so we have some
  actually multibyte characters in here.
2016-10-27 10:05:57 +10:00
Lyude
6c40b18229 libratbag-util: Return correct length from ratbag_utf8_from_enc 2016-10-27 10:05:57 +10:00
Lyude
9d51d70f70 libratbag-util: Return -errno, not errno
Whoops
2016-10-27 10:05:57 +10:00
Thomas Hindoe Paaboel Andersen
96a9a0f6a4 Move a comment to the appropriate function
The comment was effects of changing the resolution
but was added to the getter instead of setter function.
2016-10-24 08:03:27 +10:00
Thomas Hindoe Paaboel Andersen
31b513e6d7 Fix typos in documentation 2016-10-24 08:03:27 +10:00
Benjamin Tissoires
d831d6e515 Add a release script to generate the announce email
Copied from Xorg/util/modular, the license should not be an issue
here.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-10-18 09:33:18 +10:00
Lyude
89de42ad8e gskill: Convert driver to new profile oriented API
Signed-off-by: Lyude <thatslyude@gmail.com>

Changes since v1:
- has_changed→dirty
Changes since v2:
- Change loop in gskill_commit_profiles(). Ended up being different then
  what we needed for enabling/disabling profiles. Anyway, we also needed to
  move around the call to gskill_reload_profile_data() so that we call it
  only once per commit, not once per profile write.
Changes since v3:
- commit_profiles->commit
2016-10-14 08:00:54 +10:00
Lyude
01fcef9b9d Introduce new profile-oriented API
Almost all of the devices we've worked with here are the same: they have
some sort of profile on them, and if they don't (which is quite rare)
it's rather easy to simulate one in the driver. In fact basically all of
the device drivers in ratbag just update the profile and rewrite it on
calls to the write_resolution_dpi() and write_button() calls. This is
annoying since profile writes can take a good bit of time, and we're
wasting a lot of time with redundant profile writes that way.

During XDC this year, we came up with an idea that makes more sense: a
profile oriented API. Instead of having multiple entrypoints in the API
to writing and updating the profile we just cache all of the profile
changes until ratbag_device_commit_profiles() is called, at which point
we do a single set of writes for each profile and button that changed.

This has a lot of advantages:
- Where the profile is and is not written out to the device is now
  explicit.
- Changing attributes of each profile won't result in any sort of delay
  up until the library user commits the profiles, allowing us to avoid
  hanging the application every time a simple attribute is changed.
- We can cut down on workarounds in drivers to cater to the previous
  API.

Since this commit doesn't handle converting all of the device drivers
over to the new API, we also add a temporary wrapper for devices that
haven't yet been converted to this new API so that they're still usable.
Once all of the drivers have been converted to the new API, the wrapper
will be removed along with the internal driver API calls.

Signed-off-by: Lyude <thatslyude@gmail.com>

Changes since v1:
- has_changed→dirty
- Change namespace to LIBRATBAG_0.5.0
Changes since v2:
- Remove unneeded note about not setting dirty bit
- commit_profiles->commit
- Make FIXMEs more obvious
- Minor styling nitpicks
- Split up capability check fixes into seperate patch
2016-10-14 08:00:54 +10:00
Lyude
2bcacfc1ac libratbag: Check for CAP_BUTTON_MACROS in ratbag_button_set_macro() 2016-10-14 08:00:54 +10:00
Lyude
1e2edefd93 libratbag: Check for CAP_BUTTON_KEY instead of write_button()
This makes more sense then the former, and we're going to need it anyway
since write_button() is being deprecated.
2016-10-14 08:00:54 +10:00
Lyude
3aabba255d gskill: Update checksum *after* updating profile name
Noticed this while adding support for profile commits. This is definitely
a bug.

Signed-off-by: Lyude <thatslyude@gmail.com>
2016-10-11 21:11:39 +02:00
Lyude
e543723856 Add support for the gskill-mx780
This commit adds complete (with the exception of LEDs and a few other
small things) support for configuring the G.Skill Ripjaws MX780. So far
this has been tested on firmware versions 19 and 21, and both of them
seem to work perfectly with this.

Changes since v1:
- Lots of style fixes
- Remove reset hook, comment out with #if 0 in case of usage in the
  future
- Add ret = -EAGAIN check in the loop in gskill_general_cmd()
- Explain why we wait longer then the spec says to in
  gskill_general_cmd()
- Check that buf[0] is set to GSKILL_GENERAL_CMD in gskill_general_cmd()
- Add some notes as to why we don't use gskill_general_cmd() in
  gskill_select_profile()
- Cleanup return codes, change gskill_general_cmd() so it only returns
  valid error codes. In places not using gskill_general_cmd(), do
    return rc < 0 ? rc : -EPROTO;
- Split command for selecting the macro to read/write from into it's own
  function, just like we do with the profile
- Move profile count workaround into it's own function, make explicit
  note of why it's needed
- Add proper support for reading/writing profile/macro names, ensure
  they aren't left blank so as to not confuse G.Skill's software
- Use HID kbd modifier codes for translating macro kbd modifier codes
- Use AS_MASK instead of (1 << foo) (apparently we already had this :)

Signed-off-by: Lyude <cpaul@redhat.com>
2016-10-06 14:12:10 +02:00
Lyude
eb8e4bbfa8 libratbag-util: Add ratbag_utf8_(to|from)_enc() helpers
Some simple helpers to make converting strings to/from UTF-8 easier.
Required for the G.Skill driver since the profile/macro names are stored
in UTF16-LE on the mouse, and must be non-empty in order to prevent
confusing the default G.Skill config software.

Changes since v1:
- Fix potential erroneous free() if we fail to decide the target string
2016-10-06 14:12:10 +02:00
Benjamin Tissoires
821e2798b9 configure.ac: libratbag 0.5
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-09-16 11:45:31 +02:00
Benjamin Tissoires
3a0dc8f8a5 hwdb: add svg file for the new G502
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-08-31 11:38:00 +02:00
bentiss
f75152f074 Merge pull request #95 from cvuchener/g502rgb
Added G502 support and small fix
2016-08-31 11:37:08 +02:00
Clément Vuchener
f72c96914b hidpp20: fix reading has_dpi_shift
The comparison was always false because of missing bit shift.

Signed-off-by: Clément Vuchener <clement.vuchener@gmail.com>
2016-08-30 16:50:18 +02:00
Clément Vuchener
23036eec08 hwdb: Add new version for the G502
The new version is called "G502 Proteus Spectrum", so the older version comment is changed to "G502 Proteus Core" (its complete name) to avoid confusion.

Signed-off-by: Clément Vuchener <clement.vuchener@gmail.com>
2016-08-30 16:42:41 +02:00
bentiss
045a46aa9d Merge pull request #94 from whot/master
hidpp10: fix fetching of serial number
2016-07-27 11:00:44 +02:00
Peter Hutterer
01876023bb hidpp10: fix fetching of serial number
Was off by one, the message content only includes one byte of extra information
before the serial number. And add a BE 32-bit helper while we're at it.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-07-14 10:17:49 +10:00
Peter Hutterer
e2ae29efdc configure.ac: place udev files into $prefix/udev
$libdir is /usr/lib64 on some distros

Fixes #93

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-07-01 20:19:29 +10:00
Peter Hutterer
f67389c017 lur: fix the index parsing for disconnect
argv[3] is the /dev/hidraw path

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-06-23 16:13:57 +10:00
Peter Hutterer
20a57b76a8 Merge pull request #91 from bentiss/g602-fixes
G602 fixes
2016-05-18 15:53:59 +10:00
Benjamin Tissoires
d54d77c395 hidpp20drv: ignore errors of PAGE_ADJUSTABLE_DPI
The G602 seems to fail here, but it is safe to continue.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-05-17 17:19:55 +02:00
Benjamin Tissoires
a8897ca8ca tools: hidpp20-dump: prevent segfault when called on non HID++ device
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-05-17 17:19:55 +02:00
bentiss
4e62a3bdd9 Merge pull request #90 from ntzrmtthihu777/master
Added hwdb entry for G602 (046d:c537)
2016-05-15 09:15:21 +02:00
Marty Plummer
d8a60eca94 Added hwdb entry for G602 (046d:c537)
Signed-off-by: Marty Plummer <ntzrmtthihu777@gmail.com>
2016-05-14 15:09:17 -05:00
Benjamin Tissoires
9aa2120c7d hidpp20: prevent segfault if hidpp20_adjustable_dpi_get_dpi() fails
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-05-14 09:43:35 +02:00
Peter Hutterer
f7e3f51b54 Merge pull request #89 from bentiss/g900
G900
2016-05-12 12:39:37 +10:00
Benjamin Tissoires
aef7b3e9bb hidpp20drv: add support for the G900 over wireless
We need the same setting that we have on the hidpp10 side.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-05-11 14:01:15 +02:00
Benjamin Tissoires
21d0219809 hidpp20: add new special action in G900
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-05-11 14:01:15 +02:00
Peter Hutterer
3cf59af3e9 Merge pull request #85 from bentiss/g900
G900
2016-05-11 09:07:34 +10:00
Benjamin Tissoires
28438e638b hidpp20-drv: actually retrieve the current resolution value
The onboard profile do not store the current value but the default.
To retrieve the current value, we need to access the switchable resolution
feature.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-05-10 11:35:17 +02:00
Benjamin Tissoires
c1bf3be48e hidpp20: read/write the whole dictionary, not just the first 16 bytes
The G900 advertises 5 profiles, which do not fit in only 16 bytes.
We need to read at least 32 bytes, so be it.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-05-10 11:13:34 +02:00
Benjamin Tissoires
27293a22df hidpp20: enable G900 profile type
The profile is retro-compatible with the rest, so we can access it
in the same way.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-05-09 20:09:24 +02:00
Benjamin Tissoires
fc07672983 hidpp20: prevent invalid ROM access
The device can report how much rom it has through profile_count_oob.
Make sure we have something in ROM before accessing it.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-05-09 20:08:18 +02:00
Benjamin Tissoires
c14a0ca2a1 hidpp20-drv: prevent an empty profile to try to read a null macro
If the macro has not been retrieved, it's more likely that the button
is not bound to anything.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-05-09 19:52:56 +02:00
Benjamin Tissoires
64037febdc hidpp20: fix return path for hidpp20_onboard_profiles_read
A negative error means a communication error, while a positive one means
a HID++ one. Make sure we abort early enough.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-05-09 19:51:46 +02:00
bentiss
fd10ebbc87 Merge pull request #83 from whot/ignore-keyboard-devices
Ignore devices that don't have ID_INPUT_MOUSE set
2016-05-09 10:37:11 +02:00
Peter Hutterer
91eefb9a3f Ignore devices that don't have ID_INPUT_MOUSE set
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-05-05 12:57:11 +10:00
Benjamin Tissoires
1da0180e0f hidraw: do not return success if match failed
rc is set to 0 by ratbag_open_hidraw_node(), and if matched failed, we
close the hidraw node and end the loop.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-04-29 12:10:41 +02:00
bentiss
22abddc9d4 Merge pull request #82 from whot/wip/logitech-auto-detect
Wip/logitech auto detect
2016-04-29 12:04:59 +02:00
Peter Hutterer
e912f56c26 driver-hidpp20: don't log an error if we can't init a hidpp20 device
Once we start probing devices for multiple drivers we shouldn't just print
error messages for every failed driver. In the hidpp20 case an error would be
printed for any hidpp10 logitech device.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-04-29 10:14:11 +10:00
Peter Hutterer
a715679f1f Add a fallback for Logitech's HID++ devices
If a device is not specifically assigned a driver, try hidpp20 and then
hidpp10 hoping that one of them will work.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-04-29 10:02:42 +10:00
Peter Hutterer
55673442f1 Split the driver search + testing into a helper function
Prep work for being able to probe multiple drivers on a device. The function
that tests the actual driver simply takes a const char * with the driver name,
so in the future we can loop through multiple driver names easily.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-04-29 10:01:24 +10:00
Peter Hutterer
b6294d4f93 Add a new header for PID/VIDs that we need to care about
Beats having the same #define in multiple places

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-04-29 09:56:22 +10:00
Peter Hutterer
40112e6061 driver-hidpp20: re-use the destroy function for cleanup during init
No need for two separate cleanup blocks, they're doing the same thing anyway.
Just make the function more robust to uninitalized fields and we're good to
go.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-04-29 09:55:58 +10:00
Peter Hutterer
0785bf138d hidpp20: fail for hidpp10 devices
hidpp10 devices have their own driver, let's not initialize them here and just
fail. If a hidpp10 device is assigned the hidpp20 driver this is a bug.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-04-29 09:50:20 +10:00
Peter Hutterer
e18e25d685 hwdb: Add Logitech Wireless Touchpad
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-04-21 14:13:46 +10:00
Peter Hutterer
85fdea19c2 Link to the github-pages generated documentation
To update the documentation, do the following:

  	# delete current documentation
	pushd doc/html
	rm -rf $PWD/*
	# clone into current directory, checkout gh-pages
	git clone git@github.com:libratbag/libratbag .
	git checkout -b gh-pages
	# delete master branch so we don't accidentally push that one
	git branch -d master
	# return to main, generate docs
	popd
	make
	# commit updated docs, push
	pushd doc/html
	git commit -asm "Updated documentation"
	git push origin gh-pages
	popd

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-17 15:21:06 +10:00
bentiss
792b6c28df Merge pull request #79 from whot/master
Fix a couple of error messages
2016-03-16 10:17:06 +01:00
Peter Hutterer
5c68164d48 etekcity/roccat: fix error message when we can't talk to the mouse
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-16 10:00:09 +10:00
Peter Hutterer
b1d99a744b Standardize the error on device open failures
No reason for the drivers to have their own printf each, just return the
negative errno and let the common code print it.

This now also means that we only return -ENODEV on something that's not really
an error, for anything else we print the strerror.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-16 10:00:09 +10:00
Peter Hutterer
9f544cbe1c Revert "Fix error message on ENODEV"
We now get an error message for all devices that have duplicate event nodes
because we use -ENODEV for 'device not found', 'device is a duplicates' and
'device doesn't have the hidraw report'.

The original commit was triggered by a misunderstanding of the error message,
will be fixed in a follow-up.

This reverts commit 855a6bd831.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-16 10:00:09 +10:00
bentiss
ad06d29140 Merge pull request #77 from whot/master
configure.ac: udev basedir should be relative to prefix
2016-03-15 08:34:54 +01:00
Peter Hutterer
c12cb66824 configure.ac: udev basedir should be relative to prefix
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-15 09:48:52 +10:00
Peter Hutterer
c476cd0654 Merge pull request #76 from r3pek/master
Fix supported resolutions from G700s
2016-03-15 07:38:56 +10:00
Carlos Silva
98315343b0 Fix supported resolutions from G700s
I was under the impression that G700 and G700s had the same sensor.
Comparing the specs on the Logitech website, i found myself wrong.
2016-03-14 20:56:00 +00:00
bentiss
eb6787181d Merge pull request #75 from r3pek/master
Add Logitech G700s
2016-03-14 20:48:26 +01:00
Carlos Silva
f7d9ca63e2 Add Logitech G700s 2016-03-14 19:14:16 +00:00
60 changed files with 9072 additions and 878 deletions

41
.gitignore vendored
View File

@@ -1,7 +1,5 @@
*.o
*.pc
*.la
*.lo
*.swp
*~
*.sig
@@ -10,43 +8,4 @@
*.patch
*.rej
*.trs
Makefile
Makefile.in
aclocal.m4
autom4te.cache/
config.guess
config.h
config.h.in
config.log
config.status
config.sub
configure
depcomp
install-sh
libtool
ltmain.sh
missing
stamp-h1
.libs/
.deps/
src/libratbag-version.h
doc/libratbag.doxygen
doc/html
tags
test-driver
compile
m4
symbols-leak-test
symbols-leak-test.log
test-build-cxx
test-build-linker
test-build-pedantic-c99
test-build-std-gnuc90
test-suite.log
test-context
test-device
test-*.log
ratbag-command
hidpp10-dump-page
hidpp20-dump-page
lur-command

View File

@@ -1,6 +0,0 @@
SUBDIRS = src doc test tools hwdb data
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
valgrind:
(cd test; $(MAKE) valgrind)

View File

@@ -1,5 +1,3 @@
/*!@mainpage
libratbag
=========
@@ -7,6 +5,13 @@ libratbag is a configuration library for gaming mice. It provides a generic
way to access the various features exposed by these mice and abstracts away
hardware-specific and kernel-specific quirks.
Documentation
-------------
API documentation is available here:
http://libratbag.github.io/libratbag/
Architecture
------------
@@ -60,6 +65,28 @@ Source
git clone https://github.com/libratbag/libratbag.git
Building
--------
libratbag uses the meson build system, see http://mesonbuild.com which in
turn uses ninja to invoke the compiler (`ninja` may be `ninja-build` on your
distribution). From a fresh git checkout, run the
following commands to init the repository:
meson builddir --prefix=/usr/
And to build or re-build after code-changes, run:
ninja -C builddir
sudo ninja -C builddir install
Note: 'builddir' is the build output directory and can be changed to any
other directory name. To set configure-time options, use e.g.
mesonconf builddir -Denable-documentation=no
Run 'mesonconf builddir' to list the options.
Bugs
----
@@ -93,5 +120,3 @@ libratbag is licensed under the MIT license.
See the COPYING file for the full license information.
[![Build Status](https://semaphoreci.com/api/v1/projects/7905244a-c0b5-468b-9071-d846de3ce9f1/545192/badge.svg)](https://semaphoreci.com/libratbag/libratbag)
*/

View File

@@ -1,9 +0,0 @@
#!/bin/sh
test -n "$srcdir" || srcdir=`dirname "$0"`
test -n "$srcdir" || srcdir=.
(
cd "$srcdir" &&
autoreconf --force -v --install
) || exit
test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"

View File

@@ -1,164 +0,0 @@
AC_PREREQ([2.64])
m4_define([libratbag_major_version], [0])
m4_define([libratbag_minor_version], [4])
m4_define([libratbag_micro_version], [0])
m4_define([libratbag_version],
[libratbag_major_version.libratbag_minor_version.libratbag_micro_version])
AC_INIT([libratbag],
[libratbag_version],
[],
[libratbag],
[])
AC_SUBST([LIBRATBAG_VERSION_MAJOR], [libratbag_major_version])
AC_SUBST([LIBRATBAG_VERSION_MINOR], [libratbag_minor_version])
AC_SUBST([LIBRATBAG_VERSION_MICRO], [libratbag_micro_version])
AC_SUBST([LIBRATBAG_VERSION], [libratbag_version])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
# Before making a release, the LIBRATBAG_LT_VERSION and LIBLUR_LT_VERSION
# strings should be modified.
# The strings are of the form C:R:A.
# a) If binary compatibility has been broken (eg removed or changed interfaces)
# change to C+1:0:0.
# b) If interfaces have been changed or added, but binary compatibility has
# been preserved, change to C+1:0:A+1
# c) If the interface is the same as the previous version, change to C:R+1:A
LIBRATBAG_LT_VERSION=3:0:0
LIBLUR_LT_VERSION=3:0:0
AC_SUBST(LIBRATBAG_LT_VERSION)
AC_SUBST(LIBLUR_LT_VERSION)
AM_SILENT_RULES([yes])
AC_USE_SYSTEM_EXTENSIONS
# Check for programs
AC_PROG_CC_C99
AC_PROG_CXX # Only used by build C++ test
AC_PROG_GREP
# Initialize libtool
LT_PREREQ([2.2])
LT_INIT
PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(LIBUDEV, [libudev])
PKG_CHECK_MODULES(LIBEVDEV, [libevdev])
if test "x$GCC" = "xyes"; then
GCC_CXXFLAGS="-Wall -Wextra -Wno-unused-parameter -g -fvisibility=hidden"
GCC_CFLAGS="$GCC_CXXFLAGS -Wmissing-prototypes -Wstrict-prototypes"
fi
AC_SUBST(GCC_CFLAGS)
AC_SUBST(GCC_CXXFLAGS)
AC_ARG_WITH(udev-base-dir, AS_HELP_STRING([--with-udev-base-dir=DIR], [where udev base directory is]))
if test -n "$with_udev_base_dir" ; then
UDEV_BASE_DIR="$with_udev_base_dir"
else
UDEV_BASE_DIR="/usr/lib/udev"
fi
AC_SUBST(UDEV_BASE_DIR)
AC_ARG_ENABLE([documentation],
[AC_HELP_STRING([--enable-documentation],
[Enable building the documentation (default=auto)])],
[build_documentation="$enableval"],
[build_documentation="auto"])
if test "x$build_documentation" = "xyes" -o "x$build_documentation" = "xauto"; then
AC_PATH_PROG(DOXYGEN, doxygen)
if test "x$DOXYGEN" = "x"; then
if test "x$build_documentation" = "xyes"; then
AC_MSG_ERROR([Documentation build requested but doxygen not found. Install doxygen or disable the documentation using --disable-documentation])
fi
else
AC_MSG_CHECKING([for compatible doxygen version])
doxygen_version=`$DOXYGEN --version`
AS_VERSION_COMPARE([$doxygen_version], [1.6.0],
[AC_MSG_RESULT([no])
DOXYGEN=""],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([yes])])
if test "x$DOXYGEN" = "x" -a "x$build_documentation" = "xyes"; then
AC_MSG_ERROR([Doxygen $doxygen_version too old. Doxygen 1.6+ required for documentation build. Install required doxygen version or disable the documentation using --disable-documentation])
fi
fi
AC_PATH_PROG(DOT, dot)
if test "x$DOT" = "x"; then
if test "x$build_documentation" = "xyes"; then
AC_MSG_ERROR([Documentation build requested but graphviz's dot not found. Install graphviz or disable the documentation using --disable-documentation])
fi
else
AC_MSG_CHECKING([for compatible dot version])
dot_version=`$DOT -V 2>&1|$GREP -oP '(?<=version\W)@<:@0-9.@:>@*(?=\W(.*))'`
AS_VERSION_COMPARE([$dot_version], [2.26.0],
[AC_MSG_RESULT([no])
DOT=""],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([yes])])
if test "x$DOT" = "x" -a "x$build_documentation" = "xyes"; then
AC_MSG_ERROR([Graphviz dot $dot_version too old. Graphviz 2.26+ required for documentation build. Install required graphviz version or disable the documentation using --disable-documentation])
fi
fi
if test "x$DOXYGEN" != "x" -a "x$DOT" != "x"; then
build_documentation="yes"
else
build_documentation="no"
fi
fi
AC_ARG_ENABLE(tests,
AS_HELP_STRING([--enable-tests], [Build the tests (default=auto)]),
[build_tests="$enableval"],
[build_tests="auto"])
PKG_CHECK_MODULES(CHECK, [check >= 0.9.10], [HAVE_CHECK="yes"], [HAVE_CHECK="no"])
if test "x$build_tests" = "xauto"; then
build_tests="$HAVE_CHECK"
fi
if test "x$build_tests" = "xyes"; then
if test "x$HAVE_CHECK" = "xno"; then
AC_MSG_ERROR([Cannot build tests, check is missing])
fi
AC_PATH_PROG(VALGRIND, [valgrind])
fi
AC_DEFINE(BUILD_TESTS, [1], ["Build the test suite])
AM_CONDITIONAL(HAVE_VALGRIND, [test "x$VALGRIND" != "x"])
AM_CONDITIONAL(BUILD_TESTS, [test "x$build_tests" = "xyes"])
AM_CONDITIONAL(BUILD_DOCS, [test "x$build_documentation" = "xyes"])
AC_CONFIG_FILES([Makefile
data/Makefile
doc/Makefile
doc/libratbag.doxygen
hwdb/Makefile
src/Makefile
src/libratbag.pc
src/libratbag-version.h
src/liblur.pc
test/Makefile
tools/Makefile])
AC_CONFIG_FILES([test/symbols-leak-test],
[chmod +x test/symbols-leak-test])
AC_OUTPUT
AC_MSG_RESULT([
Prefix ${prefix}
udev base directory: ${UDEV_BASE_DIR}
Build documentation ${build_documentation}
Build tests ${build_tests}
Tests use valgrind ${VALGRIND}
])

View File

@@ -1,2 +0,0 @@
libratbagdatadir = $(datadir)/libratbag
dist_libratbagdata_DATA = $(shell find $(top_srcdir)/data -name "*.svg" -printf "%P\n")

1030
data/logitech-g300.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 46 KiB

578
data/logitech-g303.svg Normal file
View File

@@ -0,0 +1,578 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="750"
height="750"
viewBox="0 0 198.4375 198.43751"
version="1.1"
id="svg124"
inkscape:version="0.92.1 r"
sodipodi:docname="g303_FINAL.svg">
<defs
id="defs118">
<marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker10743"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend">
<path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path10741" />
</marker>
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="marker8938"
style="overflow:visible;"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path8936"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker8458"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"
inkscape:collect="always">
<path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path8456" />
</marker>
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="marker7654"
style="overflow:visible;"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path7652"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker7210"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"
inkscape:collect="always">
<path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path7208" />
</marker>
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path5595"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;"
inkscape:isstock="true">
<path
id="path5589"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " transform="scale(1.1) rotate(180) translate(1,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Lend"
style="overflow:visible;"
inkscape:isstock="true">
<path
id="path5571"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.8) rotate(180) translate(12.5,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5568"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.8) translate(12.5,0)" />
</marker>
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4831"
x1="85.155098"
y1="123.48504"
x2="71.876953"
y2="109.32611"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient4680-3">
<stop
style="stop-color:#212121;stop-opacity:1"
offset="0"
id="stop4676" />
<stop
style="stop-color:#525252;stop-opacity:1"
offset="1"
id="stop4678" />
</linearGradient>
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4837"
gradientUnits="userSpaceOnUse"
x1="75.392632"
y1="113.90072"
x2="78.325882"
y2="111.17885" />
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4820"
gradientUnits="userSpaceOnUse"
x1="58.802368"
y1="98.166107"
x2="67.821754"
y2="105.06921" />
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4812"
x1="65.554306"
y1="101.39091"
x2="57.845005"
y2="94.387032"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4801"
gradientUnits="userSpaceOnUse"
x1="93.544136"
y1="74.89328"
x2="97.026817"
y2="75.489372" />
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4796"
x1="100.40672"
y1="76.753265"
x2="102.15361"
y2="76.166771"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4682"
x1="78.691124"
y1="106.60219"
x2="134.32263"
y2="145.19518"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4705"
x1="63.324043"
y1="92.412109"
x2="52.236984"
y2="73.76532"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2281683,0,0,1.2458588,-20.199812,-21.797201)" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="radialGradient4695"
cx="112.96842"
cy="113.917"
fx="112.96842"
fy="113.917"
r="41.164257"
gradientTransform="matrix(1.2281683,0,0,1.296401,-19.641241,-30.306952)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4710"
gradientUnits="userSpaceOnUse"
x1="41.81852"
y1="81.23703"
x2="81.993874"
y2="81.23703"
gradientTransform="matrix(1.2281683,0,0,1.2458588,-20.199812,-21.716256)" />
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4763"
x1="67.125862"
y1="69.888031"
x2="82.802811"
y2="69.888031"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4773"
x1="77.743431"
y1="69.896729"
x2="74.04789"
y2="65.264908"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4747"
x1="80.881615"
y1="74.891853"
x2="82.567528"
y2="70.608902"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4784"
x1="89.6782"
y1="82.01104"
x2="124.02607"
y2="82.01104"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4735"
gradientUnits="userSpaceOnUse"
x1="81.290771"
y1="69.214806"
x2="58.49073"
y2="60.248428" />
<linearGradient
gradientTransform="matrix(1.2281683,0,0,1.2458588,-19.641241,-24.549337)"
inkscape:collect="always"
xlink:href="#linearGradient4680-3"
id="linearGradient4726"
x1="58.34309"
y1="66.386375"
x2="116.9311"
y2="66.386375"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.92824696"
inkscape:cx="267.03408"
inkscape:cy="338.19237"
inkscape:document-units="mm"
inkscape:current-layer="layer7"
showgrid="false"
units="px"
inkscape:snap-page="true"
inkscape:snap-others="false"
inkscape:window-width="1920"
inkscape:window-height="1004"
inkscape:window-x="0"
inkscape:window-y="48"
inkscape:window-maximized="1" />
<metadata
id="metadata121">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer7"
inkscape:label="Device"
style="display:inline">
<path
style="display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 33.641311,64.982766 -1.732765,5.398727 c 0,0 -0.629464,5.678443 3.26926,11.20272 5.812872,8.815493 5.94101,9.101914 13.151365,19.243577 7.210355,10.14167 20.613506,27.64865 32.12776,42.84454 3.87757,5.11741 16.967127,10.29767 21.949299,12.68881 11.002,5.28032 27.527,13.09076 40.11549,14.1146 15.07422,1.22601 22.74792,-23.13102 22.74792,-23.13102 L 141.75352,83.564426 123.90176,65.732277 102.89006,58.202973 62.293831,52.176488 45.213712,60.714006 Z"
id="path4839"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccczsssccccccc" />
<path
sodipodi:nodetypes="cccssccsc"
inkscape:connector-curvature="0"
id="path4728"
d="m 54.76863,55.588789 -2.295328,-2.726405 -0.27808,-2.350686 c 0,0 18.167671,-1.598465 22.431514,-0.940273 4.263843,0.658192 23.729205,5.171504 34.852264,9.590788 11.12306,4.419288 9.18583,4.11904 14.42276,6.57006 -8.85487,-3.133919 -23.66007,-1.760792 -33.812723,-1.477175 -0.158622,1.548423 -0.114268,2.600247 -0.114268,2.600247 C 82.7929,64.986082 54.76863,55.588789 54.76863,55.588789 Z"
style="display:inline;fill:url(#linearGradient4726);fill-opacity:1;stroke:none;stroke-width:0.32780018;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="display:inline;fill:url(#linearGradient4735);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 54.856148,55.855125 -2.382846,-2.992741 -0.27808,-2.350686 c 18.481975,6.870889 20.73837,7.57628 37.893815,13.7434 -0.158622,1.548423 -0.114268,2.600247 -0.114268,2.600247 C 82.7929,64.986082 54.856148,55.855125 54.856148,55.855125 Z"
id="path4716"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccsc" />
<path
style="display:inline;fill:url(#linearGradient4784);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 90.498682,64.587538 c 0.0014,2.415136 0.08886,3.214144 0.08886,3.214144 l 37.938998,22.41657 4.1571,0.443891 -3.58823,-15.047953 -32.119065,-9.854412 z"
id="path4776"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:url(#linearGradient4747);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 48.926779,56.570799 c 0,0 5.569607,-2.071605 6.497874,-2.13438 4.641336,1.255515 31.870514,10.044141 37.068811,12.052968 9.897016,3.978673 20.493146,9.162891 28.404966,13.999022 5.31807,3.262227 8.52923,5.401083 8.10688,7.972533 -0.42234,2.571452 -4.45568,5.900935 -4.45568,5.900935 0,0 -13.11951,-3.389899 -13.49083,-3.641 -0.37131,-0.251104 -18.627219,-14.438453 -18.627219,-14.438453 0,0 -13.119512,-5.335949 -13.552706,-5.587054 C 78.445686,70.444268 64.831094,63.978353 64.521671,63.915577 64.212247,63.852802 48.926779,56.570799 48.926779,56.570799 Z"
id="path4739"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccczcccccc" />
<path
inkscape:connector-curvature="0"
id="path4765"
d="m 62.800615,61.187683 c 0.06391,-0.129654 1.75539,-2.692898 4.595099,-4.082141 1.130065,-0.55285 4.463428,-1.655606 6.038322,-1.200179 4.493777,1.299505 7.905733,3.09382 12.483497,10.149265 -3.2066,1.658274 -3.2066,1.658274 -3.2066,1.658274 l -2.49426,0.310726 z"
style="display:inline;fill:url(#linearGradient4773);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="csscccc" />
<path
style="display:inline;fill:url(#linearGradient4763);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 62.800615,61.187683 c 0.08752,-0.177557 3.063124,-5.903769 8.708027,-3.639918 5.644906,2.263853 10.545906,10.120748 10.545906,10.120748 l -1.837875,0.355115 z"
id="path4755"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="csscssscc"
inkscape:connector-curvature="0"
id="path4707"
d="m 81.061034,68.55619 c 0,0 -6.675185,12.538586 -10.603076,22.010152 -1.049126,2.529831 -2.28668,5.867231 -2.803926,7.547057 -0.660926,2.146451 -0.08433,2.769521 -0.397665,4.358811 -3.844082,-2.632003 -32.505527,-27.735224 -34.22755,-29.787815 -0.242782,-0.289387 -1.233891,-1.962381 -1.42236,-4.307299 -0.05219,-0.649308 -0.04284,-1.350134 0.06099,-2.087368 0.836918,-5.94284 11.1851,-12.607804 11.1851,-12.607804 z"
style="display:inline;fill:url(#linearGradient4710);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="cscssccsscsscscc"
inkscape:connector-curvature="0"
id="path4670"
d="m 82.41522,69.014086 c 0,0 23.31399,10.073283 38.80002,21.370661 1.81334,1.322871 4.49209,1.676486 6.63621,-0.04062 2.14412,-1.717108 1.62492,-3.937097 -0.92121,-6.104499 -4.95885,-4.221241 -6.85739,-5.122344 -13.18638,-8.905281 -7.90195,-4.723139 -16.853771,-8.967037 -23.245174,-10.746814 9.140702,-0.572366 30.023744,-1.337857 33.403074,1.144738 8.87412,3.960851 24.30645,16.663516 31.24281,27.830947 8.63308,13.899132 12.33164,22.248122 14.23506,36.388812 2.80518,20.83995 -16.13732,43.5 -26.85791,40.5237 3.57946,-0.6342 7.33514,-2.175 6.9966,-5.15132 -0.74082,-6.51284 -8.57647,-16.59867 -48.52475,-38.11975 -18.315639,-9.86707 -32.387452,-23.46711 -32.387452,-23.46711 0,0 -0.214496,-1.83679 0.169271,-3.37698 C 71.534502,89.287367 82.41522,69.014083 82.41522,69.014083 Z"
style="display:inline;fill:url(#radialGradient4695);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="display:inline;fill:url(#linearGradient4705);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 67.336162,99.153458 -0.07979,3.237812 C 63.412286,99.759262 35.095451,75.716531 33.028818,72.77516 c -1.88514,-2.683054 -1.67797,-6.628929 -0.815272,-8.362198 -0.06626,1.61221 -0.485638,3.84076 1.152105,5.623249 7.675602,8.353979 25.95005,21.003692 33.970511,29.117247 z"
id="path178"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscsc" />
<path
style="display:inline;fill:url(#linearGradient4682);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 142.52172,170.47574 c 3.57946,-0.6342 7.33514,-2.17499 6.9966,-5.15132 -0.74082,-6.51285 -8.57647,-16.59867 -48.52476,-38.11975 -18.315635,-9.86707 -32.387446,-23.46711 -32.387446,-23.46711 0,0 -0.214498,-1.8368 0.169271,-3.37698 3.725669,4.47916 22.509918,18.21415 41.851065,29.0174 55.62333,31.06912 43.85529,42.27142 31.89527,41.09776 z"
id="path4672"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssccsc" />
<path
style="display:inline;fill:url(#linearGradient4796);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 91.506481,71.019901 c 0.743609,0.908331 11.844809,6.076214 14.221649,7.257645 0.71803,0.356899 1.1815,0.799004 2.31924,0.621448 1.13772,-0.177558 2.35863,-1.316437 2.47237,-1.642404 0.12819,-0.367406 -2.52196,-5.346011 -2.73493,-5.570848 -0.15339,-0.161933 -11.5427,-4.126769 -11.902433,-4.194785 -0.229293,-0.04336 -1.679248,0.660653 -2.406747,1.242898 -0.758403,0.606982 -2.320732,1.856577 -1.969149,2.286046 z"
id="path4788"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sscsssss" />
<path
sodipodi:nodetypes="csssc"
inkscape:connector-curvature="0"
id="path4798"
d="m 105.24679,73.239364 c 0.34525,0.02969 2.75099,-1.328785 2.53802,-1.553622 -0.15339,-0.161933 -11.5427,-4.126769 -11.902433,-4.194785 -0.229293,-0.04336 -2.23807,0.99583 -2.472384,1.487039 -0.208466,0.437022 10.535617,4.077407 11.836797,4.261368 z"
style="display:inline;fill:url(#linearGradient4801);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="display:inline;fill:url(#linearGradient4812);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 48.060396,93.734116 16.089967,15.317314 1.980305,-0.87885 1.732766,-3.76656 -17.080119,-14.81511 -1.856536,1.129967 z"
id="path4804"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path4814"
d="m 48.060396,93.734116 16.089967,15.317314 1.608998,-3.8921 C 61.694393,101.39187 50.213564,91.368976 48.926779,90.720877 Z"
style="display:inline;fill:url(#linearGradient4820);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="ccccc" />
<path
sodipodi:nodetypes="cccscssc"
inkscape:connector-curvature="0"
id="path4833"
d="m 68.002673,113.587 c 2.885039,3.27285 9.816521,11.87315 14.171552,17.07503 0.376971,0.45027 0.479604,0.86316 1.593523,0.36097 1.113922,-0.50223 7.5516,-4.23009 10.658617,-6.2986 0.393265,-0.26182 1.06751,-0.26234 -0.417721,-1.26676 -1.485226,-1.0044 -15.684249,-10.57631 -21.967626,-16.10646 -0.272557,-0.23987 -2.024894,0.70814 -2.197279,1.00972 -0.968716,1.69477 -1.430234,3.61135 -1.841066,5.2261 z"
style="display:inline;fill:url(#linearGradient4837);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="display:inline;fill:url(#linearGradient4831);fill-opacity:1;stroke:none;stroke-width:0.32728478px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 68.002673,113.587 c 2.885039,3.27285 9.816521,11.87315 14.171552,17.07503 0.376971,0.45027 0.479604,0.86316 1.593523,0.36097 1.113922,-0.50223 5.494927,-2.76524 8.601948,-4.83374 0.393264,-0.26183 1.067506,-0.70624 -0.417724,-1.71067 -1.485226,-1.0044 -14.896587,-10.13241 -21.179966,-15.66256 -0.272558,-0.23988 -0.755882,-0.75671 -0.928267,-0.45513 -0.968716,1.69477 -1.430234,3.61135 -1.841066,5.2261 z"
id="path4823"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccscssc" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Leaders"
style="display:inline">
<path
style="fill:none;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 87.372004,82.225427 c 0,0 19.527816,7.279613 31.390236,17.304521 0,0 3.8591,1.194482 5.60489,-0.183766 1.74578,-1.378252 1.19448,-3.032155 -0.64318,-4.686055 -1.83767,-1.6539 -20.39812,-13.414976 -29.770225,-15.987711 7.442555,-0.459415 24.445965,-1.073843 27.197485,0.918834 7.22549,3.179214 19.79081,13.375125 25.43853,22.33877 7.02923,11.15626 10.04068,17.85767 11.59048,29.20782 2.28404,16.72738 -13.13933,34.91569 -21.86825,32.52672 2.91447,-0.50904 5.97242,-1.74578 5.69677,-4.13475 -0.60319,-5.2276 -6.98314,-13.32309 -39.50986,-30.59717 -14.912972,-7.91989 -26.370532,-18.8361 -26.370532,-18.8361 0,0 -0.174647,-1.47432 0.137824,-2.71056 2.246527,-8.888015 11.105832,-25.160553 11.105832,-25.160553 z"
id="path4668"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csccccsscsscsc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="14.064545"
y="42.284489"
id="text4889"><tspan
sodipodi:role="line"
id="tspan4887"
x="14.064545"
y="42.284489"
style="font-size:4.23333311px;stroke-width:0.26458332">Button 0</tspan></text>
<text
id="text4977"
y="41.314796"
x="99.894806"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="stroke-width:0.26458332"
y="41.314796"
x="99.894806"
sodipodi:role="line"
id="tspan4979">Button 2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="55.512764"
y="30.319952"
id="text4985"><tspan
sodipodi:role="line"
x="55.512764"
y="30.319952"
style="stroke-width:0.26458332"
id="tspan4987">Button 1</tspan></text>
<text
id="text4993"
y="122.63442"
x="20.689524"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="stroke-width:0.26458332"
y="122.63442"
x="20.689524"
sodipodi:role="line"
id="tspan4995">Button 4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="55.262302"
y="153.98589"
id="text5001"><tspan
sodipodi:role="line"
x="55.262302"
y="153.98589"
style="stroke-width:0.26458332"
id="tspan5003">Button 3</tspan></text>
<text
id="text5009"
y="63.37672"
x="141.62805"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="stroke-width:0.26458332"
y="63.37672"
x="141.62805"
sodipodi:role="line"
id="tspan5011">Button 5</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.9000001;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
d="M 65.843199,148.27125 72.399014,138.295"
id="path5023"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path7206"
d="m 41.615182,120.90785 13.396667,-6.27079"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.9000001;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker7210)" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.9000001;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker7654)"
d="m 22.80284,45.37344 11.401419,9.121127"
id="path7650"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path8454"
d="m 66.128235,34.542092 4.275532,11.686446"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.9000001;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker8458)" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.9000001;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker8938)"
d="M 98.052209,43.663228 82.660292,57.629958"
id="path8934"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path10739"
d="m 140.23745,65.040889 -28.50355,5.13063"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.9000001;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker10743)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 29 KiB

1630
data/logitech-g900.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 65 KiB

View File

@@ -1,38 +0,0 @@
if BUILD_DOCS
noinst_DATA = html/index.html
header_files = \
$(top_srcdir)/src/libratbag.h \
$(top_srcdir)/README.md
style_files = \
style/header.html \
style/footer.html \
style/customdoxygen.css \
style/bootstrap.css
html/index.html: libratbag.doxygen $(header_files) $(diagram_files) $(style_files)
$(AM_V_GEN)(cat $<; \
echo "INPUT = $(header_files)"; \
) | $(DOXYGEN) -
clean-local:
$(AM_V_at)rm -rf html
doc_src= $(shell find html -type f -printf "html/%P\n" 2>/dev/null)
EXTRA_DIST = $(builddir)/html/index.html \
$(doc_src) \
$(diagram_files) \
$(header_files) \
$(style_files)
endif
# make sure doc was built before running dist
dist-hook:
@test -f $(distdir)/html/index.html || (\
echo "******************************************************" && \
echo "Couldn't find documentation files, refusing make dist." && \
echo "Install doxygen to build documentation for tarball." && \
echo "******************************************************" && \
test )

View File

@@ -8,8 +8,9 @@ EXTRACT_ALL = YES
EXTRACT_STATIC = YES
MAX_INITIALIZER_LINES = 0
QUIET = YES
# Keep this list in sync with meson.build
INPUT = @top_srcdir@/src/libratbag.h \
@top_srcdir@/README.txt
@top_srcdir@/README.md
GENERATE_HTML = YES
HTML_TIMESTAMP = YES
USE_MATHJAX = YES
@@ -24,3 +25,4 @@ HTML_HEADER = @top_srcdir@/doc/style/header.html
HTML_FOOTER = @top_srcdir@/doc/style/footer.html
HTML_EXTRA_STYLESHEET = @top_srcdir@/doc/style/customdoxygen.css \
@top_srcdir@/doc/style/bootstrap.css
USE_MDFILE_AS_MAINPAGE = README.md

View File

@@ -95,6 +95,11 @@ mouse:usb:v046dpc249:name:*:
RATBAG_HIDPP10_DPI=0:5700@23.53
RATBAG_HIDPP10_PROFILE=G500
# G300
mouse:usb:v046dpc246:name:*:
RATBAG_DRIVER=logitech_g300
RATBAG_SVG=logitech-g300.svg
# G500
mouse:usb:v046dpc068:name:*:
RATBAG_DRIVER=hidpp10
@@ -108,11 +113,16 @@ mouse:usb:v046dpc24e:name:*:
RATBAG_HIDPP10_DPI=0:8200@50
RATBAG_HIDPP10_PROFILE=G500
# G502 over USB
# G502 Proteus Core over USB
mouse:usb:v046dpc07d:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g502.svg
# G502 Proteus Spectrum over USB
mouse:usb:v046dpc332:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g502.svg
# G700 over USB
mouse:usb:v046dpc06b:name:*:
RATBAG_DRIVER=hidpp10
@@ -127,6 +137,21 @@ mouse:usb:v046dpc531:name:*:
RATBAG_HIDPP10_PROFILE=G700
RATBAG_HIDPP10_INDEX=1
# G700s over USB
mouse:usb:v046dpc07c:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_SVG=logitech-g700.svg
RATBAG_HIDPP10_DPI=0:8200@50
RATBAG_HIDPP10_PROFILE=G700
# G700s over wireless USB
mouse:usb:v046dpc531:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_SVG=logitech-g700.svg
RATBAG_HIDPP10_DPI=0:8200@50
RATBAG_HIDPP10_PROFILE=G700
RATBAG_HIDPP10_INDEX=1
# MX Master over unifying
mouse:usb:v046dp4041:name:*:
# MX Master over bluetooth
@@ -145,6 +170,26 @@ mouse:usb:v046dp400a:name:*:
# G303 over USB
mouse:usb:v046dpc080:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g303.svg
# Wireless Touchpad (Unifying)
mouse:usb:v046dp4011:name:*:
RATBAG_DRIVER=hidpp20
# G602 over wireless USB
mouse:usb:v046dpc537:name:*:
RATBAG_HIDPP20_INDEX=1
# G900 over USB
mouse:usb:v046dpc081:name:*:
RATBAG_SVG=logitech-g900.svg
# G900 over wireless USB
mouse:usb:v046dpc539:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g900.svg
RATBAG_HIDPP20_INDEX=1
##########################################
# Roccat
@@ -153,3 +198,12 @@ mouse:usb:v046dpc080:name:*:
mouse:usb:v1e7dp2e22:name:*:
RATBAG_DRIVER=roccat
RATBAG_SVG=roccat-kone-xtd.svg
##########################################
# G.Skill
##########################################
# MX-780
mouse:usb:v28dap3101:name:*:
RATBAG_DRIVER=gskill

View File

@@ -1,9 +0,0 @@
udevrulesdir = $(UDEV_BASE_DIR)/rules.d
udevrules_DATA = 70-libratbag-mouse.rules
hwdbdir = $(UDEV_BASE_DIR)/hwdb.d
hwdb_DATA = 70-libratbag-mouse.hwdb
EXTRA_DIST = \
$(udevrules_DATA) \
$(hwdb_DATA)

403
meson.build Normal file
View File

@@ -0,0 +1,403 @@
project('libratbag', 'c', 'cpp',
version : '0.9.0',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ])
libratbag_version = meson.project_version().split('.')
# We use libtool-version numbers because it's easier to understand.
# Before making a release, the libratbag_so_* and liblur_so_*
# numbers should be modified. The components are of the form C:R:A.
# a) If binary compatibility has been broken (eg removed or changed interfaces)
# change to C+1:0:0.
# b) If interfaces have been changed or added, but binary compatibility has
# been preserved, change to C+1:0:A+1
# c) If the interface is the same as the previous version, change to C:R+1:A
libratbag_so_c=4
libratbag_so_r=3
libratbag_so_a=0
liblur_so_c=3
liblur_so_r=3
liblur_so_a=0
# convert to sonames
libratbag_so_version = '@0@.@1@.@2@'.format((libratbag_so_c - libratbag_so_a),
libratbag_so_a, libratbag_so_r)
liblur_so_version = '@0@.@1@.@2@'.format((liblur_so_c-liblur_so_a),
liblur_so_a, liblur_so_r)
# Compiler setup
cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
cppflags = ['-Wno-unused-parameter', '-fvisibility=hidden']
cflags = cppflags + ['-Wmissing-prototypes', '-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')
# dependencies
pkgconfig = import('pkgconfig')
dep_udev = dependency('libudev')
dep_libevdev = dependency('libevdev')
dep_lm = cc.find_library('m')
#### libutil.a ####
src_libutil = [
'src/libratbag-util.c',
'src/libratbag-util.h'
]
deps_libutil = [
dep_udev,
]
lib_libutil = static_library('util',
src_libutil,
dependencies : deps_libutil
)
dep_libutil = declare_dependency(link_with: lib_libutil)
### libhidpp.a ####
src_libhidpp = [
'src/hidpp-generic.h',
'src/hidpp-generic.c',
'src/hidpp10.h',
'src/hidpp10.c',
'src/hidpp20.h',
'src/hidpp20.c',
'src/usb-ids.h'
]
deps_libhidpp = [ dep_lm ]
lib_libhidpp = static_library('hidpp',
src_libhidpp,
dependencies : deps_libhidpp)
dep_libhidpp = declare_dependency(link_with: lib_libhidpp)
### liblur.a ####
install_headers('src/liblur.h')
src_liblur = [
'src/liblur.c',
'src/liblur.h'
]
deps_liblur = [
dep_libutil,
dep_libhidpp,
]
lur_mapfile = 'src/liblur.sym'
lur_version_flag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), lur_mapfile)
lib_liblur = shared_library('lur',
src_liblur,
include_directories : include_directories('.'),
dependencies : deps_liblur,
version : liblur_so_version,
link_args : lur_version_flag,
link_depends : lur_mapfile,
install : true,
)
dep_liblur = declare_dependency(link_with: lib_liblur)
pkgconfig.generate (
filebase: 'liblur',
name: 'Liblur',
description: 'Logitech Unifying Receiver configuration library',
version: meson.project_version(),
libraries: lib_liblur
)
#### libratbag.so ####
install_headers('src/libratbag.h')
src_libratbag = [
'src/driver-etekcity.c',
'src/driver-hidpp20.c',
'src/driver-hidpp10.c',
'src/driver-logitech-g300.c',
'src/driver-roccat.c',
'src/driver-gskill.c',
'src/driver-test.c',
'src/libratbag.c',
'src/libratbag.h',
'src/libratbag-hidraw.c',
'src/libratbag-hidraw.h',
'src/libratbag-private.h',
'src/libratbag-test.c',
'src/libratbag-test.h',
'src/usb-ids.h'
]
deps_libratbag = [
dep_udev,
dep_libevdev,
dep_libutil,
dep_libhidpp,
]
libratbag_version_h_config = configuration_data()
libratbag_version_h_config.set('LIBRATBAG_VERSION_MAJOR', libratbag_version[0])
libratbag_version_h_config.set('LIBRATBAG_VERSION_MINOR', libratbag_version[1])
libratbag_version_h_config.set('LIBRATBAG_VERSION_MICRO', libratbag_version[2])
libratbag_version_h_config.set_quoted('LIBRATBAG_VERSION', meson.project_version())
libratbag_version_h = configure_file(
output : 'libratbag-version.h',
configuration : libratbag_version_h_config,
install: false,
)
mapfile = 'src/libratbag.sym'
version_flag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)
lib_libratbag = shared_library('ratbag',
src_libratbag,
include_directories : include_directories('.'),
dependencies : deps_libratbag,
version : libratbag_so_version,
link_args : version_flag,
link_depends : mapfile,
install : true,
)
dep_libratbag = declare_dependency(
link_with : lib_libratbag,
dependencies : deps_libratbag
)
pkgconfigdata = configuration_data()
pkgconfigdata.set('prefix', get_option('prefix'))
pkgconfigdata.set('libdir', get_option('libdir'))
pkgconfigdata.set('datadir', get_option('datadir'))
pkgconfigdata.set('includedir', get_option('includedir'))
pkgconfigdata.set('LIBRATBAG_VERSION', meson.project_version())
pkgconfigfile = configure_file(
input : 'src/libratbag.pc.in',
output : 'libratbag.pc',
configuration : pkgconfigdata,
install : true,
install_dir : get_option('libdir') + '/pkgconfig'
)
#### libshared.a ####
src_libshared = [
'tools/shared.c',
'tools/shared.h'
]
deps_libshared = [
dep_udev,
dep_libevdev,
]
lib_libshared = static_library('shared',
src_libshared,
dependencies : deps_libshared,
include_directories : include_directories('src')
)
dep_libshared = declare_dependency(link_with: lib_libshared)
#### ratbag-command ####
src_ratbag_command = [ 'tools/ratbag-command.c' ]
deps_ratbag_command = [
dep_libshared,
dep_libratbag,
dep_libevdev
]
executable('ratbag-command',
src_ratbag_command,
dependencies : deps_ratbag_command,
include_directories : include_directories('src'),
install : true,
)
man_config = configuration_data()
man_config.set('version', meson.project_version())
man_ratbag_command = configure_file (
input: 'tools/ratbag-command.man',
output: 'ratbag-command.1',
configuration: man_config,
install : true,
install_dir : join_paths(get_option('mandir'), 'man1')
)
#### hidpp10-dump-page ####
src_hidpp10_dump_page = [ 'tools/hidpp10-dump-page.c' ]
executable('hidpp10-dump-page',
src_hidpp10_dump_page,
dependencies : [ dep_libhidpp ],
include_directories : include_directories('src'),
install : false,
)
#### hidpp20-dump-page ####
src_hidpp20_dump_page = [ 'tools/hidpp20-dump-page.c' ]
executable('hidpp20-dump-page',
src_hidpp20_dump_page,
dependencies : [ dep_libhidpp ],
include_directories : include_directories('src'),
install : false,
)
#### lur-command ####
src_lur_command = [ 'tools/lur-command.c' ]
executable('lur-command',
src_lur_command,
dependencies : [ dep_libshared, dep_liblur ],
include_directories : include_directories('src'),
install : true,
)
man_lur_command = configure_file (
input: 'tools/lur-command.man',
output: 'lur-command.1',
configuration: man_config,
install : true,
install_dir : join_paths(get_option('mandir'), 'man1')
)
#### hwdb ####
udev_dir = get_option('udev-dir')
if udev_dir == ''
udev_dir = '@0@/lib/udev'.format(get_option('prefix'))
endif
udev_rules_dir = '@0@/rules.d'.format(udev_dir)
udev_hwdb_dir = '@0@/hwdb.d'.format(udev_dir)
install_data('hwdb/70-libratbag-mouse.rules',
install_dir : udev_rules_dir)
install_data('hwdb/70-libratbag-mouse.hwdb',
install_dir : udev_hwdb_dir)
#### svg files ####
svg_files = [
'data/etekcity.svg',
'data/logitech-g300.svg',
'data/logitech-g303.svg',
'data/logitech-g500s.svg',
'data/logitech-g502.svg',
'data/logitech-g700.svg',
'data/logitech-g900.svg',
'data/logitech-mx_master.svg',
'data/roccat-kone-xtd.svg'
]
install_data(svg_files,
install_dir : '@0@/libratbag'.format(get_option('datadir')))
#### tests ####
enable_tests = get_option('enable-tests')
if enable_tests
dep_check = dependency('check', version: '>= 0.9.10')
config_h.set('BUILD_TESTS', '1')
env_test = ['RATBAG_TEST=1']
valgrind_args = [ '--leak-check=full',
'--quiet',
'--error-exitcode=3',
'--suppressions=@0@/test/valgrind.suppressions'.format(meson.current_source_dir()) ]
test_context = executable('test-context',
['test/test-context.c'],
dependencies : [ dep_libratbag, dep_check ],
include_directories : include_directories('src'),
install : false)
test_device = executable('test-device',
['test/test-device.c'],
dependencies : [ dep_libratbag, dep_check ],
include_directories : include_directories('src'),
install : false)
test_iconv_helper = executable('test-iconv-helper',
['test/test-iconv-helper.c'],
dependencies : [ dep_libratbag,
dep_check,
dep_libutil],
include_directories : include_directories('src'),
install : false)
test('test-context', test_context, env : env_test, valgrind_args : valgrind_args)
test('test-device', test_device, env : env_test, valgrind_args : valgrind_args)
test('test-iconv-helper', test_iconv_helper, env : env_test, valgrind_args : valgrind_args)
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
test_symbols_leak = find_program('test/symbols-leak-test')
test('symbols-leak-test',
test_symbols_leak,
args : [ meson.current_source_dir() ])
#### documentation ####
enable_doc = get_option('enable-documentation')
if enable_doc
cmd_doxygen = find_program('doxygen')
cmd_dot = find_program('dot')
v = run_command(cmd_doxygen, ['--version']).stdout()
if not v.version_compare('>=1.8.3')
error('Doxygen $doxygen_version too old. ' +
'Doxygen 1.8.3+ required for documentation build. ' +
'Install required doxygen version or ' +
'disable the documentation using --disable-documentation')
endif
v = run_command(cmd_dot, ['-V']).stderr()
v = v.split(' ')[4]
if v.version_compare('<2.26.0')
error('Graphviz dot $dot_version too old. ' +
'Graphviz 2.26+ required for documentation build. ' +
'Install required graphviz version or ' +
'disable the documentation using --disable-documentation')
endif
doc_config = configuration_data()
doc_config.set('PACKAGE_NAME', meson.project_name())
doc_config.set('PACKAGE_VERSION', meson.project_version())
doc_config.set('top_srcdir', meson.source_root())
doxyfile = configure_file(input : 'doc/libratbag.doxygen.in',
output : 'libratbag.doxygen',
configuration : doc_config,
install : false)
src_doxygen = [ doxyfile,
'src/libratbag.h',
'README.md']
custom_target('doxygen',
input : src_doxygen,
output : [ 'html' ],
command : [ cmd_doxygen, doxyfile ],
install : false,
build_by_default : true)
endif
#### output files ####
configure_file(output: 'config.h', install: false, configuration: config_h)

14
meson_options.txt Normal file
View File

@@ -0,0 +1,14 @@
option('udev-dir',
type: 'string',
default: '',
description: 'udev base directory [default=$prefix/lib/udev]')
option('enable-tests',
type: 'boolean',
value: true,
description: 'Build the tests (default=yes)')
option('enable-documentation',
type: 'boolean',
value: true,
description: 'Build the documentation (default=yes)')

541
release.sh Executable file
View File

@@ -0,0 +1,541 @@
#!/bin/bash
#
# Generate the announce template
#
# Completely copy/paste of Xorg/util/modular/release.sh:
#
# Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
#
# 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.
export LC_ALL=C
#------------------------------------------------------------------------------
# Function: check_local_changes
#------------------------------------------------------------------------------
#
check_local_changes() {
git diff --quiet HEAD > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo ""
echo "Uncommitted changes found. Did you forget to commit? Aborting."
echo ""
echo "You can perform a 'git stash' to save your local changes and"
echo "a 'git stash apply' to recover them after the tarball release."
echo "Make sure to rebuild and run 'make distcheck' again."
echo ""
echo "Alternatively, you can clone the module in another directory"
echo "and run ./configure. No need to build if testing was finished."
echo ""
return 1
fi
return 0
}
#------------------------------------------------------------------------------
# Function: check_option_args
#------------------------------------------------------------------------------
#
# perform sanity checks on cmdline args which require arguments
# arguments:
# $1 - the option being examined
# $2 - the argument to the option
# returns:
# if it returns, everything is good
# otherwise it exit's
check_option_args() {
option=$1
arg=$2
# check for an argument
if [ x"$arg" = x ]; then
echo ""
echo "Error: the '$option' option is missing its required argument."
echo ""
usage
exit 1
fi
# does the argument look like an option?
echo $arg | $GREP "^-" > /dev/null
if [ $? -eq 0 ]; then
echo ""
echo "Error: the argument '$arg' of option '$option' looks like an option itself."
echo ""
usage
exit 1
fi
}
#------------------------------------------------------------------------------
# Function: check_modules_specification
#------------------------------------------------------------------------------
#
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
fi
}
#------------------------------------------------------------------------------
# Function: generate_announce
#------------------------------------------------------------------------------
#
generate_announce()
{
cat <<RELEASE
Subject: [ANNOUNCE] $pkg_name $pkg_version
To: $list_to
Cc: $list_cc
libratbag $tag_name is out.
The list of interesting changes are:
See the full log at the end if you are interested in the details.
`git log --no-merges "$tag_range" | git shortlog`
git tag: $tag_name
RELEASE
for tarball in $tarbz2 $targz $tarxz; do
cat <<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
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
#------------------------------------------------------------------------------
#
print_epilog() {
epilog="======== Successful Completion"
if [ x"$NO_QUIT" != x ]; then
if [ x"$failed_modules" != x ]; then
epilog="======== Partial Completion"
fi
elif [ x"$failed_modules" != x ]; then
epilog="======== Stopped on Error"
fi
echo ""
echo "$epilog `date`"
# Report about modules that failed for one reason or another
if [ x"$failed_modules" != x ]; then
echo " List of failed modules:"
for mod in $failed_modules; do
echo " $mod"
done
echo "========"
echo ""
fi
}
#------------------------------------------------------------------------------
# Function: process_modules
#------------------------------------------------------------------------------
#
# Loop through each module to release
# Exit on error if --no-quit was not specified
#
process_modules() {
for MODULE_RPATH in ${INPUT_MODULES}; do
if ! process_module ; then
echo "Error: processing module \"$MODULE_RPATH\" failed."
failed_modules="$failed_modules $MODULE_RPATH"
if [ x"$NO_QUIT" = x ]; then
print_epilog
exit 1
fi
fi
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
#------------------------------------------------------------------------------
# Code 'return 0' on success to process the next module
# Code 'return 1' on error to process next module if invoked with --no-quit
#
process_module() {
top_src=`pwd`
echo ""
echo "======== Processing \"$top_src/$MODULE_RPATH\""
# This is the location where the script has been invoked
if [ ! -d $MODULE_RPATH ] ; then
echo "Error: $MODULE_RPATH cannot be found under $top_src."
return 1
fi
# Change directory to be in the git module
cd $MODULE_RPATH
if [ $? -ne 0 ]; then
echo "Error: failed to cd to $MODULE_RPATH."
return 1
fi
# ----- Now in the git module *root* directory ----- #
# Check that this is indeed a git module
if [ ! -d .git ]; then
echo "Error: there is no git module here: `pwd`"
return 1
fi
# Check for uncommitted/queued changes.
check_local_changes
if [ $? -ne 0 ]; then
cd $top_src
return 1
fi
# Determine what is the current branch and the remote name
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")
tar_name="$pkg_name-$pkg_version"
targz=$tar_name.tar.gz
tag_name=$(echo "v$pkg_version" | sed 's|.0$||')
# 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"
curl $remote_targz_url > $targz
# Obtain the top commit SHA which should be the version bump
# It should not have been tagged yet (the script will do it later)
local_top_commit_sha=`git rev-list --max-count=1 HEAD`
if [ $? -ne 0 ]; then
echo "Error: unable to obtain the local top commit id."
cd $top_src
return 1
fi
# Check that the top commit looks like a version bump
git diff --unified=0 HEAD^ | $GREP -F $pkg_version >/dev/null 2>&1
if [ $? -ne 0 ]; then
# Wayland repos use m4_define([wayland_major_version], [0])
git diff --unified=0 HEAD^ | $GREP -E "(major|minor|micro)_version" >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Error: the local top commit does not look like a version bump."
echo " the diff does not contain the string \"$pkg_version\"."
local_top_commit_descr=`git log --oneline --max-count=1 $local_top_commit_sha`
echo " the local top commit is: \"$local_top_commit_descr\""
cd $top_src
return 1
fi
fi
# Check that the top commit has been pushed to remote
remote_top_commit_sha=`git rev-list --max-count=1 $remote_name/$remote_branch`
if [ $? -ne 0 ]; then
echo "Error: unable to obtain top commit from the remote repository."
cd $top_src
return 1
fi
if [ x"$remote_top_commit_sha" != x"$local_top_commit_sha" ]; then
echo "Error: the local top commit has not been pushed to the remote."
local_top_commit_descr=`git log --oneline --max-count=1 $local_top_commit_sha`
echo " the local top commit is: \"$local_top_commit_descr\""
cd $top_src
return 1
fi
# If a tag exists with the the tar name, ensure it is tagging the top commit
# It may happen if the version set in configure.ac has been previously released
tagged_commit_sha=`git rev-list --max-count=1 $tag_name 2>/dev/null`
if [ $? -eq 0 ]; then
# Check if the tag is pointing to the top commit
if [ x"$tagged_commit_sha" != x"$remote_top_commit_sha" ]; then
echo "Error: the \"$tag_name\" already exists."
echo " this tag is not tagging the top commit."
remote_top_commit_descr=`git log --oneline --max-count=1 $remote_top_commit_sha`
echo " the top commit is: \"$remote_top_commit_descr\""
local_tag_commit_descr=`git log --oneline --max-count=1 $tagged_commit_sha`
echo " tag \"$tag_name\" is tagging some other commit: \"$local_tag_commit_descr\""
cd $top_src
return 1
else
echo "Info: module already tagged with \"$tag_name\"."
fi
else
# Tag the top commit with the tar name
if [ x"$DRY_RUN" = x ]; then
git tag -s -m $tag_name $tag_name
if [ $? -ne 0 ]; then
echo "Error: unable to tag module with \"$tag_name\"."
cd $top_src
return 1
else
echo "Info: module tagged with \"$tag_name\"."
fi
else
echo "Info: skipping the commit tagging in dry-run mode."
fi
fi
# Mailing lists where to post the all [Announce] e-mails
list_to="input-tools@lists.freedesktop.org"
# Pushing the top commit tag to the remote repository
if [ x$DRY_RUN = x ]; then
echo "Info: pushing tag \"$tag_name\" to remote \"$remote_name\":"
git push $remote_name $tag_name
if [ $? -ne 0 ]; then
echo "Error: unable to push tag \"$tag_name\" to the remote repository."
echo " it is recommended you fix this manually and not run the script again"
cd $top_src
return 1
fi
else
echo "Info: skipped pushing tag \"$tag_name\" to the remote repository in dry-run mode."
fi
# --------- Generate the announce e-mail ------------------
# Failing to generate the announce is not considered a fatal error
# Git-describe returns only "the most recent tag", it may not be the expected one
# However, we only use it for the commit history which will be the same anyway.
tag_previous=`git describe --abbrev=0 HEAD^ 2>/dev/null`
# Git fails with rc=128 if no tags can be found prior to HEAD^
if [ $? -ne 0 ]; then
if [ $? -ne 0 ]; then
echo "Warning: unable to find a previous tag."
echo " perhaps a first release on this branch."
echo " Please check the commit history in the announce."
fi
fi
if [ x"$tag_previous" != x ]; then
# The top commit may not have been tagged in dry-run mode. Use commit.
tag_range=$tag_previous..$local_top_commit_sha
else
tag_range=$tag_name
fi
generate_announce > "$tar_name.announce"
echo "Info: [ANNOUNCE] template generated in \"$tar_name.announce\" file."
echo " Please pgp sign and send it."
# --------- Successful completion --------------------------
cd $top_src
return 0
}
#------------------------------------------------------------------------------
# Function: usage
#------------------------------------------------------------------------------
# Displays the script usage and exits successfully
#
usage() {
basename="`expr "//$0" : '.*/\([^/]*\)'`"
cat <<HELP
Usage: $basename [options] path...
Where "path" is a relative path to a git module, including '.'.
Options:
--dry-run Does everything except tagging and uploading tarballs
--force Force overwriting an existing release
--help Display this help and exit successfully
--no-quit Do not quit after error; just print error message
HELP
}
#------------------------------------------------------------------------------
# Script main line
#------------------------------------------------------------------------------
#
# Choose which grep program to use (on Solaris, must be gnu grep)
if [ "x$GREP" = "x" ] ; then
if [ -x /usr/gnu/bin/grep ] ; then
GREP=/usr/gnu/bin/grep
else
GREP=grep
fi
fi
# Process command line args
while [ $# != 0 ]
do
case $1 in
# Does everything except uploading tarball
--dry-run)
DRY_RUN=yes
;;
# Force overwriting an existing release
# Use only if nothing changed in the git repo
--force)
FORCE=yes
;;
# Display this help and exit successfully
--help)
usage
exit 0
;;
# Do not quit after error; just print error message
--no-quit)
NO_QUIT=yes
;;
--*)
echo ""
echo "Error: unknown option: $1"
echo ""
usage
exit 1
;;
-*)
echo ""
echo "Error: unknown option: $1"
echo ""
usage
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
shift
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
# Print the epilog with final status
print_epilog

View File

@@ -8,6 +8,18 @@
sudo apt-get update
sudo apt-get install -y valgrind check libevdev-dev libudev-dev doxygen graphviz
autoreconf -ivf
./configure --prefix=$PWD/_build --with-udev-base-dir=$PWD/_build
DISTCHECK_CONFIGURE_FLAGS="--with-udev-base-dir=$PWD/_build" make distcheck
# no meson on 14.04
sudo apt-get install -y python3-pip
sudo pip3 install meson
# ninja on 14.04 is too old
git clone git://github.com/ninja-build/ninja.git
cd ninja
git checkout release
./configure.py --bootstrap
sudo cp ninja /usr/bin/ninja
cd ..
meson builddir
ninja -C builddir test

View File

@@ -1,74 +0,0 @@
lib_LTLIBRARIES = libratbag.la \
liblur.la
noinst_LTLIBRARIES = libhidpp.la \
libutil.la
include_HEADERS = \
libratbag.h \
liblur.h
libratbag_la_SOURCES = \
driver-etekcity.c \
driver-hidpp20.c \
driver-hidpp10.c \
driver-roccat.c \
driver-test.c \
libratbag.c \
libratbag.h \
libratbag-hidraw.c \
libratbag-hidraw.h \
libratbag-private.h \
libratbag-test.c \
libratbag-test.h
libratbag_la_LIBADD = \
libhidpp.la \
libutil.la \
$(LIBUDEV_LIBS) $(LIBEVDEV_LIBS)
libratbag_la_CFLAGS = -I$(top_srcdir)/include \
$(LIBUDEV_CFLAGS) \
$(LIBEVDEV_CFLAGS) \
$(GCC_CFLAGS)
EXTRA_libratbag_la_DEPENDENCIES = $(srcdir)/libratbag.sym
libratbag_la_LDFLAGS = -version-info $(LIBRATBAG_LT_VERSION) -shared \
-Wl,--version-script=$(srcdir)/libratbag.sym
liblur_la_SOURCES = \
liblur.c \
liblur.h
liblur_la_LIBADD = \
libhidpp.la \
libutil.la
liblur_la_CFLAGS = \
-I$(top_srcdir)/include \
$(GCC_CFLAGS)
EXTRA_liblur_la_DEPENDENCIES = $(srcdir)/liblur.sym
liblur_la_LDFLAGS = -version-info $(LIBLUR_LT_VERSION) -shared \
-Wl,--version-script=$(srcdir)/liblur.sym
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libratbag.pc \
liblur.pc
AM_CFLAGS = $(GCC_CFLAGS)
DISTCLEANFILES = libratbag-version.h
EXTRA_DIST = libratbag-version.h.in libratbag.sym liblur.sym
# The hidpp libraries are static libraries only for in-tree tools. No API
# stability is guaranteed here.
libhidpp_la_SOURCES = \
hidpp-generic.h \
hidpp-generic.c \
hidpp10.h \
hidpp10.c \
hidpp20.h \
hidpp20.c
libhidpp_la_LDFLAGS = -lm
libutil_la_SOURCES = \
libratbag-util.c \
libratbag-util.h
libutil_la_LIBADD = $(LIBUDEV_LIBS)

View File

@@ -36,6 +36,7 @@
#define ETEKCITY_PROFILE_MAX 4
#define ETEKCITY_BUTTON_MAX 10
#define ETEKCITY_NUM_DPI 6
#define ETEKCITY_LED 0
#define ETEKCITY_REPORT_ID_CONFIGURE_PROFILE 4
#define ETEKCITY_REPORT_ID_PROFILE 5
@@ -636,13 +637,8 @@ etekcity_probe(struct ratbag_device *device)
int active_idx;
rc = ratbag_open_hidraw(device);
if (rc) {
log_error(device->ratbag,
"Can't open corresponding hidraw node: '%s' (%d)\n",
strerror(-rc),
rc);
return -ENODEV;
}
if (rc)
return rc;
if (!ratbag_hidraw_has_report(device, ETEKCITY_REPORT_ID_KEY_MAPPING)) {
ratbag_close_hidraw(device);
@@ -662,7 +658,8 @@ etekcity_probe(struct ratbag_device *device)
ratbag_device_init_profiles(device,
ETEKCITY_PROFILE_MAX + 1,
ETEKCITY_NUM_DPI,
ETEKCITY_BUTTON_MAX + 1);
ETEKCITY_BUTTON_MAX + 1,
ETEKCITY_LED);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
@@ -670,8 +667,8 @@ etekcity_probe(struct ratbag_device *device)
if (active_idx < 0) {
log_error(device->ratbag,
"Can't talk to the mouse: '%s' (%d)\n",
strerror(-rc),
rc);
strerror(-active_idx),
active_idx);
rc = -ENODEV;
goto err;
}

1512
src/driver-gskill.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -35,8 +35,6 @@
#include "config.h"
#define USB_VENDOR_ID_LOGITECH 0x046d
#include <linux/types.h>
#include <errno.h>
#include <stdbool.h>
@@ -45,6 +43,7 @@
#include <unistd.h>
#include "hidpp10.h"
#include "usb-ids.h"
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
@@ -466,7 +465,8 @@ hidpp10drv_fill_from_profile(struct ratbag_device *device, struct hidpp10_device
ratbag_device_init_profiles(device,
count,
profile.num_dpi_modes,
profile.num_buttons);
profile.num_buttons,
profile.num_leds);
if (dev->profile_type != HIDPP10_PROFILE_UNKNOWN) {
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_PROFILE);
@@ -504,16 +504,8 @@ hidpp10drv_probe(struct ratbag_device *device)
int nread = 0;
rc = ratbag_find_hidraw(device, hidpp10drv_test_hidraw);
if (rc == -ENODEV) {
return rc;
} else if (rc) {
log_error(device->ratbag,
"Can't open corresponding hidraw node: '%s' (%d)\n",
strerror(-rc),
rc);
rc = -ENODEV;
if (rc)
goto err;
}
drv_data = zalloc(sizeof(*drv_data));
hidpp_device_init(&base, device->hidraw.fd);
@@ -593,7 +585,7 @@ hidpp10drv_probe(struct ratbag_device *device)
/* Fall back to something that every mouse has */
struct ratbag_profile *profile;
ratbag_device_init_profiles(device, 1, 1, 3);
ratbag_device_init_profiles(device, 1, 1, 3, 0);
profile = ratbag_device_get_profile(device, 0);
profile->is_active = true;
ratbag_profile_unref(profile);

View File

@@ -51,7 +51,8 @@
#define HIDPP_CAP_BUTTON_KEY_1b04 (1 << 2)
#define HIDPP_CAP_BATTERY_LEVEL_1000 (1 << 3)
#define HIDPP_CAP_KBD_REPROGRAMMABLE_KEYS_1b00 (1 << 4)
#define HIDPP_CAP_ONBOARD_PROFILES_8100 (1 << 5)
#define HIDPP_CAP_COLOR_LED_EFFECTS_8070 (1 << 5)
#define HIDPP_CAP_ONBOARD_PROFILES_8100 (1 << 6)
struct hidpp20drv_data {
struct hidpp20_device *dev;
@@ -61,10 +62,12 @@ struct hidpp20drv_data {
unsigned num_controls;
struct hidpp20_control_id *controls;
struct hidpp20_profiles *profiles;
struct hidpp20_color_led_zone_info *led_infos;
unsigned int num_profiles;
unsigned int num_resolutions;
unsigned int num_buttons;
unsigned int num_leds;
};
static void
@@ -117,7 +120,7 @@ hidpp20drv_read_macro_key_8100(struct ratbag_device *device, union hidpp20_macro
return ratbag_hidraw_get_keycode_from_keyboard_usage(device, macro->key.key);
}
static void
static int
hidpp20drv_read_macro_8100(struct ratbag_button *button,
struct hidpp20_profile *profile,
union hidpp20_button_binding *binding)
@@ -130,6 +133,9 @@ hidpp20drv_read_macro_8100(struct ratbag_button *button,
macro = profile->macros[binding->macro.page];
if (!macro)
return -EINVAL;
i = 0;
m = ratbag_button_macro_new("macro");
@@ -175,6 +181,8 @@ hidpp20drv_read_macro_8100(struct ratbag_button *button,
ratbag_button_copy_macro(button, m);
ratbag_button_macro_unref(m);
return 0;
}
static void
@@ -183,6 +191,7 @@ hidpp20drv_read_button_8100(struct ratbag_button *button)
struct ratbag_device *device = button->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_profile *profile;
int rc;
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100))
return;
@@ -213,10 +222,13 @@ hidpp20drv_read_button_8100(struct ratbag_button *button)
button->action.action.special = hidpp20_onboard_profiles_get_special(profile->buttons[button->index].special.special);
break;
case HIDPP20_BUTTON_MACRO:
hidpp20drv_read_macro_8100(button, profile, &profile->buttons[button->index]);
rc = hidpp20drv_read_macro_8100(button, profile, &profile->buttons[button->index]);
if (rc)
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
break;
default:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
break;
}
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
@@ -232,6 +244,44 @@ hidpp20drv_read_button(struct ratbag_button *button)
hidpp20drv_read_button_8100(button);
}
static void
hidpp20drv_read_led(struct ratbag_led *led)
{
struct ratbag_device *device = led->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_profile *profile;
struct hidpp20_color_led_zone_info *led_info;
struct hidpp20_led *h_led;
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return;
led_info = &drv_data->led_infos[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;
}
led->type = hidpp20_8070_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;
led->hz = h_led->period; /* FIXME: should be ceil(1000.0 / h_led->period), but that would be very small */
led->brightness = h_led->brightness * 255 / 100;
}
static int
hidpp20drv_write_button_1b04(struct ratbag_button *button,
const struct ratbag_button_action *action)
@@ -268,8 +318,8 @@ hidpp20drv_write_button_1b04(struct ratbag_button *button,
}
static int
hidpp20drv_write_button_8100(struct ratbag_button *button,
const struct ratbag_button_action *action)
hidpp20drv_update_button_8100(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
struct ratbag_device *device = button->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
@@ -320,14 +370,14 @@ hidpp20drv_write_button_8100(struct ratbag_button *button,
}
static int
hidpp20drv_write_button(struct ratbag_button *button,
hidpp20drv_update_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
struct ratbag_device *device = button->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
return hidpp20drv_write_button_8100(button, action);
return hidpp20drv_update_button_8100(button, action);
if (drv_data->capabilities & HIDPP_CAP_BUTTON_KEY_1b04)
return hidpp20drv_write_button_1b04(button, action);
@@ -335,6 +385,48 @@ hidpp20drv_write_button(struct ratbag_button *button,
return -ENOTSUP;
}
static int
hidpp20drv_update_led(struct ratbag_led *led)
{
struct ratbag_profile *profile = led->profile;
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_profile *h_profile;
struct hidpp20_led *h_led;
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return -ENOTSUP;
h_profile = &drv_data->profiles->profiles[profile->index];
h_led = &(h_profile->leds[led->index]);
if (!h_led)
return -EINVAL;
switch (led->mode) {
case RATBAG_LED_ON:
h_led->mode = HIDPP20_LED_ON;
break;
case RATBAG_LED_CYCLE:
h_led->mode = HIDPP20_LED_CYCLE;
break;
case RATBAG_LED_BREATHING:
h_led->mode = HIDPP20_LED_BREATHING;
break;
default:
h_led->mode = HIDPP20_LED_OFF;
break;
}
h_led->color.red = led->color.red;
h_led->color.green = led->color.green;
h_led->color.blue = led->color.blue;
h_led->period = led->hz; /* FIXME: should be 1000 / led->hz, but that would be very small */
h_led->brightness = led->brightness * 100 / 255;
return RATBAG_SUCCESS;
}
static int
hidpp20drv_current_profile(struct ratbag_device *device)
{
@@ -452,8 +544,8 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
}
static int
hidpp20drv_write_resolution_dpi_8100(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
hidpp20drv_update_resolution_dpi_8100(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_device *device = profile->device;
@@ -468,12 +560,12 @@ hidpp20drv_write_resolution_dpi_8100(struct ratbag_resolution *resolution,
h_profile = &drv_data->profiles->profiles[profile->index];
h_profile->dpi[index] = dpi;
return hidpp20_onboard_profiles_write(drv_data->dev, profile->index, drv_data->profiles);
return RATBAG_SUCCESS;
}
static int
hidpp20drv_write_resolution_dpi(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_device *device = profile->device;
@@ -483,7 +575,7 @@ hidpp20drv_write_resolution_dpi(struct ratbag_resolution *resolution,
int dpi = dpi_x; /* dpi_x == dpi_y if we don't have the individual resolution cap */
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
return hidpp20drv_write_resolution_dpi_8100(resolution, dpi_x, dpi_y);
return hidpp20drv_update_resolution_dpi_8100(resolution, dpi_x, dpi_y);
if (!(drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201))
return -ENOTSUP;
@@ -561,6 +653,29 @@ hidpp20drv_read_kbd_reprogrammable_key(struct ratbag_device *device)
return rc;
}
static int
hidpp20drv_read_color_leds(struct ratbag_device *device)
{
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);
drv_data->led_infos = NULL;
drv_data->num_leds = 0;
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos);
if (rc > 0) {
drv_data->num_leds = rc;
rc = 0;
}
return rc;
}
static int
hidpp20drv_read_onboard_profile(struct ratbag_device *device, unsigned index)
{
@@ -584,7 +699,7 @@ hidpp20drv_read_profile_8100(struct ratbag_profile *profile, unsigned int index)
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag_resolution *res;
struct hidpp20_profile *p;
unsigned i, dpi;
unsigned i, dpi = 0;
hidpp20drv_read_onboard_profile(device, profile->index);
@@ -592,6 +707,10 @@ hidpp20drv_read_profile_8100(struct ratbag_profile *profile, unsigned int index)
if ((int)index == hidpp20drv_current_profile(device))
profile->is_active = true;
/* retrieve the resolution through 220X as the profile doesn't has it */
if (profile->is_active)
hidpp20drv_read_resolution_dpi(profile);
dpi = ratbag_resolution_get_dpi(profile->resolution.modes);
p = &drv_data->profiles->profiles[index];
@@ -625,12 +744,6 @@ hidpp20drv_read_profile(struct ratbag_profile *profile, unsigned int index)
profile->is_active = (index == 0);
}
static int
hidpp20drv_write_profile(struct ratbag_profile *profile)
{
return 0;
}
static int
hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
{
@@ -656,13 +769,13 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
}
case HIDPP_PAGE_ADJUSTABLE_DPI: {
log_debug(ratbag, "device has adjustable dpi\n");
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION);
drv_data->capabilities |= HIDPP_CAP_SWITCHABLE_RESOLUTION_2201;
/* we read the profile once to get the correct number of
* supported resolutions. */
rc = hidpp20drv_read_resolution_dpi_2201(device);
if (rc < 0)
return rc;
return 0; /* this is not a hard failure */
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION);
drv_data->capabilities |= HIDPP_CAP_SWITCHABLE_RESOLUTION_2201;
break;
}
case HIDPP_PAGE_SPECIAL_KEYS_BUTTONS: {
@@ -706,6 +819,15 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
}
case HIDPP_PAGE_COLOR_LED_EFFECTS: {
log_debug(ratbag, "device has color effects\n");
drv_data->capabilities |= HIDPP_CAP_COLOR_LED_EFFECTS_8070;
/* we read the profile once to get the correct number of
* supported leds. */
if (hidpp20drv_read_color_leds(device))
return 0;
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_LED);
device->num_leds = drv_data->num_leds;
break;
}
case HIDPP_PAGE_ONBOARD_PROFILES: {
@@ -719,6 +841,7 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
drv_data->num_profiles = drv_data->profiles->num_profiles;
drv_data->num_resolutions = drv_data->profiles->num_modes;
drv_data->num_buttons = drv_data->profiles->num_buttons;
drv_data->num_leds = drv_data->profiles->num_leds;
break;
}
@@ -732,6 +855,60 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
return 0;
}
static int
hidpp20_commit(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag_profile *profile;
struct ratbag_button *button;
struct ratbag_led *led;
struct ratbag_resolution *resolution;
int rc;
unsigned int i;
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
continue;
for (i = 0; i < profile->resolution.num_modes; i++) {
resolution = &profile->resolution.modes[i];
rc = hidpp20drv_update_resolution_dpi(resolution,
resolution->dpi_x,
resolution->dpi_y);
if (rc)
return RATBAG_ERROR_DEVICE;
}
list_for_each(button, &profile->buttons, link) {
struct ratbag_button_action action = button->action;
if (!button->dirty)
continue;
rc = hidpp20drv_update_button(button, &action);
if (rc)
return RATBAG_ERROR_DEVICE;
}
list_for_each(led, &profile->leds, link) {
if (!led->dirty)
continue;
rc = hidpp20drv_update_led(led);
if (rc)
return RATBAG_ERROR_DEVICE;
}
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
hidpp20_onboard_profiles_write(drv_data->dev,
profile->index,
drv_data->profiles);
}
return RATBAG_SUCCESS;
}
static int
hidpp20drv_20_probe(struct ratbag_device *device)
{
@@ -773,6 +950,29 @@ hidpp20_log(void *userdata, enum hidpp_log_priority priority, const char *format
log_msg_va(device->ratbag, priority, format, args);
}
static void
hidpp20drv_remove(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data;
struct hidpp20_device *dev;
if (!device)
return;
drv_data = ratbag_get_drv_data(device);
dev = drv_data->dev;
ratbag_close_hidraw(device);
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(dev, drv_data->profiles);
free(drv_data->controls);
free(drv_data->sensors);
if (drv_data->dev)
hidpp20_device_destroy(drv_data->dev);
free(drv_data);
}
static int
hidpp20drv_probe(struct ratbag_device *device)
{
@@ -780,28 +980,41 @@ hidpp20drv_probe(struct ratbag_device *device)
struct hidpp20drv_data *drv_data;
struct hidpp_device base;
struct hidpp20_device *dev;
const char *prop;
int device_idx = HIDPP_RECEIVER_IDX;
int nread = 0;
rc = ratbag_find_hidraw(device, hidpp20drv_test_hidraw);
if (rc == -ENODEV) {
if (rc)
return rc;
} else if (rc) {
log_error(device->ratbag,
"Can't open corresponding hidraw node: '%s' (%d)\n",
strerror(-rc),
rc);
return -ENODEV;
}
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
hidpp_device_init(&base, device->hidraw.fd);
hidpp_device_set_log_handler(&base, hidpp20_log, HIDPP_LOG_PRIORITY_RAW, device);
dev = hidpp20_device_new(&base, HIDPP_RECEIVER_IDX);
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP20_INDEX");
if (prop) {
sscanf(prop, "%d%n", &device_idx, &nread);
if (!nread || (prop[nread]) != '\0' || device_idx < 0) {
log_error(device->ratbag,
"Error parsing RATBAG_HIDPP20_INDEX: '%s' for %s\n",
prop,
device->name);
device_idx = HIDPP_RECEIVER_IDX;
}
}
/* In the general case, we can treat all devices as wired devices
* here. If we talk to the correct hidraw device the kernel adjusts
* the device index for us, so even for unifying receiver devices
* we can just use 0xff as device index.
*
* If there is a special need like for G900, we can pass a
* udev prop RATBAG_HIDPP20_INDEX.
*/
dev = hidpp20_device_new(&base, device_idx);
if (!dev) {
log_error(device->ratbag,
"Failed to get HID++2.0 device for %s\n",
device->name);
rc = -ENODEV;
goto err;
}
@@ -814,50 +1027,32 @@ hidpp20drv_probe(struct ratbag_device *device)
drv_data->num_profiles = 1;
drv_data->num_resolutions = 1;
drv_data->num_buttons = 8;
drv_data->num_leds = 2;
if (dev->proto_major >= 2) {
rc = hidpp20drv_20_probe(device);
if (rc)
goto err;
}
rc = hidpp20drv_20_probe(device);
if (rc)
goto err;
ratbag_device_init_profiles(device,
drv_data->num_profiles,
drv_data->num_resolutions,
drv_data->num_buttons);
drv_data->num_buttons,
drv_data->num_leds);
return rc;
err:
free(drv_data);
ratbag_set_drv_data(device, NULL);
hidpp20drv_remove(device);
return rc;
}
static void
hidpp20drv_remove(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_device *dev = drv_data->dev;
ratbag_close_hidraw(device);
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(dev, drv_data->profiles);
free(drv_data->controls);
free(drv_data->sensors);
hidpp20_device_destroy(drv_data->dev);
free(drv_data);
}
struct ratbag_driver hidpp20_driver = {
.name = "Logitech HID++2.0",
.id = "hidpp20",
.probe = hidpp20drv_probe,
.remove = hidpp20drv_remove,
.commit = hidpp20_commit,
.read_profile = hidpp20drv_read_profile,
.write_profile = hidpp20drv_write_profile,
.set_active_profile = hidpp20drv_set_current_profile,
.read_button = hidpp20drv_read_button,
.write_button = hidpp20drv_write_button,
.write_resolution_dpi = hidpp20drv_write_resolution_dpi,
.read_led = hidpp20drv_read_led,
};

500
src/driver-logitech-g300.c Normal file
View File

@@ -0,0 +1,500 @@
/*
* Copyright © 2016 Thomas Hindoe Paaboel Andersen.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <libevdev/libevdev.h>
#include <linux/input.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
#define LOGITECH_G300_PROFILE_MAX 2
#define LOGITECH_G300_BUTTON_MAX 8
#define LOGITECH_G300_NUM_DPI 4
#define LOGITECH_G300_NUM_LED 0
#define LOGITECH_G300_REPORT_ID_SET_ACTIVE_PROFILE 0xF0
#define LOGITECH_G300_REPORT_ID_GET_ACTIVE_PROFILE 0xF1
#define LOGITECH_G300_REPORT_ID_SET_ACTIVE_RESOLUTION 0xF0
#define LOGITECH_G300_REPORT_ID_PROFILE_0 0xF3
#define LOGITECH_G300_REPORT_ID_PROFILE_1 0xF4
#define LOGITECH_G300_REPORT_ID_PROFILE_2 0xF5
#define LOGITECH_G300_REPORT_SIZE_ACTIVE_PROFILE 2
#define LOGITECH_G300_REPORT_SIZE_PROFILE 35
struct logitech_g300_resolution {
uint8_t dpi :7; /* Range 1-10. dpi = 250*value */
uint8_t is_default :1;
} __attribute__((packed));
struct logitech_g300_button {
uint8_t code;
uint8_t modifier;
uint8_t key;
} __attribute__((packed));
struct logitech_g300_profile_report {
uint8_t id; /* F3, F4, F5 */
uint8_t led; /* 00=off, 01=red, 02=green, 03=yellow, 04=blue, 05=pink, 06=turquoise, 07=white */
uint8_t frequency; /* 00=1000, 01=125, 02=250, 03=500 */
struct logitech_g300_resolution dpi_levels[LOGITECH_G300_NUM_DPI];
uint8_t unknown; /* dpi index for shift, but something else too */
struct logitech_g300_button buttons[LOGITECH_G300_BUTTON_MAX + 1];
} __attribute__((packed));
struct logitech_g300_profile_data {
struct logitech_g300_profile_report report;
};
struct logitech_g300_data {
struct logitech_g300_profile_data profile_data[LOGITECH_G300_PROFILE_MAX + 1];
};
_Static_assert(sizeof(struct logitech_g300_profile_report) == LOGITECH_G300_REPORT_SIZE_PROFILE,
"Size of logitech_g300_profile_report is wrong");
struct logitech_g300_button_type_mapping {
uint8_t raw;
enum ratbag_button_type type;
};
static const struct logitech_g300_button_type_mapping logitech_g300_button_type_mapping[] = {
{ 0, RATBAG_BUTTON_TYPE_LEFT },
{ 1, RATBAG_BUTTON_TYPE_RIGHT },
{ 2, RATBAG_BUTTON_TYPE_MIDDLE },
{ 3, RATBAG_BUTTON_TYPE_THUMB },
{ 4, RATBAG_BUTTON_TYPE_THUMB2 },
{ 5, RATBAG_BUTTON_TYPE_PINKIE },
{ 6, RATBAG_BUTTON_TYPE_PINKIE2 },
{ 7, RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP },
{ 8, RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP },
};
static enum ratbag_button_type
logitech_g300_raw_to_button_type(uint8_t data)
{
const struct logitech_g300_button_type_mapping *mapping;
ARRAY_FOR_EACH(logitech_g300_button_type_mapping, mapping) {
if (mapping->raw == data)
return mapping->type;
}
return RATBAG_BUTTON_TYPE_UNKNOWN;
}
struct logitech_g300_button_mapping {
uint8_t raw;
struct ratbag_button_action action;
};
static struct logitech_g300_button_mapping logitech_g300_button_mapping[] = {
/* 0x00 is either key or unassigned. Must be handled separatly */
{ 0x01, BUTTON_ACTION_BUTTON(1) },
{ 0x02, BUTTON_ACTION_BUTTON(2) },
{ 0x03, BUTTON_ACTION_BUTTON(3) },
{ 0x04, BUTTON_ACTION_BUTTON(4) },
{ 0x05, BUTTON_ACTION_BUTTON(5) },
{ 0x06, BUTTON_ACTION_BUTTON(6) },
{ 0x07, BUTTON_ACTION_BUTTON(7) },
{ 0x08, BUTTON_ACTION_BUTTON(8) },
{ 0x09, BUTTON_ACTION_BUTTON(9) },
{ 0x0A, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP) },
{ 0x0B, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN) },
{ 0x0C, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP) },
{ 0x0D, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP) },
{ 0x0E, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE) },
{ 0x0F, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT) },
};
static const struct ratbag_button_action*
logitech_g300_raw_to_button_action(uint8_t data)
{
struct logitech_g300_button_mapping *mapping;
ARRAY_FOR_EACH(logitech_g300_button_mapping, mapping) {
if (mapping->raw == data)
return &mapping->action;
}
return NULL;
}
static uint8_t
logitech_g300_button_action_to_raw(const struct ratbag_button_action *action)
{
struct logitech_g300_button_mapping *mapping;
ARRAY_FOR_EACH(logitech_g300_button_mapping, mapping) {
if (ratbag_button_action_match(&mapping->action, action))
return mapping->raw;
}
return 0;
}
struct logitech_g300_frequency_mapping {
uint8_t raw;
unsigned int frequency;
};
static struct logitech_g300_frequency_mapping logitech_g300_frequency_mapping[] = {
{ 0, 1000 },
{ 1, 125 },
{ 2, 250 },
{ 3, 500 },
};
static unsigned int
logitech_g300_raw_to_frequency(uint8_t data)
{
struct logitech_g300_frequency_mapping *mapping;
ARRAY_FOR_EACH(logitech_g300_frequency_mapping, mapping) {
if (mapping->raw == data)
return mapping->frequency;
}
return 0;
}
static uint8_t
logitech_g300_frequency_to_raw(unsigned int frequency)
{
struct logitech_g300_frequency_mapping *mapping;
ARRAY_FOR_EACH(logitech_g300_frequency_mapping, mapping) {
if (mapping->frequency == frequency)
return mapping->raw;
}
return 0;
}
static int
logitech_g300_current_profile(struct ratbag_device *device)
{
struct logitech_g300_data *drv_data = device->drv_data;
uint8_t buf[LOGITECH_G300_REPORT_SIZE_ACTIVE_PROFILE];
int i, ret;
ret = ratbag_hidraw_raw_request(device, LOGITECH_G300_REPORT_ID_GET_ACTIVE_PROFILE, buf,
sizeof(buf), HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret < 0)
return ret;
if (ret != sizeof(buf))
return -EIO;
/* The current profile is identifed by its LED color code. */
for (i = 0; i <= LOGITECH_G300_PROFILE_MAX; i++) {
struct logitech_g300_profile_report report;
report = drv_data->profile_data[i].report;
if (report.led == buf[1])
return i;
}
return -EPROTO;
}
static int
logitech_g300_set_current_profile(struct ratbag_device *device, unsigned int index)
{
uint8_t buf[] = {LOGITECH_G300_REPORT_ID_SET_ACTIVE_PROFILE, 0x80 + index, 0x00, 0x00};
int ret;
if (index > LOGITECH_G300_PROFILE_MAX)
return -EINVAL;
ret = ratbag_hidraw_raw_request(device, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
return ret == sizeof(buf) ? 0 : ret;
}
#if 0
static int
logitech_g300_set_current_resolution(struct ratbag_device *device, unsigned int index)
{
uint8_t buf[] = {LOGITECH_G300_REPORT_ID_SET_ACTIVE_PROFILE, 0x40 + index*2, 0x00, 0x00};
int ret;
if (index >= LOGITECH_G300_NUM_DPI)
return -EINVAL;
ret = ratbag_hidraw_raw_request(device, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
return ret == sizeof(buf) ? 0 : ret;
}
#endif
static void
logitech_g300_read_profile(struct ratbag_profile *profile, unsigned int index)
{
struct ratbag_device *device = profile->device;
struct logitech_g300_data *drv_data = device->drv_data;
struct logitech_g300_profile_data *pdata;
struct logitech_g300_profile_report *report;
struct ratbag_resolution *resolution;
unsigned int i, hz;
uint8_t report_id;
int rc;
assert(index <= LOGITECH_G300_PROFILE_MAX);
pdata = &drv_data->profile_data[index];
report = &pdata->report;
switch (index) {
case 0: report_id = LOGITECH_G300_REPORT_ID_PROFILE_0; break;
case 1: report_id = LOGITECH_G300_REPORT_ID_PROFILE_1; break;
case 2: report_id = LOGITECH_G300_REPORT_ID_PROFILE_2; break;
}
rc = ratbag_hidraw_raw_request(device, report_id,
(uint8_t*)report,
sizeof(*report),
HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (rc < (signed)sizeof(*report)) {
log_error(device->ratbag,
"Error while requesting profile: %d\n", rc);
return;
}
hz = logitech_g300_raw_to_frequency(report->frequency);
for (i = 0; i < profile->resolution.num_modes; i++) {
struct logitech_g300_resolution *res =
&report->dpi_levels[i];
resolution = &profile->resolution.modes[i];
resolution->dpi_x = res->dpi * 250;
resolution->dpi_y = res->dpi * 250;
resolution->hz = hz;
resolution->is_default = res->is_default;
}
}
static void
logitech_g300_read_button(struct ratbag_button *button)
{
const struct ratbag_button_action *action;
struct ratbag_profile *profile = button->profile;
struct ratbag_device *device = profile->device;
struct logitech_g300_data *drv_data = device->drv_data;
struct logitech_g300_profile_data *pdata;
struct logitech_g300_profile_report *profile_report;
struct logitech_g300_button *button_report;
pdata = &drv_data->profile_data[profile->index];
profile_report = &pdata->report;
button_report = &profile_report->buttons[button->index];
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
button->type = logitech_g300_raw_to_button_type(button->index);
action = logitech_g300_raw_to_button_action(button_report->code);
if (action) {
button->action = *action;
}
else if (button_report->code == 0x00 && (button_report->modifier > 0x00 || button_report->key > 0x00))
{
struct ratbag_button_action *key_action = &button->action;
key_action->type = RATBAG_BUTTON_ACTION_TYPE_KEY;
key_action->action.key.key = ratbag_hidraw_get_keycode_from_keyboard_usage(
device, button_report->key);
}
}
static int
logitech_g300_test_hidraw(struct ratbag_device *device)
{
return ratbag_hidraw_has_report(device, LOGITECH_G300_REPORT_ID_GET_ACTIVE_PROFILE);
}
static int
logitech_g300_probe(struct ratbag_device *device)
{
int rc;
struct ratbag_profile *profile;
struct logitech_g300_data *drv_data = NULL;
int active_idx;
rc = ratbag_find_hidraw(device, logitech_g300_test_hidraw);
if (rc)
goto err;
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
/* profiles are 0-indexed */
ratbag_device_init_profiles(device,
LOGITECH_G300_PROFILE_MAX + 1,
LOGITECH_G300_NUM_DPI,
LOGITECH_G300_BUTTON_MAX + 1,
LOGITECH_G300_NUM_LED);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_PROFILE);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_KEY);
active_idx = logitech_g300_current_profile(device);
if (active_idx < 0) {
log_error(device->ratbag,
"Can't talk to the mouse: '%s' (%d)\n",
strerror(-active_idx),
active_idx);
rc = -ENODEV;
goto err;
}
list_for_each(profile, &device->profiles, link) {
if (profile->index == (unsigned int)active_idx) {
profile->is_active = true;
break;
}
}
log_raw(device->ratbag,
"'%s' is in profile %d\n",
ratbag_device_get_name(device),
profile->index);
return 0;
err:
free(drv_data);
ratbag_set_drv_data(device, NULL);
return rc;
}
static int
logitech_g300_write_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct logitech_g300_data *drv_data = device->drv_data;
struct logitech_g300_profile_data *pdata;
struct logitech_g300_profile_report *report;
struct ratbag_button *button;
uint8_t *buf;
unsigned int hz, i;
int rc;
pdata = &drv_data->profile_data[profile->index];
report = &pdata->report;
/* The same hz is used for all resolutions */
hz = profile->resolution.modes[0].hz;
report->frequency = logitech_g300_frequency_to_raw(hz);
for (i = 0; i < profile->resolution.num_modes; i++) {
struct ratbag_resolution *resolution = &profile->resolution.modes[i];
struct logitech_g300_resolution *res = &report->dpi_levels[i];
res->dpi = resolution->dpi_x / 250;
res->is_default = resolution->is_default;
}
list_for_each(button, &profile->buttons, link) {
struct ratbag_button_action *action = &button->action;
struct logitech_g300_button *raw_button;
if (!button->dirty)
continue;
raw_button = &report->buttons[button->index];
raw_button->code = logitech_g300_button_action_to_raw(action);
raw_button->modifier = 0x00;
raw_button->key = 0x00;
if (action->type == RATBAG_BUTTON_ACTION_TYPE_KEY) {
raw_button->key = ratbag_hidraw_get_keyboard_usage_from_keycode(
device, action->action.key.key);
}
}
buf = (uint8_t*)report;
rc = ratbag_hidraw_raw_request(device, report->id,
buf,
sizeof(*report),
HID_FEATURE_REPORT,
HID_REQ_SET_REPORT);
return rc;
}
static int
logitech_g300_commit(struct ratbag_device *device)
{
struct ratbag_profile *profile;
int rc = 0;
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
continue;
log_debug(device->ratbag,
"Profile %d changed, rewriting\n", profile->index);
rc = logitech_g300_write_profile(profile);
if (rc)
return rc;
}
return 0;
}
static void
logitech_g300_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
free(ratbag_get_drv_data(device));
}
struct ratbag_driver logitech_g300_driver = {
.name = "Logitech G300",
.id = "logitech_g300",
.probe = logitech_g300_probe,
.remove = logitech_g300_remove,
.read_profile = logitech_g300_read_profile,
.commit = logitech_g300_commit,
.set_active_profile = logitech_g300_set_current_profile,
.read_button = logitech_g300_read_button,
};

View File

@@ -36,6 +36,7 @@
#define ROCCAT_PROFILE_MAX 4
#define ROCCAT_BUTTON_MAX 23
#define ROCCAT_NUM_DPI 5
#define ROCCAT_LED_MAX 0
#define ROCCAT_MAX_RETRY_READY 10
@@ -302,6 +303,7 @@ roccat_wait_ready(struct ratbag_device *device)
return 2;
msleep(10);
count++;
}
return -ETIMEDOUT;
@@ -780,13 +782,8 @@ roccat_probe(struct ratbag_device *device)
int active_idx;
rc = ratbag_open_hidraw(device);
if (rc) {
log_error(device->ratbag,
"Can't open corresponding hidraw node: '%s' (%d)\n",
strerror(-rc),
rc);
return -ENODEV;
}
if (rc)
return rc;
if (!ratbag_hidraw_has_report(device, ROCCAT_REPORT_ID_KEY_MAPPING)) {
ratbag_close_hidraw(device);
@@ -800,7 +797,8 @@ roccat_probe(struct ratbag_device *device)
ratbag_device_init_profiles(device,
ROCCAT_PROFILE_MAX + 1,
ROCCAT_NUM_DPI,
ROCCAT_BUTTON_MAX + 1);
ROCCAT_BUTTON_MAX + 1,
ROCCAT_LED_MAX);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
@@ -808,8 +806,8 @@ roccat_probe(struct ratbag_device *device)
if (active_idx < 0) {
log_error(device->ratbag,
"Can't talk to the mouse: '%s' (%d)\n",
strerror(-rc),
rc);
strerror(-active_idx),
active_idx);
rc = -ENODEV;
goto err;
}

View File

@@ -122,6 +122,34 @@ test_read_button(struct ratbag_button *button)
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
}
static void
test_read_led(struct ratbag_led *led)
{
struct ratbag_device *device = led->profile->device;
struct ratbag_test_device *d = ratbag_get_drv_data(device);
struct ratbag_test_profile *p = &d->profiles[led->profile->index];
struct ratbag_test_led t_led = p->leds[led->index];
switch (t_led.mode) {
case RATBAG_LED_ON:
led->mode = RATBAG_LED_ON;
break;
case RATBAG_LED_CYCLE:
led->mode = RATBAG_LED_CYCLE;
break;
case RATBAG_LED_BREATHING:
led->mode = RATBAG_LED_BREATHING;
break;
default:
led->mode = RATBAG_LED_OFF;
}
led->color.red = t_led.color.red;
led->color.green = t_led.color.green;
led->color.blue = t_led.color.blue;
led->hz = t_led.hz;
led->brightness = t_led.brightness;
}
static int
test_write_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
@@ -131,6 +159,17 @@ test_write_button(struct ratbag_button *button,
return 0;
}
static int
test_write_led(struct ratbag_led *led,
enum ratbag_led_mode mode,
struct ratbag_color color, unsigned int hz,
unsigned int brightness)
{
/* check if the device is still valid */
assert(ratbag_get_drv_data(led->profile->device) != NULL);
return 0;
}
static int
test_fake_probe(struct ratbag_device *device)
{
@@ -146,7 +185,8 @@ test_probe(struct ratbag_device *device, void *data)
ratbag_device_init_profiles(device,
test_device->num_profiles,
test_device->num_resolutions,
test_device->num_buttons);
test_device->num_buttons,
test_device->num_leds);
if (test_device->num_profiles > 1)
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
@@ -175,6 +215,8 @@ struct ratbag_driver test_driver = {
.write_profile = test_write_profile,
.set_active_profile = test_set_active_profile,
.read_button = test_read_button,
.read_led = test_read_led,
.write_button = test_write_button,
.write_resolution_dpi = NULL,
.write_led = test_write_led,
};

View File

@@ -138,6 +138,21 @@ static const struct hidpp20_1b04_physical_mapping hidpp20_1b04_physical_mapping[
BUTTON_PHYS_MAPPING(221, "LedToggle" , RATBAG_BUTTON_TYPE_UNKNOWN),
};
struct hidpp20_8070_location_mapping {
uint16_t value;
const char *name;
enum ratbag_led_type type;
};
#define LED_LOC_MAPPING(v_, n_, t_) \
{ v_, n_, t_ }
static const struct hidpp20_8070_location_mapping hidpp20_8070_location_mapping[] = {
LED_LOC_MAPPING(0, "None", RATBAG_LED_TYPE_UNKNOWN),
LED_LOC_MAPPING(1, "Logo LED", RATBAG_LED_TYPE_LOGO),
LED_LOC_MAPPING(2, "Side LED", RATBAG_LED_TYPE_SIDE),
};
const struct ratbag_button_action *
hidpp20_1b04_get_logical_mapping(uint16_t value)
{
@@ -203,6 +218,32 @@ hidpp20_1b04_get_physical_mapping_name(uint16_t value)
return "UNKNOWN";
}
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;
ARRAY_FOR_EACH(hidpp20_8070_location_mapping, map) {
if (map->value == value)
return map->type;
}
return RATBAG_LED_TYPE_UNKNOWN;
}
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;
ARRAY_FOR_EACH(hidpp20_8070_location_mapping, map) {
if (map->value == value)
return map->name;
}
return "UNKNOWN";
}
int
hidpp_write_command(struct hidpp_device *dev, uint8_t *cmd, int size)
{

View File

@@ -26,13 +26,12 @@
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
*/
#pragma once
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
#ifndef HIDPP_GENERIC_H
#define HIDPP_GENERIC_H
#define HIDPP_RECEIVER_IDX 0xFF
#define HIDPP_WIRED_DEVICE_IDX 0x00
@@ -106,6 +105,12 @@ hidpp20_1b04_get_physical_mapping_name(uint16_t value);
enum ratbag_button_type
hidpp20_1b04_get_physical_mapping(uint16_t value);
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value);
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value);
const char *
hidpp20_1b04_get_logical_mapping_name(uint16_t value);
@@ -200,4 +205,8 @@ hidpp_cpu_to_le_u16(uint16_t data)
return result;
}
#endif /* HIDPP_GENERIC_H */
static inline uint32_t
hidpp_get_unaligned_be_u32(uint8_t *buf)
{
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}

View File

@@ -305,7 +305,7 @@ hidpp10_get_hidpp_notifications(struct hidpp10_device *dev,
return res;
*reporting_flags = notifications.msg.parameters[0];
*reporting_flags |= (notifications.msg.parameters[0] & 0x1F) << 8;
*reporting_flags |= (notifications.msg.parameters[1] & 0x1F) << 8;
*reporting_flags |= (notifications.msg.parameters[2] & 0x7 ) << 16;
return res;
@@ -1397,7 +1397,7 @@ hidpp10_profile_set_names(struct hidpp10_device *dev, struct hidpp10_profile *pr
memcpy(metadata->lgs02.marker, "LGS02", sizeof(metadata->lgs02.marker));
hidpp10_uchar8_to_uchar16(metadata->lgs02.name,
profile->name,
sizeof(metadata->lgs02.name));
ARRAY_LENGTH(metadata->lgs02.name));
for (i = 0; i < ARRAY_LENGTH(metadata->lgs02.macro_names); i++) {
hidpp10_uchar8_to_uchar16(metadata->lgs02.macro_names[i],
profile->macro_names[i],
@@ -1525,10 +1525,10 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
profile->dpi_modes[i].yres);
hidpp_log_raw(&dev->base,
"LED status: 1:%s 2:%s 3:%s 4:%s\n",
(profile->dpi_modes[i].led[0] & 0x2) ? "on" : "off",
(profile->dpi_modes[i].led[1] & 0x2) ? "on" : "off",
(profile->dpi_modes[i].led[2] & 0x2) ? "on" : "off",
(profile->dpi_modes[i].led[3] & 0x2) ? "on" : "off");
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);
@@ -1582,11 +1582,12 @@ static const enum ratbag_button_action_special hidpp10_profiles_specials[] = {
[0x02] = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT,
[0x03] = RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL,
[0x04] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP,
[0x05] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP,
[0x05 ... 0x07] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x06 ... 0x07] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x08] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN,
[0x09] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP,
[0x09] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN,
[0x0a ... 0x0f] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
@@ -1596,8 +1597,9 @@ static const enum ratbag_button_action_special hidpp10_profiles_specials[] = {
[0x12 ... 0x1f] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x20] = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN,
[0x21] = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_DOWN,
[0x21 ... 0xff] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x22 ... 0xff] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
};
enum ratbag_button_action_special
@@ -2555,8 +2557,7 @@ hidpp10_get_extended_pairing_information(struct hidpp10_device *dev,
if (res)
return -1;
*serial = hidpp_get_unaligned_be_u16(&info.msg.string[2]) << 16;
*serial |= hidpp_get_unaligned_be_u16(&info.msg.string[4]);
*serial = hidpp_get_unaligned_be_u32(&info.msg.string[1]);
return 0;
}

View File

@@ -26,8 +26,7 @@
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
*/
#ifndef HIDPP_10_H
#define HIDPP_10_H
#pragma once
#include <stdint.h>
#include <stdbool.h>
@@ -494,6 +493,7 @@ struct hidpp10_profile {
} buttons[PROFILE_NUM_BUTTONS];
union hidpp10_macro_data *macros[PROFILE_NUM_BUTTONS];
size_t num_buttons;
size_t num_leds;
unsigned int initialized;
};
@@ -694,5 +694,3 @@ hidpp10_get_firmare_information(struct hidpp10_device *dev,
uint8_t *major,
uint8_t *minor,
uint8_t *build_number);
#endif /* HIDPP_10_H */

View File

@@ -500,6 +500,118 @@ err:
return rc;
}
/* -------------------------------------------------------------------------- */
/* 0x8070 - Color LED effects */
/* -------------------------------------------------------------------------- */
#define CMD_COLOR_LED_EFFECTS_GET_INFO 0x00
#define CMD_COLOR_LED_EFFECTS_GET_ZONE_INFO 0x10
static int
hidpp20_color_led_zones_get_count(struct hidpp20_device *device, uint8_t reg)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_COLOR_LED_EFFECTS_GET_INFO,
};
struct hidpp20_color_led_info *info;
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_COLOR_LED_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info = (struct hidpp20_color_led_info *)msg.msg.parameters;
return info->zone_count;
}
int
hidpp20_color_led_effects_get_zone_info(struct hidpp20_device *device,
uint8_t reg,
struct hidpp20_color_led_zone_info *info)
{
int rc;
struct hidpp20_color_led_zone_info *msg_info;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_COLOR_LED_EFFECTS_GET_ZONE_INFO,
.msg.parameters[0] = info->index,
};
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
msg_info = (struct hidpp20_color_led_zone_info *)msg.msg.parameters;
info->location = hidpp_be_u16_to_cpu(msg_info->location);
info->num_effects = msg_info->num_effects;
info->persistency_caps = msg_info->persistency_caps;
return 0;
}
int
hidpp20_color_led_effects_get_zone_infos(struct hidpp20_device *device,
struct hidpp20_color_led_zone_info **infos_list)
{
uint8_t feature_index;
struct hidpp20_color_led_zone_info *i_list, *info;
uint8_t num_infos;
unsigned i;
int rc;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_COLOR_LED_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
rc = hidpp20_color_led_zones_get_count(device, feature_index);
if (rc < 0)
return rc;
num_infos = rc;
if (num_infos == 0) {
*infos_list = NULL;
return 0;
}
i_list = zalloc(num_infos * sizeof(struct hidpp20_color_led_zone_info));
for (i = 0; i < num_infos; i++) {
info = &i_list[i];
info->index = i;
rc = hidpp20_color_led_effects_get_zone_info(device, feature_index, info);
if (rc)
goto err;
hidpp_log_raw(&device->base,
"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),
info->num_effects,
info->persistency_caps);
}
*infos_list = i_list;
return num_infos;
err:
free(i_list);
return rc;
}
/* -------------------------------------------------------------------------- */
/* 0x1b04: Special keys and mouse buttons */
/* -------------------------------------------------------------------------- */
@@ -863,8 +975,9 @@ int hidpp20_adjustable_dpi_get_sensors(struct hidpp20_device *device,
*sensors_list = s_list;
return num_sensors;
err:
num_sensors = 0;
free(s_list);
return rc;
return rc > 0 ? -EPROTO : rc;
}
int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
@@ -929,6 +1042,7 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
#define HIDPP20_ONBOARD_PROFILES_MEMORY_TYPE_G402 0x01
#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_MACRO_TYPE_G402 0x01
struct hidpp20_onboard_profiles_info {
@@ -950,7 +1064,7 @@ int
hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
uint8_t read_rom,
uint8_t page,
uint8_t section,
uint16_t section,
uint8_t result[16])
{
uint8_t feature_index;
@@ -961,13 +1075,13 @@ hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
.msg.address = CMD_ONBOARD_PROFILES_MEMORY_READ,
.msg.parameters[0] = read_rom,
.msg.parameters[1] = page,
.msg.parameters[2] = 0,
.msg.parameters[3] = section,
};
if (read_rom > 1)
return -EINVAL;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[2], section);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
if (feature_index == 0)
@@ -987,8 +1101,9 @@ hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
static int
hidpp20_onboard_profiles_write_start(struct hidpp20_device *device,
uint8_t page,
uint8_t section)
uint16_t page,
uint16_t section,
uint16_t size)
{
uint8_t feature_index;
int rc;
@@ -996,13 +1111,12 @@ hidpp20_onboard_profiles_write_start(struct hidpp20_device *device,
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_ONBOARD_PROFILES_MEMORY_ADDR_WRITE,
.msg.parameters[0] = 0x00,
.msg.parameters[1] = page,
.msg.parameters[2] = 0,
.msg.parameters[3] = section,
.msg.parameters[4] = 0x01,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], page);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[2], section);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[4], size);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
if (feature_index == 0)
@@ -1044,8 +1158,8 @@ hidpp20_onboard_profiles_write_end(struct hidpp20_device *device)
static int
hidpp20_onboard_profiles_write_data(struct hidpp20_device *device,
uint8_t page,
uint8_t section,
uint16_t page,
uint16_t section,
uint8_t *data,
size_t len)
{
@@ -1065,7 +1179,10 @@ hidpp20_onboard_profiles_write_data(struct hidpp20_device *device,
msg.msg.sub_id = feature_index;
rc = hidpp20_onboard_profiles_write_start(device, page, section);
rc = hidpp20_onboard_profiles_write_start(device,
page,
section,
HIDPP20_PAGE_SIZE);
if (rc)
return rc;
@@ -1091,7 +1208,7 @@ hidpp20_onboard_profiles_write_data(struct hidpp20_device *device,
static int
hidpp20_onboard_profiles_write_page(struct hidpp20_device *device,
uint8_t page,
uint16_t page,
uint8_t data[HIDPP20_PAGE_SIZE])
{
uint16_t crc;
@@ -1242,7 +1359,8 @@ hidpp20_onboard_profiles_initialize(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_G303) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900)) {
hidpp_log_error(&device->base,
"Profile layout not supported: 0x%02x.\n",
info->profile_format_id);
@@ -1283,10 +1401,12 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
info->profile_count * sizeof(struct hidpp20_profile));
profiles->num_profiles = info->profile_count;
profiles->num_rom_profiles = info->profile_count_oob;
profiles->num_buttons = msg.msg.parameters[5] <= 16 ? msg.msg.parameters[5] : 16;
profiles->num_modes = HIDPP20_DPI_COUNT;
profiles->num_leds = HIDPP20_LED_COUNT;
profiles->has_g_shift = (info->mechanical_layout & 0x03) == 2;
profiles->has_dpi_shift = (info->mechanical_layout & 0x0c) == 2;
profiles->has_dpi_shift = ((info->mechanical_layout & 0x0c) >> 2) == 2;
switch(info->various_info & 0x07) {
case 1:
profiles->corded = 1;
@@ -1300,8 +1420,6 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
break;
}
*profiles_list = profiles;
return 0;
@@ -1467,13 +1585,29 @@ hidpp20_onboard_profiles_parse_macro(struct hidpp20_device *device,
return 0;
}
static unsigned int
hidpp20_onboard_profiles_compute_dict_size(const struct hidpp20_device *device,
const struct hidpp20_profiles *profiles)
{
unsigned p, num_offset;
num_offset = 0;
p = profiles->num_profiles;
while (p) {
p >>= 2;
num_offset += 16;
}
return num_offset;
}
int
hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
struct hidpp20_profiles **profiles_list)
{
unsigned i;
unsigned i, offset, num_offset;
int rc;
uint8_t data[16] = {0};
uint8_t data[HIDPP20_PAGE_SIZE] = {0};
struct hidpp20_profiles *profiles = NULL;
rc = hidpp20_onboard_profiles_initialize(device,
@@ -1483,16 +1617,21 @@ hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
assert(profiles);
rc = hidpp20_onboard_profiles_read_memory(device,
0x00,
0x00,
0x00,
data);
if (rc < 0)
return rc;
num_offset = hidpp20_onboard_profiles_compute_dict_size(device,
profiles);
for (offset = 0; offset < num_offset; offset += 16) {
rc = hidpp20_onboard_profiles_read_memory(device,
0x00,
0x00,
offset,
data + offset);
if (rc < 0)
return rc;
}
for (i = 0; i < profiles->num_profiles; i++) {
uint8_t *d = data + 4 * i;
uint8_t *d = data + offset + 4 * i;
if (d[0] == 0xFF && d[1] == 0xFF)
break;
@@ -1532,7 +1671,8 @@ hidpp20_onboard_profiles_destroy(struct hidpp20_device *device,
static int
hidpp20_onboard_profiles_find_and_read_profile(struct hidpp20_device *device,
unsigned int index,
uint8_t *data)
uint8_t *data,
unsigned int num_rom_profiles)
{
int rc;
unsigned i;
@@ -1565,13 +1705,19 @@ hidpp20_onboard_profiles_find_and_read_profile(struct hidpp20_device *device,
}
/* something went wrong, the mouse is using the factory profile in ROM */
if (num_rom_profiles == 0)
return -EINVAL;
if (index >= num_rom_profiles)
index = num_rom_profiles - 1;
for (i = 0; i < HIDPP20_PROFILE_SIZE / 0x10; i++) {
rc = hidpp20_onboard_profiles_read_memory(device,
1,
index + 1,
i * 0x10,
data + i * 0x10);
if (rc < 0)
if (rc != 0)
return rc;
}
@@ -1592,7 +1738,7 @@ hidpp20_onboard_profiles_enable_profile(struct hidpp20_device *device,
profiles_list->profiles[index].index = index + 1;
profiles_list->profiles[index].enabled = 0x01;
for (i = 0; i < 3; i++) {
for (i = 0; i < profiles_list->num_profiles; i++) {
data[buffer_index++] = 0x00;
data[buffer_index++] = i + 1;
data[buffer_index++] = profiles_list->profiles[i].enabled;
@@ -1607,9 +1753,13 @@ hidpp20_onboard_profiles_enable_profile(struct hidpp20_device *device,
memset(data + buffer_index, 0xff, sizeof(data) - buffer_index);
hidpp_log_buf_error(&device->base, "dictionary: ", data, 16);
hidpp_log_buf_debug(&device->base,
"dictionary: ",
data,
hidpp20_onboard_profiles_compute_dict_size(device,
profiles_list));
return hidpp20_onboard_profiles_write_page(device, 0x00, data);
return hidpp20_onboard_profiles_write_page(device, 0x0000, data);
}
union hidpp20_internal_profile {
@@ -1619,11 +1769,7 @@ union hidpp20_internal_profile {
uint8_t default_dpi;
uint8_t switched_dpi;
uint16_t dpi[5];
struct {
uint8_t red;
uint8_t green;
uint8_t blue;
} profile_color;
struct hidpp20_color profile_color;
uint8_t power_mode;
uint8_t angle_snapping;
uint8_t reserved[14];
@@ -1633,8 +1779,7 @@ union hidpp20_internal_profile {
char txt[16 * 3];
uint8_t raw[16 * 3];
} name;
uint8_t logo_effect[11]; /* G303 only */
uint8_t side_effects[11]; /* G303 only */
struct hidpp20_internal_led leds[2]; /* G303, g502, g900 only */
uint8_t free[24];
uint16_t crc;
} __attribute__((packed)) profile;
@@ -1747,6 +1892,40 @@ hidpp20_buttons_from_cpu(struct hidpp20_profile *profile,
}
}
static void
hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
struct hidpp20_internal_led internal_led)
{
uint16_t period = 0;
uint8_t brightness = 0;
led->mode = (enum hidpp20_led_mode)internal_led.mode;
switch (led->mode) {
case HIDPP20_LED_CYCLE:
period = hidpp_be_u16_to_cpu(internal_led.effect.cycle.period_or_speed);
brightness = internal_led.effect.cycle.intensity;
if (brightness == 0)
brightness = 100;
break;
case HIDPP20_LED_BREATHING:
period = hidpp_be_u16_to_cpu(internal_led.effect.breath.period_or_speed);
brightness = internal_led.effect.breath.intensity;
if (brightness == 0)
brightness = 100;
led->color = internal_led.effect.breath.color;
break;
case HIDPP20_LED_ON:
led->color = internal_led.effect.fixed.color;
break;
case HIDPP20_LED_OFF:
break;
}
led->period = period;
led->brightness = brightness;
}
int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
unsigned int index,
struct hidpp20_profiles *profiles_list)
@@ -1760,8 +1939,11 @@ int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
if (index >= profiles_list->num_profiles)
return -EINVAL;
rc = hidpp20_onboard_profiles_find_and_read_profile(device, index, data);
if (rc < 0)
rc = hidpp20_onboard_profiles_find_and_read_profile(device,
index,
data,
profiles_list->num_rom_profiles);
if (rc != 0)
return rc;
profile->report_rate = 1000 / max(1, pdata.profile.report_rate);
@@ -1772,6 +1954,9 @@ int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
profile->dpi[i] = hidpp_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_buttons_to_cpu(device, profile, pdata.profile.buttons, profiles_list->num_buttons);
memcpy(profile->name, pdata.profile.name.txt, sizeof(profile->name));
@@ -1789,6 +1974,46 @@ int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
return 0;
}
static void
hidpp20_onboard_profiles_write_led(struct hidpp20_internal_led *internal_led,
struct hidpp20_led *led)
{
uint16_t period = led->period;
uint8_t brightness = led->brightness;
memset(internal_led, 0, sizeof(*internal_led));
internal_led->mode = (uint8_t)led->mode;
switch (led->mode) {
case HIDPP20_LED_CYCLE:
internal_led->effect.cycle.period_or_speed = hidpp_cpu_to_be_u16(period);
if (brightness < 100)
internal_led->effect.cycle.intensity = brightness;
else
internal_led->effect.cycle.intensity = 0;
break;
case HIDPP20_LED_BREATHING:
internal_led->effect.breath.color.red = led->color.red;
internal_led->effect.breath.color.blue = led->color.blue;
internal_led->effect.breath.color.green = led->color.green;
internal_led->effect.breath.period_or_speed = hidpp_cpu_to_be_u16(period);
if (brightness < 100)
internal_led->effect.breath.intensity = brightness;
else
internal_led->effect.breath.intensity = 0;
break;
case HIDPP20_LED_ON:
internal_led->effect.fixed.color.red = led->color.red;
internal_led->effect.fixed.color.blue = led->color.blue;
internal_led->effect.fixed.color.green = led->color.green;
internal_led->effect.fixed.effect = 0;
break;
case HIDPP20_LED_OFF:
break;
}
}
int hidpp20_onboard_profiles_write(struct hidpp20_device *device,
unsigned int index,
struct hidpp20_profiles *profiles_list)
@@ -1802,7 +2027,10 @@ int hidpp20_onboard_profiles_write(struct hidpp20_device *device,
if (index >= profiles_list->num_profiles)
return -EINVAL;
rc = hidpp20_onboard_profiles_find_and_read_profile(device, index, data);
rc = hidpp20_onboard_profiles_find_and_read_profile(device,
index,
data,
profiles_list->num_rom_profiles);
if (rc < 0)
return rc;
@@ -1818,6 +2046,9 @@ int hidpp20_onboard_profiles_write(struct hidpp20_device *device,
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]);
hidpp20_buttons_from_cpu(profile, pdata.profile.buttons, profiles_list->num_buttons);
memcpy(pdata.profile.name.txt, profile->name, sizeof(profile->name));
@@ -1845,8 +2076,9 @@ static const enum ratbag_button_action_special hidpp20_profiles_specials[] = {
[0x09] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x0a] = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP,
[0x0b] = RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE,
[0x0c] = RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL,
[0x0c ... 0xff] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x0d ... 0xff] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
};
enum ratbag_button_action_special
@@ -1892,6 +2124,9 @@ hidpp20_device_new(const struct hidpp_device *base, unsigned int idx)
goto err;
}
if (dev->proto_major < 2)
goto err;
rc = hidpp20_feature_set_get(dev);
if (rc < 0)
goto err;

View File

@@ -26,8 +26,7 @@
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
*/
#ifndef HIDPP_20_H
#define HIDPP_20_H
#pragma once
#include <stdint.h>
@@ -258,6 +257,41 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
#define HIDPP_PAGE_COLOR_LED_EFFECTS 0x8070
struct hidpp20_color_led_info;
struct hidpp20_color_led_zone_info;
int
hidpp20_color_led_effects_get_zone_info(struct hidpp20_device *device,
uint8_t reg, struct hidpp20_color_led_zone_info *info);
int
hidpp20_color_led_effects_get_zone_infos(struct hidpp20_device *device,
struct hidpp20_color_led_zone_info **infos_list);
enum hidpp20_color_led_location {
HIDPP20_COLOR_LED_LOCATION_UNDEFINED = 0,
HIDPP20_COLOR_LED_LOCATION_PRIMARY,
HIDPP20_COLOR_LED_LOCATION_LOGO,
HIDPP20_COLOR_LED_LOCATION_LEFT,
HIDPP20_COLOR_LED_LOCATION_RIGHT,
HIDPP20_COLOR_LED_LOCATION_COMBINED,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_1,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_2,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_3,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_4,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_5,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_6,
};
enum hidpp20_color_led_persistency {
HIDPP20_COLOR_LED_PERSISTENCY_UNSUPPORTED,
HIDPP20_COLOR_LED_PERSISTENCY_ON,
HIDPP20_COLOR_LED_PERSISTENCY_OFF,
HIDPP20_COLOR_LED_PERSISTENCY_ON_OFF,
};
/* -------------------------------------------------------------------------- */
/* 0x8100 - Onboard Profiles */
/* -------------------------------------------------------------------------- */
@@ -276,6 +310,7 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
#define HIDPP20_MODIFIER_KEY_SHIFT 0x02
#define HIDPP20_DPI_COUNT 5
#define HIDPP20_LED_COUNT 2
union hidpp20_button_binding {
struct {
@@ -317,6 +352,88 @@ union hidpp20_button_binding {
} __attribute__((packed));
_Static_assert(sizeof(union hidpp20_button_binding) == 4, "Invalid size");
struct hidpp20_color_led_info {
uint8_t zone_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_color_led_info) == 5, "Invalid size");
struct hidpp20_color_led_zone_info {
uint8_t index;
uint16_t location;
uint8_t num_effects;
uint8_t persistency_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_info) == 5, "Invalid size");
struct hidpp20_color {
uint8_t red;
uint8_t green;
uint8_t blue;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color) == 3, "Invalid size");
enum hidpp20_led_type {
HIDPP20_LED_UNKNOWN = -1,
HIDPP20_LED_LOGO = 0,
HIDPP20_LED_SIDE,
};
enum hidpp20_led_mode {
HIDPP20_LED_OFF = 0x00,
HIDPP20_LED_ON = 0x01,
HIDPP20_LED_CYCLE = 0x03,
HIDPP20_LED_BREATHING = 0x0a,
};
enum hidpp20_led_waveform {
HIDPP20_LED_WF_DEFAULT = 0x00,
HIDPP20_LED_WF_SINE = 0x01,
HIDPP20_LED_WF_SQUARE = 0x02,
HIDPP20_LED_WF_TRIANGLE = 0x03,
HIDPP20_LED_WF_SAWTOOTH = 0x04,
HIDPP20_LED_WF_SHARKFIN = 0x05,
HIDPP20_LED_WF_EXPONENTIAL = 0x06,
};
struct hidpp20_internal_led {
uint8_t mode; /* enum hidpp20_led_mode */
union {
struct hidpp20_led_fixed {
struct hidpp20_color color;
uint8_t effect;
} __attribute__((packed)) fixed;
struct hidpp20_led_cycle {
uint8_t unused[5];
uint16_t period_or_speed; /* period in ms, speed is device dependent */
uint8_t intensity; /* 1 - 100 percent, 0 means 100 */
} __attribute__((packed)) cycle;
struct hidpp20_led_breath {
struct hidpp20_color color;
uint16_t period_or_speed; /* period in ms, speed is device dependent */
uint8_t waveform; /* enum hidpp20_led_waveform */
uint8_t intensity; /* 1 - 100 percent, 0 means 100 */
} __attribute__((packed)) breath;
uint8_t padding[10];
} __attribute__((packed)) effect;
};
_Static_assert(sizeof(struct hidpp20_led_fixed) == 4, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_cycle) == 8, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_breath) == 7, "Invalid size");
_Static_assert(sizeof(struct hidpp20_internal_led) == 11, "Invalid size");
typedef uint8_t percent_t;
struct hidpp20_led {
enum hidpp20_led_mode mode;
struct hidpp20_color color;
uint16_t period;
percent_t brightness;
};
#define HIDPP20_MACRO_NOOP 0x01
#define HIDPP20_MACRO_DELAY 0x40
#define HIDPP20_MACRO_KEY_PRESS 0x43
@@ -358,12 +475,15 @@ struct hidpp20_profile {
uint16_t dpi[HIDPP20_DPI_COUNT];
union hidpp20_button_binding buttons[32];
union hidpp20_macro_data *macros[32];
struct hidpp20_led leds[HIDPP20_LED_COUNT];
};
struct hidpp20_profiles {
uint8_t num_profiles;
uint8_t num_rom_profiles;
uint8_t num_buttons;
uint8_t num_modes;
uint8_t num_leds;
uint8_t has_g_shift;
uint8_t has_dpi_shift;
uint8_t corded;
@@ -426,7 +546,7 @@ int
hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
uint8_t read_rom,
uint8_t page,
uint8_t section,
uint16_t section,
uint8_t result[16]);
/* -------------------------------------------------------------------------- */
@@ -436,5 +556,3 @@ hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
#define HIDPP_PAGE_MOUSE_BUTTON_SPY 0x8110
#endif /* HIDPP_20_H */

View File

@@ -30,13 +30,20 @@
#include <sys/ioctl.h>
#include <linux/hidraw.h>
#include "usb-ids.h"
#include "hidpp10.h"
#include "libratbag-util.h"
#include "liblur.h"
#define _EXPORT_ __attribute__ ((visibility("default")))
#define MAX_DEVICES 6
#define VENDOR_ID_LOGITECH 0x046d
static inline void
cleanup_hidpp10_device_destroy(struct hidpp10_device **hidpp10_device) {
if (*hidpp10_device)
hidpp10_device_destroy(*hidpp10_device);
}
#define _cleanup_hidpp10_device_destroy_ _cleanup_(cleanup_hidpp10_device_destroy)
struct lur_receiver {
int refcount;
@@ -113,7 +120,7 @@ lur_device_disconnect(struct lur_device *dev)
_EXPORT_ int
lur_is_receiver(uint16_t vid, uint16_t pid)
{
return (vid == VENDOR_ID_LOGITECH && (pid == 0xc52b || pid == 0xc532));
return (vid == USB_VENDOR_ID_LOGITECH && (pid == 0xc52b || pid == 0xc532));
}
static bool
@@ -186,7 +193,7 @@ lur_receiver_enumerate(struct lur_receiver *lur,
dev->present = false;
for (i = 0; i < MAX_DEVICES; i++) {
struct hidpp10_device *d;
_cleanup_hidpp10_device_destroy_ struct hidpp10_device *d;
size_t name_size = 64;
char name[name_size];
uint8_t interval, type;
@@ -232,7 +239,7 @@ lur_receiver_enumerate(struct lur_receiver *lur,
lur_receiver_ref(lur);
dev->refcount = 1;
dev->name = strdup_safe(name);
dev->vid = VENDOR_ID_LOGITECH;
dev->vid = USB_VENDOR_ID_LOGITECH;
dev->pid = wpid;
dev->type = type;
dev->serial = serial;
@@ -240,8 +247,6 @@ lur_receiver_enumerate(struct lur_receiver *lur,
dev->present = true;
list_insert(&lur->devices, &dev->node);
}
hidpp10_device_destroy(d);
}
devices = zalloc(MAX_DEVICES * sizeof(*devices));

View File

@@ -23,8 +23,7 @@
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBLUR_H_
#define _LIBLUR_H_ 1
#pragma once
#ifdef __cplusplus
extern "C" {
@@ -268,4 +267,3 @@ lur_device_get_user_data(const struct lur_device *dev);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,13 +0,0 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
datarootdir=@datarootdir@
pkgdatadir=@datadir@/@PACKAGE@
libdir=@libdir@
includedir=@includedir@
Name: Liblur
Description: Logitech Unifying Receiver configuration library
Version: @LIBRATBAG_VERSION@
Cflags: -I${includedir}
Libs: -L${libdir} -llur
Libs.private: -lm -lrt

View File

@@ -1405,7 +1405,7 @@ ratbag_find_hidraw_node(struct ratbag_device *device,
udev_enumerate_add_match_parent(e, parent_udev);
udev_enumerate_scan_devices(e);
udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
_cleanup_udev_device_unref_ struct udev_device *udev_device;
_cleanup_udev_device_unref_ struct udev_device *udev_device = NULL;
path = udev_list_entry_get_name(entry);
udev_device = udev_device_new_from_syspath(udev, path);
@@ -1417,14 +1417,12 @@ ratbag_find_hidraw_node(struct ratbag_device *device,
goto skip;
matched = match(device);
if (matched == 1) {
rc = 0;
rc = matched ? 0 : -ENODEV;
if (matched == 1)
goto out;
}
skip:
ratbag_close_hidraw(device);
rc = -ENODEV;
}
out:

View File

@@ -21,8 +21,7 @@
* DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRATBAG_HIDRAW_H
#define LIBRATBAG_HIDRAW_H
#pragma once
#include <linux/hid.h>
#include <stdint.h>
@@ -186,4 +185,3 @@ ratbag_hidraw_get_keycode_from_consumer_usage(struct ratbag_device *device,
uint16_t
ratbag_hidraw_get_consumer_usage_from_keycode(struct ratbag_device *device,
unsigned keycode);
#endif /* LIBRATBAG_HIDRAW_H */

View File

@@ -21,8 +21,7 @@
* DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRATBAG_PRIVATE_H
#define LIBRATBAG_PRIVATE_H
#pragma once
#include <linux/input.h>
#include <stdint.h>
@@ -95,6 +94,7 @@ struct ratbag_device {
struct list profiles;
unsigned num_buttons;
unsigned num_leds;
void *drv_data;
@@ -130,6 +130,18 @@ struct ratbag_driver {
*/
void (*remove)(struct ratbag_device *device);
/**
* Callback called when the driver should write any profiles that
* were modified back to the device.
*
* Both profile and button structs have a dirty variable that can
* be used to tell whether or not they've actually changed since
* the last commit. In order to reduce the amount of time
* committing takes, drivers should use this information to avoid
* writing back profiles and buttons that haven't actually changed.
*/
int (*commit)(struct ratbag_device *device);
/**
* Callback called when a read profile is requested by the
* caller of the library.
@@ -141,9 +153,10 @@ struct ratbag_driver {
*/
void (*read_profile)(struct ratbag_profile *profile, unsigned int index);
/**
* Here, the driver should actually write the profile to the
* device.
/*
* FIXME: This function is deprecated and should not be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_profile)(struct ratbag_profile *profile);
@@ -168,29 +181,44 @@ struct ratbag_driver {
*/
void (*read_button)(struct ratbag_button *button);
/**
* For the given button, store in the profile and in the device
* the given struct ratbag_button.
/*
* FIXME: This function is deprecated and should not be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_button)(struct ratbag_button *button,
const struct ratbag_button_action *action);
/**
* For the given profile, overwrite the current resolution
* of the sensor expressed in DPI, and commit it to the hardware.
*
* Mandatory if the driver exports RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION.
/*
* FIXME: This function is deprecated and should not be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_resolution_dpi)(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y);
/**
* For the given led, fill in the struct ratbag_led
* with the available information.
*/
void (*read_led)(struct ratbag_led *led);
/*
* FIXME: This function is deprecated and should be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_led)(struct ratbag_led *led, enum ratbag_led_mode mode,
struct ratbag_color color, unsigned int hz,
unsigned int brightness);
/* private */
int (*test_probe)(struct ratbag_device *device, void *data);
struct list link;
};
struct ratbag_resolution{
struct ratbag_resolution {
struct ratbag_profile *profile;
int refcount;
void *userdata;
@@ -202,6 +230,20 @@ struct ratbag_resolution{
uint32_t capabilities;
};
struct ratbag_led {
int refcount;
void *userdata;
struct list link;
struct ratbag_profile *profile;
unsigned index;
enum ratbag_led_type type;
enum ratbag_led_mode mode;
struct ratbag_color color;
unsigned int hz; /**< rate of action in hz */
unsigned int brightness; /**< brightness of the LED */
bool dirty;
};
struct ratbag_profile {
int refcount;
void *userdata;
@@ -216,8 +258,11 @@ struct ratbag_profile {
struct ratbag_resolution *modes;
unsigned int num_modes;
} resolution;
struct list leds;
bool is_active; /**< profile is the currently active one */
bool is_enabled;
bool dirty; /**< profile changed since last commit */
};
#define BUTTON_ACTION_NONE \
@@ -277,6 +322,7 @@ struct ratbag_button {
enum ratbag_button_type type;
struct ratbag_button_action action;
uint32_t action_caps;
bool dirty; /* changed since last commit to device */
};
static inline void
@@ -318,7 +364,8 @@ int
ratbag_device_init_profiles(struct ratbag_device *device,
unsigned int num_profiles,
unsigned int num_resolutions,
unsigned int num_buttons);
unsigned int num_buttons,
unsigned int num_leds);
void
ratbag_device_set_capability(struct ratbag_device *device,
@@ -417,7 +464,9 @@ log_buffer(struct ratbag *ratbag,
struct ratbag_driver etekcity_driver;
struct ratbag_driver hidpp20_driver;
struct ratbag_driver hidpp10_driver;
struct ratbag_driver logitech_g300_driver;
struct ratbag_driver roccat_driver;
struct ratbag_driver gskill_driver;
struct ratbag_device*
ratbag_device_new(struct ratbag *ratbag, struct udev_device *udev_device,
@@ -441,5 +490,3 @@ void
ratbag_button_copy_macro(struct ratbag_button *button,
const struct ratbag_button_macro *macro);
#endif /* LIBRATBAG_PRIVATE_H */

View File

@@ -22,14 +22,14 @@
*/
#ifndef LIBRATBAG_TEST_H
#define LIBRATBAG_TEST_H
#pragma once
#include "libratbag.h"
#define RATBAG_TEST_MAX_PROFILES 12
#define RATBAG_TEST_MAX_BUTTONS 25
#define RATBAG_TEST_MAX_RESOLUTIONS 8
#define RATBAG_TEST_MAX_LEDS 8
struct ratbag_test_button {
enum ratbag_button_action_type type;
@@ -48,9 +48,24 @@ struct ratbag_test_resolution {
uint32_t caps;
};
struct ratbag_test_color {
unsigned short red;
unsigned short green;
unsigned short blue;
};
struct ratbag_test_led {
enum ratbag_led_type type;
enum ratbag_led_mode mode;
struct ratbag_test_color color;
unsigned int hz;
unsigned int brightness;
};
struct ratbag_test_profile {
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];
bool active;
bool dflt;
};
@@ -59,6 +74,7 @@ struct ratbag_test_device {
unsigned int num_profiles;
unsigned int num_resolutions;
unsigned int num_buttons;
unsigned int num_leds;
struct ratbag_test_profile profiles[RATBAG_TEST_MAX_PROFILES];
void (*destroyed)(struct ratbag_device *device, void *data);
void *destroyed_data;
@@ -67,4 +83,3 @@ struct ratbag_test_device {
struct ratbag_device* ratbag_device_new_test_device(struct ratbag *ratbag,
struct ratbag_test_device *test_device);
#endif /* LIBRATBAG_TEST_H */

View File

@@ -35,6 +35,8 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <iconv.h>
#include <errno.h>
#include "libratbag-util.h"
#include "libratbag-private.h"
@@ -85,3 +87,95 @@ udev_prop_value(struct udev_device *device,
return prop_value;
}
ssize_t
ratbag_utf8_to_enc(char *buf, size_t buf_len, const char *to_enc,
const char *format, ...)
{
va_list args;
iconv_t converter;
char str[buf_len];
char *in_buf = str, *out_buf = (char*)buf;
size_t in_bytes_left, out_bytes_left = buf_len;
int ret;
memset(buf, 0, buf_len);
va_start(args, format);
ret = vsnprintf(str, buf_len, format, args);
va_end(args);
if (ret < 0)
return ret;
in_bytes_left = ret;
converter = iconv_open(to_enc, "UTF-8");
if (converter == (iconv_t)-1)
return -errno;
ret = iconv(converter, &in_buf, &in_bytes_left, &out_buf,
&out_bytes_left);
if (ret)
ret = -errno;
else
ret = buf_len - out_bytes_left;
iconv_close(converter);
return ret;
}
ssize_t
ratbag_utf8_from_enc(char *in_buf, size_t in_len, const char *from_enc,
char **out)
{
iconv_t converter;
size_t len = in_len * 6,
in_bytes_left = in_len,
out_bytes_left = len;
char *pos;
ssize_t ret;
converter = iconv_open("UTF-8", from_enc);
if (converter == (iconv_t)-1)
return -errno;
/*
* We *could* dynamically allocate the out buffer. However iconv's
* obnoxious semantics, mainly the fact that it modifies every pointer
* given to it, would make this code a lot larger and complex than it
* needs to be for a mouse with blinking lights.
*
* So, since there's no encoding that takes more then 6 bytes per
* character, allocate for that and just fail if that's not enough.
*/
*out = zalloc(len);
pos = *out;
if (!*out) {
ret = -errno;
goto err;
}
ret = iconv(converter, &in_buf, &in_bytes_left, &pos, &out_bytes_left);
if (ret) {
ret = -errno;
goto err;
}
/* Now get rid of any space in the buffer we don't need */
*out = realloc(*out, (len - out_bytes_left) + 1);
if (!*out)
ret = -errno;
else
ret = (len - out_bytes_left) + 1;
err:
if (ret < 0 && *out) {
free(*out);
*out = NULL;
}
iconv_close(converter);
return ret;
}

View File

@@ -21,8 +21,7 @@
* OF THIS SOFTWARE.
*/
#ifndef LIBRATBAG_UTIL_H
#define LIBRATBAG_UTIL_H
#pragma once
#include <stdlib.h>
#include <stdio.h>
@@ -200,4 +199,20 @@ long_set_bit_state(unsigned long *array, int bit, int state)
const char *
udev_prop_value(struct udev_device *device,
const char *property_name);
#endif /* LIBRATBAG_UTIL_H */
/**
* Converts a string from UTF-8 to the encoding specified. Returns the number
* of bytes written to buf on success, or negative errno value on failure.
*/
ssize_t
ratbag_utf8_to_enc(char *buf, size_t buf_len, const char *to_enc,
const char *format, ...) __attribute__((format(printf, 4, 5)));
/**
* Converts a string from the given encoding into UTF-8. The memory for the
* result is allocated and a pointer to the result is placed in *out. Returns
* the number of bytes in the UTF-8 version of the string on success, negative
* errno value on failure.
*/
ssize_t
ratbag_utf8_from_enc(char *in_buf, size_t in_len, const char *from_enc,
char **out);

View File

@@ -1,32 +0,0 @@
/*
* Copyright © 2013 Jonas Ådahl
*
* 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.
*/
#ifndef LIBRATBAG_VERSION_H
#define LIBRATBAG_VERSION_H
#define LIBRATBAG_VERSION_MAJOR @LIBRATBAG_VERSION_MAJOR@
#define LIBRATBAG_VERSION_MINOR @LIBRATBAG_VERSION_MINOR@
#define LIBRATBAG_VERSION_MICRO @LIBRATBAG_VERSION_MICRO@
#define LIBRATBAG_VERSION "@LIBRATBAG_VERSION@"
#endif

View File

@@ -34,6 +34,7 @@
#include <unistd.h>
#include <limits.h>
#include "usb-ids.h"
#include "libratbag-private.h"
#include "libratbag-util.h"
@@ -58,6 +59,8 @@ static void
ratbag_profile_destroy(struct ratbag_profile *profile);
static void
ratbag_button_destroy(struct ratbag_button *button);
static void
ratbag_led_destroy(struct ratbag_led *led);
static void
ratbag_default_log_func(struct ratbag *ratbag,
@@ -283,23 +286,15 @@ out:
return rc;
}
bool
ratbag_assign_driver(struct ratbag_device *device,
const struct input_id *dev_id,
struct ratbag_test_device *test_device)
static inline bool
ratbag_test_driver(struct ratbag_device *device,
const struct input_id *dev_id,
const char *driver_name,
struct ratbag_test_device *test_device)
{
struct ratbag *ratbag = device->ratbag;
struct ratbag_driver *driver;
int rc;
const char *driver_name;
if (!test_device) {
driver_name = udev_prop_value(device->udev_device, "RATBAG_DRIVER");
if (!driver_name)
return false;
} else {
driver_name = "test_driver";
}
list_for_each(driver, &ratbag->drivers, link) {
if (streq(driver->id, driver_name)) {
@@ -311,7 +306,7 @@ ratbag_assign_driver(struct ratbag_device *device,
if (!device->driver) {
log_error(ratbag, "%s: driver '%s' does not exist\n",
device->name, driver_name);
return false;
goto error;
}
if (test_device)
@@ -320,21 +315,64 @@ ratbag_assign_driver(struct ratbag_device *device,
rc = device->driver->probe(device);
if (rc == 0) {
if (!ratbag_sanity_check_device(device)) {
return false;
goto error;
} else {
log_debug(ratbag, "driver match found: %s\n", driver->name);
log_debug(ratbag,
"driver match found: %s\n",
device->driver->name);
return true;
}
}
if (rc != -ENODEV)
log_error(ratbag, "%s: error opening hidraw node (%s)\n",
device->name, strerror(-rc));
error:
device->driver = NULL;
if (rc == -ENODEV)
log_error(ratbag, "%s: no hidraw device found\n", device->name);
return false;
}
static inline bool
ratbag_driver_fallback_logitech(struct ratbag_device *device,
const struct input_id *dev_id)
{
int rc;
if (dev_id->vendor != USB_VENDOR_ID_LOGITECH)
return false;
rc = ratbag_test_driver(device, dev_id, "hidpp20", NULL);
if (!rc)
rc = ratbag_test_driver(device, dev_id, "hidpp10", NULL);
return rc;
}
bool
ratbag_assign_driver(struct ratbag_device *device,
const struct input_id *dev_id,
struct ratbag_test_device *test_device)
{
bool rc;
const char *driver_name;
if (!test_device) {
driver_name = udev_prop_value(device->udev_device, "RATBAG_DRIVER");
} else {
driver_name = "test_driver";
}
if (driver_name) {
rc = ratbag_test_driver(device, dev_id, driver_name, test_device);
} else {
rc = ratbag_driver_fallback_logitech(device, dev_id);
}
return rc;
}
static inline char*
get_device_name(struct udev_device *device)
{
@@ -382,6 +420,9 @@ ratbag_device_new_from_udev_device(struct ratbag *ratbag,
assert(udev_device != NULL);
assert(device_out != NULL);
if (udev_prop_value(udev_device, "ID_INPUT_MOUSE") == NULL)
goto out_err;
if (get_product_id(udev_device, &id) != 0)
goto out_err;
@@ -494,7 +535,9 @@ ratbag_create_context(const struct ratbag_interface *interface,
ratbag_register_driver(ratbag, &etekcity_driver);
ratbag_register_driver(ratbag, &hidpp20_driver);
ratbag_register_driver(ratbag, &hidpp10_driver);
ratbag_register_driver(ratbag, &logitech_g300_driver);
ratbag_register_driver(ratbag, &roccat_driver);
ratbag_register_driver(ratbag, &gskill_driver);
return ratbag;
}
@@ -541,6 +584,25 @@ ratbag_create_button(struct ratbag_profile *profile, unsigned int index)
return button;
}
static struct ratbag_led *
ratbag_create_led(struct ratbag_profile *profile, unsigned int index)
{
struct ratbag_device *device = profile->device;
struct ratbag_led *led;
led = zalloc(sizeof(*led));
led->refcount = 0;
led->profile = profile;
led->index = index;
list_insert(&profile->leds, &led->link);
if (device->driver->read_led)
device->driver->read_led(led);
return led;
}
static int
ratbag_profile_init_buttons(struct ratbag_profile *profile, unsigned int count)
{
@@ -554,11 +616,25 @@ ratbag_profile_init_buttons(struct ratbag_profile *profile, unsigned int count)
return 0;
}
static int
ratbag_profile_init_leds(struct ratbag_profile *profile, unsigned int count)
{
unsigned int i;
for (i = 0; i < count; i++)
ratbag_create_led(profile, i);
profile->device->num_leds = count;
return 0;
}
static struct ratbag_profile *
ratbag_create_profile(struct ratbag_device *device,
unsigned int index,
unsigned int num_resolutions,
unsigned int num_buttons)
unsigned int num_buttons,
unsigned int num_leds)
{
struct ratbag_profile *profile;
unsigned i;
@@ -570,9 +646,11 @@ ratbag_create_profile(struct ratbag_device *device,
profile->resolution.modes = zalloc(num_resolutions *
sizeof(*profile->resolution.modes));
profile->resolution.num_modes = num_resolutions;
profile->is_enabled = true;
list_insert(&device->profiles, &profile->link);
list_init(&profile->buttons);
list_init(&profile->leds);
for (i = 0; i < num_resolutions; i++)
ratbag_resolution_init(profile, i, 0, 0, 0);
@@ -581,6 +659,7 @@ ratbag_create_profile(struct ratbag_device *device,
device->driver->read_profile(profile, index);
ratbag_profile_init_buttons(profile, num_buttons);
ratbag_profile_init_leds(profile, num_leds);
return profile;
}
@@ -589,12 +668,13 @@ int
ratbag_device_init_profiles(struct ratbag_device *device,
unsigned int num_profiles,
unsigned int num_resolutions,
unsigned int num_buttons)
unsigned int num_buttons,
unsigned int num_leds)
{
unsigned int i;
for (i = 0; i < num_profiles; i++) {
ratbag_create_profile(device, i, num_resolutions, num_buttons);
ratbag_create_profile(device, i, num_resolutions, num_buttons, num_leds);
}
device->num_profiles = num_profiles;
@@ -610,6 +690,9 @@ ratbag_device_init_profiles(struct ratbag_device *device,
if (num_resolutions > 1)
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION);
if (num_leds > 1)
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_LED);
return 0;
}
@@ -626,14 +709,18 @@ ratbag_profile_ref(struct ratbag_profile *profile)
static void
ratbag_profile_destroy(struct ratbag_profile *profile)
{
struct ratbag_button *button, *next;
struct ratbag_button *button, *b_next;
struct ratbag_led *led, *l_next;
/* if we get to the point where the profile is destroyed, buttons,
* resolutions , etc. are at a refcount of 0, so we can destroy
* everything */
list_for_each_safe(button, next, &profile->buttons, link)
list_for_each_safe(button, b_next, &profile->buttons, link)
ratbag_button_destroy(button);
list_for_each_safe(led, l_next, &profile->leds, link)
ratbag_led_destroy(led);
free(profile->resolution.modes);
list_remove(&profile->link);
@@ -674,12 +761,31 @@ ratbag_device_get_profile(struct ratbag_device *device, unsigned int index)
return NULL;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_profile_set_enabled(struct ratbag_profile *profile, bool enabled)
{
if (!ratbag_device_has_capability(profile->device,
RATBAG_DEVICE_CAP_DISABLE_PROFILE))
return RATBAG_ERROR_CAPABILITY;
profile->is_enabled = enabled;
profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT int
ratbag_profile_is_active(struct ratbag_profile *profile)
{
return profile->is_active;
}
LIBRATBAG_EXPORT bool
ratbag_profile_is_enabled(const struct ratbag_profile *profile)
{
return profile->is_enabled;
}
LIBRATBAG_EXPORT unsigned int
ratbag_device_get_num_profiles(struct ratbag_device *device)
{
@@ -692,6 +798,12 @@ ratbag_device_get_num_buttons(struct ratbag_device *device)
return device->num_buttons;
}
LIBRATBAG_EXPORT unsigned int
ratbag_device_get_num_leds(struct ratbag_device *device)
{
return device->num_leds;
}
void
ratbag_device_set_capability(struct ratbag_device *device,
enum ratbag_device_capability cap)
@@ -709,6 +821,105 @@ ratbag_device_has_capability(const struct ratbag_device *device,
return !!(device->capabilities & (1UL << cap));
}
static inline enum ratbag_error_code
write_led_helper(struct ratbag_device *device, struct ratbag_led *led)
{
return device->driver->write_led(led, led->mode, led->color, led->hz, led->brightness);
}
/* FIXME: This is a temporary fix for all of the drivers that have yet to be
* converted to the new profile-oriented API. Once all of the drivers have been
* converted, this code should be removed.
*/
static enum ratbag_error_code
ratbag_old_write_profile(struct ratbag_device *device)
{
struct ratbag_profile *profile;
struct ratbag_button *button;
struct ratbag_led *led;
struct ratbag_resolution *resolution;
int rc;
unsigned int i;
assert(device->driver->write_profile);
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
continue;
rc = device->driver->write_profile(profile);
if (rc)
return RATBAG_ERROR_DEVICE;
if (device->driver->write_resolution_dpi) {
for (i = 0; i < profile->resolution.num_modes; i++) {
resolution = &profile->resolution.modes[i];
rc = device->driver->write_resolution_dpi(
resolution, resolution->dpi_x,
resolution->dpi_y);
if (rc)
return RATBAG_ERROR_DEVICE;
}
}
if (device->driver->write_button) {
list_for_each(button, &profile->buttons, link) {
struct ratbag_button_action action =
button->action;
if (!button->dirty)
continue;
rc = device->driver->write_button(button,
&action);
if (rc)
return RATBAG_ERROR_DEVICE;
}
}
if (device->driver->write_led) {
list_for_each(led, &profile->leds, link) {
if (!led->dirty)
continue;
rc = write_led_helper(device, led);
if (rc)
return RATBAG_ERROR_DEVICE;
}
}
}
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_device_commit(struct ratbag_device *device)
{
struct ratbag_profile *profile;
struct ratbag_button *button;
int rc;
if (!device->driver->commit) {
rc = ratbag_old_write_profile(device);
if (rc != RATBAG_SUCCESS)
return rc;
} else {
rc = device->driver->commit(device);
if (rc)
return RATBAG_ERROR_DEVICE;
}
list_for_each(profile, &device->profiles, link) {
profile->dirty = false;
list_for_each(button, &profile->buttons, link)
button->dirty = false;
}
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_profile_set_active(struct ratbag_profile *profile)
{
@@ -719,11 +930,6 @@ ratbag_profile_set_active(struct ratbag_profile *profile)
if (!ratbag_device_has_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_PROFILE))
return RATBAG_ERROR_CAPABILITY;
assert(device->driver->write_profile);
rc = device->driver->write_profile(profile);
if (rc)
return RATBAG_ERROR_DEVICE;
assert(device->driver->set_active_profile);
rc = device->driver->set_active_profile(device, profile->index);
if (rc)
@@ -803,15 +1009,14 @@ ratbag_resolution_set_dpi(struct ratbag_resolution *resolution,
unsigned int dpi)
{
struct ratbag_profile *profile = resolution->profile;
int rc;
resolution->dpi_x = dpi;
resolution->dpi_y = dpi;
if (resolution->dpi_x != dpi || resolution->dpi_y != dpi) {
resolution->dpi_x = dpi;
resolution->dpi_y = dpi;
profile->dirty = true;
}
assert(profile->device->driver->write_resolution_dpi);
rc = profile->device->driver->write_resolution_dpi(resolution, dpi, dpi);
return rc == 0 ? RATBAG_SUCCESS : RATBAG_ERROR_DEVICE;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
@@ -819,7 +1024,6 @@ ratbag_resolution_set_dpi_xy(struct ratbag_resolution *resolution,
unsigned int x, unsigned int y)
{
struct ratbag_profile *profile = resolution->profile;
int rc;
if (!ratbag_resolution_has_capability(resolution,
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION))
@@ -828,18 +1032,41 @@ ratbag_resolution_set_dpi_xy(struct ratbag_resolution *resolution,
if ((x == 0 && y != 0) || (x != 0 && y == 0))
return RATBAG_ERROR_VALUE;
assert(profile->device->driver->write_resolution_dpi);
rc = profile->device->driver->write_resolution_dpi(resolution, x, y);
return rc == 0 ? RATBAG_SUCCESS : RATBAG_ERROR_DEVICE;
if (resolution->dpi_x != x || resolution->dpi_y != y) {
resolution->dpi_x = x;
resolution->dpi_y = y;
profile->dirty = true;
}
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_resolution_set_report_rate(struct ratbag_resolution *resolution,
unsigned int hz)
{
resolution->hz = hz;
/* FIXME: call into the driver */
return RATBAG_ERROR_IMPLEMENTATION;
if (ratbag_resolution_has_capability(resolution,
RATBAG_RESOLUTION_CAP_INDIVIDUAL_REPORT_RATE)) {
if (resolution->hz != hz) {
resolution->hz = hz;
resolution->profile->dirty = true;
}
} else {
struct ratbag_profile *profile = resolution->profile;
unsigned int i;
/* No indvidual report rate per resolution. Loop through all of
* them and update. */
for (i = 0; i < profile->resolution.num_modes; i++) {
struct ratbag_resolution *res = &profile->resolution.modes[i];
if (res->hz != hz) {
res->hz = hz;
profile->dirty = true;
}
}
}
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT int
@@ -890,9 +1117,26 @@ ratbag_resolution_is_default(const struct ratbag_resolution *resolution)
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_resolution_set_default(struct ratbag_resolution *resolution)
{
resolution->is_default = true;
/* FIXME: call into the driver */
return RATBAG_ERROR_IMPLEMENTATION;
struct ratbag_profile *profile = resolution->profile;
unsigned int i;
/* Unset the default on the other resolutions */
for (i = 0; i < profile->resolution.num_modes; i++) {
struct ratbag_resolution *other = &profile->resolution.modes[i];
if (other == resolution || !other->is_default)
continue;
other->is_default = false;
profile->dirty = true;
}
if (!resolution->is_default) {
resolution->is_default = true;
profile->dirty = true;
}
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT struct ratbag_button*
@@ -960,20 +1204,19 @@ LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_button_set_button(struct ratbag_button *button, unsigned int btn)
{
struct ratbag_button_action action = {0};
int rc;
if (!button->profile->device->driver->write_button)
if (!ratbag_device_has_capability(button->profile->device,
RATBAG_DEVICE_CAP_BUTTON_KEY))
return RATBAG_ERROR_CAPABILITY;
action.type = RATBAG_BUTTON_ACTION_TYPE_BUTTON;
action.action.button = btn;
rc = button->profile->device->driver->write_button(button, &action);
button->action = action;
button->dirty = true;
button->profile->dirty = true;
if (rc == 0)
button->action = action;
return rc == 0 ? RATBAG_SUCCESS : RATBAG_ERROR_DEVICE;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_button_action_special
@@ -990,22 +1233,21 @@ ratbag_button_set_special(struct ratbag_button *button,
enum ratbag_button_action_special act)
{
struct ratbag_button_action action = {0};
int rc;
/* FIXME: range checks */
if (!button->profile->device->driver->write_button)
if (!ratbag_device_has_capability(button->profile->device,
RATBAG_DEVICE_CAP_BUTTON_KEY))
return RATBAG_ERROR_CAPABILITY;
action.type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL;
action.action.special = act;
rc = button->profile->device->driver->write_button(button, &action);
button->action = action;
button->dirty = true;
button->profile->dirty = true;
if (rc == 0)
button->action = action;
return rc == 0 ? RATBAG_SUCCESS : RATBAG_ERROR_DEVICE;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT unsigned int
@@ -1028,41 +1270,41 @@ ratbag_button_set_key(struct ratbag_button *button,
size_t sz)
{
struct ratbag_button_action action = {0};
int rc;
/* FIXME: range checks */
if (!button->profile->device->driver->write_button)
if (!ratbag_device_has_capability(button->profile->device,
RATBAG_DEVICE_CAP_BUTTON_KEY))
return RATBAG_ERROR_CAPABILITY;
/* FIXME: modifiers */
action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
action.action.key.key = key;
rc = button->profile->device->driver->write_button(button, &action);
if (rc == 0)
button->action = action;
button->action = action;
button->dirty = true;
button->profile->dirty = true;
return rc == 0 ? RATBAG_SUCCESS : RATBAG_ERROR_DEVICE;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_button_disable(struct ratbag_button *button)
{
struct ratbag_button_action action;
int rc;
struct ratbag_button_action action = {0};
if (!button->profile->device->driver->write_button)
if (!ratbag_device_has_capability(button->profile->device,
RATBAG_DEVICE_CAP_BUTTON_KEY))
return RATBAG_ERROR_CAPABILITY;
action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
rc = button->profile->device->driver->write_button(button, &action);
if (rc == 0)
button->action = action;
button->action = action;
button->dirty = true;
button->profile->dirty = true;
return rc == 0 ? RATBAG_SUCCESS : RATBAG_ERROR_DEVICE;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT struct ratbag_button *
@@ -1075,6 +1317,16 @@ ratbag_button_ref(struct ratbag_button *button)
return button;
}
LIBRATBAG_EXPORT struct ratbag_led *
ratbag_led_ref(struct ratbag_led *led)
{
assert(led->refcount < INT_MAX);
ratbag_profile_ref(led->profile);
led->refcount++;
return led;
}
static void
ratbag_button_destroy(struct ratbag_button *button)
{
@@ -1087,6 +1339,13 @@ ratbag_button_destroy(struct ratbag_button *button)
free(button);
}
static void
ratbag_led_destroy(struct ratbag_led *led)
{
list_remove(&led->link);
free(led);
}
LIBRATBAG_EXPORT struct ratbag_button *
ratbag_button_unref(struct ratbag_button *button)
{
@@ -1101,6 +1360,126 @@ ratbag_button_unref(struct ratbag_button *button)
return NULL;
}
LIBRATBAG_EXPORT struct ratbag_led *
ratbag_led_unref(struct ratbag_led *led)
{
if (led == NULL)
return NULL;
assert(led->refcount > 0);
led->refcount--;
ratbag_profile_unref(led->profile);
return NULL;
}
LIBRATBAG_EXPORT enum ratbag_led_mode
ratbag_led_get_mode(struct ratbag_led *led)
{
return led->mode;
}
LIBRATBAG_EXPORT enum ratbag_led_type
ratbag_led_get_type(struct ratbag_led *led)
{
return led->type;
}
LIBRATBAG_EXPORT struct ratbag_color
ratbag_led_get_color(struct ratbag_led *led)
{
return led->color;
}
LIBRATBAG_EXPORT int
ratbag_led_get_effect_rate(struct ratbag_led *led)
{
return led->hz;
}
LIBRATBAG_EXPORT unsigned int
ratbag_led_get_brightness(struct ratbag_led *led)
{
return led->brightness;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_led_set_mode(struct ratbag_led *led, enum ratbag_led_mode mode)
{
if (!ratbag_device_has_capability(led->profile->device,
RATBAG_DEVICE_CAP_LED))
return RATBAG_ERROR_CAPABILITY;
led->mode = mode;
led->dirty = true;
led->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_led_set_color(struct ratbag_led *led, struct ratbag_color color)
{
if (!ratbag_device_has_capability(led->profile->device,
RATBAG_DEVICE_CAP_LED))
return RATBAG_ERROR_CAPABILITY;
led->color = color;
led->dirty = true;
led->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_led_set_effect_rate(struct ratbag_led *led, unsigned int hz)
{
if (!ratbag_device_has_capability(led->profile->device,
RATBAG_DEVICE_CAP_LED))
return RATBAG_ERROR_CAPABILITY;
led->hz = hz;
led->dirty = true;
led->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_led_set_brightness(struct ratbag_led *led, unsigned int brightness)
{
if (!ratbag_device_has_capability(led->profile->device,
RATBAG_DEVICE_CAP_LED))
return RATBAG_ERROR_CAPABILITY;
led->brightness = brightness;
led->dirty = true;
led->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT struct ratbag_led *
ratbag_profile_get_led(struct ratbag_profile *profile,
unsigned int index)
{
struct ratbag_device *device = profile->device;
struct ratbag_led *led;
if (index >= ratbag_device_get_num_leds(device)) {
log_bug_client(device->ratbag, "Requested invalid led %d\n", index);
return NULL;
}
list_for_each(led, &profile->leds, link) {
if (led->index == index)
return ratbag_led_ref(led);
}
log_bug_libratbag(device->ratbag, "Led %d, profile %d not found\n",
index, profile->index);
return NULL;
}
LIBRATBAG_EXPORT void
ratbag_set_user_data(struct ratbag *ratbag, void *userdata)
{
@@ -1201,13 +1580,15 @@ LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_button_set_macro(struct ratbag_button *button,
const struct ratbag_button_macro *macro)
{
int rc;
if (!ratbag_device_has_capability(button->profile->device,
RATBAG_DEVICE_CAP_BUTTON_MACROS))
return RATBAG_ERROR_CAPABILITY;
ratbag_button_copy_macro(button, macro);
button->dirty = true;
button->profile->dirty = true;
rc = button->profile->device->driver->write_button(button, &button->action);
return rc == 0 ? RATBAG_SUCCESS : RATBAG_ERROR_DEVICE;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code

View File

@@ -21,8 +21,7 @@
* DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRATBAG_H
#define LIBRATBAG_H
#pragma once
#ifdef __cplusplus
extern "C" {
@@ -30,6 +29,7 @@ extern "C" {
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <libudev.h>
#define LIBRATBAG_ATTRIBUTE_PRINTF(_format, _args) \
@@ -54,6 +54,8 @@ extern "C" {
*
* @defgroup button Button configuration
*
* @defgroup led LED configuration
*
* @defgroup resolution Resolution and frequency mappings
*
* A device's sensor resolution and report rate can be configured per
@@ -125,6 +127,28 @@ struct ratbag_button;
*/
struct ratbag_resolution;
/**
* @ingroup led
* @struct ratbag_color
*
* Represents LED color in RGB format.
* each color component is integer 0 - 255
*/
struct ratbag_color {
unsigned int red;
unsigned int green;
unsigned int blue;
};
/**
* @ingroup led
* @struct ratbag_led
*
* Represents a led on the device.
*/
struct ratbag_led;
/**
* @ingroup button
* @struct ratbag_macro
@@ -151,7 +175,7 @@ enum ratbag_error_code {
RATBAG_ERROR_DEVICE = -1000,
/**
* Insufficient capabilities. This error occurs when a requestd change is
* Insufficient capabilities. This error occurs when a requested change is
* beyond the device's capabilities.
*/
RATBAG_ERROR_CAPABILITY = -1001,
@@ -490,6 +514,11 @@ enum ratbag_device_capability {
*/
RATBAG_DEVICE_CAP_BUTTON_KEY,
/**
* The device supports assigning LED colors and effects
*/
RATBAG_DEVICE_CAP_LED,
/**
* The device supports user-defined key or button sequences.
*/
@@ -519,6 +548,15 @@ enum ratbag_device_capability {
* libratbag can be used to query the device as normal.
*/
RATBAG_DEVICE_CAP_QUERY_CONFIGURATION,
/**
* The device has the capability to disable and enable profiles. While
* profiles are not immediately deleted after being disabled, it is not
* guaranteed that the device will remember any disabled profiles the
* next time ratbag runs. Furthermore, the order of profiles may get
* changed the next time ratbag runs if profiles are disabled.
*/
RATBAG_DEVICE_CAP_DISABLE_PROFILE,
};
/**
@@ -537,6 +575,18 @@ int
ratbag_device_has_capability(const struct ratbag_device *device,
enum ratbag_device_capability cap);
/**
* @ingroup device
*
* Write any changes to the device. Depending on the device, this may take
* a couple of seconds.
*
* @param device A previously initialized ratbag device
* @return 0 on success or an error code otherwise
*/
enum ratbag_error_code
ratbag_device_commit(struct ratbag_device *device);
/**
* @ingroup device
*
@@ -566,6 +616,17 @@ ratbag_device_get_num_profiles(struct ratbag_device *device);
unsigned int
ratbag_device_get_num_buttons(struct ratbag_device *device);
/**
* @ingroup device
*
* Return the number of LEDs available on this device.
*
* @param device A previously initialized ratbag device
* @return The number of LEDs available on this device.
*/
unsigned int
ratbag_device_get_num_leds(struct ratbag_device *device);
/**
* @ingroup profile
*
@@ -591,6 +652,34 @@ ratbag_profile_ref(struct ratbag_profile *profile);
struct ratbag_profile *
ratbag_profile_unref(struct ratbag_profile *profile);
/**
* @ingroup profile
*
* Enable/disable the ratbag profile. For this to work, the device must support
* @ref RATBAG_DEVICE_CAP_DISABLE_PROFILE.
*
* @param profile A previously initialized ratbag profile
* @param enabled Whether to enable or disable the profile
*
* @return 0 on success or an error code otherwise
*/
enum ratbag_error_code
ratbag_profile_set_enabled(struct ratbag_profile *profile, bool enabled);
/**
* @ingroup profile
*
* Check whether the ratbag profile is enabled or not. For devices that don't
* support @ref RATBAG_DEVICE_CAP_DISABLE_PROFILE the profile will always be
* set to enabled.
*
* @param profile A previously initialized ratbag profile
*
* @return Whether the profile is enabled or not.
*/
bool
ratbag_profile_is_enabled(const struct ratbag_profile *profile);
/**
* @ingroup profile
*
@@ -677,7 +766,7 @@ ratbag_profile_set_active(struct ratbag_profile *profile);
*
* @param profile A previously initialized ratbag profile
*
* @return The number of profiles available.
* @return The number of resolutions available.
*/
unsigned int
ratbag_profile_get_num_resolutions(struct ratbag_profile *profile);
@@ -885,13 +974,18 @@ ratbag_resolution_get_dpi_y(struct ratbag_resolution *resolution);
/**
* @ingroup resolution
*
* Set the the report rate in Hz for the resolution mode.
* Set the report rate in Hz for the resolution mode.
*
* A value of 0 hz disables the mode.
*
* If the resolution mode is the currently active mode and the profile is
* the currently active profile, the change takes effect immediately.
*
* If the resolution does not have the @ref
* RATBAG_RESOLUTION_CAP_INDIVIDUAL_REPORT_RATE capability, changing the
* report rate on one resolution changes the report rate for all resolutions
* in this profile.
*
* @param resolution A previously initialized ratbag resolution
* @param hz Set to the report rate in Hz, may be 0
*
@@ -904,15 +998,10 @@ ratbag_resolution_set_report_rate(struct ratbag_resolution *resolution,
/**
* @ingroup resolution
*
* Get the the report rate in Hz for the resolution mode.
* Get the report rate in Hz for the resolution mode.
*
* A value of 0 hz indicates the mode is disabled.
*
* If the resolution does not have the @ref
* RATBAG_RESOLUTION_CAP_INDIVIDUAL_REPORT_RATE capability, changing the
* report rate on one resolution changes the report rate for all resolutions
* in this profile.
*
* @param resolution A previously initialized ratbag resolution
*
* @return The report rate for this resolution in Hz
@@ -1177,6 +1266,7 @@ enum ratbag_button_action_special {
/* DPI switch */
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP,
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN,
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP,
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN,
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE,
@@ -1184,6 +1274,7 @@ enum ratbag_button_action_special {
/* Profile */
RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP,
RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_DOWN,
RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP,
RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN,
@@ -1249,6 +1340,172 @@ ratbag_button_set_button(struct ratbag_button *button,
enum ratbag_button_action_special
ratbag_button_get_special(struct ratbag_button *button);
/**
* @ingroup led
*
* RATBAG_LED_OFF - led is now off,
* RATBAG_LED_ON - led is on with static color,
* RATBAG_LED_CYCLE - led is cycling between all colors.
* RATBAG_LED_BREATHING - led is pulsating with static color
*
* Each LED mode has different properties, e.g. the brightness and rate are only
* available in modes @ref RATBAG_LED_CYCLE and @ref RATBAG_LED_BREATHING modes
*/
enum ratbag_led_mode {
RATBAG_LED_OFF = 0,
RATBAG_LED_ON,
RATBAG_LED_CYCLE,
RATBAG_LED_BREATHING,
};
/**
* @ingroup led
*
* LED types, usually based on their physical location
*/
enum ratbag_led_type {
RATBAG_LED_TYPE_UNKNOWN = -1,
RATBAG_LED_TYPE_LOGO = 0,
RATBAG_LED_TYPE_SIDE
};
/**
* @ingroup led
*
* Return a reference to the LED given by the index. The order of the
* LEDs is device-specific though.
*
* The LED is refcounted with an initial value of at least 1.
* Use ratbag_led_unref() to release the LED.
*
* @param profile A previously initialized ratbag profile
* @param index The index of the LED
*
* @return A LED context, or NULL if the LED does not exist.
*
* @see ratbag_device_get_profile
*/
struct ratbag_led *
ratbag_profile_get_led(struct ratbag_profile *profile, unsigned int index);
/**
* @ingroup led
*
* This function returns the type for ratbag_led.
*
* @param led A previously initialized ratbag LED
* @return The LED type @ref ratbag_led_type
*
* @see ratbag_led_set_mode
*/
enum ratbag_led_type
ratbag_led_get_type(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the mode for ratbag_led.
*
* @param led A previously initialized ratbag LED
* @return The LED mod @ref ratbag_led_mode
*
* @see ratbag_led_set_mode
*/
enum ratbag_led_mode
ratbag_led_get_mode(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the led color.
*
* @param led A previously initialized ratbag LED
* @return The LED color in @ref ratbag_led_mode
*
* @see ratbag_led_set_color
*/
struct ratbag_color
ratbag_led_get_color(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the LED effect rate.
*
* @param led A previously initialized ratbag LED
* @return The LED rate in Hz, can be 100 - 20000
*
* @see ratbag_led_set_effect_rate
*/
int
ratbag_led_get_effect_rate(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the LED brightness.
*
* @param led A previously initialized ratbag LED
* @return The LED brightness 0 - 255
*
* @see ratbag_led_get_brightness
*/
unsigned int
ratbag_led_get_brightness(struct ratbag_led *led);
/**
* @ingroup led
*
* this function sets the LED mode.
*
* @param led A previously initialized ratbag LED
* @param mode LED mode @ref ratbag_led_mode.
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_mode
*/
enum ratbag_error_code
ratbag_led_set_mode(struct ratbag_led *led, enum ratbag_led_mode mode);
/**
* @ingroup led
*
* If the LED's mode is @ref RATBAG_LED_ON or @ref RATBAG_LED_BREATHING
* then this function sets the LED color, otherwise it has no effect.
*
* @param led A previously initialized ratbag LED
* @param color A LED color.
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_color
*/
enum ratbag_error_code
ratbag_led_set_color(struct ratbag_led *led, struct ratbag_color color);
/**
* @ingroup led
*
* If the LED's mode is @ref RATBAG_LED_CYCLE or @ref RATBAG_LED_BREATHING
* then this function sets the LED rate in Hz
*
* @param led A previously initialized ratbag LED
* @param rate Effect rate in hz, 100 - 20000
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_effect_rate
*/
enum ratbag_error_code
ratbag_led_set_effect_rate(struct ratbag_led *led, unsigned int rate);
/**
* @ingroup led
*
* If the LED's mode is @ref RATBAG_LED_CYCLE or @ref RATBAG_LED_BREATHING
* then this function sets the LED brightness, otherwise it has no effect.
*
* @param led A previously initialized ratbag LED
* @param brightness Effect brightness 0 - 255
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_brightness
*/
enum ratbag_error_code
ratbag_led_set_brightness(struct ratbag_led *led, unsigned int brightness);
/**
* @ingroup button
*
@@ -1532,7 +1789,31 @@ ratbag_button_ref(struct ratbag_button *button);
struct ratbag_button *
ratbag_button_unref(struct ratbag_button *button);
/**
* @ingroup led
*
* Add a reference to the led. A led is destroyed whenever the
* reference count reaches 0. See @ref ratbag_led_unref.
*
* @param led A previously initialized valid ratbag led
* @return The passed ratbag led
*/
struct ratbag_led *
ratbag_led_ref(struct ratbag_led *led);
/**
* @ingroup led
*
* Dereference the ratbag led. When the internal refcount reaches
* zero, all resources associated with this object are released. The object
* must be considered invalid once unref is called.
*
* @param led A previously initialized ratbag led
* @return Always NULL
*/
struct ratbag_led *
ratbag_led_unref(struct ratbag_led *led);
#ifdef __cplusplus
}
#endif
#endif /* LIBRATBAG_H */

View File

@@ -1,9 +1,8 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
datarootdir=@datarootdir@
pkgdatadir=@datadir@/@PACKAGE@
libdir=@libdir@
includedir=@includedir@
datadir=${prefix}/@datadir@
pkgdatadir=${datadir}/libratbag
libdir=${prefix}/@libdir@
includedir=${prefix}/@includedir@
Name: Libratbag
Description: Mouse configuration library

View File

@@ -1,6 +1,6 @@
/* in alphabetical order! */
LIBRATBAG_0.3.0 {
LIBRATBAG_0.5.0 {
global:
ratbag_create_context;
ratbag_button_disable;
@@ -28,6 +28,7 @@ global:
ratbag_button_set_special;
ratbag_button_set_user_data;
ratbag_button_unref;
ratbag_device_commit;
ratbag_device_get_name;
ratbag_device_get_num_buttons;
ratbag_device_get_num_profiles;
@@ -44,14 +45,29 @@ global:
ratbag_log_get_priority;
ratbag_log_set_handler;
ratbag_log_set_priority;
ratbag_profile_get_led;
ratbag_led_get_mode;
ratbag_led_get_type;
ratbag_led_get_color;
ratbag_led_get_effect_rate;
ratbag_led_get_brightness;
ratbag_led_ref;
ratbag_led_set_mode;
ratbag_led_set_color;
ratbag_led_set_effect_rate;
ratbag_led_set_brightness;
ratbag_led_unref;
ratbag_device_get_num_leds;
ratbag_profile_get_button;
ratbag_profile_get_num_resolutions;
ratbag_profile_get_resolution;
ratbag_profile_get_user_data;
ratbag_profile_is_active;
ratbag_profile_is_enabled;
ratbag_profile_ref;
ratbag_profile_set_user_data;
ratbag_profile_set_active;
ratbag_profile_set_enabled;
ratbag_profile_unref;
ratbag_resolution_get_dpi;
ratbag_resolution_get_dpi_x;

26
src/usb-ids.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* Copyright © 2016 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#pragma once
#define USB_VENDOR_ID_LOGITECH 0x046d

View File

@@ -1,65 +0,0 @@
if BUILD_TESTS
AM_CPPFLAGS = -I$(top_srcdir)/include \
-I$(top_srcdir)/src \
$(CHECK_CFLAGS) \
$(LIBEVDEV_CFLAGS)
AM_CFLAGS = $(GCC_CFLAGS)
AM_CXXFLAGS = $(GCC_CXXFLAGS)
TEST_LIBS = $(CHECK_LIBS) $(LIBEVDEV_LIBS) $(top_builddir)/src/libratbag.la
run_tests = \
test-context \
test-device
build_tests = \
test-build-cxx \
test-build-linker \
test-build-pedantic-c99 \
test-build-std-gnuc90
noinst_PROGRAMS = $(build_tests) $(run_tests)
noinst_SCRIPTS = symbols-leak-test
TESTS = $(run_tests) symbols-leak-test
test_context_SOURCES = test-context.c
test_context_LDADD = $(TEST_LIBS)
test_context_LDFLAGS = -no-install
test_device_SOURCES = test-device.c
test_device_LDADD = $(TEST_LIBS)
test_device_LDFLAGS = -no-install
# build-test only
test_build_pedantic_c99_SOURCES = build-pedantic.c
test_build_pedantic_c99_CFLAGS = -std=c99 -pedantic -Werror
test_build_std_gnuc90_SOURCES = build-pedantic.c
test_build_std_gnuc90_CFLAGS = -std=gnu90 -Werror
# test for linking with the minimal linker flags
test_build_linker_SOURCES = build-pedantic.c
test_build_linker_CFLAGS = -I$(top_srcdir)/src
test_build_linker_LDADD = $(top_builddir)/src/libratbag.la
# test including from C++
test_build_cxx_SOURCES = build-cxx.cc
test_build_cxx_CXXFLAGS = -Wall -Wextra -Wno-unused-parameter $(AM_CXXFLAGS)
AM_TESTS_ENVIRONMENT=RATBAG_TEST=1; export RATBAG_TEST;
if HAVE_VALGRIND
VALGRIND_FLAGS=--leak-check=full \
--quiet \
--error-exitcode=3 \
--suppressions=$(srcdir)/valgrind.suppressions
valgrind:
$(MAKE) check-TESTS LOG_COMPILER="$(VALGRIND)" LOG_FLAGS="$(VALGRIND_FLAGS)" CK_FORK=no USING_VALGRIND=yes
check: valgrind
endif
endif
EXTRA_DIST=valgrind.suppressions

13
test/symbols-leak-test.in → test/symbols-leak-test Normal file → Executable file
View File

@@ -2,16 +2,21 @@
### simple check for exported symbols
TOP_SRCDIR="$1"
if [ -z "$TOP_SRCDIR" ]; then
echo "usage: $0 path/to/top_srcdir"
exit 1
fi
# make bash exit if any command will exit with non-0 return value
set -e
# make sure the paths are alright
cd `dirname $0`
diff -a -u \
<(cat @top_srcdir@/src/libratbag.sym | \
<(cat "$TOP_SRCDIR"/src/libratbag.sym | \
grep '^\s\+ratbag_.*' | \
sed -e 's/^\s\+\(.*\);/\1/' | sort) \
<(cat @top_srcdir@/src/*.c | \
<(cat "$TOP_SRCDIR"/src/*.c | \
grep LIBRATBAG_EXPORT -A 1 | grep '^ratbag_.*' | \
sed -e 's/(.*//' | sort)

View File

@@ -53,6 +53,7 @@ const struct ratbag_test_device sane_device = {
.num_profiles = 3,
.num_resolutions = 3,
.num_buttons = 1,
.num_leds = 2,
.profiles = {
{
.resolutions = {
@@ -78,6 +79,20 @@ const struct ratbag_test_device sane_device = {
{ .xres = 2200, .yres = 2300, .hz = 3000 },
{ .xres = 2300, .yres = 2400, .hz = 3000 },
},
.leds = {
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.hz = 1,
.brightness = 20
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.hz = 3,
.brightness = 40
}
},
.active = false,
.dflt = false,
},
@@ -110,7 +125,7 @@ START_TEST(device_init)
{
struct ratbag *r;
struct ratbag_device *d;
int nprofiles, nbuttons;
int nprofiles, nbuttons, nleds;
struct ratbag_test_device td = sane_device;
int device_freed_count = 0;
@@ -124,6 +139,8 @@ START_TEST(device_init)
ck_assert_int_eq(nprofiles, 3);
nbuttons = ratbag_device_get_num_buttons(d);
ck_assert_int_eq(nbuttons, 1);
nleds = ratbag_device_get_num_leds(d);
ck_assert_int_eq(nleds, 2);
ratbag_device_unref(d);
ratbag_unref(r);
@@ -751,6 +768,119 @@ START_TEST(device_buttons_set)
}
END_TEST
static void
assert_led_equals(struct ratbag_led *l, struct ratbag_test_led e_l)
{
enum ratbag_led_mode mode;
struct ratbag_color color;
int brightness, hz;
ck_assert(l != NULL);
mode = ratbag_led_get_mode(l);
color = ratbag_led_get_color(l);
hz = ratbag_led_get_effect_rate(l);
brightness = ratbag_led_get_brightness(l);
ck_assert_int_eq(mode, e_l.mode);
ck_assert_int_eq(color.red, e_l.color.red);
ck_assert_int_eq(color.green, e_l.color.green);
ck_assert_int_eq(color.blue, e_l.color.blue);
ck_assert_int_eq(hz, e_l.hz);
ck_assert_int_eq(brightness, e_l.brightness);
}
START_TEST(device_leds)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
struct ratbag_led *led_logo, *led_side;
int nprofiles;
int device_freed_count = 0;
struct ratbag_test_device td = sane_device;
td.destroyed_data = &device_freed_count;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
nprofiles = ratbag_device_get_num_profiles(d);
ck_assert_int_eq(nprofiles, 3);
p = ratbag_device_get_profile(d, 2);
ck_assert(p != NULL);
led_logo = ratbag_profile_get_led(p, RATBAG_LED_TYPE_LOGO);
assert_led_equals(led_logo, td.profiles[2].leds[RATBAG_LED_TYPE_LOGO]);
led_side = ratbag_profile_get_led(p, RATBAG_LED_TYPE_SIDE);
assert_led_equals(led_side, td.profiles[2].leds[RATBAG_LED_TYPE_SIDE]);
ratbag_led_unref(led_logo);
ratbag_led_unref(led_side);
ratbag_profile_unref(p);
ratbag_device_unref(d);
ratbag_unref(r);
ck_assert_int_eq(device_freed_count, 1);
}
END_TEST
START_TEST(device_leds_set)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
struct ratbag_led *l;
int nprofiles;
int device_freed_count = 0;
struct ratbag_test_device td = sane_device;
struct ratbag_color c = {
.red = 0,
.green = 111,
.blue = 222
};
td.destroyed_data = &device_freed_count;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
nprofiles = ratbag_device_get_num_profiles(d);
ck_assert_int_eq(nprofiles, 3);
p = ratbag_device_get_profile(d, 0);
ck_assert(p != NULL);
l = ratbag_profile_get_led(p, RATBAG_LED_TYPE_LOGO);
ratbag_led_set_mode(l, RATBAG_LED_BREATHING);
ratbag_led_set_color(l, c);
ratbag_led_set_effect_rate(l, 11);
ratbag_led_set_brightness(l, 22);
l = ratbag_profile_get_led(p, RATBAG_LED_TYPE_LOGO);
struct ratbag_test_led e_l = {
.mode = RATBAG_LED_BREATHING,
.color = {
.red = c.red,
.green = c.green,
.blue = c.blue
},
.hz = 11,
.brightness = 22
};
assert_led_equals(l, e_l);
ratbag_led_unref(l);
ratbag_led_unref(l);
ratbag_profile_unref(p);
ratbag_device_unref(d);
ratbag_unref(r);
ck_assert_int_eq(device_freed_count, 1);
}
END_TEST
static Suite *
test_context_suite(void)
{
@@ -789,6 +919,11 @@ test_context_suite(void)
tcase_add_test(tc, device_buttons_set);
suite_add_tcase(s, tc);
tc = tcase_create("led");
tcase_add_test(tc, device_leds);
tcase_add_test(tc, device_leds_set);
suite_add_tcase(s, tc);
return s;
}

178
test/test-iconv-helper.c Normal file
View File

@@ -0,0 +1,178 @@
/*
* Copyright © 2016 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <config.h>
#include <check.h>
#include <string.h>
#include <errno.h>
#include "libratbag-util.h"
static const char sample_utf16le[] = { 'F', '\0', 'o', '\0', 'o', '\0' };
static const char sample_single_char_utf16le[] = { 'A', '\0' };
/* Sample UTF-8 string used: 🐺🖖🗺🗹💯👏 */
static const char sample_emoji_utf8[] = {
0xf0, 0x9f, 0x90, 0xba, 0xf0, 0x9f, 0x96, 0x96, 0xf0, 0x9f, 0x97, 0xba,
0xf0, 0x9f, 0x97, 0xb9, 0xf0, 0x9f, 0x92, 0xaf, 0xf0, 0x9f, 0x91, 0x8f,
0x0a, 0x00
};
static const char sample_emoji_utf16le[] = {
0x3d, 0xd8, 0x3a, 0xdc, 0x3d, 0xd8, 0x96, 0xdd, 0x3d, 0xd8, 0xfa, 0xdd,
0x3d, 0xd8, 0xf9, 0xdd, 0x3d, 0xd8, 0xaf, 0xdc, 0x3d, 0xd8, 0x4f, 0xdc,
0x0a, 0x00
};
START_TEST(iconv_convert_to_utf16le)
{
char output[4096];
ssize_t rc;
rc = ratbag_utf8_to_enc(output, sizeof(sample_utf16le),
"UTF-16LE", "%s", "Foo");
ck_assert_int_eq(rc, sizeof(sample_utf16le));
ck_assert(memcmp(output, sample_utf16le, sizeof(sample_utf16le)) == 0);
rc = ratbag_utf8_to_enc(output, sizeof(sample_emoji_utf16le),
"UTF-16LE", "%s", sample_emoji_utf8);
ck_assert_int_eq(rc, sizeof(sample_emoji_utf16le));
ck_assert(memcmp(output, sample_emoji_utf16le,
sizeof(sample_emoji_utf16le)) == 0);
rc = ratbag_utf8_to_enc(output, sizeof(sample_single_char_utf16le),
"UTF-16LE", "%s", "A");
ck_assert_int_eq(rc, sizeof(sample_single_char_utf16le));
ck_assert(memcmp(output, sample_single_char_utf16le,
sizeof(sample_single_char_utf16le)) == 0);
}
END_TEST
START_TEST(iconv_convert_from_utf16le)
{
char input[4096];
char *output;
ssize_t rc;
memcpy(input, sample_utf16le, sizeof(sample_utf16le));
rc = ratbag_utf8_from_enc(input, sizeof(sample_utf16le),
"UTF-16LE", &output);
ck_assert_int_eq(rc, sizeof("Foo"));
ck_assert(memcmp(output, "Foo", sizeof("Foo")) == 0);
free(output);
memcpy(input, sample_emoji_utf16le, sizeof(sample_emoji_utf16le));
rc = ratbag_utf8_from_enc(input, sizeof(sample_emoji_utf16le),
"UTF-16LE", &output);
ck_assert_int_eq(rc, sizeof(sample_emoji_utf8));
ck_assert(memcmp(output, sample_emoji_utf8,
sizeof(sample_emoji_utf8)) == 0);
free(output);
memcpy(input, sample_single_char_utf16le,
sizeof(sample_single_char_utf16le));
rc = ratbag_utf8_from_enc(input, sizeof(sample_single_char_utf16le),
"UTF-16LE", &output);
ck_assert_int_eq(rc, sizeof("A"));
ck_assert(memcmp(output, "A", sizeof("A")) == 0);
free(output);
}
END_TEST
START_TEST(iconv_invalid_encoding)
{
char output[10] = { 0 };
ssize_t rc;
rc = ratbag_utf8_to_enc(output, sizeof(output),
"This encoding is invalid", "%s", "Foo");
ck_assert_int_eq(rc, -EINVAL);
}
END_TEST
START_TEST(iconv_bad_utf16le)
{
char odd_numbered[] = { 'F', '\0', 'o' };
char single_char[] = { 'F' };
char single_null[] = { '\0' };
char double_null[] = { 'F', '\0', '\0', 'o', '\0', 'o', '\0' };
char *output;
ssize_t rc;
rc = ratbag_utf8_from_enc(odd_numbered, sizeof(odd_numbered),
"UTF-16LE", &output);
ck_assert_int_eq(rc, -EINVAL);
rc = ratbag_utf8_from_enc(single_char, sizeof(single_char), "UTF-16LE",
&output);
ck_assert_int_eq(rc, -EINVAL);
rc = ratbag_utf8_from_enc(single_null, sizeof(single_null), "UTF-16LE",
&output);
ck_assert_int_eq(rc, -EINVAL);
rc = ratbag_utf8_from_enc(double_null, sizeof(double_null), "UTF-16LE",
&output);
ck_assert_int_eq(rc, -EINVAL);
}
END_TEST
static Suite *
test_context_suite(void)
{
TCase *tc;
Suite *s;
s = suite_create("iconv-helper");
tc = tcase_create("iconv-helper");
tcase_add_test(tc, iconv_convert_to_utf16le);
tcase_add_test(tc, iconv_convert_from_utf16le);
tcase_add_test(tc, iconv_invalid_encoding);
tcase_add_test(tc, iconv_bad_utf16le);
suite_add_tcase(s, tc);
return s;
}
int main(void)
{
int nfailed;
Suite *s;
SRunner *sr;
s = test_context_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_ENV);
nfailed = srunner_ntests_failed(sr);
srunner_free(sr);
return (nfailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@@ -1,32 +0,0 @@
noinst_PROGRAMS = hidpp10-dump-page hidpp20-dump-page
bin_PROGRAMS = ratbag-command lur-command
noinst_LTLIBRARIES = libshared.la
AM_CPPFLAGS = -I$(top_srcdir)/include \
-I$(top_srcdir)/src \
-I$(top_builddir)/src # for libratbag-version.h
libshared_la_SOURCES = \
shared.c \
shared.h
libshared_la_CFLAGS = $(LIBUDEV_LIBS) $(LIBEVDEV_CFLAGS)
libshared_la_LIBADD = $(LIBUDEV_CFLAGS) $(LIBEVDEV_LIBS)
ratbag_command_SOURCES = ratbag-command.c
ratbag_command_LDADD = ../src/libratbag.la libshared.la $(LIBUDEV_LIBS) $(LIBEVDEV_LIBS)
ratbag_command_CFLAGS = $(LIBUDEV_CFLAGS) $(LIBEVDEV_CFLAGS)
dist_man1_MANS = ratbag-command.man
hidpp10_dump_page_SOURCES = hidpp10-dump-page.c
hidpp10_dump_page_LDADD = ../src/libhidpp.la
hidpp10_dump_page_LDFLAGS = -no-install
hidpp10_dump_page_CFLAGS = -I$(top_srcdir)/src
hidpp20_dump_page_SOURCES = hidpp20-dump-page.c
hidpp20_dump_page_LDADD = ../src/libhidpp.la
hidpp20_dump_page_LDFLAGS = -no-install
hidpp20_dump_page_CFLAGS = -I$(top_srcdir)/src
lur_command_SOURCeS = lur-command.c
lur_command_LDADD = ../src/liblur.la
lur_command_CFLAGS = -I$(top_srcdir)/src

View File

@@ -71,7 +71,7 @@ dump_all_pages(struct hidpp10_device *dev)
return rc;
}
void
static void
usage(void)
{
printf("Usage: %s [page] [offset] /dev/hidraw0\n", program_invocation_short_name);
@@ -80,7 +80,7 @@ usage(void)
int
main(int argc, char **argv)
{
_cleanup_close_ int fd;
_cleanup_close_ int fd = 0;
const char *path;
size_t page = 0, offset = 0;
struct hidpp10_device *dev = NULL;

View File

@@ -83,7 +83,7 @@ dump_everything(struct hidpp20_device *dev)
return dump_all_pages(dev, 1);
}
void
static void
usage(void)
{
printf("Usage: %s [page] [offset] /dev/hidraw0\n", program_invocation_short_name);
@@ -92,7 +92,7 @@ usage(void)
int
main(int argc, char **argv)
{
_cleanup_close_ int fd;
_cleanup_close_ int fd = 0;
const char *path;
size_t page = 0, offset = 0;
struct hidpp20_device *dev = NULL;
@@ -111,6 +111,8 @@ main(int argc, char **argv)
hidpp_device_init(&base, fd);
dev = hidpp20_device_new(&base, 0xff);
if (!dev)
error(1, 0, "Failed to open %s as a HID++ 2.0 device", path);
if (argc == 2)
rc = dump_everything(dev);

View File

@@ -119,7 +119,7 @@ disconnect_device(struct lur_receiver *receiver, int index)
return;
}
if (index >= ndevices) {
if (index < 0 || index >= ndevices) {
fprintf(stderr, "Invalid index %d, only %d devices connected\n",
index, ndevices);
} else {
@@ -243,7 +243,7 @@ main(int argc, char **argv)
return EXIT_FAILURE;
}
index = atoi(argv[3]);
index = atoi(argv[2]);
disconnect_device(receiver, index);
} else {
usage();

49
tools/lur-command.man Normal file
View File

@@ -0,0 +1,49 @@
.TH lur\-command 1 "@version@" lur\-command
.SH NAME
lur\-command \- list, pair and unpair Logitech Unifying devices
.SH SYNOPSIS
.B lur\-command find
.br
.B lur\-command list
.RI < device >
.br
.B lur\-command open
.RI < device >
.br
.B lur\-command close
.RI < device >
.br
.B lur\-command disconnect
.RI < index "> <" device >
.SH DESCRIPTION
.SS Common options
.TP
.BR \-h
displays a short help message.
.SS Listing receivers
.TP
.B find
lists all identified receivers on the system (scanning
.BI /dev/hidraw n
devices).
.SS Listing connected devices
.TP
.BR list " <" \fIdevice\fP >
lists the devices connected to the given receiver device.
.SS Pairing devices
.TP
.BR open " <" \fIdevice\fP >
opens the given receiver for pairing, for 30s.
.SS Closing a receiver
.TP
.BR close " <" \fIdevice\fP >
closes the given receiver, if it is currently open for pairing.
.SS Disconnecting a device
.TP
.BR disconnect " <" \fIindex\fP "> <" \fIdevice\fP >
disconnects the device matching the given index from the given
receiver. The index is as determined by the
.B list
command.
.SH SEE ALSO
.BR ratbag\-command (1)

View File

@@ -21,7 +21,8 @@
* DEALINGS IN THE SOFTWARE.
*/
#define _GNU_SOURCE
#include "config.h"
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
@@ -34,9 +35,11 @@
#include <sys/stat.h>
#include <linux/input.h>
#include "libratbag-version.h"
#include "shared.h"
enum options {
OPT_VERSION,
OPT_VERBOSE,
OPT_HELP,
};
@@ -58,6 +61,7 @@ enum cmd_flags {
FLAG_NEED_PROFILE = 1 << 11,
FLAG_NEED_RESOLUTION = 1 << 12,
FLAG_NEED_BUTTON = 1 << 13,
FLAG_NEED_LED = 1 << 14,
};
struct ratbag_cmd_options {
@@ -66,6 +70,7 @@ struct ratbag_cmd_options {
struct ratbag_profile *profile;
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct ratbag_led *led;
};
struct ratbag_cmd {
@@ -88,60 +93,74 @@ usage(void)
"Query or change a device's settings:\n"
"\n"
"Options:\n"
" --verbose Print debugging output\n"
" --verbose=raw Print debugging output with protocol output.\n"
" --help Print this help.\n"
" --verbose Print debugging output\n"
" --verbose=raw Print debugging output with protocol output.\n"
" --help Print this help.\n"
"\n"
"General Commands:\n"
" list List supported devices (does not take a device argument)\n"
" list List supported devices (does not take a device argument)\n"
"\n"
"Device Commands:\n"
" info Print information about a device \n"
" info Print information about a device \n"
"\n"
"Profile Commands:\n"
" profile active get Print the currently active profile\n"
" profile active set N Set profile N as to the active profile\n"
" profile N {COMMAND} Use profile N for COMMAND\n"
" profile active get Print the currently active profile\n"
" profile active set N Set profile N as to the active profile\n"
" profile enable N Enable profile N\n"
" profile disable N Disable profile N\n"
" profile N {COMMAND} Use profile N for COMMAND\n"
"\n"
"Resolution Commands\n"
" Resolution commands work on the given profile, or on the\n"
" active profile if none is given.\n"
"\n"
" resolution active get Print the currently active resolution\n"
" resolution active set N Set resolution N as the active resolution\n"
" resolution N {COMMAND} Use resolution N for COMMAND\n"
" resolution active get Print the currently active resolution\n"
" resolution active set N Set resolution N as the active resolution\n"
" resolution N {COMMAND} Use resolution N for COMMAND\n"
"\n"
"DPI Commands:\n"
" DPI commands work on the given profile and resolution, or on the\n"
" active resolution of the active profile if none are given.\n"
"\n"
" dpi get Print the dpi value\n"
" dpi set N Set the dpi value to N\n"
" rate get Print the report rate in Hz\n"
" rate set N Set the report rate in N Hz\n"
" dpi get Print the dpi value\n"
" dpi set N Set the dpi value to N\n"
" rate get Print the report rate in Hz\n"
" rate set N Set the report rate in N Hz\n"
"\n"
"Button Commands:\n"
" Button commands work on the given profile, or on the\n"
" active profile if none is given.\n"
"\n"
" button count Print the number of buttons\n"
" button N action get Print the button action\n"
" button N action set button B Set the button action to button B\n"
" button N action set special S Set the button action to special action S\n"
" button N action set macro ... Set the button action to the given macro \n"
" button count Print the number of buttons\n"
" button N action get Print the button action\n"
" button N action set button B Set the button action to button B\n"
" button N action set special S Set the button action to special action S\n"
" button N action set macro ... Set the button action to the given macro \n"
"\n"
" Macro syntax:\n"
" A macro is a series of key events or waiting periods.\n"
" Keys must be specified in linux/input.h key names.\n"
" KEY_A Press and release 'a'\n"
" +KEY_A Press 'a'\n"
" -KEY_A Release 'a'\n"
" t300 Wait 300ms\n"
" A macro is a series of key events or waiting periods.\n"
" Keys must be specified in linux/input.h key names.\n"
" KEY_A Press and release 'a'\n"
" +KEY_A Press 'a'\n"
" -KEY_A Release 'a'\n"
" t300 Wait 300ms\n"
"\n"
"LED Commands:\n"
" LED commands work on the given profile, or on the\n"
" active profile if none is given.\n"
"\n"
" led N {COMMAND}\n"
" get print the LED properties\n"
" set {COMMAND} ... sets LED properties (one or more)\n"
" mode [on|off|cycle|breathing] sets LED mode\n"
" color COLOR sets LED's color in hex format\n"
" rate N sets LED's effect rate in Hz\n"
" brightness N sets LED's effect brightness\n"
"\n"
"Special Commands:\n"
"These commands are for testing purposes and may be removed without notice\n"
"\n"
" switch-etekcity Switch the Etekcity mouse active profile\n"
" switch-etekcity Switch the Etekcity mouse active profile\n"
"\n"
"Examples:\n"
" %s profile active get\n"
@@ -149,21 +168,33 @@ usage(void)
" %s profile 0 resolution 1 dpi get\n"
" %s resolution 4 rate get\n"
" %s dpi set 800\n"
" %s profile 0 led 0 set mode on\n"
" %s profile 0 led 0 set color ff00ff\n"
" %s profile 0 led 0 set rate 20\n"
"\n"
"Exit codes:\n"
" 0 Success\n"
" 1 Unsupported feature or index out of available range\n"
" 2 Commandline arguments are invalid\n"
" 3 Invalid device or a command failed on the device\n"
" 0 Success\n"
" 1 Unsupported feature or index out of available range\n"
" 2 Commandline arguments are invalid\n"
" 3 Invalid device or a command failed on the device\n"
"\n",
program_invocation_short_name,
program_invocation_short_name,
program_invocation_short_name,
program_invocation_short_name,
program_invocation_short_name,
program_invocation_short_name,
program_invocation_short_name,
program_invocation_short_name,
program_invocation_short_name);
}
static void
version(void)
{
printf("%s\n", LIBRATBAG_VERSION);
}
static inline int
ratbag_cmd_device_from_arg(struct ratbag *ratbag,
int *argc, char **argv,
@@ -194,9 +225,8 @@ static inline struct ratbag_profile *
ratbag_cmd_get_active_profile(struct ratbag_device *device)
{
struct ratbag_profile *profile = NULL;
int i;
for (i = 0; i < ratbag_device_get_num_profiles(device); i++) {
for (unsigned int i = 0; i < ratbag_device_get_num_profiles(device); i++) {
profile = ratbag_device_get_profile(device, i);
if (ratbag_profile_is_active(profile))
return profile;
@@ -215,9 +245,8 @@ static inline struct ratbag_resolution *
ratbag_cmd_get_active_resolution(struct ratbag_profile *profile)
{
struct ratbag_resolution *resolution = NULL;
int i;
for (i = 0; i < ratbag_profile_get_num_resolutions(profile); i++) {
for (unsigned int i = 0; i < ratbag_profile_get_num_resolutions(profile); i++) {
resolution = ratbag_profile_get_resolution(profile, i);
if (ratbag_resolution_is_active(resolution))
return resolution;
@@ -244,7 +273,7 @@ fill_options(struct ratbag *ratbag,
struct ratbag_button *button = options->button;
int rc;
if ((flags & (FLAG_NEED_DEVICE|FLAG_NEED_PROFILE|FLAG_NEED_RESOLUTION)) &&
if ((flags & (FLAG_NEED_DEVICE|FLAG_NEED_PROFILE|FLAG_NEED_RESOLUTION|FLAG_NEED_LED)) &&
device == NULL) {
rc = ratbag_cmd_device_from_arg(ratbag, argc, argv,
&device);
@@ -253,7 +282,7 @@ fill_options(struct ratbag *ratbag,
options->device = device;
}
if ((flags & (FLAG_NEED_PROFILE|FLAG_NEED_RESOLUTION)) &&
if ((flags & (FLAG_NEED_PROFILE|FLAG_NEED_RESOLUTION|FLAG_NEED_LED)) &&
profile == NULL) {
profile = ratbag_cmd_get_active_profile(device);
if (!profile)
@@ -307,6 +336,55 @@ run_subcommand(const char *command,
return ERR_USAGE;
}
static int
ratbag_printf_led(struct ratbag_led *led, const char *prefix_format, ...)
{
va_list arg;
enum ratbag_led_mode mode;
struct ratbag_color color;
int hz;
int brightness;
enum ratbag_led_type type;
type = ratbag_led_get_type(led);
mode = ratbag_led_get_mode(led);
color = ratbag_led_get_color(led);
hz = ratbag_led_get_effect_rate(led);
brightness = ratbag_led_get_brightness(led);
va_start(arg, prefix_format);
vprintf(prefix_format, arg);
va_end(arg);
switch (mode) {
case RATBAG_LED_OFF:
printf("type: %s, mode: %s\n",
led_type_to_str(type),
led_mode_to_str(mode));
break;
case RATBAG_LED_ON:
printf("type: %s, mode: %s, color: %02x%02x%02x\n",
led_type_to_str(type),
led_mode_to_str(mode),
color.red, color.green, color.blue);
break;
case RATBAG_LED_CYCLE:
printf("type: %s, mode: %s, rate: %d, brightness: %d\n",
led_type_to_str(type),
led_mode_to_str(mode),
hz, brightness);
break;
case RATBAG_LED_BREATHING:
printf("type: %s, mode: %s, color: %02x%02x%02x, rate: %d, brightness: %d\n",
led_type_to_str(type),
led_mode_to_str(mode),
color.red, color.green, color.blue,
hz, brightness);
break;
}
return SUCCESS;
}
static int
ratbag_cmd_info(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
@@ -316,9 +394,10 @@ ratbag_cmd_info(const struct ratbag_cmd *cmd,
struct ratbag_device *device;
struct ratbag_profile *profile;
struct ratbag_button *button;
struct ratbag_led *led;
char *action;
int num_profiles, num_buttons;
int i, j, b;
int num_profiles, num_buttons, num_leds;
int b, l;
device = options->device;
@@ -337,24 +416,31 @@ ratbag_cmd_info(const struct ratbag_cmd *cmd,
if (ratbag_device_has_capability(device,
RATBAG_DEVICE_CAP_BUTTON_MACROS))
printf(" btn-macros");
if (ratbag_device_has_capability(device,
RATBAG_DEVICE_CAP_LED))
printf(" led");
printf("\n");
num_buttons = ratbag_device_get_num_buttons(device);
printf("Number of buttons: %d\n", num_buttons);
num_leds = ratbag_device_get_num_leds(device);
printf("Number of leds: %d\n", num_leds);
num_profiles = ratbag_device_get_num_profiles(device);
printf("Profiles supported: %d\n", num_profiles);
for (i = 0; i < num_profiles; i++) {
for (int i = 0; i < num_profiles; i++) {
int dpi, rate;
profile = ratbag_device_get_profile(device, i);
if (!profile)
continue;
printf(" Profile %d%s\n", i,
printf(" Profile %d (%s)%s\n", i,
ratbag_profile_is_enabled(profile) ? "enabled" : "disabled",
ratbag_profile_is_active(profile) ? " (active)" : "");
printf(" Resolutions:\n");
for (j = 0; j < ratbag_profile_get_num_resolutions(profile); j++) {
for (unsigned int j = 0; j < ratbag_profile_get_num_resolutions(profile); j++) {
struct ratbag_resolution *res;
res = ratbag_profile_get_resolution(profile, j);
@@ -390,7 +476,13 @@ ratbag_cmd_info(const struct ratbag_cmd *cmd,
button = ratbag_button_unref(button);
}
profile = ratbag_profile_unref(profile);
for (l = 0; l < num_leds; l++) {
led = ratbag_profile_get_led(profile, l);
ratbag_printf_led(led, " LED: %d ", l);
led = ratbag_led_unref(led);
}
ratbag_profile_unref(profile);
}
return SUCCESS;
@@ -472,7 +564,7 @@ str_to_macro(const char *action_arg, struct macro *m)
char *str, *s;
enum ratbag_macro_event_type type;
int code;
int idx = 0;
unsigned int idx = 0;
int rc = ERR_USAGE;
/* FIXME: handle per-device maximum lengths of macros */
@@ -560,10 +652,9 @@ ratbag_cmd_change_button(const struct ratbag_cmd *cmd,
int button_index;
enum ratbag_button_action_type action_type;
int rc = ERR_DEVICE;
unsigned int btnkey;
unsigned int btnkey = 0;
enum ratbag_button_action_special special;
struct macro macro = {0};
int i;
if (argc != 3)
return ERR_USAGE;
@@ -632,7 +723,7 @@ ratbag_cmd_change_button(const struct ratbag_cmd *cmd,
break;
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
m = ratbag_button_macro_new(macro.name);
for (i = 0; i < ARRAY_LENGTH(macro.events); i++) {
for (size_t i = 0; i < ARRAY_LENGTH(macro.events); i++) {
if (macro.events[i].type == RATBAG_MACRO_EVENT_NONE)
break;
@@ -711,7 +802,7 @@ ratbag_cmd_list_supported_devices(const struct ratbag_cmd *cmd,
device = ratbag_cmd_open_device(ratbag, path);
if (device) {
printf("%s:\t%s\n", path, ratbag_device_get_name(device));
device = ratbag_device_unref(device);
ratbag_device_unref(device);
supported++;
}
free(input_list[i]);
@@ -1270,13 +1361,12 @@ ratbag_cmd_button_set_macro(const struct ratbag_cmd *cmd,
struct ratbag_button_macro *m;
struct macro macro = {0};
int rc;
int i;
char macro_str[PATH_MAX] = {0};
if (argc < 1)
return ERR_USAGE;
for (i = 0; i < argc; i++) {
for (int i = 0; i < argc; i++) {
strncat(macro_str, argv[i], sizeof(macro_str) - strlen(macro_str) - 1);
strcat(macro_str, " ");
}
@@ -1293,7 +1383,7 @@ ratbag_cmd_button_set_macro(const struct ratbag_cmd *cmd,
button = options->button;
m = ratbag_button_macro_new(macro.name);
for (i = 0; i < ARRAY_LENGTH(macro.events); i++) {
for (size_t i = 0; i < ARRAY_LENGTH(macro.events); i++) {
if (macro.events[i].type == RATBAG_MACRO_EVENT_NONE)
break;
@@ -1431,6 +1521,297 @@ static const struct ratbag_cmd cmd_button = {
},
};
static int
ratbag_cmd_led_set_brightness(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
struct ratbag_cmd_options *options,
int argc, char **argv)
{
struct ratbag_led *led;
int brightness;
int rc;
if (argc < 1)
return ERR_USAGE;
led = options->led;
brightness = atoi(argv[0]);
rc = ratbag_led_set_brightness(led, brightness);
if (rc != 0)
return ERR_DEVICE;
if (argc == 1)
return SUCCESS;
return run_subcommand(argv[1], cmd, ratbag, options,
argc - 1, argv + 1);
}
static const struct ratbag_cmd cmd_led_set_color;
static const struct ratbag_cmd cmd_led_set_effect_rate;
static const struct ratbag_cmd cmd_led_set_brightness = {
.name = "brightness",
.cmd = ratbag_cmd_led_set_brightness,
.flags = FLAG_NEED_DEVICE | FLAG_NEED_PROFILE | FLAG_NEED_LED,
.subcommands = {
&cmd_led_set_color,
&cmd_led_set_effect_rate,
NULL,
},
};
static int
ratbag_cmd_led_set_effect_rate(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
struct ratbag_cmd_options *options,
int argc, char **argv)
{
struct ratbag_led *led;
char *str, *endptr;
int hz;
int rc;
if (argc < 1)
return ERR_USAGE;
led = options->led;
str = argv[0];
hz = strtoul(str, &endptr, 10);
if (*endptr != '\0')
return ERR_USAGE;
rc = ratbag_led_set_effect_rate(led, hz);
if (rc != 0)
return ERR_DEVICE;
if (argc == 1)
return SUCCESS;
return run_subcommand(argv[1], cmd, ratbag, options,
argc - 1, argv + 1);
}
static const struct ratbag_cmd cmd_led_set_effect_rate = {
.name = "rate",
.cmd = ratbag_cmd_led_set_effect_rate,
.flags = FLAG_NEED_DEVICE | FLAG_NEED_PROFILE | FLAG_NEED_LED,
.subcommands = {
&cmd_led_set_color,
&cmd_led_set_brightness,
NULL,
},
};
static int
ratbag_cmd_led_set_color(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
struct ratbag_cmd_options *options,
int argc, char **argv)
{
struct ratbag_led *led;
struct ratbag_color color;
int rc;
char red[3], green[3], blue[3];
char *p;
if (argc < 1)
return ERR_USAGE;
led = options->led;
sscanf(argv[0], "%2s%2s%2s", red, green, blue);
color.red = strtoul(red, &p, 16);
color.green = strtoul(green, &p, 16);
color.blue = strtoul(blue, &p, 16);
rc = ratbag_led_set_color(led, color);
if (rc != 0)
return ERR_DEVICE;
if (argc == 1)
return SUCCESS;
return run_subcommand(argv[1], cmd, ratbag, options,
argc - 1, argv + 1);
}
static const struct ratbag_cmd cmd_led_set_color = {
.name = "color",
.cmd = ratbag_cmd_led_set_color,
.flags = FLAG_NEED_DEVICE | FLAG_NEED_PROFILE | FLAG_NEED_LED,
.subcommands = {
&cmd_led_set_effect_rate,
&cmd_led_set_brightness,
NULL,
},
};
static int
ratbag_cmd_led_set_mode(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
struct ratbag_cmd_options *options,
int argc, char **argv)
{
struct ratbag_led *led;
struct ratbag_color color;
enum ratbag_led_mode mode;
char *str;
int rc;
if (argc < 1)
return ERR_USAGE;
led = options->led;
str = argv[0];
if (streq(str, "off"))
mode = RATBAG_LED_OFF;
else if (streq(str, "on"))
mode = RATBAG_LED_ON;
else if (streq(str, "cycle"))
mode = RATBAG_LED_CYCLE;
else if (streq(str, "breathing"))
mode = RATBAG_LED_BREATHING;
else {
usage();
return ERR_USAGE;
}
/* set default rate and brightness */
switch (mode) {
case RATBAG_LED_ON:
color.red = 255;
color.green = 255;
color.blue = 255;
ratbag_led_set_color(led, color);
break;
case RATBAG_LED_CYCLE:
case RATBAG_LED_BREATHING:
ratbag_led_set_effect_rate(led, 20);
ratbag_led_set_brightness(led, 100);
break;
case RATBAG_LED_OFF:
break;
}
rc = ratbag_led_set_mode(led, mode);
if (rc != 0)
return ERR_DEVICE;
if (argc == 1)
return SUCCESS;
return run_subcommand(argv[1], cmd, ratbag, options,
argc - 1, argv + 1);
}
static const struct ratbag_cmd cmd_led_set_mode = {
.name = "mode",
.cmd = ratbag_cmd_led_set_mode,
.flags = FLAG_NEED_DEVICE | FLAG_NEED_PROFILE | FLAG_NEED_LED,
.subcommands = {
&cmd_led_set_color,
&cmd_led_set_effect_rate,
&cmd_led_set_brightness,
NULL,
},
};
static int
ratbag_cmd_led_set(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
struct ratbag_cmd_options *options,
int argc, char **argv)
{
if (argc < 1)
return ERR_USAGE;
return run_subcommand(argv[0], cmd, ratbag, options,
argc, argv);
}
static const struct ratbag_cmd cmd_led_set = {
.name = "set",
.cmd = ratbag_cmd_led_set,
.flags = FLAG_NEED_DEVICE | FLAG_NEED_PROFILE | FLAG_NEED_LED,
.subcommands = {
&cmd_led_set_mode,
&cmd_led_set_color,
&cmd_led_set_effect_rate,
&cmd_led_set_brightness,
NULL,
},
};
static int
ratbag_cmd_led_get(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
struct ratbag_cmd_options *options,
int argc, char **argv)
{
struct ratbag_led *led = options->led;
ratbag_printf_led(led, "");
return SUCCESS;
}
static const struct ratbag_cmd cmd_led_get = {
.name = "get",
.cmd = ratbag_cmd_led_get,
.flags = FLAG_NEED_DEVICE | FLAG_NEED_PROFILE | FLAG_NEED_LED,
.subcommands = {
NULL,
},
};
static int
ratbag_cmd_led(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
struct ratbag_cmd_options *options,
int argc, char **argv)
{
struct ratbag_profile *profile;
struct ratbag_led *led;
int rc, index;
if (argc < 1)
return ERR_USAGE;
profile = options->profile;
index = atoi(argv[0]);
led = ratbag_profile_get_led(profile, index);
if (!led) {
error("Invalid led %d\n", index);
return ERR_UNSUPPORTED;
}
options->led = led;
argc--;
argv++;
rc = run_subcommand(argv[0], cmd, ratbag, options,
argc, argv);
return rc;
}
static const struct ratbag_cmd cmd_led = {
.name = "led",
.cmd = ratbag_cmd_led,
.flags = FLAG_NEED_DEVICE | FLAG_NEED_PROFILE,
.subcommands = {
&cmd_led_get,
&cmd_led_set,
NULL,
},
};
static int
ratbag_cmd_profile_active_set(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
@@ -1562,6 +1943,60 @@ static const struct ratbag_cmd cmd_profile_active = {
},
};
static int
ratbag_cmd_profile_enable(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
struct ratbag_cmd_options *options,
int argc, char **argv)
{
int rc;
if (!ratbag_device_has_capability(options->device,
RATBAG_DEVICE_CAP_DISABLE_PROFILE))
return ERR_UNSUPPORTED;
ratbag_profile_set_enabled(options->profile, true);
rc = ratbag_device_commit(options->device);
return rc ? ERR_DEVICE : SUCCESS;
}
static const struct ratbag_cmd cmd_profile_enable = {
.name = "enable",
.cmd = ratbag_cmd_profile_enable,
.flags = FLAG_NEED_DEVICE | FLAG_NEED_PROFILE,
.subcommands = {
NULL,
},
};
static int
ratbag_cmd_profile_disable(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
struct ratbag_cmd_options *options,
int argc, char **argv)
{
int rc;
if (!ratbag_device_has_capability(options->device,
RATBAG_DEVICE_CAP_DISABLE_PROFILE))
return ERR_UNSUPPORTED;
ratbag_profile_set_enabled(options->profile, false);
rc = ratbag_device_commit(options->device);
return rc ? ERR_DEVICE : SUCCESS;
}
static const struct ratbag_cmd cmd_profile_disable = {
.name = "disable",
.cmd = ratbag_cmd_profile_disable,
.flags = FLAG_NEED_DEVICE | FLAG_NEED_PROFILE,
.subcommands = {
NULL,
},
};
static int
ratbag_cmd_profile(const struct ratbag_cmd *cmd,
struct ratbag *ratbag,
@@ -1613,8 +2048,11 @@ static const struct ratbag_cmd cmd_profile = {
.flags = FLAG_NEED_DEVICE,
.subcommands = {
&cmd_profile_active,
&cmd_profile_enable,
&cmd_profile_disable,
&cmd_resolution,
&cmd_button,
&cmd_led,
NULL,
},
};
@@ -1632,6 +2070,7 @@ static const struct ratbag_cmd top_level_commands = {
&cmd_profile,
&cmd_resolution_dpi,
&cmd_resolution_rate,
&cmd_led,
NULL,
},
};
@@ -1659,8 +2098,10 @@ main(int argc, char **argv)
int c;
int option_index = 0;
static struct option opts[] = {
{ "verbose", 2, 0, OPT_VERBOSE },
{ "help", 0, 0, OPT_HELP },
{ "verbose", optional_argument, 0, OPT_VERBOSE },
{ "version", no_argument, 0, OPT_VERSION },
{ "help", no_argument, 0, OPT_HELP },
{ 0, 0, 0, 0 },
};
c = getopt_long(argc, argv, "+h", opts, &option_index);
@@ -1671,6 +2112,9 @@ main(int argc, char **argv)
case OPT_HELP:
usage();
goto out;
case OPT_VERSION:
version();
goto out;
case OPT_VERBOSE:
if (optarg && streq(optarg, "raw"))
options.flags |= FLAG_VERBOSE_RAW;
@@ -1701,9 +2145,14 @@ main(int argc, char **argv)
ratbag,
&options,
argc, argv);
if (options.device)
rc = ratbag_device_commit(options.device);
out:
ratbag_resolution_unref(options.resolution);
ratbag_button_unref(options.button);
ratbag_led_unref(options.led);
ratbag_profile_unref(options.profile);
ratbag_device_unref(options.device);
ratbag_unref(ratbag);

View File

@@ -1,26 +1,29 @@
.TH RATBAG_COMMAND "1"
.TH RATBAG_COMMAND "1" "@version@"
.SH NAME
ratbag-command \- Query or change a device's settings
ratbag\-command \- query or change a device's settings
.SH SYNOPSIS
.B ratbag-command [OPTIONS] {COMMAND} ... /path/to/device
.B ratbag\-command [OPTIONS] {COMMAND} ... /path/to/device
.SH DESCRIPTION
.PP
The
.I ratbag-commandq
tool query or change a device's settings.
.I ratbag\-command
tool queries or changes a device's settings.
.PP
This tool usually needs to be run as root to have access to the
/dev/input/eventX nodes.
.SH OPTIONS
.TP 8
.B --verbose
Print debugging output
.B \-\-verbose
Print debugging output.
.TP 8
.B --verbose=raw
.B \-\-verbose=raw
Print debugging output with protocol output. No guarantees are given regarding
the output format when this flag is on.
.TP 8
.B --help
.B \-\-version
Print the tool's version.
.TP 8
.B \-\-help
Print the help.
.SH General Commands
.TP 8
@@ -98,7 +101,7 @@ Press and release 'a'
.B +KEY_A
Press 'a'
.TP 8
.B -KEY_A
.B \-KEY_A
Release 'a'
.TP 8
.B t300
@@ -107,19 +110,19 @@ Wait 300ms
.SH Special Commands
These commands are for testing purposes and may be removed without notice
.TP 8
.B switch-etekcity
.B switch\-etekcity
Switch the Etekcity mouse active profile
.SH Examples
.TP 8
ratbag-command profile active get
ratbag\-command profile active get
.TP 8
ratbag-command profile 0 resolution active set 4
ratbag\-command profile 0 resolution active set 4
.TP 8
ratbag-command profile 0 resolution 1 dpi get
ratbag\-command profile 0 resolution 1 dpi get
.TP 8
ratbag-command resolution 4 rate get
ratbag\-command resolution 4 rate get
.TP 8
ratbag-command dpi set 800
ratbag\-command dpi set 800
.SH Exit codes
.TP 8
.B 0
@@ -136,6 +139,6 @@ Invalid device or a command failed on the device
.SH NOTES
.PP
There is currently no guarantees that the output format of
.B ratbag-command
.B ratbag\-command
will not change in the future. There should be some stability with the commands
mentioned in this man page, but do not expect it to stay the same.

View File

@@ -48,6 +48,48 @@ udev_device_from_path(struct udev *udev, const char *path)
return udev_device;
}
const char*
led_type_to_str(enum ratbag_led_type type)
{
const char *str = "UNKNOWN";
switch(type) {
case RATBAG_LED_TYPE_UNKNOWN:
str = "unknown";
break;
case RATBAG_LED_TYPE_LOGO:
str = "logo";
break;
case RATBAG_LED_TYPE_SIDE:
str = "side";
break;
}
return str;
}
const char *
led_mode_to_str(enum ratbag_led_mode mode)
{
const char *str = "UNKNOWN";
switch (mode) {
case RATBAG_LED_OFF:
str = "off";
break;
case RATBAG_LED_ON:
str = "on";
break;
case RATBAG_LED_CYCLE:
str = "cycle";
break;
case RATBAG_LED_BREATHING:
str = "breathing";
break;
}
return str;
}
const char*
button_type_to_str(enum ratbag_button_type type)
{
@@ -87,7 +129,7 @@ button_type_to_str(enum ratbag_button_type type)
return str;
}
const static struct map {
static const struct map {
enum ratbag_button_action_special special;
const char *str;
} special_map[] = {
@@ -99,9 +141,11 @@ const static struct map {
{ RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT, "wheel right" },
{ RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP, "wheel up" },
{ RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN, "wheel down" },
{ RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH, "ratchet mode switch" },
/* DPI switch */
{ RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP, "resolution cycle up" },
{ RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN, "resolution cycle down" },
{ RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP, "resolution up" },
{ RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN, "resolution down" },
{ RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE, "resolution alternate" },
@@ -109,6 +153,7 @@ const static struct map {
/* Profile */
{ RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP, "profile cycle up" },
{ RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_DOWN, "profile cycle down" },
{ RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP, "profile up" },
{ RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN, "profile down" },

View File

@@ -21,6 +21,8 @@
* DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include "config.h"
#include <errno.h>
@@ -59,6 +61,12 @@ udev_device_from_path(struct udev *udev, const char *path);
const char*
button_type_to_str(enum ratbag_button_type type);
const char*
led_type_to_str(enum ratbag_led_type type);
const char *
led_mode_to_str(enum ratbag_led_mode mode);
const char *
button_action_special_to_str(struct ratbag_button *button);

View File

@@ -58,7 +58,12 @@ cmds+=("profile 0 button 0 action set key KEY_ENTER")
cmds+=("button 0 action set special doubleclick")
cmds+=("profile 0 button 0 action set special doubleclick")
cmds+=("profile 0 button 0 action set macro +KEY_ENTER t05 -KEY_ENTER")
cmds+=("profile 0 led 0 get")
cmds+=("profile 0 led side get")
cmds+=("profile 0 led 0 set mode on")
cmds+=("profile 0 led 0 set color ffffff")
cmds+=("profile 0 led 0 set rate 1")
cmds+=("profile 0 led 0 set brightness 1")
for i in "${cmds[@]}"; do
echo "Testing arguments '$i $device'"
eval $command "$i" $device