1
0
forked from drath/libratbag

Compare commits

...

222 Commits

Author SHA1 Message Date
Peter Hutterer
3bc3da967c meson.build: libratbag 0.9.901
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-12-18 13:46:50 +10:00
Peter Hutterer
b50ffef327 meson: require meson version 0.36
For add_project_arguments

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-12-18 13:21:32 +10:00
Ogier Bouvier
ec223eb2e6 hidpp20: force at least one profile to be enabled
fix #428

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>
2017-12-13 15:06:39 +01:00
Ignacio Vazquez-Abrams
573d3d399e Allow meson to skip the test that requires root if root is unavailable 2017-12-04 09:05:09 +10:00
Thomas Hindoe Paaboel Andersen
0f5beb619e steelseries: support cycle and breathing LED mode
To specify the colors to cycle trough we write a list of
colors and their individual share of the total cycle duration.

Solid colors only have a single color in their list.

Breathing mode will cycle to and from the picked color and black.

Cycle mode will cycle through red, green, and blue.
2017-11-27 08:54:06 +10:00
Thomas Hindoe Paaboel Andersen
89aff23c11 use SVGs from the source dir in .devel
When using ratbagctl.devel we would use the .device files
from the source dir. This patch also makes it use the .svg
files from the source dir. This makes it easier to edit the
svg and less surprising now that it follows the .devices files.

To match the source file layout the .devices files are now
installed into a 'devices' subdirectory.
2017-11-25 16:46:50 +01:00
Thomas Hindoe Paaboel Andersen
fd27957b57 steelseries: add a support for the Kinzu V2 2017-11-22 14:35:09 +10:00
Thomas Hindoe Paaboel Andersen
554ee88c86 steelseries: move hardcoded values to .device
The steelseries driver used some hardcoded values for as the
only supported device was the rival 310. This moves that data
to the .device file.

The default resolutions used are changed from hardcoded 1000
and 2000, to the two first entries in the dpi list. Picking
actual values from the specified dpi list or range is safer.
2017-11-22 14:35:09 +10:00
Thomas Hindoe Paaboel Andersen
868d71a08b data: add section for SteelSeries mice
Add a section in the .device files for SteelSeries mice.
The mice cannot be queried and we need to store data for each
device of number of buttons, leds, and dpi list/range. Lastly
a "device version" is added. The latest models use a slightly
different protocol, so we needed something to distinguish them
with.
2017-11-22 14:35:09 +10:00
Jente Hidskes
9b3c33a2fe Convert LED effect rate in Hz to effect duration in ms
As discussed in https://github.com/libratbag/piper/issues/175
2017-11-15 08:53:56 +01:00
Benjamin Tissoires
109cc69da9 circle.yml: fix CI builds
Looks like https://bugzilla.redhat.com/show_bug.cgi?id=1483553 is
the key.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-11-03 19:44:05 +01:00
Thomas Hindoe Paaboel Andersen
113bd4859e steelseries: add writing buttons 2017-10-27 08:35:10 +10:00
Thomas Hindoe Paaboel Andersen
2a2b7487d4 steelseries: init the buttons to defaults 2017-10-26 10:25:56 +10:00
Thomas Hindoe Paaboel Andersen
2b4022dcbf steelseries: set report rate list 2017-10-26 10:25:56 +10:00
Thomas Hindoe Paaboel Andersen
7555cf1607 G300: set DPIs as a fixed list
Set the list of DPIs directly instead of use the _from_range call.

The G300 has few and coarse resolution options. Using the
exponential'ish range is not a good fit for devices with so
few dpis.

The logitech G300 can set:
250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500

but piper would offer:
250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750,
800, 850, 900, 950, 1000, 1100, 1200, 1300, 1400, 1500,
1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500

We overpromise by offering more finegrainded options than the
mouse can do and end up with three times as many options than
we should.
2017-10-25 23:21:28 +02:00
Benjamin Tissoires
9c75a7cc7e tools: ratbagctl: add --nocommit
Allows to batch commands for setting multiple parameters at once.

Fixes #399

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-25 09:19:24 +02:00
Benjamin Tissoires
0337ae41ab tools: valgrind: shut up some non relevant memory leaks
Fixes #405

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-24 08:25:17 +10:00
Peter Hutterer
ff39e67615 Don't install the default theme svgs
No-one appears to be using these and the efforts to create SVGs
go into the GNOME ones (used by piper). Leave them in the git tree in case
someone wants to pick them up but until we have a consumer it's not worth it
installing them.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-23 09:30:09 +02:00
Peter Hutterer
7ba5563eeb meson: silence compiler warning in swig generated files
libratbag@sha/libratbag.c: In function ‘_wrap_error__varargs__’:
libratbag@sha/libratbag.c:6392:3: warning: format not a string literal, argument types not checked [-Wformat-nonliteral]
   error((char const *)arg1,arg2);

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-23 10:19:57 +10:00
Thomas Hindoe Paaboel Andersen
968b9383d3 G300: move LED leader to LEDs layer
It does not seem to make any difference in Piper but let's be
correct anyway
2017-10-20 07:47:48 +10:00
Joshua Strobl
be6c381751 docs: Ensure we also check for python3 sphinx build exec suffixed without a hyphen. 2017-10-20 07:46:12 +10:00
Joshua Strobl
27a0db37d8 Merge Logitech G403 devices files. 2017-10-20 07:37:03 +10:00
Peter Hutterer
5858816b53 hidpp20: fill the led modes from the 8070 effect ids
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-19 09:23:56 +10:00
Peter Hutterer
a1376180b4 hidpp20: check for monochrome only effects in 8070
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-19 09:23:56 +10:00
Peter Hutterer
75d1400206 hidpp20: set the LED caps based on what the device reports
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-19 09:23:56 +10:00
Peter Hutterer
78475a6d15 hidpp20: use the full getInfo request, not just the count
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-19 09:23:56 +10:00
Peter Hutterer
63f93111fb hidpp20: remove unused reg paramater
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-19 09:23:56 +10:00
Benjamin Tissoires
1ea21ed6ce ratbagd: clean up ratbag devices on SIGINT
Or valgrind complains a lot.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-18 16:12:44 +02:00
Benjamin Tissoires
18a8fb9651 hidpp20-drv: cleanup hidpp20drv_update_button*
There is no point anymore to have an extra 'action' argument, and make
both type of function look the same.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-18 08:25:45 +10:00
Benjamin Tissoires
2e836a40d3 macro: fix memleak, again
action->macro is supposed to be allocated once and stay for the
entire life of the button. However, it was outrageously overwritten
when setting the action. Take some actions and keep the macro action
around.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-18 08:25:45 +10:00
Benjamin Tissoires
c09872e589 macro: fix memleak
The group is set but never freed.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-18 08:25:45 +10:00
Thomas Hindoe Paaboel Andersen
575b7bce98 add driver and svg for steelseries rival 310
The driver does not yet support button mappings or any
non-solid LED modes.

The device does not support reading out the current
configuration. On probe some default values are used
to get a usable profile.
2017-10-18 00:19:12 +02:00
Thomas Hindoe Paaboel Andersen
3e04abde30 add function to remove capabilites 2017-10-18 00:19:12 +02:00
Thomas Hindoe Paaboel Andersen
790c5e90bd add RATBAG_LED_TYPE_SCROLL_WHEEL 2017-10-18 00:19:12 +02:00
Thomas Hindoe Paaboel Andersen
85a358e07c G9: remove DPI 0 2017-10-18 08:12:27 +10:00
Thomas Hindoe Paaboel Andersen
3af0544421 hidpp10: fix last entry in dpi_list_from_range 2017-10-18 08:12:27 +10:00
Peter Hutterer
f69b4d53f6 hidpp20: implement 8060 adjustable report rate
The bitmask in the 8060 report is for ms between reports. We could do all, but
really we only care about a few resolutions, anything else is too niche to
bother with.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Peter Hutterer
cb4be02a56 tools: add 'rate get-all' to the resolutions
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Peter Hutterer
bacdab2596 Sanity-check the list of dpi values and report rates
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Peter Hutterer
ec7bb2f63a Add list of report rates to the resolutions
Same blueprint as the list of DPI values

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Peter Hutterer
74a5551a8d Allow for zero number of dpi values to be set
At least in the DBus API we can't just say "doesn't exist", so let's allow for
a zero-sized list.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Peter Hutterer
3e0c803a37 driver-hidpp20: fix some indentation
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Peter Hutterer
55682c3a5d tools: add dpi get-all to the ratbagctl man page
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Peter Hutterer
ae9e287b8d Fix a comment referring to a function name in the typemap
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Peter Hutterer
03c264cec1 tools: remove erroneous 'self' in function call
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Peter Hutterer
410e01de61 gskill: add a missing empty line
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Peter Hutterer
d167dfd83b gskill: init the DPI list correctly
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Peter Hutterer
9b7d8f5f24 Fix missing nres assignments
For directly set resolution lists, nres remained at 0

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-17 10:55:14 +02:00
Benjamin Tissoires
366fbc3fda Fix some flake8 warnings
$> flake8-3 tools/ratbagctl.devel.in tools/ratbagctl.in tools/ratbagctl.test.in .
tools/ratbagctl.in:166:1: E302 expected 2 blank lines, found 1
tools/ratbagctl.in:173:25: E201 whitespace after '['
tools/ratbagctl.in:173:68: E202 whitespace before ']'
tools/ratbagctl.in:314:1: E302 expected 2 blank lines, found 1
tools/ratbagctl.in:318:1: E302 expected 2 blank lines, found 1
tools/ratbagctl.test.in:281:43: E261 at least two spaces before inline comment
tools/ratbagctl.test.in:302:31: E261 at least two spaces before inline comment
tools/ratbagctl.test.in:306:43: E261 at least two spaces before inline comment
tools/ratbagctl.test.in:337:43: E261 at least two spaces before inline comment
tools/ratbagctl.test.in:576:1: E302 expected 2 blank lines, found 1

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-16 19:06:57 +02:00
Benjamin Tissoires
f1d20f1f35 tools: ratbag-command.py: fix new resolutions list
This one gets a little bit meesy given that the libratbag expects a
C array. Provide some wrapping aound it and hope we won't have to
do it again and again.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-16 19:06:57 +02:00
Benjamin Tissoires
558f59b52c tools: replace ratbag-command.c with ratbag-command.py
Let the .c file die, and try to maintain the python version only.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-16 19:06:57 +02:00
Benjamin Tissoires
1a3ef63519 tools: ratbag-command.py: add verbose mode
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-16 19:06:57 +02:00
Benjamin Tissoires
17380baae9 tools: ratbag-command.py: lazy evaluation of the devices
We do not need to parse all the devices, but only the one given in the
command line. Make ratbagc.py handle this case properly.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-16 19:06:57 +02:00
Benjamin Tissoires
314c3e2f78 ratbag-command.py: allow valgrind to show something useful
sudo valgrind --suppressions=./tools/valgrind-ratbag-command.supp \
              --leak-check=full \
              python3 ./build/ratbag-command.py

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-16 19:06:57 +02:00
Benjamin Tissoires
6ba5cc3319 meson.build: shut up a warning for the generated swig python wrapper
It's generated and it works, no need to have the missing-prototypes
warning on.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-16 19:06:57 +02:00
Benjamin Tissoires
1148d172b5 Generate a direct python wrapper around libratbag with swig
And use it to create ratabg-command.py

Benefits are that we bypass ratbagd, meaning we can attach gdb or run
valgrind on the tools themselves.

Problem is the python wrapper doesn't clean up after itself, and so
running valgrind is moot

Fixes #274

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-16 19:06:57 +02:00
Benjamin Tissoires
a930cdad87 ratbagctl: update man
Should be closer to the truth now.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-16 12:02:56 +02:00
Peter Hutterer
b951d83a64 Add ratbagd_led_has_mode() and the required dbus additions
To be able to query which modes are available.

Exposed via ratbagctl as
./build/ratbagctl.devel led 0 capabilities event4

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-16 08:47:32 +10:00
Benjamin Tissoires
f56df6b1d0 enums.h: few doc fixups
Replacing the doctext where they should be in enum ratbag_led_mode.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-16 07:09:26 +10:00
Benjamin Tissoires
1e35b2ace6 circle.yml: cache dnf packages
We are reusing a lot the fedora container, but we need to always download
and install the same set of packages. Store them in the workspace to
speed up the tests.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-13 18:29:35 +02:00
Benjamin Tissoires
e75c20a249 circle.yml: fix doc deploy key
Of course, it was working on the local tests, but broke on upstream.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-13 18:24:54 +02:00
Benjamin Tissoires
c63ffdd3ef circle.yml: deploy docs when the master branch is updated
Note that the push requires a specific private key in the libratbag
circle CI organization, meaning that only the master branch of libratbag
will be allowed to push to the doc repo.

Note also that only the master branch will trigger the request:
- any PR will not trigger the request
- any push on the master branch of a personal repo will fail while
  attempting to write the docs, if the repo is setup to use circleCI too.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-13 17:47:26 +02:00
Peter Hutterer
40658c6d0e hidpp10: pass the dpi list to libratbag as-is
If our device only has a discrete number of DPIs, pass it on as-is to
libratbag rather than pretending it's a range and then failing to set the
DPIs.

For the actual ranged devices we use libratbag's now-exponential range
mapping rather than passing all possible ones on. This relies on us having the
same steps that libratbag uses, should be safe enough.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-13 17:46:05 +02:00
Peter Hutterer
251efd3b40 tools: add dpi get-all to ratbagctl
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-13 17:46:05 +02:00
Peter Hutterer
94320099b0 Convert the resolution range into something resembling an exponential one
Because 8000 vs 8050 dpi shouldn't matter...

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-13 17:46:05 +02:00
Peter Hutterer
f592cabcd7 Replace the resolutions min/max with an explicit list of dpi values
Previously, we could get a min/max value but there was no guarantee that it
any value in between would actually work on the device. Some devices have a
discrete number of resolutions (e.g. 500, 1000, 2000, 2500), most others have
at least a minimum step size (so 1234 isn't a valid dpi value).

Change the min/max to a discrete list of which resolutions are supported. This
allows a UI to adjust its interface dynamically - e.g. a combobox if there are
only 3 resolutions or a slider if there are a million of them.

This is a DBus API break

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-13 17:46:05 +02:00
Peter Hutterer
a58096fade tools: always print 'dpi' after resolutions
We printed '500' but '500x500dpi', let's make this consistent.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-13 17:46:05 +02:00
Peter Hutterer
b140bc2827 test: add dpi min/max tests
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-13 17:46:05 +02:00
Peter Hutterer
aeaa69b9e1 Move the log defines higher so we can use them anywhere
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-13 17:46:05 +02:00
Peter Hutterer
a535897ae7 Use Sphinx for the DBus interface documentation
Better than the markdown we were using before, even though we're currently
(ab)using the js/c++ domains for the structure. There is no DBus domain in
sphinx yet.

The main motivation was to get the enums into the documentation since they are
exported ABI.

Generating the documentation is quirky - we use doxygen to extract the
comments from the C header file and then use XSLT to convert to .rst which
sphinx can use. Still better than having to maintain even more copies of the
enums.

Since this is only of interest for maintainers that need to upload the
documentation, it is turned off by default.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-13 08:59:33 +02:00
Peter Hutterer
1074a2b2e6 circle.yml: drop doxygen and graphviz - obsolete now
We're not building docs anymore

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-13 08:59:33 +02:00
Peter Hutterer
51c1be3806 Move the enums into their separate header file
libratbag is not a library anymore. Move all the stuff that we need for the
DBus interface (i.e. the enum values) into its own header file so they're
all in one place and we don't have to worry about it anymore.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-13 08:59:33 +02:00
Peter Hutterer
b9e647ccb8 Remove c++ guard
libratbag is not a public header, so we don't have to worry about C++ anymore.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-13 08:59:33 +02:00
Thomas Hindoe Paaboel Andersen
e063aa3d2a hidpp10: Fix LED read/write
With this the LEDs can be read and written on the G9.

The G9 is as far as I know the only hid++1.0 mouse with RGB LED. The
other mice are monochrome.
2017-10-12 08:07:59 +10:00
Peter Hutterer
9a74e30c64 Use foreach loops to iterate though the profiles
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-11 09:14:56 +02:00
Peter Hutterer
f26de4bb29 Change driver probe() as a flat function
Previously, probe() would call ratbag_init_profiles() which in turn would call
the various function pointers in the driver to fill in profiles, buttons and
leds (the latter while filling in profiles). This made the probe() function
harder to understand than necessary and unreliable because it wasn't always
clear which bits were already set up while some driver function was called and
which ones weren't yet.

This patch switches to a flat probe(). The driver still needs to call
ratbag_init_profiles(), libratbag sets up everything (to default values). Then
the driver can fill in the rest as it wants.

This patch still makes use of the current infrastructure, the drivers
just call the read_profile and read_button functions manually now.

Fixes #370

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-11 09:14:56 +02:00
Peter Hutterer
e9008eeaba Align resolution creation with the button/profile/led creation bits
Resolutions were still stored in an array over a linked list like the
buttons/leds/profiles and initializing them from a driver was different than
buttons or leds. This brings the resolutions in line with the buttons and
leds.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-11 09:14:56 +02:00
Peter Hutterer
d5dc8eccd6 DBus: some more documentation
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-11 09:13:44 +02:00
Peter Hutterer
13736d0ced DBus: thighten the definition of the 'Id' for a device
We can't guarantee it's a sysname even though that is what we use for evdev
devices. Future devices may just be some custom-assigned name for a remote
dbus API

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-11 09:13:44 +02:00
Peter Hutterer
a926afe8de ratbagd: standardise the log messages a bit better
Prefix with the sysname of the device, then have a 'failed to ...'

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-11 09:13:44 +02:00
Peter Hutterer
af78dc4ec2 ratbagd: rename device->name to device->sysname
Because that's what it is and it makes it less confusing in the code

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-11 09:13:44 +02:00
Peter Hutterer
1c85d77d56 data: add LedTypes keyword argument to the device files
At least on HID++ 2.0, the LED types appear to be random and don't match the
protocol specs. Other protocols don't even have those bits, so let's make this
a generic entry.

This is pre-filled for all LEDs but drivers can override it where required.

Fixes #377

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-10 09:30:50 +10:00
Peter Hutterer
c8929b50fa meson: fix default value in meson_options.txt
Keyword is named 'value', not 'default'

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-10 08:41:55 +10:00
Peter Hutterer
e58e79cb8d Fix a comment
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-10 08:41:55 +10:00
Joshua Strobl
049e12b2eb Correct G403 ID. Added Wireless-specific G403 device file. 2017-10-09 21:37:22 +02:00
Benjamin Tissoires
5c71f9552d tools: ratbagctl: make flake8 happy
tools/ratbagctl.in:146:92: E128 continuation line under-indented for visual indent
tools/ratbagctl.in:147:92: E126 continuation line over-indented for hanging indent
tools/ratbagctl.in:154:91: E128 continuation line under-indented for visual indent
tools/ratbagctl.in:155:91: E126 continuation line over-indented for hanging indent
tools/ratbagctl.in:161:118: E128 continuation line under-indented for visual indent
tools/ratbagctl.in:162:118: E126 continuation line over-indented for hanging indent

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-09 19:17:32 +02:00
Benjamin Tissoires
c3be9e1af1 tools: ratbagctl: add missing led types
Or the G502 fails while reading the LEDs

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-09 19:14:04 +02:00
Peter Hutterer
186d626c40 Remove RATBAG_LED_TYPE_UNKNOWN from the enum list
A driver is supposed to know what type an LED is, unknown doesn't help anyone
here. So replace this with a private #define and abort when a driver doesn't
set the type. This also adds a requirement that any driver initializing a LED
must have the read_led() function, otherwise we can't fill it in correctly.

And bump the enum up by one, the first valid value should start at 1 (or some
other non-boolean value).

Related #365
Fixes #364

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-09 19:12:17 +02:00
Peter Hutterer
dfc433cb74 test: fix LED indexing
Leftover from when we used the LED type as indices, that hasn't been true for
a while.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-09 19:12:17 +02:00
Peter Hutterer
ab2dae47af hidpp20: require HIDPP_PAGE_COLOR_LED_EFFECTS for LEDs to work
If HIDPP_PAGE_COLOR_LED_EFFECTS fails but the profile returns a nonzero LED
count we crash later in hidpp20drv_read_led() because all the led info is
null.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-09 19:12:17 +02:00
Peter Hutterer
ec442b960d hidpp20: don't set the device's num_leds, it's overwritten later
This should all be handled by libratbag, not the driver.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-09 19:12:17 +02:00
Benjamin Tissoires
670720b40f hidpp20: fix .type parsing for sw_leds
struct hidpp20_led_sw_ctrl_led_info holds a u8 for the .type field.
Attempting to convert a value which is already a u8 ends up setting
0 and thus discarding the type information.

Fix by removing the extra step.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-10-09 19:12:00 +02:00
Ogier Bouvier
0787e35964 data: add device file and svg for Logitech G703 wired
Signed-off-by: Ogier Bouvier <obouvier@logitech.com>
2017-10-09 12:12:26 +10:00
Peter Hutterer
9f4df9582c test: move an not-null assert to above where we deference it
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-09 12:01:56 +10:00
Peter Hutterer
d282590746 test: set all leds to logo
Because that's exactly what we rely on right now - zalloc sets it to 0 which
is type logo.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-09 12:01:56 +10:00
Peter Hutterer
9e5047976b hidpp10: rename a variable from 'profile' to 'typestr'
To avoid confusion

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-09 12:01:56 +10:00
Peter Hutterer
d69a2ec5ba hidpp10: use _cleanup_ for a profile
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-09 12:01:56 +10:00
Peter Hutterer
4bfd5d9a13 Add helper functions to loop through profiles, buttons and leds
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-09 12:01:56 +10:00
Peter Hutterer
091e651831 Fix a comment, the deprecated functions *should* be removed
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-09 12:01:56 +10:00
Thomas Hindoe Paaboel Andersen
d52c2c0320 hidpp10: remove empty callback
write_profile is deprecated and none of the other drivers have an empty
implementation like this.
2017-10-07 09:39:22 +10:00
Thomas Hindoe Paaboel Andersen
cd71ce67b1 hidpp10: only update current resolution once per commit
With the swtich to the commit style the function
hidpp10drv_write_resolution_dpi is doing too much work.

Before commit style the function would:
* get the profile
* get the current resolution
* write the profile (resets it to default dpi)
* set the current (updated) resolution

With the commit style we call the function for every resolution
in every dirty profile we now:
* get the current resolution
* set the current (updated) resolution
Then later:
* write the profile (resets to default dpi)

This patch changes this to only doing one set of the current resolution
when the active profile has been written. This reduces the traffic to the
device and sets the current resolution after the profile write.
2017-10-05 17:20:49 +02:00
Thomas Hindoe Paaboel Andersen
43f501907e hidpp10: fix setting resolution on G9
The G9 sends a short report and does not
have separate DPIs for x and y.
2017-10-05 17:20:49 +02:00
Peter Hutterer
d26d5b83f1 test: set RATBAG_TEST in code, not in meson
It's a fixed value so it's much easier to set this in the test binaries rather
than requiring the user to set this in the env before running a test manually.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-04 18:53:25 +10:00
Peter Hutterer
c401414310 tools: update ratbagd.py for the new logo types
Added in 835a694c4b

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-04 10:13:53 +10:00
Thomas Hindoe Paaboel Andersen
c34fe85780 G9: add entry for LEDs count 2017-10-03 23:06:01 +02:00
Thomas Hindoe Paaboel Andersen
a19a79d33a hidpp10: add Leds entry to .device file
For the device I have I do not see a way to get
this information by querying the device.

If the value is set in the .device file it will
override anything that the hidpp10 driver itself
would have found.
2017-10-03 23:06:01 +02:00
Ogier Bouvier
8edf6e7609 tools: use new RATBAG_LED_TYPEs
Signed-off-by: Ogier Bouvier <obouvier@logitech.com>
2017-10-03 19:48:32 +10:00
Ogier Bouvier
835a694c4b driver-hidpp20: add feature discovery for 0x1300
fix #325

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>
2017-10-03 19:48:32 +10:00
Ogier Bouvier
a47492e62c hidpp20: add support for non-RGB LEDs, feature 0x1300
Signed-off-by: Ogier Bouvier <obouvier@logitech.com>
2017-10-03 19:48:32 +10:00
Peter Hutterer
186a1deda8 led: change RGB to RGB888 and add 1-bit RGB
1-bit RGB is needed for the Logitech G300, let's rename the main RGB to
indicate how many bits we actually have.

See #335

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-03 08:58:34 +02:00
Thomas Hindoe Paaboel Andersen
132212652f G9: add .svg for GNOME 2017-10-02 22:31:40 +02:00
Peter Hutterer
5a40d93a30 ratbagd: disable coredumps in ratbagd.devel
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-02 10:25:24 +02:00
Peter Hutterer
f62940c1e5 tools: specify the full path for the dbus permissions file
Since we unconditionally call unlink() on that path later, let's make sure
we actually copy to that file.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-02 10:25:24 +02:00
Peter Hutterer
0f808ed387 tools: fix a linebreak to be less confusing
We os.path.join the first to arguments, fix the line breaks so this is obvious

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-02 10:25:24 +02:00
Peter Hutterer
d8edf2d9e4 tools: unlink the dbus file if ratbagd.devel fails to start
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-10-02 10:25:24 +02:00
Jente Hidskes
6827f5ed81 tools/ratbagd.py: sync with Piper 2017-09-30 08:51:50 +10:00
Thomas Hindoe Paaboel Andersen
f3208b499b G300: set the color depth 2017-09-29 00:18:22 +02:00
Thomas Hindoe Paaboel Andersen
94e0842a06 G300: add support LEDs 2017-09-29 00:18:22 +02:00
Thomas Hindoe Paaboel Andersen
e0de98ba01 set CAP_LED for mice with only 1 LED
Even if there is only 1 LED we should set
the cap to let Piper change it in the GUI
2017-09-28 09:06:28 +10:00
Thomas Hindoe Paaboel Andersen
1adb147f6d expand documentation of probe call
Add a comment that drivers are expected to initialize and
read all profiles in the probe callback.

Fixes #333
2017-09-28 09:05:59 +10:00
Peter Hutterer
b1c4bba3b2 led: add support for color depth
The initial value of 400 in the enum is chosen so it looks weird when someone
tries to interpret the value as integer. Values of 1, 2, 4, etc. could be
confused with something realistic.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-22 09:18:28 +02:00
Thomas Hindoe Paaboel Andersen
cdbdcd23e6 G300: set min/max DPI 2017-09-21 19:21:28 +02:00
Thomas Hindoe Paaboel Andersen
23d1290b22 G300: set the active resolution
It was implemented but never used before. Use it when writing the
active profile.O
2017-09-21 08:51:21 +10:00
Thomas Hindoe Paaboel Andersen
5edcdfba5b G300: use int instead of signed 2017-09-20 15:55:37 +10:00
Thomas Hindoe Paaboel Andersen
6d105a002c G300: fix writing multiple profiles
Writing multiple changed profiles was broken as we would exit the
loop after the first profile write. We were returning the number
of bytes written and this was interpreted as an error.

Fix it my returning 0 when we have written the correct number of
bytes to the mouse.
2017-09-20 15:55:37 +10:00
Ogier Bouvier
3b1a41f154 hidpp20: fix special button mapping
fix #348

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>
2017-09-19 13:48:17 +02:00
Peter Hutterer
0153efbf9c Remove now superfluous libratbag.sym
We don't ship a library anymore, so dealing with library symbols isn't
necessary.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-18 14:24:50 +02:00
Jente Hidskes
db34ab5255 RatbagdButton: fix missed dbus return value
I missed this one in #336.
2017-09-17 21:34:47 +10:00
Benjamin Tissoires
52e10d029f tools: ratbagctl.test: assert on the ratbagd process
If we could not start the process, there is a high chance we are running
the tests against a different ratbagd implementation so there is no point.

Also, an exception will get raised later during the tear down of the
module saying that "'NoneType' object has no attribute 'terminate'".

This is less than ideal to notify that the process has not been started.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-15 08:55:34 +02:00
Benjamin Tissoires
1c94ef5c56 tools: ratbagctl: only warn that no devices are available in list_devices
If the other commands try to access a node, there will be an other error
("Unable to find device X"). So there is no points in having this as a
global check.

For the .devel and .test versions, we are guaranteed to have the
test_device at least, so no worries on this side too.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-15 08:55:34 +02:00
Benjamin Tissoires
5efd83fc41 tools: make sure we check for ratbagd_process returncode
It was accidentally put as a dead code in 6cfca9e34b.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-15 08:55:34 +02:00
Jente Hidskes
33966989db ratbagd: add ACTION_SPECIAL_INVALID to the table
So we at least don't crash, see #383.
2017-09-14 16:02:56 +02:00
Ogier Bouvier
c56c76e07d hidpp20: fix resolution count detection
fix #343

Signed-off-by: Ogier Bouvier <obouvier@logitech.com>
2017-09-14 16:02:40 +02:00
Thomas Hindoe Paaboel Andersen
8440a36f92 G300 SVG: add 20 px margin 2017-09-14 09:49:41 +02:00
Thomas Hindoe Paaboel Andersen
3e31b45db0 SVG for G300 in GNOME theme 2017-09-14 09:49:41 +02:00
Ogier Bouvier
548a40dba6 hidpp20: store correct size for feature list
Signed-off-by: Ogier Bouvier <obouvier@logitech.com>
2017-09-13 10:17:37 +02:00
Benjamin Tissoires
c437882e96 tools: ratbag-command: info: make use of _cleanup_
Better code :)

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Ogier Bouvier
4a61dc413b support sector size that are not multiple of 16
Signed-off-by: Ogier Bouvier <obouvier@logitech.com>

Squashed the comment into this
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
59df7defdb tools: add hidpp20-reset
Basically a nuclear weapon to reset the flash to 0.
Use it with caution!

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
77fa6a1e65 hidpp20: fill in all profiles, even if they are not there
If a profile is either wrong or not there, we rely on the ROM to get
a correct profile.

Fixes #280

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
0f8031c201 hidpp20: add commit API for onboard profiles
The driver is supposed to prepare everything in memory and then, at the
end commit on the device.

Bug #280 is still not fixed as we should initialize empty profiles first.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
5842dccc2a hidpp20: check if crcs are valid once on plug
To check if device is using ROM or Flash settings.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
0c5b1998a5 hidpp20: move down hidpp20_initialize
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
f52ebd0dba hidpp20: move up hidpp20_internal_profile
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
1857378a76 hidpp20: new memory model
Only reading/writing full sectors are allowed (because we need to set and
read the crc anyway).

Fixes #122

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
d2600583bb hidpp20: do not mixed hex and decimal
because this is bad

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
e2a4684f83 hidpp20: use min when possible
s/x <= y ? x : y/min(x, y)/

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
ed39cf3dee hidpp20: reshuffle onboard_profile allocate/initialize
allocate was actually both initializing and allocating through initialize.
Things started to get complicated.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
c4d3b1e606 hidpp20: fix Invalid writes
Valgrind complains...

Fixes 19451f9df7

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Benjamin Tissoires
be1fb30751 tools: ratbag-command: fix a memory leak
valgrind complains that we did not release the profile, so we shall.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-13 09:21:53 +02:00
Jente Hidskes
a2b3c32124 RatbagdResolution: clamp values instead of returning an error
We cannot return error codes, but requesting a resync is overkill.
Instead, let's be nice and clamp these values.
2017-09-13 08:44:50 +10:00
Jente Hidskes
26e7237010 RatbagdLed: clamp values instead of returning an error
We cannot return error codes, but requesting a resync is overkill.
Instead, let's be nice and clamp these values.
2017-09-13 08:44:50 +10:00
Jente Hidskes
26a6215122 RatbagdResolution: rename get_rate to get_report_rate for consistency 2017-09-13 08:44:50 +10:00
Jente Hidskes
376adfdf2a ratbagd: fix return values in dbus methods
See #316.
2017-09-13 08:44:50 +10:00
Jente Hidskes
d990e3f027 RatbagdDevice: add a Resync signal
This signal is emitted upon an internal error, signalling clients to
resync their values.

Related to #316.
2017-09-13 08:44:50 +10:00
Jente Hidskes
2473f66cb3 Move note on return values to the interface documentation 2017-09-13 08:44:50 +10:00
Jente Hidskes
8e9bf7b48a Add the Name property to the interface documentation 2017-09-13 08:44:50 +10:00
Peter Hutterer
7edae05b37 ratbagd: fix verbose_raw setting
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-11 19:05:51 +10:00
Peter Hutterer
909749fc00 tools: drop the parser debug printfs
Not required anymore, I think

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-11 09:26:07 +02:00
Peter Hutterer
15b4b4a3ca tools: pass the verbosity arguments down to ratbagd
Starting with -vv, we print out the ratbagd log messages to stdout/stderr,
-vvv switches ratbagd to raw mode. We start at level 2 so that we can have the
single -v reserved for ratbagctl itself.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-11 09:26:07 +02:00
Peter Hutterer
cad05f1256 tools: parse the argument before starting ratbagd
No point starting ratbagd for things like --help or invalid commands

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-11 09:26:07 +02:00
Peter Hutterer
703e1f6f0d ratbagd: allow for --verbose=raw
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-11 09:26:07 +02:00
Ogier Bouvier
19451f9df7 hidpp20: fix last feature discovery
Signed-off-by: Ogier Bouvier <obouvier@logitech.com>
2017-09-08 15:40:55 +02:00
Thomas Hindoe Paaboel Andersen
3314b41202 hidpp10: fix outdated info message 2017-09-08 08:56:03 +10:00
Thomas Hindoe Paaboel Andersen
f636fd8f53 G300: set active resolution
For the active profile we read the active resolution
from the mouse during probe.

When changing the active profile we set the default
resolution to active.
2017-09-07 10:30:37 +02:00
Jente Hidskes
77847f570c tools/ratbagd: remove KeyMapping methods
Cleanup after afaf3fa83d.
2017-09-07 10:24:38 +02:00
Peter Hutterer
afaf3fa83d ratbagd: Drop KeyMapping from the DBus interface
Plan is to remove the key mapping altogether and make it transparent from the
Macro mappings (in libratbag/the drivers). Meanwhile, remove the DBus API so
nothing can actually start using it.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-07 08:25:59 +02:00
Peter Hutterer
4c7f644599 hwdb: drop the remaining hwdb files
Now that we use the .ini style device configuration and everything has been
migrated, we don't need the hwdb/rules files anymore

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-07 09:21:45 +10:00
Peter Hutterer
692f7f008a Fix typo
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-07 09:21:45 +10:00
Benjamin Tissoires
0a62c3dfa8 hidpp20: fix mem leak
Valgrind complains about it, so let's fix it.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-05 19:18:10 +10:00
Benjamin Tissoires
7cd2cfed44 tools: ratbagctl.devel: fix commit() async call
If we leave ratbagctl.devel too soon, ratbagd gets killed before it even
receives the async call for commmit(), meaning everything we do on the
device is lost.

0.5 is empirical. It seems on my system 0.2 is enough (0.1 doesn't seem
to work). Hopefully this value is not device dependent.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-04 19:55:53 +10:00
Benjamin Tissoires
c31e11d385 tools: fix permissions for ratbagctl
not sure why, but stat.S_IEXEC only sets exec permission for the user,
meaning that the installed file can't be run by anybody else than root.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-09-04 19:55:53 +10:00
Thomas Hindoe Paaboel Andersen
efe6021e76 G300: fix profile switching
The profile ID's used are:
  profile 0: 0x80
  profile 1: 0x90
  profile 2: 0xA0

These were miscalculated as 0x80, 0x81, and 0x82.
2017-09-04 09:25:38 +10:00
Stephen Kitt
8d67389042 Don't install ratbag-command manpage
Since we no longer install ratbag-command itself, there isn't much
point installing its manpage.

Signed-off-by: Stephen Kitt <steve@sk2.org>
2017-09-04 08:24:34 +10:00
Peter Hutterer
4037587f74 Get the libratbag data dir from the environment variable
And set it for the tests, so we can test with in-tree files.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-01 09:49:32 +02:00
Peter Hutterer
94daeb1444 ratbagd: split the test device out into a seprate file
It always bothered me that it was in the middle of the normal ratbagd, which
made fiding it more effort than neccessary. Split it out into a separate file
so it's easily identifiable.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-01 09:30:38 +02:00
Peter Hutterer
937b30c415 circle.yml: set LANG to UTF-8
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-01 07:37:03 +02:00
Peter Hutterer
6f0242f05b Add missing include for stdint.h to libratbag-test.h
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-09-01 10:53:37 +10:00
Thomas Hindoe Paaboel Andersen
921837fd0f ratbagctl: typo fix 2017-08-31 21:32:28 +02:00
Jente Hidskes
d06cdcc53c tools/ratbagd.py: sync with Piper 2017-08-31 15:25:38 +02:00
Benjamin Tissoires
5828355426 Fix flake 8 warnings
./tools/toolbox.py:105:1: E302 expected 2 blank lines, found 1
tools/ratbagctl.in:369:13: F821 undefined name 'RatbagdMacro'

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-31 15:06:08 +02:00
Jente Hidskes
50dc321b7b tools/ratbagctl.in: use RatbagDevice's active_profile property 2017-08-31 14:34:03 +02:00
Jente Hidskes
43f033db80 tools/ratbagctl.in: use RatbagProfile's active_resolution property 2017-08-31 14:34:03 +02:00
Jente Hidskes
623c53f1e4 tools/ratbagctl.in: use RatbagdMacro to print the macro 2017-08-31 14:34:03 +02:00
Jente Hidskes
39762718b9 tools/ratbagd.py: sync with Piper 2017-08-31 14:34:03 +02:00
Benjamin Tissoires
ae3804f7b4 tools/toolbox.py: add dbus sync helper function
A quick and easy way to sync anything coming from dbus (like property updates)
without needing to be hooked into a glib mainloop directly.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 14:34:03 +02:00
Peter Hutterer
0dd08dfb84 tools/ratbagctl.test.in: decode ratbagctl output with utf8 for the tests
The macros with their fancy characters need utf8, not ascii

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 14:34:03 +02:00
Peter Hutterer
bb23f68d53 util: avoid reallocation of zero-sized memory in dpi list parsing
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 09:48:52 +02:00
Peter Hutterer
83345a17d2 hidpp20: add an assert to shut up scan-build
Not sure why it thinks we're potentially dereferencing a NULL-pointer here but
from the trace it looks like it thinks macro and a previously nullified
profile->macros are the same. They aren't, macro was just allocated here and
shouldn't be NULL if we get here.

Anyway, add an assert and move on.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 09:48:52 +02:00
Peter Hutterer
b9f6554767 ratbagd: fix enum type for resolutions
Found by coverity

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 09:48:52 +02:00
Peter Hutterer
7eadd20879 hidpp10: fix a coverity warning about OOB access
Make sure that the index we pass into long_clear_bit is never higher than the
number of bits in the single long we use as array here.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 09:48:52 +02:00
Peter Hutterer
3844a60837 hidp20: fix dead code in macro parsing
If rc is positive, it's the number of items.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 09:48:52 +02:00
Peter Hutterer
6cdf8a901a ratbagd: fix use of potentially unused variable
Simply abort if the macro value is bad, that's a libratbag bug and should
never happen.

Found in #312

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 09:48:52 +02:00
Peter Hutterer
c868c43179 tools: fix use of unset variable
Found in #312

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 09:48:52 +02:00
Peter Hutterer
01ca2a6de7 ratbagd: drop the unused IsActive and IsDefault resolution signals
Obsolete, now that we're just updating the property.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 09:37:59 +02:00
Peter Hutterer
d64af72f74 ratbagd: fix profile IsActive property changes
We sent a signal out but not the "property has changed" signal we actually
needed.

Fixes #295

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 09:37:59 +02:00
Peter Hutterer
c2fe745813 data/gnome: add a quick and dirty SVG checker for the GNOME SVGs
Checks for the three layers we need and that any buttons in the files are
consecutive and have the right layers

Fixes #196

Co-authored-by: Jente Hidskes <hjdskes@gmail.com>
2017-08-31 08:40:58 +02:00
Peter Hutterer
ddefbf95a3 test: ignore python allocation leaks in our valgrind runs
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 08:40:58 +02:00
Peter Hutterer
a2b651985e data/gnome: fallback: add missing button6-path
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 08:40:58 +02:00
Jente Hidskes
788c261ce9 data/gnome: roccat kone XTD: add missing leaders for wheel tilt buttons 2017-08-31 08:40:58 +02:00
Peter Hutterer
35070cc17e data/gnome: roccat kone XTD: fix button numbering
Somehow I skipped over buttons 7 and 8

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 08:40:58 +02:00
Jente Hidskes
79be057e91 data/gnome: G502: add missing text-align style property 2017-08-31 08:40:58 +02:00
Peter Hutterer
aa8f506dd0 data/gnome: G502: fix SVG to be 400 pixels height
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 08:40:58 +02:00
Peter Hutterer
c6486b1960 tools: add missing vim config line to toolbox.py
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 14:08:23 +10:00
Peter Hutterer
2f8e3a1610 tools: fix typos in test case class names
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 14:08:20 +10:00
Peter Hutterer
432d32bedc ratbagd: set the active profile before sending signals
Found by @bentiss, see #295

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 13:08:41 +10:00
Peter Hutterer
8cb2d21ff5 Remove unnecessary NULL check before free()
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 13:03:07 +10:00
Peter Hutterer
1103c208a8 ratbagd: if the svg doesn't exist, supply the fallback one
We keep the hardcoded fallback.svg for the test device, otherwise we get an
error every time we start ratbagd.devel

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-31 10:48:38 +10:00
Peter Hutterer
7e59f0f974 data: add a fallback.svg file
This SVG is generic enough to cover 7-button mice with up to 3 LEDs that are
supported but don't have an SVG. The SVG style is intentionally NOT a generic
mouse to make it obvious that something isn't right here. If we use a generic
mouse, people may not put the effort into a proper SVG style because the
generic mouse is good enough.

The fallback is currently only used for test devices
2017-08-31 10:48:38 +10:00
Benjamin Tissoires
c647d6df99 profiles: add name get/set capability
Some devices can have a custom name, so let's export that capability.

In ratbagd, libratbag/ratbag_profile_set_name() returns an error, we
put it under silence, the client has to check for the capabilities
before asking ratbagd to change the profile name.

Fixes: #266

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-31 10:22:51 +10:00
Benjamin Tissoires
97a927bb7d profiles: add a per profile capabilities field
Doesn't do much now without the next commit

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-31 10:22:51 +10:00
Benjamin Tissoires
7c650aeeca tools: ratbagctl: add "name" command to return the device name
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-31 10:22:51 +10:00
Thomas Hindoe Paaboel Andersen
3fd7cd0137 improve error for missing device files 2017-08-31 08:17:15 +10:00
Thomas Hindoe Paaboel Andersen
7e97936f0e reset GError before reuse
If the error variable is not reset then we may end up freeing it
again in the next block. This can cause problems later for glib.

Fixes #311
2017-08-31 08:07:19 +10:00
Peter Hutterer
643a4a89f4 ratbagd: fix assertion error when quitting with more than 1 devices attached
RATBAGD_FOREACH_DEVICE does not allow unlinking the device during the loop.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-30 09:42:47 +10:00
Peter Hutterer
cfd93cf332 drop now obsolete ratbag_device_get_udev_property
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-28 17:50:20 +02:00
Peter Hutterer
340e5b7d86 Switch to a .ini-file based device database
The hwdb was a quick and easy way to add entries for new devices but we need
more and more info about the various types of devices. It's better to move
this to a proper database.

Side-effect: this also gives us an easy way to point to the list of devices
supported and makes checking for e.g. missing svgs easier.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-28 17:50:20 +02:00
Peter Hutterer
be624fe943 Move dpi range parsing to a helper function
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-28 17:50:20 +02:00
Peter Hutterer
cb102e115c Add xasprintf
Like asprintf, but leaves strp as NULL on failure

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-28 17:50:20 +02:00
Clément Vuchener
952eb1cf0e Add Logitech G900 SVG for gnome theme 2017-08-28 12:16:49 +02:00
Peter Hutterer
709ffa37b2 hwdb: add two missing driver assignments
We have autodetection for Logitech, but let's always specify the driver for
devices we have in the hwdb.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-28 09:18:00 +02:00
Peter Hutterer
f4f206f3e5 Add missing config.h includes
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-28 09:18:00 +02:00
110 changed files with 12057 additions and 5650 deletions

View File

@@ -1,303 +0,0 @@
The ratbagd DBus-Interface
--------------------------
Note: **the DBus interface is subject to change**
Interfaces:
- org.freedesktop.ratbag1.Manager
- org.freedesktop.ratbag1.Device
- org.freedesktop.ratbag1.Profile
- org.freedesktop.ratbag1.Resolution
- org.freedesktop.ratbag1.Button
- org.freedesktop.ratbag1.Led
For a list of dbus types as used below, see https://dbus.freedesktop.org/doc/dbus-specification.html
Properties marked as 'constant' do not change for the lifetime of the
object. Properties marked as 'mutable' may change, and a
`org.freedesktop.DBus.Properties.PropertyChanged` signal is sent for those
unless otherwise specified.
org.freedesktop.ratbag1.Manager
===============================
The **org.freedesktop.ratbag1.Manager** interface is the entry point to
interact with ratbagd.
### Properties:
#### `Devices`
- type `ao`, read-only, mutable
Provides the object paths of all current devices, see **org.freedesktop.ratbag1.Device**
#### `Themes`
- type `as`, read-only, constant
Provides the list of available theme names. This list is guaranteed to have
one theme available ('default'). Other themes are implementation defined.
A theme listed here is only a guarantee that the theme is known to libratbag
and that SVGs *may* exist, it is not a guarantee that the SVG for any
specific device exists. In other words, a device may not have an SVG for a
specific theme.
This list is static for the lifetime of ratbagd.
org.freedesktop.ratbag1.Device
==============================
The **org.freedesktop.ratbag1.Device** interface describes a single device
known to ratbagd.
### Properties:
#### `Id`
- type: `s`, read-only, constant
An ID describing this device. This ID should not be used for presentation to
the user.
#### `Description`
- type: `s`, read-only, constant
The device's name, suitable for presentation to the user.
#### `Capabilities`
- type: `au`, read-only, constant
The capabilities supported by this device. see `enum
ratbag_device_capability` in libratbag.h for the list of permissible
capabilities.
#### `Svg`
- type: `s`, read-only, constant
The device's SVG file name, without path.
#### `Profiles`
- type: `ao`, read-only, mutable
This property is mutable if the device supports adding and removing
profiles.
Provides the list of profile paths for all profiles on this device, see
**org.freedesktop.ratbag1.Profile**.
### Methods:
#### `Commit() → ()`
Commits the changes to the device. Changes to the device are batched; they
are not written to the hardware until `Commit()` is invoked.
#### `GetSvg(s) → (s)`
Returns the full path to the SVG for the given theme or an empty string if
none is available. The theme must be one of
**org.freedesktop.ratbag1.Manager.Themes**. The theme 'default' is
guaranteed to be available. ratbagd may return the path to a file that
doesn't exist. This is the case if the device has SVGs available but not for
the given theme.
org.freedesktop.ratbag1.Profile
===============================
### Properties:
#### `Index`
- type: `u`, read-only, constant
The index of this profile
#### `Enabled`
- type: `b`, read-write, mutable
True if this is the profile is enabled, false otherwise.
Note that a disabled profile might not have correct bindings, so it's
a good thing to rebind everything before calling `Commit()` on the
**org.freedesktop.ratbag1.Device**.
#### `Resolutions`
- type: `ao`, read-only, mutable
This property is mutable if the device supports adding and removing
resolutions.
Provides the object paths of all resolutions in this profile, see
**org.freedesktop.ratbag1.Resolution**
#### `Buttons`
- type: `ao`, read-only, constant
Provides the object paths of all buttons in this profile, see
**org.freedesktop.ratbag1.Button**
#### `Leds`
- type: `ao`, read-only, constant
Provides the object paths of all LEDs in this profile, see
**org.freedesktop.ratbag1.Led**
### `IsActive`
- type: `b`, read-only, mutable
True if this is the currently active profile, false otherwise.
Profiles can only be set to active, but never to not active - at least one
profile must be active at all times. This property is read-only, use the
`SetActive()` method to activate a profile.
### Methods:
#### `SetActive() → ()`
Set this profile to be the active profile
org.freedesktop.ratbag1.Resolution
==================================
### Properties:
#### `Index`
- type: `u`, read-only, constant
Index of the resolution
#### `Capabilities`
- type: `au`, read-only, constant
Array of uints with the capabilities enum from libratbag
#### `IsActive`
- type: `b`, read-only, mutable
True if this is the currently active resolution, false otherwise.
Resolutions can only be set to active, but never to not active - at least
one resoultion must be active at all times. This property is read-only, use the
`SetActive()` method to set a resolution as the active resolution.
#### `IsDefault`
- type: `b`, read-only, mutable
True if this is the currently default resolution, false otherwise. If the
device does not have the default resolution capability, this property is
always false.
Resolutions can only be set to default, but never to not default - at least
one resolution must be default at all times. This property is read-only, use the
`SetDefault()` method to set a resolution as the default resolution.
#### `Resolution`
- type: `uu`, read-write, mutable
uint for the x and y resolution assigned to this entry, respectively
#### `ReportRate`
- type: `u`, read-write, mutable
uint for the report rate in Hz assigned to this entry
#### `Maximum`
- type: `u`, read-only, constant
uint for the maximum possible resolution
#### `Minimum`
- type: `u`, read-only, constant
uint for the minimum possible resolution
### Methods:
#### `SetDefault() → () `
Set this resolution to be the default
#### `SetActive() → () `
Set this resolution to be the active one
org.freedesktop.ratbag1.Button
==============================
### Properties:
#### `Index`
- type: `u`, read-only, constant
Index of the button
#### `Type`
- type: `u`, read-only, constant
Enum describing the button physical type. This type is unrelated to the
logical button mapping and serves to easily identify the button on the
device.
#### `ButtonMapping`
- type: `u`, read-write, mutable
uint of the current button mapping (if mapping to button)
#### `SpecialMapping`
- type: `u`, read-write, mutable
Enum describing the current special mapping (if mapped to special)
#### `KeyMapping`
- type: `au`, read-write, mutable
Array of uints, first entry is the keycode, other entries, if any, are
modifiers (if mapped to key)
#### `Macro`
-type: `a(uu)`, read-write, mutable
Array of (type, keycode), where type may be one of
`RATBAG_MACRO_EVENT_KEY_PRESSED` or `RATBAG_MACRO_EVENT_KEY_RELEASED`.
#### `ActionType`
- type: `u`, read-only, mutable
An enum describing the action type of the button, see
ratbag\_button\_get\_action\_type for the list of enums.
This decides which *Mapping* property has a value.
#### `ActionTypes`
- type: `au`, read-only, constant
Array of enum ratbag\_button\_action\_type, possible values for ActionType
on the current device
### Methods:
#### `Disable() → ()`
Disable this button
org.freedesktop.ratbag1.Led
===========================
### Properties:
#### `Index`
- type: `u`, read-only, constant
Index of the LED
#### `Mode`
- type: `u`, read-write, mutable
uint mapping to the mode enum from libratbag
#### `Type`
- type: `u`, read-only, mutable
enum describing the LED type
#### `Color`
- type: `(uuu)`, read-write, mutable
uint triplet (RGB) of the LED's color
#### `EffectRate`
- type: `u`, read-write, mutable
The effect rate in Hz, possible values are in the range 100 - 20000
#### `Brightness`
- type: `u`, read-write, mutable
The brightness of the LED, possible values are in the range 0 - 255
For easier debugging, objects paths are constructed from the device. e.g.
`/org/freedesktop/ratbag/button/event5/p0/b10` is the button interface for
button 10 on profile 0 on event5. The naming is subject to change. Do not
rely on a constructed object path in your application.

View File

@@ -80,33 +80,16 @@ library in the future, if other projects require it.
Adding Devices to libratbag
---------------------------
As of commit 3605bede4 libratbag now uses a hwdb entry to match the device
with the drivers. To access a device through libratbag, the udev property
RATBAG_DRIVER must be set for a device's event node. Check with
libratbag relies on a device database to match a device with the drivers.
See the data/devices/ directory for the set of known devices. These files
are usually installed into $prefix/$datadir (e.g. /usr/share/libratbag/).
sudo udevadm info /sys/class/input/eventX | grep RATBAG_DRIVER
To add a new device to libratbag, simply drop a new device file into that
directory and restart ratbagd. See the data/devices/device.example file for
guidance on what information must be set.
If your device is not yet assigned the property, it is not in the hwdb. It
may however be supported by one of our existing drivers. Try enabling it by
adding the device vendor-id/product-id to the hwdb file in
`hwdb/70-libratbag-mouse.hwdb`. For example, for a HID++ 1.0 device, edit
the 70-libratbag-mouse.hwdb file and add an entry with `RATBAG_DRIVER=hidpp10`.
For the other drivers, look for the id of the driver in driver-{drivername}.c
file and do the same.
Once your device is added to the hwdb, install libratbag and trigger a hwdb
update:
sudo udevadm hwdb --update
sudo udevadm control --reload
Then unplug/replug your mouse. `RATBAG_DRIVER` should appear in the udev
properties of your device with the value you previously set. If the property
is not assigned, the hwdb entry does not correctly match your device or the
installed udev rules/hwdb entries are not picked up by udev.
If the device doesn't work, you'll have to start reverse-engineering the
device-specific protocol. Good luck :)
If the device doesn't work after adding the device file, you'll have to
start reverse-engineering the device-specific protocol. Good luck :)
Source
------

View File

@@ -1,6 +1,9 @@
# vim: set expandtab shiftwidth=2 tabstop=8:
libratbag_references:
default_settings: &default_settings
working_directory: ~/libratbag
environment:
LANG: C.UTF-8
build_default: &build_default
name: Build
command: |
@@ -16,6 +19,12 @@ libratbag_references:
name: Build and test
environment:
NINJA_ARGS: test
build_with_docs: &build_with_docs
run:
<<: *build_default
name: Build with documentation
environment:
MESON_PARAMS: -Ddocumentation=true
install: &install
run:
name: Installing
@@ -52,16 +61,34 @@ libratbag_references:
mkdir /run/dbus
/usr/bin/dbus-daemon --system --fork
fedora_prep_cache: &fedora_prep_cache
<<: *default_settings
steps:
- run:
name: Initializing Fedora dnf cache
command: dnf install -y --downloadonly libsolv tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml
- persist_to_workspace:
root: /var/cache/
paths:
- dnf/*
fedora_fetch_cache: &fedora_fetch_cache
attach_workspace:
at: /var/cache/
fedora_install: &fedora_install
run:
name: Install prerequisites
command: dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel doxygen graphviz valgrind python3-gobject python3-evdev
command: |
echo keepcache=1 >> /etc/dnf/dnf.conf
dnf upgrade -y libsolv
dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml python3-devel swig
fedora_settings: &fedora_settings
<<: *default_settings
steps:
- *fedora_fetch_cache
- *fedora_install
- checkout
- *start_dbus
@@ -81,7 +108,7 @@ ubuntu_settings: &ubuntu_settings
apt-get install -y software-properties-common
add-apt-repository universe
apt-get update
apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev doxygen graphviz valgrind python3-gi python3-evdev
apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev valgrind python3-gi python3-evdev libglib2.0-dev python3-lxml python3-dev swig
- checkout
- *start_dbus
- *build_and_test
@@ -93,6 +120,7 @@ ubuntu_settings: &ubuntu_settings
scan_build_run: &scan_build_run
<<: *default_settings
steps:
- *fedora_fetch_cache
- *fedora_install
- run:
name: Install clang and find
@@ -104,6 +132,61 @@ scan_build_run: &scan_build_run
name: Check scan-build results
command: test ! -d ~/libratbag/build/meson-logs/scanbuild || test $(find ~/libratbag/build/meson-logs/scanbuild -maxdepth 0 ! -empty -exec echo "not empty" \; | wc -l) -eq 0 || (echo "Check scan-build results" && false)
doc_build: &doc_build
<<: *default_settings
steps:
- *fedora_fetch_cache
- *fedora_install
- run:
name: Install documentation build-deps
command: dnf install -y python3-sphinx python3-sphinx_rtd_theme doxygen libxslt
- checkout
- *build_with_docs
- *export_logs
- store_artifacts:
path: ~/libratbag/build/doc/html
- persist_to_workspace:
root: build
paths:
- doc/html/*
docs_deploy: &docs_deploy
<<: *default_settings
steps:
- *fedora_fetch_cache
- run:
name: Install prerequisites
command: dnf install -y git tree
- checkout
- attach_workspace:
at: build
- run:
name: Setup the deploy SSH key
command: |
ssh-add -L | tail -n1 > ~/.ssh/deploy.pub
cat <<EOF > ~/.ssh/config
Host github.com
IdentityFile /root/.ssh/deploy.pub
IdentitiesOnly yes
EOF
- run:
name: Clone doc repository
command: |
cd ~
git clone ssh://git@github.com/${CIRCLE_PROJECT_REPONAME}/${CIRCLE_PROJECT_REPONAME}.github.io
- run:
name: Copy the doc, commit and push
command: |
set +e
cd ~/${CIRCLE_PROJECT_REPONAME}.github.io
\cp -r ~/${CIRCLE_PROJECT_REPONAME}/build/doc/html/* .
if ! git diff-index --quiet HEAD --; then
git config --global user.email "libratbag@librabtag.github.io"
git config --global user.name "The libratbag crew"
git add .
git commit -m "update docs from ${CIRCLE_SHA1}"
git push
fi
version: 2
jobs:
@@ -111,6 +194,10 @@ jobs:
<<: *fedora_settings
docker:
- image: fedora:rawhide
fedora_cache:
<<: *fedora_prep_cache
docker:
- image: fedora:latest
fedora_latest:
<<: *fedora_settings
docker:
@@ -119,10 +206,18 @@ jobs:
<<: *ubuntu_settings
docker:
- image: ubuntu:zesty
doc_build:
<<: *doc_build
docker:
- image: fedora:latest
scan_build:
<<: *scan_build_run
docker:
- image: fedora:latest
doc_deploy:
<<: *docs_deploy
docker:
- image: fedora:latest
workflows:
@@ -130,6 +225,21 @@ workflows:
compile_and_test:
jobs:
# - fedora_rawhide
- scan_build
- fedora_cache
- scan_build:
requires:
- fedora_cache
- ubuntu_17_04
- fedora_latest
- fedora_latest:
requires:
- fedora_cache
- doc_build:
requires:
- fedora_cache
- doc_deploy:
requires:
- fedora_cache
- doc_build
filters:
branches:
only: master

234
data/devices/data-parse-test.py Executable file
View File

@@ -0,0 +1,234 @@
#!/usr/bin/env python3
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright © 2017 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.
#
# Device data verification script
#
import argparse
import os
import configparser
# Set on commandline with --svg-dir
svg_dirs = []
def check_svg_str(string):
assert(string.endswith('.svg'))
svg_file_found = False
for svg_dir in svg_dirs:
files = os.listdir(svg_dir)
if string in files:
svg_file_found = True
break
assert(svg_file_found)
def check_match_str(string):
bustypes = ['usb', 'bluetooth']
matches = string.split(';')
for match in matches:
if not match: # empty string if trailing ;
continue
parts = match.split(':')
assert(len(parts) == 3)
assert(parts[0] in bustypes)
vid = parts[1]
assert(vid == '{:04x}'.format(int(vid, 16)))
pid = parts[2]
assert(pid == '{:04x}'.format(int(pid, 16)))
def check_ledtypes_str(string):
permitted_types = ['logo', 'side', 'battery', 'dpi']
types = string.split(';')
for t in types:
if not t: # emtpy string if trailing ;
continue
assert(t in permitted_types)
def check_section_device(section):
required_keys = ['Name', 'Driver', 'DeviceMatch']
permitted_keys = required_keys + ['Svg', 'LedTypes']
for key in section.keys():
assert(key in permitted_keys)
for r in required_keys:
assert(r in section)
try:
check_svg_str(section['Svg'])
except KeyError:
pass
try:
check_ledtypes_str(section['LedTypes'])
except KeyError:
pass
check_match_str(section['DeviceMatch'])
def check_dpi_range_str(string):
import re
m = re.search('^([0-9]+):([0-9]+)@([0-9\.]+)$', string)
assert(m is not None)
min = int(m.group(1))
max = int(m.group(2))
steps = float(m.group(3))
assert(min >= 0 and min <= 400)
assert(max >= 2000 and max <= 12000)
assert(steps > 0 and steps <= 100)
if int(steps) == steps:
steps = int(steps)
assert(string == '{}:{}@{}'.format(min, max, steps))
def check_dpi_list_str(string):
entries = string.split(';')
# Remove possible empty last entry if trailing with a ;
if not entries[len(entries) - 1]:
entries = entries[:-1]
for idx, entry in enumerate(entries):
dpi = int(entry)
assert(dpi >= 0 and dpi <= 12000)
if idx > 0:
prev = entries[idx - 1]
prev_dpi = int(prev)
assert(dpi > prev_dpi)
def check_profile_type_str(string):
types = ['G9', 'G500', 'G700']
assert(string in types)
def check_section_hidpp10(section):
permitted = ['Profiles', 'ProfileType', 'DpiRange', 'DpiList', 'DeviceIndex', 'Leds']
for key in section.keys():
assert(key in permitted)
try:
nprofiles = int(section['Profiles'])
# 10 is arbitrarily chosen
assert(nprofiles > 0 and nprofiles < 10)
except KeyError:
pass
try:
index = int(section['DeviceIndex'])
# 10 is arbitrarily chosen
assert(index > 0 and index < 10)
except KeyError:
pass
try:
check_dpi_range_str(section['DpiRange'])
assert('DpiList' not in section.keys())
except KeyError:
pass
try:
check_dpi_list_str(section['DpiList'])
assert('DpiRange' not in section.keys())
except KeyError:
pass
try:
check_profile_type_str(section['ProfileType'])
except KeyError:
pass
try:
leds = int(section['Leds'])
# 10 is arbitrarily chosen
assert(leds > 0 and leds < 10)
except KeyError:
pass
def check_section_hidpp20(section):
permitted = ['DeviceIndex']
for key in section.keys():
assert(key in permitted)
try:
index = int(section['DeviceIndex'])
# 10 is arbitrarily chosen
assert(index > 0 and index < 10)
except KeyError:
pass
def check_section_driver(driver, section):
if driver == 'hidpp10':
check_section_hidpp10(section)
elif driver == 'hidpp20':
check_section_hidpp20(section)
else:
assert('Unsupported driver section {}'.format(driver))
def parse_data_file(path):
print('Parsing file {}'.format(path))
data = configparser.ConfigParser(strict=True)
# Don't convert to lowercase
data.optionxform = lambda option: option
data.read(path)
assert('Device' in data.sections())
check_section_device(data['Device'])
driver = data['Device']['Driver']
driver_section = 'Driver/{}'.format(driver)
permitted_sections = ['Device', driver_section]
for s in data.sections():
assert(s in permitted_sections)
if data.has_section(driver_section):
check_section_driver(driver, data[driver_section])
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Device data-file checker")
parser.add_argument('file', nargs='+')
parser.add_argument('--svg-dir', metavar='dir', action='append',
type=str,
help='Directory to check for SVG files (may be given multiple times)')
args = parser.parse_args()
svg_dirs = args.svg_dir
for path in args.file:
parse_data_file(path)

View File

@@ -0,0 +1,57 @@
# Example description file for a libratbag device
#
# Make sure the file has a recognizable name with a '.device' suffix
[Device]
# The kernel name of the device. This name is not used by libratbag, it is
# used by debugging tools.
Name=Logitech Example Device
# DeviceMatch is a triplet of bus:vid:pid, where bus is 'usb' or bluetooth'
# and pid/vid are lowercase 4-digit hexadecimal numbers with preceding
# zeroes.
DeviceMatch=usb:12ab:00f2
# The file name of the SVG file representing this device
Svg=logitech-example.svg
# The libratbag driver backend
Driver=hidpp10
# The types of the LEDs, in the indexed order they appear in, separated by a
# semicolon.
# Allowed values: logo, side, battery, dpi
LedTypes=logo;side
# Only one group of driver-specific properties is permitted and it must be
# [Driver/<drivername>]. It is a bug do have any other [Driver/foo] group in
# a device file, but this example file lists all.
[Driver/hidpp10]
# The number of profiles, must be greater than 0
Profiles=1
# The type of the profile
ProfileType=G9
# The range of available resolutions in the form min:max@step
# Mutually exclusive with DpiList
DpiRange=50:5700@50
# The list of available resolutions, separated by semicolons.
# Mutually exclusive with DpiRange
# Where possible, DpiRange should be preferred
DpiList=50;200;400;800
# The HID++ 1.0 device index
# Should be unset unless required
# DeviceIndex=1
# The number of LEDs
Leds=1
[Driver/hidpp20]
# The HID++ 2.0 device index
# Should be unset unless required
# DeviceIndex=1

View File

@@ -0,0 +1,6 @@
# FIXME: which device is this exactly?
[Device]
Name=Etekcity FIXME
DeviceMatch=usb:1ea7:4011
Driver=etekcity
Svg=etekcity.svg

View File

@@ -0,0 +1,5 @@
# G.Skill MX-780
[Device]
Name=G.Skill MX-780
DeviceMatch=usb:28da:3101
Driver=gskill

View File

@@ -0,0 +1,5 @@
# Logitech M325 over unifying
[Device]
Name=Logitech M325
DeviceMatch=usb:046d:400a
Driver=hidpp20

View File

@@ -0,0 +1,5 @@
# Logitech M570
[Device]
Name=Logitech M570
DeviceMatch=usb:046d:1028
Driver=hidpp10

View File

@@ -0,0 +1,5 @@
# Logitech M705
[Device]
Name=Logitech M705
DeviceMatch=usb:046d:101b
Driver=hidpp10

View File

@@ -0,0 +1,6 @@
# Logitech MX Master
[Device]
Name=Logitech MX Master
DeviceMatch=usb:046d:4041;bluetooth:046d:b012
Driver=hidpp20
Svg=logitech-mx_master.svg

View File

@@ -0,0 +1,5 @@
# Logitech T650 over unifying
[Device]
Name=Logitech T650
DeviceMatch=usb:046d:4101
Driver=hidpp20

View File

@@ -0,0 +1,5 @@
# Logitech Wireless Touchpad (Unifying)
[Device]
Name=Logitech Wireless Touchpad
DeviceMatch=usb:046d:4011
Driver=hidpp20

View File

@@ -0,0 +1,6 @@
# WARNING: this file was automatically converted and has not yet been vetted
[Device]
Name=Logitech G300
DeviceMatch=usb:046d:c246
Driver=logitech_g300
Svg=logitech-g300.svg

View File

@@ -0,0 +1,7 @@
# G303 over USB
[Device]
Name=Logitech Gaming Mouse G303
DeviceMatch=usb:046d:c080
Svg=logitech-g303.svg
Driver=hidpp20
LedTypes=logo;side;

View File

@@ -0,0 +1,6 @@
# Logitech G403
[Device]
Name=Logitech G403
DeviceMatch=usb:046d:c082;usb:046d:c083
Driver=hidpp20
Svg=logitech-g403.svg

View File

@@ -0,0 +1,9 @@
# Name=Logitech G5 2007
[Device]
Name=Logitech G5
DeviceMatch=usb:046d:c049
Driver=hidpp10
[Driver/hidpp10]
DpiList=400;800;1600;2000
Profiles=1

View File

@@ -0,0 +1,7 @@
[Device]
Name=G5
DeviceMatch=usb:046d:c041
Driver=hidpp10
[Driver/hidpp10]
DpiList=400;800;1600;2000

View File

@@ -0,0 +1,10 @@
[Device]
Name=Logitech G500
DeviceMatch=usb:046d:c068
Driver=hidpp10
Svg=logitech-g500.svg
[Driver/hidpp10]
DpiRange=0:5700@23.53
ProfileType=G500
Profiles=1

View File

@@ -0,0 +1,10 @@
[Device]
Name=Logitech G500s
DeviceMatch=usb:046d:c24e
Driver=hidpp10
Svg=logitech-g500s.svg
[Driver/hidpp10]
DpiRange=0:8200@50
ProfileType=G500
Profiles=1

View File

@@ -0,0 +1,6 @@
# Logitech G502 Proteus Core over USB
[Device]
Name=Logitech G502 Proteus Core
DeviceMatch=usb:046d:c07d
Driver=hidpp20
Svg=logitech-g502.svg

View File

@@ -0,0 +1,6 @@
# Logitech G502 Proteus Spectrum over USB
[Device]
Name=Logitech G502 Proteus Spectrum
DeviceMatch=usb:046d:c332
Driver=hidpp20
Svg=logitech-g502.svg

View File

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

View File

@@ -0,0 +1,9 @@
# Logitech G7
# http://support.logitech.com/en_us/product/g7-laser-cordless-mouse/specs
[Device]
Name=Logitech G7
DeviceMatch=usb:046d:c51a
Driver=hidpp10
[Driver/hidpp10]
Profiles=1

View File

@@ -0,0 +1,12 @@
# Logitech G700 over wireless USB
# http://support.logitech.com/en_us/product/wireless-gaming-mouse-g700/specs
[Device]
Name=Logitech G700
DeviceMatch=usb:046d:c531
Driver=hidpp10
[Driver/hidpp10]
DpiRange=0:5700@23.53
ProfileType=G700
DeviceIndex=1
Profiles=5

View File

@@ -0,0 +1,12 @@
# Logitech G700 over USB
# http://support.logitech.com/en_us/product/wireless-gaming-mouse-g700/specs
[Device]
Name=Logitech G700
DeviceMatch=usb:046d:c06b
Driver=hidpp10
Svg=logitech-g700.svg
[Driver/hidpp10]
DpiRange=0:5700@23.53
ProfileType=G700
Profiles=5

View File

@@ -0,0 +1,13 @@
# Logitech G700s over wireless USB
# http://support.logitech.com/en_us/product/g700s-rechargable-wireless-gaming-mouse/specs
[Device]
Name=Logitech G700s
DeviceMatch=usb:046d:c531
Driver=hidpp10
Svg=logitech-g700.svg
[Driver/hidpp10]
DpiRange=0:8200@50
ProfileType=G700
DeviceIndex=1
Profiles=5

View File

@@ -0,0 +1,12 @@
# Logitech G700s over USB
# http://support.logitech.com/en_us/product/g700s-rechargable-wireless-gaming-mouse/specs
[Device]
Name=Logitech G700s
DeviceMatch=usb:046d:c07c
Driver=hidpp10
Svg=logitech-g700.svg
[Driver/hidpp10]
DpiRange=0:8200@50
ProfileType=G700
Profiles=5

View File

@@ -0,0 +1,6 @@
[Device]
Name=Logitech G703
DeviceMatch=usb:046d:c087
Driver=hidpp20
Svg=logitech-g703.svg

View File

@@ -0,0 +1,14 @@
# Logitech G9
# http://support.logitech.com/en_us/product/g9-laser-mouse/specs
[Device]
Name=Logitech G9
DeviceMatch=usb:046d:c048
LedTypes=dpi
Driver=hidpp10
Svg=logitech-g9.svg
[Driver/hidpp10]
DpiList=200;400;600;800;1000;1200;1400;1600;1800;2000;2200;2400;2600;2800;3000;3200
ProfileType=G9
Profiles=5
Leds=1

View File

@@ -0,0 +1,9 @@
# Logitech G900 and G403 over wireless USB
[Device]
Name=Logitech G900, G403
DeviceMatch=usb:046d:c539
Driver=hidpp20
Svg=logitech-g900.svg
[Driver/hidpp20]
DeviceIndex=1

View File

@@ -0,0 +1,6 @@
# Logitech G900 over USB
[Device]
Name=Logitech G900
DeviceMatch=usb:046d:c081
Driver=hidpp20
Svg=logitech-g900.svg

View File

@@ -0,0 +1,11 @@
# Logitech G9x [Call of Duty MW3 Edition]
# http://support.logitech.com/en_us/product/laser-mouse-g9x/specs
[Device]
Name=Logitech G9x [Call of Duty MW3 Edition]
DeviceMatch=usb:046d:c249
Driver=hidpp10
[Driver/hidpp10]
DpiRange=0:5700@23.53
ProfileType=G500
Profiles=5

View File

@@ -0,0 +1,11 @@
# Logitech G9x [Original]
# http://support.logitech.com/en_us/product/g9x-laser-mouse/specs
[Device]
Name=G9x [Original]
DeviceMatch=usb:046d:c066
Driver=hidpp10
[Driver/hidpp10]
DpiRange=0:5700@23.53
ProfileType=G500
Profiles=5

View File

@@ -0,0 +1,5 @@
[Device]
Name=Roccat Kone XTD
DeviceMatch=usb:1e7d:2e22
Driver=roccat
Svg=roccat-kone-xtd.svg

View File

@@ -0,0 +1,11 @@
[Device]
Name=SteelSeries Kinzu V2
DeviceMatch=usb:1038:1378
Driver=steelseries
Svg=steelseries-kinzu-v2.svg
[Driver/steelseries]
DeviceVersion=1
Buttons=3
Leds=0
DpiList=400;800;1600;3200

View File

@@ -0,0 +1,11 @@
[Device]
Name=SteelSeries Rival 310
DeviceMatch=usb:1038:1720
Driver=steelseries
Svg=steelseries-rival310.svg
[Driver/steelseries]
DeviceVersion=2
Buttons=6
Leds=2
DpiRange=100:12000@100

118
data/gnome/check-svg.py Executable file
View File

@@ -0,0 +1,118 @@
#!/usr/bin/python3
# vim: set expandtab shiftwidth=4 tabstop=4:
import sys
from lxml import etree
import logging
ns = {'svg': 'http://www.w3.org/2000/svg'}
style_query = '//svg:rect[@id=\"{}\"][contains(@style, \"{}\")]'
logger = None
class SVGLogger(logging.Logger):
def __init__(self, name, level=logging.NOTSET):
self.success = True
return super().__init__(name, level)
def error(self, msg, *args, **kwargs):
self.success = False
return super().error(msg, *args, **kwargs)
@classmethod
def get_logger(cls, path):
logging.setLoggerClass(SVGLogger)
logging.basicConfig(level=logging.DEBUG)
return logging.getLogger(path)
def check_size(root):
width = float(root.attrib['width'])
height = float(root.attrib['height'])
if not 400 < width < 500:
logger.error("Width is outside of range: {}".format(width))
if not 400 < height < 500:
logger.error("Height is outside of range: {}".format(height))
def check_layers(root):
"""
Check there are layers (well, groups) for the components we require.
"""
layer_ids = [g.attrib['id'] for g in root.iterfind('svg:g', ns)]
for layer in ["Device", "Buttons", "LEDs"]:
if layer not in layer_ids:
logger.error("Missing layer: {}".format(layer))
def check_elements(root, prefix, required=0):
"""
Checks for elements of the form 'prefixN' in the root tag. Any elements
found must be consecutive or an error is printed, i.e. if there's a
'button8' there has to be a 'button7'.
If required is nonzero, an error is logged for any missing element with
an index less than required.
"""
# elements can be paths and rects
# This includes leaders and lines
element_ids = [p.attrib['id'] for p in root.xpath('//svg:path', namespaces=ns) if p.attrib['id'].startswith(prefix)]
element_ids += [p.attrib['id'] for p in root.xpath('//svg:rect', namespaces=ns) if p.attrib['id'].startswith(prefix)]
element_ids += [g.attrib['id'] for g in root.xpath('//svg:g', namespaces=ns) if g.attrib['id'].startswith(prefix)]
idx = 0
highest = -1
for idx in range(0, 20):
e = '{}{}'.format(prefix, idx)
if e in element_ids:
highest = idx
previous = '{}{}'.format(prefix, idx - 1)
if idx > 0 and previous not in element_ids:
logger.error("Non-consecutive {}: {}".format(prefix, e))
leader = '{}{}-leader'.format(prefix, idx)
if leader not in element_ids:
logger.error("Missing {} for {}".format(leader, e))
else:
element = root.xpath(style_query.format(leader, 'text-align'), namespaces=ns)
if element is None or len(element) != 1 or element[0] is None:
logger.error("Missing style property for {}".format(leader))
path = '{}{}-path'.format(prefix, idx)
if path not in element_ids:
logger.error("Missing {} for {}".format(path, e))
elif idx < required:
logger.error("Missing {}: {}".format(prefix, e))
logger.info("Found {} {}s".format(highest + 1, prefix))
def check_leds(root):
check_elements(root, "led")
def check_buttons(root):
check_elements(root, "button", 3)
def check_svg(path):
svg = etree.parse(path)
root = svg.getroot()
check_size(root)
check_layers(root)
check_buttons(root)
check_leds(root)
if __name__ == "__main__":
success = True
for path in sys.argv[1:]:
logger = SVGLogger.get_logger(path)
check_svg(path)
if not logger.success:
success = False
if not success:
sys.exit(1)

506
data/gnome/fallback.svg Normal file
View File

@@ -0,0 +1,506 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="450"
height="450"
viewBox="0 0 450.00001 450.00001"
id="svg4844"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="fallback.svg">
<defs
id="defs4846" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="162.83067"
inkscape:cy="254.74718"
inkscape:document-units="px"
inkscape:current-layer="Device"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1101"
inkscape:window-x="1920"
inkscape:window-y="28"
inkscape:window-maximized="1"
showguides="true"
inkscape:guide-bbox="true">
<sodipodi:guide
position="20.203051,215.1625"
orientation="1,0"
id="guide6533" />
<sodipodi:guide
position="263.64982,40.406102"
orientation="0,1"
id="guide6535" />
<sodipodi:guide
position="119.198,430.32499"
orientation="0,1"
id="guide6537" />
</sodipodi:namedview>
<metadata
id="metadata4849">
<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="Device"
inkscape:label="Device"
style="display:inline">
<g
id="mouse"
transform="matrix(1.4157338,0,0,1.4150394,-15.907361,-83.397825)"
inkscape:label="#g5682">
<path
sodipodi:nodetypes="csssssscssssccccccccsccccscccccccc"
inkscape:connector-curvature="0"
id="path5554"
d="m 107.18942,257.50398 c -4.99714,2.43535 -12.465604,2.63664 -17.258052,-1.44306 -13.182502,-11.22196 -9.97511,-20.42755 -11.852724,-27.60442 -2.086919,-7.9769 -8.394971,-14.22057 -14.204755,-19.22015 -2.452298,-2.11032 -15.074169,-8.92571 -22.581405,-6.39459 -7.284157,2.45591 -8.927109,7.96865 -7.039536,13.91417 0.942265,2.96796 6.341731,12.02153 3.965931,13.71887 -6.197029,4.42735 -6.062903,-6.5818 -10.547143,-6.17694 -3.155437,2.60598 5.722132,16.20922 13.501397,8.54975 5.287042,-5.20562 -7.079343,-19.36707 -3.390295,-23.44484 9.484404,-10.48379 25.055087,3.37163 28.181213,6.1766 3.122791,2.80198 9.248496,8.52414 9.875553,20.12323 0.843858,15.60941 10.74282,34.96801 34.650426,29.65168 5.06065,14.01778 13.49867,22.70826 23.95424,24.35573 -0.73268,4.29744 8.14899,6.47132 -4.44275,21.19745 3.06561,2.85685 8.01758,11.19489 5.36011,25.42154 -5.7187,3.96316 -16.72683,16.45412 0.58015,5.39321 7.47117,-0.31027 14.12258,9.86529 24.83436,3.31115 -4.03605,-1.39212 -8.28293,-7.96174 -18.1528,-9.13334 -0.39969,-10.26297 -0.36143,-17.7796 5.94065,-24.85378 14.19011,-17.96422 10.92982,-12.10811 13.41925,-19.23937 2.48943,-7.13126 2.52109,-11.34 2.33441,-19.25558 -2.9569,-16.18074 -2.70058,-16.2775 -0.11605,-1.49727 16.61294,-0.76638 32.95879,-6.2945 39.23502,-17.29952 9.79039,-1.32985 22.51147,-7.39605 29.90276,0.56675 -5.22785,1.2715 -19.1955,26.95192 0.95314,7.4515 11.02869,-10.67388 23.99028,5.40711 27.28604,-12.1447 -31.80018,1.43261 -37.30275,-17.43649 -56.67811,-17.25445 -7.30408,-0.27416 -20.44309,-0.17002 -25.52504,9.24102 9.11653,-12.47147 0.88952,-27.94431 -1.51863,-41.85017 -15.02015,-21.13729 -30.18152,-29.86148 -43.79923,-20.54814 -13.65771,11.51411 -22.41864,26.83759 -23.4676,44.34506 -1.03036,11.6519 -3.29737,12.1189 -3.40046,33.94261 z"
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:2.11956239;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="zcsccczzczz"
inkscape:connector-curvature="0"
id="button6"
d="m 125.00072,147.3762 c -3.94854,2.46476 -1.91248,4.22271 -6.56842,3.47664 -6.04044,-0.49004 -12.10893,4.61986 -13.01838,8.83118 -1.05538,4.887 0.82228,9.01123 6.09563,9.60013 4.20528,0.52222 4.02895,-1.99743 6.87568,-2.5868 4.10235,6.95899 19.61805,11.70942 26.38361,11.76956 4.71763,-0.6919 4.81094,-3.03006 11.87015,-12.70472 7.05921,-9.67466 6.92045,-10.39092 6.98781,-15.5586 0.0674,-5.16768 5.35659,-7.43289 0.64282,-10.74954 -1.91632,-0.43449 -6.15926,0.21143 -17.24225,0.33896 -11.08299,0.12753 -18.07811,5.11843 -22.02665,7.58319 z"
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.11956239;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path5399" />
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path5405"
d="m 163.20277,150.23723 c -1.06521,-0.0557 -3.7617,0.42888 -4.65914,1.09722 -2,1.49899 -2.78079,2.58243 -4.28466,4.28408"
style="fill:none;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:0.70652068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="zszzcz"
inkscape:connector-curvature="0"
id="path5414"
d="m 140.75099,145.17797 c -1.18229,0.40938 -2.17328,0.089 -3.548,0.54388 -1.29024,0.42688 -0.38888,1.67372 0.70446,2.31451 1.09334,0.64079 4.0134,-0.29332 5.06491,-1.10649 1.05151,-0.81317 1.6576,-2.35673 1.26243,-2.48934 -0.39517,-0.13262 -2.30151,0.32805 -3.4838,0.73744 z"
style="fill:none;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:0.70652068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="cscccccccccc"
inkscape:connector-curvature="0"
id="button4"
d="m 78.728764,157.07155 c 2.729308,11.45367 -4.592785,23.20521 4.683418,32.67799 4.193755,4.28262 13.716552,5.67648 28.077598,4.49959 -16.964136,7.07876 -15.435974,11.82485 -18.038124,14.76602 -0.367398,1.93959 3.51961,2.8971 16.874634,15.52043 5.69621,-9.78547 8.32004,-20.52393 15.45362,-28.52298 15.91171,-4.8206 -2.52533,-22.7944 -2.52533,-22.7944 0,0 -6.14675,-4.9701 -9.92352,3.09571 -7.99103,-6.69016 -16.706109,-2.41957 -17.256932,4.64391 1.356748,-6.41288 0.152959,-21.95234 -2.71781,-27.4956 -9.538417,1.9887 -10.559123,3.86954 -14.594684,3.59772 -0.398494,-4.23259 11.719989,-4.91038 14.599568,-3.59704"
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:2.11956239;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path5425" />
<path
sodipodi:nodetypes="sscscccs"
inkscape:connector-curvature="0"
id="path5459"
d="m 170.57402,194.85052 c 1.27147,3.99383 5.43056,12.02629 7.61859,15.67135 1.67031,2.78261 3.12076,3.57306 4.37974,10.16536 3.35106,-2.36328 2.26625,-4.37687 7.86716,-6.63393 6.78116,-2.73268 10.74994,-17.14078 7.59325,-20.21486 -2.56921,-1.4377 -13.90652,-7.54721 -16.5961,-8.75778 -8.64652,-2.75344 -18.11595,-5.26303 -26.29721,-3.34848 9.47658,1.68644 14.2189,9.2998 15.43457,13.11834 z"
style="fill:#babdb6;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.11956239;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccczccszccccc"
inkscape:connector-curvature="0"
id="button5"
d="m 156.57364,174.60716 c 0.61451,3.45259 -4.17141,8.40305 2.31606,7.85954 11.99907,4.07348 25.62973,4.1469 36.30113,11.66165 7.04625,4.94297 4.95335,13.00607 7.04625,4.94297 1.1521,-9.09961 -1.67322,-15.30143 -7.75899,-16.73475 -6.08578,-1.43332 -8.13701,-3.55535 -13.61967,-4.14262 -2.80249,-1.72124 -8.8609,-0.0129 -18.78998,0.48317 7.50071,-0.84183 14.47216,-0.73196 18.56074,-2.80565 0,0 6.30334,-9.54951 7.53958,-14.37709 1.23624,-4.82758 1.50074,-6.5184 3.27709,-10.38873 -4.42899,-1.01319 -7.07586,-3.6542 -10.44975,-4.61991 -2.18062,4.20089 -3.52881,8.12787 -6.83587,11.50939 -4.41968,6.55835 -8.29193,5.97237 -14.60181,12.79024 -0.89825,2.92739 -2.98478,3.82179 -2.98478,3.82179 z"
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.11956239;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path5453" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="button2"
d="m 129.85315,182.50093 c 3.6135,2.58802 10.74551,5.75132 14.60758,1.93282 0.45507,-2.97455 -0.19296,-6.87423 -0.19285,-7.3212 -7.27257,-0.13193 -16.12143,-4.19401 -22.95494,-6.35683 1.4347,5.55248 6.71272,10.53825 8.54021,11.74521 z"
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:2.11956239;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path5540" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="button3"
d="m 147.13503,177.00627 c 1.16519,3.28142 3.63994,3.84875 5.91906,6.18906 4.77775,-2.47336 5.12452,-13.43108 3.9723,-16.57185 -3.79251,3.28442 -5.96683,8.30504 -9.89136,10.38279 z"
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:2.11956239;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path5547" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="button7"
d="m 96.280703,132.06305 c 0,0 58.294677,-1.61611 100.929457,-0.83206 -1.27875,-12.91598 2.4481,-38.633128 2.05831,-56.889372 -46.40187,1.475128 -77.8249,2.200365 -105.250613,1.551238 4.495692,30.284264 2.262846,56.170194 2.262846,56.170194 z"
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:2.11956239;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path5566" />
<path
sodipodi:nodetypes="cscsccsccscsccccc"
inkscape:connector-curvature="0"
id="button0"
d="m 82.544643,155.53571 c 0.305172,-3.31535 0.980416,-11.89491 2.489066,-14.57767 2.810136,-4.99714 6.565433,-8.72285 8.454552,-11.91369 2.686043,-3.2364 2.701225,-4.20604 4.184853,-5.44313 1.47935,-1.23353 7.182396,-9.27661 8.097236,-8.77569 1.51464,3.42922 -5.03984,11.03401 -6.93589,13.34978 2.62462,-2.24644 4.10033,-4.85435 7.49772,-7.85092 3.00882,-2.65384 6.26227,-2.5427 3.72007,0.68937 -2.64048,3.02394 -3.55215,4.11719 -8.76065,8.25327 10.88502,-8.17285 19.09259,-9.06063 9.30462,-2.01748 -1.40825,1.01334 -8.4791,4.08043 -5.58663,2.88614 2.72294,-0.18651 10.84559,-2.68876 10.05588,-0.49162 -1.65227,4.59693 -8.23017,2.61804 -12.09164,4.12386 -2.17312,2.61683 -5.158266,3.64905 -7.879445,5.87814 -1.853363,2.41097 -3.914211,8.13746 -5.08138,13.723 -0.977626,0.29825 -5.450462,1.56098 -7.468362,2.16661 z"
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:1.41304147;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path5561" />
<text
sodipodi:linespacing="100%"
id="text5583"
y="117.5"
x="110.17857"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif Italic';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#bfc2bb;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Italic';fill:#bfc2bb;fill-opacity:1"
y="117.5"
x="110.17857"
id="tspan5585"
sodipodi:role="line">404</tspan></text>
<path
sodipodi:nodetypes="cccccsscssccccc"
inkscape:connector-curvature="0"
id="button1"
d="m 183.97321,147.27678 c 3.33335,-1.48288 3.09826,-6.77076 3.85156,-9.86053 -0.35674,-3.91061 0.37686,-5.05433 -1.93804,-6.90092 -2.10346,-1.08198 -0.13041,-3.85244 1.93457,-2.9861 2.92925,1.60616 0.94495,6.22838 4.12041,7.61394 0,0 1.87775,-1.15731 1.99206,-3.40725 0.1301,-2.56065 -1.15427,-11.79601 1.03251,-13.08991 0.96059,-0.56838 1.75458,9.82486 2.96125,11.86818 -0.30586,-2.13132 -0.65718,-12.83967 0.19291,-12.16594 3.65618,2.89768 1.37042,11.98208 3.4547,14.61683 1.72735,2.18354 -3.64795,6.53893 -6.93051,11.03314 -2.12109,2.77912 -2.89583,2.76412 -4.9476,6.04875 -2.40971,-1.47777 -3.69229,-1.95944 -5.72384,-2.77019 l 0,0 z"
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:1.41304147;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path5587" />
<path
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:3.02452755;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 129.71002,288.41558 c -7.1173,2.84692 -18.1026,3.61499 -24.6415,-2.32569 C 86.783262,269.47748 90.830889,256.94793 88.150941,246.7094 85.172248,235.32955 76.168664,226.42233 67.876267,219.28992 64.376063,216.27934 46.360664,206.5565 35.645466,210.16739 25.248672,213.671 22.903661,221.53548 25.597824,230.01736 c 1.34491,4.23409 9.051654,17.14992 5.660636,19.57135 -8.845118,6.31606 -8.653678,-9.3896 -15.054105,-8.81203 -4.503806,3.71769 8.16729,23.12409 19.27076,12.19708 7.546279,-7.42634 -10.10446,-27.62907 -4.839023,-33.44642 13.537242,-14.95619 35.761527,4.80997 40.223496,8.81154 4.45721,3.9973 13.200527,12.16054 14.095535,28.70781 1.204452,22.2684 15.333397,49.88539 49.444437,42.2623 -0.73591,-2.53682 -4.15602,-6.43263 -4.68954,-10.89341 z"
id="led0"
transform="matrix(0.7006157,0,0,0.70096699,16.318711,55.521354)"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csssssscsssscc"
inkscape:label="#path6431" />
<path
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:3.02452755;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 169.91186,400.60038 c -8.16239,5.65385 -23.87447,23.47346 0.82806,7.69396 10.66372,-0.44263 20.15739,14.07383 35.44648,4.72369 -5.76072,-1.986 -11.82236,-11.35822 -25.88738,-13.10742 -3.01274,0.0336 -8.44501,-0.32858 -10.38716,0.68977 z"
id="path6437"
transform="matrix(0.7006157,0,0,0.70096699,16.318711,55.521354)"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
<path
inkscape:label="#path6478"
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:3.00000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 175.72866,392.52094 c -8.09615,5.60803 -23.68073,23.28322 0.82135,7.6316 10.57718,-0.43903 19.9938,13.95978 35.15883,4.68542 -5.71397,-1.96992 -11.72643,-11.26617 -25.67731,-13.0012 -2.98831,0.0334 -8.37648,-0.32592 -10.30287,0.68418 z"
id="led1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
sodipodi:nodetypes="csccc"
inkscape:connector-curvature="0"
id="led2"
d="m 314.43906,276.47676 c -7.40123,1.79921 -27.17571,38.13802 1.3494,10.54417 15.61369,-15.10398 33.96384,7.65126 38.62976,-17.18524 -11.69141,0.52644 -20.87204,-0.88448 -28.52139,-3.25631 -0.91845,1.9989 -7.62356,10.08149 -11.45777,9.89738 z"
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:3.00000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
style="display:inline">
<g
style="display:inline"
id="button1-path"
inkscape:label="#g131"
transform="translate(12.063466,68.669278)">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 282.94764,50.505764 154.05236,0"
id="path958"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect968"
width="6.999999"
height="6.999999"
x="277"
y="47.005764" />
<rect
inkscape:label="#rect867"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button1-leader"
width="1"
height="1"
x="437"
y="50.005764" />
</g>
<g
transform="translate(12.063466,28.669278)"
inkscape:label="#g131"
id="button0-path"
style="display:inline">
<path
inkscape:connector-curvature="0"
id="path5628"
d="m 282.94764,50.505764 154.05236,0"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
sodipodi:nodetypes="cc" />
<rect
y="47.005764"
x="277"
height="6.999999"
width="6.999999"
id="rect5630"
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
y="50.005764"
x="437"
height="1"
width="1"
id="button0-leader"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect867" />
</g>
<g
transform="translate(12.063466,148.66928)"
inkscape:label="#g131"
id="button3-path"
style="display:inline">
<path
inkscape:connector-curvature="0"
id="path5636"
d="m 282.94764,50.505764 154.05236,0"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
sodipodi:nodetypes="cc" />
<rect
y="47.005764"
x="277"
height="6.999999"
width="6.999999"
id="rect5638"
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
y="50.005764"
x="437"
height="1"
width="1"
id="button3-leader"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect867" />
</g>
<g
style="display:inline"
id="button2-path"
inkscape:label="#g131"
transform="translate(12.063466,108.66928)">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 282.94764,50.505764 154.05236,0"
id="path5644"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect5646"
width="6.999999"
height="6.999999"
x="277"
y="47.005764" />
<rect
inkscape:label="#rect867"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button2-leader"
width="1"
height="1"
x="437"
y="50.005764" />
</g>
<g
transform="translate(12.063466,228.66928)"
inkscape:label="#g131"
id="button5-path"
style="display:inline">
<path
inkscape:connector-curvature="0"
id="path5652"
d="m 282.94764,50.505764 154.05236,0"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
sodipodi:nodetypes="cc" />
<rect
y="47.005764"
x="277"
height="6.999999"
width="6.999999"
id="rect5654"
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
y="50.005764"
x="437"
height="1"
width="1"
id="button5-leader"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect867" />
</g>
<g
style="display:inline"
id="button4-path"
inkscape:label="#g131"
transform="translate(12.063466,188.66928)">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 282.94764,50.505764 154.05236,0"
id="path5660"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect5662"
width="6.999999"
height="6.999999"
x="277"
y="47.005764" />
<rect
inkscape:label="#rect867"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button4-leader"
width="1"
height="1"
x="437"
y="50.005764" />
</g>
<g
style="display:inline"
id="button7-path"
inkscape:label="#g131"
transform="translate(12.063466,308.66928)">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 282.94764,50.505764 154.05236,0"
id="path5668"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect5670"
width="6.999999"
height="6.999999"
x="277"
y="47.005764" />
<rect
inkscape:label="#rect867"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button7-leader"
width="1"
height="1"
x="437"
y="50.005764" />
</g>
<g
transform="translate(12.063466,268.66928)"
inkscape:label="#g131"
id="button6-path"
style="display:inline">
<path
inkscape:connector-curvature="0"
id="path5676"
d="m 282.94764,50.505764 154.05236,0"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
sodipodi:nodetypes="cc" />
<rect
y="47.005764"
x="277"
height="6.999999"
width="6.999999"
id="rect5678"
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
y="50.005764"
x="437"
height="1"
width="1"
id="button6-leader"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect867" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs">
<g
style="display:inline"
id="led0-path"
inkscape:label="#g131"
transform="translate(12.063466,28.669278)">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 282.94764,50.505764 154.05236,0"
id="path5785"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect5787"
width="6.999999"
height="6.999999"
x="277"
y="47.005764" />
<rect
inkscape:label="#rect867"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="led0-leader"
width="1"
height="1"
x="437"
y="50.005764" />
</g>
<g
transform="translate(12.063466,68.669278)"
inkscape:label="#g131"
id="led1-path"
style="display:inline">
<path
inkscape:connector-curvature="0"
id="path5797"
d="m 282.94764,50.505764 154.05236,0"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
sodipodi:nodetypes="cc" />
<rect
y="47.005764"
x="277"
height="6.999999"
width="6.999999"
id="rect5799"
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
y="50.005764"
x="437"
height="1"
width="1"
id="led1-leader"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect867" />
</g>
<g
style="display:inline"
id="led2-path"
inkscape:label="#g131"
transform="translate(12.063466,108.66928)">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 282.94764,50.505764 154.05236,0"
id="path6411"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect6413"
width="6.999999"
height="6.999999"
x="277"
y="47.005764" />
<rect
inkscape:label="#rect867"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="led2-leader"
width="1"
height="1"
x="437"
y="50.005764" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,514 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="490"
height="490"
viewBox="0 0 129.64583 129.64583"
version="1.1"
id="svg8"
sodipodi:docname="logitech-g300.svg"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4142136"
inkscape:cx="202.78034"
inkscape:cy="279.63686"
inkscape:document-units="px"
inkscape:current-layer="LEDs"
inkscape:document-rotation="0"
showgrid="false"
showguides="false"
inkscape:window-width="1920"
inkscape:window-height="1163"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
showborder="true"
inkscape:guide-bbox="true"
units="px"
inkscape:snap-global="false"
inkscape:measure-start="200.26,379.645"
inkscape:measure-end="199.294,334.274">
<inkscape:grid
type="xygrid"
id="grid78" />
<sodipodi:guide
position="58.722772,112.40709"
orientation="0,1"
id="guide79"
inkscape:locked="false" />
<sodipodi:guide
position="39.611923,100.54373"
orientation="0,1"
id="guide81"
inkscape:locked="false" />
<sodipodi:guide
position="51.756014,88.614617"
orientation="0,1"
id="guide83"
inkscape:locked="false" />
<sodipodi:guide
position="57.778386,76.729167"
orientation="0,1"
id="guide85"
inkscape:locked="false" />
<sodipodi:guide
position="45.047078,64.795712"
orientation="0,1"
inkscape:locked="false"
id="guide87" />
<sodipodi:guide
position="57.646696,52.899321"
orientation="0,1"
id="guide89"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="Device"
inkscape:label="Device"
style="display:inline;opacity:1"
transform="translate(0,-167.35416)">
<path
id="path41-6"
style="display:inline;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:0.98645592;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 64.822903,179.22931 h -3.479995 l -3.915,-6.09 c 0,0 -11.305381,-0.13059 -17.399991,5.22 0,0 -8.990317,5.04814 -8.69995,26.96998 0,0 -0.262683,13.22939 2.609964,26.96999 -8.15696,17.08865 -6.959951,19.57498 -6.959951,19.57498 3.486257,28.75756 24.359929,38.27998 24.359929,38.27998 4.318847,1.41447 22.65114,1.41447 26.969986,0 0,0 20.873694,-9.52242 24.359955,-38.27998 0,0 1.19699,-2.48633 -6.959957,-19.57498 2.87265,-13.7406 2.609996,-26.96999 2.609996,-26.96999 0.290352,-21.92184 -8.700007,-26.96998 -8.700007,-26.96998 -6.094602,-5.35059 -17.399985,-5.22 -17.399985,-5.22 l -3.914995,6.09 h -3.479999"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccccc" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.61653525;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 55.252905,176.68083 4.35,6.95997 h 10.439993 l 4.349999,-6.95997 c 0,0 4.713304,-0.60703 13.049968,4.34999 0,0 5.4795,2.46746 6.960015,18.26998 0,0 0.682398,10.45737 -0.869997,20.88 -6.631477,30.71466 -9.233113,35.45823 -12.179994,44.36996 -0.0055,0.3105 -0.606568,3.30574 -2.609998,12.17998 0,0 -1.945797,7.04942 -13.919989,6.96001 -13.44865,0.0748 -13.919991,-6.96001 -13.919991,-6.96001 -1.238296,-8.85308 -14.879861,-47.03985 -14.789987,-56.54994 0,0 -1.799981,-15.15662 -0.869993,-20.88 0,0 1.16252,-13.31705 6.089986,-18.26998 8.388589,-5.24851 13.919988,-4.34999 13.919988,-4.34999 z"
id="path4607"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccc" />
<path
style="fill:none;stroke:#babdb6;stroke-width:0.61653525;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 64.822903,183.6408 c -0.06118,25.29724 0,55.68 0,55.68 v 0"
id="path4530"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="display:inline;fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61653525;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 93.967914,196.69083 c 0,0 -3.141541,-12.93515 -4.350032,-13.05003 -2.701088,1.93408 -2.614239,2.22922 -4.349992,4.35002 2.394025,7.89236 3.479995,9.57001 3.479995,9.57001 z"
id="button6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#path4541-3" />
<rect
style="fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61653525;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="button2"
width="8.6999846"
height="14.789989"
x="60.472919"
y="189.29581"
ry="3.9149992"
inkscape:label="#rect4615" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.61653525;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 44.286958,270.37954 c -1.184921,4.53183 -5.479801,5.02739 -9.331541,1.1532 -3.062052,-4.76909 -6.934437,-16.17092 -6.816123,-19.86098 0.118269,-3.69003 1.372851,-5.59065 5.281209,-14.91112 1.954179,-4.66034 1.9368,-3.20903 2.958075,0.49871 1.190396,4.32175 9.093304,28.58842 7.90838,33.12019 z"
id="path4617"
inkscape:connector-curvature="0"
sodipodi:nodetypes="zczssz" />
<path
sodipodi:type="arc"
style="display:inline;fill:none;fill-opacity:0.39215686;stroke:#babdb6;stroke-width:2.08971024;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="led0-5"
sodipodi:cx="64.773148"
sodipodi:cy="257.64056"
sodipodi:rx="3.7614744"
sodipodi:ry="3.7614779"
d="m 67.432912,254.9808 a 3.7614744,3.7614779 0 0 1 0,5.31953 3.7614744,3.7614779 0 0 1 -5.319528,0 3.7614744,3.7614779 0 0 1 -1e-6,-5.31953"
sodipodi:start="5.4977871"
sodipodi:end="3.9269907"
sodipodi:open="true"
sodipodi:arc-type="arc" />
<path
style="display:inline;opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.61653525;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 85.335258,270.71558 c 1.18491,4.53186 5.479785,5.02738 9.331555,1.15324 3.062033,-4.76913 6.934417,-16.17102 6.816127,-19.86101 -0.11829,-3.69003 -1.37286,-5.59071 -5.281228,-14.91121 -1.95418,-4.66025 -1.936795,-3.20892 -2.95807,0.49877 -1.190396,4.32176 -9.093291,28.58839 -7.908384,33.12021 z"
id="path4617-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="zczssz" />
<path
style="display:inline;opacity:1;fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61653525;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 93.967914,214.09081 c 0.815935,-16.75448 0,-17.39998 0,-17.39998 l -5.220029,0.87 c 0,0 0.239252,0.36327 -0.870008,12.17998 6.651607,6.71191 6.090037,4.35 6.090037,4.35 z"
id="button5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#path4541-3-3" />
<path
style="display:inline;opacity:1;fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61653525;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 35.737567,196.69083 c 0,0 3.141541,-12.93515 4.350032,-13.05003 2.701088,1.93408 2.614239,2.22922 4.349992,4.35002 -2.394025,7.89236 -3.479995,9.57001 -3.479995,9.57001 z"
id="button4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#path4541-3-5" />
<path
style="display:inline;opacity:1;fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61653525;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 35.737567,214.09081 c -0.815935,-16.75448 0,-17.39998 0,-17.39998 l 5.220029,0.87 c 0,0 -0.239252,0.36327 0.870008,12.17998 -6.651607,6.71191 -6.090037,4.35 -6.090037,4.35 z"
id="button3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#path4541-3-3-6" />
<path
style="display:inline;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:0.61653525;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 60.426305,217.35644 v -6.74249 c -0.01103,-1.73802 1.304999,-1.74 1.304999,-1.74 h 6.089995 c 0,0 1.352705,0.0157 1.304999,1.74 v 6.74249 z"
id="button8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:label="#path4589-9" />
<path
style="display:inline;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:0.61653525;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 60.426305,217.35644 v 6.74249 c -0.01103,1.73802 1.304999,1.74 1.304999,1.74 H 67.8213 c 0,0 1.352705,-0.0157 1.304998,-1.74 v -6.74249 z"
id="button7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:label="#path4589-9-1" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 46.143947,189.21374 -3.642001,8.50625 1.101002,10.95189 16.221334,-7.70159 -0.921076,-16.08486 z"
id="button0"
inkscape:connector-curvature="0"
inkscape:label="#path3664"
sodipodi:nodetypes="cccccc" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 70.954987,185.09032 -1.109402,16.05391 16.940367,7.82608 0.477526,-11.0865 -3.352352,-9.32016 z"
id="button1"
inkscape:connector-curvature="0"
inkscape:label="#path4486"
sodipodi:nodetypes="cccccc" />
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
transform="translate(0,-2.6458338)"
style="display:inline">
<g
transform="matrix(-0.28471359,0,0,-0.26458333,70.320391,74.101432)"
style="display:inline;opacity:1"
id="button4-path"
inkscape:label="#g151">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path964"
d="m 109.31232,181.51438 38.69266,43.39973 h 98.98144"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
inkscape:label="#rect875"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button4-leader"
width="1"
height="1"
x="245.98642"
y="223.91411" />
<rect
y="177"
x="105"
height="6.999999"
width="6.999999"
id="rect974"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
</g>
<g
transform="matrix(-0.30394757,0,0,0.26458333,100.78231,-2.4556928)"
style="display:inline"
id="button0-path"
inkscape:label="#g141">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path960"
d="m 331.57794,109.27355 -66.76739,-0.0347 -34.51493,21.26646 h -68.95164"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
y="127.00576"
x="157"
height="6.999999"
width="6.999999"
id="rect970"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
inkscape:label="#rect871"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button0-leader"
width="1"
height="1"
x="330.57794"
y="108.28136" />
</g>
<g
transform="matrix(-0.29294531,0,0,0.26458333,70.19679,-27.761975)"
style="display:inline"
id="button3-path"
inkscape:label="#g156">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 114.17805,250.50576 125.44617,0.0352"
id="path966"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect976"
width="6.999999"
height="6.999999"
x="108"
y="247.00577" />
<rect
y="250.00575"
x="238.62422"
height="1"
width="1"
id="button3-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect875" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,40.502648,-6.6313911)"
style="display:inline"
id="button5-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 336.91912,170.14213 -143.57514,0.36405"
id="path51"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53"
width="6.999999"
height="6.999999"
x="189"
y="167" />
<rect
y="169.75269"
x="336.91913"
height="1"
width="1"
id="button5-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(-0.2847136,0,0,0.26458333,87.493539,-7.2646706)"
style="display:inline;opacity:1"
id="button2-path"
inkscape:label="#g151">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path964-6"
d="m 79.210952,148.6933 62.691188,68.76371 165.40155,1e-5"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
inkscape:label="#rect875"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button2-leader"
width="1"
height="1"
x="306.30368"
y="216.45702" />
<rect
y="145.19263"
x="76.187187"
height="6.999999"
width="6.999999"
id="rect974-5"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,13.746167,11.443545)"
style="display:inline"
id="button7-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 438.04598,191.7488 H 219.48839 l -26.14441,-21.24262"
id="path51-1-9-6-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-2-3-2-7"
width="6.999999"
height="6.999999"
x="189"
y="167" />
<rect
y="191.3428"
x="438.04599"
height="1"
width="1"
id="button7-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,13.746167,3.106915)"
style="display:inline"
id="button8-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 438.04598,178.25732 H 203.75439 l -10.41041,-7.75114"
id="path51-1-9-6-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-2-3-2-2"
width="6.999999"
height="6.999999"
x="189"
y="167" />
<rect
y="177.79317"
x="438.04599"
height="1"
width="1"
id="button8-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(0.2847136,0,0,-0.26458333,59.129968,74.101431)"
style="display:inline;opacity:1"
id="button6-path"
inkscape:label="#g151">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path964-67"
d="m 109.31232,181.51438 38.69266,43.39973 h 99.66799"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
inkscape:label="#rect875"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button6-leader"
width="1"
height="1"
x="247.67297"
y="223.9144" />
<rect
y="177"
x="105"
height="6.999999"
width="6.999999"
id="rect974-3"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
</g>
<g
transform="matrix(0.30394757,0,0,0.26458333,28.636101,-2.4556931)"
style="display:inline"
id="button1-path"
inkscape:label="#g141">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path960-6"
d="m 332.32616,109.27355 -67.51561,-0.0347 -34.51493,21.26646 h -68.95164"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
y="127.00576"
x="157"
height="6.999999"
width="6.999999"
id="rect970-2"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
inkscape:label="#rect871"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button1-leader"
width="1"
height="1"
x="332.32617"
y="109.27355" />
</g>
<g
id="led0"
inkscape:label="#g4411"
style="fill:#eeee35;fill-opacity:1">
<g
id="g4457">
<path
id="led0-0"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:0.28991818;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 36.851726,81.915105 C 41.99239,103.21594 40.604019,101.71476 43.62439,106.21567 44.61563,98.208963 38.156084,80.727537 35.152327,69.797094 Z"
inkscape:label="#path5630"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
id="led0-1"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:0.28991818;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 93.039402,81.561768 C 87.898738,102.8626 89.287109,101.36142 86.266738,105.86233 85.275498,97.855627 91.735044,80.3742 94.738801,69.443757 Z"
inkscape:label="#path5630"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
</g>
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
transform="translate(0,-2.6458338)"
style="display:inline">
<g
style="display:inline"
id="led0-path"
inkscape:label="#g161"
transform="matrix(0.26458333,0,0,0.26458333,38.838972,19.46058)">
<path
inkscape:connector-curvature="0"
id="path877"
d="M 343.20703,290.50577 H 208.64438 l -18.97696,-18.97696"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
sodipodi:nodetypes="ccc" />
<rect
y="269.96875"
x="188.28125"
height="6.999999"
width="6.999999"
id="rect879"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
inkscape:label="#rect881"
y="290.00577"
x="343.20703"
height="1"
width="1"
id="led0-leader"
style="color:#000000;text-align:start;display:inline;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -10,7 +10,7 @@
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="467.83105"
height="398.87393"
height="400.87393"
viewBox="0 0 467.83105 398.87393"
version="1.1"
id="svg8"
@@ -296,7 +296,7 @@
height="1"
width="1"
id="button2-leader"
style="color:#000000;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
</g>
<g
id="button6-path"

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1 @@
logitech-g403.svg

441
data/gnome/logitech-g9.svg Normal file
View File

@@ -0,0 +1,441 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="490"
height="490"
viewBox="0 0 129.64583 129.64583"
version="1.1"
id="svg8"
sodipodi:docname="G9.svg"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
enable-background="new">
<defs
id="defs2">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath145">
<rect
id="rect147"
width="82.020836"
height="120.65"
x="20.902082"
y="3.174999"
style="stroke-width:0.26458332" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4142136"
inkscape:cx="108.9024"
inkscape:cy="270.58583"
inkscape:document-units="px"
inkscape:current-layer="LEDs"
inkscape:document-rotation="0"
showgrid="false"
showguides="false"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
showborder="true"
inkscape:guide-bbox="true"
units="px"
inkscape:snap-global="false"
inkscape:measure-start="200.26,379.645"
inkscape:measure-end="199.294,334.274">
<inkscape:grid
type="xygrid"
id="grid78" />
<sodipodi:guide
position="58.722772,112.40709"
orientation="0,1"
id="guide79"
inkscape:locked="false" />
<sodipodi:guide
position="39.611923,100.54373"
orientation="0,1"
id="guide81"
inkscape:locked="false" />
<sodipodi:guide
position="51.756014,88.614617"
orientation="0,1"
id="guide83"
inkscape:locked="false" />
<sodipodi:guide
position="57.778386,76.729167"
orientation="0,1"
id="guide85"
inkscape:locked="false" />
<sodipodi:guide
position="45.047078,64.795712"
orientation="0,1"
inkscape:locked="false"
id="guide87" />
<sodipodi:guide
position="57.646696,52.899321"
orientation="0,1"
id="guide89"
inkscape:locked="false" />
<sodipodi:guide
position="64.822916,108.74375"
orientation="1,0"
id="guide141"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="Device"
inkscape:label="Device"
style="display:inline;opacity:1"
transform="translate(0,-167.35416)">
<path
style="fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 94.675437,179.69604 c 3.984039,23.55769 3.665039,48.23792 2.691228,71.79562 -2.08441,12.87379 -9.220213,24.06379 -17.197916,34.13125 -5.788734,2.89177 -12.325823,4.09974 -19.05,5.02708 -6.713477,-0.22178 -12.6786,-0.44356 -19.579166,-2.91041 -7.729571,-8.24539 -13.148669,-16.741 -17.727083,-25.4 -0.02339,-1.79858 0,-5.84224 0,-8.20209 -1.45391,-5.19812 -2.159466,-9.36726 -2.116667,-15.875 3.006557,-20.49321 9.287166,-40.14452 13.977522,-59.88937 2.143416,-0.99154 2.603034,-1.14118 4.278728,-1.75854 8.680505,-0.97549 16.051389,-1.76389 24.077083,-2.64584 h 7.14375 z"
id="path4643"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccc" />
<path
sodipodi:type="arc"
style="display:inline;opacity:0.72399998;fill:none;fill-opacity:0.39215686;stroke:#eeeeec;stroke-width:2.089679;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="logo"
sodipodi:cx="62.113438"
sodipodi:cy="277.58575"
sodipodi:rx="3.7614744"
sodipodi:ry="3.7614779"
d="m 64.773202,274.92599 a 3.7614744,3.7614779 0 0 1 0,5.31953 3.7614744,3.7614779 0 0 1 -5.319528,0 3.7614744,3.7614779 0 0 1 -10e-7,-5.31953"
sodipodi:start="5.4977871"
sodipodi:end="3.9269907"
sodipodi:open="true"
sodipodi:arc-type="arc"
inkscape:label="path" />
<path
style="fill:none;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 36.247916,178.59895 C 31.541511,203.86131 25.993208,228.84304 23.8125,254.66666"
id="path4820"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.61647916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 90.83155,179.22767 c 1.340802,7.60827 2.120337,15.21655 2.338607,22.82482 -0.06236,15.27891 -0.685992,30.65136 -2.15152,45.27546 -0.124725,1.27845 -0.997805,2.18271 -2.057974,2.71279 -9.011437,2.99341 -16.339078,5.7062 -27.595579,6.73519 -10.523737,-0.10917 -22.099849,-1.22387 -25.537604,-3.27405 -1.656483,-1.67598 -2.466318,-3.00581 -2.899876,-5.23849 -0.200352,-25.84328 2.343475,-44.91236 8.699625,-71.28078 7.577091,-0.93544 15.154181,-1.87089 22.731272,-2.80633 l 7.296459,0.0936 c 6.392197,1.6526 12.784393,3.30519 19.17659,4.95779 z"
id="path4822"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccc" />
<path
style="fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61647916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 34.237226,219.26465 30.09955,-1.86805 -0.492771,5.00179 c -0.904262,2.26065 -1.153714,2.27624 -3.554685,3.55468 -3.25846,1.27844 -3.710591,1.10694 -7.249684,1.44994 -6.485741,0.3274 -12.971481,0.65481 -19.457222,0.98221 z"
id="led0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:label="#path894" />
<path
style="fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61647916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 34.611402,214.21326 -0.467722,5.00462 16.18317,-1.07576 0.04677,-4.77076 z"
id="button3"
inkscape:connector-curvature="0"
inkscape:label="#path896" />
<path
style="fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61647916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 64.405272,212.43591 -14.066729,0.93544 0.02888,4.76741 13.98589,-0.84138 z"
id="button4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#path898" />
<path
style="fill:none;stroke:#babdb6;stroke-width:0.61647916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 64.624478,173.96874 -0.264583,38.43073 z"
id="path4589"
inkscape:connector-curvature="0" />
<rect
style="display:inline;opacity:1;fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.6744734;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="button2"
width="8.6420469"
height="17.819014"
x="64.999809"
y="187.58092"
ry="1.5417958"
inkscape:label="#rect4615"
rx="1.752861" />
<path
style="fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61647916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 24.274755,248.26339 c -0.124726,-9.60388 -0.342996,-10.78877 0.748354,-17.86696 0.576857,0.0935 1.153713,0.18709 1.73057,0.28063 -0.826308,5.86211 -1.652616,11.72422 -2.478924,17.58633 z"
id="button6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
inkscape:label="#path4606" />
<path
style="fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61647916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 27.104469,228.7594 c -0.561265,-0.0779 -1.122531,-0.15591 -1.683796,-0.23386 0.919852,-4.78635 2.120338,-8.77758 3.671614,-12.95589 -0.662606,4.39658 -1.325212,8.79317 -1.987818,13.18975 z"
id="button5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
inkscape:label="#path4608" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 41.814318,177.35679 -6.781966,36.38874 28.764883,-1.68379 0.467722,-37.41774 z"
id="button0"
inkscape:connector-curvature="0"
inkscape:label="#path4610" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 65.054426,174.53098 -0.09922,12.60078 9.128124,-0.0661 0.03307,18.88463 -9.293489,-0.13229 -0.03307,6.15156 27.78125,-0.85989 0.132291,-9.1612 -0.959114,-13.32838 -1.35599,-9.09506 -18.884635,-4.89479 z"
id="button1"
inkscape:connector-curvature="0"
inkscape:label="#path4612" />
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
transform="translate(0,-2.6458338)"
style="display:inline">
<g
transform="matrix(0.26458333,0,0,0.26458333,40.502648,-13.272558)"
style="display:inline"
id="button2-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 336.91912,170.14213 -225.57514,0.36405"
id="path51"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53"
width="6.999999"
height="6.999999"
x="107.41756"
y="166.91161" />
<rect
y="169.75269"
x="336.91913"
height="1"
width="1"
id="button2-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(-0.26458333,0,0,-0.26458333,116.08398,94.455601)"
style="display:inline"
id="button3-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 438.04598,191.7488 H 311.48839 l -34.64441,-29.86762"
id="path51-1-9-6-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-2-3-2-7"
width="6.999999"
height="6.999999"
x="273.625"
y="158.5" />
<rect
y="191.3428"
x="438.04599"
height="1"
width="1"
id="button3-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(-0.29294531,0,0,0.26458333,70.284218,-46.390214)"
style="display:inline"
id="button0-path"
inkscape:label="#g156">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 58.180682,250.50576 181.443538,0.0352"
id="path966-9"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect976-8"
width="6.999999"
height="6.999999"
x="55.471279"
y="247.18254" />
<rect
y="250.00575"
x="238.62422"
height="1"
width="1"
id="button0-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect875" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,40.402294,-25.12739)"
style="display:inline"
id="button1-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 336.91912,170.14213 -189.57514,0.36405"
id="path51-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-0"
width="6.999999"
height="6.999999"
x="143.59541"
y="166.59467" />
<rect
y="169.75269"
x="336.91913"
height="1"
width="1"
id="button1-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(0.26458333,0,0,-0.26458333,13.573867,94.421678)"
style="display:inline"
id="button4-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 438.04598,191.7488 H 193.48839 l -29.95691,-26.43012"
id="path51-1-9-6-8-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-2-3-2-7-4"
width="6.999999"
height="6.999999"
x="160.13437"
y="161.68198" />
<rect
y="191.3428"
x="438.04599"
height="1"
width="1"
id="button4-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(-0.26458333,0,0,-0.26458333,116.07548,106.30868)"
style="display:inline"
id="button5-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 438.04598,191.7488 H 359.48839 L 337.39512,173.19489"
id="path51-1-9-6-8-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-2-3-2-7-6"
width="6.999999"
height="6.999999"
x="333.90585"
y="169.28337" />
<rect
y="191.3428"
x="438.04599"
height="1"
width="1"
id="button5-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(-0.26458333,0,0,-0.26458333,116.05287,118.29899)"
style="display:inline"
id="button6-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 438.04598,191.7488 H 359.48839 L 344.11263,179.02852"
id="path51-1-9-6-8-7-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-2-3-2-7-6-5"
width="6.999999"
height="6.999999"
x="340.26981"
y="174.94023" />
<rect
y="191.3428"
x="438.04599"
height="1"
width="1"
id="button6-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
transform="translate(0,-2.6458338)"
style="display:inline">
<g
transform="matrix(0.26458333,0,0,-0.26458333,13.686363,102.7808)"
style="display:inline"
id="led0-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 438.04598,178.25732 H 151.75439 l -10.41041,-7.75114"
id="path51-1-9-6-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-2-3-2-2"
width="6.999999"
height="6.999999"
x="137.02765"
y="165.93933" />
<rect
y="177.79317"
x="438.04599"
height="1"
width="1"
id="led0-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,554 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="460"
height="440"
id="svg2"
version="1.1"
inkscape:version="0.92+devel unknown"
sodipodi:docname="logitech-g900.svg"
viewBox="0 0 460 440">
<style
id="style5346" />
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="333.51156"
inkscape:cy="187.93422"
inkscape:document-units="px"
inkscape:current-layer="Device"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1853"
inkscape:window-height="1016"
inkscape:window-x="1507"
inkscape:window-y="27"
inkscape:window-maximized="1"
units="px"
fit-margin-top="20"
fit-margin-left="-40"
fit-margin-right="-40"
fit-margin-bottom="20"
inkscape:document-rotation="0"
inkscape:pagecheckerboard="false">
<sodipodi:guide
orientation="0,1"
position="-115.05694,356.25"
id="guide5340"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
orientation="0,1"
position="-338.2349,318.75"
id="guide5342"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
orientation="0,1"
position="-411.98489,243.75"
id="guide5344"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
orientation="0,1"
position="-447.39711,281.25"
id="guide5346"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
orientation="0,1"
position="-458.85989,206.25"
id="guide5348"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
orientation="0,1"
position="-443.23489,168.75"
id="guide5350"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<inkscape:grid
type="xygrid"
id="grid5352"
empspacing="4"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
originx="0"
originy="0"
spacingx="0.93749999"
spacingy="0.93749999" />
<sodipodi:guide
orientation="0,1"
position="-305.47013,131.25"
id="guide5354"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
orientation="1,0"
position="230,443.73334"
id="guide3885"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
position="160,100"
orientation="0,1"
id="guide5247"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="Device"
inkscape:label="Device"
style="display:inline"
transform="translate(0,2.6666495)">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 321.875,220.80227 -17.8125,-0.9375 10.3125,-93.75 c 7.5,33.75 8.4375,56.71875 7.5,94.6875 z"
id="button6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="button4"
d="m 137.20625,220.80227 17.8125,-0.9375 -10.3125,-93.75 c -7.5,33.75 -8.4375,56.71875 -7.5,94.6875 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<path
id="path3894"
style="display:inline;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 145.625,141.11478 c 0,0 -3.75,59.99999 0,84.37499 3.75,24.375 -9.375,25.82353 -9.375,61.875 0,67.5 39.375,116.25001 93.75,116.25 54.375,0 93.75,-48.75 93.75,-116.25 0,-36.05147 -13.125,-33.75 -9.375,-61.875 3.75,-28.125 0,-84.37499 0,-84.37499"
inkscape:connector-curvature="0"
sodipodi:nodetypes="czszszc" />
<path
id="path3891"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d9dad7;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 254.375,134.55227 22.5,49.21875 -17.8125,46.40625 c -1.65577,5.51921 -10.3125,15 -16.875,15 h -24.375 c -6.5625,0 -15.21923,-9.48079 -16.875,-15 l -17.8125,-46.40625 22.5,-49.21875"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssccc" />
<path
id="path3887"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 220.625,231.11477 c -3.75,0 -5.82083,-5.76156 -8.4375,-15.9375 l -8.4375,-32.8125 c -0.9643,-3.75 -1.17686,-11.25 -0.9375,-15 L 211.25,35.177274 c 0.29859,-4.678 0.9375,-4.6875 2.8125,-4.6875 1.875,0 1.875,1.875 1.875,1.875 h 28.125 c 0,0 0,-1.875 1.875,-1.875 1.875,-1e-6 2.5139,0.0095 2.8125,4.6875 l 8.4375,132.187496 c 0.23936,3.75 0.0268,11.25 -0.9375,15 l -8.4375,32.8125 c -2.61667,10.17593 -4.6875,15.9375 -8.4375,15.9375 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssssszcczssssss" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 248.75,36.114773 h 5.625 l 43.125,30 c 2.8125,1.956522 3.99101,3.729263 5.625,7.5 l 12.1875,28.124997 c 1.625,3.75 2.01562,9.375 1.875,13.125 l -2.8125,75 -3.75,3.75 -7.5,30 -41.25,-7.5 5.625,-28.125 c 1.125,-5.625 0.225,-9.375 -1.875,-15.9375 l -10.3125,-23.4375 z"
id="button1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccssssccccsscc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 320.9375,229.23977 -7.5,-0.9375 -15.9375,58.125 c -0.77117,2.8125 -1.53214,6.12475 0,6.5625 2.01565,0.57591 4.42284,-1.09513 5.625,-2.8125 l 6.5625,-9.375 c 1.6522,-2.3603 3.09783,-5.625 3.75,-9.375 z"
id="button5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccsssssc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 232.8125,176.73977 v 7.5 c 0,1.875 0.36208,2.76945 0.9375,4.6875 l 2.8125,9.375 c 0.5625,1.875 0.92837,1.875 2.8125,1.875 h 0.9375 c 1.875,0 2.37981,0 2.8125,-1.875 l 2.8125,-12.1875 c 0.43118,-1.86846 0.9375,-3.75 0.9375,-5.625 v -3.75 c 0,-1.875 -0.9375,-2.8125 -2.8125,-2.8125 h -8.4375 c -1.875,0 -2.8125,0.9375 -2.8125,2.8125 z"
id="button8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssssss" />
<path
sodipodi:nodetypes="ccsssssc"
inkscape:connector-curvature="0"
id="button3"
d="m 139.0625,229.23977 7.5,-0.9375 15.9375,58.125 c 0.77117,2.8125 1.53214,6.12476 0,6.5625 -2.01565,0.57591 -4.42284,-1.09513 -5.625,-2.8125 l -6.5625,-9.375 c -1.6522,-2.36028 -3.08377,-5.62747 -3.75,-9.375 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<path
sodipodi:nodetypes="ccssssccccsscc"
inkscape:connector-curvature="0"
id="button0"
d="m 211.25,36.114774 h -5.625 L 162.5,66.114773 c -2.8125,1.956522 -3.99101,3.729262 -5.625,7.5 l -12.1875,28.124997 c -1.625,3.75 -2.02163,9.37524 -1.875,13.12501 l 2.8125,74.99999 3.75,3.75 7.5,30 41.25,-7.5 -5.625,-28.125 c -1.125,-5.625 -0.25547,-9.38482 1.875,-15.9375 l 10.3125,-23.43749 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<path
id="path4352"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:9.375;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 220.625,206.73977 c 1.875,0 2.68807,0.75516 2.8125,1.875 l 1.875,16.875 c 0.10424,0.93821 0,1.875 -0.9375,1.875 -1.875,0 -2.48162,-0.9375 -2.8125,-2.8125 l -2.8125,-15.9375 c -0.19581,-1.10959 0,-1.875 1.875,-1.875 z m 18.75,0 c -1.875,0 -2.68808,0.75516 -2.8125,1.875 l -1.875,16.875 c -0.10424,0.93821 0,1.875 0.9375,1.875 1.875,0 2.48162,-0.9375 2.8125,-2.8125 l 2.8125,-15.9375 c 0.19581,-1.10959 0,-1.875 -1.875,-1.875 z m -7.5,18.75 0.9375,-16.875 c 0.049,-0.88251 0.0604,-1.875 -2.78231,-1.875 -2.84269,0 -2.89172,0.99249 -2.84269,1.875 l 0.9375,16.875 c 0.049,0.88253 0,1.875 1.90519,1.875 1.90518,0 1.79581,-0.99247 1.84481,-1.875 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssssssscszsczc" />
<path
id="button2"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 236.5625,135.48978 c 2.8125,0 5.625,-2.8125 5.625,-5.625 V 79.239773 c 0,-2.8125 -2.8125,-5.625 -5.625,-5.625 h -13.125 c -2.8125,0 -5.625,2.8125 -5.625,5.625 v 50.625007 c 0,2.8125 2.8125,5.625 5.625,5.625 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssss" />
<path
id="path3927"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 221.5625,142.98978 c -1.875,0 -2.70684,0.94013 -2.80606,2.81249 l -0.94394,17.81251 c -0.0992,1.87237 0.9375,2.81249 2.8125,2.81249 h 18.75 c 1.875,0 2.911,-0.94009 2.8125,-2.8125 l -0.9375,-17.8125 c -0.0985,-1.8724 -0.9375,-2.8125 -2.8125,-2.8125 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sscsscsss" />
<path
sodipodi:nodetypes="sssssssssssss"
inkscape:connector-curvature="0"
id="button7"
d="m 227.1875,176.73977 v 7.5 c 0,1.875 -0.36208,2.76945 -0.9375,4.6875 l -2.8125,9.375 c -0.5625,1.875 -0.92837,1.875 -2.8125,1.875 h -0.9375 c -1.875,0 -2.37981,0 -2.8125,-1.875 l -2.8125,-12.1875 c -0.43118,-1.86845 -0.9375,-3.75 -0.9375,-5.625 v -3.75 c 0,-1.875 0.9375,-2.8125 2.8125,-2.8125 h 8.4375 c 1.875,0 2.8125,0.9375 2.8125,2.8125 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1.875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<path
sodipodi:type="arc"
style="fill:none;fill-opacity:0.39215686;stroke:#babdb6;stroke-width:9.375;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="led0"
sodipodi:cx="230"
sodipodi:cy="317.36478"
sodipodi:rx="16.875"
sodipodi:ry="16.875002"
d="m 241.93243,305.43235 a 16.875,16.875002 0 0 1 0,23.86485 16.875,16.875002 0 0 1 -23.86486,1e-5 16.875,16.875002 0 0 1 0,-23.86486"
sodipodi:start="5.4977871"
sodipodi:end="3.9269907"
sodipodi:open="true"
sodipodi:arc-type="arc" />
<path
style="opacity:1;vector-effect:none;fill:#babdb6;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:9.375;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 202,104 v 2 l -2,2 2,2 v 2 l -4,-4 z"
id="button9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
transform="translate(0,-2.6666495)" />
<path
style="opacity:1;vector-effect:none;fill:#babdb6;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:9.375;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 258,104 v 2 l 2,2 -2,2 v 2 l 4,-4 z"
id="button10"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
transform="translate(0,-2.6666495)" />
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
style="display:inline">
<g
id="button0-path">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4699"
d="M 180,99.999995 H 1"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="96.5"
x="176.5"
height="7"
width="7"
id="rect4744-7"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
<rect
style="text-align:end;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="button0-leader"
width="1"
height="1"
x="-6.0989584e-07"
y="99.5"
inkscape:label="#rect5374" />
</g>
<g
id="button9-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path4697"
d="m 212,108 -32,32 H 1"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="104.5"
x="208.5"
height="7"
width="7"
id="rect4744"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
<rect
style="text-align:end;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="button9-leader"
width="1"
height="1"
x="-6.0989584e-07"
y="139.5"
inkscape:label="#rect5374" />
</g>
<g
id="button7-path">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4695"
d="M 221,180 H 1"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="176.5"
x="216.5"
height="7"
width="7"
id="rect4744-0"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
<rect
style="text-align:end;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="button7-leader"
width="1"
height="1"
x="-6.0989584e-07"
y="179.5"
inkscape:label="#rect5374" />
</g>
<g
id="button4-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path4693"
d="m 140,200 -20,20 H 1"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="196.5"
x="136.5"
height="7"
width="7"
id="rect4744-9"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
<rect
style="text-align:end;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="button4-leader"
width="1"
height="1"
x="-6.0989584e-07"
y="219.5"
inkscape:label="#rect5374" />
</g>
<g
id="button3-path">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4691"
d="M 148,260 H 1"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="256.5"
x="144.5"
height="7"
width="7"
id="rect4744-1"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
<rect
style="text-align:end;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="button3-leader"
width="1"
height="1"
x="-6.0989584e-07"
y="259.5"
inkscape:label="#rect5374" />
</g>
<g
id="button5-path">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4711"
d="M 312,260 H 459"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="256.5"
x="308.5"
height="7"
width="7"
id="rect4744-27"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
<rect
style="text-align:start;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="button5-leader"
width="1"
height="1"
x="459"
y="259.5"
inkscape:label="#rect5374" />
</g>
<g
id="button6-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path4709"
d="m 320,200 20,20 h 119"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="196.5"
x="316.5"
height="7"
width="7"
id="rect4744-2"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
<rect
style="text-align:start;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="button6-leader"
width="1"
height="1"
x="459"
y="219.5"
inkscape:label="#rect5374" />
</g>
<g
id="button8-path">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4707"
d="M 240,180 H 459"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="176.5"
x="236.5"
height="7"
width="7"
id="rect4744-93"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
<rect
style="text-align:start;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="button8-leader"
width="1"
height="1"
x="459"
y="179.5"
inkscape:label="#rect5374" />
</g>
<g
id="button10-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path4701"
d="m 248,108 32,32 h 179"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="104.5"
x="244.5"
height="7"
width="7"
id="rect4744-56"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
<rect
style="text-align:start;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="button10-leader"
width="1"
height="1"
x="459"
y="139.5"
inkscape:label="#rect5374" />
</g>
<g
id="button1-path">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4703"
d="M 280,99.999995 H 459"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="96.5"
x="276.5"
height="7"
width="7"
id="rect4744-3"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
<rect
style="text-align:start;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="button1-leader"
width="1"
height="1"
x="459"
y="99.5"
inkscape:label="#rect5374" />
</g>
<g
id="button2-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path4705"
d="M 230,87.999999 258,59.999995 H 459"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="84.5"
x="226.5"
height="7"
width="7"
id="rect4744-5"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
<rect
style="text-align:start;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="button2-leader"
width="1"
height="1"
x="459"
y="59.5"
inkscape:label="#rect5374" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs">
<g
id="led0-path">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4705-0"
d="m 230,320 229.375,0"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
style="text-align:start;display:inline;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994"
id="led0-leader"
width="1"
height="1"
x="459"
y="319.5"
inkscape:label="#rect5374" />
<rect
y="316.5"
x="226.5"
height="7"
width="7"
id="rect4744-5-3"
style="display:inline;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -102,23 +102,28 @@
<sodipodi:guide
position="125.24434,292.81279"
orientation="1,0"
id="guide4350" />
id="guide4350"
inkscape:locked="false" />
<sodipodi:guide
position="232.01745,389.68642"
orientation="0,1"
id="guide4352" />
id="guide4352"
inkscape:locked="false" />
<sodipodi:guide
position="339.49769,179.67571"
orientation="1,0"
id="guide4354" />
id="guide4354"
inkscape:locked="false" />
<sodipodi:guide
position="262.42305,21.283798"
orientation="0,1"
id="guide4356" />
id="guide4356"
inkscape:locked="false" />
<sodipodi:guide
position="61.51829,157.33126"
orientation="1,0"
id="guide5867" />
id="guide5867"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata5">
@@ -128,7 +133,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@@ -193,7 +198,7 @@
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="button9"
id="button7"
d="m 209.20722,524.3296 -0.65676,27.92859 -17.18836,0 -0.65677,-27.92859 z"
style="color:#000000;overflow:visible;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
inkscape:label="#path942" />
@@ -201,7 +206,7 @@
inkscape:label="#path942"
style="color:#000000;overflow:visible;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 208.41172,565.23341 -1.86126,32.17123 c -0.25984,4.4912 -1.83301,4.12045 -6.59419,4.12045 -4.76117,0 -6.33658,0.37088 -6.59417,-4.12045 l -1.94966,-31.99445 z"
id="button10"
id="button8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssscc" />
<path
@@ -220,7 +225,7 @@
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="button11"
id="button9"
d="m 206.55097,441.9448 1.9995,15.20984 -17.18836,0 1.99948,-15.17859 z"
style="color:#000000;overflow:visible;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
inkscape:label="#path942" />
@@ -258,11 +263,11 @@
<g
id="button1-path"
inkscape:label="#g131"
transform="translate(8,72)">
transform="translate(8,111.99976)">
<path
sodipodi:nodetypes="cc"
sodipodi:nodetypes="ccc"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 242.94764,50.505764 194.05236,0"
d="M 221.19764,23.005764 248.71312,50.628685 437,50.505764"
id="path958"
inkscape:connector-curvature="0" />
<rect
@@ -270,8 +275,8 @@
id="rect968"
width="6.999999"
height="6.999999"
x="237"
y="47.005764" />
x="217.125"
y="19.255764" />
<rect
inkscape:label="#rect867"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
@@ -282,7 +287,7 @@
y="50.005764" />
</g>
<g
id="button11-path"
id="button9-path"
inkscape:label="#g141"
transform="translate(8,-88)">
<path
@@ -303,7 +308,7 @@
<rect
inkscape:label="#rect871"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button11-leader"
id="button9-leader"
width="1"
height="1"
x="437"
@@ -336,12 +341,12 @@
y="92" />
</g>
<g
id="button10-path"
id="button8-path"
inkscape:label="#g146"
transform="translate(8,-8)">
transform="matrix(1,0,0,-1,8,371.99953)">
<path
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 436.55132,170.50615 -223.20734,0 -18.11371,18.15319"
d="M 436.55132,170.50615 H 213.34398 l -18.11371,18.15319"
id="path51"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
@@ -357,14 +362,14 @@
x="437"
height="1"
width="1"
id="button10-leader"
id="button8-leader"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="translate(-72.0872,-77.953227)"
style="display:inline"
id="button9-path"
id="button7-path"
inkscape:label="#g191">
<path
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
@@ -383,7 +388,7 @@
<rect
transform="scale(-1,-1)"
style="color:#000000;text-align:end;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button9-leader"
id="button7-leader"
width="1"
height="1"
x="-37.168953"
@@ -452,12 +457,12 @@
inkscape:label="#g191"
id="button0-path"
style="display:inline"
transform="translate(-72.0872,-137.95323)">
transform="translate(-72.0872,-177.95275)">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="button0-line"
d="m 37.16895,320.50027 59.327469,0 99.344411,-99.98695 26.85261,0"
d="m 37.16895,320.50027 h 59.327469 l 99.344411,-99.98695 h 26.85261"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
inkscape:label="#path5841" />
<rect
@@ -467,7 +472,7 @@
width="6.999999"
id="button0-rect"
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
transform="scale(-1,-1)"
transform="scale(-1)"
inkscape:label="#rect5843" />
<rect
inkscape:label="#rect873"
@@ -477,7 +482,64 @@
width="1"
id="button0-leader"
style="color:#000000;text-align:end;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
transform="scale(-1,-1)" />
transform="scale(-1)" />
</g>
<g
transform="translate(-72.0872,-137.95275)"
style="display:inline"
id="button5-path"
inkscape:label="#g191">
<path
inkscape:label="#path5841"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 37.16895,320.50027 h 59.327469 l 88.152021,-88.86195 h 55.67"
id="path85"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
inkscape:label="#rect5843"
transform="scale(-1)"
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect87"
width="6.999999"
height="6.999999"
x="-244.00287"
y="-235.48463" />
<rect
transform="scale(-1)"
style="color:#000000;text-align:end;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button5-leader"
width="1"
height="1"
x="-37.168953"
y="-321.00027"
inkscape:label="#rect873" />
</g>
<g
transform="translate(10.625,71.49976)"
inkscape:label="#g131"
id="button6-path">
<path
inkscape:connector-curvature="0"
id="path93"
d="m 221.19764,23.005764 27.51548,27.622921 185.72362,-0.122921"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
sodipodi:nodetypes="ccc" />
<rect
y="19.255764"
x="217.125"
height="6.999999"
width="6.999999"
id="rect95"
style="color:#000000;overflow:visible;opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
y="50.005764"
x="434.37524"
height="1"
width="1"
id="button6-leader"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect867" />
</g>
</g>
<g

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 739 KiB

View File

@@ -0,0 +1,443 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="490"
height="490"
viewBox="0 0 129.64583 129.64583"
version="1.1"
id="svg8"
sodipodi:docname="steelseries-rival310.svg"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4142136"
inkscape:cx="158.34512"
inkscape:cy="283.9471"
inkscape:document-units="px"
inkscape:current-layer="Device"
inkscape:document-rotation="0"
showgrid="false"
showguides="false"
inkscape:window-width="1920"
inkscape:window-height="1163"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
showborder="true"
inkscape:guide-bbox="true"
units="px"
inkscape:snap-global="false"
inkscape:measure-start="241.5,424.5"
inkscape:measure-end="193,423">
<inkscape:grid
type="xygrid"
id="grid78" />
<sodipodi:guide
position="58.722772,112.40709"
orientation="0,1"
id="guide79"
inkscape:locked="false" />
<sodipodi:guide
position="39.611923,100.54373"
orientation="0,1"
id="guide81"
inkscape:locked="false" />
<sodipodi:guide
position="51.756014,88.614617"
orientation="0,1"
id="guide83"
inkscape:locked="false" />
<sodipodi:guide
position="57.778386,76.729167"
orientation="0,1"
id="guide85"
inkscape:locked="false" />
<sodipodi:guide
position="45.047078,64.795712"
orientation="0,1"
inkscape:locked="false"
id="guide87" />
<sodipodi:guide
position="57.646696,52.899321"
orientation="0,1"
id="guide89"
inkscape:locked="false" />
<sodipodi:guide
position="64.031094,118.70775"
orientation="1,0"
id="guide996"
inkscape:locked="false" />
<sodipodi:guide
position="76.729165,115.65599"
orientation="1,0"
id="guide4799"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="Device"
inkscape:label="Device"
style="display:inline;opacity:1"
transform="translate(0,-167.35416)">
<path
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;opacity:1"
d="m 39.422916,178.73125 c 8.753298,-5.1263 8.742273,-5.15937 19.909893,-6.35 0.86322,1.81872 1.399035,2.16412 3.571875,2.57968 0.815799,-0.022 1.631597,-0.0441 2.447396,-0.0661 1.808025,-0.44162 2.142728,-0.67277 3.108854,-2.4474 11.230355,1.36029 11.188618,1.38757 18.25625,6.74688 2.745052,3.1309 3.671094,5.00504 4.365625,8.59896 0.264583,5.1263 1.254823,10.18645 1.320969,15.27968 4.850693,20.06424 6.726771,34.70452 8.270172,54.63646 -0.57326,10.05417 -1.411108,14.15521 -6.879162,21.43125 -5.55625,5.60034 -10.31875,7.8934 -17.859375,10.84791 -7.805208,1.74184 -11.509375,1.76389 -18.653125,0.46302 -6.32795,-1.67569 -10.384244,-5.1794 -14.221351,-10.58333 -4.882794,-8.38114 -5.236545,-14.94896 -7.408333,-22.62187 -0.782726,-9.55807 -1.367014,-19.31459 -2.050521,-28.97188 l 2.38125,-0.39687 c -0.0441,-0.50712 -0.08819,-1.01423 -0.132292,-1.52135 l -2.38125,-0.99219 v -17.19792 c 0.617361,-1.6316 1.234723,-3.26319 1.852084,-4.89479 l 0.926041,-18.1901 c 0.496094,-3.10887 1.256771,-4.69638 3.175,-6.35004 z"
id="path882"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccccccccccc" />
<path
sodipodi:type="arc"
style="display:inline;opacity:1;fill:none;fill-opacity:0.39215686;stroke:#babdb6;stroke-width:3.62082291;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="led0"
sodipodi:cx="64.507607"
sodipodi:cy="278.93338"
sodipodi:rx="6.5174346"
sodipodi:ry="6.5174408"
d="m 69.116129,274.32485 a 6.5174346,6.5174408 0 0 1 0,9.21706 6.5174346,6.5174408 0 0 1 -9.217044,0 6.5174346,6.5174408 0 0 1 -10e-7,-9.21706"
sodipodi:start="5.4977871"
sodipodi:end="3.9269907"
sodipodi:open="true"
sodipodi:arc-type="arc"
inkscape:label="#led1" />
<path
style="display:inline;opacity:1;fill:none;stroke:#babdb6;stroke-width:0.51011664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 63.998032,175.33704 c -0.0617,17.18181 0,37.8177 0,37.8177 v 0"
id="path4530-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
style="display:inline;opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#bfc2bb;stroke-width:0.65934163;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="button2"
width="8.6572962"
height="16.992365"
x="59.667027"
y="183.8551"
ry="1.5045637"
inkscape:label="#rect4615"
rx="2.8998742" />
<rect
style="display:inline;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#bfc2bb;stroke-width:0.51011665;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="button5"
width="4.0773749"
height="11.664907"
x="61.934288"
y="213.306"
ry="0.74915975"
inkscape:label="#rect4615"
rx="0.63142258" />
<path
style="fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:0.65934163;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;opacity:1"
d="m 92.234711,203.24519 c 0.280629,9.51034 -0.467724,16.09741 -0.467724,24.43845 -0.763945,13.64188 -1.153714,9.79098 -1.590254,28.48425 -0.04677,3.7028 1.344699,5.74518 2.50231,6.78196 1.157611,1.03679 4.41997,3.85091 6.618261,5.14494 0.941986,-4.54444 1.338226,-4.86758 1.335156,-10.49869 -0.4989,-10.35224 -1.350744,-19.81747 -3.369743,-31.52609 -1.652616,-7.76419 -1.878682,-12.53495 -5.028008,-22.82482 z"
id="path959"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccsccccc" />
<path
style="fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:0.65934163;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;opacity:1"
d="m 37.603906,265.61379 c 1.378038,-1.69774 1.896181,-2.56866 2.447396,-4.49791 0.275607,-1.59853 0.319704,-2.27101 0.132291,-3.70417 -0.176389,-0.97014 -0.352777,-1.94028 -0.529166,-2.91042 -0.352778,0.022 -0.705556,0.0441 -1.058334,0.0661 -1.113455,-0.31971 -1.631597,-0.93707 -2.248958,-1.65365 -0.429948,-0.89297 -0.859896,-1.78593 -1.289844,-2.6789 0.176389,2.30408 0.352778,4.60816 0.529167,6.91224 0.672483,2.82224 1.344965,5.64447 2.017448,8.46671 z"
id="path961"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:0.51011665;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;opacity:1"
d="m 39.662795,224.50314 c -1.418755,-15.53616 -1.294028,-30.04332 -0.748352,-45.27546 7.464057,-5.23848 10.578303,-5.75298 20.392662,-7.01583 0.604141,2.15932 2.552981,2.88039 4.583673,2.89988 0.06236,2.93106 -0.0039,5.75688 0.05847,8.68793 -0.966626,0.0156 -1.617539,-0.0389 -2.677709,0.15206 -0.576857,0.12082 -1.668207,0.59244 -1.683798,1.44993 0.03118,4.73958 0.06237,9.47916 0.09355,14.21874 0.428974,0.80223 0.791803,1.0753 1.683798,1.21608 h 2.666014 c 0.09354,3.89768 -0.04677,7.79536 0.04677,11.69304 -1.777343,0.0624 -3.554686,0.12473 -5.332029,0.18709 -4.677216,3.30523 -9.354432,6.61047 -14.031648,9.9157 -1.948383,1.15278 -3.036871,1.77639 -5.051401,1.87084 z"
id="button0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccc"
inkscape:label="#path971" />
<path
style="display:inline;opacity:1;fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:0.51011664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 88.538966,224.46383 c 1.418756,-15.53616 -0.763946,-29.5756 -1.309622,-44.80774 -6.341526,-5.61266 -8.520329,-6.2207 -18.334688,-7.48355 -0.604141,2.15932 -2.833614,2.83362 -4.864306,2.85311 -0.06236,2.93106 0.284533,5.80365 0.222163,8.7347 0.966626,0.0156 1.617539,-0.0389 2.677709,0.15206 0.576857,0.12082 1.668207,0.59244 1.683798,1.44993 -0.03118,4.73958 -0.06237,9.47916 -0.09355,14.21874 -0.428974,0.80223 -0.791803,1.0753 -1.683798,1.21608 h -2.80633 c -0.09354,3.89768 0.187088,7.79536 0.09354,11.69304 1.777343,0.0624 3.554686,0.12473 5.332029,0.18709 4.677216,3.30523 9.354432,6.61047 14.031649,9.9157 1.948382,1.15278 3.036871,1.77639 5.0514,1.87084 z"
id="button1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccc"
inkscape:label="#path971-5" />
<path
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:0.51011664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;opacity:1"
d="m 39.721262,254.48409 c -2.127995,-12.76601 -2.492187,-15.11204 -3.531298,-26.68352 -0.880877,0.16371 -1.761753,0.32741 -2.64263,0.49111 0.510597,7.26528 1.021194,14.53055 1.531791,21.79583 0.415673,1.21059 0.673654,1.8504 1.39147,2.98173 0.557369,0.65481 1.067966,1.08745 1.882582,1.5084 0.738934,0.12313 0.851361,0.031 1.368085,-0.0935 z"
id="button3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:label="#path998" />
<path
style="fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#bfc2bb;stroke-width:0.51011664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;opacity:1"
d="m 36.061341,226.30387 -2.595857,-0.95884 v -17.51617 l 2.128134,-4.89938 z"
id="button4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#path1000" />
<g
id="g27"
inkscape:label="#g84"
style="opacity:1">
<path
inkscape:label="#path42"
inkscape:connector-curvature="0"
id="led1"
d="m 60.202463,185.20746 c 0,14.31228 0,14.31228 0,14.31228"
style="fill:#c8c8c8;fill-opacity:1;stroke:#babdb6;stroke-width:0.23228861px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:label="#path42"
inkscape:connector-curvature="0"
id="led0-3"
d="m 67.749483,185.21452 c 0,14.31228 0,14.31228 0,14.31228"
style="display:inline;opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:#babdb6;stroke-width:0.23228861px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
transform="translate(0,-2.6458338)"
style="display:inline">
<g
transform="matrix(-0.29294531,0,0,0.26458333,70.19679,-46.28282)"
style="display:inline"
id="button0-path"
inkscape:label="#g156">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 69.018882,250.50576 170.100828,0.008"
id="path966"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect976"
width="6.999999"
height="6.999999"
x="65.211685"
y="247.25577" />
<rect
y="250.01747"
x="238.66302"
height="1"
width="1"
id="button0-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect875" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,40.502648,-25.152234)"
style="display:inline"
id="button1-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 336.41912,170.11088 -197.26264,0.0828"
id="path51"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53"
width="6.999999"
height="6.999999"
x="137"
y="166.75" />
<rect
y="169.61206"
x="335.92694"
height="1"
width="1"
id="button1-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,39.927878,-13.516602)"
style="display:inline"
id="button2-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 338.62224,171.07963 91.069203,171.06803"
id="path51-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-3"
width="6.999999"
height="6.999999"
x="87.596474"
y="167.57452" />
<rect
y="170.56519"
x="338.10663"
height="1"
width="1"
id="button2-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,39.855422,10.357346)"
style="display:inline"
id="button5-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 338.91912,170.14213 91.343981,170.50618"
id="path51-2-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-3-4"
width="6.999999"
height="6.999999"
x="88"
y="167.375" />
<rect
y="169.65894"
x="338.32538"
height="1"
width="1"
id="button5-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(-0.29294531,0,0,0.26458333,70.354375,-10.600792)"
style="display:inline"
id="button4-path"
inkscape:label="#g156">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 121.40352,250.50576 118.2207,0.0352"
id="path966-1"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect976-3"
width="6.999999"
height="6.999999"
x="118.01969"
y="247.06827" />
<rect
y="250.06044"
x="239.16049"
height="1"
width="1"
id="button4-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect875" />
</g>
<g
transform="matrix(-0.29294531,0,0,0.26458333,70.260832,1.1857932)"
style="display:inline"
id="button3-path"
inkscape:label="#g156">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 119.59715,250.50576 120.02707,0.0352"
id="path966-2"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect976-8"
width="6.999999"
height="6.999999"
x="116.24155"
y="247.19327" />
<rect
y="250.00575"
x="238.62422"
height="1"
width="1"
id="button3-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect875" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
transform="translate(0,-2.6458338)"
style="display:inline">
<g
transform="matrix(-0.29294531,0,0,0.26458333,69.912779,-34.520744)"
style="display:inline"
id="led1-path"
inkscape:label="#g156">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 33.260958,250.40632 204.900782,0.0964"
id="path966-0"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect976-2"
width="6.999999"
height="6.999999"
x="29.527279"
y="246.99062" />
<rect
y="249.97328"
x="237.64519"
height="1"
width="1"
id="led1-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect875" />
</g>
<g
transform="matrix(-0.29294531,0,0,0.26458333,69.953208,49.201431)"
style="display:inline"
id="led0-path"
inkscape:label="#g156">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 19.28901,250.49471 219.01243,0.008"
id="path966-0-2"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect976-2-1"
width="6.999999"
height="6.999999"
x="16.834169"
y="246.63705" />
<rect
y="249.97328"
x="237.7849"
height="1"
width="1"
id="led0-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect875" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 23 KiB

157
doc/conf.py.in Normal file
View File

@@ -0,0 +1,157 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# @PACKAGE_NAME@ documentation build configuration file, created by
# sphinx-quickstart on Wed Oct 11 13:45:08 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = '@PACKAGE_NAME@'
copyright = '2017, The ratbaggers'
author = 'The ratbaggers'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = ''
# The full version, including alpha/beta/rc tags.
release = ''
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
# html_theme = 'alabaster'
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = '@PACKAGE_NAME@doc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, '@PACKAGE_NAME@.tex', '@PACKAGE_NAME@ Documentation',
'The ratbaggers', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, '@PACKAGE_NAME@', '@PACKAGE_NAME@ Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, '@PACKAGE_NAME@', '@PACKAGE_NAME@ Documentation',
author, '@PACKAGE_NAME@', 'One line description of project.',
'Miscellaneous'),
]

446
doc/dbus.rst Normal file
View File

@@ -0,0 +1,446 @@
********
DBus API
********
Note: **the DBus interface is subject to change**
Interfaces:
* org.freedesktop.ratbag1.Manager
* org.freedesktop.ratbag1.Device
* org.freedesktop.ratbag1.Profile
* org.freedesktop.ratbag1.Resolution
* org.freedesktop.ratbag1.Button
* org.freedesktop.ratbag1.Led
For a list of dbus types as used below, see https://dbus.freedesktop.org/doc/dbus-specification.html
Properties marked as **constant** do not change for the lifetime of the
object. Properties marked as **mutable** may change, and a
``org.freedesktop.DBus.Properties.PropertyChanged`` signal is sent for those
unless otherwise specified.
All setters (whether implicit for read-write properties or explicit) return
success instead of the internal return value because DBus does not like
non-standard error values. Upon a ratbag error, the `Resync` signal is emitted
on the device, indicating that clients should resync their values.
Values listed as enums are defined in `libratbag-enums.h <https://github.com/libratbag/libratbag/blob/master/src/libratbag-enums.h>`_
org.freedesktop.ratbag1.Manager
-------------------------------
.. js:class:: Manager
The **org.freedesktop.ratbag1.Manager** interface is the entry point to
interact with ratbagd.
.. js:attribute:: Devices
:type: ao
An array of read-only **object paths** to :js:class:`Device` objects
.. js:attribute:: Themes
:type: as
:flags: read-only, constant
Provides the list of available theme names. This list is guaranteed to have
one theme available ('default'). Other themes are implementation defined.
A theme listed here is only a guarantee that the theme is known to libratbag
and that SVGs *may* exist, it is not a guarantee that the SVG for any
specific device exists. In other words, a device may not have an SVG for a
specific theme.
This list is static for the lifetime of ratbagd.
org.freedesktop.ratbag1.Device
-------------------------------
.. js:class:: Device
The **org.freedesktop.ratbag1.Device** interface describes a single device
known to ratbagd.
.. js:attribute:: Id
:type: as
:flags: read-only, constant
An ID describing this device. This ID should not be used for presentation to
the user. This ID is unique for the device for the lifetime of the device
but may be recycled when the device is removed. No guarantee is given for
the content of the ID, the client should treat it as an opaque string.
.. js:attribute:: Description
:type: s
:flags: read-only, constant
The device's name, suitable for presentation to the user.
.. js:attribute:: Capabilities
:type: au
:flags: read-only, constant
The capabilities supported by this device. see `enum
ratbag_device_capability` in libratbag-enums.h for the list of permissible
capabilities.
.. js:attribute:: Svg
:type: s
:flags: read-only, constant
The device's SVG file name, without path.
.. js:attribute:: Profiles
:type: ao
:flags: read-only, mutable
This property is mutable if the device supports adding and removing
profiles.
Provides the list of profile paths for all profiles on this device, see
:js:class:`Profile`.
.. js:function:: Commit() → ()
Commits the changes to the device. Changes to the device are batched; they
are not written to the hardware until :js:func:`Commit()` is invoked.
.. js:function:: GetSvg(s) → (s)
:param s: the theme name
:returns: A full path to the SVG for the given theme
Returns the full path to the SVG for the given theme or an
empty string if none is available. The theme must be one of
:js:attr:`Themes`.
The theme **'default'** is guaranteed to be available.
ratbagd may return the path to a file that doesn't exist.
This is the case if the device has SVGs available but not
for the given theme.
.. js:function:: Resync()
:type: Signal
Emitted when an internal error occurs. Upon receiving this
signal, clients are expected to resync their values with
ratbagd.
org.freedesktop.ratbag1.Profile
-------------------------------
.. js:class:: Profile
.. js:attribute:: Index
:type: u
:flags: read-only, constant
The index of this profile
.. js:attribute:: Name
:type: s
:flags: read-write, mutable
The name of this profile.
.. js:attribute:: Enabled
:type: b
:flags: read-write, mutable
True if this is the profile is enabled, false otherwise.
Note that a disabled profile might not have correct bindings, so it's
a good thing to rebind everything before calling :js:func:`Commit()` on the
:js:class:`Device`.
.. js:attribute:: Resolutions
:type: ao
:flags: read-only, mutable
This property is mutable if the device supports adding and removing
resolutions.
Provides the object paths of all resolutions in this profile, see
:js:class:`Resolution`
.. js:attribute:: Buttons
:type: ao
:flags: read-only, constant
Provides the object paths of all buttons in this profile, see
:js:class:`Button`
.. js:attribute:: Leds
:type: ao
:flags: read-only, constant
Provides the object paths of all LEDs in this profile, see
:js:class:`Led`
.. js:attribute:: IsActive
:type: b
:flags: read-only, mutable
True if this is the currently active profile, false otherwise.
Profiles can only be set to active, but never to not active - at least one
profile must be active at all times. This property is read-only, use the
:js:func:`SetActive()` method to activate a profile.
.. js:function:: SetActive() → ()
Set this profile to be the active profile
org.freedesktop.ratbag1.Resolution
----------------------------------
.. js:class:: Resolution
.. js:attribute:: Index
:type: u
:flags: read-only, constant
Index of the resolution
.. js:attribute:: Capabilities
:type: au
:flags: read-only, constant
Array of uints from the ratbag\_resolution\_capability from libratbag.h.
.. js:attribute:: IsActive
:type: b
:flags: read-only, mutable
True if this is the currently active resolution, false otherwise.
Resolutions can only be set to active, but never to not
active - at least one resoultion must be active at all
times. This property is read-only, use the
:js:func:`SetActive()` method to set a resolution as the
active resolution.
.. js:attribute:: IsDefault
:type: b
:flags: read-only, mutable
True if this is the currently default resolution, false
otherwise. If the device does not have the default
resolution capability, this property is always false.
Resolutions can only be set to default, but never to not
default - at least one resolution must be default at all
times. This property is read-only, use the
:js:func:`SetDefault()` method to set a resolution as
the default resolution.
.. js:attribute:: Resolution
:type: uu
:flags: read-write, mutable
uint for the x and y resolution assigned to this entry,
respectively. The value for the resolution must be equal to
one of the values in :js:attr:`Resolutions`.
If the resolution does not support separate x/y resolutions,
x and y must be the same value.
.. js:attribute:: Resolutions
:type: au
:flags: read-only, constant
A list of permitted resolutions. Values in this list may be used in
the :js:attr:`Resolution` property. This list is always sorted
ascending, the lowest resolution is the first item in the list.
This list may be empty if the device does not support reading and/or
writing to resolutions.
.. js:attribute:: ReportRate
:type: u
:flags: read-write, mutable
uint for the report rate in Hz assigned to this entry
If the resolution does not have the individual report rate
capability, changing the report rate on one resolution will
change the report rate on all resolutions.
.. js:attribute:: ReportRates
:type: au
:flags: read-write, constant
A list of permitted report rates. Values in this list may be used
in the :js:attr:`ReportRate` property. This list is always sorted
ascending, the lowest report rate is the first item in the list.
This list may be empty if the device does not support reading and/or
writing to resolutions.
.. js:function:: SetDefault() → ()
Set this resolution to be the default
.. js:function:: SetActive() → ()
Set this resolution to be the active one
org.freedesktop.ratbag1.Button
------------------------------
.. js:class:: Button
.. js:attribute:: Index
:type: u
:flags: read-only, constant
Index of the button
.. js:attribute:: Type
:type: u
:flags: read-only, constant
Enum describing the button physical type, see
:cpp:enum:`ratbag_button_type`. This type is unrelated to the
logical button mapping and serves to easily identify the button on
the device.
.. js:attribute:: ButtonMapping
:type: u
:flags: read-write, mutable
uint of the current button mapping (if mapping to button)
.. js:attribute:: SpecialMapping
:type: u
:flags: read-write, mutable
Enum describing the current special mapping (if mapped to special)
.. js:attribute:: Macro
:type: a(uu)
:flags: read-write, mutable
Array of (type, keycode), where type may be one of
:cpp:enumerator:`RATBAG_MACRO_EVENT_KEY_PRESSED` or
:cpp:enumerator:`RATBAG_MACRO_EVENT_KEY_RELEASED`.
.. js:attribute:: ActionType
:type: u
:flags: read-only, mutable
An enum describing the action type of the button, see
:cpp:enum:`ratbag_button_action_type` for the list of enums.
This decides which one of :js:attr:`ButtonMapping`,
:js:attr:`SpecialMapping` and :js:attr:`Macro` has a value.
.. js:attribute:: ActionTypes
:type: au
:flags: read-only, constant
Array of :cpp:enum:`ratbag_button_action_type`, possible values
for ActionType on the current device
.. js:function:: Disable() → ()
Disable this button
org.freedesktop.ratbag1.Led
---------------------------
.. js:class:: Led
.. js:attribute:: Index
:type: u
:flags: read-only, constant
Index of the LED
.. js:attribute:: Mode
:type: u
:flags: read-write, mutable
uint mapping to the mode enum from libratbag
.. js:attribute:: Modes
:type: au
:flags: read-only, constant
A list of modes supported by this LED
.. js:attribute:: Type
:type: u
:flags: read-only, mutable
enum describing the LED type
.. js:attribute:: Color
:type: (uuu)
:flags: read-write, mutable
uint triplet (RGB) of the LED's color
.. js:attribute:: ColorDepth
:type: u
:flags: read-only, constant
The color depth of this LED as one of the constants in libratbag-enums.h
.. js:attribute:: EffectDuration
:type: u
:flags: read-write, mutable
The effect duration in ms, possible values are in the range 0 - 10000
.. js:attribute:: Brightness
:type: u
:flags: read-write, mutable
The brightness of the LED, possible values are in the range 0 - 255
For easier debugging, objects paths are constructed from the device. e.g.
`/org/freedesktop/ratbag/button/event5/p0/b10` is the button interface for
button 10 on profile 0 on event5. The naming is subject to change. Do not
rely on a constructed object path in your application.

View File

@@ -0,0 +1,53 @@
<!-- XSLT script to combine the generated output into a single file.
If you have xsltproc you could use:
xsltproc combine.xslt index.xml >all.xml
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" indent="yes"/>
<xsl:strip-space elements="detaileddescription briefdescription"/>
<xsl:template match="/">
<xsl:text>
************
Enumerations
************
This document contains the enums used by libratbag's DBus interface.
</xsl:text>
<xsl:apply-templates select="//memberdef"/>
</xsl:template>
<xsl:template match="memberdef">
<xsl:value-of select="name"/>
---------------------------------------
<xsl:text>&#xa;</xsl:text>
<xsl:if test="briefdescription != ''">
<xsl:value-of select="briefdescription"/>
</xsl:if>
<xsl:if test="detaileddescription != ''">
<xsl:value-of select="detaileddescription"/>
</xsl:if>
.. cpp:enum:: <xsl:value-of select="name"/>
<xsl:text>&#xa;&#xa;</xsl:text>
<xsl:apply-templates select="./enumvalue"/>
</xsl:template>
<xsl:template match="enumvalue">
.. cpp:enumerator:: <xsl:value-of select="name"/><xsl:text> </xsl:text><xsl:value-of select="initializer"/>
<xsl:text>&#xa;&#xa;</xsl:text>
<xsl:if test="briefdescription != ''">
<xsl:text> </xsl:text><xsl:value-of select="briefdescription"/>
</xsl:if>
<xsl:if test="detaileddescription != ''">
<xsl:text> </xsl:text><xsl:value-of select="detaileddescription"/>
</xsl:if>
<xsl:text>&#xa;&#xa;</xsl:text>
</xsl:template>
</xsl:stylesheet>

20
doc/index.rst Normal file
View File

@@ -0,0 +1,20 @@
.. libratbag documentation master file, created by
sphinx-quickstart on Wed Oct 11 13:45:08 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
**********************
The libratbag DBus API
**********************
.. toctree::
:maxdepth: 2
:caption: Contents:
The DBus Interfaces <dbus.rst>
Enumerations <enums.rst>
This document describes the DBus API that is the public interface for
libratbag.
Note: **the DBus interface is subject to change**

15
doc/libratbag.doxygen.in Normal file
View File

@@ -0,0 +1,15 @@
PROJECT_NAME = @PACKAGE_NAME@
PROJECT_NUMBER = @PACKAGE_VERSION@
PROJECT_BRIEF = "A mouse configuration library"
JAVADOC_AUTOBRIEF = YES
TAB_SIZE = 8
OPTIMIZE_OUTPUT_FOR_C = YES
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-enums.h
GENERATE_HTML = NO
GENERATE_XML = YES
GENERATE_LATEX = NO

73
doc/meson.build Normal file
View File

@@ -0,0 +1,73 @@
if get_option('documentation')
cmd_sphinx = find_program('sphinx-build-3', 'sphinx-build3')
cmd_doxygen = find_program('doxygen')
cmd_xsltproc = find_program('xsltproc')
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())
# We have a dependency chain here:
# - generate doxygen xml from the source
# - convert the xml to rst with xstlproc
# - generate the sphinx conf
# - run sphinx against the generated rst
doxyfile = configure_file(input : 'libratbag.doxygen.in',
output : 'libratbag.doxygen',
configuration : doc_config,
install : false)
src_doxygen = [ doxyfile,
files('../src/libratbag-enums.h') ]
doxygen = custom_target('doxygen',
input : src_doxygen,
output : [ 'xml' ],
command : [ cmd_doxygen, doxyfile ],
install : false,
build_by_default : true)
# Sphinx really really wants a _static directory in its source tree. Let's
# make it happy
run_command('mkdir', '-p', join_paths(meson.build_root(), 'doc', '_static'))
xsl = join_paths(meson.source_root(), 'doc', 'doxygen-enums-to-rst.xsl')
src_xsltproc = join_paths(meson.build_root(), 'xml', 'group__enums.xml')
dst_xstlproc = join_paths(meson.build_root(), 'doc', 'enums.rst')
rst_enum = custom_target('xsltproc',
input : doxygen,
output : 'enums.rst',
command: [ cmd_xsltproc, '-o', dst_xstlproc, xsl, src_xsltproc],
build_by_default : true)
# Effectively a noop copy. We *must* generate enums.rst so that will end up
# in the build directory. sphinx doesn't allow for multiple source
# directories, so any .rst and the conf.py must be copied over to the
# build directory too
src_rsts = ['index.rst', 'dbus.rst']
dst_rsts = [rst_enum]
foreach rst : src_rsts
f = configure_file(input : rst,
output : rst,
configuration : doc_config,
install : false)
dst_rsts += f
endforeach
sphinx_conf = configure_file(input : 'conf.py.in',
output : 'conf.py',
configuration : doc_config,
install : false)
custom_target('sphinx',
input : [ sphinx_conf, dst_rsts ],
output : 'html',
command : [ cmd_sphinx, '-b', 'html', '-q', '-n',
join_paths(meson.build_root(), 'doc'),
join_paths(meson.build_root(), 'doc', 'html') ],
build_by_default : true,
install : false)
endif

View File

@@ -1,237 +0,0 @@
# Database for libratbag to select the correct driver to use for gaming mice.
#
# The lookup keys are composed in:
# 70-libratbag-mouse.rules
#
# Match string format:
# mouse:<subsystem>:v<vid>p<pid>:name:<name>:
#
# Supported subsystems: usb, bluetooth
# vid/pid as 4-digit hex lowercase vendor/product
#
# if vid/pid is unavailable, use
# mouse:*:name:<name>:
# if name is unavailable, use
# mouse:<subsystem>:v<vid>p<pid>:*
#
# For example, the following 5 matches all match the same mouse:
# mouse:usb:v17efp6019:name:Lenovo Optical USB Mouse:
# mouse:usb:*:name:Lenovo Optical USB Mouse:
# mouse:usb:v17efp6019:*
# mouse:*:name:Lenovo Optical USB Mouse:
#
# To add local entries, create a new file
# /etc/udev/hwdb.d/71-libratbat-mouse-local.hwdb
# and add your rules there. To load the new rules execute (as root):
# udevadm hwdb --update
# udevadm trigger /dev/input/eventXX
# where /dev/input/eventXX is the mouse in question. If in
# doubt, simply use /dev/input/event* to reload all input rules.
#
# If your changes are generally applicable, preferably send them as a pull
# request to
# https://github.com/libratbag/libratbag
# or create a bug report on https://github.com/libratbag/libratbag/issues and
# include your new rules, a description of the device, and the output of
# udevadm info /dev/input/eventXX
# (or /dev/input/event*).
#
# Allowed properties are:
# RATBAG_DRIVER
# RATBAG_SVG
# Sort by brand, driver
##########################################
# Etekcity
##########################################
mouse:usb:v1ea7p4011:name:*:
RATBAG_DRIVER=etekcity
RATBAG_SVG=etekcity.svg
##########################################
# Logitech
##########################################
# M705
mouse:usb:v046dp101b:name:*:
RATBAG_DRIVER=hidpp10
# M570
mouse:usb:v046dp1028:name:*:
RATBAG_DRIVER=hidpp10
# G5
mouse:usb:v046dpc041:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_HIDPP10_DPI_LIST=400;800;1600;2000
# G5 2007
# http://support.logitech.com/en_us/product/g5-laser-mouse-product/specs
mouse:usb:v046dpc049:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_HIDPP10_DPI_LIST=400;800;1600;2000
RATBAG_HIDPP10_PROFILE_COUNT=1
# G7
# http://support.logitech.com/en_us/product/g7-laser-cordless-mouse/specs
mouse:usb:v046dpc51a:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_HIDPP10_PROFILE_COUNT=1
# G9
# http://support.logitech.com/en_us/product/g9-laser-mouse/specs
mouse:usb:v046dpc048:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_HIDPP10_DPI_LIST=0;200;400;600;800;1000;1200;1400;1600;1800;2000;2200;2400;2600;2800;3000;3200
RATBAG_HIDPP10_PROFILE=G9
RATBAG_HIDPP10_PROFILE_COUNT=5
# G9x [Original]
# http://support.logitech.com/en_us/product/g9x-laser-mouse/specs
mouse:usb:v046dpc066:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_HIDPP10_DPI=0:5700@23.53
RATBAG_HIDPP10_PROFILE=G500
RATBAG_HIDPP10_PROFILE_COUNT=5
# G9x [Call of Duty MW3 Edition]
# http://support.logitech.com/en_us/product/laser-mouse-g9x/specs
mouse:usb:v046dpc249:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_HIDPP10_DPI=0:5700@23.53
RATBAG_HIDPP10_PROFILE=G500
RATBAG_HIDPP10_PROFILE_COUNT=5
# G300
mouse:usb:v046dpc246:name:*:
RATBAG_DRIVER=logitech_g300
RATBAG_SVG=logitech-g300.svg
# G403 over USB
mouse:usb:v046dpc082:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g403.svg
# G500
# http://support.logitech.com/en_us/product/gaming-mouse-g500/specs
mouse:usb:v046dpc068:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_SVG=logitech-g500.svg
RATBAG_HIDPP10_DPI=0:5700@23.53
RATBAG_HIDPP10_PROFILE=G500
RATBAG_HIDPP10_PROFILE_COUNT=1
# G500s
# http://support.logitech.com/en_us/product/g500s-laser-gaming-mouse/specs
mouse:usb:v046dpc24e:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_SVG=logitech-g500s.svg
RATBAG_HIDPP10_DPI=0:8200@50
RATBAG_HIDPP10_PROFILE=G500
RATBAG_HIDPP10_PROFILE_COUNT=1
# G502 Proteus Core over USB
mouse:usb:v046dpc07d:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g502.svg
# G502 Proteus Spectrum over USB
mouse:usb:v046dpc332:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g502.svg
# G700 over USB
# http://support.logitech.com/en_us/product/wireless-gaming-mouse-g700/specs
mouse:usb:v046dpc06b:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_SVG=logitech-g700.svg
RATBAG_HIDPP10_DPI=0:5700@23.53
RATBAG_HIDPP10_PROFILE=G700
RATBAG_HIDPP10_PROFILE_COUNT=5
# G700 over wireless USB
# http://support.logitech.com/en_us/product/wireless-gaming-mouse-g700/specs
mouse:usb:v046dpc531:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_HIDPP10_DPI=0:5700@23.53
RATBAG_HIDPP10_PROFILE=G700
RATBAG_HIDPP10_INDEX=1
RATBAG_HIDPP10_PROFILE_COUNT=5
# G700s over USB
# http://support.logitech.com/en_us/product/g700s-rechargable-wireless-gaming-mouse/specs
mouse:usb:v046dpc07c:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_SVG=logitech-g700.svg
RATBAG_HIDPP10_DPI=0:8200@50
RATBAG_HIDPP10_PROFILE=G700
RATBAG_HIDPP10_PROFILE_COUNT=5
# G700s over wireless USB
# http://support.logitech.com/en_us/product/g700s-rechargable-wireless-gaming-mouse/specs
mouse:usb:v046dpc531:name:*:
RATBAG_DRIVER=hidpp10
RATBAG_SVG=logitech-g700.svg
RATBAG_HIDPP10_DPI=0:8200@50
RATBAG_HIDPP10_PROFILE=G700
RATBAG_HIDPP10_INDEX=1
RATBAG_HIDPP10_PROFILE_COUNT=5
# MX Master over unifying
mouse:usb:v046dp4041:name:*:
# MX Master over bluetooth
mouse:bluetooth:v046dpb012:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-mx_master.svg
# T650 over unifying
mouse:usb:v046dp4101:name:*:
RATBAG_DRIVER=hidpp20
# M325 over unifying
mouse:usb:v046dp400a:name:*:
RATBAG_DRIVER=hidpp20
# G303 over USB
mouse:usb:v046dpc080:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g303.svg
# Wireless Touchpad (Unifying)
mouse:usb:v046dp4011:name:*:
RATBAG_DRIVER=hidpp20
# G602 over wireless USB
mouse:usb:v046dpc537:name:*:
RATBAG_HIDPP20_INDEX=1
# G900 over USB
mouse:usb:v046dpc081:name:*:
RATBAG_SVG=logitech-g900.svg
# G900, G403 over wireless USB
mouse:usb:v046dpc539:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g900.svg
RATBAG_HIDPP20_INDEX=1
##########################################
# Roccat
##########################################
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,24 +0,0 @@
# do not edit this file, it will be overwritten on update
ACTION=="remove", GOTO="mouse_end"
KERNEL!="event*", GOTO="mouse_end"
ENV{ID_INPUT}=="", GOTO="mouse_end"
# mouse:<subsystem>:v<vid>p<pid>:name:<name>:*
KERNELS=="input*", ENV{ID_BUS}=="usb", \
IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
GOTO="mouse_end"
KERNELS=="input*", ENV{ID_BUS}=="bluetooth", \
IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
GOTO="mouse_end"
KERNELS=="input*", ENV{DEVPATH}=="/devices/virtual/misc/uhid/0003:*", \
IMPORT{builtin}="hwdb 'mouse:usb:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
GOTO="mouse_end"
KERNELS=="input*", ENV{DEVPATH}=="/devices/virtual/misc/uhid/0005:*", \
IMPORT{builtin}="hwdb 'mouse:bluetooth:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
GOTO="mouse_end"
DRIVERS=="psmouse", SUBSYSTEMS=="serio", \
IMPORT{builtin}="hwdb 'mouse:ps2::name:$attr{device/name}:'", \
GOTO="mouse_end"
LABEL="mouse_end"

View File

@@ -1,7 +1,8 @@
project('libratbag', 'c', 'cpp',
version : '0.9.900',
version : '0.9.901',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ])
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.36.0')
libratbag_version = meson.project_version().split('.')
@@ -37,9 +38,11 @@ add_project_arguments(cppflags, language: 'cpp')
# is generated at the end of this file
config_h = configuration_data()
config_h.set('_GNU_SOURCE', '1')
config_h.set_quoted('FALLBACK_SVG_NAME', 'fallback.svg')
libratbag_data_dir = join_paths(get_option('prefix'),
get_option('datadir'),
'libratbag')
libratbag_data_dir_devel = join_paths(meson.source_root(), 'data')
config_h.set_quoted('LIBRATBAG_DATA_DIR', libratbag_data_dir)
# dependencies
@@ -48,6 +51,7 @@ dep_udev = dependency('libudev')
dep_libevdev = dependency('libevdev')
dep_libsystemd = dependency('libsystemd', version : '>=227')
dep_systemd = dependency('systemd')
dep_glib = dependency('glib-2.0')
dep_lm = cc.find_library('m')
#### libutil.a ####
@@ -121,6 +125,7 @@ pkgconfig.generate (
#### libratbag.so ####
src_libratbag = [
'src/libratbag-enums.h',
'src/libratbag.h',
'src/driver-etekcity.c',
'src/driver-hidpp20.c',
@@ -128,9 +133,12 @@ src_libratbag = [
'src/driver-logitech-g300.c',
'src/driver-roccat.c',
'src/driver-gskill.c',
'src/driver-steelseries.c',
'src/driver-test.c',
'src/libratbag.c',
'src/libratbag.h',
'src/libratbag-data.c',
'src/libratbag-data.h',
'src/libratbag-hidraw.c',
'src/libratbag-hidraw.h',
'src/libratbag-private.h',
@@ -142,6 +150,7 @@ src_libratbag = [
deps_libratbag = [
dep_udev,
dep_libevdev,
dep_glib,
dep_libutil,
dep_libhidpp,
]
@@ -183,33 +192,6 @@ lib_libshared = static_library('shared',
)
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 : false,
)
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',
@@ -228,6 +210,15 @@ executable('hidpp20-dump-page',
install : false,
)
#### hidpp20-reset ####
src_hidpp20_reset = [ 'tools/hidpp20-reset.c' ]
executable('hidpp20-reset',
src_hidpp20_reset,
dependencies : [ dep_libhidpp ],
include_directories : include_directories('src'),
install : false,
)
#### lur-command ####
src_lur_command = [ 'tools/lur-command.c' ]
executable('lur-command',
@@ -237,6 +228,10 @@ executable('lur-command',
install : true,
)
man_config = configuration_data()
man_config.set('version', meson.project_version())
man_lur_command = configure_file (
input: 'tools/lur-command.man',
output: 'lur-command.1',
@@ -245,54 +240,80 @@ man_lur_command = configure_file (
install_dir : join_paths(get_option('mandir'), 'man1')
)
#### hwdb ####
udev_dir = get_option('udev-dir')
if udev_dir == ''
udev_dir = '@0@/lib/udev'.format(get_option('prefix'))
endif
udev_rules_dir = '@0@/rules.d'.format(udev_dir)
udev_hwdb_dir = '@0@/hwdb.d'.format(udev_dir)
#### data files ####
data_files = files(
'data/devices/etekcity-fixme.device',
'data/devices/gskill-MX-780.device',
'data/devices/logitech-g300.device',
'data/devices/logitech-g303.device',
'data/devices/logitech-g403.device',
'data/devices/logitech-g500.device',
'data/devices/logitech-g500s.device',
'data/devices/logitech-g502-Proteus-Core-over-USB.device',
'data/devices/logitech-g502-Proteus-Spectrum-over-USB.device',
'data/devices/logitech-g5-2007.device',
'data/devices/logitech-g5.device',
'data/devices/logitech-g602.device',
'data/devices/logitech-g700.device',
'data/devices/logitech-g700s.device',
'data/devices/logitech-g700s-wireless.device',
'data/devices/logitech-g700-wireless.device',
'data/devices/logitech-g703.device',
'data/devices/logitech-g7.device',
'data/devices/logitech-g900.device',
'data/devices/logitech-g900-wireless.device',
'data/devices/logitech-g9.device',
'data/devices/logitech-g9x-[Call-of-Duty-MW3-Edition].device',
'data/devices/logitech-g9x-[Original].device',
'data/devices/logitech-M325.device',
'data/devices/logitech-M570.device',
'data/devices/logitech-M705.device',
'data/devices/logitech-MX-Master.device',
'data/devices/logitech-T650.device',
'data/devices/logitech-Wireless-Touchpad.device',
'data/devices/roccat-kone-xtd.device',
'data/devices/steelseries-kinzu-v2.device',
'data/devices/steelseries-rival-310.device',
)
install_data('hwdb/70-libratbag-mouse.rules',
install_dir : udev_rules_dir)
install_data('hwdb/70-libratbag-mouse.hwdb',
install_dir : udev_hwdb_dir)
install_data(data_files,
install_dir : join_paths(get_option('datadir'), 'libratbag', 'devices'))
data_parse_test = find_program(join_paths(meson.source_root(), 'data/devices/data-parse-test.py'))
test('data-parse-test', data_parse_test,
args : [ '--svg-dir', join_paths(meson.source_root(), 'data/default'),
'--svg-dir', join_paths(meson.source_root(), 'data/gnome') ] + data_files)
#### svg files ####
default_svg_files = [
'data/default/etekcity.svg',
'data/default/logitech-g300.svg',
'data/default/logitech-g303.svg',
'data/default/logitech-g500s.svg',
'data/default/logitech-g502.svg',
'data/default/logitech-g700.svg',
'data/default/logitech-g900.svg',
'data/default/logitech-mx_master.svg',
'data/default/roccat-kone-xtd.svg'
]
install_data(default_svg_files,
install_dir : join_paths(get_option('datadir'), 'libratbag', 'default'))
gnome_svg_files = [
gnome_svg_files = files(
'data/gnome/fallback.svg',
'data/gnome/logitech-g300.svg',
'data/gnome/logitech-g303.svg',
'data/gnome/logitech-g403.svg',
'data/gnome/logitech-g500.svg',
'data/gnome/logitech-g500s.svg',
'data/gnome/logitech-g502.svg',
'data/gnome/logitech-g700.svg',
'data/gnome/logitech-g703.svg',
'data/gnome/logitech-g9.svg',
'data/gnome/logitech-g900.svg',
'data/gnome/roccat-kone-xtd.svg',
]
'data/gnome/steelseries-kinzu-v2.svg',
'data/gnome/steelseries-rival310.svg',
)
install_data(gnome_svg_files,
install_dir : join_paths(get_option('datadir'), 'libratbag', 'gnome'))
test_svg_files = find_program('data/gnome/check-svg.py')
test('check-svg', test_svg_files, args : gnome_svg_files)
#### 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 = environment()
env_test.set('RATBAG_TEST', '1')
test_context = executable('test-context',
['test/test-context.c'],
@@ -304,6 +325,11 @@ if enable_tests
dependencies : [ dep_libratbag, dep_check ],
include_directories : include_directories('src'),
install : false)
test_util = executable('test-util',
['test/test-util.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,
@@ -311,9 +337,10 @@ if enable_tests
dep_libutil],
include_directories : include_directories('src'),
install : false)
test('test-context', test_context, env : env_test)
test('test-device', test_device, env : env_test)
test('test-iconv-helper', test_iconv_helper, env : env_test)
test('test-context', test_context)
test('test-device', test_device)
test('test-util', test_util)
test('test-iconv-helper', test_iconv_helper)
valgrind = find_program('valgrind')
valgrind_suppressions_file = join_paths(meson.source_root(), 'test', 'valgrind.suppressions')
@@ -324,7 +351,6 @@ if enable_tests
'--quiet',
'--error-exitcode=3',
'--suppressions=' + valgrind_suppressions_file ],
env : env_test,
timeout_multiplier: 5)
executable('test-build-cxx',
@@ -352,11 +378,6 @@ if enable_tests
install : false)
endif
test_symbols_leak = find_program('test/symbols-leak-test')
test('symbols-leak-test',
test_symbols_leak,
args : [ meson.current_source_dir() ])
#### ratbagd ####
src_ratbagd = [
'src/shared-macro.h',
@@ -369,6 +390,7 @@ src_ratbagd = [
'ratbagd/ratbagd-device.c',
'ratbagd/ratbagd-profile.c',
'ratbagd/ratbagd-resolution.c',
'ratbagd/ratbagd-test.c',
'src/libratbag-util.h',
'src/libratbag-util.c',
]
@@ -395,7 +417,8 @@ executable('ratbagd.devel',
dependencies : deps_ratbagd,
include_directories : include_directories('src'),
install : false,
c_args : '-DRATBAG_DBUS_INTERFACE="ratbag_devel1_@0@"'.format(ratbagd_sha),
c_args : ['-DRATBAG_DBUS_INTERFACE="ratbag_devel1_@0@"'.format(ratbagd_sha),
'-DDISABLE_COREDUMP=1'],
)
config_ratbagd_devel = configuration_data()
@@ -462,19 +485,29 @@ custom_target('ratbagctl',
'--version', meson.project_version()],
install: true,
install_dir: get_option('bindir'))
install_man('tools/ratbagctl.1')
man_ratbagctl = configure_file (
input: 'tools/ratbagctl.1',
output: 'ratbagctl.1',
configuration: man_config,
install : true,
install_dir : join_paths(get_option('mandir'), 'man1')
)
config_ratbagctl_devel = configuration_data()
config_ratbagctl_devel.set('ratbagd_sha', ratbagd_sha)
config_ratbagctl_devel.set('MESON_BUILD_ROOT', meson.build_root())
config_ratbagctl_devel.set('LIBRATBAG_DATA_DIR', libratbag_data_dir_devel)
configure_file(input : 'tools/ratbagctl.devel.in',
output : 'ratbagctl.devel',
configuration : config_ratbagctl_devel)
configure_file(input : 'tools/ratbagctl.test.in',
output : 'ratbagctl.test',
configuration : config_ratbagctl_devel)
env_test = environment()
env_test.set('LIBRATBAG_DATA_DIR', libratbag_data_dir_devel)
ratbagctl_test = find_program(join_paths(meson.build_root(), 'ratbagctl.test'))
test('ratbagctl-test', ratbagctl_test, args: ['-v'])
test('ratbagctl-test', ratbagctl_test, args: ['-v'], env : env_test)
configure_file(input : 'tools/toolbox.py',
output : 'toolbox.py',
@@ -483,3 +516,46 @@ configure_file(input : 'tools/toolbox.py',
#### output files ####
configure_file(output: 'config.h', install: false, configuration: config_h)
subdir('doc')
#### swig generated ####
swig = find_program('swig')
swig_gen = generator(
swig,
output: ['@BASENAME@.c'],
arguments: ['-python', '-py3', '-o', './@OUTPUT@',
'-outdir', '.',
'-I' + join_paths(meson.source_root(), 'src'),
'-I' + join_paths(meson.source_root(), 'tools'),
'@INPUT@'],
)
wrapper_deps = [
dependency('python3'),
dep_libratbag,
dep_libshared,
]
i_source = join_paths(meson.source_root(), 'src', 'libratbag.i')
c_source = swig_gen.process(i_source)
shared_library(
'libratbag',
c_source,
name_prefix: '_',
c_args : ['-Wno-missing-prototypes', '-Wno-format-nonliteral'],
extra_files: [ i_source ] + src_libratbag ,
dependencies: deps_libratbag + wrapper_deps,
include_directories : include_directories('src', 'tools'),
install: false,
)
custom_target('ratbag-command',
output : 'ratbag-command',
input : ['tools/ratbagctl.in', 'tools/ratbagc.py'],
build_by_default : true,
command : [py3, join_paths(meson.source_root(), 'tools', 'merge_ratbagd.py'),
'@INPUT@',
'--output', '@OUTPUT@',
'--version', meson.project_version()],
install: false)

View File

@@ -1,6 +1,6 @@
option('udev-dir',
type: 'string',
default: '',
value: '',
description: 'udev base directory [default=$prefix/lib/udev]')
option('enable-tests',
@@ -10,10 +10,15 @@ option('enable-tests',
option('systemd-unit-dir',
type : 'string',
default : '',
value : '',
description : 'systemd unit directory [default=$libdir/systemd/system]')
option('dbus-root-dir',
type : 'string',
default : '',
value : '',
description : 'dbus service directory [default=$datadir/dbus-1]')
option('documentation',
type: 'boolean',
value: false,
description: 'Enable documentation build (default=no)')

View File

@@ -39,6 +39,7 @@
#include "libratbag-util.h"
struct ratbagd_button {
struct ratbagd_device *device;
struct ratbag_button *lib_button;
unsigned int index;
char *path;
@@ -112,7 +113,7 @@ static int ratbagd_button_set_button(sd_bus *bus,
NULL);
}
return r;
return 0;
}
static int ratbagd_button_get_special(sd_bus *bus,
@@ -164,104 +165,7 @@ static int ratbagd_button_set_special(sd_bus *bus,
NULL);
}
return r;
}
static int ratbagd_button_get_key(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_button *button = userdata;
unsigned int key;
unsigned int modifiers[10] = {0};
size_t nmodifiers = ELEMENTSOF(modifiers);
size_t i;
int r;
/* Return an array with the first element the key, the rest
are modifiers (or 0 if unset). If no key is set, the array is
just [0] */
r = sd_bus_message_open_container(reply, 'a', "u");
if (r < 0)
return r;
key = ratbag_button_get_key(button->lib_button,
modifiers,
&nmodifiers);
r = sd_bus_message_append(reply, "u", key);
if (r < 0)
return r;
if (key == 0)
nmodifiers = 0;
for (i = 0; i < nmodifiers; i++) {
r = sd_bus_message_append(reply, "u", modifiers[0]);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int ratbagd_button_set_key(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_button *button = userdata;
unsigned int key;
size_t nmodifiers = 10;
unsigned int modifiers[nmodifiers];
int r;
/* Expect an array with the first element the key, the rest
are modifiers (or 0 if unset). */
r = sd_bus_message_enter_container(m, 'a', "u");
if (r < 0)
return r;
r = sd_bus_message_read(m, "u", &key);
if (r < 0)
return r;
nmodifiers = 0;
while ((r = sd_bus_message_read_basic(m, 'u',
&modifiers[nmodifiers++])) > 0)
;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
r = ratbag_button_set_key(button->lib_button, key, modifiers, nmodifiers);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"KeyMapping",
NULL);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"ActionType",
NULL);
}
return r;
return 0;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbag_button_macro *, ratbag_button_macro_unref);
@@ -305,6 +209,8 @@ static int ratbagd_button_get_macro(sd_bus *bus,
case RATBAG_MACRO_EVENT_WAIT:
value = ratbag_button_macro_get_event_timeout(macro, idx);
break;
default:
abort();
}
r = sd_bus_message_append(reply, "(uu)", type, value);
@@ -336,8 +242,11 @@ static int ratbagd_button_set_macro(sd_bus *bus,
macro = ratbag_button_macro_new("macro");
while ((r = sd_bus_message_read(m, "(uu)", &type, &value)) > 0) {
r = ratbag_button_macro_set_event(macro, idx++, type, value);
if (r < 0)
return r;
if (r < 0) {
r = ratbagd_device_resync(button->device, bus);
if (r < 0)
return r;
}
}
if (r < 0)
return r;
@@ -347,8 +256,11 @@ static int ratbagd_button_set_macro(sd_bus *bus,
return r;
r = ratbag_button_set_macro(button->lib_button, macro);
if (r < 0)
return r;
if (r < 0) {
r = ratbagd_device_resync(button->device, bus);
if (r < 0)
return r;
}
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
@@ -365,7 +277,7 @@ static int ratbagd_button_set_macro(sd_bus *bus,
NULL);
}
return r;
return 0;
}
static int ratbagd_button_get_action_type(sd_bus *bus,
@@ -380,6 +292,8 @@ static int ratbagd_button_get_action_type(sd_bus *bus,
enum ratbag_button_action_type type;
type = ratbag_button_get_action_type(button->lib_button);
if (type == RATBAG_BUTTON_ACTION_TYPE_KEY)
type = RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
return sd_bus_message_append(reply, "u", type);
}
@@ -397,7 +311,6 @@ static int ratbagd_button_get_action_types(sd_bus *bus,
enum ratbag_button_action_type types[] = {
RATBAG_BUTTON_ACTION_TYPE_BUTTON,
RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
RATBAG_BUTTON_ACTION_TYPE_KEY,
RATBAG_BUTTON_ACTION_TYPE_MACRO
};
enum ratbag_button_action_type *t;
@@ -431,8 +344,14 @@ static int ratbagd_button_disable(sd_bus_message *m,
return r;
r = ratbag_button_disable(button->lib_button);
if (r < 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
r = ratbagd_device_resync(button->device, bus);
if (r < 0)
return r;
}
return sd_bus_reply_method_return(m, "u", r);
return sd_bus_reply_method_return(m, "u", 0);
}
const sd_bus_vtable ratbagd_button_vtable[] = {
@@ -447,10 +366,6 @@ const sd_bus_vtable ratbagd_button_vtable[] = {
ratbagd_button_get_special,
ratbagd_button_set_special,
0, SD_BUS_VTABLE_UNPRIVILEGED | SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("KeyMapping", "au",
ratbagd_button_get_key,
ratbagd_button_set_key,
0, SD_BUS_VTABLE_UNPRIVILEGED | SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("Macro", "a(uu)",
ratbagd_button_get_macro,
ratbagd_button_set_macro,
@@ -476,6 +391,7 @@ int ratbagd_button_new(struct ratbagd_button **out,
assert(lib_button);
button = zalloc(sizeof(*button));
button->device = device;
button->lib_button = lib_button;
button->index = index;
@@ -483,7 +399,7 @@ int ratbagd_button_new(struct ratbagd_button **out,
sprintf(button_buffer, "b%u", index);
r = sd_bus_path_encode_many(&button->path,
RATBAGD_OBJ_ROOT "/button/%/%/%",
ratbagd_device_get_name(device),
ratbagd_device_get_sysname(device),
profile_buffer,
button_buffer);
if (r < 0)

View File

@@ -43,7 +43,7 @@
struct ratbagd_device {
struct ratbagd *ctx;
RBNode node;
char *name;
char *sysname;
char *path;
struct ratbag_device *lib_device;
@@ -125,8 +125,8 @@ static int ratbagd_device_get_device_name(sd_bus *bus,
name = ratbag_device_get_name(device->lib_device);
if (!name) {
log_error("Unable to fetch name for %s\n",
ratbagd_device_get_name(device));
log_error("%s: failed to fetch name\n",
ratbagd_device_get_sysname(device));
name = "";
}
@@ -146,8 +146,8 @@ static int ratbagd_device_get_svg(sd_bus *bus,
svg = ratbag_device_get_svg_name(device->lib_device);
if (!svg) {
log_error("Unable to fetch SVG for %s\n",
ratbagd_device_get_name(device));
log_error("%s: failed to fetch SVG\n",
ratbagd_device_get_sysname(device));
svg = "";
}
@@ -163,6 +163,11 @@ static int ratbagd_device_get_theme_svg(sd_bus_message *m,
const char *theme;
const char *svg;
int r;
const char *datadir;
datadir = getenv("LIBRATBAG_DATA_DIR");
if (!datadir)
datadir = LIBRATBAG_DATA_DIR;
r = sd_bus_message_read(m, "s", &theme);
if (r < 0)
@@ -171,14 +176,13 @@ static int ratbagd_device_get_theme_svg(sd_bus_message *m,
svg = ratbag_device_get_svg_name(device->lib_device);
if (!svg) {
log_error("Unable to fetch SVG for %s\n",
ratbagd_device_get_name(device));
goto out;
log_error("%s: failed to fetch SVG, using fallback\n",
ratbagd_device_get_sysname(device));
svg = FALLBACK_SVG_NAME;
}
sprintf(svg_path, "%s/%s/%s", LIBRATBAG_DATA_DIR, theme, svg);
snprintf(svg_path, sizeof(svg_path), "%s/%s/%s", datadir, theme, svg);
out:
return sd_bus_reply_method_return(m, "s", svg_path);
}
@@ -222,8 +226,14 @@ static int ratbagd_device_commit(sd_bus_message *m,
int r;
r = ratbag_device_commit(device->lib_device);
if (r < 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
r = ratbagd_device_resync(device, bus);
if (r < 0)
return r;
}
return sd_bus_reply_method_return(m, "u", r);
return sd_bus_reply_method_return(m, "u", 0);
}
static int
@@ -272,19 +282,20 @@ ratbagd_device_get_capabilities(sd_bus *bus,
const sd_bus_vtable ratbagd_device_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Id", "s", NULL, offsetof(struct ratbagd_device, name), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Id", "s", NULL, offsetof(struct ratbagd_device, sysname), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Capabilities", "au", ratbagd_device_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Name", "s", ratbagd_device_get_device_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Svg", "s", ratbagd_device_get_svg, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Profiles", "ao", ratbagd_device_get_profiles, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD("GetSvg", "s", "s", ratbagd_device_get_theme_svg, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Commit", "", "u", ratbagd_device_commit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("Resync", "", 0),
SD_BUS_VTABLE_END,
};
int ratbagd_device_new(struct ratbagd_device **out,
struct ratbagd *ctx,
const char *name,
const char *sysname,
struct ratbag_device *lib_device)
{
_cleanup_(ratbagd_device_freep) struct ratbagd_device *device = NULL;
@@ -294,17 +305,17 @@ int ratbagd_device_new(struct ratbagd_device **out,
assert(out);
assert(ctx);
assert(name);
assert(sysname);
device = zalloc(sizeof(*device));
device->ctx = ctx;
rbnode_init(&device->node);
device->lib_device = ratbag_device_ref(lib_device);
device->name = strdup_safe(name);
device->sysname = strdup_safe(sysname);
r = sd_bus_path_encode(RATBAGD_OBJ_ROOT "/device",
device->name,
device->sysname,
&device->path);
if (r < 0)
return r;
@@ -314,7 +325,7 @@ int ratbagd_device_new(struct ratbagd_device **out,
device->profiles = zalloc(device->n_profiles * sizeof(*device->profiles));
log_verbose("%s: \"%s\", %d profiles\n",
name,
sysname,
ratbag_device_get_name(lib_device),
device->n_profiles);
@@ -329,8 +340,8 @@ int ratbagd_device_new(struct ratbagd_device **out,
i);
if (r < 0) {
errno = -r;
log_error("Cannot allocate profile for '%s': %m\n",
device->name);
log_error("%s: failed to allocate profile: %m\n",
device->sysname);
}
}
@@ -354,17 +365,17 @@ struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device)
device->profiles = mfree(device->profiles);
device->lib_device = ratbag_device_unref(device->lib_device);
device->path = mfree(device->path);
device->name = mfree(device->name);
device->sysname = mfree(device->sysname);
assert(!device->lib_device); /* ratbag yields !NULL if still pinned */
return mfree(device);
}
const char *ratbagd_device_get_name(struct ratbagd_device *device)
const char *ratbagd_device_get_sysname(struct ratbagd_device *device)
{
assert(device);
return device->name;
return device->sysname;
}
const char *ratbagd_device_get_path(struct ratbagd_device *device)
@@ -385,6 +396,17 @@ unsigned int ratbagd_device_get_num_leds(struct ratbagd_device *device)
return ratbag_device_get_num_leds(device->lib_device);
}
int ratbagd_device_resync(struct ratbagd_device *device, sd_bus *bus)
{
assert(device);
assert(bus);
return sd_bus_emit_signal(bus,
device->path,
RATBAGD_NAME_ROOT ".Device",
"Resync",
NULL);
}
bool ratbagd_device_linked(struct ratbagd_device *device)
{
return device && rbnode_linked(&device->node);
@@ -407,7 +429,7 @@ void ratbagd_device_link(struct ratbagd_device *device)
while (*node) {
parent = *node;
iter = ratbagd_device_from_node(parent);
v = strcmp(device->name, iter->name);
v = strcmp(device->sysname, iter->sysname);
/* if there's a duplicate, the caller screwed up */
assert(v != 0);
@@ -425,7 +447,7 @@ void ratbagd_device_link(struct ratbagd_device *device)
/* register profile interfaces */
r = sd_bus_path_encode_many(&prefix,
RATBAGD_OBJ_ROOT "/profile/%",
device->name);
device->sysname);
if (r >= 0) {
r = sd_bus_add_fallback_vtable(device->ctx->bus,
&device->profile_vtable_slot,
@@ -443,8 +465,8 @@ void ratbagd_device_link(struct ratbagd_device *device)
}
if (r < 0) {
errno = -r;
log_error("Cannot register profile interfaces for '%s': %m\n",
device->name);
log_error("%s: failed to register profile interfaces: %m\n",
device->sysname);
return;
}
@@ -453,24 +475,24 @@ void ratbagd_device_link(struct ratbagd_device *device)
device,
device->profiles[i]);
if (r < 0) {
log_error("Cannot register resolutions for '%s': %m\n",
device->name);
log_error("%s: failed to register resolutions: %m\n",
device->sysname);
}
r = ratbagd_profile_register_buttons(device->ctx->bus,
device,
device->profiles[i]);
if (r < 0) {
log_error("Cannot register buttons for '%s': %m\n",
device->name);
log_error("%s: failed to register buttons: %m\n",
device->sysname);
}
r = ratbagd_profile_register_leds(device->ctx->bus,
device,
device->profiles[i]);
if (r < 0) {
log_error("Cannot register leds for '%s': %m\n",
device->name);
log_error("%s: failed to register leds: %m\n",
device->sysname);
}
}
}
@@ -502,7 +524,7 @@ struct ratbagd_device *ratbagd_device_lookup(struct ratbagd *ctx,
node = ctx->device_map.root;
while (node) {
device = ratbagd_device_from_node(node);
v = strcmp(name, device->name);
v = strcmp(name, device->sysname);
if (!v)
return device;
else if (v < 0)

View File

@@ -37,8 +37,35 @@ struct ratbagd_led {
struct ratbag_led *lib_led;
unsigned int index;
char *path;
enum ratbag_led_colordepth colordepth;
};
static int ratbagd_led_get_modes(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_led *led = userdata;
enum ratbag_led_mode mode = 0;
int r;
r = sd_bus_message_open_container(reply, 'a', "u");
if (r < 0)
return r;
while (mode <= RATBAG_LED_BREATHING) {
if (ratbag_led_has_mode(led->lib_led, mode))
sd_bus_message_append(reply, "u", mode);
mode++;
}
return sd_bus_message_close_container(reply);
}
static int ratbagd_led_get_mode(sd_bus *bus,
const char *path,
const char *interface,
@@ -81,7 +108,7 @@ static int ratbagd_led_set_mode(sd_bus *bus,
NULL);
}
return r;
return 0;
}
static int ratbagd_led_get_type(sd_bus *bus,
@@ -131,8 +158,12 @@ static int ratbagd_led_set_color(sd_bus *bus,
if (r < 0)
return r;
if (c.red > 255 || c.green > 255 || c.blue > 255)
return RATBAG_ERROR_VALUE;
if (c.red > 255)
c.red = 255;
if (c.green > 255)
c.green = 255;
if (c.blue > 255)
c.blue = 255;
r = ratbag_led_set_color(led->lib_led, c);
@@ -145,31 +176,31 @@ static int ratbagd_led_set_color(sd_bus *bus,
NULL);
}
return r;
return 0;
}
static int ratbagd_led_get_effect_rate(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
static int ratbagd_led_get_effect_duration(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_led *led = userdata;
int rate;
rate = ratbag_led_get_effect_rate(led->lib_led);
rate = ratbag_led_get_effect_duration(led->lib_led);
return sd_bus_message_append(reply, "u", rate);
}
static int ratbagd_led_set_effect_rate(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *m,
void *userdata,
sd_bus_error *error)
static int ratbagd_led_set_effect_duration(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_led *led = userdata;
unsigned int rate;
@@ -179,21 +210,21 @@ static int ratbagd_led_set_effect_rate(sd_bus *bus,
if (r < 0)
return r;
if (rate < 100 || rate > 20000)
return 0;
if (rate > 10000)
rate = 10000;
r = ratbag_led_set_effect_rate(led->lib_led, rate);
r = ratbag_led_set_effect_duration(led->lib_led, rate);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
led->path,
RATBAGD_NAME_ROOT ".Led",
"EffectRate",
"EffectDuration",
NULL);
}
return r;
return 0;
}
static int ratbagd_led_get_brightness(sd_bus *bus,
@@ -228,7 +259,7 @@ static int ratbagd_led_set_brightness(sd_bus *bus,
return r;
if (brightness > 255)
return 0;
brightness = 255;
r = ratbag_led_set_brightness(led->lib_led, brightness);
@@ -241,12 +272,13 @@ static int ratbagd_led_set_brightness(sd_bus *bus,
NULL);
}
return r;
return 0;
}
const sd_bus_vtable ratbagd_led_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_led, index), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Modes", "au", ratbagd_led_get_modes, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_WRITABLE_PROPERTY("Mode", "u",
ratbagd_led_get_mode,
ratbagd_led_set_mode, 0,
@@ -255,8 +287,9 @@ const sd_bus_vtable ratbagd_led_vtable[] = {
SD_BUS_WRITABLE_PROPERTY("Color", "(uuu)",
ratbagd_led_get_color, ratbagd_led_set_color, 0,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("EffectRate", "u",
ratbagd_led_get_effect_rate, ratbagd_led_set_effect_rate, 0,
SD_BUS_PROPERTY("ColorDepth", "u", NULL, offsetof(struct ratbagd_led, colordepth), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_WRITABLE_PROPERTY("EffectDuration", "u",
ratbagd_led_get_effect_duration, ratbagd_led_set_effect_duration, 0,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("Brightness", "u",
ratbagd_led_get_brightness, ratbagd_led_set_brightness, 0,
@@ -281,12 +314,13 @@ int ratbagd_led_new(struct ratbagd_led **out,
led = zalloc(sizeof(*led));
led->lib_led = lib_led;
led->index = index;
led->colordepth = ratbag_led_get_colordepth(lib_led);
sprintf(profile_buffer, "p%u", ratbagd_profile_get_index(profile));
sprintf(led_buffer, "l%u", index);
r = sd_bus_path_encode_many(&led->path,
RATBAGD_OBJ_ROOT "/led/%/%/%",
ratbagd_device_get_name(device),
ratbagd_device_get_sysname(device),
profile_buffer,
led_buffer);
if (r < 0)

View File

@@ -268,17 +268,13 @@ static int ratbagd_profile_find_led(sd_bus *bus,
static int ratbagd_profile_active_signal_cb(sd_bus *bus,
struct ratbagd_profile *profile)
{
struct ratbag_profile *lib_profile = profile->lib_profile;
/* FIXME: we should cache is active and only send the signal for
* those profiles where it changed */
(void) sd_bus_emit_signal(bus,
profile->path,
RATBAGD_NAME_ROOT ".Profile",
"IsActive",
"b",
ratbag_profile_is_active(lib_profile));
(void) sd_bus_emit_properties_changed(bus,
profile->path,
RATBAGD_NAME_ROOT ".Profile",
"IsActive",
NULL);
return 0;
}
@@ -294,12 +290,19 @@ static int ratbagd_profile_set_active(sd_bus_message *m,
if (r < 0)
return r;
r = ratbag_profile_set_active(profile->lib_profile);
if (r < 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
r = ratbagd_device_resync(profile->device, bus);
if (r < 0)
return r;
}
ratbagd_for_each_profile_signal(sd_bus_message_get_bus(m),
profile->device,
ratbagd_profile_active_signal_cb);
return sd_bus_reply_method_return(m, "u",
ratbag_profile_set_active(profile->lib_profile));
return sd_bus_reply_method_return(m, "u", 0);
}
static int
@@ -329,7 +332,7 @@ ratbagd_profile_set_enabled(sd_bus *bus,
NULL);
}
return r;
return 0;
}
static int
@@ -347,13 +350,97 @@ ratbagd_profile_is_enabled(sd_bus *bus,
return sd_bus_message_append(reply, "b", enabled);
}
static int
ratbagd_profile_set_name(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_profile *profile = userdata;
char *name;
int r;
r = sd_bus_message_read(m, "s", &name);
if (r < 0)
return r;
r = ratbag_profile_set_name(profile->lib_profile, name);
if (r == 0) {
sd_bus_emit_properties_changed(bus,
profile->path,
RATBAGD_NAME_ROOT ".Profile",
"Name",
NULL);
}
return 0;
}
static int
ratbagd_profile_get_name(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_profile *profile = userdata;
const char *name = ratbag_profile_get_name(profile->lib_profile);
return sd_bus_message_append(reply, "s", name);
}
static int
ratbagd_profile_get_capabilities(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_profile *profile = userdata;
struct ratbag_profile *lib_profile = profile->lib_profile;
enum ratbag_profile_capability cap;
enum ratbag_profile_capability caps[] = {
RATBAG_PROFILE_CAP_WRITABLE_NAME,
};
int r;
size_t i;
r = sd_bus_message_open_container(reply, 'a', "u");
if (r < 0)
return r;
for (i = 0; i < ELEMENTSOF(caps); i++) {
cap = caps[i];
if (ratbag_profile_has_capability(lib_profile, cap)) {
r = sd_bus_message_append(reply, "u", cap);
if (r < 0)
return r;
}
}
return sd_bus_message_close_container(reply);
}
const sd_bus_vtable ratbagd_profile_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_WRITABLE_PROPERTY("Name", "s",
ratbagd_profile_get_name,
ratbagd_profile_set_name, 0,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("Enabled", "b",
ratbagd_profile_is_enabled,
ratbagd_profile_set_enabled, 0,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_profile, index), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Capabilities", "au", ratbagd_profile_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Resolutions", "ao", ratbagd_profile_get_resolutions, 0, 0),
SD_BUS_PROPERTY("Buttons", "ao", ratbagd_profile_get_buttons, 0, 0),
SD_BUS_PROPERTY("Leds", "ao", ratbagd_profile_get_leds, 0, 0),
@@ -387,7 +474,7 @@ int ratbagd_profile_new(struct ratbagd_profile **out,
sprintf(index_buffer, "p%u", index);
r = sd_bus_path_encode_many(&profile->path,
RATBAGD_OBJ_ROOT "/profile/%/%",
ratbagd_device_get_name(device),
ratbagd_device_get_sysname(device),
index_buffer);
if (r < 0)
return r;
@@ -413,8 +500,8 @@ int ratbagd_profile_new(struct ratbagd_profile **out,
i);
if (r < 0) {
errno = -r;
log_error("Cannot allocate resolution for '%s': %m\n",
ratbagd_device_get_name(device));
log_error("%s: failed to allocate resolution: %m\n",
ratbagd_device_get_sysname(device));
}
}
@@ -430,8 +517,8 @@ int ratbagd_profile_new(struct ratbagd_profile **out,
i);
if (r < 0) {
errno = -r;
log_error("Cannot allocate button for '%s': %m\n",
ratbagd_device_get_name(device));
log_error("%s: failed to allocate button: %m\n",
ratbagd_device_get_sysname(device));
}
}
@@ -447,8 +534,8 @@ int ratbagd_profile_new(struct ratbagd_profile **out,
i);
if (r < 0) {
errno = -r;
log_error("Cannot allocate led for '%s': %m\n",
ratbagd_device_get_name(device));
log_error("%s: failed to allocate led: %m\n",
ratbagd_device_get_sysname(device));
}
}
@@ -540,7 +627,7 @@ int ratbagd_profile_register_resolutions(struct sd_bus *bus,
/* register resolution interfaces */
r = sd_bus_path_encode_many(&prefix,
RATBAGD_OBJ_ROOT "/resolution/%/%",
ratbagd_device_get_name(device),
ratbagd_device_get_sysname(device),
index_buffer);
if (r >= 0) {
@@ -560,8 +647,8 @@ int ratbagd_profile_register_resolutions(struct sd_bus *bus,
}
if (r < 0) {
errno = -r;
log_error("Cannot register resolutions for '%s': %m\n",
ratbagd_device_get_name(device));
log_error("%s: failed to register resolutions: %m\n",
ratbagd_device_get_sysname(device));
}
return 0;
@@ -606,7 +693,7 @@ int ratbagd_profile_register_buttons(struct sd_bus *bus,
/* register button interfaces */
r = sd_bus_path_encode_many(&prefix,
RATBAGD_OBJ_ROOT "/button/%/%",
ratbagd_device_get_name(device),
ratbagd_device_get_sysname(device),
index_buffer);
if (r >= 0) {
@@ -626,8 +713,8 @@ int ratbagd_profile_register_buttons(struct sd_bus *bus,
}
if (r < 0) {
errno = -r;
log_error("Cannot register buttons for '%s': %m\n",
ratbagd_device_get_name(device));
log_error("%s: failed to register buttons: %m\n",
ratbagd_device_get_sysname(device));
}
return 0;
@@ -672,7 +759,7 @@ int ratbagd_profile_register_leds(struct sd_bus *bus,
/* register led interfaces */
r = sd_bus_path_encode_many(&prefix,
RATBAGD_OBJ_ROOT "/led/%/%",
ratbagd_device_get_name(device),
ratbagd_device_get_sysname(device),
index_buffer);
if (r >= 0) {
@@ -692,8 +779,8 @@ int ratbagd_profile_register_leds(struct sd_bus *bus,
}
if (r < 0) {
errno = -r;
log_error("Cannot register leds for '%s': %m\n",
ratbagd_device_get_name(device));
log_error("%s: failed to register leds: %m\n",
ratbagd_device_get_sysname(device));
}
return 0;

View File

@@ -38,62 +38,19 @@
#include "libratbag-util.h"
struct ratbagd_resolution {
struct ratbagd_device *device;
struct ratbagd_profile *profile;
struct ratbag_resolution *lib_resolution;
unsigned int index;
char *path;
};
static int
ratbagd_resolution_set_report_rate(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_resolution *resolution = userdata;
unsigned int rate;
int r;
r = sd_bus_message_read(m, "u", &rate);
if (r < 0)
return r;
/* basic sanity check */
if (rate > 5000 || rate % 100)
return 0;
r = ratbag_resolution_set_report_rate(resolution->lib_resolution,
rate);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
resolution->path,
RATBAGD_NAME_ROOT ".Resolution",
"ReportRate",
NULL);
}
return r;
}
static int ratbagd_resolution_active_signal_cb(sd_bus *bus,
struct ratbagd_resolution *resolution)
{
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
/* FIXME: we should cache is_active and only send the signal for
* those resolutions where it changed */
(void) sd_bus_emit_signal(bus,
resolution->path,
RATBAGD_NAME_ROOT ".Resolution",
"IsActive",
"b",
ratbag_resolution_is_active(lib_resolution));
(void) sd_bus_emit_properties_changed(bus,
resolution->path,
RATBAGD_NAME_ROOT ".Resolution",
@@ -111,31 +68,26 @@ static int ratbagd_resolution_set_active(sd_bus_message *m,
int r;
r = ratbag_resolution_set_active(resolution->lib_resolution);
if (r < 0)
return r;
if (r < 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
r = ratbagd_device_resync(resolution->device, bus);
if (r < 0)
return r;
}
ratbagd_for_each_resolution_signal(sd_bus_message_get_bus(m),
resolution->profile,
ratbagd_resolution_active_signal_cb);
return sd_bus_reply_method_return(m, "u", r);
return sd_bus_reply_method_return(m, "u", 0);
}
static int ratbagd_resolution_default_signal_cb(sd_bus *bus,
struct ratbagd_resolution *resolution)
{
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
/* FIXME: we should cache is default and only send the signal for
* those resolutions where it changed */
(void) sd_bus_emit_signal(bus,
resolution->path,
RATBAGD_NAME_ROOT ".Resolution",
"IsDefault",
"b",
ratbag_resolution_is_default(lib_resolution));
(void) sd_bus_emit_properties_changed(bus,
resolution->path,
RATBAGD_NAME_ROOT ".Resolution",
@@ -153,14 +105,18 @@ static int ratbagd_resolution_set_default(sd_bus_message *m,
int r;
r = ratbag_resolution_set_default(resolution->lib_resolution);
if (r < 0)
return r;
if (r < 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
r = ratbagd_device_resync(resolution->device, bus);
if (r < 0)
return r;
}
ratbagd_for_each_resolution_signal(sd_bus_message_get_bus(m),
resolution->profile,
ratbagd_resolution_default_signal_cb);
return sd_bus_reply_method_return(m, "u", r);
return sd_bus_reply_method_return(m, "u", 0);
}
static int
@@ -174,7 +130,7 @@ ratbagd_resolution_get_capabilities(sd_bus *bus,
{
struct ratbagd_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
enum ratbag_device_capability cap;
enum ratbag_resolution_capability cap;
int r;
r = sd_bus_message_open_container(reply, 'a', "u");
@@ -234,6 +190,38 @@ ratbagd_resolution_is_default(sd_bus *bus,
return sd_bus_message_append(reply, "b", is_default);
}
static int
ratbagd_resolution_get_resolutions(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
unsigned int dpis[300];
unsigned int ndpis = ARRAY_LENGTH(dpis);
int r;
r = sd_bus_message_open_container(reply, 'a', "u");
if (r < 0)
return r;
ndpis = ratbag_resolution_get_dpi_list(lib_resolution,
dpis, ndpis);
assert(ndpis <= ARRAY_LENGTH(dpis));
for (unsigned int i = 0; i < ndpis; i++) {
r = sd_bus_message_append(reply, "u", dpis[i]);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int
ratbagd_resolution_get_resolution(sd_bus *bus,
const char *path,
@@ -288,17 +276,17 @@ ratbagd_resolution_set_resolution(sd_bus *bus,
NULL);
}
return r;
return 0;
}
static int
ratbagd_resolution_get_rate(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
ratbagd_resolution_get_report_rate(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
@@ -309,37 +297,73 @@ ratbagd_resolution_get_rate(sd_bus *bus,
}
static int
ratbagd_resolution_get_minimum(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
ratbagd_resolution_get_report_rates(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
int min;
unsigned int rates[5];
unsigned int nrates = ARRAY_LENGTH(rates);
int r;
min = ratbag_resolution_get_dpi_minimum(lib_resolution);
return sd_bus_message_append(reply, "u", min);
r = sd_bus_message_open_container(reply, 'a', "u");
if (r < 0)
return r;
nrates = ratbag_resolution_get_report_rate_list(lib_resolution,
rates, nrates);
assert(nrates <= ARRAY_LENGTH(rates));
for (unsigned int i = 0; i < nrates; i++) {
r = sd_bus_message_append(reply, "u", rates[i]);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int
ratbagd_resolution_get_maximum(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
ratbagd_resolution_set_report_rate(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
int max;
unsigned int rate;
int r;
max = ratbag_resolution_get_dpi_maximum(lib_resolution);
return sd_bus_message_append(reply, "u", max);
r = sd_bus_message_read(m, "u", &rate);
if (r < 0)
return r;
/* basic sanity check */
if (rate > 5000)
rate = 5000;
else if (rate % 100)
rate = rate - (rate % 100);
r = ratbag_resolution_set_report_rate(resolution->lib_resolution,
rate);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
resolution->path,
RATBAGD_NAME_ROOT ".Resolution",
"ReportRate",
NULL);
}
return 0;
}
const sd_bus_vtable ratbagd_resolution_vtable[] = {
@@ -352,12 +376,12 @@ const sd_bus_vtable ratbagd_resolution_vtable[] = {
ratbagd_resolution_get_resolution,
ratbagd_resolution_set_resolution, 0,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Resolutions", "au", ratbagd_resolution_get_resolutions, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_WRITABLE_PROPERTY("ReportRate", "u",
ratbagd_resolution_get_rate,
ratbagd_resolution_get_report_rate,
ratbagd_resolution_set_report_rate, 0,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Maximum", "u", ratbagd_resolution_get_maximum, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Minimum", "u", ratbagd_resolution_get_minimum, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ReportRates", "au", ratbagd_resolution_get_report_rates, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD("SetActive", "", "u", ratbagd_resolution_set_active, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetDefault", "", "u", ratbagd_resolution_set_default, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
@@ -381,6 +405,7 @@ int ratbagd_resolution_new(struct ratbagd_resolution **out,
if (!resolution)
return -ENOMEM;
resolution->device = device;
resolution->profile = profile;
resolution->lib_resolution = lib_resolution;
resolution->index = index;
@@ -389,7 +414,7 @@ int ratbagd_resolution_new(struct ratbagd_resolution **out,
sprintf(resolution_buffer, "r%u", index);
r = sd_bus_path_encode_many(&resolution->path,
RATBAGD_OBJ_ROOT "/resolution/%/%/%",
ratbagd_device_get_name(device),
ratbagd_device_get_sysname(device),
profile_buffer,
resolution_buffer);
if (r < 0)

225
ratbagd/ratbagd-test.c Normal file
View File

@@ -0,0 +1,225 @@
/*
* Copyright © 2017 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 "ratbagd-test.h"
#ifdef RATBAG_DEVELOPER_EDITION
#include <linux/input.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include "libratbag-test.h"
/* A pre-setup sane device. Use for sanity testing by toggling the various
* error conditions.
*/
static const struct ratbag_test_device ratbagd_test_device_descr = {
.num_profiles = 4,
.num_resolutions = 3,
.num_buttons = 4,
.num_leds = 3,
.svg = "fallback.svg",
.profiles = {
{
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 0 },
{ .button_type = RATBAG_BUTTON_TYPE_MIDDLE,
.action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = KEY_3 },
{ .button_type = RATBAG_BUTTON_TYPE_RIGHT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
.special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO,
.macro = {
{ .type = RATBAG_MACRO_EVENT_KEY_PRESSED,
.value = KEY_B },
{ .type = RATBAG_MACRO_EVENT_KEY_RELEASED,
.value = KEY_B },
{ .type = RATBAG_MACRO_EVENT_WAIT,
.value = 300 },
},
}
},
.resolutions = {
{ .xres = 100, .yres = 200, .hz = 1000,
.dpi_min = 50, .dpi_max = 5000,
.report_rates = { 500, 1000 } },
{ .xres = 200, .yres = 300, .hz = 1000, .active = true, .dflt = true },
{ .xres = 300, .yres = 400, .hz = 1000 },
},
.active = true,
.dflt = false,
.leds = {
{
.mode = RATBAG_LED_OFF,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20,
.type = RATBAG_LED_TYPE_LOGO,
},
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20,
.type = RATBAG_LED_TYPE_SIDE,
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.ms = 333,
.brightness = 40,
.type = RATBAG_LED_TYPE_SIDE,
}
},
},
{
.buttons = {
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 4 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 5 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 6 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 7 },
},
.resolutions = {
{ .xres = 1100, .yres = 1200, .hz = 2000, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 1200, .yres = 1300, .hz = 2000, .dflt = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 1300, .yres = 1400, .hz = 2000, .active = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
},
.active = false,
.dflt = true,
.caps = { RATBAG_PROFILE_CAP_WRITABLE_NAME },
},
{
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
.special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO,
.macro = {
{ .type = RATBAG_MACRO_EVENT_KEY_PRESSED,
.value = KEY_A },
{ .type = RATBAG_MACRO_EVENT_KEY_RELEASED,
.value = KEY_A },
{ .type = RATBAG_MACRO_EVENT_WAIT,
.value = 150 },
}
},
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 2 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 3 },
},
.resolutions = {
{ .xres = 2100, .yres = 2200, .hz = 3000, .active = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 2200, .yres = 2300, .hz = 3000, .dflt = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 2300, .yres = 2400, .hz = 3000, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
},
.leds = {
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.ms = 333,
.brightness = 40
}
},
.active = false,
.dflt = false,
},
{
.disabled = true,
},
},
.destroyed = NULL,
.destroyed_data = NULL,
};
int ratbagd_reset_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
static struct ratbagd_device *ratbagd_test_device = NULL;
struct ratbagd *ctx = userdata;
struct ratbag_device *device;
int r;
if (ratbagd_test_device) {
ratbagd_device_unlink(ratbagd_test_device);
ratbagd_device_free(ratbagd_test_device);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
RATBAGD_NAME_ROOT ".Manager",
"Devices",
NULL);
}
device = ratbag_device_new_test_device(ctx->lib_ctx, &ratbagd_test_device_descr);
r = ratbagd_device_new(&ratbagd_test_device, ctx, "test_device", device);
/* the ratbagd_device takes its own reference, drop ours */
ratbag_device_unref(device);
if (r < 0) {
log_error("Cannot track test device\n");
return r;
}
ratbagd_device_link(ratbagd_test_device);
if (m) {
sd_bus_reply_method_return(m, "u", r);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
RATBAGD_NAME_ROOT ".Manager",
"Devices",
NULL);
}
return 0;
}
#endif
void ratbagd_init_test_device(struct ratbagd *ctx)
{
#ifdef RATBAG_DEVELOPER_EDITION
setenv("RATBAG_TEST", "1", 0);
ratbagd_reset_test_device(NULL, ctx, NULL);
#endif
}

35
ratbagd/ratbagd-test.h Normal file
View File

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

View File

@@ -33,13 +33,17 @@
#include <libudev.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include "ratbagd.h"
#include "shared-macro.h"
#include "libratbag-util.h"
#include "ratbagd-test.h"
static bool verbose = false;
static bool verbose_raw = false;
static const char *SVG_THEMES[] = {
"default",
@@ -184,195 +188,6 @@ static int ratbagd_get_themes(sd_bus *bus,
return sd_bus_message_close_container(reply);
}
#ifdef RATBAG_DEVELOPER_EDITION
#include "libratbag-test.h"
/* A pre-setup sane device. Use for sanity testing by toggling the various
* error conditions.
*/
static const struct ratbag_test_device ratbagd_test_device_descr = {
.num_profiles = 4,
.num_resolutions = 3,
.num_buttons = 4,
.num_leds = 3,
.svg = "logitech-g500.svg",
.profiles = {
{
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 0 },
{ .button_type = RATBAG_BUTTON_TYPE_MIDDLE,
.action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = KEY_3 },
{ .button_type = RATBAG_BUTTON_TYPE_RIGHT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
.special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO,
.macro = {
{ .type = RATBAG_MACRO_EVENT_KEY_PRESSED,
.value = KEY_B },
{ .type = RATBAG_MACRO_EVENT_KEY_RELEASED,
.value = KEY_B },
{ .type = RATBAG_MACRO_EVENT_WAIT,
.value = 300 },
},
}
},
.resolutions = {
{ .xres = 100, .yres = 200, .hz = 1000 },
{ .xres = 200, .yres = 300, .hz = 1000, .active = true, .dflt = true },
{ .xres = 300, .yres = 400, .hz = 1000 },
},
.active = true,
.dflt = false,
.leds = {
{
.mode = RATBAG_LED_OFF,
.color = { .red = 255, .green = 0, .blue = 0 },
.hz = 1,
.brightness = 20
},
{
.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
}
},
},
{
.buttons = {
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 4 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 5 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 6 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 7 },
},
.resolutions = {
{ .xres = 1100, .yres = 1200, .hz = 2000, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 1200, .yres = 1300, .hz = 2000, .dflt = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 1300, .yres = 1400, .hz = 2000, .active = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
},
.active = false,
.dflt = true,
},
{
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
.special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO,
.macro = {
{ .type = RATBAG_MACRO_EVENT_KEY_PRESSED,
.value = KEY_A },
{ .type = RATBAG_MACRO_EVENT_KEY_RELEASED,
.value = KEY_A },
{ .type = RATBAG_MACRO_EVENT_WAIT,
.value = 150 },
}
},
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 2 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 3 },
},
.resolutions = {
{ .xres = 2100, .yres = 2200, .hz = 3000, .active = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 2200, .yres = 2300, .hz = 3000, .dflt = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 2300, .yres = 2400, .hz = 3000, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
},
.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,
},
{
.disabled = true,
},
},
.destroyed = NULL,
.destroyed_data = NULL,
};
static int ratbagd_reset_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
static struct ratbagd_device *ratbagd_test_device = NULL;
struct ratbagd *ctx = userdata;
struct ratbag_device *device;
int r;
if (ratbagd_test_device) {
ratbagd_device_unlink(ratbagd_test_device);
ratbagd_device_free(ratbagd_test_device);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
RATBAGD_NAME_ROOT ".Manager",
"Devices",
NULL);
}
device = ratbag_device_new_test_device(ctx->lib_ctx, &ratbagd_test_device_descr);
r = ratbagd_device_new(&ratbagd_test_device, ctx, "test_device", device);
/* the ratbagd_device takes its own reference, drop ours */
ratbag_device_unref(device);
if (r < 0) {
log_error("Cannot track test device\n");
return r;
}
ratbagd_device_link(ratbagd_test_device);
if (m) {
sd_bus_reply_method_return(m, "u", r);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
RATBAGD_NAME_ROOT ".Manager",
"Devices",
NULL);
}
return 0;
}
static void ratbagd_init_test_device(struct ratbagd *ctx)
{
setenv("RATBAG_TEST", "1", 0);
ratbagd_reset_test_device(NULL, ctx, NULL);
}
#else /* RATBAG_DEVELOPER_EDITION */
static inline void ratbagd_init_test_device(struct ratbagd *ctx) {}
#endif /* RATBAG_DEVELOPER_EDITION */
static const sd_bus_vtable ratbagd_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Devices", "ao", ratbagd_get_devices, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -436,7 +251,7 @@ static void ratbagd_process_device(struct ratbagd *ctx,
ratbag_device_unref(lib_device);
if (r < 0) {
log_error("Cannot track device '%s'\n", name);
log_error("%s: cannot track device\n", name);
return;
}
@@ -486,13 +301,15 @@ static const struct ratbag_interface ratbagd_lib_interface = {
static struct ratbagd *ratbagd_free(struct ratbagd *ctx)
{
struct ratbagd_device *device;
struct ratbagd_device *device, *tmp;
if (!ctx)
return NULL;
RATBAGD_DEVICE_FOREACH(device, ctx)
RATBAGD_DEVICE_FOREACH_SAFE(device, tmp, ctx) {
ratbagd_device_unlink(device);
ratbagd_device_free(device);
}
ctx->bus = sd_bus_flush_close_unref(ctx->bus);
ctx->monitor_source = sd_event_source_unref(ctx->monitor_source);
@@ -567,7 +384,10 @@ static int ratbagd_new(struct ratbagd **out)
if (!ctx->lib_ctx)
return -ENOMEM;
if (verbose)
if (verbose_raw)
ratbag_log_set_priority(ctx->lib_ctx,
RATBAG_LOG_PRIORITY_RAW);
else if (verbose)
ratbag_log_set_priority(ctx->lib_ctx,
RATBAG_LOG_PRIORITY_DEBUG);
@@ -706,11 +526,19 @@ int main(int argc, char *argv[])
struct ratbagd *ctx = NULL;
int r;
#if DISABLE_COREDUMP
const struct rlimit corelimit = { 0, 0 };
setrlimit(RLIMIT_CORE, &corelimit);
#endif
if (argc > 1) {
if (streq(argv[1], "--verbose")) {
if (streq(argv[1], "--verbose=raw")) {
verbose_raw = true;
} else if (streq(argv[1], "--verbose")) {
verbose = true;
} else {
fprintf(stderr, "Usage: %s [--verbose]\n",
fprintf(stderr, "Usage: %s [--verbose[=raw]]\n",
program_invocation_short_name);
r = -EINVAL;
goto exit;

View File

@@ -148,10 +148,11 @@ int ratbagd_device_new(struct ratbagd_device **out,
const char *name,
struct ratbag_device *lib_device);
struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device);
const char *ratbagd_device_get_name(struct ratbagd_device *device);
const char *ratbagd_device_get_sysname(struct ratbagd_device *device);
const char *ratbagd_device_get_path(struct ratbagd_device *device);
unsigned int ratbagd_device_get_num_buttons(struct ratbagd_device *device);
unsigned int ratbagd_device_get_num_leds(struct ratbagd_device *device);
int ratbagd_device_resync(struct ratbagd_device *device, sd_bus *bus);
bool ratbagd_device_linked(struct ratbagd_device *device);
void ratbagd_device_link(struct ratbagd_device *device);
@@ -169,6 +170,12 @@ struct ratbagd_device *ratbagd_device_next(struct ratbagd_device *device);
(_device); \
(_device) = ratbagd_device_next(_device))
#define RATBAGD_DEVICE_FOREACH_SAFE(_device, _safe, _ctx) \
for (_device = ratbagd_device_first(_ctx), \
_safe = (_device) ? ratbagd_device_next(_device) : NULL; \
(_device); \
_device = (_safe), \
_safe = (_safe) ? ratbagd_device_next(_safe) : NULL)
/*
* Context
*/

View File

@@ -324,78 +324,6 @@ etekcity_button_to_action(struct ratbag_profile *profile,
return etekcity_raw_to_button_action(data);
}
static void
etekcity_read_profile(struct ratbag_profile *profile, unsigned int index)
{
struct ratbag_device *device = profile->device;
struct etekcity_data *drv_data;
struct ratbag_resolution *resolution;
struct etekcity_settings_report *setting_report;
uint8_t *buf;
unsigned int report_rate;
unsigned int i;
int dpi_x, dpi_y, hz;
int rc;
assert(index <= ETEKCITY_PROFILE_MAX);
drv_data = ratbag_get_drv_data(device);
setting_report = &drv_data->settings[index];
buf = (uint8_t*)setting_report;
etekcity_set_config_profile(device, index, ETEKCITY_CONFIG_SETTINGS);
rc = ratbag_hidraw_raw_request(device, ETEKCITY_REPORT_ID_SETTINGS,
buf, ETEKCITY_REPORT_SIZE_SETTINGS,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (rc < ETEKCITY_REPORT_SIZE_SETTINGS)
return;
/* first retrieve the report rate, it is set per profile */
switch (setting_report->report_rate) {
case 0x00: report_rate = 125; break;
case 0x01: report_rate = 250; break;
case 0x02: report_rate = 500; break;
case 0x03: report_rate = 1000; break;
default:
log_error(device->ratbag,
"error while reading the report rate of the mouse (0x%02x)\n",
buf[26]);
report_rate = 0;
}
for (i = 0; i < profile->resolution.num_modes; i++) {
dpi_x = setting_report->xres[i] * 50;
dpi_y = setting_report->yres[i] * 50;
hz = report_rate;
if (!(setting_report->dpi_mask & (1 << i))) {
/* the profile is disabled, overwrite it */
dpi_x = 0;
dpi_y = 0;
hz = 0;
}
resolution = ratbag_resolution_init(profile, i, dpi_x, dpi_y, hz);
ratbag_resolution_set_cap(resolution,
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
resolution->is_active = (i == setting_report->current_dpi);
}
buf = drv_data->profiles[index];
etekcity_set_config_profile(device, index, ETEKCITY_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_raw_request(device, ETEKCITY_REPORT_ID_KEY_MAPPING,
buf, ETEKCITY_REPORT_SIZE_PROFILE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
msleep(10);
if (rc < ETEKCITY_REPORT_SIZE_PROFILE)
return;
log_raw(device->ratbag, "profile: %d %s:%d\n",
buf[2],
__FILE__, __LINE__);
}
static int
etekcity_write_profile(struct ratbag_profile *profile)
{
@@ -440,7 +368,7 @@ etekcity_read_button(struct ratbag_button *button)
action = etekcity_button_to_action(button->profile, button->index);
if (action)
button->action = *action;
ratbag_button_set_action(button, action);
button->type = etekcity_raw_to_button_type(button->index);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
@@ -490,6 +418,83 @@ etekcity_read_button(struct ratbag_button *button)
}
}
static void
etekcity_read_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct etekcity_data *drv_data;
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct etekcity_settings_report *setting_report;
uint8_t *buf;
unsigned int report_rate;
int dpi_x, dpi_y, hz;
int rc;
unsigned int report_rates[] = { 125, 250, 500, 1000 };
assert(profile->index <= ETEKCITY_PROFILE_MAX);
drv_data = ratbag_get_drv_data(device);
setting_report = &drv_data->settings[profile->index];
buf = (uint8_t*)setting_report;
etekcity_set_config_profile(device, profile->index, ETEKCITY_CONFIG_SETTINGS);
rc = ratbag_hidraw_raw_request(device, ETEKCITY_REPORT_ID_SETTINGS,
buf, ETEKCITY_REPORT_SIZE_SETTINGS,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (rc < ETEKCITY_REPORT_SIZE_SETTINGS)
return;
/* first retrieve the report rate, it is set per profile */
if (setting_report->report_rate < ARRAY_LENGTH(report_rates)) {
report_rate = report_rates[setting_report->report_rate];
} else {
log_error(device->ratbag,
"error while reading the report rate of the mouse (0x%02x)\n",
buf[26]);
report_rate = 0;
}
ratbag_profile_for_each_resolution(profile, resolution) {
dpi_x = setting_report->xres[resolution->index] * 50;
dpi_y = setting_report->yres[resolution->index] * 50;
hz = report_rate;
if (!(setting_report->dpi_mask & (1 << resolution->index))) {
/* the profile is disabled, overwrite it */
dpi_x = 0;
dpi_y = 0;
hz = 0;
}
ratbag_resolution_set_resolution(resolution, dpi_x, dpi_y, hz);
ratbag_resolution_set_cap(resolution,
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
resolution->is_active = (resolution->index == setting_report->current_dpi);
ratbag_resolution_set_report_rate_list(resolution, report_rates,
ARRAY_LENGTH(report_rates));
}
ratbag_profile_for_each_button(profile, button)
etekcity_read_button(button);
buf = drv_data->profiles[profile->index];
etekcity_set_config_profile(device, profile->index, ETEKCITY_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_raw_request(device, ETEKCITY_REPORT_ID_KEY_MAPPING,
buf, ETEKCITY_REPORT_SIZE_PROFILE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
msleep(10);
if (rc < ETEKCITY_REPORT_SIZE_PROFILE)
return;
log_raw(device->ratbag, "profile: %d %s:%d\n",
buf[2],
__FILE__, __LINE__);
}
static int
etekcity_write_macro(struct ratbag_button *button,
const struct ratbag_button_action *action)
@@ -594,7 +599,6 @@ etekcity_write_resolution_dpi(struct ratbag_resolution *resolution,
struct ratbag_device *device = profile->device;
struct etekcity_data *drv_data = ratbag_get_drv_data(device);
struct etekcity_settings_report *settings_report;
unsigned int index;
uint8_t *buf;
int rc;
@@ -605,13 +609,10 @@ etekcity_write_resolution_dpi(struct ratbag_resolution *resolution,
settings_report = &drv_data->settings[profile->index];
/* retrieve which resolution is asked to be changed */
index = resolution - profile->resolution.modes;
settings_report->x_sensitivity = 0x0a;
settings_report->y_sensitivity = 0x0a;
settings_report->xres[index] = dpi_x / 50;
settings_report->yres[index] = dpi_y / 50;
settings_report->xres[resolution->index] = dpi_x / 50;
settings_report->yres[resolution->index] = dpi_y / 50;
buf = (uint8_t*)settings_report;
etekcity_set_config_profile(device, profile->index, ETEKCITY_CONFIG_SETTINGS);
@@ -664,6 +665,9 @@ etekcity_probe(struct ratbag_device *device)
ETEKCITY_BUTTON_MAX + 1,
ETEKCITY_LED);
ratbag_device_for_each_profile(device, profile)
etekcity_read_profile(profile);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
@@ -709,10 +713,8 @@ struct ratbag_driver etekcity_driver = {
.id = "etekcity",
.probe = etekcity_probe,
.remove = etekcity_remove,
.read_profile = etekcity_read_profile,
.write_profile = etekcity_write_profile,
.set_active_profile = etekcity_set_current_profile,
.read_button = etekcity_read_button,
.write_button = etekcity_write_button,
.write_resolution_dpi = etekcity_write_resolution_dpi,
};

View File

@@ -973,72 +973,11 @@ gskill_write_button_macro(struct ratbag_device *device,
return 0;
}
static int
gskill_probe(struct ratbag_device *device)
{
struct gskill_data *drv_data = NULL;
struct ratbag_profile *profile;
unsigned int active_idx;
int ret;
ret = ratbag_open_hidraw(device);
if (ret)
return ret;
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
ret = gskill_get_firmware_version(device);
if (ret < 0)
goto err;
log_debug(device->ratbag,
"Firmware version: %d\n", ret);
ret = gskill_get_profile_count(device);
if (ret < 0)
goto err;
drv_data->profile_count = ret;
ratbag_device_init_profiles(device, GSKILL_PROFILE_MAX, GSKILL_NUM_DPI,
GSKILL_BUTTON_MAX, GSKILL_NUM_LED);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_QUERY_CONFIGURATION);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_KEY);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_DISABLE_PROFILE);
ret = gskill_get_active_profile_idx(device);
if (ret < 0)
goto err;
active_idx = ret;
list_for_each(profile, &device->profiles, link) {
if (profile->index == active_idx) {
profile->is_active = true;
break;
}
}
return 0;
err:
if (drv_data) {
ratbag_set_drv_data(device, NULL);
free(drv_data);
}
ratbag_close_hidraw(device);
return ret;
}
static void
gskill_read_resolutions(struct ratbag_profile *profile,
struct gskill_profile_report *report)
{
struct gskill_profile_data *pdata = profile_to_pdata(profile);
struct ratbag_resolution *resolution;
int dpi_x, dpi_y, hz, i;
log_debug(profile->device->ratbag,
@@ -1048,16 +987,24 @@ gskill_read_resolutions(struct ratbag_profile *profile,
hz = GSKILL_MAX_POLLING_RATE / (report->polling_rate + 1);
for (i = 0; i < report->dpi_num; i++) {
_cleanup_resolution_ struct ratbag_resolution *resolution;
unsigned int rates[] = { 500, 1000 }; /* let's assume that is true */
dpi_x = report->dpi_levels[i].x * GSKILL_DPI_UNIT;
dpi_y = report->dpi_levels[i].y * GSKILL_DPI_UNIT;
resolution = ratbag_resolution_init(profile, i, dpi_x, dpi_y,
hz);
resolution = ratbag_profile_get_resolution(profile, i);
ratbag_resolution_set_resolution(resolution, dpi_x, dpi_y, hz);
resolution->is_active = (i == report->current_dpi_level);
pdata->res_idx_to_dev_idx[i] = i;
ratbag_resolution_set_cap(resolution,
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
ratbag_resolution_set_dpi_list_from_range(resolution,
GSKILL_MIN_DPI, GSKILL_MAX_DPI);
ratbag_resolution_set_report_rate_list(resolution, rates,
ARRAY_LENGTH(rates));
}
}
@@ -1083,7 +1030,7 @@ gskill_read_profile_name(struct ratbag_device *device,
}
static void
gskill_read_profile(struct ratbag_profile *profile, unsigned int index)
gskill_read_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct gskill_data *drv_data = ratbag_get_drv_data(device);
@@ -1092,7 +1039,7 @@ gskill_read_profile(struct ratbag_profile *profile, unsigned int index)
uint8_t checksum;
int rc, retries;
if (index >= drv_data->profile_count) {
if (profile->index >= drv_data->profile_count) {
profile->is_enabled = false;
return;
}
@@ -1106,7 +1053,7 @@ gskill_read_profile(struct ratbag_profile *profile, unsigned int index)
* mouse we're doing something wrong.
*/
for (retries = 0; retries < 3; retries++) {
rc = gskill_select_profile(device, index, false);
rc = gskill_select_profile(device, profile->index, false);
if (rc < 0)
return;
@@ -1124,12 +1071,11 @@ gskill_read_profile(struct ratbag_profile *profile, unsigned int index)
return;
}
if (report->profile_num == index)
if (report->profile_num == profile->index)
break;
log_debug(device->ratbag,
"Mouse send wrong profile %d instead of %d, retrying...\n",
profile->index, index);
"Mouse send wrong profile, retrying...\n");
}
checksum = gskill_calculate_checksum((uint8_t*)report, sizeof(*report));
@@ -1162,10 +1108,11 @@ gskill_update_resolutions(struct ratbag_profile *profile)
* lost on exit
*/
for (i = 0; i < GSKILL_NUM_DPI; i++) {
struct ratbag_resolution *res = &profile->resolution.modes[i];
_cleanup_resolution_ struct ratbag_resolution *res;
struct gskill_raw_dpi_level *level =
&report->dpi_levels[report->dpi_num];
res = ratbag_profile_get_resolution(profile, i);
if (!res->dpi_x || !res->dpi_y)
continue;
@@ -1173,8 +1120,8 @@ gskill_update_resolutions(struct ratbag_profile *profile)
level->y = res->dpi_y / GSKILL_DPI_UNIT;
pdata->res_idx_to_dev_idx[i] = report->dpi_num;
log_debug(device->ratbag, "Profile %d res %ld mapped to %d\n",
profile->index, res - profile->resolution.modes,
log_debug(device->ratbag, "Profile %d res %d mapped to %d\n",
profile->index, res->index,
report->dpi_num);
report->dpi_num++;
@@ -1429,6 +1376,73 @@ gskill_update_profile(struct ratbag_profile *profile)
return 0;
}
static int
gskill_probe(struct ratbag_device *device)
{
struct gskill_data *drv_data = NULL;
struct ratbag_profile *profile;
unsigned int active_idx;
int ret;
ret = ratbag_open_hidraw(device);
if (ret)
return ret;
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
ret = gskill_get_firmware_version(device);
if (ret < 0)
goto err;
log_debug(device->ratbag,
"Firmware version: %d\n", ret);
ret = gskill_get_profile_count(device);
if (ret < 0)
goto err;
drv_data->profile_count = ret;
ratbag_device_init_profiles(device, GSKILL_PROFILE_MAX, GSKILL_NUM_DPI,
GSKILL_BUTTON_MAX, GSKILL_NUM_LED);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_QUERY_CONFIGURATION);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_KEY);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_DISABLE_PROFILE);
ret = gskill_get_active_profile_idx(device);
if (ret < 0)
goto err;
active_idx = ret;
ratbag_device_for_each_profile(device, profile) {
struct ratbag_button *button;
gskill_read_profile(profile);
ratbag_profile_for_each_button(profile, button)
gskill_read_button(button);
if (profile->index == active_idx) {
profile->is_active = true;
break;
}
}
return 0;
err:
if (drv_data) {
ratbag_set_drv_data(device, NULL);
free(drv_data);
}
ratbag_close_hidraw(device);
return ret;
}
static int
gskill_commit(struct ratbag_device *device)
{
@@ -1506,8 +1520,6 @@ struct ratbag_driver gskill_driver = {
.id = "gskill",
.probe = gskill_probe,
.remove = gskill_remove,
.read_profile = gskill_read_profile,
.commit = gskill_commit,
.set_active_profile = gskill_set_active_profile,
.read_button = gskill_read_button,
};

View File

@@ -47,6 +47,7 @@
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
#include "libratbag-data.h"
struct hidpp10drv_data {
struct hidpp10_device *dev;
@@ -336,6 +337,43 @@ hidpp10drv_write_button(struct hidpp10_profile *profile,
return 0;
}
static void
hidpp10drv_read_led(struct ratbag_led *led)
{
struct ratbag_device *device = led->profile->device;
struct hidpp10drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp10_device *hidpp10 = drv_data->dev;
struct hidpp10_profile profile;
int ret;
ret = hidpp10_get_profile(hidpp10, led->profile->index, &profile);
if (ret)
return;
switch (hidpp10->profile_type) {
case HIDPP10_PROFILE_G500:
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
break;
default:
led->colordepth = RATBAG_LED_COLORDEPTH_MONOCHROME;
break;
}
led->mode = RATBAG_LED_ON;
led->color.red = profile.red;
led->color.green = profile.green;
led->color.blue = profile.blue;
}
static void
hidpp10drv_write_led(struct hidpp10_profile *profile,
struct ratbag_led *led)
{
profile->red = led->color.red;
profile->green = led->color.green;
profile->blue = led->color.blue;
}
static int
hidpp10drv_set_current_profile(struct ratbag_device *device, unsigned int index)
{
@@ -346,21 +384,26 @@ hidpp10drv_set_current_profile(struct ratbag_device *device, unsigned int index)
}
static void
hidpp10drv_read_profile(struct ratbag_profile *profile, unsigned int index)
hidpp10drv_read_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct ratbag_button *button;
struct ratbag_led *led;
struct hidpp10drv_data *drv_data;
struct hidpp10_device *hidpp10;
struct hidpp10_profile p = {0};
struct ratbag_resolution *res;
int rc;
unsigned int i;
uint16_t xres, yres;
uint8_t idx;
/* 0x64 USB_REFRESH_RATE has time between reports in ms. so let's
* assume the 1000/500/250 rates exist on the devices */
unsigned int rates[] = {250, 500, 1000};
drv_data = ratbag_get_drv_data(device);
hidpp10 = drv_data->dev;
rc = hidpp10_get_profile(hidpp10, index, &p);
rc = hidpp10_get_profile(hidpp10, profile->index, &p);
if (rc)
return;
@@ -377,75 +420,55 @@ hidpp10drv_read_profile(struct ratbag_profile *profile, unsigned int index)
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_RESOLUTION);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION);
for (i = 0; i < profile->resolution.num_modes; i++) {
unsigned int min, max;
ratbag_profile_for_each_resolution(profile, res) {
unsigned int dpis[hidpp10->dpi_count];
res = ratbag_resolution_init(profile, i,
p.dpi_modes[i].xres,
p.dpi_modes[i].yres,
p.refresh_rate);
ratbag_resolution_set_resolution(res,
p.dpi_modes[res->index].xres,
p.dpi_modes[res->index].yres,
p.refresh_rate);
ratbag_resolution_set_cap(res,
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
if (profile->is_active &&
res->dpi_x == xres &&
res->dpi_y == yres)
res->is_active = true;
if (i == p.default_dpi_mode) {
if (res->index == p.default_dpi_mode) {
res->is_default = true;
if (!profile->is_active)
res->is_active = true;
}
min = hidpp10_dpi_table_get_min_dpi(hidpp10);
max = hidpp10_dpi_table_get_max_dpi(hidpp10);
ratbag_resolution_set_range(res, min, max);
}
}
if (hidpp10->dpi_table_is_range) {
unsigned int min, max;
static int
hidpp10drv_write_profile(struct ratbag_profile *profile)
{
return 0;
}
min = hidpp10_dpi_table_get_min_dpi(hidpp10);
max = hidpp10_dpi_table_get_max_dpi(hidpp10);
/* FIXME: this relies on libratbag using the
* same steps that we support */
ratbag_resolution_set_dpi_list_from_range(res, min, max);
} else {
for (int i = 0; i < hidpp10->dpi_count; i++)
dpis[i] = hidpp10->dpi_table[i].dpi;
static int
hidpp10drv_write_resolution_dpi(struct hidpp10_profile *p,
struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_device *device = profile->device;
struct hidpp10drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp10_device *hidpp10 = drv_data->dev;
unsigned int index;
uint16_t cur_dpi_x, cur_dpi_y;
int rc;
ratbag_resolution_set_dpi_list(res, dpis, hidpp10->dpi_count);
}
/* store the current resolution */
rc = hidpp10_get_current_resolution(hidpp10, &cur_dpi_x, &cur_dpi_y);
if (rc)
return rc;
if (resolution->is_active) {
/* we need to switch to the new resolution */
cur_dpi_x = dpi_x;
cur_dpi_y = dpi_y;
ratbag_resolution_set_report_rate_list(res, rates,
ARRAY_LENGTH(rates));
}
/* retrieve which resolution is asked to be changed */
index = resolution - profile->resolution.modes;
ratbag_profile_for_each_button(profile, button)
hidpp10drv_read_button(button);
p->dpi_modes[index].xres = dpi_x;
p->dpi_modes[index].yres = dpi_y;
/* restore the current setting */
return hidpp10_set_current_resolution(hidpp10, cur_dpi_x, cur_dpi_y);
ratbag_profile_for_each_led(profile, led)
hidpp10drv_read_led(led);
}
static int
hidpp10drv_fill_from_profile(struct ratbag_device *device, struct hidpp10_device *dev)
{
int rc;
int rc, num_leds;
struct hidpp10_profile profile = {0};
unsigned int i;
@@ -461,6 +484,11 @@ hidpp10drv_fill_from_profile(struct ratbag_device *device, struct hidpp10_device
break;
}
/* let the .device file override LED count from the profile */
num_leds = ratbag_device_data_hidpp10_get_led_count(device->data);
if (num_leds >= 0)
profile.num_leds = num_leds;
ratbag_device_init_profiles(device,
dev->profile_count,
profile.num_dpi_modes,
@@ -500,10 +528,11 @@ hidpp10drv_commit(struct ratbag_device *device)
struct hidpp10_device *dev = drv_data->dev;
struct ratbag_profile *profile;
struct ratbag_button *button;
struct ratbag_led *led;
struct ratbag_resolution *resolution;
struct ratbag_resolution *active_resolution = NULL;
struct hidpp10_profile p;
int rc;
unsigned int i;
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
@@ -515,15 +544,12 @@ hidpp10drv_commit(struct ratbag_device *device)
p.enabled = profile->is_enabled;
for (i = 0; i < profile->resolution.num_modes; i++) {
resolution = &profile->resolution.modes[i];
ratbag_profile_for_each_resolution(profile, resolution) {
p.dpi_modes[resolution->index].xres = resolution->dpi_x;
p.dpi_modes[resolution->index].yres = resolution->dpi_y;
rc = hidpp10drv_write_resolution_dpi(&p,
resolution,
resolution->dpi_x,
resolution->dpi_y);
if (rc)
return RATBAG_ERROR_DEVICE;
if (profile->is_active && resolution->is_active)
active_resolution = resolution;
}
list_for_each(button, &profile->buttons, link) {
@@ -537,11 +563,25 @@ hidpp10drv_commit(struct ratbag_device *device)
return RATBAG_ERROR_DEVICE;
}
ratbag_profile_for_each_led(profile, led)
hidpp10drv_write_led(&p, led);
if (dev->profile_type != HIDPP10_PROFILE_UNKNOWN) {
rc = hidpp10_set_profile(dev, profile->index, &p);
if (rc)
return RATBAG_ERROR_DEVICE;
}
/* Update the current resolution in case it changed */
if (active_resolution) {
rc = hidpp10_set_current_resolution(dev,
active_resolution->dpi_x,
active_resolution->dpi_y);
if (rc)
return RATBAG_ERROR_DEVICE;
active_resolution = NULL;
}
}
return RATBAG_SUCCESS;
@@ -555,10 +595,10 @@ hidpp10drv_probe(struct ratbag_device *device)
struct hidpp10_device *dev = NULL;
struct hidpp_device base;
enum hidpp10_profile_type type = HIDPP10_PROFILE_UNKNOWN;
const char *prop;
const char *typestr;
int device_idx = HIDPP_WIRED_DEVICE_IDX;
int nread = 0;
unsigned int profile_count = 1;
struct ratbag_profile *profile;
rc = ratbag_find_hidraw(device, hidpp10drv_test_hidraw);
if (rc)
@@ -568,38 +608,29 @@ hidpp10drv_probe(struct ratbag_device *device)
hidpp_device_init(&base, device->hidraw.fd);
hidpp_device_set_log_handler(&base, hidpp10_log, HIDPP_LOG_PRIORITY_RAW, device);
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_PROFILE");
if (prop) {
if (strcasecmp("G500", prop) == 0)
typestr = ratbag_device_data_hidpp10_get_profile_type(device->data);
if (typestr) {
if (strcasecmp("G500", typestr) == 0)
type = HIDPP10_PROFILE_G500;
else if (strcasecmp("G700", prop) == 0)
else if (strcasecmp("G700", typestr) == 0)
type = HIDPP10_PROFILE_G700;
else if (strcasecmp("G9", prop) == 0)
else if (strcasecmp("G9", typestr) == 0)
type = HIDPP10_PROFILE_G9;
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_PROFILE_COUNT");
if (prop)
profile_count = atoi(prop);
profile_count = ratbag_device_data_hidpp10_get_profile_count(device->data);
}
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_INDEX");
if (prop) {
sscanf(prop, "%d%n", &device_idx, &nread);
if (!nread || (prop[nread]) != '\0' || device_idx < 0) {
log_error(device->ratbag,
"Error parsing RATBAG_HIDPP10_INDEX: '%s' for %s\n",
prop,
device->name);
device_idx = HIDPP_WIRED_DEVICE_IDX;
}
}
device_idx = ratbag_device_data_hidpp10_get_index(device->data);
if (device_idx == -1)
device_idx = HIDPP_WIRED_DEVICE_IDX;
/* In the general case, we can treat all devices as wired devices
* here. If we talk to the correct hidraw device the kernel adjusts
* the device index for us, so even for unifying receiver devices
* we can just use 0x00 as device index.
*
* If there is a special need like for G700(s), we can pass a
* udev prop RATBAG_HIDPP10_INDEX.
* If there is a special need like for G700(s), then add a DeviceIndex
* entry to the .device file.
*/
dev = hidpp10_device_new(&base, device_idx, type, profile_count);
@@ -611,31 +642,31 @@ hidpp10drv_probe(struct ratbag_device *device)
}
if (type != HIDPP10_PROFILE_UNKNOWN) {
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_DPI");
if (prop) {
rc = hidpp10_build_dpi_table_from_dpi_info(dev, prop);
_cleanup_(dpi_list_freep) struct dpi_list *list = NULL;
_cleanup_(freep) struct dpi_range *range = NULL;
range = ratbag_device_data_hidpp10_get_dpi_range(device->data);
if (range) {
rc = hidpp10_build_dpi_table_from_dpi_info(dev, range);
if (rc)
log_error(device->ratbag,
"Error parsing RATBAG_HIDPP10_DPI: '%s' for %s\n",
prop,
"Error parsing DpiRange for %s\n",
device->name);
}
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_DPI_LIST");
if (prop) {
rc = hidpp10_build_dpi_table_from_list(dev, prop);
list = ratbag_device_data_hidpp10_get_dpi_list(device->data);
if (list) {
rc = hidpp10_build_dpi_table_from_list(dev, list);
if (rc)
log_error(device->ratbag,
"Error parsing RATBAG_HIDPP10_DPI_LIST: '%s' for %s\n",
prop,
"Error parsing DpiList for %s\n",
device->name);
}
if (!dev->dpi_count)
log_info(device->ratbag,
"Device %s might have wrong dpi settings. "
"Please add RATBAG_HIDPP10_DPI or RATBAG_HIDPP10_DPI_LIST "
"to the udev properties.\n",
"Please adjust the .device file.\n",
device->name);
}
@@ -648,14 +679,16 @@ hidpp10drv_probe(struct ratbag_device *device)
if (hidpp10drv_fill_from_profile(device, dev)) {
/* Fall back to something that every mouse has */
struct ratbag_profile *profile;
_cleanup_profile_ struct ratbag_profile *profile;
ratbag_device_init_profiles(device, 1, 1, 3, 0);
profile = ratbag_device_get_profile(device, 0);
profile->is_active = true;
ratbag_profile_unref(profile);
}
ratbag_device_for_each_profile(device, profile)
hidpp10drv_read_profile(profile);
return 0;
err:
free(drv_data);
@@ -687,9 +720,6 @@ struct ratbag_driver hidpp10_driver = {
.id = "hidpp10",
.probe = hidpp10drv_probe,
.remove = hidpp10drv_remove,
.read_profile = hidpp10drv_read_profile,
.write_profile = hidpp10drv_write_profile,
.set_active_profile = hidpp10drv_set_current_profile,
.read_button = hidpp10drv_read_button,
.commit = hidpp10drv_commit,
};

View File

@@ -45,14 +45,17 @@
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
#include "libratbag-data.h"
#define HIDPP_CAP_RESOLUTION_2200 (1 << 0)
#define HIDPP_CAP_SWITCHABLE_RESOLUTION_2201 (1 << 1)
#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_COLOR_LED_EFFECTS_8070 (1 << 5)
#define HIDPP_CAP_COLOR_LED_EFFECTS_8070 (1 << 5)
#define HIDPP_CAP_ONBOARD_PROFILES_8100 (1 << 6)
#define HIDPP_CAP_LED_SW_CONTROL_1300 (1 << 7)
#define HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060 (1 << 8)
struct hidpp20drv_data {
struct hidpp20_device *dev;
@@ -62,7 +65,10 @@ struct hidpp20drv_data {
unsigned num_controls;
struct hidpp20_control_id *controls;
struct hidpp20_profiles *profiles;
struct hidpp20_color_led_zone_info *led_infos;
union hidpp20_generic_led_zone_info led_infos;
unsigned int report_rates[4];
unsigned int num_report_rates;
unsigned int num_profiles;
unsigned int num_resolutions;
@@ -96,7 +102,7 @@ hidpp20drv_read_button_1b04(struct ratbag_button *button)
button->type = hidpp20_1b04_get_physical_mapping(control->task_id);
action = hidpp20_1b04_get_logical_mapping(mapping);
if (action)
button->action = *action;
ratbag_button_set_action(button, action);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
@@ -245,18 +251,75 @@ hidpp20drv_read_button(struct ratbag_button *button)
}
static void
hidpp20drv_read_led(struct ratbag_led *led)
hidpp20drv_read_led_1300(struct ratbag_led *led, struct hidpp20drv_data* data)
{
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;
struct hidpp20_led_sw_ctrl_led_state state;
struct hidpp20_led_sw_ctrl_led_info *info;
int rc;
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
info = &data->led_infos.leds[led->index];
rc = hidpp20_led_sw_control_get_led_state(data->dev, led->index, &state);
if (rc != 0)
return;
led_info = &drv_data->led_infos[led->index];
led->colordepth = RATBAG_LED_COLORDEPTH_MONOCHROME;
switch (info->type)
{
case HIDPP20_LED_TYPE_LOGO:
led->type = RATBAG_LED_TYPE_LOGO;
break;
case HIDPP20_LED_TYPE_BATTERY:
led->type = RATBAG_LED_TYPE_BATTERY;
break;
case HIDPP20_LED_TYPE_DPI:
led->type = RATBAG_LED_TYPE_DPI;
break;
case HIDPP20_LED_TYPE_PROFILE:
case HIDPP20_LED_TYPE_COSMETIC:
led->type = RATBAG_LED_TYPE_SIDE;
break;
}
switch (state.mode)
{
case HIDPP20_LED_MODE_OFF:
led->mode = RATBAG_LED_OFF;
break;
case HIDPP20_LED_MODE_ON:
led->mode = RATBAG_LED_ON;
break;
case HIDPP20_LED_MODE_BREATHING:
led->mode = RATBAG_LED_BREATHING;
led->ms = state.breathing.period;
led->brightness = state.breathing.brightness;
break;
default:
led->mode = RATBAG_LED_ON;
}
if (info->caps & HIDPP20_LED_MODE_ON)
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
if (info->caps & HIDPP20_LED_MODE_OFF)
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
if (info->caps & (HIDPP20_LED_MODE_BLINK | HIDPP20_LED_MODE_TRAVEL |
HIDPP20_LED_MODE_RAMP_UP | HIDPP20_LED_MODE_RAMP_DOWN |
HIDPP20_LED_MODE_HEARTBEAT | HIDPP20_LED_MODE_BREATHING))
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
}
static void
hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_data)
{
struct hidpp20_profile *profile;
struct hidpp20_led *h_led;
struct hidpp20_color_led_zone_info* led_info;
struct hidpp20_color_led_info info;
int rc;
led_info = &drv_data->led_infos.color_leds[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
@@ -274,21 +337,74 @@ hidpp20drv_read_led(struct ratbag_led *led)
led->mode = RATBAG_LED_OFF;
break;
}
led->type = hidpp20_8070_get_location_mapping(led_info->location);
/* pre-filled, only override if unknown */
if (led->type == RATBAG_LED_TYPE_UNKNOWN)
led->type = hidpp20_8070_get_location_mapping(led_info->location);
led->color.red = h_led->color.red;
led->color.green = h_led->color.green;
led->color.blue = h_led->color.blue;
led->hz = h_led->period; /* FIXME: should be ceil(1000.0 / h_led->period), but that would be very small */
led->ms = h_led->period;
led->brightness = h_led->brightness * 255 / 100;
rc = hidpp20_color_led_effects_get_info(drv_data->dev, &info);
if (rc == 0 &&
info.ext_caps & HIDPP20_COLOR_LED_INFO_EXT_CAP_MONOCHROME_ONLY)
led->colordepth = RATBAG_LED_COLORDEPTH_MONOCHROME;
else
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
for (int i = 0; i < led_info->num_effects; i++) {
struct hidpp20_color_led_zone_effect_info ei;
rc = hidpp20_color_led_effect_get_zone_effect_info(drv_data->dev,
led_info->index,
i, &ei);
if (rc < 0)
break;
switch (ei.effect_id) {
case HIDPP20_COLOR_LED_ZONE_EFFECT_DISABLED:
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_FIXED:
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_CYCLING:
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_WAVE:
case HIDPP20_COLOR_LED_ZONE_EFFECT_BREATHING:
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
break;
default:
log_bug_libratbag(led->profile->device->ratbag,
"%s: Unknown effect id %d\n",
led->profile->device->name,
ei.effect_id);
break;
}
}
}
static void
hidpp20drv_read_led(struct ratbag_led *led)
{
struct ratbag_device *device = led->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
hidpp20drv_read_led_8070(led, drv_data);
else if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
hidpp20drv_read_led_1300(led, drv_data);
}
static int
hidpp20drv_write_button_1b04(struct ratbag_button *button,
const struct ratbag_button_action *action)
hidpp20drv_update_button_1b04(struct ratbag_button *button)
{
struct ratbag_device *device = button->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_control_id *control;
struct ratbag_button_action *action = &button->action;
uint16_t mapping;
int rc;
@@ -318,12 +434,12 @@ hidpp20drv_write_button_1b04(struct ratbag_button *button,
}
static int
hidpp20drv_update_button_8100(struct ratbag_button *button,
const struct ratbag_button_action *action)
hidpp20drv_update_button_8100(struct ratbag_button *button)
{
struct ratbag_device *device = button->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_profile *profile;
struct ratbag_button_action *action = &button->action;
uint8_t code, type, subtype;
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100))
@@ -366,37 +482,80 @@ hidpp20drv_update_button_8100(struct ratbag_button *button,
return -ENOTSUP;
}
return hidpp20_onboard_profiles_write(drv_data->dev, button->profile->index, drv_data->profiles);
return 0;
}
static int
hidpp20drv_update_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
hidpp20drv_update_button(struct ratbag_button *button)
{
struct ratbag_device *device = button->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
return hidpp20drv_update_button_8100(button, action);
return hidpp20drv_update_button_8100(button);
if (drv_data->capabilities & HIDPP_CAP_BUTTON_KEY_1b04)
return hidpp20drv_write_button_1b04(button, action);
return hidpp20drv_update_button_1b04(button);
return -ENOTSUP;
}
static int
hidpp20drv_update_led(struct ratbag_led *led)
hidpp20drv_update_led_1300(struct ratbag_led *led, struct hidpp20drv_data *data)
{
const uint16_t led_caps = data->led_infos.leds[led->index].caps;
struct hidpp20_led_sw_ctrl_led_state h_led;
int rc;
h_led.index = led->index;
switch(led->mode)
{
case RATBAG_LED_BREATHING:
h_led.mode = HIDPP20_LED_MODE_BREATHING;
h_led.breathing.brightness = led->brightness;
h_led.breathing.period = led->ms;
h_led.breathing.timeout = 300;
break;
case RATBAG_LED_OFF:
h_led.mode = HIDPP20_LED_MODE_OFF;
h_led.on.index = HIDPP20_LED_SW_CONTROL_LED_INDEX_ALL;
break;
case RATBAG_LED_ON:
h_led.mode = HIDPP20_LED_MODE_ON;
h_led.on.index = HIDPP20_LED_SW_CONTROL_LED_INDEX_ALL;
break;
case RATBAG_LED_CYCLE:
return -ENOTSUP;
}
if (!(h_led.mode & led_caps)) {
hidpp_log_error(&data->dev->base, "LED %d does not support effect %s(%04x), supports %04x\n",
led->index, hidpp20_sw_led_control_get_mode_string(h_led.mode),
h_led.mode, led_caps);
return -ENOTSUP;
}
if (!hidpp20_led_sw_control_get_sw_ctrl(data->dev)) {
rc = hidpp20_led_sw_control_set_sw_ctrl(data->dev, true);
if (rc)
return rc;
}
rc = hidpp20_led_sw_control_set_led_state(data->dev, &h_led);
if (rc)
return rc;
return RATBAG_SUCCESS;
}
static int
hidpp20drv_update_led_8070(struct ratbag_led *led, struct ratbag_profile* profile,
struct hidpp20drv_data *drv_data)
{
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]);
@@ -421,12 +580,28 @@ hidpp20drv_update_led(struct ratbag_led *led)
h_led->color.red = led->color.red;
h_led->color.green = led->color.green;
h_led->color.blue = led->color.blue;
h_led->period = led->hz; /* FIXME: should be 1000 / led->hz, but that would be very small */
h_led->period = led->ms;
h_led->brightness = led->brightness * 100 / 255;
return RATBAG_SUCCESS;
}
static int
hidpp20drv_update_led(struct ratbag_led *led)
{
struct ratbag_profile *profile = led->profile;
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
return hidpp20drv_update_led_8070(led, profile, drv_data);
if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
return hidpp20drv_update_led_1300(led, drv_data);
return RATBAG_ERROR_CAPABILITY;
}
static int
hidpp20drv_current_profile(struct ratbag_device *device)
{
@@ -458,7 +633,8 @@ hidpp20drv_set_current_profile(struct ratbag_device *device, unsigned int index)
h_profile = &drv_data->profiles->profiles[index];
if (!h_profile->enabled) {
rc = hidpp20_onboard_profiles_write(drv_data->dev, index, drv_data->profiles);
h_profile->enabled = 1;
rc = hidpp20_onboard_profiles_commit(drv_data->dev, drv_data->profiles);
if (rc)
return rc;
}
@@ -493,7 +669,43 @@ hidpp20drv_read_resolution_dpi_2201(struct ratbag_device *device)
drv_data->sensors[0].dpi_max);
drv_data->num_sensors = rc;
drv_data->num_resolutions = drv_data->num_sensors;
/* if 0x8100 has already been enumerated we already have the supported
* number of resolutions and shouldn't overwrite it
*/
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100))
drv_data->num_resolutions = drv_data->num_sensors;
return 0;
}
static int
hidpp20drv_read_report_rate_8060(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag *ratbag = device->ratbag;
uint8_t bitflags_ms;
int nrates = 0;
int rc;
rc = hidpp20_adjustable_report_rate_get_report_rate_list(drv_data->dev,
&bitflags_ms);
if (rc < 0)
return rc;
/* We only care about 'standard' rates */
if (bitflags_ms & 0x80)
drv_data->report_rates[nrates++] = 125;
if (bitflags_ms & 0x8)
drv_data->report_rates[nrates++] = 250;
if (bitflags_ms & 0x2)
drv_data->report_rates[nrates++] = 500;
if (bitflags_ms & 0x1)
drv_data->report_rates[nrates++] = 1000;
drv_data->num_report_rates = nrates;
log_debug(ratbag, "device has %d report rates\n", nrates);
return 0;
}
@@ -504,9 +716,7 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
struct ratbag_device *device = profile->device;
struct ratbag *ratbag = device->ratbag;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag_resolution *res;
int rc;
unsigned int i;
if (drv_data->capabilities & HIDPP_CAP_RESOLUTION_2200) {
uint16_t resolution;
@@ -524,11 +734,13 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
}
if (drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201) {
struct ratbag_resolution *res;
rc = hidpp20drv_read_resolution_dpi_2201(device);
if (rc < 0)
return rc;
for (i = 0; i < profile->resolution.num_modes; i++) {
ratbag_profile_for_each_resolution(profile, res) {
struct hidpp20_sensor *sensor;
/* We only look at the first sensor. Multiple
@@ -536,9 +748,10 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
sensor = &drv_data->sensors[0];
/* FIXME: retrieve the refresh rate */
res = ratbag_resolution_init(profile, i, sensor->dpi, sensor->dpi, 0);
ratbag_resolution_set_range(res, sensor->dpi_min, sensor->dpi_max);
ratbag_resolution_set_resolution(res, sensor->dpi, sensor->dpi, 0);
ratbag_resolution_set_dpi_list_from_range(res,
sensor->dpi_min,
sensor->dpi_max);
/* FIXME: we mark all resolutions as active because
* they are from different sensors */
res->is_active = true;
@@ -547,6 +760,19 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
return 0;
}
if (drv_data->capabilities & HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060) {
struct ratbag_resolution *res;
rc = hidpp20drv_read_report_rate_8060(device);
if (rc < 0)
return rc;
ratbag_profile_for_each_resolution(profile, res)
ratbag_resolution_set_report_rate_list(res,
drv_data->report_rates,
drv_data->num_report_rates);
}
return 0;
}
@@ -558,14 +784,10 @@ hidpp20drv_update_resolution_dpi_8100(struct ratbag_resolution *resolution,
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_profile *h_profile;
unsigned int index;
int dpi = dpi_x; /* dpi_x == dpi_y if we don't have the individual resolution cap */
/* retrieve which resolution is asked to be changed */
index = resolution - profile->resolution.modes;
h_profile = &drv_data->profiles->profiles[profile->index];
h_profile->dpi[index] = dpi;
h_profile->dpi[resolution->index] = dpi;
return RATBAG_SUCCESS;
}
@@ -669,11 +891,11 @@ hidpp20drv_read_color_leds(struct ratbag_device *device)
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return 0;
free(drv_data->led_infos);
drv_data->led_infos = NULL;
free(drv_data->led_infos.color_leds);
drv_data->led_infos.color_leds = NULL;
drv_data->num_leds = 0;
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos);
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos.color_leds);
if (rc > 0) {
drv_data->num_leds = rc;
@@ -684,37 +906,39 @@ hidpp20drv_read_color_leds(struct ratbag_device *device)
}
static int
hidpp20drv_read_onboard_profile(struct ratbag_device *device, unsigned index)
hidpp20drv_read_leds(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
int rc;
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100))
if (!(drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300))
return 0;
rc = hidpp20_onboard_profiles_read(drv_data->dev, index, drv_data->profiles);
if (rc < 0)
return rc;
free(drv_data->led_infos.leds);
drv_data->led_infos.leds = NULL;
return 0;
rc = hidpp20_led_sw_control_read_leds(drv_data->dev, &drv_data->led_infos.leds);
if (rc > 0) {
drv_data->num_leds = rc;
rc = 0;
}
return rc;
}
static void
hidpp20drv_read_profile_8100(struct ratbag_profile *profile, unsigned int index)
hidpp20drv_read_profile_8100(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag_resolution *res;
struct hidpp20_profile *p;
unsigned int i;
int dpi_index = 0xff;
profile->is_enabled = drv_data->profiles->profiles[index].enabled;
hidpp20drv_read_onboard_profile(device, profile->index);
profile->is_enabled = drv_data->profiles->profiles[profile->index].enabled;
profile->is_active = false;
if ((int)index == hidpp20drv_current_profile(device))
if ((int)profile->index == hidpp20drv_current_profile(device))
profile->is_active = true;
if (profile->is_active)
@@ -723,46 +947,60 @@ hidpp20drv_read_profile_8100(struct ratbag_profile *profile, unsigned int index)
if (dpi_index < 0)
dpi_index = 0xff;
p = &drv_data->profiles->profiles[index];
p = &drv_data->profiles->profiles[profile->index];
for (i = 0; i < profile->resolution.num_modes; i++) {
ratbag_profile_for_each_resolution(profile, res) {
struct hidpp20_sensor *sensor;
/* We only look at the first sensor. Multiple
* sensors is too niche to care about right now */
sensor = &drv_data->sensors[0];
res = ratbag_resolution_init(profile, i,
p->dpi[i],
p->dpi[i],
p->report_rate);
ratbag_resolution_set_resolution(res,
p->dpi[res->index],
p->dpi[res->index],
p->report_rate);
if (profile->is_active &&
i == (unsigned int)dpi_index)
res->index == (unsigned int)dpi_index)
res->is_active = true;
if (i == p->default_dpi) {
if (res->index == p->default_dpi) {
res->is_default = true;
if (!profile->is_active || dpi_index < 0 || dpi_index > 4)
res->is_active = true;
}
ratbag_resolution_set_range(res, sensor->dpi_min, sensor->dpi_max);
ratbag_resolution_set_dpi_list_from_range(res,
sensor->dpi_min,
sensor->dpi_max);
ratbag_resolution_set_report_rate_list(res,
drv_data->report_rates,
drv_data->num_report_rates);
}
}
static void
hidpp20drv_read_profile(struct ratbag_profile *profile, unsigned int index)
hidpp20drv_read_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag_led *led;
struct ratbag_button *button;
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
return hidpp20drv_read_profile_8100(profile, index);
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
hidpp20drv_read_profile_8100(profile);
} else {
hidpp20drv_read_resolution_dpi(profile);
hidpp20drv_read_special_key_mouse(device);
hidpp20drv_read_resolution_dpi(profile);
hidpp20drv_read_special_key_mouse(device);
profile->is_active = (profile->index == 0);
}
profile->is_active = (index == 0);
ratbag_profile_for_each_led(profile, led)
hidpp20drv_read_led(led);
ratbag_profile_for_each_button(profile, button)
hidpp20drv_read_button(button);
}
static int
@@ -841,6 +1079,14 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
}
case HIDPP_PAGE_ADJUSTABLE_REPORT_RATE: {
log_debug(ratbag, "device has adjustable report rate\n");
/* we read the profile once to get the correct number of
* supported report rates. */
rc = hidpp20drv_read_report_rate_8060(device);
if (rc < 0)
return 0; /* this is not a hard failure */
drv_data->capabilities |= HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060;
break;
}
case HIDPP_PAGE_COLOR_LED_EFFECTS: {
@@ -856,6 +1102,19 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
device->num_leds = drv_data->num_leds;
break;
}
case HIDPP_PAGE_LED_SW_CONTROL: {
log_debug(ratbag, "device has non-rgb leds\n");
drv_data->capabilities |= HIDPP_CAP_LED_SW_CONTROL_1300;
if (drv_data->capabilities & HIDPP_PAGE_COLOR_LED_EFFECTS)
return 0;
if (hidpp20drv_read_leds(device))
return 0;
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_LED);
break;
}
case HIDPP_PAGE_ONBOARD_PROFILES: {
log_debug(ratbag, "device has onboard profiles\n");
drv_data->capabilities |= HIDPP_CAP_ONBOARD_PROFILES_8100;
@@ -866,10 +1125,16 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
if (rc < 0)
return rc;
rc = hidpp20_onboard_profiles_initialize(drv_data->dev, drv_data->profiles);
if (rc < 0)
return rc;
drv_data->num_profiles = drv_data->profiles->num_profiles;
drv_data->num_resolutions = drv_data->profiles->num_modes;
drv_data->num_buttons = drv_data->profiles->num_buttons;
drv_data->num_leds = drv_data->profiles->num_leds;
/* We ignore the profile's num_leds and require
* HIDPP_PAGE_COLOR_LED_EFFECTS to succeed instead
*/
break;
}
@@ -892,7 +1157,7 @@ hidpp20drv_commit(struct ratbag_device *device)
struct ratbag_led *led;
struct ratbag_resolution *resolution;
int rc;
unsigned int i, dpi_index = 0;
unsigned int dpi_index = 0;
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
@@ -900,10 +1165,9 @@ hidpp20drv_commit(struct ratbag_device *device)
drv_data->profiles->profiles[profile->index].enabled = profile->is_enabled;
for (i = 0; i < profile->resolution.num_modes; i++) {
resolution = &profile->resolution.modes[i];
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->is_active)
dpi_index = i;
dpi_index = resolution->index;
rc = hidpp20drv_update_resolution_dpi(resolution,
resolution->dpi_x,
@@ -913,12 +1177,10 @@ hidpp20drv_commit(struct ratbag_device *device)
}
list_for_each(button, &profile->buttons, link) {
struct ratbag_button_action action = button->action;
if (!button->dirty)
continue;
rc = hidpp20drv_update_button(button, &action);
rc = hidpp20drv_update_button(button);
if (rc)
return RATBAG_ERROR_DEVICE;
}
@@ -932,10 +1194,15 @@ hidpp20drv_commit(struct ratbag_device *device)
return RATBAG_ERROR_DEVICE;
}
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
hidpp20_onboard_profiles_write(drv_data->dev,
profile->index,
drv_data->profiles);
}
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
rc = hidpp20_onboard_profiles_commit(drv_data->dev,
drv_data->profiles);
if (rc)
return RATBAG_ERROR_DEVICE;
list_for_each(profile, &device->profiles, link) {
if (profile->is_active)
hidpp20_onboard_profiles_set_current_dpi_index(drv_data->dev,
dpi_index);
@@ -1000,6 +1267,7 @@ hidpp20drv_remove(struct ratbag_device *device)
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(drv_data->profiles);
free(drv_data->led_infos.color_leds);
free(drv_data->controls);
free(drv_data->sensors);
if (drv_data->dev)
@@ -1007,6 +1275,22 @@ hidpp20drv_remove(struct ratbag_device *device)
free(drv_data);
}
static void
hidpp20drv_init_device(struct ratbag_device *device,
struct hidpp20drv_data *drv_data)
{
struct ratbag_profile *profile;
ratbag_device_init_profiles(device,
drv_data->num_profiles,
drv_data->num_resolutions,
drv_data->num_buttons,
drv_data->num_leds);
ratbag_device_for_each_profile(device, profile)
hidpp20drv_read_profile(profile);
}
static int
hidpp20drv_probe(struct ratbag_device *device)
{
@@ -1014,9 +1298,7 @@ hidpp20drv_probe(struct ratbag_device *device)
struct hidpp20drv_data *drv_data;
struct hidpp_device base;
struct hidpp20_device *dev;
const char *prop;
int device_idx = HIDPP_RECEIVER_IDX;
int nread = 0;
rc = ratbag_find_hidraw(device, hidpp20drv_test_hidraw);
if (rc)
@@ -1027,25 +1309,17 @@ hidpp20drv_probe(struct ratbag_device *device)
hidpp_device_init(&base, device->hidraw.fd);
hidpp_device_set_log_handler(&base, hidpp20_log, HIDPP_LOG_PRIORITY_RAW, device);
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP20_INDEX");
if (prop) {
sscanf(prop, "%d%n", &device_idx, &nread);
if (!nread || (prop[nread]) != '\0' || device_idx < 0) {
log_error(device->ratbag,
"Error parsing RATBAG_HIDPP20_INDEX: '%s' for %s\n",
prop,
device->name);
device_idx = HIDPP_RECEIVER_IDX;
}
}
device_idx = ratbag_device_data_hidpp20_get_index(device->data);
if (device_idx == -1)
device_idx = HIDPP_RECEIVER_IDX;
/* In the general case, we can treat all devices as wired devices
* here. If we talk to the correct hidraw device the kernel adjusts
* the device index for us, so even for unifying receiver devices
* we can just use 0xff as device index.
*
* If there is a special need like for G900, we can pass a
* udev prop RATBAG_HIDPP20_INDEX.
* If there is a special need like for G900, we can add this in the
* device data file.
*/
dev = hidpp20_device_new(&base, device_idx);
if (!dev) {
@@ -1067,11 +1341,7 @@ hidpp20drv_probe(struct ratbag_device *device)
if (rc)
goto err;
ratbag_device_init_profiles(device,
drv_data->num_profiles,
drv_data->num_resolutions,
drv_data->num_buttons,
drv_data->num_leds);
hidpp20drv_init_device(device, drv_data);
return rc;
err:
@@ -1085,8 +1355,5 @@ struct ratbag_driver hidpp20_driver = {
.probe = hidpp20drv_probe,
.remove = hidpp20drv_remove,
.commit = hidpp20drv_commit,
.read_profile = hidpp20drv_read_profile,
.set_active_profile = hidpp20drv_set_current_profile,
.read_button = hidpp20drv_read_button,
.read_led = hidpp20drv_read_led,
};

View File

@@ -36,16 +36,18 @@
#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_NUM_LED 1
#define LOGITECH_G300_DPI_MIN 250
#define LOGITECH_G300_DPI_MAX 2500
#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_GET_ACTIVE 0xF0
#define LOGITECH_G300_REPORT_ID_SET_ACTIVE 0xF0
#define LOGITECH_G300_REPORT_ID_GET_ACTIVE_LED 0xF1
#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_ACTIVE 4
#define LOGITECH_G300_REPORT_SIZE_PROFILE 35
struct logitech_g300_resolution {
@@ -61,10 +63,13 @@ struct logitech_g300_button {
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 led_red :1;
uint8_t led_green :1;
uint8_t led_blue :1;
uint8_t unknown1 :5;
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 */
uint8_t unknown2; /* dpi index for shift, but something else too */
struct logitech_g300_button buttons[LOGITECH_G300_BUTTON_MAX + 1];
} __attribute__((packed));
@@ -197,14 +202,24 @@ logitech_g300_frequency_to_raw(unsigned int frequency)
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;
struct logitech_g300_F0_report {
uint8_t id;
uint8_t unknown1 :1;
uint8_t resolution :3;
uint8_t profile :4;
uint8_t unknown2;
uint8_t unknown3;
} __attribute__((packed));
ret = ratbag_hidraw_raw_request(device, LOGITECH_G300_REPORT_ID_GET_ACTIVE_PROFILE, buf,
static int
logitech_g300_get_active_profile_and_resolution(struct ratbag_device *device)
{
struct ratbag_profile *profile;
struct logitech_g300_F0_report buf;
int ret;
ret = ratbag_hidraw_raw_request(device, 0xF0, (uint8_t*)&buf,
sizeof(buf), HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret < 0)
@@ -213,22 +228,26 @@ logitech_g300_current_profile(struct ratbag_device *device)
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;
list_for_each(profile, &device->profiles, link) {
struct ratbag_resolution *resolution;
report = drv_data->profile_data[i].report;
if (report.led == buf[1])
return i;
if (profile->index != buf.profile)
continue;
profile->is_active = true;
ratbag_profile_for_each_resolution(profile, resolution) {
resolution->is_active = resolution->index == buf.resolution;
}
}
return -EPROTO;
return 0;
}
static int
logitech_g300_set_current_profile(struct ratbag_device *device, unsigned int index)
logitech_g300_set_active_profile(struct ratbag_device *device, unsigned int index)
{
uint8_t buf[] = {LOGITECH_G300_REPORT_ID_SET_ACTIVE_PROFILE, 0x80 + index, 0x00, 0x00};
struct ratbag_profile *profile;
uint8_t buf[] = {LOGITECH_G300_REPORT_ID_SET_ACTIVE, 0x80 | (index << 4), 0x00, 0x00};
int ret;
if (index > LOGITECH_G300_PROFILE_MAX)
@@ -237,14 +256,28 @@ logitech_g300_set_current_profile(struct ratbag_device *device, unsigned int ind
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 (ret != sizeof(buf))
return ret;
/* Update the active resolution. After profile change the default is used. */
list_for_each(profile, &device->profiles, link) {
struct ratbag_resolution *resolution;
if (profile->index != index)
continue;
ratbag_profile_for_each_resolution(profile, resolution) {
resolution->is_active = resolution->is_default;
}
}
return 0;
}
#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};
uint8_t buf[] = {LOGITECH_G300_REPORT_ID_SET_ACTIVE, 0x40 | (index << 1), 0x00, 0x00};
int ret;
if (index >= LOGITECH_G300_NUM_DPI)
@@ -255,56 +288,6 @@ logitech_g300_set_current_resolution(struct ratbag_device *device, unsigned int
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)
@@ -329,7 +312,7 @@ logitech_g300_read_button(struct ratbag_button *button)
action = logitech_g300_raw_to_button_action(button_report->code);
if (action) {
button->action = *action;
ratbag_button_set_action(button, action);
}
else if (button_report->code == 0x00 && (button_report->modifier > 0x00 || button_report->key > 0x00))
{
@@ -341,19 +324,102 @@ logitech_g300_read_button(struct ratbag_button *button)
}
}
static void
logitech_g300_read_led(struct ratbag_led *led)
{
struct ratbag_profile *profile = led->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;
pdata = &drv_data->profile_data[profile->index];
profile_report = &pdata->report;
led->type = RATBAG_LED_TYPE_SIDE;
led->mode = RATBAG_LED_ON;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_111;
led->color.red = profile_report->led_red * 255;
led->color.green = profile_report->led_green * 255;
led->color.blue = profile_report->led_blue * 255;
}
static void
logitech_g300_read_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_resolution *resolution;
unsigned int hz;
uint8_t report_id;
int rc;
struct ratbag_button *button;
struct ratbag_led *led;
assert(profile->index <= LOGITECH_G300_PROFILE_MAX);
pdata = &drv_data->profile_data[profile->index];
report = &pdata->report;
switch (profile->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 < (int)sizeof(*report)) {
log_error(device->ratbag,
"Error while requesting profile: %d\n", rc);
return;
}
hz = logitech_g300_raw_to_frequency(report->frequency);
ratbag_profile_for_each_resolution(profile, resolution) {
struct logitech_g300_resolution *res =
&report->dpi_levels[resolution->index];
unsigned int dpis[10] = { 250, 500, 750, 1000, 1250, 1500, 1750,
2000, 2250, 2500 };
resolution->dpi_x = res->dpi * 250;
resolution->dpi_y = res->dpi * 250;
resolution->hz = hz;
resolution->is_default = res->is_default;
resolution->is_active = res->is_default;
ratbag_resolution_set_dpi_list(resolution, dpis,
ARRAY_LENGTH(dpis));
ratbag_resolution_set_report_rate_list(resolution, &hz, 1);
}
ratbag_profile_for_each_button(profile, button)
logitech_g300_read_button(button);
ratbag_profile_for_each_led(profile, led)
logitech_g300_read_led(led);
}
static int
logitech_g300_test_hidraw(struct ratbag_device *device)
{
return ratbag_hidraw_has_report(device, LOGITECH_G300_REPORT_ID_GET_ACTIVE_PROFILE);
return ratbag_hidraw_has_report(device, LOGITECH_G300_REPORT_ID_GET_ACTIVE);
}
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;
struct ratbag_profile *profile;
rc = ratbag_find_hidraw(device, logitech_g300_test_hidraw);
if (rc)
@@ -369,6 +435,9 @@ logitech_g300_probe(struct ratbag_device *device)
LOGITECH_G300_BUTTON_MAX + 1,
LOGITECH_G300_NUM_LED);
ratbag_device_for_each_profile(device, profile)
logitech_g300_read_profile(profile);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_RESOLUTION);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_PROFILE);
@@ -376,7 +445,7 @@ logitech_g300_probe(struct ratbag_device *device)
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_KEY);
active_idx = logitech_g300_current_profile(device);
active_idx = logitech_g300_get_active_profile_and_resolution(device);
if (active_idx < 0) {
log_error(device->ratbag,
@@ -387,17 +456,10 @@ logitech_g300_probe(struct ratbag_device *device)
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);
active_idx);
return 0;
@@ -414,24 +476,33 @@ logitech_g300_write_profile(struct ratbag_profile *profile)
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;
struct ratbag_button *button;
struct ratbag_led *led;
uint8_t *buf;
unsigned int hz, i;
unsigned int hz;
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];
ratbag_profile_for_each_resolution(profile, resolution) {
struct logitech_g300_resolution *res =
&report->dpi_levels[resolution->index];
res->dpi = resolution->dpi_x / 250;
res->is_default = resolution->is_default;
if (profile->is_active && resolution->is_active)
logitech_g300_set_current_resolution(device,
resolution->index);
/* The same hz is used for all resolutions */
if (resolution->index == 0) {
hz = resolution->hz;
report->frequency = logitech_g300_frequency_to_raw(hz);
}
}
list_for_each(button, &profile->buttons, link) {
@@ -452,6 +523,17 @@ logitech_g300_write_profile(struct ratbag_profile *profile)
}
}
list_for_each(led, &profile->leds, link) {
if (!led->dirty)
continue;
/* Clamp the 8 bit colors to 1 bit */
report->led_red = led->color.red > 127;
report->led_green = led->color.green > 127;
report->led_blue = led->color.blue > 127;
}
buf = (uint8_t*)report;
rc = ratbag_hidraw_raw_request(device, report->id,
@@ -460,7 +542,13 @@ logitech_g300_write_profile(struct ratbag_profile *profile)
HID_FEATURE_REPORT,
HID_REQ_SET_REPORT);
return rc;
if (rc < (int)sizeof(*report)) {
log_error(device->ratbag,
"Error while writing profile: %d\n", rc);
return rc;
}
return 0;
}
static int
@@ -496,8 +584,6 @@ struct ratbag_driver logitech_g300_driver = {
.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,
.set_active_profile = logitech_g300_set_active_profile,
};

View File

@@ -391,83 +391,6 @@ roccat_button_to_action(struct ratbag_profile *profile,
return roccat_raw_to_button_action(data);
}
static void
roccat_read_profile(struct ratbag_profile *profile, unsigned int index)
{
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data;
struct ratbag_resolution *resolution;
struct roccat_settings_report *setting_report;
uint8_t *buf;
unsigned int report_rate;
unsigned int i;
int dpi_x, dpi_y, hz;
int rc;
assert(index <= ROCCAT_PROFILE_MAX);
drv_data = ratbag_get_drv_data(device);
setting_report = &drv_data->settings[index];
buf = (uint8_t*)setting_report;
roccat_set_config_profile(device, index, ROCCAT_CONFIG_SETTINGS);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (rc < ROCCAT_REPORT_SIZE_SETTINGS)
return;
/* first retrieve the report rate, it is set per profile */
switch (setting_report->report_rate) {
case 0x00: report_rate = 125; break;
case 0x01: report_rate = 250; break;
case 0x02: report_rate = 500; break;
case 0x03: report_rate = 1000; break;
default:
log_error(device->ratbag,
"error while reading the report rate of the mouse (0x%02x)\n",
buf[26]);
report_rate = 0;
}
for (i = 0; i < profile->resolution.num_modes; i++) {
dpi_x = setting_report->xres[i] * 50;
dpi_y = setting_report->yres[i] * 50;
hz = report_rate;
if (!(setting_report->dpi_mask & (1 << i))) {
/* the profile is disabled, overwrite it */
dpi_x = 0;
dpi_y = 0;
hz = 0;
}
resolution = ratbag_resolution_init(profile, i, dpi_x, dpi_y, hz);
ratbag_resolution_set_cap(resolution,
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
resolution->is_active = (i == setting_report->current_dpi);
}
buf = drv_data->profiles[index];
roccat_set_config_profile(device, index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
msleep(10);
if (rc < ROCCAT_REPORT_SIZE_PROFILE)
return;
if (!roccat_crc_is_valid(device, buf, ROCCAT_REPORT_SIZE_PROFILE))
log_error(device->ratbag,
"Error while reading profile %d, continuing...\n",
profile->index);
log_raw(device->ratbag, "profile: %d %s:%d\n",
buf[2],
__FILE__, __LINE__);
}
static int
roccat_write_profile(struct ratbag_profile *profile)
{
@@ -521,7 +444,7 @@ roccat_read_button(struct ratbag_button *button)
drv_data = ratbag_get_drv_data(device);
action = roccat_button_to_action(button->profile, button->index);
if (action)
button->action = *action;
ratbag_button_set_action(button, action);
button->type = roccat_raw_to_button_type(button->index);
// if (action == NULL)
// log_error(device->ratbag, "button: %d -> %d %s:%d\n",
@@ -733,7 +656,6 @@ roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data = ratbag_get_drv_data(device);
struct roccat_settings_report *settings_report;
unsigned int index;
uint8_t *buf;
int rc;
@@ -744,11 +666,8 @@ roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
settings_report = &drv_data->settings[profile->index];
/* retrieve which resolution is asked to be changed */
index = resolution - profile->resolution.modes;
settings_report->xres[index] = dpi_x / 50;
settings_report->yres[index] = dpi_y / 50;
settings_report->xres[resolution->index] = dpi_x / 50;
settings_report->yres[resolution->index] = dpi_y / 50;
buf = (uint8_t*)settings_report;
@@ -773,6 +692,88 @@ roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
return rc;
}
static void
roccat_read_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data;
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct roccat_settings_report *setting_report;
uint8_t *buf;
unsigned int report_rate;
int dpi_x, dpi_y, hz;
int rc;
unsigned int report_rates[] = { 125, 250, 500, 1000 };
assert(profile->index <= ROCCAT_PROFILE_MAX);
drv_data = ratbag_get_drv_data(device);
setting_report = &drv_data->settings[profile->index];
buf = (uint8_t*)setting_report;
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_SETTINGS);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (rc < ROCCAT_REPORT_SIZE_SETTINGS)
return;
/* first retrieve the report rate, it is set per profile */
if (setting_report->report_rate < ARRAY_LENGTH(report_rates)) {
report_rate = report_rates[setting_report->report_rate];
} else {
log_error(device->ratbag,
"error while reading the report rate of the mouse (0x%02x)\n",
buf[26]);
report_rate = 0;
}
ratbag_profile_for_each_resolution(profile, resolution) {
dpi_x = setting_report->xres[resolution->index] * 50;
dpi_y = setting_report->yres[resolution->index] * 50;
hz = report_rate;
if (!(setting_report->dpi_mask & (1 << resolution->index))) {
/* the profile is disabled, overwrite it */
dpi_x = 0;
dpi_y = 0;
hz = 0;
}
ratbag_resolution_set_resolution(resolution, dpi_x, dpi_y, hz);
ratbag_resolution_set_cap(resolution,
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
resolution->is_active = (resolution->index == setting_report->current_dpi);
ratbag_resolution_set_report_rate_list(resolution, report_rates,
ARRAY_LENGTH(report_rates));
}
ratbag_profile_for_each_button(profile, button)
roccat_read_button(button);
buf = drv_data->profiles[profile->index];
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
msleep(10);
if (rc < ROCCAT_REPORT_SIZE_PROFILE)
return;
if (!roccat_crc_is_valid(device, buf, ROCCAT_REPORT_SIZE_PROFILE))
log_error(device->ratbag,
"Error while reading profile %d, continuing...\n",
profile->index);
log_raw(device->ratbag, "profile: %d %s:%d\n",
buf[2],
__FILE__, __LINE__);
}
static int
roccat_probe(struct ratbag_device *device)
{
@@ -800,6 +801,9 @@ roccat_probe(struct ratbag_device *device)
ROCCAT_BUTTON_MAX + 1,
ROCCAT_LED_MAX);
ratbag_device_for_each_profile(device, profile)
roccat_read_profile(profile);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
@@ -845,10 +849,8 @@ struct ratbag_driver roccat_driver = {
.id = "roccat",
.probe = roccat_probe,
.remove = roccat_remove,
.read_profile = roccat_read_profile,
.write_profile = roccat_write_profile,
.set_active_profile = roccat_set_current_profile,
.read_button = roccat_read_button,
.write_button = roccat_write_button,
.write_resolution_dpi = roccat_write_resolution_dpi,
};

533
src/driver-steelseries.c Normal file
View File

@@ -0,0 +1,533 @@
/*
* Copyright © 2017 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"
#include "libratbag-data.h"
#include "hidpp-generic.h"
#define STEELSERIES_NUM_PROFILES 1
#define STEELSERIES_NUM_DPI 2
/* not sure these two are used for */
#define STEELSERIES_REPORT_ID_1 0x01
#define STEELSERIES_REPORT_ID_2 0x02
#define STEELSERIES_REPORT_SIZE_SHORT 32
#define STEELSERIES_REPORT_SIZE 64
#define STEELSERIES_REPORT_LONG_SIZE 262
#define STEELSERIES_ID_DPI_SHORT 0x03
#define STEELSERIES_ID_REPORT_RATE_SHORT 0x04
#define STEELSERIES_ID_SAVE_SHORT 0x09
#define STEELSERIES_ID_BUTTONS 0x31
#define STEELSERIES_ID_DPI 0x53
#define STEELSERIES_ID_REPORT_RATE 0x54
#define STEELSERIES_ID_LED 0x5b
#define STEELSERIES_ID_SAVE 0x59
#define STEELSERIES_BUTTON_OFF 0x00
#define STEELSERIES_BUTTON_RES_CYCLE 0x30
#define STEELSERIES_BUTTON_WHEEL_UP 0x31
#define STEELSERIES_BUTTON_WHEEL_DOWN 0x32
#define STEELSERIES_BUTTON_KBD 0x51
#define STEELSERIES_BUTTON_CONSUMER 0x61
static const enum ratbag_button_type button_types[6] =
{
RATBAG_BUTTON_TYPE_LEFT,
RATBAG_BUTTON_TYPE_RIGHT,
RATBAG_BUTTON_TYPE_MIDDLE,
RATBAG_BUTTON_TYPE_THUMB,
RATBAG_BUTTON_TYPE_THUMB2,
RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP,
};
static const struct ratbag_button_action button_actions[6] =
{
BUTTON_ACTION_BUTTON(1),
BUTTON_ACTION_BUTTON(2),
BUTTON_ACTION_BUTTON(3),
BUTTON_ACTION_BUTTON(4),
BUTTON_ACTION_BUTTON(5),
BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP),
};
static int
steelseries_test_hidraw(struct ratbag_device *device)
{
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
if (device_version > 1)
return ratbag_hidraw_has_report(device, STEELSERIES_REPORT_ID_1);
else
return true;
}
static int
steelseries_probe(struct ratbag_device *device)
{
struct ratbag_profile *profile = NULL;
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct ratbag_led *led;
int rc, button_count, led_count, device_version;
_cleanup_(dpi_list_freep) struct dpi_list *dpilist = NULL;
_cleanup_(freep) struct dpi_range *dpirange = NULL;
unsigned int report_rates[] = { 125, 250, 500, 1000 };
rc = ratbag_find_hidraw(device, steelseries_test_hidraw);
if (rc)
return rc;
device_version = ratbag_device_data_steelseries_get_device_version(device->data);
button_count = ratbag_device_data_steelseries_get_button_count(device->data);
led_count = ratbag_device_data_steelseries_get_led_count(device->data);
dpirange = ratbag_device_data_steelseries_get_dpi_range(device->data);
dpilist = ratbag_device_data_steelseries_get_dpi_list(device->data);
ratbag_device_init_profiles(device,
STEELSERIES_NUM_PROFILES,
STEELSERIES_NUM_DPI,
button_count,
led_count);
/* only later models allow setting buttons on the device */
if (device_version > 1) {
/* set these caps manually as they are not assumed with only 1 profile */
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_KEY);
}
ratbag_device_unset_capability(device, RATBAG_DEVICE_CAP_QUERY_CONFIGURATION);
/* The device does not support reading the current settings. Fall back
to some sensible defaults */
ratbag_device_for_each_profile(device, profile) {
profile->is_active = true;
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->index == 0) {
resolution->is_active = true;
resolution->is_default = true;
}
if (dpirange)
ratbag_resolution_set_dpi_list_from_range(resolution,
dpirange->min,
dpirange->max);
if (dpilist)
ratbag_resolution_set_dpi_list(resolution,
(unsigned int *)dpilist->entries,
dpilist->nentries);
resolution->dpi_x = resolution->dpis[resolution->index];
resolution->dpi_y = resolution->dpis[resolution->index];
ratbag_resolution_set_report_rate_list(resolution, report_rates,
ARRAY_LENGTH(report_rates));
resolution->hz = 1000;
}
ratbag_profile_for_each_button(profile, button) {
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
button->type = button_types[button->index];
ratbag_button_set_action(button, &button_actions[button->index]);
}
ratbag_profile_for_each_led(profile, led) {
led->type = led->index == 0 ? RATBAG_LED_TYPE_LOGO : RATBAG_LED_TYPE_WHEEL;
led->mode = RATBAG_LED_ON;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
led->color.red = 0;
led->color.green = 0;
led->color.blue = 255;
}
}
return 0;
}
static int
steelseries_write_dpi(struct ratbag_resolution *resolution)
{
struct ratbag_device *device = resolution->profile->device;
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
struct dpi_list *dpilist = NULL;
struct dpi_range *dpirange = NULL;
int ret;
size_t buf_len;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
dpirange = ratbag_device_data_steelseries_get_dpi_range(device->data);
dpilist = ratbag_device_data_steelseries_get_dpi_list(device->data);
if (device_version == 1) {
int i = 0;
/* when using lists the entries are enumerated in reverse */
if (dpilist) {
for (i = 0; i < (int)resolution->ndpis; i++) {
if (resolution->dpis[i] == resolution->dpi_x)
break;
}
i = resolution->ndpis - i;
} else {
i = resolution->dpi_x / dpirange->step - 1;
}
buf_len = STEELSERIES_REPORT_SIZE_SHORT;
buf[0] = STEELSERIES_ID_DPI_SHORT;
buf[1] = resolution->index + 1;
buf[2] = i;
} else {
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_DPI;
buf[2] = resolution->index + 1;
buf[3] = resolution->dpi_x / dpirange->step - 1;
buf[6] = 0x42; /* not sure if needed */
}
ret = ratbag_hidraw_output_report(device, buf, buf_len);
if ((size_t)ret != buf_len)
return ret;
return 0;
}
static int
steelseries_write_report_rate(struct ratbag_resolution *resolution)
{
struct ratbag_device *device = resolution->profile->device;
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
int ret;
size_t buf_len;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
if (device_version == 1) {
buf_len = STEELSERIES_REPORT_SIZE_SHORT;
buf[0] = STEELSERIES_ID_REPORT_RATE_SHORT;
buf[2] = 1000 / resolution->hz;
} else {
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_REPORT_RATE;
buf[2] = 1000 / resolution->hz;
}
ret = ratbag_hidraw_output_report(device, buf, buf_len);
if ((size_t)ret != buf_len)
return ret;
return 0;
}
static int
steelseries_write_buttons(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
uint8_t buf[STEELSERIES_REPORT_LONG_SIZE] = {0};
struct ratbag_button *button;
int ret;
if (device_version < 2)
return 0;
buf[0] = STEELSERIES_ID_BUTTONS;
ratbag_profile_for_each_button(profile, button) {
struct ratbag_button_action *action = &button->action;
uint16_t code;
int idx;
/* Each button takes up 5 bytes starting from index 2 */
idx = 2 + button->index * 5;
switch (action->type) {
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
buf[idx] = action->action.button;
break;
case RATBAG_BUTTON_ACTION_TYPE_KEY:
code = ratbag_hidraw_get_keyboard_usage_from_keycode(
device, action->action.key.key);
if (code) {
buf[idx] = STEELSERIES_BUTTON_KBD;
buf[idx + 1] = code;
} else {
code = ratbag_hidraw_get_consumer_usage_from_keycode(
device, action->action.key.key);
buf[idx] = STEELSERIES_BUTTON_CONSUMER;
buf[idx + 1] = code;
}
break;
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
switch (action->action.special) {
case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP:
buf[idx] = STEELSERIES_BUTTON_RES_CYCLE;
break;
case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP:
buf[idx] = STEELSERIES_BUTTON_WHEEL_UP;
break;
case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN:
buf[idx] = STEELSERIES_BUTTON_WHEEL_DOWN;
break;
default:
break;
}
break;
case RATBAG_BUTTON_ACTION_TYPE_NONE:
default:
buf[idx] = STEELSERIES_BUTTON_OFF;
break;
}
}
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
if (ret != sizeof(buf))
return ret;
return 0;
}
static int
steelseries_write_led(struct ratbag_led *led)
{
struct ratbag_device *device = led->profile->device;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
uint16_t duration, min_duration;
int ret;
buf[0] = STEELSERIES_ID_LED;
buf[2] = led->index;
/* not sure if these two are needed */
buf[15] = 0x01;
buf[17] = 0x01;
switch(led->mode) {
case RATBAG_LED_OFF:
buf[19] = 0x01;
buf[27] = 0x01;
buf[28] = buf[31] = 0x00;
buf[29] = buf[32] = 0x00;
buf[30] = buf[33] = 0x00;
/* not sure why the duration is set for the steady color or why
it is different for the two LEDs */
duration = led->index == 0 ? 10000 : 5000; /* 0x1027 or 0x8813 */
break;
case RATBAG_LED_ON:
buf[19] = 0x01;
buf[27] = 0x01;
buf[28] = buf[31] = led->color.red;
buf[29] = buf[32] = led->color.green;
buf[30] = buf[33] = led->color.blue;
/* not sure why the duration is set for the steady color or why
it is different for the two LEDs */
duration = led->index == 0 ? 10000 : 5000; /* 0x1027 or 0x8813 */
break;
case RATBAG_LED_CYCLE:
buf[27] = 0x04; /* number of steps in cycle */
/* start color */
buf[28] = buf[31] = 0xFF;
buf[29] = buf[32] = 0x00;
buf[30] = buf[33] = 0x00;
/* Cycle to green */
buf[35] = 0x00;
buf[36] = 0xFF;
buf[37] = 0x00;
buf[38] = 0x54; /* normalized time share of animation */
/* Cycle to blue */
buf[39] = 0x00;
buf[40] = 0x00;
buf[41] = 0xFF;
buf[42] = 0x54; /* normalized time share of animation */
/* Cycle to red */
buf[43] = 0xFF;
buf[44] = 0x00;
buf[45] = 0x00;
buf[46] = 0x56; /* normalized time share of animation */
duration = led->ms;
break;
case RATBAG_LED_BREATHING:
buf[27] = 0x03; /* number of steps in cycle */
/* start color */
buf[28] = buf[31] = led->color.red;
buf[29] = buf[32] = led->color.green;
buf[30] = buf[33] = led->color.blue;
/* Cycle to black */
buf[35] = 0x00;
buf[36] = 0x00;
buf[37] = 0x00;
buf[38] = 0x7F; /* normalized time share of animation */
/* Cycle to selected color */
buf[39] = led->color.red;
buf[40] = led->color.green;
buf[41] = led->color.blue;
buf[42] = 0x7F; /* normalized time share of animation */
duration = led->ms;
break;
default:
return -EINVAL;
}
/* this seems to be the minimum allowed */
min_duration = buf[27] * 330;
if (duration < min_duration)
duration = min_duration;
hidpp_set_unaligned_le_u16(&buf[3], duration);
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
if (ret != sizeof(buf))
return ret;
return 0;
}
static int
steelseries_write_save(struct ratbag_device *device)
{
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
int ret;
size_t buf_len;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
if (device_version == 1) {
buf_len = STEELSERIES_REPORT_SIZE_SHORT;
buf[0] = STEELSERIES_ID_SAVE_SHORT;
} else {
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_SAVE;
}
ret = ratbag_hidraw_output_report(device, buf, buf_len);
if ((size_t)ret != buf_len)
return ret;
return 0;
}
static int
steelseries_write_profile(struct ratbag_profile *profile)
{
struct ratbag_resolution *resolution;
struct ratbag_led *led;
int rc;
ratbag_profile_for_each_resolution(profile, resolution) {
rc = steelseries_write_dpi(resolution);
if (rc != 0)
return rc;
/* The same hz is used for all resolutions. Only write once. */
if (resolution->index > 0)
continue;
rc = steelseries_write_report_rate(resolution);
if (rc != 0)
return rc;
}
rc = steelseries_write_buttons(profile);
if (rc != 0)
return rc;
ratbag_profile_for_each_led(profile, led) {
if (!led->dirty)
continue;
rc = steelseries_write_led(led);
if (rc != 0)
return rc;
}
return 0;
}
static int
steelseries_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 = steelseries_write_profile(profile);
if (rc)
return rc;
/* persist the current settings on the device */
rc = steelseries_write_save(device);
if (rc)
return rc;
}
return 0;
}
static void
steelseries_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
}
struct ratbag_driver steelseries_driver = {
.name = "SteelSeries",
.id = "steelseries",
.probe = steelseries_probe,
.remove = steelseries_remove,
.commit = steelseries_commit,
};

View File

@@ -29,49 +29,6 @@
#include "libratbag-private.h"
#include "libratbag-test.h"
static void
test_read_profile(struct ratbag_profile *profile, unsigned int index)
{
struct ratbag_test_device *d = ratbag_get_drv_data(profile->device);
struct ratbag_test_profile *p;
struct ratbag_test_resolution *r;
struct ratbag_resolution *res;
unsigned int i;
bool active_set = false;
bool default_set = false;
assert(index < d->num_profiles);
p = &d->profiles[index];
for (i = 0; i < d->num_resolutions; i++) {
r = &p->resolutions[i];
res = ratbag_resolution_init(profile, i, r->xres, r->yres, r->hz);
res->is_active = r->active;
if (r->active)
active_set = true;
res->is_default = r->dflt;
if (r->dflt)
default_set = true;
ratbag_resolution_set_cap(res, r->caps);
res->hz = r->hz;
assert(res);
}
/* special case triggered by the test suite when num_resolutions is 0 */
if (d->num_resolutions) {
res = ratbag_profile_get_resolution(profile, 0);
assert(res);
if (!active_set)
res->is_active = true;
if (!default_set)
res->is_default = true;
ratbag_resolution_unref(res);
}
profile->is_active = p->active;
profile->is_enabled = !p->disabled;
}
static int
test_write_profile(struct ratbag_profile *profile)
{
@@ -149,6 +106,11 @@ test_read_led(struct ratbag_led *led)
struct ratbag_test_profile *p = &d->profiles[led->profile->index];
struct ratbag_test_led t_led = p->leds[led->index];
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
switch (t_led.mode) {
case RATBAG_LED_ON:
led->mode = RATBAG_LED_ON;
@@ -165,8 +127,13 @@ test_read_led(struct ratbag_led *led)
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->ms = t_led.ms;
led->brightness = t_led.brightness;
/* The led type has to be the same anyway so make writing test
* devices easier by always getting the first profile's LED type.
*/
led->type = d->profiles[0].leds[led->index].type;
}
static int
@@ -181,7 +148,7 @@ test_write_button(struct ratbag_button *button,
static int
test_write_led(struct ratbag_led *led,
enum ratbag_led_mode mode,
struct ratbag_color color, unsigned int hz,
struct ratbag_color color, unsigned int ms,
unsigned int brightness)
{
/* check if the device is still valid */
@@ -202,10 +169,91 @@ test_fake_probe(struct ratbag_device *device)
return -ENODEV;
}
static void
test_read_profile(struct ratbag_profile *profile)
{
struct ratbag_test_device *d = ratbag_get_drv_data(profile->device);
struct ratbag_test_profile *p;
struct ratbag_test_profile *p0;
struct ratbag_test_resolution *r;
struct ratbag_test_resolution *r0;
struct ratbag_button *button;
struct ratbag_led *led;
unsigned int i;
bool active_set = false;
bool default_set = false;
assert(profile->index < d->num_profiles);
p = &d->profiles[profile->index];
p0 = &d->profiles[0];
r0 = &p0->resolutions[0];
for (i = 0; i < d->num_resolutions; i++) {
_cleanup_resolution_ struct ratbag_resolution *res;
size_t nrates = 0;
r = &p->resolutions[i];
res = ratbag_profile_get_resolution(profile, i);
assert(res);
ratbag_resolution_set_resolution(res, r->xres, r->yres, r->hz);
if (r0->dpi_min != 0 && r0->dpi_max != 0)
ratbag_resolution_set_dpi_list_from_range(res,
r0->dpi_min,
r0->dpi_max);
for (size_t r = 0; r < ARRAY_LENGTH(r0->report_rates); r++) {
if (r0->report_rates[r] > 0)
nrates++;
}
if (nrates > 0)
ratbag_resolution_set_report_rate_list(res,
r0->report_rates,
nrates);
res->is_active = r->active;
if (r->active)
active_set = true;
res->is_default = r->dflt;
if (r->dflt)
default_set = true;
ratbag_resolution_set_cap(res, r->caps);
res->hz = r->hz;
}
/* special case triggered by the test suite when num_resolutions is 0 */
if (d->num_resolutions) {
_cleanup_resolution_ struct ratbag_resolution *res;
res = ratbag_profile_get_resolution(profile, 0);
assert(res);
if (!active_set)
res->is_active = true;
if (!default_set)
res->is_default = true;
}
ratbag_profile_for_each_button(profile, button)
test_read_button(button);
ratbag_profile_for_each_led(profile, led)
test_read_led(led);
profile->is_active = p->active;
profile->is_enabled = !p->disabled;
for (i = 0; i < ARRAY_LENGTH(p->caps) && p->caps[i]; i++) {
ratbag_profile_set_cap(profile, p->caps[i]);
}
}
static int
test_probe(struct ratbag_device *device, const void *data)
{
struct ratbag_test_device *test_device;
struct ratbag_profile *profile;
test_device = zalloc(sizeof(*test_device));
memcpy(test_device, data, sizeof(*test_device));
@@ -216,11 +264,15 @@ test_probe(struct ratbag_device *device, const void *data)
test_device->num_resolutions,
test_device->num_buttons,
test_device->num_leds);
if (test_device->num_profiles > 1) {
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_DISABLE_PROFILE);
}
ratbag_device_for_each_profile(device, profile)
test_read_profile(profile);
return 0;
}
@@ -244,11 +296,8 @@ struct ratbag_driver test_driver = {
.test_probe = test_probe,
.remove = test_remove,
.get_svg_name = test_get_svg_name,
.read_profile = test_read_profile,
.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

@@ -114,57 +114,23 @@ hidpp10_dpi_table_get_min_dpi(struct hidpp10_device *dev)
int
hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev,
const char *str_list)
const struct dpi_list *list)
{
unsigned int i, count, index;
int nread, dpi = 0;
char c;
/*
* str_list is in the form:
* "0;200;400;600;800;1000;1200"
*/
size_t i;
/* first, count how many elements do we have in the table */
count = 1;
i = 0;
while (str_list[i] != 0) {
c = str_list[i++];
if (c == ';')
count++;
}
index = 0;
/* check that the max raw value fits in a uint8_t */
if (count + 0x80 - 1> 0xff)
return -EINVAL;
dev->dpi_count = count;
dev->dpi_table = zalloc(count * sizeof(*dev->dpi_table));
while (*str_list != 0 && index < count) {
if (*str_list == ';') {
str_list++;
continue;
}
nread = 0;
sscanf(str_list, "%d%n", &dpi, &nread);
if (!nread || dpi < 0)
goto err;
dev->dpi_table[index].raw_value = index + 0x80;
dev->dpi_table[index].dpi = dpi;
str_list += nread;
index++;
}
if (index != count)
if (list->nentries + 0x80 - 1> 0xff)
goto err;
return 0;
dev->dpi_count = list->nentries;
dev->dpi_table = zalloc(list->nentries * sizeof(*dev->dpi_table));
dev->dpi_table_is_range = false;
for (i = 0; i < list->nentries; i++) {
dev->dpi_table[i].raw_value = i + 0x80;
dev->dpi_table[i].dpi = list->entries[i];
}
return 0;
err:
dev->dpi_count = 0;
free(dev->dpi_table);
@@ -174,30 +140,22 @@ err:
int
hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev,
const char *str_dpi)
const struct dpi_range *range)
{
float min, max, step;
unsigned raw_max, i;
int rc;
/*
* str_list is in the form:
* "MIN:MAX@STEP"
*/
rc = sscanf(str_dpi, "%f:%f@%f", &min, &max, &step);
if (rc != 3)
return -EINVAL;
raw_max = (max - min) / step;
raw_max = (range->max - range->min) / range->step;
if (raw_max > 0xff)
return -EINVAL;
dev->dpi_count = raw_max + 1;
dev->dpi_table = zalloc((raw_max + 1) * sizeof(*dev->dpi_table));
dev->dpi_table_is_range = true;
for (i = 1; i <= raw_max; i++) {
dev->dpi_table[i].raw_value = i;
dev->dpi_table[i].dpi = round((min + step * i) / 25.0f) * 25;
dev->dpi_table[i].dpi = round((range->min + range->step * i) / 25.0f) * 25;
}
return 0;
@@ -1186,8 +1144,7 @@ hidpp10_onboard_profiles_read_macro(struct hidpp10_device *device,
return index;
out_err:
if (macro)
free(macro);
free(macro);
return rc;
}
@@ -1495,8 +1452,12 @@ hidpp10_read_profile(struct hidpp10_device *dev, uint8_t number)
long_clear_bit(&pages, 0);
long_clear_bit(&pages, 1);
for (i = 0; i < dev->profile_count; i++)
long_clear_bit(&pages, dev->profiles[i].page);
for (i = 0; i < dev->profile_count; i++) {
uint8_t page = dev->profiles[i].page;
assert(page < sizeof(pages) * 8);
long_clear_bit(&pages, page);
}
page = ffsl(pages) - 1;
profile->page = page;
@@ -1556,6 +1517,9 @@ hidpp10_read_profile(struct hidpp10_device *dev, uint8_t number)
hidpp10_fill_buttons(dev, profile, buttons, PROFILE_NUM_BUTTONS);
break;
case HIDPP10_PROFILE_G9:
profile->red = p9->red;
profile->green = p9->green;
profile->blue = p9->blue;
profile->default_dpi_mode = p9->default_dpi_mode;
profile->refresh_rate = p9->usb_refresh_rate ? 1000/p9->usb_refresh_rate : 0;
@@ -2150,12 +2114,24 @@ hidpp10_set_current_resolution(struct hidpp10_device *dev,
uint16_t yres)
{
unsigned idx = dev->index;
union hidpp10_message resolution = CMD_CURRENT_RESOLUTION(REPORT_ID_LONG, idx, SET_LONG_REGISTER_REQ);
union hidpp10_message resolution = CMD_CURRENT_RESOLUTION(REPORT_ID_SHORT, idx, SET_REGISTER_REQ);
union hidpp10_message resolution_long = CMD_CURRENT_RESOLUTION(REPORT_ID_LONG, idx, SET_LONG_REGISTER_REQ);
int res;
hidpp_set_unaligned_le_u16(&resolution.data[4], hidpp10_get_dpi_mapping(dev, xres));
hidpp_set_unaligned_le_u16(&resolution.data[6], hidpp10_get_dpi_mapping(dev, yres));
switch (dev->profile_type) {
case HIDPP10_PROFILE_G9:
resolution.data[4] = hidpp10_get_dpi_mapping(dev, xres);
res = hidpp10_request_command(dev, &resolution);
break;
default:
hidpp_set_unaligned_le_u16(&resolution_long.data[4], hidpp10_get_dpi_mapping(dev, xres));
hidpp_set_unaligned_le_u16(&resolution_long.data[6], hidpp10_get_dpi_mapping(dev, yres));
return hidpp10_request_command(dev, &resolution);
res = hidpp10_request_command(dev, &resolution_long);
break;
}
return res;
}
/* -------------------------------------------------------------------------- */
@@ -2767,8 +2743,7 @@ hidpp10_device_destroy(struct hidpp10_device *dev)
union hidpp10_macro_data **macro;
unsigned i;
if (dev->dpi_table)
free(dev->dpi_table);
free(dev->dpi_table);
for (i = 0; i < dev->profile_count; i++) {
ARRAY_FOR_EACH(dev->profiles[i].macros, macro) {
if (*macro) {

View File

@@ -33,6 +33,9 @@
#include "hidpp-generic.h"
struct dpi_list;
struct dpi_range;
/* FIXME: that's what my G500s supports, but only pages 3-5 are valid.
* 0 is zeroed, 1 and 2 are garbage, all above 6 is garbage */
#define HIDPP10_NUM_PROFILES 5
@@ -57,6 +60,8 @@ struct hidpp10_dpi_mapping {
struct hidpp10_device {
struct hidpp_device base;
unsigned index;
bool dpi_table_is_range;
uint8_t dpi_count;
struct hidpp10_dpi_mapping *dpi_table;
enum hidpp10_profile_type profile_type;
@@ -88,7 +93,7 @@ hidpp10_device_destroy(struct hidpp10_device *dev);
*/
int
hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev,
const char *str_list);
const struct dpi_list *list);
/**
* Builds the table of dpi for the device from the given dpi description.
@@ -103,7 +108,7 @@ hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev,
*/
int
hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev,
const char *str_dpi);
const struct dpi_range *range);
/**
* Returns the lowest dpi stored in the dpi mapping table generated by

File diff suppressed because it is too large Load Diff

View File

@@ -31,6 +31,7 @@
#include <stdint.h>
#include "hidpp-generic.h"
#include "libratbag-util.h"
struct _hidpp20_message {
uint8_t report_id;
@@ -125,6 +126,130 @@ int hidpp20_batterylevel_get_battery_level(struct hidpp20_device *device,
uint16_t *level,
uint16_t *next_level);
/* -------------------------------------------------------------------------- */
/* 0x1300: LED software control */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_LED_SW_CONTROL 0x1300
enum hidpp20_led_sw_ctrl_led_type {
HIDPP20_LED_TYPE_BATTERY = 0x01,
HIDPP20_LED_TYPE_DPI,
HIDPP20_LED_TYPE_PROFILE,
HIDPP20_LED_TYPE_LOGO,
HIDPP20_LED_TYPE_COSMETIC
};
enum hidpp20_led_sw_ctrl_led_mode {
HIDPP20_LED_MODE_OFF = 0x01,
HIDPP20_LED_MODE_ON = 0x02,
HIDPP20_LED_MODE_BLINK = 0x04,
HIDPP20_LED_MODE_TRAVEL = 0x08,
HIDPP20_LED_MODE_RAMP_UP = 0x10,
HIDPP20_LED_MODE_RAMP_DOWN = 0x20,
HIDPP20_LED_MODE_HEARTBEAT = 0x40,
HIDPP20_LED_MODE_BREATHING = 0x80
};
#define HIDPP20_LED_SW_CONTROL_LED_INDEX_ALL 0xff
struct hidpp20_led_sw_ctrl_led_info {
uint8_t index;
uint8_t type;
uint8_t physical_count;
uint16_t caps;
uint8_t nvconfig_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_led_sw_ctrl_led_info) == 6, "Invalid size");
struct hidpp20_led_sw_ctrl_led_state {
uint8_t index;
uint16_t mode;
union {
struct {
uint16_t brightness;
uint16_t period;
uint16_t timeout;
} breathing;
struct {
uint16_t unused;
uint16_t delay;
} traveling;
struct {
uint16_t index;
uint16_t on_time;
uint16_t off_time;
} blink;
struct {
/* Logical information to display on the LED
* Meaning and value range depend on the LED
*/
uint16_t index;
} on;
};
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_led_sw_ctrl_led_state) == 9, "Invalid size");
int hidpp20_led_sw_control_read_leds(struct hidpp20_device* device,
struct hidpp20_led_sw_ctrl_led_info** info_list);
const char*
hidpp20_sw_led_control_get_mode_string(const enum hidpp20_led_sw_ctrl_led_mode mode);
int hidpp20_led_sw_control_read_leds(struct hidpp20_device* device,
struct hidpp20_led_sw_ctrl_led_info** info_list);
/**
* Retrieves the number of non-RGB logical leds from the mouse
* @return the led count or a negative errno
*/
int hidpp20_led_sw_control_get_led_count(struct hidpp20_device* device);
/**
* Retrieves the info for a given logical led
* @return 0 or a negative errno in case of error
*/
int hidpp20_led_sw_control_get_led_info(struct hidpp20_device* device,
uint8_t led_idx,
struct hidpp20_led_sw_ctrl_led_info* info);
/**
* Reads all LEDs on the device for their current state
* @return 0 on success or a negative errno
*/
int hidpp20_led_sw_control_read_leds(struct hidpp20_device* device,
struct hidpp20_led_sw_ctrl_led_info** info_list);
/**
* Get the current sw control value
* @return 0 if the leds are sofware-controlled 1 if they are firmware controlled
*/
bool hidpp20_led_sw_control_get_sw_ctrl(struct hidpp20_device* device);
/**
* Sets who controls the LEDs
* ctrl = 0, the software controls the LEDs
* ctrl = 1, the firmware controls the LEDs
* @return 0 on success or a negative errno
*/
int hidpp20_led_sw_control_set_sw_ctrl(struct hidpp20_device* device, bool ctrl);
/**
* Gets the state of a LED
* @return 0 on success or a negative errno
*/
int hidpp20_led_sw_control_get_led_state(struct hidpp20_device* device,
uint8_t led_idx,
struct hidpp20_led_sw_ctrl_led_state* state);
/**
* Sets the state of a LED
* The LED index should be passed in the state structure
* @return 0 on success or a negative errno
*/
int hidpp20_led_sw_control_set_led_state(struct hidpp20_device* device,
const struct hidpp20_led_sw_ctrl_led_state* state);
/* -------------------------------------------------------------------------- */
/* 0x1b00: KBD reprogrammable keys and mouse buttons */
/* -------------------------------------------------------------------------- */
@@ -251,15 +376,33 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
#define HIDPP_PAGE_ADJUSTABLE_REPORT_RATE 0x8060
/**
* set the bitmap_ms to the supported report rates. Bits enabled reflect a
* supported report rate, where bit 0 equals 1ms, bit 1 2ms, bit 2 3ms, etc.
*/
int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *device,
uint8_t *bitflags_ms);
/* -------------------------------------------------------------------------- */
/* 0x8070v4 - Color LED effects */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_COLOR_LED_EFFECTS 0x8070
enum hidpp20_color_led_info_ext_caps {
HIDPP20_COLOR_LED_INFO_EXT_CAP_HAS_ZONE_EFFECT = 0x01,
HIDPP20_COLOR_LED_INFO_EXT_CAP_NO_GET_EFFECT_SETTINGS = 0x02,
HIDPP20_COLOR_LED_INFO_EXT_CAP_HAS_SET_LED_BIN_INFO = 0x04,
HIDPP20_COLOR_LED_INFO_EXT_CAP_MONOCHROME_ONLY = 0x08,
};
struct hidpp20_color_led_info;
int
hidpp20_color_led_effects_get_info(struct hidpp20_device *device,
struct hidpp20_color_led_info *info);
struct hidpp20_color_led_zone_info;
int
@@ -270,6 +413,14 @@ int
hidpp20_color_led_effects_get_zone_infos(struct hidpp20_device *device,
struct hidpp20_color_led_zone_info **infos_list);
struct hidpp20_color_led_zone_effect_info;
int
hidpp20_color_led_effect_get_zone_effect_info(struct hidpp20_device *device,
uint8_t zone_index,
uint8_t zone_effect_index,
struct hidpp20_color_led_zone_effect_info *info);
enum hidpp20_color_led_location {
HIDPP20_COLOR_LED_LOCATION_UNDEFINED = 0,
HIDPP20_COLOR_LED_LOCATION_PRIMARY,
@@ -292,6 +443,19 @@ enum hidpp20_color_led_persistency {
HIDPP20_COLOR_LED_PERSISTENCY_ON_OFF,
};
enum hidpp20_color_led_zone_effect {
HIDPP20_COLOR_LED_ZONE_EFFECT_DISABLED = 0,
HIDPP20_COLOR_LED_ZONE_EFFECT_FIXED = 1,
HIDPP20_COLOR_LED_ZONE_EFFECT_CYCLING = 3,
HIDPP20_COLOR_LED_ZONE_EFFECT_WAVE = 4,
HIDPP20_COLOR_LED_ZONE_EFFECT_STARLIGHT = 5,
HIDPP20_COLOR_LED_ZONE_EFFECT_LIGHT_ON_PRESS = 6,
HIDPP20_COLOR_LED_ZONE_EFFECT_AUDIO_VISUALIZER = 7,
HIDPP20_COLOR_LED_ZONE_EFFECT_BOOT_UP = 8,
HIDPP20_COLOR_LED_ZONE_EFFECT_DEMO_MODE = 8,
HIDPP20_COLOR_LED_ZONE_EFFECT_BREATHING = 10,
};
/* -------------------------------------------------------------------------- */
/* 0x8100 - Onboard Profiles */
/* -------------------------------------------------------------------------- */
@@ -361,6 +525,14 @@ struct hidpp20_color_led_info {
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_info) == 5, "Invalid size");
struct hidpp20_non_color_led_zone_info {
uint8_t index;
uint8_t type;
uint16_t capabilities;
uint8_t nv_config;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_non_color_led_zone_info) == 5, "Invalid size");
struct hidpp20_color_led_zone_info {
uint8_t index;
uint16_t location;
@@ -369,6 +541,20 @@ struct hidpp20_color_led_zone_info {
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_info) == 5, "Invalid size");
union hidpp20_generic_led_zone_info {
struct hidpp20_led_sw_ctrl_led_info* leds;
struct hidpp20_color_led_zone_info* color_leds;
};
struct hidpp20_color_led_zone_effect_info {
uint8_t zone_index;
uint8_t zone_effect_index;
uint16_t effect_id;
uint16_t effect_caps;
uint16_t effect_period;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_effect_info) == 8, "Invalid size");
struct hidpp20_color {
uint8_t red;
uint8_t green;
@@ -466,7 +652,7 @@ union hidpp20_macro_data {
_Static_assert(sizeof(union hidpp20_macro_data) == 3, "Invalid size");
struct hidpp20_profile {
uint8_t index;
uint16_t address;
uint8_t enabled;
char name[16 * 3];
unsigned report_rate;
@@ -479,6 +665,21 @@ struct hidpp20_profile {
struct hidpp20_led leds[HIDPP20_LED_COUNT];
};
struct hidpp20_onboard_profiles_info {
uint8_t memory_model_id;
uint8_t profile_format_id;
uint8_t macro_format_id;
uint8_t profile_count;
uint8_t profile_count_oob;
uint8_t button_count;
uint8_t sector_count;
uint16_t sector_size;
uint8_t mechanical_layout;
uint8_t various_info;
uint8_t reserved[5];
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_onboard_profiles_info) == 16, "Invalid size");
struct hidpp20_profiles {
uint8_t num_profiles;
uint8_t num_rom_profiles;
@@ -489,9 +690,20 @@ struct hidpp20_profiles {
uint8_t has_dpi_shift;
uint8_t corded;
uint8_t wireless;
struct hidpp20_profile profiles[0];
uint8_t sector_count;
uint16_t sector_size;
struct hidpp20_profile *profiles;
};
/**
* fetches the profiles description as reported by the mouse.
*
* returns 0 or a negative error.
*/
int
hidpp20_onboard_profiles_get_profiles_desc(struct hidpp20_device *device,
struct hidpp20_onboard_profiles_info *info);
/**
* allocates a list of profiles that has to be destroyed by the caller.
* The caller must use hidpp20_onboard_profiles_destroy() to free the memory.
@@ -507,6 +719,14 @@ int hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
void
hidpp20_onboard_profiles_destroy(struct hidpp20_profiles *profiles_list);
/**
* initialize a struct hidpp20_profiles previous allocated with
* hidpp20_onboard_profiles_allocate().
*/
int
hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
struct hidpp20_profiles *profiles);
/**
* return the current profile index or a negative error.
*/
@@ -539,18 +759,11 @@ hidpp20_onboard_profiles_set_current_dpi_index(struct hidpp20_device *device,
uint8_t index);
/**
* parse a given profile from the mouse and fill in the right profile in
* profiles_list.
*
* return 0 or a negative error.
* Write the internal state of the device onto the FLASH.
*/
int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
unsigned int index,
struct hidpp20_profiles *profiles_list);
int hidpp20_onboard_profiles_write(struct hidpp20_device *device,
unsigned int index,
struct hidpp20_profiles *profiles_list);
int
hidpp20_onboard_profiles_commit(struct hidpp20_device *device,
struct hidpp20_profiles *profiles_list);
enum ratbag_button_action_special
hidpp20_onboard_profiles_get_special(uint8_t code);
@@ -559,16 +772,26 @@ uint8_t
hidpp20_onboard_profiles_get_code_from_special(enum ratbag_button_action_special special);
int
hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
uint8_t read_rom,
uint8_t page,
uint16_t section,
uint8_t result[16]);
hidpp20_onboard_profiles_read_sector(struct hidpp20_device *device,
uint16_t sector,
uint16_t sector_size,
uint8_t *data);
int
hidpp20_onboard_profiles_write_sector(struct hidpp20_device *device,
uint16_t sector,
uint16_t sector_size,
uint8_t *data,
bool write_crc);
static inline uint8_t *
hidpp20_onboard_profiles_allocate_sector(struct hidpp20_profiles *profiles)
{
return zalloc(profiles->sector_size);
}
/* -------------------------------------------------------------------------- */
/* 0x8110 - Mouse Button Spy */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_MOUSE_BUTTON_SPY 0x8110

View File

@@ -23,6 +23,8 @@
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <stdbool.h>

586
src/libratbag-data.c Normal file
View File

@@ -0,0 +1,586 @@
/*
* Copyright © 2017 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 <linux/input.h>
#include <assert.h>
#include <stdlib.h>
#include <glib.h>
#include "libratbag.h"
#include "libratbag-private.h"
#include "libratbag-data.h"
#define GROUP_DEVICE "Device"
DEFINE_TRIVIAL_CLEANUP_FUNC(GKeyFile *, g_key_file_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(GError *, g_error_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(char **, g_strfreev);
enum driver {
NONE = 0,
HIDPP10,
HIDPP20,
ROCCAT,
ETEKCITY,
GSKILL,
LOGITECH_G300,
STEELSERIES,
};
struct data_hidpp20 {
int index;
};
struct data_hidpp10 {
int index;
int profile_count;
char *profile_type;
struct dpi_list *dpi_list;
struct dpi_range *dpi_range;
int led_count;
};
struct data_steelseries {
int device_version;
int button_count;
int led_count;
struct dpi_list *dpi_list;
struct dpi_range *dpi_range;
};
struct ratbag_device_data {
int refcount;
char *name;
char *driver;
char *svg;
enum driver drivertype;
union {
struct data_hidpp20 hidpp20;
struct data_hidpp10 hidpp10;
struct data_steelseries steelseries;
};
enum ratbag_led_type led_types[20];
size_t nled_types;
};
static void
init_data_hidpp10(struct ratbag *ratbag,
GKeyFile *keyfile,
struct ratbag_device_data *data)
{
const char *group = "Driver/hidpp10";
char *profile_type;
GError *error = NULL;
_cleanup_(freep) char *str = NULL;
int num;
data->hidpp10.index = -1;
data->hidpp10.profile_count = -1;
data->hidpp10.profile_type = NULL;
data->hidpp10.led_count = -1;
num = g_key_file_get_integer(keyfile, group, "DeviceIndex", &error);
if (num != 0 || !error)
data->hidpp10.index = num;
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "Profiles", &error);
if (num > 0 || !error)
data->hidpp10.profile_count = num;
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "Leds", &error);
if (num > 0 || !error)
data->hidpp10.led_count = num;
if (error)
g_error_free(error);
profile_type = g_key_file_get_string(keyfile, group, "ProfileType", NULL);
if (profile_type)
data->hidpp10.profile_type = profile_type;
str = g_key_file_get_string(keyfile, group, "DpiRange", NULL);
if (str) {
data->hidpp10.dpi_range = dpi_range_from_string(str);
} else {
str = g_key_file_get_string(keyfile, group, "DpiList", NULL);
if (str)
data->hidpp10.dpi_list = dpi_list_from_string(str);
}
}
static void
init_data_hidpp20(struct ratbag *ratbag,
GKeyFile *keyfile,
struct ratbag_device_data *data)
{
const char *group = "Driver/hidpp20";
GError *error = NULL;
int num;
data->hidpp20.index = -1;
num = g_key_file_get_integer(keyfile, group, "DeviceIndex", &error);
if (num != 0 || !error)
data->hidpp20.index = num;
if (error)
g_error_free(error);
}
static void
init_data_steelseries(struct ratbag *ratbag,
GKeyFile *keyfile,
struct ratbag_device_data *data)
{
const char *group = "Driver/steelseries";
GError *error = NULL;
_cleanup_(freep) char *str = NULL;
int num;
data->steelseries.device_version = -1;
data->steelseries.button_count = -1;
data->steelseries.led_count = -1;
data->steelseries.dpi_list = NULL;
data->steelseries.dpi_range = NULL;
num = g_key_file_get_integer(keyfile, group, "Buttons", &error);
if (num != 0 || !error)
data->steelseries.button_count = num;
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "Leds", &error);
if (num > 0 || !error)
data->steelseries.led_count = num;
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "DeviceVersion", &error);
if (num > 0 || !error)
data->steelseries.device_version = num;
if (error)
g_error_free(error);
str = g_key_file_get_string(keyfile, group, "DpiRange", NULL);
if (str) {
data->steelseries.dpi_range = dpi_range_from_string(str);
} else {
str = g_key_file_get_string(keyfile, group, "DpiList", NULL);
if (str)
data->steelseries.dpi_list = dpi_list_from_string(str);
}
}
static const struct driver_map {
enum driver map;
const char *driver;
void (*init)(struct ratbag *ratbag,
GKeyFile *keyfile,
struct ratbag_device_data *data);
} driver_map[] = {
{ HIDPP10, "hidpp10", init_data_hidpp10 },
{ HIDPP20, "hidpp20", init_data_hidpp20 },
{ ROCCAT, "roccat", NULL },
{ ETEKCITY, "etekcity", NULL},
{ GSKILL, "gskill", NULL },
{ LOGITECH_G300, "logitech_g300", NULL},
{ STEELSERIES, "steelseries", init_data_steelseries },
};
const char *
ratbag_device_data_get_driver(const struct ratbag_device_data *data)
{
return data->driver;
}
const char *
ratbag_device_data_get_name(const struct ratbag_device_data *data)
{
return data->name;
}
const char *
ratbag_device_data_get_svg(const struct ratbag_device_data *data)
{
return data->svg;
}
enum ratbag_led_type
ratbag_device_data_get_led_type(const struct ratbag_device_data *data,
unsigned int index)
{
assert(index < ARRAY_LENGTH(data->led_types));
return data->led_types[index];
}
struct ratbag_device_data *
ratbag_device_data_ref(struct ratbag_device_data *data)
{
data->refcount++;
return data;
}
static void
ratbag_device_data_destroy(struct ratbag_device_data *data)
{
switch (data->drivertype) {
case HIDPP10:
free(data->hidpp10.profile_type);
break;
default:
break;
}
free(data->name);
free(data->driver);
free(data->svg);
free(data);
}
struct ratbag_device_data *
ratbag_device_data_unref(struct ratbag_device_data *data)
{
if (data == NULL)
return NULL;
assert(data->refcount > 0);
data->refcount--;
if (data->refcount == 0)
ratbag_device_data_destroy(data);
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbag_device_data *, ratbag_device_data_unref);
static int
parse_ledtypes(char **strv, enum ratbag_led_type *types, size_t ntypes)
{
unsigned int i;
int count = 0;
for (i = 0; i < ntypes; i++)
types[i] = RATBAG_LED_TYPE_UNKNOWN;
if (!strv)
return count;
i = 0;
while(strv[i]) {
const char *s = strv[i];
if (streq(s, "logo"))
types[i] = RATBAG_LED_TYPE_LOGO;
else if (streq(s, "side"))
types[i] = RATBAG_LED_TYPE_SIDE;
else if (streq(s, "dpi"))
types[i] = RATBAG_LED_TYPE_SIDE;
else if (streq(s, "battery"))
types[i] = RATBAG_LED_TYPE_SIDE;
else if (streq(s, "wheel"))
types[i] = RATBAG_LED_TYPE_WHEEL;
else
return -1;
count++;
i++;
}
return count;
}
static bool
match(const struct input_id *id, char **strv)
{
const char *bus;
char str[64];
int i = 0;
switch (id->bustype) {
case BUS_USB: bus = "usb"; break;
case BUS_BLUETOOTH: bus = "bluetooth"; break;
default:
return false;
}
snprintf(str, sizeof(str), "%s:%04x:%04x", bus, id->vendor, id->product);
while (strv[i]) {
if (streq(strv[i], str))
return true;
i++;
}
return false;
}
static bool
file_data_matches(struct ratbag *ratbag,
const char *path, const struct input_id *id,
struct ratbag_device_data **data_out)
{
_cleanup_(g_key_file_freep) GKeyFile *keyfile = NULL;
_cleanup_(g_error_freep) GError *error = NULL;
_cleanup_(g_strfreevp) char **match_strv = NULL;
_cleanup_(g_strfreevp) char **ledtypes_strv = NULL;
_cleanup_(ratbag_device_data_unrefp) struct ratbag_device_data *data = NULL;
int rc;
keyfile = g_key_file_new();
rc = g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, &error);
if (!rc) {
log_error(ratbag, "Failed to parse keyfile %s: %s\n", path, error->message);
return false;
}
match_strv = g_key_file_get_string_list(keyfile, GROUP_DEVICE, "DeviceMatch", NULL, NULL);
if (!match_strv) {
log_error(ratbag, "Missing DeviceMatch in %s\n", basename(path));
return false;
}
if (!match(id, match_strv))
return false;
data = zalloc(sizeof(*data));
data->refcount = 1;
data->name = g_key_file_get_string(keyfile, GROUP_DEVICE, "Name", NULL);
if (!data->name) {
return false;
}
data->driver = g_key_file_get_string(keyfile, GROUP_DEVICE, "Driver", NULL);
if (!data->driver) {
log_error(ratbag, "Missing Driver in %s\n", basename(path));
return false;
} else {
const struct driver_map *map;
data->drivertype = NONE;
ARRAY_FOR_EACH(driver_map, map) {
if (streq(map->driver, data->driver)) {
data->drivertype = map->map;
if (map->init)
map->init(ratbag, keyfile, data);
break;
}
}
if (data->drivertype == NONE) {
log_error(ratbag, "Unknown driver %s in %s\n",
data->driver, basename(path));
return false;
}
}
data->svg = g_key_file_get_string(keyfile, GROUP_DEVICE, "Svg", NULL);
ledtypes_strv = g_key_file_get_string_list(keyfile, GROUP_DEVICE, "LedTypes", NULL, NULL);
if (parse_ledtypes(ledtypes_strv, data->led_types, ARRAY_LENGTH(data->led_types)) < 0) {
log_error(ratbag, "Invalid LedTypes string in '%s'\n", basename(path));
return false;
}
*data_out = data;
data = NULL;
return true;
}
static int
filter_device_files(const struct dirent *entry)
{
const char *SUFFIX = ".device";
const char *name = entry->d_name;
int len, slen;
if (!name || name[0] == '.')
return 0;
len = strlen(name);
slen = strlen(SUFFIX);
if (len <= slen)
return 0;
return streq(&name[len - slen], SUFFIX);
}
struct ratbag_device_data *
ratbag_device_data_new_for_id(struct ratbag *ratbag, const struct input_id *id)
{
struct ratbag_device_data *data = NULL;
struct dirent **files;
int n, nfiles;
const char *datadir;
char devicedir[PATH_MAX] = {0};
datadir = getenv("LIBRATBAG_DATA_DIR");
if (!datadir)
datadir = LIBRATBAG_DATA_DIR;
snprintf(devicedir, sizeof(devicedir), "%s/devices", datadir);
n = scandir(devicedir, &files, filter_device_files, alphasort);
if (n <= 0) {
log_error(ratbag, "Unable to locate device files in %s: %s\n",
devicedir, n == 0 ? "No files found" : strerror(errno));
return NULL;
}
nfiles = n;
while(n--) {
_cleanup_(freep) char *file = NULL;
int rc;
rc = xasprintf(&file, "%s/%s", devicedir, files[n]->d_name);
if (rc == -1)
goto out;
if (file_data_matches(ratbag, file, id, &data))
goto out;
}
out:
while(nfiles--)
free(files[nfiles]);
free(files);
return data;
}
/* HID++ 1.0 */
int
ratbag_device_data_hidpp10_get_index(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP10);
return data->hidpp10.index;
}
int
ratbag_device_data_hidpp10_get_profile_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP10);
return data->hidpp10.profile_count;
}
const char *
ratbag_device_data_hidpp10_get_profile_type(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP10);
return data->hidpp10.profile_type;
}
struct dpi_list *
ratbag_device_data_hidpp10_get_dpi_list(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP10);
return data->hidpp10.dpi_list;
}
struct dpi_range *
ratbag_device_data_hidpp10_get_dpi_range(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP10);
return data->hidpp10.dpi_range;
}
int
ratbag_device_data_hidpp10_get_led_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP10);
return data->hidpp10.led_count;
}
/* HID++ 2.0 */
int
ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP20);
return data->hidpp20.index;
}
/* SteelSeries */
int
ratbag_device_data_steelseries_get_device_version(const struct ratbag_device_data *data)
{
assert(data->drivertype == STEELSERIES);
return data->steelseries.device_version;
}
int
ratbag_device_data_steelseries_get_button_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == STEELSERIES);
return data->steelseries.button_count;
}
int
ratbag_device_data_steelseries_get_led_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == STEELSERIES);
return data->steelseries.led_count;
}
struct dpi_list *
ratbag_device_data_steelseries_get_dpi_list(const struct ratbag_device_data *data)
{
assert(data->drivertype == STEELSERIES);
return data->steelseries.dpi_list;
}
struct dpi_range *
ratbag_device_data_steelseries_get_dpi_range(const struct ratbag_device_data *data)
{
assert(data->drivertype == STEELSERIES);
return data->steelseries.dpi_range;
}

110
src/libratbag-data.h Normal file
View File

@@ -0,0 +1,110 @@
/*
* Copyright © 2017 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#pragma once
struct ratbag_device_data;
struct ratbag_device_data *
ratbag_device_data_new_for_id(struct ratbag *ratbag, const struct input_id *id);
struct ratbag_device_data *
ratbag_device_data_unref(struct ratbag_device_data *data);
struct ratbag_device_data *
ratbag_device_data_ref(struct ratbag_device_data *data);
const char *
ratbag_device_data_get_driver(const struct ratbag_device_data *data);
const char *
ratbag_device_data_get_name(const struct ratbag_device_data *data);
const char *
ratbag_device_data_get_svg(const struct ratbag_device_data *data);
enum ratbag_led_type
ratbag_device_data_get_led_type(const struct ratbag_device_data *data,
unsigned int index);
/* HID++ 1.0 */
/**
* @return The device index or -1 if not set
*/
int
ratbag_device_data_hidpp10_get_index(const struct ratbag_device_data *data);
const char *
ratbag_device_data_hidpp10_get_profile_type(const struct ratbag_device_data *data);
/**
* @return The profile count index or -1 if not set
*/
int
ratbag_device_data_hidpp10_get_profile_count(const struct ratbag_device_data *data);
struct dpi_list *
ratbag_device_data_hidpp10_get_dpi_list(const struct ratbag_device_data *data);
struct dpi_range *
ratbag_device_data_hidpp10_get_dpi_range(const struct ratbag_device_data *data);
/**
* @return The led count index or -1 if not set
*/
int
ratbag_device_data_hidpp10_get_led_count(const struct ratbag_device_data *data);
/* HID++ 2.0 */
/**
* @return The device index or -1 if not set
*/
int
ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data);
/* SteelSeries */
/**
* @return The device version or -1 if not set
*/
int
ratbag_device_data_steelseries_get_device_version(const struct ratbag_device_data *data);
/**
* @return The button count or -1 if not set
*/
int
ratbag_device_data_steelseries_get_button_count(const struct ratbag_device_data *data);
/**
* @return The led count or -1 if not set
*/
int
ratbag_device_data_steelseries_get_led_count(const struct ratbag_device_data *data);
struct dpi_list *
ratbag_device_data_steelseries_get_dpi_list(const struct ratbag_device_data *data);
struct dpi_range *
ratbag_device_data_steelseries_get_dpi_range(const struct ratbag_device_data *data);

390
src/libratbag-enums.h Normal file
View File

@@ -0,0 +1,390 @@
/*
* Copyright © 2017 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#pragma once
/**
* This file contains enum values that are used in the DBus API and thus
* considered ABI.
*/
/**
* @defgroup enums Enumerations used in the DBus API
*/
/**
* @ingroup enums
*
* Error codes used by libratbag.
*/
enum ratbag_error_code {
RATBAG_SUCCESS = 0,
/**
* An error occured on the device. Either the device is not a
* libratbag device or communication with the device failed.
*/
RATBAG_ERROR_DEVICE = -1000,
/**
* Insufficient capabilities. This error occurs when a requested change is
* beyond the device's capabilities.
*/
RATBAG_ERROR_CAPABILITY = -1001,
/**
* Invalid value or value range. The provided value or value range
* is outside of the legal or supported range.
*/
RATBAG_ERROR_VALUE = -1002,
/**
* A low-level system error has occured, e.g. a failure to access
* files that should be there. This error is usually unrecoverable
* and libratbag will print a log message with details about the
* error.
*/
RATBAG_ERROR_SYSTEM = -1003,
/**
* Implementation bug, either in libratbag or in the caller. This
* error is usually unrecoverable and libratbag will print a log
* message with details about the
* error.
*/
RATBAG_ERROR_IMPLEMENTATION = -1004,
};
/**
* @ingroup enums
*/
enum ratbag_device_capability {
RATBAG_DEVICE_CAP_NONE = 0,
/**
* The device has the capability to query the current hardware
* configuration. If this capability is missing, libratbag cannot
* query the device for its current configuration and the
* configured resolutions and button mappings are unknown.
* libratbag will still provide information about the structure of
* the device such as the number of buttons and resolutions.
* Clients that encounter a device without this resolution are
* encouraged to upload a configuration stored on-disk to the
* device to reset the device to a known state.
*
* Any changes uploaded to the device will be cached in libratbag,
* once a client has sent a full configuration to the device
* libratbag can be used to query the device as normal.
*/
RATBAG_DEVICE_CAP_QUERY_CONFIGURATION,
/**
* The device provides read and/or write access to one or more
* resolutions.
*/
RATBAG_DEVICE_CAP_RESOLUTION = 100,
/**
* The device can change resolution, either software-controlled or
* by a hardware button.
*
* FIXME: what about devices that only have hw buttons? can we
* query that, even if we can't switch it ourselves? Maybe better to
* have a separate cap for that then.
*/
RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION,
/**
* The device provides read and/or write access to one or more
* profiles.
*/
RATBAG_DEVICE_CAP_PROFILE = 200,
/**
* The device can switch between hardware profiles.
* A device with this capability can store multiple profiles in the
* hardware and provides the ability to switch between the profiles,
* possibly with a button.
* Devices without this capability will only have a single profile.
*/
RATBAG_DEVICE_CAP_SWITCHABLE_PROFILE,
/**
* 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,
/**
* The device can have one profile assigned as a default profile.
* A default profile is the profile that is selected when the device
* is plugged in. Devices without this capability may select the
* last-used profile or a specific profile (usually the first).
*/
RATBAG_DEVICE_CAP_DEFAULT_PROFILE,
/**
* The device provides read and/or write access to one or more
* buttons.
*/
RATBAG_DEVICE_CAP_BUTTON = 300,
/**
* The device supports assigning button numbers, key events or key +
* modifier combinations.
*/
RATBAG_DEVICE_CAP_BUTTON_KEY,
/**
* The device supports user-defined key or button sequences.
*/
RATBAG_DEVICE_CAP_BUTTON_MACROS,
/**
* The device supports assigning LED colors and effects
*/
RATBAG_DEVICE_CAP_LED = 400,
};
/**
* @ingroup enums
*/
enum ratbag_profile_capability {
RATBAG_PROFILE_CAP_NONE = 0,
/**
* The device has the capability to write a name to a profile
* and store it in its flash.
*
* Any changes uploaded to the device will be cached in libratbag
* and will be committed to it after a commit only.
*/
RATBAG_PROFILE_CAP_WRITABLE_NAME = 100,
};
/**
* @ingroup enums
*/
enum ratbag_resolution_capability {
/**
* The report rate can be set per resolution mode. If this property
* is not available, all resolutions within the same profile have
* the same report rate and changing one changes the others.
*/
RATBAG_RESOLUTION_CAP_INDIVIDUAL_REPORT_RATE = 1,
/**
* The resolution can be set for x and y separately.
*/
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION,
};
/**
* @ingroup enums
*
* Button types describing the physical button.
*/
enum ratbag_button_type {
RATBAG_BUTTON_TYPE_UNKNOWN = 0,
/* mouse buttons */
RATBAG_BUTTON_TYPE_LEFT,
RATBAG_BUTTON_TYPE_MIDDLE,
RATBAG_BUTTON_TYPE_RIGHT,
RATBAG_BUTTON_TYPE_THUMB,
RATBAG_BUTTON_TYPE_THUMB2,
RATBAG_BUTTON_TYPE_THUMB3,
RATBAG_BUTTON_TYPE_THUMB4,
RATBAG_BUTTON_TYPE_WHEEL_LEFT,
RATBAG_BUTTON_TYPE_WHEEL_RIGHT,
RATBAG_BUTTON_TYPE_WHEEL_CLICK,
RATBAG_BUTTON_TYPE_WHEEL_UP,
RATBAG_BUTTON_TYPE_WHEEL_DOWN,
/**
* A button to toggle the wheel from free-spinning to click-based.
*/
RATBAG_BUTTON_TYPE_WHEEL_RATCHET_MODE_SHIFT,
RATBAG_BUTTON_TYPE_EXTRA,
RATBAG_BUTTON_TYPE_SIDE,
RATBAG_BUTTON_TYPE_PINKIE,
RATBAG_BUTTON_TYPE_PINKIE2,
/* DPI switch */
RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP,
RATBAG_BUTTON_TYPE_RESOLUTION_UP,
RATBAG_BUTTON_TYPE_RESOLUTION_DOWN,
/* Profile */
RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP,
RATBAG_BUTTON_TYPE_PROFILE_UP,
RATBAG_BUTTON_TYPE_PROFILE_DOWN,
};
/**
* @ingroup enums
*
* The type assigned to a button.
*/
enum ratbag_button_action_type {
/**
* Button is disabled
*/
RATBAG_BUTTON_ACTION_TYPE_NONE = 0,
/**
* Button sends numeric button events
*/
RATBAG_BUTTON_ACTION_TYPE_BUTTON,
/**
* Button triggers a mouse-specific special function. This includes
* resolution changes and profile changes.
*/
RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
/**
* Button sends a key or key + modifier combination
*/
RATBAG_BUTTON_ACTION_TYPE_KEY,
/**
* Button sends a user-defined key or button sequence
*/
RATBAG_BUTTON_ACTION_TYPE_MACRO,
/**
* Button action is unknown
*/
RATBAG_BUTTON_ACTION_TYPE_UNKNOWN = 1000,
};
/**
* @ingroup enums
*/
enum ratbag_button_action_special {
/**
* This button is not set up for a special action
*/
RATBAG_BUTTON_ACTION_SPECIAL_INVALID = -1,
RATBAG_BUTTON_ACTION_SPECIAL_UNKNOWN = (1 << 30),
RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK,
/* Wheel mappings */
RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT,
RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT,
RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP,
RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN,
RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH,
/* 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,
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT,
/* 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,
/* second mode for buttons */
RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE,
/* battery level */
RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL,
};
/**
* @ingroup enums
*
* 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 {
/**
* led is now off
*/
RATBAG_LED_OFF = 0,
/**
* led is on with static color
*/
RATBAG_LED_ON,
/**
* led is cycling between all colors
*/
RATBAG_LED_CYCLE,
/**
* led is pulsating with static color
*/
RATBAG_LED_BREATHING,
};
/**
* @ingroup enums
*
* LED types, usually based on their physical location
*/
enum ratbag_led_type {
RATBAG_LED_TYPE_LOGO = 1,
RATBAG_LED_TYPE_SIDE,
RATBAG_LED_TYPE_BATTERY,
RATBAG_LED_TYPE_DPI,
RATBAG_LED_TYPE_WHEEL,
};
/**
* @ingroup enums
*/
enum ratbag_led_colordepth {
/**
* The device only supports a single color.
* All color components should be set to 255.
*/
RATBAG_LED_COLORDEPTH_MONOCHROME = 400,
/**
* The device supports RBG color with 8 bits per color.
*/
RATBAG_LED_COLORDEPTH_RGB_888,
/**
* The device supports RBG colors with 1 bit per color.
*/
RATBAG_LED_COLORDEPTH_RGB_111,
};
/**
* @ingroup enums
*
* Macro event types describing the event.
*/
enum ratbag_macro_event_type {
RATBAG_MACRO_EVENT_INVALID = -1,
RATBAG_MACRO_EVENT_NONE = 0,
RATBAG_MACRO_EVENT_KEY_PRESSED,
RATBAG_MACRO_EVENT_KEY_RELEASED,
RATBAG_MACRO_EVENT_WAIT,
};

View File

@@ -21,6 +21,8 @@
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <poll.h>

View File

@@ -32,6 +32,36 @@
#include "libratbag-util.h"
#include "libratbag-hidraw.h"
#define RATBAG_LED_TYPE_UNKNOWN 0
void
log_msg_va(struct ratbag *ratbag,
enum ratbag_log_priority priority,
const char *format,
va_list args)
LIBRATBAG_ATTRIBUTE_PRINTF(3, 0);
void log_msg(struct ratbag *ratbag,
enum ratbag_log_priority priority,
const char *format, ...)
LIBRATBAG_ATTRIBUTE_PRINTF(3, 4);
void
log_buffer(struct ratbag *ratbag,
enum ratbag_log_priority priority,
const char *header,
uint8_t *buf, size_t len);
#define log_raw(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_RAW, __VA_ARGS__)
#define log_debug(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_DEBUG, __VA_ARGS__)
#define log_info(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_INFO, __VA_ARGS__)
#define log_error(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_ERROR, __VA_ARGS__)
#define log_bug_kernel(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__)
#define log_bug_libratbag(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_ERROR, "libratbag bug: " __VA_ARGS__)
#define log_bug_client(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__)
#define log_buf_raw(li_, h_, buf_, len_) log_buffer(li_, RATBAG_LOG_PRIORITY_RAW, h_, buf_, len_)
#define log_buf_debug(li_, h_, buf_, len_) log_buffer(li_, RATBAG_LOG_PRIORITY_DEBUG, h_, buf_, len_)
#define log_buf_info(li_, h_, buf_, len_) log_buffer(li_, RATBAG_LOG_PRIORITY_INFO, h_, buf_, len_)
#define log_buf_error(li_, h_, buf_, len_) log_buffer(li_, RATBAG_LOG_PRIORITY_ERROR, h_, buf_, len_)
static inline void
cleanup_device(struct ratbag_device **d)
{
@@ -50,9 +80,23 @@ cleanup_resolution(struct ratbag_resolution **r)
ratbag_resolution_unref(*r);
}
static inline void
cleanup_button(struct ratbag_button **b)
{
ratbag_button_unref(*b);
}
static inline void
cleanup_led(struct ratbag_led **l)
{
ratbag_led_unref(*l);
}
#define _cleanup_device_ _cleanup_(cleanup_device)
#define _cleanup_profile_ _cleanup_(cleanup_profile)
#define _cleanup_resolution_ _cleanup_(cleanup_resolution)
#define _cleanup_button_ _cleanup_(cleanup_button)
#define _cleanup_led_ _cleanup_(cleanup_led)
#define BUS_ANY 0xffff
#define VENDOR_ANY 0xffff
@@ -90,6 +134,7 @@ struct ratbag_device {
struct input_id ids;
struct ratbag_driver *driver;
struct ratbag *ratbag;
struct ratbag_device_data *data;
unsigned long capabilities[NLONGS(MAX_CAP)];
unsigned num_profiles;
@@ -120,6 +165,10 @@ struct ratbag_driver {
*
* Return -ENODEV to ignore the device and let other drivers
* probe the device. Any other error code will stop the probing.
*
* If a matching driver is found it should initialize itself
* and synchronize all profiles with the current state on the device
* in this callback.
*/
int (*probe)(struct ratbag_device *device);
@@ -145,27 +194,16 @@ struct ratbag_driver {
int (*commit)(struct ratbag_device *device);
/**
* Callback called when the device doesn't have the udev property
* RATBAG_SVG.
* Callback called when the device doesn't have a .device file with
* an SVG name set.
*
* The returned value must not be free by the caller, the driver
* has to free it during .remove().
*/
const char *(*get_svg_name)(const struct ratbag_device *device);
/**
* Callback called when a read profile is requested by the
* caller of the library.
*
* The driver should probe here the device for the requested
* profile and populate the related information.
* There is no need to populate the various struct ratbag_button
* as they are allocated when the user needs it.
*/
void (*read_profile)(struct ratbag_profile *profile, unsigned int index);
/*
* FIXME: This function is deprecated and should not be removed. Once
* 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.
*/
@@ -179,21 +217,8 @@ struct ratbag_driver {
*/
int (*set_active_profile)(struct ratbag_device *device, unsigned int index);
/**
* For the given button, fill in the struct ratbag_button
* with the available information.
*
* For devices with profiles, profile will not be NULL. For
* device without, profile will be set to NULL.
*
* For devices with profile, there should not be the need to
* actually read the profile from the device. The caller
* should make sure that the profile is up to date.
*/
void (*read_button)(struct ratbag_button *button);
/*
* FIXME: This function is deprecated and should not be removed. Once
* 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.
*/
@@ -201,26 +226,20 @@ struct ratbag_driver {
const struct ratbag_button_action *action);
/*
* FIXME: This function is deprecated and should not be removed. Once
* 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_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,
struct ratbag_color color, unsigned int ms,
unsigned int brightness);
/* private */
@@ -233,11 +252,19 @@ struct ratbag_resolution {
struct ratbag_profile *profile;
int refcount;
void *userdata;
unsigned int dpi_max; /**< max resolution in dpi */
unsigned int dpi_min; /**< min resolution in dpi */
struct list link;
unsigned index;
unsigned int dpis[300];
size_t ndpis;
unsigned int dpi_x; /**< x resolution in dpi */
unsigned int dpi_y; /**< y resolution in dpi */
unsigned int hz; /**< report rate in Hz */
unsigned int rates[5];
size_t nrates;
bool is_active;
bool is_default;
uint32_t capabilities;
@@ -251,8 +278,10 @@ struct ratbag_led {
unsigned index;
enum ratbag_led_type type;
enum ratbag_led_mode mode;
uint32_t modes; /**< supported modes */
struct ratbag_color color;
unsigned int hz; /**< rate of action in hz */
enum ratbag_led_colordepth colordepth;
unsigned int ms; /**< duration of action in ms */
unsigned int brightness; /**< brightness of the LED */
bool dirty;
};
@@ -260,6 +289,7 @@ struct ratbag_led {
struct ratbag_profile {
int refcount;
void *userdata;
char *name;
struct list link;
unsigned index;
@@ -267,17 +297,29 @@ struct ratbag_profile {
struct list buttons;
void *drv_data;
void *user_data;
struct {
struct ratbag_resolution *modes;
unsigned int num_modes;
} resolution;
struct list resolutions;
struct list leds;
unsigned int num_resolutions;
bool is_active; /**< profile is the currently active one */
bool is_enabled;
bool dirty; /**< profile changed since last commit */
unsigned long capabilities[NLONGS(MAX_CAP)];
};
#define ratbag_device_for_each_profile(device_, profile_) \
list_for_each(profile_, &(device_)->profiles, link)
#define ratbag_profile_for_each_button(profile_, button_) \
list_for_each(button_, &(profile_)->buttons, link) \
#define ratbag_profile_for_each_led(profile_, led_) \
list_for_each(led_, &(profile_)->leds, link)
#define ratbag_profile_for_each_resolution(profile_, resolution_) \
list_for_each(resolution_, &(profile_)->resolutions, link)
#define BUTTON_ACTION_NONE \
{ .type = RATBAG_BUTTON_ACTION_TYPE_NONE }
#define BUTTON_ACTION_BUTTON(num_) \
@@ -338,6 +380,10 @@ struct ratbag_button {
bool dirty; /* changed since last commit to device */
};
void
ratbag_button_set_action(struct ratbag_button *button,
const struct ratbag_button_action *action);
static inline void
ratbag_button_enable_action_type(struct ratbag_button *button,
enum ratbag_button_action_type type)
@@ -384,6 +430,10 @@ void
ratbag_device_set_capability(struct ratbag_device *device,
enum ratbag_device_capability cap);
void
ratbag_device_unset_capability(struct ratbag_device *device,
enum ratbag_device_capability cap);
static inline void
ratbag_profile_set_drv_data(struct ratbag_profile *profile, void *drv_data)
{
@@ -396,6 +446,15 @@ ratbag_profile_get_drv_data(struct ratbag_profile *profile)
return profile->drv_data;
}
static inline void
ratbag_profile_set_cap(struct ratbag_profile *profile,
enum ratbag_profile_capability cap)
{
assert(cap <= MAX_CAP);
long_set_bit(profile->capabilities, cap);
}
static inline int
ratbag_button_action_match(const struct ratbag_button_action *action,
const struct ratbag_button_action *match)
@@ -419,30 +478,82 @@ ratbag_button_action_match(const struct ratbag_button_action *action,
return 0;
}
static inline struct ratbag_resolution *
ratbag_resolution_init(struct ratbag_profile *profile, int index,
int dpi_x, int dpi_y, int hz)
static inline void
ratbag_resolution_set_resolution(struct ratbag_resolution *res,
int dpi_x, int dpi_y, int hz)
{
struct ratbag_resolution *res = &profile->resolution.modes[index];
res->profile = profile;
res->dpi_x = dpi_x;
res->dpi_y = dpi_y;
res->hz = hz;
res->is_active = false;
res->is_default = false;
res->capabilities = 0;
res->dpi_min = 0;
res->dpi_max = 0;
return res;
}
static inline void
ratbag_resolution_set_range(struct ratbag_resolution *res, int min, int max)
ratbag_resolution_set_dpi_list_from_range(struct ratbag_resolution *res,
unsigned int min, unsigned int max)
{
res->dpi_min = min;
res->dpi_max = max;
unsigned int stepsize = 50;
unsigned int dpi = min;
bool maxed_out = false;
res->ndpis = 0;
while (res->ndpis < ARRAY_LENGTH(res->dpis)) {
if (dpi > (unsigned)max) {
maxed_out = true;
break;
}
res->dpis[res->ndpis] = dpi;
res->ndpis++;
if (dpi < 1000)
stepsize = 50;
else if (dpi < 2600)
stepsize = 100;
else if (dpi < 5000)
stepsize = 200;
else
stepsize = 500;
dpi += stepsize;
}
if (!maxed_out)
log_bug_libratbag(res->profile->device->ratbag,
"%s: resolution range exceeds available space.\n",
res->profile->device->name);
}
static inline void
ratbag_resolution_set_dpi_list(struct ratbag_resolution *res,
unsigned int *dpis,
size_t ndpis)
{
assert(ndpis <= ARRAY_LENGTH(res->dpis));
_Static_assert(sizeof(*dpis) == sizeof(*res->dpis), "Mismatching size");
for (size_t i = 0; i < ndpis; i++) {
res->dpis[i] = dpis[i];
if (i > 0)
assert(dpis[i] > dpis[i - 1]);
}
res->ndpis = ndpis;
}
static inline void
ratbag_resolution_set_report_rate_list(struct ratbag_resolution *res,
unsigned int *rates,
size_t nrates)
{
assert(nrates <= ARRAY_LENGTH(res->rates));
_Static_assert(sizeof(*rates) == sizeof(*res->rates), "Mismatching size");
for (size_t i = 0; i < nrates; i++) {
res->rates[i] = rates[i];
if (i > 0)
assert(rates[i] > rates[i - 1]);
}
res->nrates = nrates;
}
static inline void
@@ -454,33 +565,15 @@ ratbag_resolution_set_cap(struct ratbag_resolution *res,
res->capabilities = (1 << cap);
}
void
log_msg_va(struct ratbag *ratbag,
enum ratbag_log_priority priority,
const char *format,
va_list args)
LIBRATBAG_ATTRIBUTE_PRINTF(3, 0);
void log_msg(struct ratbag *ratbag,
enum ratbag_log_priority priority,
const char *format, ...)
LIBRATBAG_ATTRIBUTE_PRINTF(3, 4);
void
log_buffer(struct ratbag *ratbag,
enum ratbag_log_priority priority,
const char *header,
uint8_t *buf, size_t len);
static inline void
ratbag_led_set_mode_capability(struct ratbag_led *led,
enum ratbag_led_mode mode)
{
assert(mode <= RATBAG_LED_BREATHING);
assert(mode < sizeof(led->modes) * 8);
#define log_raw(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_RAW, __VA_ARGS__)
#define log_debug(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_DEBUG, __VA_ARGS__)
#define log_info(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_INFO, __VA_ARGS__)
#define log_error(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_ERROR, __VA_ARGS__)
#define log_bug_kernel(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__)
#define log_bug_libratbag(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_ERROR, "libratbag bug: " __VA_ARGS__)
#define log_bug_client(li_, ...) log_msg((li_), RATBAG_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__)
#define log_buf_raw(li_, h_, buf_, len_) log_buffer(li_, RATBAG_LOG_PRIORITY_RAW, h_, buf_, len_)
#define log_buf_debug(li_, h_, buf_, len_) log_buffer(li_, RATBAG_LOG_PRIORITY_DEBUG, h_, buf_, len_)
#define log_buf_info(li_, h_, buf_, len_) log_buffer(li_, RATBAG_LOG_PRIORITY_INFO, h_, buf_, len_)
#define log_buf_error(li_, h_, buf_, len_) log_buffer(li_, RATBAG_LOG_PRIORITY_ERROR, h_, buf_, len_)
led->modes |= (1 << mode);
}
/* list of all supported drivers */
struct ratbag_driver etekcity_driver;
@@ -489,6 +582,7 @@ struct ratbag_driver hidpp10_driver;
struct ratbag_driver logitech_g300_driver;
struct ratbag_driver roccat_driver;
struct ratbag_driver gskill_driver;
struct ratbag_driver steelseries_driver;
struct ratbag_device*
ratbag_device_new(struct ratbag *ratbag, struct udev_device *udev_device,

View File

@@ -24,6 +24,8 @@
#pragma once
#include <stdint.h>
#include "libratbag.h"
#define RATBAG_TEST_MAX_PROFILES 12
@@ -53,6 +55,9 @@ struct ratbag_test_resolution {
bool active;
bool dflt;
uint32_t caps;
int dpi_min, dpi_max;
unsigned int report_rates[5];
};
struct ratbag_test_color {
@@ -65,7 +70,7 @@ struct ratbag_test_led {
enum ratbag_led_type type;
enum ratbag_led_mode mode;
struct ratbag_test_color color;
unsigned int hz;
unsigned int ms;
unsigned int brightness;
};
@@ -76,6 +81,7 @@ struct ratbag_test_profile {
bool active;
bool dflt;
bool disabled;
uint32_t caps[10];
};
struct ratbag_test_device {

View File

@@ -23,6 +23,8 @@
#pragma once
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -116,6 +118,24 @@ snprintf_safe(char *buf, size_t n, const char *fmt, ...)
#define sprintf_safe(buf, fmt, ...) \
snprintf_safe(buf, ARRAY_LENGTH(buf), fmt, __VA_ARGS__)
__attribute__((format(printf, 1, 2)))
static inline void *
asprintf_safe(const char *fmt, ...)
{
va_list args;
int rc;
char *result;
va_start(args, fmt);
rc = vasprintf(&result, fmt, args);
va_end(args);
if (rc < 0)
abort();
return result;
}
static inline void
msleep(unsigned int ms)
{
@@ -169,3 +189,120 @@ ratbag_utf8_to_enc(char *buf, size_t buf_len, const char *to_enc,
ssize_t
ratbag_utf8_from_enc(char *in_buf, size_t in_len, const char *from_enc,
char **out);
__attribute__((format(printf, 2, 3)))
static inline int
xasprintf(char **strp, const char *fmt, ...)
{
int rc = 0;
va_list args;
va_start(args, fmt);
rc = vasprintf(strp, fmt, args);
va_end(args);
if ((rc == -1) && strp)
*strp = NULL;
return rc;
}
struct dpi_range {
unsigned int min;
unsigned int max;
float step;
};
/* Parse a string in the form min:max@step to a dpi_range */
static inline struct dpi_range *
dpi_range_from_string(const char *str)
{
float min, max, step;
int rc;
struct dpi_range *range;
rc = sscanf(str, "%f:%f@%f", &min, &max, &step);
if (rc != 3 || min < 0 || max <= min || step < 20)
return NULL;
range = zalloc(sizeof(*range));
range->min = min;
range->max = max;
range->step = step;
return range;
}
struct dpi_list {
int *entries;
size_t nentries;
};
static inline void
dpi_list_free(struct dpi_list *list)
{
free(list->entries);
free(list);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct dpi_list *, dpi_list_free);
/* Parse a string in the form "100;200;400"to a dpi_list */
static inline struct dpi_list *
dpi_list_from_string(const char *str)
{
struct dpi_list *list;
unsigned int i, count, index;
int nread, dpi = 0;
char c;
void *tmp;
if (!str || str[0] == '\0')
return NULL;
printf("%s: ", str);
/* first, count how many elements do we have in the table */
count = 1;
i = 0;
while (str[i] != 0) {
c = str[i++];
if (c == ';')
count++;
}
list = zalloc(sizeof *list);
list->nentries = count;
list->entries = zalloc(count * sizeof(list->entries[0]));
index = 0;
while (*str != 0 && index < count) {
if (*str == ';') {
str++;
continue;
}
nread = 0;
sscanf(str, "%d%n", &dpi, &nread);
if (!nread || dpi < 0)
goto err;
list->entries[index] = dpi;
str += nread;
index++;
}
if (index == 0)
goto err;
/* Drop empty entries for trailing semicolons */
list->nentries = index;
tmp = realloc(list->entries, index * sizeof(list->entries[0]));
if (tmp)
list->entries = tmp;
return list;
err:
dpi_list_free(list);
return NULL;
}

View File

@@ -37,6 +37,7 @@
#include "usb-ids.h"
#include "libratbag-private.h"
#include "libratbag-util.h"
#include "libratbag-data.h"
static enum ratbag_error_code
error_code(enum ratbag_error_code code)
@@ -61,6 +62,8 @@ static void
ratbag_button_destroy(struct ratbag_button *button);
static void
ratbag_led_destroy(struct ratbag_led *led);
static void
ratbag_resolution_destroy(struct ratbag_resolution *resolution);
static void
ratbag_default_log_func(struct ratbag *ratbag,
@@ -180,6 +183,7 @@ ratbag_device_new(struct ratbag *ratbag, struct udev_device *udev_device,
device->refcount = 1;
device->udev_device = udev_device_ref(udev_device);
device->ids = *id;
device->data = ratbag_device_data_new_for_id(ratbag, id);
list_init(&device->profiles);
list_insert(&ratbag->devices, &device->link);
@@ -217,6 +221,7 @@ ratbag_device_destroy(struct ratbag_device *device)
list_remove(&device->link);
ratbag_unref(device->ratbag);
ratbag_device_data_unref(device->data);
free(device->name);
free(device);
}
@@ -225,11 +230,10 @@ static inline bool
ratbag_sanity_check_device(struct ratbag_device *device)
{
struct ratbag *ratbag = device->ratbag;
_cleanup_profile_ struct ratbag_profile *profile = NULL;
struct ratbag_profile *profile = NULL;
bool has_active = false;
unsigned int nres, nprofiles;
unsigned int nres;
bool rc = false;
unsigned int i;
/* arbitrary number: max 16 profiles, does any mouse have more? but
* since we have num_profiles unsigned, it also checks for
@@ -242,11 +246,8 @@ ratbag_sanity_check_device(struct ratbag_device *device)
goto out;
}
nprofiles = ratbag_device_get_num_profiles(device);
for (i = 0; i < nprofiles; i++) {
profile = ratbag_device_get_profile(device, i);
if (!profile)
goto out;
ratbag_device_for_each_profile(device, profile) {
struct ratbag_resolution *resolution;
/* Allow max 1 active profile */
if (profile->is_active) {
@@ -268,8 +269,29 @@ ratbag_sanity_check_device(struct ratbag_device *device)
goto out;
}
ratbag_profile_unref(profile);
profile = NULL;
ratbag_profile_for_each_resolution(profile, resolution) {
unsigned int vals[300];
unsigned int nvals = ARRAY_LENGTH(vals);
if (!ratbag_device_has_capability(device, RATBAG_DEVICE_CAP_RESOLUTION))
break;
nvals = ratbag_resolution_get_dpi_list(resolution, vals, nvals);
if (nvals == 0) {
log_bug_libratbag(ratbag,
"%s: invalid dpi list\n",
device->name);
goto out;
}
nvals = ratbag_resolution_get_report_rate_list(resolution, vals, nvals);
if (nvals == 0) {
log_bug_libratbag(ratbag,
"%s: invalid report rate list\n",
device->name);
goto out;
}
}
}
/* Require 1 active profile */
@@ -280,6 +302,29 @@ ratbag_sanity_check_device(struct ratbag_device *device)
goto out;
}
/* Require LED to be the same type in each profile */
ratbag_device_for_each_profile(device, profile) {
_cleanup_profile_ struct ratbag_profile *p0 = ratbag_device_get_profile(device, 0);
struct ratbag_led *led;
ratbag_profile_for_each_led(profile, led) {
_cleanup_led_ struct ratbag_led *p0_led = ratbag_profile_get_led(p0, led->index);
assert(led->type != RATBAG_LED_TYPE_UNKNOWN);
if (p0_led->type != led->type) {
log_bug_libratbag(ratbag,
"%s: mismatching LED types (profile %d led %d)\n",
device->name,
profile->index,
led->type);
goto out;
}
}
}
rc = true;
out:
@@ -358,7 +403,7 @@ ratbag_assign_driver(struct ratbag_device *device,
const char *driver_name;
if (!test_device) {
driver_name = udev_prop_value(device->udev_device, "RATBAG_DRIVER");
driver_name = ratbag_device_data_get_driver(device->data);
} else {
driver_name = "test_driver";
}
@@ -433,7 +478,7 @@ ratbag_device_new_from_udev_device(struct ratbag *ratbag,
goto out_err;
device = ratbag_device_new(ratbag, udev_device, name, &id);
if (!device)
if (!device || !device->data)
goto out_err;
if (!ratbag_assign_driver(device, &device->ids, NULL))
@@ -483,22 +528,16 @@ ratbag_device_get_name(const struct ratbag_device* device)
LIBRATBAG_EXPORT const char *
ratbag_device_get_svg_name(const struct ratbag_device* device)
{
const char *name;
const char *name = NULL;
name = udev_prop_value(device->udev_device, "RATBAG_SVG");
if (device->data)
name = ratbag_device_data_get_svg(device->data);
if (!name && device->driver->get_svg_name)
name = device->driver->get_svg_name(device);
return name;
}
const char *
ratbag_device_get_udev_property(const struct ratbag_device* device,
const char *name)
{
return udev_prop_value(device->udev_device, name);
}
void
ratbag_register_driver(struct ratbag *ratbag, struct ratbag_driver *driver)
{
@@ -546,6 +585,7 @@ ratbag_create_context(const struct ratbag_interface *interface,
ratbag_register_driver(ratbag, &logitech_g300_driver);
ratbag_register_driver(ratbag, &roccat_driver);
ratbag_register_driver(ratbag, &gskill_driver);
ratbag_register_driver(ratbag, &steelseries_driver);
return ratbag;
}
@@ -576,19 +616,16 @@ ratbag_unref(struct ratbag *ratbag)
static struct ratbag_button *
ratbag_create_button(struct ratbag_profile *profile, unsigned int index)
{
struct ratbag_device *device = profile->device;
struct ratbag_button *button;
button = zalloc(sizeof(*button));
button->refcount = 0;
button->profile = profile;
button->index = index;
button->type = RATBAG_BUTTON_TYPE_UNKNOWN;
list_insert(&profile->buttons, &button->link);
if (device->driver->read_button)
device->driver->read_button(button);
return button;
}
@@ -602,41 +639,42 @@ ratbag_create_led(struct ratbag_profile *profile, unsigned int index)
led->refcount = 0;
led->profile = profile;
led->index = index;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
led->type = RATBAG_LED_TYPE_UNKNOWN;
if (device->data)
led->type = ratbag_device_data_get_led_type(device->data, led->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)
LIBRATBAG_EXPORT int
ratbag_profile_has_capability(const struct ratbag_profile *profile,
enum ratbag_profile_capability cap)
{
unsigned int i;
if (cap == RATBAG_PROFILE_CAP_NONE || cap >= MAX_CAP)
abort();
for (i = 0; i < count; i++)
ratbag_create_button(profile, i);
profile->device->num_buttons = count;
return 0;
return long_bit_is_set(profile->capabilities, cap);
}
static int
ratbag_profile_init_leds(struct ratbag_profile *profile, unsigned int count)
static inline void
ratbag_create_resolution(struct ratbag_profile *profile, int index)
{
unsigned int i;
struct ratbag_resolution *res;
for (i = 0; i < count; i++)
ratbag_create_led(profile, i);
res = zalloc(sizeof(*res));
res->refcount = 0;
res->profile = profile;
res->index = index;
profile->device->num_leds = count;
list_insert(&profile->resolutions, &res->link);
return 0;
profile->num_resolutions++;
}
static struct ratbag_profile *
ratbag_create_profile(struct ratbag_device *device,
unsigned int index,
@@ -651,24 +689,26 @@ ratbag_create_profile(struct ratbag_device *device,
profile->refcount = 0;
profile->device = device;
profile->index = index;
if (num_resolutions)
profile->resolution.modes = zalloc(num_resolutions *
sizeof(*profile->resolution.modes));
profile->resolution.num_modes = num_resolutions;
profile->num_resolutions = 0;
profile->is_enabled = true;
profile->name = asprintf_safe("Profile %d", index + 1);
list_insert(&device->profiles, &profile->link);
list_init(&profile->buttons);
list_init(&profile->leds);
list_init(&profile->resolutions);
profile->device->num_buttons = num_buttons;
profile->device->num_leds = num_leds;
for (i = 0; i < num_resolutions; i++)
ratbag_resolution_init(profile, i, 0, 0, 0);
ratbag_create_resolution(profile, i);
assert(device->driver->read_profile);
device->driver->read_profile(profile, index);
for (i = 0; i < num_buttons; i++)
ratbag_create_button(profile, i);
ratbag_profile_init_buttons(profile, num_buttons);
ratbag_profile_init_leds(profile, num_leds);
for (i = 0; i < num_leds; i++)
ratbag_create_led(profile, i);
return profile;
}
@@ -703,7 +743,7 @@ ratbag_device_init_profiles(struct ratbag_device *device,
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION);
}
if (num_leds > 1)
if (num_leds > 0)
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_LED);
return 0;
@@ -724,6 +764,7 @@ ratbag_profile_destroy(struct ratbag_profile *profile)
{
struct ratbag_button *button, *b_next;
struct ratbag_led *led, *l_next;
struct ratbag_resolution *res, *r_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
@@ -734,7 +775,10 @@ ratbag_profile_destroy(struct ratbag_profile *profile)
list_for_each_safe(led, l_next, &profile->leds, link)
ratbag_led_destroy(led);
free(profile->resolution.modes);
list_for_each_safe(res, r_next, &profile->resolutions, link)
ratbag_resolution_destroy(res);
free(profile->name);
list_remove(&profile->link);
free(profile);
@@ -838,6 +882,16 @@ ratbag_device_set_capability(struct ratbag_device *device,
long_set_bit(device->capabilities, cap);
}
void
ratbag_device_unset_capability(struct ratbag_device *device,
enum ratbag_device_capability cap)
{
if (cap == RATBAG_DEVICE_CAP_NONE || cap >= MAX_CAP)
abort();
long_clear_bit(device->capabilities, cap);
}
LIBRATBAG_EXPORT int
ratbag_device_has_capability(const struct ratbag_device *device,
enum ratbag_device_capability cap)
@@ -851,7 +905,7 @@ ratbag_device_has_capability(const struct ratbag_device *device,
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);
return device->driver->write_led(led, led->mode, led->color, led->ms, led->brightness);
}
/* FIXME: This is a temporary fix for all of the drivers that have yet to be
@@ -866,7 +920,6 @@ ratbag_old_write_profile(struct ratbag_device *device)
struct ratbag_led *led;
struct ratbag_resolution *resolution;
int rc;
unsigned int i;
assert(device->driver->write_profile);
@@ -879,9 +932,7 @@ ratbag_old_write_profile(struct ratbag_device *device)
return RATBAG_ERROR_DEVICE;
if (device->driver->write_resolution_dpi) {
for (i = 0; i < profile->resolution.num_modes; i++) {
resolution = &profile->resolution.modes[i];
ratbag_profile_for_each_resolution(profile, resolution) {
rc = device->driver->write_resolution_dpi(
resolution, resolution->dpi_x,
resolution->dpi_y);
@@ -972,12 +1023,13 @@ ratbag_profile_set_active(struct ratbag_profile *profile)
LIBRATBAG_EXPORT unsigned int
ratbag_profile_get_num_resolutions(struct ratbag_profile *profile)
{
return profile->resolution.num_modes;
return profile->num_resolutions;
}
LIBRATBAG_EXPORT struct ratbag_resolution *
ratbag_profile_get_resolution(struct ratbag_profile *profile, unsigned int idx)
{
struct ratbag_device *device = profile->device;
struct ratbag_resolution *res;
unsigned max = ratbag_profile_get_num_resolutions(profile);
@@ -987,9 +1039,15 @@ ratbag_profile_get_resolution(struct ratbag_profile *profile, unsigned int idx)
return NULL;
}
res = &profile->resolution.modes[idx];
ratbag_profile_for_each_resolution(profile, res) {
if (res->index == idx)
return ratbag_resolution_ref(res);
}
return ratbag_resolution_ref(res);
log_bug_libratbag(device->ratbag, "Resolution %d, profile %d not found\n",
idx, profile->index);
return NULL;
}
LIBRATBAG_EXPORT struct ratbag_resolution *
@@ -1080,12 +1138,11 @@ ratbag_resolution_set_report_rate(struct ratbag_resolution *resolution,
}
} else {
struct ratbag_profile *profile = resolution->profile;
unsigned int i;
struct ratbag_resolution *res;
/* 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];
ratbag_profile_for_each_resolution(profile, res) {
if (res->hz != hz) {
res->hz = hz;
profile->dirty = true;
@@ -1102,16 +1159,22 @@ ratbag_resolution_get_dpi(struct ratbag_resolution *resolution)
return resolution->dpi_x;
}
LIBRATBAG_EXPORT int
ratbag_resolution_get_dpi_maximum(struct ratbag_resolution *resolution)
LIBRATBAG_EXPORT size_t
ratbag_resolution_get_dpi_list(struct ratbag_resolution *resolution,
unsigned int *resolutions,
size_t nres)
{
return resolution->dpi_max;
}
_Static_assert(sizeof(*resolutions) == sizeof(*resolution->dpis), "type mismatch");
LIBRATBAG_EXPORT int
ratbag_resolution_get_dpi_minimum(struct ratbag_resolution *resolution)
{
return resolution->dpi_min;
assert(nres > 0);
if (resolution->ndpis == 0)
return 0;
memcpy(resolutions, resolution->dpis,
sizeof(unsigned int) * min(nres, resolution->ndpis));
return resolution->ndpis;
}
LIBRATBAG_EXPORT int
@@ -1132,6 +1195,24 @@ ratbag_resolution_get_report_rate(struct ratbag_resolution *resolution)
return resolution->hz;
}
LIBRATBAG_EXPORT size_t
ratbag_resolution_get_report_rate_list(struct ratbag_resolution *resolution,
unsigned int *rates,
size_t nrates)
{
_Static_assert(sizeof(*rates) == sizeof(*resolution->rates), "type mismatch");
assert(nrates > 0);
if (resolution->nrates == 0)
return 0;
memcpy(rates, resolution->rates,
sizeof(unsigned int) * min(nrates, resolution->nrates));
return resolution->nrates;
}
LIBRATBAG_EXPORT int
ratbag_resolution_is_active(const struct ratbag_resolution *resolution)
{
@@ -1142,10 +1223,10 @@ LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_resolution_set_active(struct ratbag_resolution *resolution)
{
struct ratbag_profile *profile = resolution->profile;
unsigned int i;
struct ratbag_resolution *res;
for (i = 0; i < profile->resolution.num_modes; i++)
profile->resolution.modes[i].is_active = false;
ratbag_profile_for_each_resolution(profile, res)
res->is_active = false;
resolution->is_active = true;
profile->dirty = true;
@@ -1163,12 +1244,10 @@ LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_resolution_set_default(struct ratbag_resolution *resolution)
{
struct ratbag_profile *profile = resolution->profile;
unsigned int i;
struct ratbag_resolution *other;
/* Unset the default on the other resolutions */
for (i = 0; i < profile->resolution.num_modes; i++) {
struct ratbag_resolution *other = &profile->resolution.modes[i];
ratbag_profile_for_each_resolution(profile, other) {
if (other == resolution || !other->is_default)
continue;
@@ -1245,6 +1324,17 @@ ratbag_button_get_button(struct ratbag_button *button)
return button->action.action.button;
}
void
ratbag_button_set_action(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
struct ratbag_macro *macro = button->action.macro;
button->action = *action;
if (action->type != RATBAG_BUTTON_ACTION_TYPE_MACRO)
button->action.macro = macro;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_button_set_button(struct ratbag_button *button, unsigned int btn)
{
@@ -1257,7 +1347,7 @@ ratbag_button_set_button(struct ratbag_button *button, unsigned int btn)
action.type = RATBAG_BUTTON_ACTION_TYPE_BUTTON;
action.action.button = btn;
button->action = action;
ratbag_button_set_action(button, &action);
button->dirty = true;
button->profile->dirty = true;
@@ -1288,7 +1378,7 @@ ratbag_button_set_special(struct ratbag_button *button,
action.type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL;
action.action.special = act;
button->action = action;
ratbag_button_set_action(button, &action);
button->dirty = true;
button->profile->dirty = true;
@@ -1327,7 +1417,7 @@ ratbag_button_set_key(struct ratbag_button *button,
action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
action.action.key.key = key;
button->action = action;
ratbag_button_set_action(button, &action);
button->dirty = true;
button->profile->dirty = true;
@@ -1345,7 +1435,7 @@ ratbag_button_disable(struct ratbag_button *button)
action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
button->action = action;
ratbag_button_set_action(button, &action);
button->dirty = true;
button->profile->dirty = true;
@@ -1377,8 +1467,8 @@ ratbag_button_destroy(struct ratbag_button *button)
{
list_remove(&button->link);
if (button->action.macro) {
if (button->action.macro->name)
free(button->action.macro->name);
free(button->action.macro->name);
free(button->action.macro->group);
free(button->action.macro);
}
free(button);
@@ -1391,6 +1481,13 @@ ratbag_led_destroy(struct ratbag_led *led)
free(led);
}
static void
ratbag_resolution_destroy(struct ratbag_resolution *res)
{
list_remove(&res->link);
free(res);
}
LIBRATBAG_EXPORT struct ratbag_button *
ratbag_button_unref(struct ratbag_button *button)
{
@@ -1431,6 +1528,18 @@ ratbag_led_get_type(struct ratbag_led *led)
return led->type;
}
LIBRATBAG_EXPORT int
ratbag_led_has_mode(struct ratbag_led *led,
enum ratbag_led_mode mode)
{
assert(mode <= RATBAG_LED_BREATHING);
if (mode == RATBAG_LED_OFF)
return 1;
return (led->modes & (1 << mode));
}
LIBRATBAG_EXPORT struct ratbag_color
ratbag_led_get_color(struct ratbag_led *led)
{
@@ -1438,9 +1547,9 @@ ratbag_led_get_color(struct ratbag_led *led)
}
LIBRATBAG_EXPORT int
ratbag_led_get_effect_rate(struct ratbag_led *led)
ratbag_led_get_effect_duration(struct ratbag_led *led)
{
return led->hz;
return led->ms;
}
LIBRATBAG_EXPORT unsigned int
@@ -1476,14 +1585,24 @@ ratbag_led_set_color(struct ratbag_led *led, struct ratbag_color color)
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_led_set_effect_rate(struct ratbag_led *led, unsigned int hz)
LIBRATBAG_EXPORT enum ratbag_led_colordepth
ratbag_led_get_colordepth(struct ratbag_led *led)
{
if (!ratbag_device_has_capability(led->profile->device,
RATBAG_DEVICE_CAP_LED))
return RATBAG_ERROR_CAPABILITY;
led->hz = hz;
return led->colordepth;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_led_set_effect_duration(struct ratbag_led *led, unsigned int ms)
{
if (!ratbag_device_has_capability(led->profile->device,
RATBAG_DEVICE_CAP_LED))
return RATBAG_ERROR_CAPABILITY;
led->ms = ms;
led->dirty = true;
led->profile->dirty = true;
return RATBAG_SUCCESS;
@@ -1525,6 +1644,31 @@ ratbag_profile_get_led(struct ratbag_profile *profile,
return NULL;
}
LIBRATBAG_EXPORT const char *
ratbag_profile_get_name(struct ratbag_profile *profile)
{
return profile->name;
}
LIBRATBAG_EXPORT int
ratbag_profile_set_name(struct ratbag_profile *profile,
const char *name)
{
char *name_copy;
if (!ratbag_profile_has_capability(profile,
RATBAG_PROFILE_CAP_WRITABLE_NAME))
return RATBAG_ERROR_CAPABILITY;
name_copy = strdup_safe(name);
if (profile->name)
free(profile->name);
profile->name = name_copy;
return 0;
}
LIBRATBAG_EXPORT void
ratbag_set_user_data(struct ratbag *ratbag, void *userdata)
{
@@ -1608,8 +1752,8 @@ ratbag_button_copy_macro(struct ratbag_button *button,
if (!button->action.macro)
button->action.macro = zalloc(sizeof(struct ratbag_macro));
else {
if (button->action.macro->name)
free(button->action.macro->name);
free(button->action.macro->name);
free(button->action.macro->group);
memset(button->action.macro, 0, sizeof(struct ratbag_macro));
}

View File

@@ -23,15 +23,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <libudev.h>
#include "libratbag-enums.h"
#define LIBRATBAG_ATTRIBUTE_PRINTF(_format, _args) \
__attribute__ ((format (printf, _format, _args)))
#define LIBRATBAG_ATTRIBUTE_DEPRECATED __attribute__ ((deprecated))
@@ -160,49 +158,6 @@ struct ratbag_led;
*/
struct ratbag_button_macro;
/**
* @ingroup base
*
* Error codes used by libratbag.
*/
enum ratbag_error_code {
RATBAG_SUCCESS = 0,
/**
* An error occured on the device. Either the device is not a
* libratbag device or communication with the device failed.
*/
RATBAG_ERROR_DEVICE = -1000,
/**
* Insufficient capabilities. This error occurs when a requested change is
* beyond the device's capabilities.
*/
RATBAG_ERROR_CAPABILITY = -1001,
/**
* Invalid value or value range. The provided value or value range
* is outside of the legal or supported range.
*/
RATBAG_ERROR_VALUE = -1002,
/**
* A low-level system error has occured, e.g. a failure to access
* files that should be there. This error is usually unrecoverable
* and libratbag will print a log message with details about the
* error.
*/
RATBAG_ERROR_SYSTEM = -1003,
/**
* Implementation bug, either in libratbag or in the caller. This
* error is usually unrecoverable and libratbag will print a log
* message with details about the
* error.
*/
RATBAG_ERROR_IMPLEMENTATION = -1004,
};
/**
* @ingroup base
*
@@ -485,99 +440,6 @@ ratbag_device_get_name(const struct ratbag_device* device);
const char *
ratbag_device_get_svg_name(const struct ratbag_device* device);
/**
* @ingroup device
*/
enum ratbag_device_capability {
RATBAG_DEVICE_CAP_NONE = 0,
/**
* The device has the capability to query the current hardware
* configuration. If this capability is missing, libratbag cannot
* query the device for its current configuration and the
* configured resolutions and button mappings are unknown.
* libratbag will still provide information about the structure of
* the device such as the number of buttons and resolutions.
* Clients that encounter a device without this resolution are
* encouraged to upload a configuration stored on-disk to the
* device to reset the device to a known state.
*
* Any changes uploaded to the device will be cached in libratbag,
* once a client has sent a full configuration to the device
* libratbag can be used to query the device as normal.
*/
RATBAG_DEVICE_CAP_QUERY_CONFIGURATION,
/**
* The device provides read and/or write access to one or more
* resolutions.
*/
RATBAG_DEVICE_CAP_RESOLUTION = 100,
/**
* The device can change resolution, either software-controlled or
* by a hardware button.
*
* FIXME: what about devices that only have hw buttons? can we
* query that, even if we can't switch it ourselves? Maybe better to
* have a separate cap for that then.
*/
RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION,
/**
* The device provides read and/or write access to one or more
* profiles.
*/
RATBAG_DEVICE_CAP_PROFILE = 200,
/**
* The device can switch between hardware profiles.
* A device with this capability can store multiple profiles in the
* hardware and provides the ability to switch between the profiles,
* possibly with a button.
* Devices without this capability will only have a single profile.
*/
RATBAG_DEVICE_CAP_SWITCHABLE_PROFILE,
/**
* 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,
/**
* The device can have one profile assigned as a default profile.
* A default profile is the profile that is selected when the device
* is plugged in. Devices without this capability may select the
* last-used profile or a specific profile (usually the first).
*/
RATBAG_DEVICE_CAP_DEFAULT_PROFILE,
/**
* The device provides read and/or write access to one or more
* buttons.
*/
RATBAG_DEVICE_CAP_BUTTON = 300,
/**
* The device supports assigning button numbers, key events or key +
* modifier combinations.
*/
RATBAG_DEVICE_CAP_BUTTON_KEY,
/**
* The device supports user-defined key or button sequences.
*/
RATBAG_DEVICE_CAP_BUTTON_MACROS,
/**
* The device supports assigning LED colors and effects
*/
RATBAG_DEVICE_CAP_LED = 400,
};
/**
* @ingroup device
*
@@ -671,6 +533,42 @@ ratbag_profile_ref(struct ratbag_profile *profile);
struct ratbag_profile *
ratbag_profile_unref(struct ratbag_profile *profile);
/**
* @ingroup profile
*
* Check if a profile has a specific capability.
*
* @return non-zero if the capability is available, zero otherwise.
*/
int
ratbag_profile_has_capability(const struct ratbag_profile *profile,
enum ratbag_profile_capability cap);
/**
* @ingroup profile
*
* Return the ratbag profile name.
*
* @param profile A previously initialized ratbag profile
* @return the profile name
*/
const char *
ratbag_profile_get_name(struct ratbag_profile *profile);
/**
* @ingroup profile
*
* Set the name of a ratbag profile.
*
* @param profile A previously initialized ratbag profile
* @param name the profile name
*
* @return 0 on success or an error code otherwise
*/
int
ratbag_profile_set_name(struct ratbag_profile *profile,
const char *name);
/**
* @ingroup profile
*
@@ -866,20 +764,6 @@ ratbag_resolution_set_user_data(struct ratbag_resolution *resolution, void *user
void*
ratbag_resolution_get_user_data(const struct ratbag_resolution *resolution);
enum ratbag_resolution_capability {
/**
* The report rate can be set per resolution mode. If this property
* is not available, all resolutions within the same profile have
* the same report rate and changing one changes the others.
*/
RATBAG_RESOLUTION_CAP_INDIVIDUAL_REPORT_RATE = 1,
/**
* The resolution can be set for x and y separately.
*/
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION,
};
/**
* @ingroup resolution
*
@@ -959,28 +843,26 @@ ratbag_resolution_get_dpi(struct ratbag_resolution *resolution);
/**
* @ingroup resolution
*
* Get the maximum allowed resolution in DPI for the resolution mode.
* Get the number of resolutions in DPI available for this resolution.
* The list of DPI values is sorted in ascending order but may be filtered
* by libratbag and does not necessarily reflect all resolutions supported
* by the physical device.
*
* @param resolution A previously initialized ratbag resolution
* This function writes at most nres values but returns the number of
* DPI values available on this resolution. In other words, if it returns a
* number larger than nres, call it again with an array the size of the
* return value.
*
* @return The maximum resolution in dpi
* @retval 0 The maximum resolution is unknown to libratbag
* @param[out] resolutions Set to the supported DPI values in ascending order
* @param[in] nres The number of elements in resolutions
*
* @return The number of valid items in resolutions. If the returned value
* is larger than nres, the list was truncated.
*/
int
ratbag_resolution_get_dpi_maximum(struct ratbag_resolution *resolution);
/**
* @ingroup resolution
*
* Get the minimum allowed resolution in DPI for the resolution mode.
*
* @param resolution A previously initialized ratbag resolution
*
* @return The minimum resolution in dpi
* @retval 0 The minimum resolution is unknown to libratbag
*/
int
ratbag_resolution_get_dpi_minimum(struct ratbag_resolution *resolution);
size_t
ratbag_resolution_get_dpi_list(struct ratbag_resolution *resolution,
unsigned int *resolutions,
size_t nres);
/**
* @ingroup resolution
@@ -1054,6 +936,29 @@ ratbag_resolution_set_report_rate(struct ratbag_resolution *resolution,
int
ratbag_resolution_get_report_rate(struct ratbag_resolution *resolution);
/**
* @ingroup resolution
*
* Get the number of report rates in Hz available for this resolution.
* The list of report rates is sorted in ascending order but may be filtered
* by libratbag and does not necessarily reflect all report rates supported by
* the physical device.
*
* This function writes at most nrates values but returns the number of
* report rates available on this resolution. In other words, if it returns a
* number larger than nrates, call it again with an array the size of the
* return value.
*
* @param[out] rates Set to the supported report rates in ascending order
* @param[in] nrates The number of elements in resolutions
*
* @return The number of valid items in rates. If the returned value
* is larger than nrates, the list was truncated.
*/
size_t
ratbag_resolution_get_report_rate_list(struct ratbag_resolution *resolution,
unsigned int *rates,
size_t nrates);
/**
* @ingroup resolution
*
@@ -1168,47 +1073,6 @@ ratbag_button_set_user_data(struct ratbag_button *button, void *userdata);
void*
ratbag_button_get_user_data(const struct ratbag_button *button);
/**
* @ingroup button
*
* Button types describing the physical button.
*/
enum ratbag_button_type {
RATBAG_BUTTON_TYPE_UNKNOWN = 0,
/* mouse buttons */
RATBAG_BUTTON_TYPE_LEFT,
RATBAG_BUTTON_TYPE_MIDDLE,
RATBAG_BUTTON_TYPE_RIGHT,
RATBAG_BUTTON_TYPE_THUMB,
RATBAG_BUTTON_TYPE_THUMB2,
RATBAG_BUTTON_TYPE_THUMB3,
RATBAG_BUTTON_TYPE_THUMB4,
RATBAG_BUTTON_TYPE_WHEEL_LEFT,
RATBAG_BUTTON_TYPE_WHEEL_RIGHT,
RATBAG_BUTTON_TYPE_WHEEL_CLICK,
RATBAG_BUTTON_TYPE_WHEEL_UP,
RATBAG_BUTTON_TYPE_WHEEL_DOWN,
/**
* A button to toggle the wheel from free-spinning to click-based.
*/
RATBAG_BUTTON_TYPE_WHEEL_RATCHET_MODE_SHIFT,
RATBAG_BUTTON_TYPE_EXTRA,
RATBAG_BUTTON_TYPE_SIDE,
RATBAG_BUTTON_TYPE_PINKIE,
RATBAG_BUTTON_TYPE_PINKIE2,
/* DPI switch */
RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP,
RATBAG_BUTTON_TYPE_RESOLUTION_UP,
RATBAG_BUTTON_TYPE_RESOLUTION_DOWN,
/* Profile */
RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP,
RATBAG_BUTTON_TYPE_PROFILE_UP,
RATBAG_BUTTON_TYPE_PROFILE_DOWN,
};
/**
* @ingroup button
*
@@ -1228,39 +1092,6 @@ enum ratbag_button_type {
enum ratbag_button_type
ratbag_button_get_type(struct ratbag_button *button);
/**
* @ingroup button
*
* The type assigned to a button.
*/
enum ratbag_button_action_type {
/**
* Button is disabled
*/
RATBAG_BUTTON_ACTION_TYPE_NONE = 0,
/**
* Button sends numeric button events
*/
RATBAG_BUTTON_ACTION_TYPE_BUTTON,
/**
* Button triggers a mouse-specific special function. This includes
* resolution changes and profile changes.
*/
RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
/**
* Button sends a key or key + modifier combination
*/
RATBAG_BUTTON_ACTION_TYPE_KEY,
/**
* Button sends a user-defined key or button sequence
*/
RATBAG_BUTTON_ACTION_TYPE_MACRO,
/**
* Button action is unknown
*/
RATBAG_BUTTON_ACTION_TYPE_UNKNOWN = 1000,
};
/**
* @ingroup button
*
@@ -1290,47 +1121,6 @@ int
ratbag_button_has_action_type(struct ratbag_button *button,
enum ratbag_button_action_type action_type);
/**
* @ingroup button
*/
enum ratbag_button_action_special {
/**
* This button is not set up for a special action
*/
RATBAG_BUTTON_ACTION_SPECIAL_INVALID = -1,
RATBAG_BUTTON_ACTION_SPECIAL_UNKNOWN = (1 << 30),
RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK,
/* Wheel mappings */
RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT,
RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT,
RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP,
RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN,
RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH,
/* 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,
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT,
/* 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,
/* second mode for buttons */
RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE,
/* battery level */
RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL,
};
/**
* @ingroup button
*
@@ -1385,34 +1175,6 @@ 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
*
@@ -1443,13 +1205,30 @@ ratbag_profile_get_led(struct ratbag_profile *profile, unsigned int index);
*/
enum ratbag_led_type
ratbag_led_get_type(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns true if the given mode is supported by the LED, or
* false otherwise.
*
* @param led A previously initialized ratbag LED
* @param mode The LED mode @ref ratbag_led_mode to check for
* @return True if supported, false otherwise
*
* @see ratbag_led_set_mode
*/
int
ratbag_led_has_mode(struct ratbag_led *led,
enum ratbag_led_mode mode);
/**
* @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
* @return The LED mode @ref ratbag_led_mode
*
* @see ratbag_led_set_mode
*/
@@ -1460,6 +1239,11 @@ ratbag_led_get_mode(struct ratbag_led *led);
*
* This function returns the led color.
*
* If any color scaling applies because of the device's color depth
* this is not reflected in the returned value. In other words,
* the returned value always matches the most recent value provided
* to ratbag_led_set_color().
*
* @param led A previously initialized ratbag LED
* @return The LED color in @ref ratbag_led_mode
*
@@ -1467,18 +1251,32 @@ ratbag_led_get_mode(struct ratbag_led *led);
*/
struct ratbag_color
ratbag_led_get_color(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the LED effect rate.
* This function returns the color depth of this LED.
*
* @param led A previously initialized ratbag LED
* @return The LED rate in Hz, can be 100 - 20000
* @return The bit depth of this LED
*
* @see ratbag_led_set_effect_rate
* @see ratbag_led_set_color
*/
enum ratbag_led_colordepth
ratbag_led_get_colordepth(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the LED effect duration.
*
* @param led A previously initialized ratbag LED
* @return The LED duration in ms, can be 0 - 10000
*
* @see ratbag_led_set_effect_duration
*/
int
ratbag_led_get_effect_rate(struct ratbag_led *led);
ratbag_led_get_effect_duration(struct ratbag_led *led);
/**
* @ingroup led
*
@@ -1512,6 +1310,12 @@ ratbag_led_set_mode(struct ratbag_led *led, enum ratbag_led_mode mode);
* 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.
*
* The color provided has to be within the allowed color range (see @ref
* ratbag_color). libratbag silently scales and/or clamps this range into
* the device's color depth. It is the caller's responsibility to set the
* colors in a non-ambiguous way for the device's bit depth. See @ref
* ratbag_led_colordepth for more details.
*
* @param led A previously initialized ratbag LED
* @param color A LED color.
* @return 0 on success or an error code otherwise.
@@ -1525,16 +1329,16 @@ 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
* then this function sets the LED duration in ms
*
* @param led A previously initialized ratbag LED
* @param rate Effect rate in hz, 100 - 20000
* @param rate Effect duration in ms, 0 - 10000
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_effect_rate
* @see ratbag_led_get_effect_duration
*/
enum ratbag_error_code
ratbag_led_set_effect_rate(struct ratbag_led *led, unsigned int rate);
ratbag_led_set_effect_duration(struct ratbag_led *led, unsigned int rate);
/**
* @ingroup led
@@ -1624,19 +1428,6 @@ ratbag_button_set_key(struct ratbag_button *button,
enum ratbag_error_code
ratbag_button_disable(struct ratbag_button *button);
/**
* @ingroup button
*
* Macro event types describing the event.
*/
enum ratbag_macro_event_type {
RATBAG_MACRO_EVENT_INVALID = -1,
RATBAG_MACRO_EVENT_NONE = 0,
RATBAG_MACRO_EVENT_KEY_PRESSED,
RATBAG_MACRO_EVENT_KEY_RELEASED,
RATBAG_MACRO_EVENT_WAIT,
};
/**
* @ingroup button
*
@@ -1859,6 +1650,3 @@ ratbag_led_ref(struct ratbag_led *led);
struct ratbag_led *
ratbag_led_unref(struct ratbag_led *led);
#ifdef __cplusplus
}
#endif

58
src/libratbag.i Normal file
View File

@@ -0,0 +1,58 @@
/* wrapping libratbag functions from libratbag.h using SWIG. */
%module libratbag
%{
/* the resulting C file should be built as a python extension */
#define SWIG_FILE_WITH_INIT
/* Includes the header in the wrapper code */
#include <libratbag.h>
#include <shared.h>
%}
#define __attribute__(x)
/* custom typemap for handling ratbag_resolution_get_dpi_list */
%typemap(in) (unsigned int *resolutions, size_t nres) {
unsigned int i;
if (!PyList_Check($input)) {
PyErr_SetString(PyExc_ValueError, "Expecting a list");
return NULL;
}
$2 = PyList_Size($input);
$1 = (unsigned int *) malloc(($2 + 1) * sizeof(unsigned int));
for (i = 0; i < $2; i++) {
PyObject *s = PyList_GetItem($input, i);
if (!PyInt_Check(s)) {
free($1);
PyErr_SetString(PyExc_ValueError, "List items must be integers");
return NULL;
}
$1[i] = PyInt_AsLong(s);
}
$1[i] = 0;
}
%typemap(argout) (unsigned int *resolutions, size_t nres) {
unsigned int i;
for (i = 0; i < $2; i++) {
PyList_SetItem($input, i, PyInt_FromLong($1[i]));
}
}
%typemap(freearg) (unsigned int *resolutions, size_t nres) {
if ($1)
free($1);
}
/* END OF custom typemap for handling ratbag_resolution_get_dpi_list */
/* custom typemap for handling ratbag_resolution_get_report_rate_list */
/* We re-use the ratbag_resolution_get_dpi_list typemap */
%typemap(in) (unsigned int *rates, size_t nrates) = (unsigned int *resolutions, size_t nres);
%typemap(argout) (unsigned int *rates, size_t nrates) = (unsigned int *resolutions, size_t nres);
%typemap(freearg) (unsigned int *rates, size_t nrates) = (unsigned int *resolutions, size_t nres);
/* END OF custom typemap for handling ratbag_resolution_get_report_rate_list */
/* Parse the header file to generate wrappers */
%include "libratbag.h"
%include "libratbag-enums.h"
%include "shared.h"

View File

@@ -1,95 +0,0 @@
/* in alphabetical order! */
LIBRATBAG_0.5.0 {
global:
ratbag_create_context;
ratbag_button_disable;
ratbag_button_get_action_type;
ratbag_button_get_type;
ratbag_button_get_button;
ratbag_button_get_key;
ratbag_button_get_macro;
ratbag_button_get_special;
ratbag_button_get_user_data;
ratbag_button_has_action_type;
ratbag_button_macro_get_event_key;
ratbag_button_macro_get_event_timeout;
ratbag_button_macro_get_event_type;
ratbag_button_macro_get_name;
ratbag_button_macro_get_num_events;
ratbag_button_macro_new;
ratbag_button_macro_ref;
ratbag_button_macro_set_event;
ratbag_button_macro_unref;
ratbag_button_ref;
ratbag_button_set_button;
ratbag_button_set_key;
ratbag_button_set_macro;
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;
ratbag_device_get_profile;
ratbag_device_get_svg_name;
ratbag_device_get_user_data;
ratbag_device_has_capability;
ratbag_device_new_from_udev_device;
ratbag_device_new_test_device;
ratbag_device_ref;
ratbag_device_set_user_data;
ratbag_device_unref;
ratbag_get_user_data;
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_maximum;
ratbag_resolution_get_dpi_minimum;
ratbag_resolution_get_dpi_x;
ratbag_resolution_get_dpi_y;
ratbag_resolution_get_report_rate;
ratbag_resolution_get_user_data;
ratbag_resolution_has_capability;
ratbag_resolution_is_active;
ratbag_resolution_is_default;
ratbag_resolution_ref;
ratbag_resolution_set_active;
ratbag_resolution_set_default;
ratbag_resolution_set_dpi;
ratbag_resolution_set_dpi_xy;
ratbag_resolution_set_report_rate;
ratbag_resolution_set_user_data;
ratbag_resolution_unref;
ratbag_ref;
ratbag_set_user_data;
ratbag_unref;
local:
*;
};

View File

@@ -1,22 +0,0 @@
#!/bin/bash
### 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
diff -a -u \
<(cat "$TOP_SRCDIR"/src/libratbag.sym | \
grep '^\s\+ratbag_.*' | \
sed -e 's/^\s\+\(.*\);/\1/' | sort) \
<(cat "$TOP_SRCDIR"/src/*.c | \
grep LIBRATBAG_EXPORT -A 1 | grep '^ratbag_.*' | \
sed -e 's/(.*//' | sort)

View File

@@ -165,6 +165,8 @@ int main(void)
bool using_valgrind;
const struct rlimit corelimit = { 0, 0 };
setenv("RATBAG_TEST", "1", 0);
setrlimit(RLIMIT_CORE, &corelimit);
/* when running under valgrind we're using nofork mode, so a

View File

@@ -34,6 +34,7 @@
#include <sys/resource.h>
#include "libratbag.h"
#include "libratbag-util.h"
#include "libratbag-test.h"
static void
@@ -58,10 +59,16 @@ const struct ratbag_test_device sane_device = {
.profiles = {
{
.resolutions = {
{ .xres = 100, .yres = 200, .hz = 1000 },
{ .xres = 100, .yres = 200, .hz = 1000,
.dpi_min = 100, .dpi_max = 5000,
.report_rates = { 500, 1000 } },
{ .xres = 200, .yres = 300, .hz = 1000 },
{ .xres = 300, .yres = 400, .hz = 1000 },
},
.leds = {
{ .type = RATBAG_LED_TYPE_SIDE, },
{ .type = RATBAG_LED_TYPE_LOGO, },
},
.active = true,
.dflt = false,
},
@@ -84,14 +91,14 @@ const struct ratbag_test_device sane_device = {
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.hz = 1,
.brightness = 20
.ms = 1000,
.brightness = 20,
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.hz = 3,
.brightness = 40
.ms = 333,
.brightness = 40,
}
},
.active = false,
@@ -349,7 +356,9 @@ START_TEST(device_resolutions)
.profiles = {
{
.resolutions = {
{ .xres = 100, .yres = 200, .hz = 1000 },
{ .xres = 100, .yres = 200, .hz = 1000,
.dpi_min = 50, .dpi_max = 5000,
.report_rates = { 500, 1000 } },
{ .xres = 200, .yres = 300, .hz = 1000, .active = true },
{ .xres = 300, .yres = 400, .hz = 1000 },
},
@@ -384,6 +393,9 @@ START_TEST(device_resolutions)
ck_assert_int_eq(nresolutions, 3);
for (j = 0; j < nresolutions; j++) {
unsigned int dpis[200];
int ndpis = ARRAY_LENGTH(dpis);
res = ratbag_profile_get_resolution(p, j);
xres = ratbag_resolution_get_dpi_x(res);
@@ -391,11 +403,18 @@ START_TEST(device_resolutions)
rate = ratbag_resolution_get_report_rate(res);
is_active = ratbag_resolution_is_active(res);
ndpis = ratbag_resolution_get_dpi_list(res, dpis, ndpis);
ck_assert_int_lt(ndpis, ARRAY_LENGTH(dpis));
ck_assert_int_gt(ndpis, 20);
ck_assert_int_eq(xres, i * 1000 + (j + 1) * 100);
ck_assert_int_eq(yres, i * 1000 + (j + 1) * 100 + 100);
ck_assert_int_eq(xres, ratbag_resolution_get_dpi(res));
ck_assert_int_eq(is_active, (j == 1));
ck_assert_int_eq(dpis[0], 50);
ck_assert_int_eq(dpis[ndpis - 1], 5000);
ck_assert_int_eq(rate, (i + 1) * 1000);
ratbag_resolution_unref(res);
@@ -774,18 +793,18 @@ 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;
int brightness, ms;
ck_assert(l != NULL);
mode = ratbag_led_get_mode(l);
color = ratbag_led_get_color(l);
hz = ratbag_led_get_effect_rate(l);
ms = ratbag_led_get_effect_duration(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(ms, e_l.ms);
ck_assert_int_eq(brightness, e_l.brightness);
}
@@ -811,10 +830,10 @@ START_TEST(device_leds)
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]);
led_logo = ratbag_profile_get_led(p, 0);
assert_led_equals(led_logo, td.profiles[2].leds[0]);
led_side = ratbag_profile_get_led(p, 1);
assert_led_equals(led_side, td.profiles[2].leds[1]);
ratbag_led_unref(led_logo);
ratbag_led_unref(led_side);
@@ -853,14 +872,14 @@ START_TEST(device_leds_set)
p = ratbag_device_get_profile(d, 0);
ck_assert(p != NULL);
l = ratbag_profile_get_led(p, RATBAG_LED_TYPE_LOGO);
l = ratbag_profile_get_led(p, 0);
ratbag_led_set_mode(l, RATBAG_LED_BREATHING);
ratbag_led_set_color(l, c);
ratbag_led_set_effect_rate(l, 11);
ratbag_led_set_effect_duration(l, 90);
ratbag_led_set_brightness(l, 22);
l = ratbag_profile_get_led(p, RATBAG_LED_TYPE_LOGO);
l = ratbag_profile_get_led(p, 0);
struct ratbag_test_led e_l = {
.mode = RATBAG_LED_BREATHING,
.color = {
@@ -868,7 +887,7 @@ START_TEST(device_leds_set)
.green = c.green,
.blue = c.blue
},
.hz = 11,
.ms = 90,
.brightness = 22
};
assert_led_equals(l, e_l);
@@ -935,6 +954,8 @@ int main(void)
SRunner *sr;
const struct rlimit corelimit = { 0, 0 };
setenv("RATBAG_TEST", "1", 0);
setrlimit(RLIMIT_CORE, &corelimit);
s = test_context_suite();

View File

@@ -169,6 +169,8 @@ int main(void)
SRunner *sr;
const struct rlimit corelimit = { 0, 0 };
setenv("RATBAG_TEST", "1", 0);
setrlimit(RLIMIT_CORE, &corelimit);
s = test_context_suite();

159
test/test-util.c Normal file
View File

@@ -0,0 +1,159 @@
/*
* Copyright © 2017 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 <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include "libratbag-util.h"
START_TEST(dpi_range_parser)
{
struct testcase {
const char *str;
bool success;
struct dpi_range result;
} tests[] = {
{ "", false, { 0, 0, 0 }},
{ "1", false, { 0, 0, 0 }},
{ "a", false, { 0, 0, 0 }},
{ "1:1", false, { 0, 0, 0 }},
{ "2:1", false, { 0, 0, 0 }},
{ "2:1@0", false, { 0, 0, 0 }},
{ "10:100@0", false, { 0, 0, 0 }},
{ "100:10@50", false, { 0, 0, 0 }},
{ "100:10@", false, { 0, 0, 0 }},
{ ":10@50", false, { 0, 0, 0 }},
{ "10:100@50", true, { 10, 100, 50 }},
{ "100:12000@20", true, { 100, 12000, 20 }},
{ "50:12000@250", true, { 50, 12000, 250 }},
};
struct testcase *t;
ARRAY_FOR_EACH(tests, t) {
struct dpi_range *range;
range = dpi_range_from_string(t->str);
if (!t->success) {
ck_assert(range == NULL);
continue;
}
ck_assert(range != NULL);
ck_assert_int_eq(t->result.min, range->min);
ck_assert_int_eq(t->result.max, range->max);
ck_assert_int_eq(t->result.step, range->step);
free(range);
}
}
END_TEST
START_TEST(dpi_list_parser)
{
struct testcase {
const char *str;
int nentries;
int entries[64];
} tests[] = {
{ "", -1, {0}},
{ "a", -1, {0}},
{ "a;b", -1, {0}},
{ "1;a;b", -1, {0}},
{ "100;200;b", -1, {0}},
{ "10.2;200", -1, {0}},
{ "0xab;100", -1, {0}},
{ "100", 1, { 100 }},
{ "100;200", 2, { 100, 200 }},
{ "100;200;", 2, { 100, 200 }},
{ "100;300;;;;", 2, { 100, 300 }},
{ "0;300;", 2, { 0, 300 }},
{ "0;300;400;", 3, { 0, 300, 400 }},
{ "0;300;400;500;100;23;", 6, { 0, 300, 400, 500, 100, 23 }},
};
struct testcase *t;
ARRAY_FOR_EACH(tests, t) {
struct dpi_list *list;
list = dpi_list_from_string(t->str);
if (t->nentries == -1) {
ck_assert(list == NULL);
continue;
}
ck_assert(list != NULL);
ck_assert_int_eq(t->nentries, list->nentries);
ck_assert_int_eq(memcmp(t->entries, list->entries,
t->nentries * sizeof(int)), 0);
dpi_list_free(list);
}
}
END_TEST
static Suite *
test_context_suite(void)
{
TCase *tc;
Suite *s;
s = suite_create("device");
tc = tcase_create("util");
tcase_add_test(tc, dpi_range_parser);
tcase_add_test(tc, dpi_list_parser);
suite_add_tcase(s, tc);
return s;
}
int main(void)
{
int nfailed;
Suite *s;
SRunner *sr;
const struct rlimit corelimit = { 0, 0 };
setenv("RATBAG_TEST", "1", 0);
setrlimit(RLIMIT_CORE, &corelimit);
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

@@ -7,3 +7,15 @@
...
fun:main
}
{
Python::PyObject_Malloc
Memcheck:Leak
fun:malloc
obj:/*lib*/libpython3*
}
{
Python::PyObject_Malloc
Memcheck:Leak
fun:realloc
obj:/*lib*/libpython3*
}

View File

@@ -30,32 +30,36 @@
#include <libratbag-util.h>
static inline int
dump_page(struct hidpp20_device *dev, uint8_t rom, size_t page, size_t offset)
dump_page(struct hidpp20_device *dev, uint16_t sector_size, uint8_t rom, size_t page, size_t offset)
{
int rc = 0;
uint8_t bytes[16];
uint16_t sector = (rom << 8) | page;
uint8_t *data = zalloc(sector_size);
rc = hidpp20_onboard_profiles_read_sector(dev, sector, sector_size, data);
if (rc != 0)
goto out;
while (offset < 256) {
hidpp_log_info(&dev->base, "%s: page 0x%02zx off 0x%02zx: ", rom ? "ROM " : "FLASH", page, offset);
rc = hidpp20_onboard_profiles_read_memory(dev, rom, page, offset, bytes);
if (rc != 0)
break;
hidpp_log_buffer(&dev->base, HIDPP_LOG_PRIORITY_INFO, " ", bytes, ARRAY_LENGTH(bytes));
hidpp_log_buffer(&dev->base, HIDPP_LOG_PRIORITY_INFO, " ", data + offset, 16);
offset += 16;
}
out:
free(data);
return rc;
}
static inline int
dump_all_pages(struct hidpp20_device *dev, uint8_t rom)
dump_all_pages(struct hidpp20_device *dev, uint16_t sector_size, uint8_t rom)
{
uint8_t page;
int rc = 0;
for (page = 0; page < 31; page++) {
rc = dump_page(dev, rom, page, 0);
rc = dump_page(dev, sector_size, rom, page, 0);
if (rc != 0)
break;
}
@@ -72,15 +76,15 @@ dump_all_pages(struct hidpp20_device *dev, uint8_t rom)
}
static inline int
dump_everything(struct hidpp20_device *dev)
dump_everything(struct hidpp20_device *dev, uint16_t sector_size)
{
int rc;
rc = dump_all_pages(dev, 0);
rc = dump_all_pages(dev, sector_size, 0);
if (rc)
return rc;
return dump_all_pages(dev, 1);
return dump_all_pages(dev, sector_size, 1);
}
static void
@@ -97,6 +101,7 @@ main(int argc, char **argv)
size_t page = 0, offset = 0;
struct hidpp20_device *dev = NULL;
struct hidpp_device base;
struct hidpp20_onboard_profiles_info info = { 0 };
int rc;
if (argc < 2 || argc > 4) {
@@ -114,13 +119,15 @@ main(int argc, char **argv)
if (!dev)
error(1, 0, "Failed to open %s as a HID++ 2.0 device", path);
hidpp20_onboard_profiles_get_profiles_desc(dev, &info);
if (argc == 2)
rc = dump_everything(dev);
rc = dump_everything(dev, info.sector_size);
else {
page = atoi(argv[1]);
if (argc > 3)
offset = atoi(argv[2]);
rc = dump_page(dev, 0, page, offset);
rc = dump_page(dev, info.sector_size, 0, page, offset);
}
hidpp20_device_destroy(dev);

110
tools/hidpp20-reset.c Normal file
View File

@@ -0,0 +1,110 @@
/*
* Copyright © 2017 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 <errno.h>
#include <error.h>
#include <fcntl.h>
#include <hidpp20.h>
#include <libratbag-util.h>
static inline int
reset_sector(struct hidpp20_device *dev, uint16_t sector_size, uint16_t sector)
{
int rc = 0;
_cleanup_free_ uint8_t *data = NULL;
data = zalloc(sector_size);
/* this returns error 4: hardware error and is expected */
rc = hidpp20_onboard_profiles_write_sector(dev, sector, sector_size, data, false);
if (rc == 4)
rc = 0;
return rc;
}
static int
reset_all_sectors(struct hidpp20_device *dev, uint16_t sector_size)
{
uint8_t sector;
int rc = 0;
for (sector = 1; sector < 31; sector++) {
rc = reset_sector(dev, sector_size, sector);
if (rc != 0)
break;
}
if (!rc)
rc = reset_sector(dev, sector_size, 0);
return rc;
}
static void
usage(void)
{
printf("Usage: %s [sector] /dev/hidraw0\n", program_invocation_short_name);
}
int
main(int argc, char **argv)
{
_cleanup_close_ int fd = -1;
const char *path;
size_t sector = 0;
struct hidpp20_device *dev = NULL;
struct hidpp_device base;
struct hidpp20_onboard_profiles_info info = { 0 };
int rc;
if (argc < 2 || argc > 3) {
usage();
return 1;
}
path = argv[argc - 1];
fd = open(path, O_RDWR);
if (fd < 0)
error(1, errno, "Failed to open path %s", path);
hidpp_device_init(&base, fd);
dev = hidpp20_device_new(&base, 0xff);
if (!dev)
error(1, 0, "Failed to open %s as a HID++ 2.0 device", path);
hidpp20_onboard_profiles_get_profiles_desc(dev, &info);
if (argc == 2)
rc = reset_all_sectors(dev, info.sector_size);
else {
sector = atoi(argv[1]);
rc = reset_sector(dev, info.sector_size, sector);
}
hidpp20_device_destroy(dev);
return rc;
}

View File

@@ -55,7 +55,7 @@ def main(argv):
if ns.output:
ns.output_file = open(ns.output, 'w')
st = os.stat(ns.output)
os.chmod(ns.output, st.st_mode | stat.S_IEXEC)
os.chmod(ns.output, st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
sys.stdout = ns.output_file
print_ratbagctl(ns.ratbagctl, ns.ratbagd, ns.version)
try:

File diff suppressed because it is too large Load Diff

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