Compare commits

...

69 Commits
v0.5 ... v0.7

Author SHA1 Message Date
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
50 changed files with 8486 additions and 529 deletions

2
.gitignore vendored
View File

@@ -36,12 +36,12 @@ 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-iconv-helper
test-suite.log
test-context
test-device

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
=========
@@ -67,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
----
@@ -100,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,164 +0,0 @@
AC_PREREQ([2.64])
m4_define([libratbag_major_version], [0])
m4_define([libratbag_minor_version], [5])
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:1:0
LIBLUR_LT_VERSION=3:1: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="$prefix/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
@@ -165,6 +170,7 @@ 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:*:
@@ -174,9 +180,14 @@ mouse:usb:v046dp4011:name:*:
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
@@ -187,3 +198,12 @@ mouse:usb:v046dpc539: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)

390
meson.build Normal file
View File

@@ -0,0 +1,390 @@
project('libratbag', 'c', 'cpp',
version : '0.7.0',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99' ])
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=1
libratbag_so_a=0
liblur_so_c=3
liblur_so_r=1
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 = ['-Wall', '-Wextra', '-Wno-unused-parameter', '-g', '-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('LIBRATBAG_VERSION', meson.project_version())
libratbag_version_h = configure_file(
ratbag : 'src/libratbag-version.h.in',
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
)
pkgconfig.generate(
filebase: 'libratbag',
name: 'Libratbag',
description: 'Mouse configuration library',
version: meson.project_version(),
libraries: lib_libratbag
)
#### 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,
)
#### 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)')

574
release.sh Executable file
View File

@@ -0,0 +1,574 @@
#!/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
# Change directory to be in the git build directory (could be out-of-source)
# More than one can be found when distcheck has run and failed
configNum=`find . -name config.status -type f | wc -l | sed 's:^ *::'`
if [ $? -ne 0 ]; then
echo "Error: failed to locate config.status."
echo "Has the module been configured?"
return 1
fi
if [ x"$configNum" = x0 ]; then
echo "Error: failed to locate config.status, has the module been configured?"
return 1
fi
if [ x"$configNum" != x1 ]; then
echo "Error: more than one config.status file was found,"
echo " clean-up previously failed attempts at distcheck"
return 1
fi
status_file=`find . -name config.status -type f`
if [ $? -ne 0 ]; then
echo "Error: failed to locate config.status."
echo "Has the module been configured?"
return 1
fi
build_dir=`dirname $status_file`
cd $build_dir
if [ $? -ne 0 ]; then
echo "Error: failed to cd to $MODULE_RPATH/$build_dir."
cd $top_src
return 1
fi
# ----- Now in the git module *build* directory ----- #
# 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,76 +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 \
usb-ids.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 \
usb-ids.h
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
@@ -657,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);

1510
src/driver-gskill.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -465,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);
@@ -584,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
@@ -225,6 +228,7 @@ hidpp20drv_read_button_8100(struct ratbag_button *button)
break;
default:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
break;
}
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
@@ -240,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_ONBOARD_PROFILES_8100))
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->rate;
led->brightness = h_led->brightness * 255.0 / 100;
}
static int
hidpp20drv_write_button_1b04(struct ratbag_button *button,
const struct ratbag_button_action *action)
@@ -343,6 +385,52 @@ hidpp20drv_write_button(struct ratbag_button *button,
return -ENOTSUP;
}
static int
hidpp20drv_write_led(struct ratbag_led *led,
enum ratbag_led_mode mode,
struct ratbag_color color, unsigned int hz,
unsigned int brightness)
{
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 (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 = color.red;
h_led->color.green = color.green;
h_led->color.blue = color.blue;
h_led->rate = hz;
h_led->brightness = brightness / 255.0 * 100;
return hidpp20_onboard_profiles_write(drv_data->dev, profile->index,
drv_data->profiles);
}
static int
hidpp20drv_current_profile(struct ratbag_device *device)
{
@@ -569,6 +657,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)
{
@@ -718,6 +829,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: {
@@ -731,6 +851,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;
}
@@ -859,6 +980,7 @@ 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;
rc = hidpp20drv_20_probe(device);
if (rc)
@@ -867,7 +989,8 @@ hidpp20drv_probe(struct ratbag_device *device)
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:
@@ -884,6 +1007,8 @@ struct ratbag_driver hidpp20_driver = {
.write_profile = hidpp20drv_write_profile,
.set_active_profile = hidpp20drv_set_current_profile,
.read_button = hidpp20drv_read_button,
.read_led = hidpp20drv_read_led,
.write_button = hidpp20drv_write_button,
.write_resolution_dpi = hidpp20drv_write_resolution_dpi,
.write_led = hidpp20drv_write_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
@@ -795,7 +796,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);

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(256, "Logo LED", RATBAG_LED_TYPE_LOGO),
LED_LOC_MAPPING(512, "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

@@ -106,6 +106,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);

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

View File

@@ -494,6 +494,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;
};

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 = 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 */
/* -------------------------------------------------------------------------- */
@@ -1289,6 +1401,7 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
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) == 2;
switch(info->various_info & 0x07) {
@@ -1653,11 +1766,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];
@@ -1667,8 +1776,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;
@@ -1781,6 +1889,30 @@ hidpp20_buttons_from_cpu(struct hidpp20_profile *profile,
}
}
static void
hidpp20_onboard_profile_read_led(struct hidpp20_led *led,
struct hidpp20_internal_led internal_led)
{
uint16_t rate = 0;
uint8_t brightness = 0;
switch (internal_led.mode) {
case HIDPP20_LED_CYCLE:
rate = internal_led.effect.cycle.rate;
brightness = internal_led.effect.cycle.brightness;
break;
case HIDPP20_LED_BREATHING:
rate = internal_led.effect.breath.rate;
brightness = internal_led.effect.breath.brightness;
break;
}
led->mode = (enum hidpp20_led_mode)internal_led.mode;
led->color = internal_led.color;
led->rate = rate;
led->brightness = brightness;
}
int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
unsigned int index,
struct hidpp20_profiles *profiles_list)
@@ -1809,6 +1941,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_profile_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));
@@ -1826,6 +1961,33 @@ int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
return 0;
}
static void
hidpp20_onboard_write_profile_led(struct hidpp20_internal_led *internal_led,
struct hidpp20_led *led)
{
uint16_t rate = led->rate;
uint8_t brightness = led->brightness;
internal_led->mode = (uint8_t)led->mode;
internal_led->color.red = led->color.red;
internal_led->color.blue = led->color.blue;
internal_led->color.green = led->color.green;
switch (led->mode) {
case HIDPP20_LED_CYCLE:
internal_led->effect.cycle.rate = rate;
internal_led->effect.cycle.brightness = brightness;
break;
case HIDPP20_LED_BREATHING:
internal_led->effect.breath.rate = rate;
internal_led->effect.breath.brightness = brightness;
break;
case HIDPP20_LED_ON:
case HIDPP20_LED_OFF:
break;
}
}
int hidpp20_onboard_profiles_write(struct hidpp20_device *device,
unsigned int index,
struct hidpp20_profiles *profiles_list)
@@ -1858,6 +2020,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_write_profile_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));

View File

@@ -117,6 +117,13 @@ enum hidpp20_battery_status {
BATTERY_STATUS_INVALID,
};
enum hidpp20_led_mode {
HIDPP20_LED_OFF = 0x00,
HIDPP20_LED_ON = 0x01,
HIDPP20_LED_CYCLE = 0x03,
HIDPP20_LED_BREATHING = 0x0a,
};
/**
* Retrieves the battery level status.
*
@@ -258,6 +265,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 +318,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 +360,68 @@ 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,
};
struct hidpp20_internal_led {
uint8_t mode; /* 00 - off; 01 - on; 03 - cycle; 0a - breath */
struct hidpp20_color color;
union {
struct hidpp20_led_cycle {
uint16_t pad_1;
uint16_t rate; /* 16000 - 20000Hz */
uint8_t brightness; /* 0 - 100 percent */
uint16_t pad_2;
} __attribute__((packed)) cycle;
struct hidpp20_led_breath {
uint16_t rate;
uint8_t pad_1;
uint8_t brightness;
uint8_t pad_2;
uint16_t pad_3;
} __attribute__((packed)) breath;
} __attribute__((packed)) effect;
};
_Static_assert(sizeof(struct hidpp20_led_cycle) == 7, "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 rate;
percent_t brightness;
};
#define HIDPP20_MACRO_NOOP 0x01
#define HIDPP20_MACRO_DELAY 0x40
#define HIDPP20_MACRO_KEY_PRESS 0x43
@@ -358,6 +463,7 @@ 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[32];
};
struct hidpp20_profiles {
@@ -365,6 +471,7 @@ struct hidpp20_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;

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);

View File

@@ -95,6 +95,7 @@ struct ratbag_device {
struct list profiles;
unsigned num_buttons;
unsigned num_leds;
void *drv_data;
@@ -130,6 +131,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 +154,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 +182,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 +231,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 +259,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 +323,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 +365,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 +465,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,

View File

@@ -30,6 +30,7 @@
#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 +49,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 +75,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;

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

@@ -200,4 +200,21 @@ long_set_bit_state(unsigned long *array, int bit, int state)
const char *
udev_prop_value(struct udev_device *device,
const char *property_name);
/**
* 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);
#endif /* LIBRATBAG_UTIL_H */

View File

@@ -59,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,
@@ -533,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;
}
@@ -580,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)
{
@@ -593,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;
@@ -609,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);
@@ -620,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;
}
@@ -628,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;
@@ -649,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;
}
@@ -665,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);
@@ -713,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)
{
@@ -731,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)
@@ -748,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)
{
@@ -758,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)
@@ -842,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
@@ -858,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))
@@ -867,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
@@ -929,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*
@@ -999,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
@@ -1029,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
@@ -1067,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;
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 *
@@ -1114,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)
{
@@ -1126,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)
{
@@ -1140,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)
{
@@ -1240,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

@@ -30,6 +30,7 @@ extern "C" {
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <libudev.h>
#define LIBRATBAG_ATTRIBUTE_PRINTF(_format, _args) \
@@ -54,6 +55,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 +128,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 +176,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 +515,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 +549,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 +576,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 +617,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 +653,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 +767,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 +975,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 +999,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 +1267,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 +1275,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 +1341,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,6 +1790,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

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;

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;

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>
@@ -58,6 +59,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 +68,7 @@ struct ratbag_cmd_options {
struct ratbag_profile *profile;
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct ratbag_led *led;
};
struct ratbag_cmd {
@@ -101,6 +104,8 @@ usage(void)
"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 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"
@@ -143,12 +148,27 @@ usage(void)
"\n"
" switch-etekcity Switch the Etekcity mouse active profile\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\n"
" mode [on|off|cycle|breath] 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"
"Examples:\n"
" %s profile active get\n"
" %s profile 0 resolution active set 4\n"
" %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"
@@ -161,6 +181,9 @@ usage(void)
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);
}
@@ -194,9 +217,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 +237,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;
@@ -316,9 +337,11 @@ 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;
struct ratbag_color color;
device = options->device;
@@ -337,24 +360,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,6 +420,21 @@ ratbag_cmd_info(const struct ratbag_cmd *cmd,
button = ratbag_button_unref(button);
}
for (l = 0; l < num_leds; l++) {
led = ratbag_profile_get_led(profile, l);
color = ratbag_led_get_color(led);
printf(" LED: %d type %s mode %s color %02x%02x%02x rate %d brightness: %d\n",
l,
led_type_to_str(ratbag_led_get_type(led)),
led_mode_to_str(ratbag_led_get_mode(led)),
color.red, color.green, color.blue,
ratbag_led_get_effect_rate(led),
ratbag_led_get_brightness(led));
led = ratbag_led_unref(led);
}
profile = ratbag_profile_unref(profile);
}
@@ -472,7 +517,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 +605,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 +676,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;
@@ -1270,13 +1314,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 +1336,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 +1474,285 @@ 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;
return SUCCESS;
}
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 = {
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;
return SUCCESS;
}
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 = {
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[2], green[2], blue[2];
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;
return SUCCESS;
}
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 = {
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;
return SUCCESS;
}
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 = {
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;
enum ratbag_led_mode mode;
struct ratbag_color color;
int hz;
int brightness;
enum ratbag_led_type type;
led = options->led;
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);
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);
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 +1884,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 +1989,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,
},
};
@@ -1701,9 +2080,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,4 +1,4 @@
.TH RATBAG_COMMAND "1"
.TH RATBAG_COMMAND "1" "@version@"
.SH NAME
ratbag-command \- Query or change a device's settings
.SH SYNOPSIS

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 = "breath";
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

@@ -59,6 +59,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