553 Commits

Author SHA1 Message Date
Peter Hutterer
7036428066 meson.build: libratbag 0.9.900
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-25 13:48:39 +10:00
Peter Hutterer
18239f1aee DBusInterface: add missing 'Methods' header
And consistently use ':' after Properties/Methods headers

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-24 09:25:29 +10:00
Benjamin Tissoires
fe8fde79f6 tools: ratbagctl: fix len(array) > 0 check
"for python, instead of checking len > 0 we're supposed to use
if input_string"

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-23 09:28:07 +02:00
Peter Hutterer
9f805a4ebe tools: don't return a ratbagd_process if things failed
If we couldn't find the name on the bus or the ratbagd process already
terminated, return None and fail.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-23 08:05:17 +02:00
Peter Hutterer
572f00cf2a Add GNOME svg for Roccat Kone XTD 2017-08-23 08:04:12 +02:00
Benjamin Tissoires
330b4641ad tools: ratbagctl: have a full description of the commands in the usage
I originally intended to have a dynamic use of argparse to generate
the commands, but it turned out to be a nightmare.

Just go down the tree of arguments and format the help properly.

Fixes #288

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-23 14:25:35 +10:00
Benjamin Tissoires
c1c8b1eda2 tools: ratbagctl: fixes for parsing arguments
We shouldn't try to access input_string[0] if len(input_string) is null.
Meaning a condition in the break in the while loop could be checked
earlier (or later).

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-23 14:25:35 +10:00
Benjamin Tissoires
aa251448d3 tools: rabtagctl.devel: only output the dbus name when using keepalive
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-23 14:25:35 +10:00
Benjamin Tissoires
e27b1eb99f tools: ratbagctl: remove useless try/catch
ns is never reset, so once it has a subparser, it'll keep it, so the try
catch is just too much

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-23 14:25:35 +10:00
Benjamin Tissoires
faaf93b194 tools: ratbagctl: kill an infinite loop in parsing
Calling "ratbagctl profile -t" resulted in an infinite loop because it
tried again and again to parse "-t" in the profile

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-23 14:25:35 +10:00
Benjamin Tissoires
58957667e1 tools: ratbagctl: shout the usage if the parsing is incomplete
Too easy to trigger ("ratbagctl.devel profile" is just enough), so we
probably better have a proper usage call here.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-23 14:25:35 +10:00
Benjamin Tissoires
0db7ba0c85 tools: ratbagctl: rename .parse into add_to_subparsers
.parse did not consumed the input_string, it only appends the given
parsing to the parent. Rename the function and kill the debug output
in nested calls as this was misleading while re-reading the code.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-23 14:25:35 +10:00
Benjamin Tissoires
c42c15ad0e tools: ratbagctl: have the global options global
The help option (or verbose FWIW) were not global which makes some random
behavior when using -h at different places for the input string.

Retrieve those options once and remove them from the list of things to
process.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-23 14:25:35 +10:00
Benjamin Tissoires
fc50e38ae2 tools: ratbagctl.devel: honor --help too
Now that ratbagctl properly shows the help, the devel version should
too.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-23 14:25:35 +10:00
Benjamin Tissoires
b2d29a0285 tools: ratbagctl.devel: fix exception if the given arguments are wrong
"sudo ./build/ratbagctl.devel profile 2 4" returns an exception with cmd
being unknown.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-23 14:25:35 +10:00
Peter Hutterer
87ecd6e252 tools: add --keepalive option to ratbagd devel
Keeps the ratbagd daemon running until a signal is received. This is helpful
for using an external client (like piper) to run against the ratbagd.devel
instance. And to make that even easier, print the RATBAGCTL_DEVEL environment
variable for use with ratbagd.py. In the future, this should probably be a
more generic name.

The integration is a bit odd because we need to add --keepalive to the actual
parser, but we recreate that on every parse() call. So we set a flag on the
parent object and check for that during parser creation.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-22 08:17:21 +02:00
Peter Hutterer
2b49550b9b tools: ratbagctl: skip on AttributeError exception handling for --help
We default to False anyway, so it's guaranteed to be an attribute on the
parser

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-22 08:17:21 +02:00
Jente Hidskes
6ddba76c93 RatbagdButton: make mapping properties writable
This brings RatbagdButton in line with the other Ratbagd* objects, and
more importantly solves https://github.com/libratbag/piper/issues/140.
2017-08-22 10:22:51 +10:00
Peter Hutterer
d9e7298aee ratbagctl: improve the top-level --help output
Don't automatically handle the help output - call print_help() on the parser
manually.

It's unclear why this doesn't just get called correctly, but at least this
fixes the immediate issue. The subcommand help is still pretty terrible
though.

https://github.com/libratbag/libratbag/issues/288

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-21 11:06:32 +02:00
Clément Vuchener
49b7029506 hidpp: Fix error string array size
Extend error strings array so reading 0xFF as an error code would not overflow.
2017-08-18 04:26:50 +10:00
Clément Vuchener
468787601e hidpp: Distinguish 1.0 and 2.0 error codes 2017-08-18 04:26:50 +10:00
Jente Hidskes
804d8f7969 RatbagdResolution: emit properties changed from resolution_set_default
This allows clients to respond to changes in the default resolution. It's
not the cleanest approach, but it works. To clean it up, we could cache
the changed resolutions and make sure to only emit the signal on those,
just as the FIXME already notes.

References https://github.com/libratbag/piper/pull/36.
2017-08-17 10:35:09 +10:00
Jente Hidskes
9b53119121 RatbagdResolution: emit properties changed from resolution_set_active
This allows clients to respond to changes in the active resolution. It's
not the cleanest approach, but it works. To clean it up, we could cache
the changed resolutions and make sure to only emit the signal on those,
just as the FIXME already notes.

References https://github.com/libratbag/piper/pull/132.
2017-08-17 10:35:09 +10:00
Jente Hidskes
b773ac085b ratbagd.c: unlink before emitting property changed 2017-08-11 11:12:24 +01:00
Peter Hutterer
13419dcbb8 doc: drop documentation
Our DBus interface is the only API now and libratbag is just the internal
library. Let's not build doxygen for it, there's no need.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-08-02 10:14:23 +01:00
Benjamin Tissoires
c9322fa983 tools: ratbagctl.devel: do not spawn a subprocess to run a python script
We do not really care using the actual ratbagctl command, we just want
to run it. So we can simply call out the python functions directly
and live happily ever after.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-01 10:31:43 +01:00
Benjamin Tissoires
6cfca9e34b tools: share the devel boiler plate in a separate python file
We want to share as much as possible between ratbagctl.devel and
ratbagctl.test, so better have a separate file that knows how to
properly spawn the dbus daemon and terminate it.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-08-01 10:31:43 +01:00
Jente Hidskes
282398493b ratbagd_button: emit property changed on ActionType when setting mappings 2017-07-31 14:38:24 +01:00
Jente Hidskes
022dc74354 ratbagd_button: emit property changed on Macro 2017-07-31 14:38:24 +01:00
Jente Hidskes
cd6ffca413 ratbagd_button: emit property changed on KeyMapping 2017-07-31 14:38:24 +01:00
Jente Hidskes
b19a6ebcfc DBusInterface: document Macro and SetMacro 2017-07-31 14:38:24 +01:00
Peter Hutterer
811281372b ratbagd: fix return code for SetSpecialMapping (#262)
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-30 14:23:25 +02:00
Benjamin Tissoires
c361b8500f test: make sure the dbus daemon is available before starting the test suite
On CircleCI, the builds fail one over four times because we are launching
the testsuite while ratbagd.devel is still not ready.

Do a basic polling after we launch the daemon so that we are aware when
the ratbagd daemon is ready.

A proper solution would be to use "new" for querying the proxy, not
"new_sync". However, this would require us to have a GTK mainloop
running, so it's a PITA.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-29 09:05:43 +01:00
Thomas Hindoe Paaboel Andersen
bcf302904d meson: allow builddir other than 'build'
Configuring with 'meson builddir' would fail with:

Program build/ratbagctl.test found: NO
Meson encountered an error in file meson.build, line 512, column 0:
Program "build/ratbagctl.test" not found or not executable
2017-07-29 08:24:21 +01:00
Benjamin Tissoires
e0e4d5062a hidpp20: fix resolution active set/get
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-28 16:19:26 +01:00
Benjamin Tissoires
abd1a04fed tools: ratbagctl: remove useless function
it was used while we were missing implementations, no it's useless.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-28 15:51:26 +01:00
Benjamin Tissoires
ab1d7628f1 tools: ratbagctl: fix 'set' processing when the device is missing
is it a hack or the correct way?

triggered by: sudo ./build/ratbagctl.devel led 0 set color ffffff

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-28 15:51:26 +01:00
Peter Hutterer
670cf90e11 ratbagd: handle SIGINT for quitting
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-28 14:48:10 +01:00
Peter Hutterer
f5210e1902 ratbagd: remove devices before removing the context
This code isn't actually hit because we don't handle signals correctly, but
once we do, ratbagd_free() will assert because the devices never get removed.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-28 14:48:10 +01:00
Peter Hutterer
5bcafb5bee Reduce to one special->special string mapping dictionary
No need to have two copies here, simply have the dict and then flip it so it
works with the strings as keys too.

The slightly odd list comprehension is because we can't add to a dictionary
while iterating through it, so we need to get everything out first.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-28 14:17:57 +01:00
Jente Hidskes
d8e132f550 tools/ratbagd.py: sync with Piper 2017-07-27 09:37:31 +02:00
Benjamin Tissoires
7e9566b00f Start dbus in the container for being able to run the test suite
We don't need a full systemd service, just dbus-daemon to be started.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-26 18:10:03 +02:00
Benjamin Tissoires
7c313a31fd tools: ratbagctl: fixes for specials
few minor issues which were unseen because driver-test was missing to set
the special key mappings

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-26 18:08:39 +02:00
Jente Hidskes
861562e58a ratbagctl: fix typo 2017-07-26 18:07:19 +02:00
Jente Hidskes
0a6b4b3778 ratbagd: change the led type to the enum 2017-07-26 18:07:19 +02:00
Jente Hidskes
b4f451285e ratbagd: change the special mapping to the enum 2017-07-26 18:07:19 +02:00
Jente Hidskes
5f0ebdead7 tools/ratbagd.py: sync with Piper 2017-07-26 10:16:55 +02:00
Benjamin Tissoires
de3bb0e579 implement resolution active set (#249)
Fixes #80

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-26 08:32:16 +02:00
Benjamin Tissoires
9dcd2c6c05 trivial flake8 fixes
whitespaces, missing RatbagdButton and more whitespaces

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 18:44:04 +02:00
Benjamin Tissoires
34196c9960 tools: ratbagd.py: add readwrite=False when we are using explicit setters
Assigning an action on a button has some side effects, and this is why we
do not have writable properties on those.
Mark the _set_dbus_property() as read only so we do not get a bunch of
PropertyReadOnly exceptions.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 18:24:10 +02:00
Peter Hutterer
5399d15658 ratbagd: add support for test device button types
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 18:24:10 +02:00
Peter Hutterer
5f98d8d029 ratbagd: rename test's button type to action_type
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 18:24:10 +02:00
Peter Hutterer
10903e2434 ratbagd: change the button type to the enum value
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 18:24:10 +02:00
Peter Hutterer
b7f53effa1 ratbagd: add macro support
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 18:24:03 +02:00
Peter Hutterer
129551f3f4 ratbagd: add support for macros in the test device
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 17:17:03 +02:00
Peter Hutterer
4f62424e8a driver-test: fix macro initialization
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 17:17:03 +02:00
Peter Hutterer
ae65ab3ae8 tools: ratbagctl: fix printing of special key map
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 17:17:03 +02:00
Peter Hutterer
96067f18ca ratbagd: change the key action types to enums
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 17:16:54 +02:00
Peter Hutterer
67e1c8667b Change enum value of the unknown value type
Keeps the values as uints

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 16:47:27 +02:00
Peter Hutterer
ab67517584 tools: fix typo - s/UNKOWN/UNKNOWN/
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 16:47:27 +02:00
Peter Hutterer
9fe892f10f tools: ratbagctl: add support for resolution default set/get
https://github.com/libratbag/libratbag/issues/246

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 12:17:15 +02:00
Peter Hutterer
bf18e9b795 ratbagd: fix resolution_set_default
https://github.com/libratbag/libratbag/issues/246

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 12:17:15 +02:00
Peter Hutterer
e95bb9601c tools: ratbagd: skip superfluous is None check
We'll throw an exception before this happens

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 12:17:15 +02:00
Jente Hidskes
a16ffa3946 DBusInterface: mark Resolution as read-write 2017-07-25 11:27:08 +10:00
Peter Hutterer
8c97a12741 Don't install ratbag-command
libratbag is now a static library only and the only exposed interface is the
DBus interface. We have ratbagctl for that interface.

ratbag-command shouldn't be installed, it becomes a pure developer tool now.
We still keep it around for that reason, but let's not install it.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 11:14:13 +10:00
Peter Hutterer
0982d6a4a9 Drop libratbag as separate library
Now that ratbagd is part of libratbag, the DBus interface is the preferred way
to interact with a device. Shipping libratbag as a .so file means we have to
worry about two ABIs when it's unlikely that anyone ever uses the libratbag.so
directly.

Make it a static library internally instead.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-25 11:14:13 +10:00
Benjamin Tissoires
3160d86050 tools: ratbagctl: do not use chevrons and report active profile
ratbag-command uses parentheses, not chevrons, and it also reports
which profile is currently active.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 11:09:24 +10:00
Benjamin Tissoires
6faa59c70d tools: ratbagctl: add support for separate X/Y resolutions
Big commit because of the test suite changes.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 11:09:24 +10:00
Benjamin Tissoires
ced958f135 hidpp10: implement enable/disable profiles
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 11:06:59 +10:00
Benjamin Tissoires
d5ccaed457 hidpp10: implement commit API
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 11:06:59 +10:00
Benjamin Tissoires
2c799eca30 hidpp10: correctly retrieve the profile directory and use it
We need to read the directory and the profiles only once.
Nothing tells us how many profiles are supported, so use the specifications
from Logitech website, and add a hwdb entry for those.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 11:06:59 +10:00
Benjamin Tissoires
98fb63e2e3 hidpp10: assign the active resolution when the profile is not active
Same than hidpp20.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 11:06:59 +10:00
Benjamin Tissoires
55bab29233 hidpp20: enable/disable profiles and report it correctly
Bunch of errors while processing the dictionary of enabled profiles.
Got everything working out quite well on the G900 and G403, so I guess
others should behave properly.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 11:06:59 +10:00
Benjamin Tissoires
c0c77100ad ratbagd: implement enable/disable profile
We also maps the commands to ratbag-command.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 11:06:59 +10:00
Benjamin Tissoires
aa50df8eaf tools: ratbag-command: fix enable/disable profiles
The usage help is wrong, it's not how it works.
It'll be too much pain to fix it the other way around, and I prefer
using "ratbag* profile N enable" than "ratbag* profile enable N".

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 11:06:59 +10:00
Benjamin Tissoires
c12ca6008a tools: ratbagctl: add missing commits
On devices supporting the commit API, ratbagctl did not write anything
to the device.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-25 11:06:59 +10:00
Peter Hutterer
c6bc6b2576 tools: ratbagd: don't silence UnknownProperty errors
Now that we've moved all properties to read-write where required, unleash the
exception again - any UnknownProperty error is now a bug.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
f53bf7d4ad ratbagd: make the LED properties writable
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
40bd189cc0 ratbagd: make the resolution properties writable
This also drops the separate x/y properties and instead just returns a tuple
for "Resolution".

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
7097f1dea6 ratbagd: remove GetResolutionByIndex
This is superfluous, it's trivial enough to get the Resolutions property and then
work with that. We're not talking about a huge amount of data anyway.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
70f3da98db ratbagd: add IsActive/IsDefault to Resolution
This replaces the calls in the profile.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
f281bd751e ratbagd: drop 'GetProfileByIndex'
This is superfluous, it's trivial enough to get the Profiles property and then
work with that. We're not talking about a huge amount of data anyway.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
04b54d26e2 ratbagd: add 'IsActive' property to the profile
Instead of a GetActiveProfile in the device, make this a property of the
profile. The SetActive method stays around, it's less prone to mis-use than
the property since we cannot allow a caller to go "profile.is_active = False"

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
19cdf6d7e9 ratbagd: drop SvgPath property from device
Obsolete now that we have theme support, use GetSvg() instead

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
0018431f31 ratbagd: remove DeviceNew/DeviceRemoved signals
Our device list is so small that having separate signals is a bit pointless,
updating the property should be sufficient.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
8380e1686b Tidy up the dbus documentation
Use more sectioning and more precise descriptions of the function signatures.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
c14ffe36e3 ratbagd: send a PropertyChanged signal when modifying the device list
We have DeviceAdded and DeviceRemoved signals but given the expected number
of devices (less than 2 in most cases) it's easier to just update the Devices
property. The DeviceAdded/Removed signals stick around for now, they will be
removed later.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
a4dd9bcdf3 ratbagd: change the remote property value when writing a property
set_cached_property merely updates the proxy's own copy, it doesn't do
anything to the remote. Manually call the DBus.Properties.Set interface
to do that.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 11:36:09 +02:00
Peter Hutterer
c1cabeeeeb meson: reduce the valgrind test timeouts
Factor 5 should be enough, factor 100 is a bit excessive

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 10:32:41 +02:00
Peter Hutterer
ca69365a86 meson: run ratbagctl.test as part of ninja test
Or at least do so when we're root.

Test is skipped on CircleCI because we don't have DBus.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 10:32:41 +02:00
Peter Hutterer
98fa9c168a tools: ratbagd: throw an error if the system bus is unavailable
When running in a container (CircleCI), dbus may not be available. Throw one
of our errors to be consistent with our behavior when the name owner isn't
available.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 10:32:41 +02:00
Peter Hutterer
0e26b6e2be tools: ratbagctl: check ratbagd for immediate exit before continuing
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 10:32:41 +02:00
Peter Hutterer
760c3f3ff9 tools: ratbagctl: Fix typo in comment
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 10:32:41 +02:00
Peter Hutterer
8aaa6894df tools: ratbagctl: don't start a shell for ratbagd's subprocess
This is not required.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 10:32:41 +02:00
Peter Hutterer
636d254d41 tools: ratbagctl: remove duplicate subprocess.terminate() call
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 10:32:41 +02:00
Peter Hutterer
f6e5dd6c2c test: disable core dumps in the test suite
No need for this to end up in the journal

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 10:32:41 +02:00
Peter Hutterer
5637755345 Only reset the driver to NULL on unusual exits
If a driver succeeded but then fails sanity checks, leave the driver set and
let the cleanup code handle the rest. Otherwise, memory allocated by the
driver's probe() function is never released.

Fixes a valgrind memory leak complaint when running tests for invalid devices
(e.g. device_profiles_num_0).

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 10:32:41 +02:00
Peter Hutterer
a9c4f46412 ratbagd: change the led path to .../event4/p0/l0 (#240)
Was using 'b0' as enumerator, copy-paste error from the button code. Switch to
'l0' to make it easier to differentiate in d-feet or other dbus viewers.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-24 06:58:30 +02:00
Peter Hutterer
7aed3640c4 tools: ratbagctl: hook up report rate argparsing
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-21 10:12:07 +02:00
Peter Hutterer
85f2ec3910 tools: ratbagctl: compress two print statements into one
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-21 10:12:07 +02:00
Peter Hutterer
6274dea4ac meson: add ratbagd.py as input source for merge_ratbagd.py
Otherwise we won't notice changes to it

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-20 07:09:28 +02:00
Peter Hutterer
a938521c9e ratbagd: use zalloc instead of calloc
Because we really don't care about OOM beside blowing up.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-19 10:09:01 +02:00
Peter Hutterer
9450d1402f Move the _cleanup_ functions over to shared-macro.h
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-19 10:09:01 +02:00
Peter Hutterer
8209ce56b6 Move a few #defines over to shared-macro.h
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-19 10:09:01 +02:00
Peter Hutterer
a6d03fde18 ratbagd: use strdup_safe for all string copies
Because if we can't strdup a device name or dbus path, we have problems we
can't recover from anyway.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-19 10:09:01 +02:00
Peter Hutterer
5fff0ece5b tools: include config.h from lur-command
Removes duplicate GNU_SOURCE define

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-19 10:09:01 +02:00
Peter Hutterer
d915fdfe45 Include shared-macro.h from libratbag-util.h
These headers need merging at some point, for now just include it and remove
the duplicates.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-19 10:09:01 +02:00
Peter Hutterer
7b861ada4c Move the shared sources from ratbagd into src
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-19 10:09:01 +02:00
Benjamin Tissoires
fa1799eef3 tools: copy ratbagd.py from piper and integrate it in ratbagctl
We are using a custom python script to generate the final ratbagctl
so we can strip out the headers.

Now merging and testing ratbagd.py in piper will be easier than ever.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-19 08:39:29 +02:00
Peter Hutterer
77cc2fa82d Add logitech G303 SVG for gnome theme (#228)
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-18 13:15:48 +02:00
Benjamin Tissoires
de4bd4a2d2 tools: ratbagctl tests: do not spawn a new process for each call
This reduces the time to run the tests from (now with previous commit) 10s
to a mere 2 seconds.

The --subprocess argument is voluntarily masked from the help message,
because I couldn't figure out how to add it to the unittest parser.
And OTOH, we should not run in subprocess unless debugging...

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-18 19:33:32 +10:00
Benjamin Tissoires
c61782f1a4 tools: ratbagctl: Sync ratbagd.py with piper
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-18 19:33:32 +10:00
Benjamin Tissoires
6503407007 tools: ratbagctl: add missing capabilities strings
Prevents some failures while showing the information of the devices.

Fixes de16f1a4b1

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-18 19:33:32 +10:00
Jente Hidskes
09295d9d0e data/gnome/logitech-g700: fix overlapping paths
See https://github.com/libratbag/piper/issues/48
2017-07-18 17:52:43 +10:00
Jente Hidskes
2c379d09c9 data/gnome/README: include note on z-ordering
As discussed in https://github.com/libratbag/piper/issues/48
2017-07-18 17:49:25 +10:00
Jente Hidskes
994ade7fff data/gnome/logitech-502.svg: fix text alignment of button5-leader 2017-07-18 17:04:20 +10:00
Peter Hutterer
769289fe01 circleci: use fedora-latest, not rawhide
Otherwise we get build errors when rawhide is broken, like today. Not worth
chasing that moving target.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-14 09:11:14 +10:00
Jente Hidskes
f05950f37c RatbagdLed: emit DefaultResolutionChanged on the resolution's object
path

See https://github.com/libratbag/piper/pull/36#issuecomment-315065722
2017-07-14 08:33:36 +10:00
Peter Hutterer
de16f1a4b1 Set the parent capabilities before setting any child ones (#223)
Any specific capability like 'switchable resolution' requires the basic
'resolution' capabilty to be set.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-13 09:17:14 +02:00
Peter Hutterer
d1db382607 ratbagd: drop caching of libratbag variables (#222)
Instead of copying them into our own struct, just query libratbag when we need
them. This brings the resolution code in line with the button and LED code
which don't do caching either.

Eventually we may want to cache them again to detect changes behind your
back (if libratbag value != our cached value) but for now this reduces the
work ratbagd does.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-13 08:29:58 +02:00
Peter Hutterer
82a0cd4ec4 ratbagd: send property changed signals when we change the properties
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-13 07:02:08 +10:00
Clément Vuchener
ec85b6db3e hidpp20: Fix changing flags in hidpp20_special_key_mouse_set_control
The first bit of each pair set is new state for the flag, the second must be set for the change to be made.
2017-07-12 09:05:11 +02:00
Clément Vuchener
a9f4049841 driver-hidpp20: do not divert button when remapping
Temporary diversion has priority over remapping and should not be set at the same time.
2017-07-12 09:05:11 +02:00
Benjamin Tissoires
dab5d2c0b7 ratabgctl.test: reset the test device before each test
Now that we have a method which resets the device, use it so we can
rely on the state of the device.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-12 08:16:39 +02:00
Benjamin Tissoires
51d625244d ratbagd: add a method to reset the test device to its defaults
Make sure we keep the defaults as such by duplicating them in
driver-test.c (and mark the input data as const).

Add a dbus method to reset the test device from the caller.
This is much useful when running tests given we have no guarantees on the
ordering.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-12 08:16:39 +02:00
Benjamin Tissoires
7b1e7dc4e7 tools: add a test suite for ratbagctl
Using unittest to be fancy

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-12 08:16:39 +02:00
Benjamin Tissoires
019d020535 tools: ratbagctl: output device information like ratbag-command
Better having a consistent device information across tools.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-12 08:16:39 +02:00
Benjamin Tissoires
89d071bb80 Fix flake-8 complains
Piper already uses this, we might as well.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-12 08:16:39 +02:00
Benjamin Tissoires
499ebc7d18 tools: ratbagctl: sync ratbagd bindings with piper
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-12 08:16:39 +02:00
Benjamin Tissoires
e522e0bb07 drivers: add a fallback for fetching the SVG name
In case the udev property is not present, we might want to query
the driver for the SVG name.
This is useful for driver-test as there is no udev device attached to
it, and for hidpp20 as we have auto-detection. We should then be able
to implement this in hidpp20 also so that the name gets automatically
created.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-12 08:16:39 +02:00
Benjamin Tissoires
f55a314666 ratbagd: add a test device while in developer mode
This allows to run tests against a device without having a proper device
plugged in.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-12 08:16:39 +02:00
Benjamin Tissoires
97ac0f81f2 hidpp20: shut one ratbagd warning
If the profile is not active, we do not know which resolution is currently
used. Instead of having ratbagd complaining, just set the default as
active.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-12 08:16:39 +02:00
Peter Hutterer
8cfcab3363 circle.yml: add ninja scan-build to the jobs
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-11 10:50:22 +02:00
Peter Hutterer
6f72accbee circle.yml: split fedora install out and make ninja more flexible
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-11 10:50:22 +02:00
Peter Hutterer
e021937867 Change device name to use a stack buffer
This shuts up a scan-build warning about a memleak that looked like a false
positive but I couldn't get rid of.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-11 10:50:22 +02:00
Peter Hutterer
82d197462f hidpp20: fix NULL-pointer dereference warning
rc may be a negative errno or a positive hidpp error code. If the latter we
used to continue with a NULL macro pointer, eventually crashing.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-11 10:50:22 +02:00
Peter Hutterer
78bdfe7a59 Don't allocate zero-sized memory
Triggered by the tests with 0 resolutions

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-11 10:50:22 +02:00
Peter Hutterer
586fc7a97d hidpp20: squash memory leak on profile initialization error
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-11 10:50:22 +02:00
Peter Hutterer
2faa94920e hidpp20: drop unused argument from the profile destroy function
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-11 10:50:22 +02:00
Peter Hutterer
01163d8f77 ratbagd: fix a false-positive unused variable warning
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-11 10:50:22 +02:00
Peter Hutterer
04990e4ea2 hidpp10: error out if we deal with unsupported profiles
Just logging and error and continuing is not enough, we'll eventually
dereference garbage values.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-11 10:50:22 +02:00
Peter Hutterer
37c8dec45c Fix the various scan-build warnings for dead assignments
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-11 10:50:22 +02:00
Peter Hutterer
df2a06690d Fix device capability check
We now have a lot more bits to cater for, so let's use an array.

Broken with 487e66e64d

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-11 10:50:22 +02:00
Jente Hidskes
2c4cdc6dc6 ratbagd-led: mark DBus properties as EMITS_CHANGE 2017-07-10 20:41:12 +10:00
Jente Hidskes
96c2f404d2 ratbagd-led: add error check for RGB 0-255 range 2017-07-10 20:41:12 +10:00
Clément Vuchener
e33a515fd6 data: add toned-down SVG for Logitech G500/G500s (#214)
* data/gnome: add SVG for Logitech G500/G500s

* data/gnome: add buttons 0 and 1 to the G700
2017-07-10 09:30:56 +02:00
Jente Hidskes
8fc58e259c Use toned-down device SVGs (#182)
* data/gnome: add GNOME theme requirements

* data/logitech-g403.svg: add G403 GNOME SVG

* data/logitech-g502.svg: add G502 GNOME SVG

* data/logitech-g700.svg: add G700 GNOME SVG

* meson: install GNOME SVG files
2017-07-07 16:05:36 +02:00
Christian Kellner
caadd86091 hwdb: add G403 entry
The G403 comes in a (optional) wireless version too; the dongle
that come with it has the product id c539, which is already
handled and also works (by the G900) entry.

Signed-off-by: Christian Kellner <christian@kellner.me>
2017-07-07 15:12:55 +02:00
Jente Hidskes
e16aafe86c data: move default SVGs under default/ subdirectory..
.. and install them in the appropriate directory after #209
2017-07-07 15:07:45 +02:00
Peter Hutterer
6a0ebe9e45 ratbagd: add support for svg theming
Two new DBus interfaces:
org.freedesktop.ratbagd1.Manager.Themes -> string array
org.freedesktop.ratbagd1.Device.GetSvg('theme') -> string

The Themes property is a static list of available themes. These are currently
hardcoded in ratbagd, but the theme 'default' is guaranteed to exist.

The GetSvg() method takes a theme name and returns the full file path to the
themed SVG.

Longer-term this will deprecate the Svg and SvgPath properties in the Device
but for now we leave them there.

https://github.com/libratbag/libratbag/issues/204

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-07 08:20:01 +02:00
Benjamin Tissoires
79f4b9c0d6 tools: add ratbagctl.devel to launch both ratbagd and ratbagctl
When developing with ratbagd, we need to test against master, not
the system-wide installed daemon.

Allow to name space ratbagd with the creation of a new (not installed)
rabtagd.devel daemon, that is tagged with the git sha of the ratbagd
folder.

Handle this namespace in ratbagctl by looking at an environment variable
RATBAGCTL_DEVEL.

Then provide a python script that launches both rabtagd.devel and
ratbagctl with the correct environment variable set so that they can
talk to each other.

Note that we blindly overwrite the dbus .conf file as nobody else than
developers should have one.

Note: the tagging of ratbagd.devel (like the meson bit) won't run unless
a rebuild is triggered. So we don't necessarily always get the right git
tag. However, touching ratbagctl.in triggers a rebuild, so we should be
OK-ish.

Fixes #206

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-07 16:02:46 +10:00
Benjamin Tissoires
e6433abd09 tools: import ratbagd.py from piper
We are updating it, let us keep it in sync

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-07 16:02:46 +10:00
Peter Hutterer
96f3b7858b tools: fix ratbagctl error when dbus isn't available (#210)
File "./build/ratbagctl", line 713, in main
    cmd.func(r, cmd)
UnboundLocalError: local variable 'r' referenced before assignment

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-06 08:39:27 +02:00
Peter Hutterer
f5abc191d8 ratbagd: drop the ActiveResolutionChanged signal generation (#208)
* ratbagd: drop the ActiveResolutionChanged signal generation

Leave the signal in the DBus API for now so we can hook it up later, but
stop the signal being generated. Changing the dpi does not change the active
resolution.

https://github.com/libratbag/libratbag/issues/205

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>

* ratbagd: fix signal dbus interface name

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-06 08:20:43 +02:00
Benjamin Tissoires
251b602323 ratbagd: call the correct set_resolution based on the capabilities
ratbagd unconditionally called set_resolution_xy while libratbag expects
the caller to check on the capabilities. Instead of letting this burden
to the dbus user, have ratbagd handle this for us.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-05 11:33:21 +02:00
Peter Hutterer
fe3169182c meson: fix valgrind setup
The previous approach had no effect, it wasn't doing things correctly.
Add a proper valgrind setup, run with:

       mesontest --setup=valgrind

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
[fixed rebase conflict]
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-05 09:36:30 +02:00
Peter Hutterer
642a5339bc meson: drop invalid configuration: tag for install_data
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-05 09:35:51 +02:00
Benjamin Tissoires
c251053f03 circle.yml: add workaround for meson upstream bug
Meson currently doesn't clean up after itself on uninstall.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-05 08:37:18 +10:00
Benjamin Tissoires
4b97b00518 circle.yml: uninstall and checks if there are any leftovers
Check if $PREFIX is still there after uninstall, and if so show
which files are left.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-05 08:37:18 +10:00
Jente Hidskes
faa1a24c25 RatbagdDevice: add commit method
Fixes #198
2017-07-04 17:02:18 +02:00
Benjamin Tissoires
b78b5cd73e circle.yml: add install step
It doesn't cost much to actually install the files, so we can check
where they are installed.

Also add mesonconf before building so we can see in the logs
the various configure environment.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-03 19:14:10 +10:00
Peter Hutterer
4eb5411bf1 meson: fix svg data dir (#191)
It was missing the prefix

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-03 10:28:20 +02:00
Peter Hutterer
22af1176d3 meson: hack around Fedora's systemd libdir confusion
Fedora uses /usr/lib64 as libdir but systemd uses /usr/lib for its files. We
used libdir to install the unit files, which then end up in
/usr/lib64/systemd/systemd where it will be ignored. There's no good way to
avoid this beyond detecting this mismatch and hacking around it.

To keep custom prefixes intact, this patch detects a /usr prefix and checks
with systemd's pkconfig, if the libdir and the systemd unitdir mismatch, then
we adjust the path.

https://github.com/libratbag/libratbag/issues/188

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-07-03 09:57:20 +02:00
Benjamin Tissoires
bb6d8a6ea4 update circle CI link 2017-07-01 17:43:26 +02:00
Benjamin Tissoires
935f68ae8f enhance circle.yml with workflows
Circle CI uses docker, so nothing prevents us from running several
tests in parallel for different distributions. Ideally, we should also
be able to test the install and be sure we can talk to ratbagd
with the dbus activation on those various distributions.

This version runs both Fedora rawhide and Ubuntu 17.04 at the same
time.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-07-01 10:38:01 +10:00
Peter Hutterer
2fd939f514 resolution: add support for min/max resolutions
https://github.com/libratbag/libratbag/issues/180

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-30 09:07:24 +02:00
Peter Hutterer
dbc0672061 README: clarify the two developer-specific notes
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-30 07:24:54 +02:00
Peter Hutterer
804f2eec76 README: clean up the Running ratbagd section
We install everything, so rather than having commands that aren't needed,
simply describe what we're doing and hope for the best.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-30 07:24:54 +02:00
Peter Hutterer
73c24319d1 README: move the dbus interface description out
No-one reads anything longer than 140 char anymore, so let's move the DBus
Interface description out. The average visitor to this site won't care.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-30 07:24:54 +02:00
Peter Hutterer
6b3fc6338e README: cleanup, de-duplication, etc.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-30 07:24:54 +02:00
Peter Hutterer
c6855df5fc Drop the semaphore status in the README and the build script
Semaphore only provides Ubuntu 14.04 which is too old for us. We now have
CircleCI integration which can run docker images, so let's leave that and drop
Semaphore.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-30 07:24:54 +02:00
Benjamin Tissoires
9ed61fd072 Add circle.yml
Semaphore CI is still using Ubuntu 14.04, and the shipped systemd version
is just too old.
OTOH, Circle CI 2.0 (still beta as of today) allows to use any docker
image as a base for running the tests. Meaning that we can have a fedora
rawhide image as a base and we do not have to handle the update of systemd
meson and others.

Ship a basic circle.yml for Circle CI to run our tests and add the badge
in the README.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-30 07:53:22 +10:00
Benjamin Tissoires
1a764944e7 Merge pull request #184 from whot/wip/ratbagd-merge
This merges ratbagd into libratbag, keeping the histories of both projects intact (hence the rather large merge). The meson.build files are merged and a few things are shuffled around to look like one normal project directory. The build artifacts are still the same, i.e. we still build libratbag and ratbagd.
2017-06-29 17:19:30 +02:00
Peter Hutterer
e311f89357 Provide a min/max dpi for the resolutions
For a GUI application like Piper, it's required that we know the range to
present to the user.

This patch includes the code for HID++ 1.0 and 2.0, other drivers need to
follow.

https://github.com/libratbag/libratbag/issues/180

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-29 09:07:18 +02:00
Peter Hutterer
ecdba84977 hidpp20: use min/max macros over ternary condition
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-29 09:07:18 +02:00
Peter Hutterer
c003eccb1e hidpp10: fix a comment, dpi_table is not null-terminated
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-29 09:07:18 +02:00
Peter Hutterer
f36fdc0492 meson: drop the separate shared library for ratbagd
We can reinstate that when we need it

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-29 15:03:11 +10:00
Peter Hutterer
907b96d447 meson: drop unused defines
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-29 15:03:11 +10:00
Peter Hutterer
b4682e836c ratbagd: move the source from ratbagd/src to just ratbagd/
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-29 15:02:56 +10:00
Peter Hutterer
30e8fbedd8 Drop separate ratbagd COPYING file
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-29 15:02:38 +10:00
Peter Hutterer
fc1887c01f Merge ratbagd README into libratbag
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-29 15:01:08 +10:00
Peter Hutterer
d51acfd730 Move ratbagd dbus and tools to top-level directory
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-29 15:01:07 +10:00
Peter Hutterer
35e38a5469 Merge ratbagd's meson file into libratbag's
I couldn't get the subdir() to work, it would throw exceptions somewhere
internal to meson. So instead, just merge the two together.
This still builds everything as before, it's just a single meson file now.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-29 15:01:05 +10:00
Peter Hutterer
2ae3976868 Merge ratbagd into libratbag 2017-06-29 14:59:13 +10:00
Jente Hidskes
5dcdc3c652 Add an API for LEDs 2017-06-29 11:56:37 +10:00
Peter Hutterer
af68f6046c Fix a doxygen reference
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-20 14:07:28 +10:00
Jente Hidskes
c0c4ba156a README: fix install instructions
The instructions still referred to in-source builds. Meson instead uses
an out of tree build directory, hence we update the paths to point to
the build directory.

Fixes #32.
2017-06-14 10:52:41 +02:00
Jente Hidskes
22135f31d5 ratbagd-device: update for new capabilities in libratbag
Fixes #29.
2017-06-12 18:49:44 +10:00
Raghavendra Kamath
0c7ca85f5b Add new artwork of Logitech G9 2017-06-10 12:25:07 +10:00
Raghavendra Kamath
478c5bbb68 Adjust text and resolution in svg.
To make the text look consistent font size is now set to 12pt in all svg & default resolution is adjust according to new inkscape (0.92) defaults
2017-06-10 12:25:07 +10:00
Peter Hutterer
487e66e64d Group the the capabilities and provide some more generic topic-like caps
This makes code that just checks for general abilities easier to write, the
specific thing we can do (e.g. switch profiles) is expressed as separate bit.

This is an ABI break

Related to #172

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-09 08:51:26 +02:00
Jente Hidskes
2b5b0b1772 Fix boolean instruction for Meson
https://github.com/libratbag/libratbag/issues/175
2017-06-08 12:30:02 +02:00
bentiss
13bb5b52e1 Fix boolean instruction for Meson
Fixes issue #175

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-08 11:14:52 +02:00
Peter Hutterer
90d2a48cfb ratbagctl: default to list-devices without argv
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-08 09:15:21 +02:00
Peter Hutterer
97a37d21e1 Drop the python bindings, move them into ratbagctl
The python bindings were mostly boilerplate and a 1:1 mapping of the dbus
interface anyway. Packaging them for distributions is difficult, so given how
little benefit they provide, let's just stop shipping them.

Those who need those bindings (like piper) can either make their own or copy
them.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-08 09:15:21 +02:00
Peter Hutterer
9cf6100e00 python: switch ratbagctl shebang to python3
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-06-07 09:19:05 +02:00
Benjamin Tissoires
932cf905ef ratbagd 0.4
- changed licence of rb-tree to have a consistent MIT licence
- new signal for active resolution changes

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-06-06 16:04:39 +02:00
Benjamin Tissoires
7da53c61cf meson.build: libratbag 0.9
- convert Logitech devices to the commit API (those compatible)
- fixes for ratbag-command regarding LEDs (still few caveats left)
- some coverity fixes
- a brand new lur-command man page

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Stephen Kitt <steve@sk2.org>
2017-05-30 14:11:48 +10:00
Peter Hutterer
ad88f755a4 shared: relicense rbtree to MIT
To avoid having multiple licenses within a project as small as ratbagd, and to
avoid having to deal with any fallout from mixing MIT and LGPL.

Pre-approved by David Herrmann who wrote that code, see
https://github.com/libratbag/ratbagd/issues/23#issuecomment-302741861

Fixes https://github.com/libratbag/ratbagd/issues/23

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-29 13:52:38 +02:00
Benjamin Tissoires
f243abb65c release.sh: remove unreachable test now that we switched to meson
and I must say, why would you not run this script in the current
directory.

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

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

Fixes #152

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

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

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

Signed-off-by: Christian Kellner <christian@kellner.me>
2017-05-24 09:05:15 +10:00
Jente Hidskes
dd71c23a04 resolution: add set_default method
Fixes #8.
2017-05-23 13:30:21 +10:00
Jente Hidskes
299c49c291 README: remove ambiguity in signal descriptions 2017-05-17 12:50:35 +10:00
Jente Hidskes
b1feabaf64 resolution: add signal for active resolution changes
This implements a signal that is fired whenever the active resolution
changes. For now the signal is only fired when the resolution changes
through the setter, ratbagd_resolution_set_resolution, as profile changes may
not always be hooked up in the hardware.

Reference #8.
2017-05-17 12:50:35 +10:00
Jente Hidskes
2722e008e0 profile: add signal for active profile changes
This implements a signal that is fired whenever the active profile
changes. For now the signal is only fired when the profile changes
through the setter, ratbagd_profile_set_active, as profile changes may
not always be hooked up in the hardware.

Fixes #7.
2017-05-17 12:50:35 +10:00
Benjamin Tissoires
178b08cd0a ratbagd 0.3
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2017-05-12 16:35:41 +02:00
Benjamin Tissoires
bf20845249 meson.build: libratbag 0.8
- coverity fixes
- new --version to ratbag-command
- cleaning up the meson build tools

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2017-05-09 14:55:15 +02:00
Jente Hidskes
4dc15451b7 README: s/make install/ninja install/ 2017-05-09 14:38:38 +02:00
Jente Hidskes
3a764b6aae README: replace configure switches with mesonconf switches 2017-05-09 14:38:38 +02:00
Jente Hidskes
190e78c79c README: small documentation fixes 2017-05-09 14:38:38 +02:00
Peter Hutterer
eb01399f26 tools: fix OOB for LED color parsing
Let's not forget about the null-terminator, he feels lonely otherwise.

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

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

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

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

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

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-09 09:24:41 +02:00
Peter Hutterer
2750f8c49b Mark the unknown button type as explicit fallthrough
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-09 07:50:21 +10:00
Peter Hutterer
57ffc9c5d3 meson: drop a bunch of compiler flags
These all affect the final executable and should be under the control of the
distro.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-09 07:50:21 +10:00
Jente Hidskes
a2b858008f meson: use join_paths with dbusdir for consistency 2017-05-08 20:44:30 +10:00
Jente Hidskes
28956126ec meson: correctly install service files
https://github.com/libratbag/ratbagd/issues/18
2017-05-08 20:44:30 +10:00
Peter Hutterer
698d8ab365 Replace ifndef include guards with pragma once
Now that we use meson exclusively and meson uses this for its config.h, you
wouldn't get around it anyway. Besides, the main compilers support it and
libratbag is too niche to worry about others.

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

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-05 09:02:15 +02:00
Peter Hutterer
44471d3c42 meson: use warning_level=2 instead of manual -Wall -Wextra
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 10:14:50 +02:00
Peter Hutterer
ad1fa22196 hidpp10: fix out-of-bounds access
sizeof() gives us twice as many elements as we have indices for on this 16-bit
array.

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

Coverity is a champ, isn't it?

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

Found by coverity

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

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

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

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

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

Found by coverity

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:52:59 +02:00
Peter Hutterer
6a097bd691 meson: drop -g, the meson buildtype option takes care of that
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:49:34 +02:00
Peter Hutterer
2776d1ce8e meson: use warning_level=2 instead of manual -Wall -Wextra
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-05-02 09:49:34 +02:00
Peter Hutterer
fa12517fb2 Drop autotools
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-26 10:13:56 +02:00
Peter Hutterer
9624d1bdcd Add meson build file
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-26 10:13:56 +02:00
Peter Hutterer
8f7bf150df Use automake to generate ratbagctl
All we do is a single sed, we can leave that to automake.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-26 10:13:56 +02:00
Peter Hutterer
f5270e1539 Change the sed replacement for meson's benefit
No functional changes

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-26 10:13:56 +02:00
Peter Hutterer
0834b210ca Remove now-obsolete autogen.sh script
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-26 08:26:07 +02:00
Peter Hutterer
cc41adbbf9 gitignore: drop build/ from gitignore again
As pointed out elsewhere, the build directory is decided by the user and
should thus be a user-specific gitignore configuration.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-26 08:26:07 +02:00
Peter Hutterer
8944353716 Update for new caps in libratbag 0.6
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-25 08:26:14 +02:00
Peter Hutterer
53ff42ea55 README: remove the compilation instructions for systemd
By now you should have 229.

And replace the instructions with actual ratbagd build instructions.

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

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-25 08:11:40 +02:00
Peter Hutterer
25a92e61ef configure.ac: drop xslt check
Some leftover from the triassic period or so

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-24 10:13:22 +02:00
Peter Hutterer
70a33ec78d Drop AM_MAINTAINER_MODE
This isn't useful

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-24 10:13:22 +02:00
Peter Hutterer
d358d7433b Include config.h before anything else
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-24 10:13:22 +02:00
Peter Hutterer
08ce9773dd Remove liblur.pc.in
Should've been removed with b8ed1ea

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

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

Fixes #145

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-03 09:55:53 +02:00
Peter Hutterer
f82c1119bf tools: fix Makefile, it's SOURCES, not SOURCeS
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-04-03 09:55:53 +02:00
Peter Hutterer
f60f8a12ab tools: fix string parsing for LED colors 2017-03-31 15:07:01 +10:00
Peter Hutterer
fd348d456d tools: add missing LED_OFF switch case
And add the missing break
2017-03-31 15:07:01 +10:00
Peter Hutterer
0f73a97bc8 tools: move variable declaration up
We do C99, but not this :)
2017-03-31 15:07:01 +10:00
Peter Hutterer
7783f50110 tools: fix unused variable warnings 2017-03-31 15:07:01 +10:00
Jente Hidskes
617cd628ea python: replace == None with is None
== checks for value equivalence, but in this case what we
really want is address equivalence. Hence we use is.
2017-03-31 08:22:21 +10:00
Jente Hidskes
9601ba1323 python: catch more DBus exceptions
Gio.bus_get_sync may return None when the DBus service is not available (see
libratbag/piper#2). In addition to this, the service may time out when it is
being started in which case Gio.DBusProxy.new_sync will raise a GLib.Gerror.

This commit checks for both scenarios and raises a RatbagdDBusUnavailable
exception when either one of them occurs. This will in turn be caught in for
example Piper to properly deal with this.
2017-03-31 08:22:21 +10:00
Benjamin Tissoires
b85fcd0cee hwdb: add missing G900 over USB entry
Allows to have the nice picture

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

Add a default case which should never get reached.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Fixes #93

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-04-29 09:50:20 +10:00
Peter Hutterer
e18e25d685 hwdb: Add Logitech Wireless Touchpad
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-04-21 14:13:46 +10:00
bentiss
a9dc630fae Merge pull request #10 from whot/master
dbus cleanup
2016-03-17 09:14:41 +01:00
Peter Hutterer
85fdea19c2 Link to the github-pages generated documentation
To update the documentation, do the following:

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

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

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

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

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

This reverts commit 855a6bd831.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-16 10:00:09 +10:00
bentiss
ad06d29140 Merge pull request #77 from whot/master
configure.ac: udev basedir should be relative to prefix
2016-03-15 08:34:54 +01:00
Peter Hutterer
a658b117a7 device: add support for QUERY_CONFIGURATION device capability
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-15 14:40:44 +10:00
Peter Hutterer
638b505ade manager: drop GetDeviceById
We don't really expect more than 2-3 devices even on a test box. This function
serves little purpose, we can easily require a caller to just get all devices
(usually just one anyway) and then go from there.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-15 13:39:28 +10:00
Peter Hutterer
ff3730f3fb device: use "Name" instead of "Description"
This is the terminology used in libratbag as well (and everywhere else where
we deal with input devices).

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-15 13:29:19 +10:00
Peter Hutterer
00f488dc13 resolution: switch the capabilities to an enum
Strings can suffer from locale issues, the official recommendation for DBus
APIs is to use enums here.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-15 13:12:18 +10:00
Peter Hutterer
ca2f32ea58 device: switch the capabilities to an enum
Strings can suffer from locale issues, the official recommendation for DBus
APIs is to use enums here.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-15 13:12:18 +10:00
Peter Hutterer
0ebbccc6cf ratbagctl: fix error when printing separate x/y resolutions
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-15 12:23:44 +10:00
Peter Hutterer
7c622ecc58 Adjust for new libratbag 0.4 API
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-15 10:18:23 +10:00
Peter Hutterer
c12cb66824 configure.ac: udev basedir should be relative to prefix
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-15 09:48:52 +10:00
Peter Hutterer
c476cd0654 Merge pull request #76 from r3pek/master
Fix supported resolutions from G700s
2016-03-15 07:38:56 +10:00
Carlos Silva
98315343b0 Fix supported resolutions from G700s
I was under the impression that G700 and G700s had the same sensor.
Comparing the specs on the Logitech website, i found myself wrong.
2016-03-14 20:56:00 +00:00
bentiss
eb6787181d Merge pull request #75 from r3pek/master
Add Logitech G700s
2016-03-14 20:48:26 +01:00
Carlos Silva
f7d9ca63e2 Add Logitech G700s 2016-03-14 19:14:16 +00:00
Benjamin Tissoires
b563467164 configure.ac: libratbag 0.4
Soname bump for liblur and libratbag as their ABI has been broken in
this release.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-14 19:55:43 +01:00
Benjamin Tissoires
86e9a7abcd lur: bump the version of the library
commit b3237fd0 ("lur: change new_from_hidraw to return an errno") broke
the API, so we need to bump the version too.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-14 19:54:39 +01:00
Benjamin Tissoires
030eaa7c1c Add G502 svg data
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-14 17:19:52 +01:00
Benjamin Tissoires
ae129fe911 Add G700 svg data
Most of the SVG work was made by Peter. Cheers!

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-14 16:20:56 +01:00
Benjamin Tissoires
6190f0c7bf Make sure we do not put random values in the .macro field of buttons
Otherwise, as soon as we set an action for a button, we get a nice
invalid free on cleanup.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-14 16:20:56 +01:00
Benjamin Tissoires
717505daef Test: add a set_button test
Which currently fails.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-14 16:20:56 +01:00
Benjamin Tissoires
8ede0b734f Test: make sure the macros ref/unref are tested
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-14 16:20:53 +01:00
Benjamin Tissoires
090851fce1 Test: add some generic tests for the buttons
Currently only ref/unref

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-14 16:20:50 +01:00
bentiss
164232bbbc Merge pull request #74 from whot/wip/macro-rewrite
Rewrite the macro API, which contains PR #72 (Some more cleanup) and PR #73 (Use error enums)
2016-03-14 11:07:28 +01:00
Peter Hutterer
917308d24d Document the DBus interface in the README
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-14 17:02:17 +10:00
Peter Hutterer
4054616190 Revamp the button macro code
The previous API was inconsistent and different to the rest of libratbag's
API. Replace with one where a macro is a separate struct, can be initialized
and then written to the device with a set_macro() call. The device copies the
contents, the macro is otherwise unmodified and can be re-used by the client
if need be.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-14 14:22:22 +10:00
Peter Hutterer
578142964d Change ratbag_find_driver to ratbag_assign_driver()
We don't do anything with the return value anyway and we do assign the driver
in this function. Rename it and return a boolean only.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-14 14:20:50 +10:00
Peter Hutterer
37bd43511b Switch to using an emum for error codes
Whenever we do something that may fail, return an enum of possible errors.

This also changes the API for ratbag_device_new_from_udev_device() to return
an error code now too instead of just NULL on failure.

Error numbers start at -1000 to avoid overlap with errnos, error_code() should
be used internally to avoid returning an errno.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-14 14:20:47 +10:00
Peter Hutterer
b53981c439 Add a few client error messages
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-14 11:54:46 +10:00
Peter Hutterer
bf8442a250 Use __cleanup__ to free the device name
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-14 11:54:46 +10:00
Peter Hutterer
198523fe8f Simplify validity checks with assert
Let the caller check that what they're passing in is correct, we don't have to
respond with sensible error codes here.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-14 11:54:46 +10:00
bentiss
7f7507a0dd Merge pull request #70 from whot/master
miscellaneous cleanups
2016-03-12 09:58:54 +01:00
bentiss
a08c8ebc60 Merge pull request #71 from cvuchener/fix-g500s-button-mapping
data: Fix G500s button labels and arrows

Fixes bug #69
2016-03-11 11:17:52 +01:00
Clément Vuchener
aa76cb5005 data: Fix G500s button labels and arrows
Changed button labels with their proper numbers and rotated arrows for wheel tilt buttons.
2016-03-11 11:07:01 +01:00
Peter Hutterer
be008cd86c Switch ratbag_get_num_resolutions() to return an unsigned
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 16:22:19 +10:00
Peter Hutterer
6f9aa5aa24 Coding style fixes
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 16:22:19 +10:00
Peter Hutterer
bfe6d446ad Comment typo fixes
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 16:22:15 +10:00
Peter Hutterer
855a6bd831 Fix error message on ENODEV
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 16:16:42 +10:00
Peter Hutterer
f5ff267c54 Improve a few error messages
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 16:16:39 +10:00
Peter Hutterer
014c4105f4 Remove superfluous check for a NULL udev device
If the udev device is NULL, we should fail a lot earlier

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 16:16:37 +10:00
Peter Hutterer
52fea6f3ce Remove superfluous check for strdup() ENOMEM
If that happens, strdup_safe() will abort.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 16:16:35 +10:00
Peter Hutterer
97eee9e0c5 ratbagd 0.1.2
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 12:02:19 +10:00
Peter Hutterer
5cc234e27e configure.ac: add missing AC_SUBST for the dbus config dir
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 11:27:16 +10:00
Peter Hutterer
2bbcc51a8b ratbagd 0.1.1
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 10:40:01 +10:00
Peter Hutterer
1824b977f7 Fix typo in README
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 09:17:34 +10:00
Peter Hutterer
99011fd3bf configure.ac: fix dbus install directories
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-11 09:16:48 +10:00
bentiss
43ab3c961e Merge pull request #68 from whot/wip/query-capability
Add a QUERY_CONFIGURATION capability
2016-03-10 14:31:39 +01:00
Peter Hutterer
f7523f000e Add a QUERY_CONFIGURATION capability
Designates a device that can be queried for the current configuration. Some
devices lack this ability so we rely on the caller to upload a known
configuration to the device before we can really proceed.

Set by default, devices without that capability should be the exception, not
the norm.

This patch just adds the capability, actual handling for this needs to be
added when we add the first device without this capability.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-10 16:53:55 +10:00
Peter Hutterer
346631cd75 Skip the manual NULL assignment after unref
libratbag guarantees that unref() always returns NULL

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-10 13:03:56 +10:00
Peter Hutterer
8517e103b0 ratbagd 0.1
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-10 12:05:40 +10:00
Peter Hutterer
815a81a610 Spice up the README a bit
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-10 12:05:40 +10:00
Peter Hutterer
20b9339620 README: update install instructions for dbus
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-10 12:05:40 +10:00
Peter Hutterer
2e93ac07f6 Update gitignore for a few things
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-10 12:05:40 +10:00
Peter Hutterer
f385622ec6 Makefile: add a comment marker for the dbus/systemd config files
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-10 12:05:40 +10:00
Peter Hutterer
de83186c3c Create the dbus directory in $builddir
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-10 12:05:40 +10:00
Peter Hutterer
fa0d57dd47 Install the dbus config as well
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-10 12:05:40 +10:00
Peter Hutterer
b164fa622f Change to a dbus-activated service
We don't shut down once activated but that doesn't mean we need to run when
never requested.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
2016-03-10 12:05:40 +10:00
bentiss
e4c654841b Merge pull request #3 from whot/master
fix cleanup code  and restart on abort
2016-03-09 09:41:26 +01:00
bentiss
a411248c7c Merge pull request #2 from whot/wip/pkg-cleanup
minor autotools cleanup
2016-03-09 09:39:45 +01:00
Peter Hutterer
a066203b4a systemd: fix typo in service file
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-09 14:08:20 +10:00
Peter Hutterer
819c70fa9a systemd: restart on abort
Now that we clean up properly, the most common crash should be fixed. Restart
on abort by default so ideally all we need to do is restart piper or whatever
consumes the API.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-09 13:45:03 +10:00
Peter Hutterer
9f9383e6ff profile: free the buttons/resolutions on profile cleanup
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-09 13:45:03 +10:00
Peter Hutterer
70222b3033 Use PKG_CHECK_VAR to check for the pkgdatadir
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-09 13:10:14 +10:00
Peter Hutterer
5e2d7de6c2 configure.ac: remove a couple of unused items
This configure.ac is largely a copy from systemd so it included things that we
don't use locally.
- we don't use grep, awk or ln
- AC_SYS_LARGEFILE and AC_CANONICAL_HOST are unecessary
- pkg.m4 - if that's missing you don't have pkgconfig installed. stop trying
  to build things then :)

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-09 13:04:22 +10:00
bentiss
765ee6c08c Merge pull request #67 from phomes/g9-read-dpi
hidpp10: Fix getting current resolution on G9
2016-03-04 09:02:55 +01:00
Peter Hutterer
aceef4d0d0 ratbagd 0.0.2
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-04 14:27:18 +10:00
Peter Hutterer
654956baa6 Fix distcheck
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-04 14:27:08 +10:00
Thomas Hindoe Paaboel Andersen
577a7c52fc hidpp10: Fix getting current resolution on G9
On the G9 the response to a 0x63 is a short report. The G9 does
not have individual resolutions for X and Y and thus only needs
to report a single value.

The set version of 0x63 should probably also be updated. However,
while my testing of such a change worked better than with long
reports there are still strange side effects of setting resolution,
like turning off all the LED's. LGS also seems to do a full profile
read/write instead of using 0x63 when changing the resolution.
2016-03-04 00:19:10 +01:00
Peter Hutterer
212ab08c15 device: export the full path to the SVG as well
Exporting just the filename requires the caller to know where that file sits.
That's useful for things like e.g. icons where callers may several sets to
chose from but here most callers will just use the default svg provided by
libratbag.

Export that, so that we don't have build-time dependencies in the callers just
for that single path.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-04 08:54:26 +10:00
bentiss
18a0be561f Merge pull request #1 from whot/python-bindings
Add a python module for ratbagd, replace ratbagctl with a python version
2016-03-03 15:30:19 +01:00
Peter Hutterer
0528981a0a Replace ratbagctl with a python version
It's a lot easier to interact with ratbagd from a python script than it is
with C code. Plus this means we test the bindings whenever we use ratbagctl.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-03 20:13:10 +10:00
Peter Hutterer
eac1e91638 Add a python module to talk to ratbagd
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-03 20:12:48 +10:00
Peter Hutterer
a8600faeb3 button: use dash, not underscore, for button type names
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-03 15:11:29 +10:00
Peter Hutterer
9c33eede5c Add a COPYING file
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-03 14:56:37 +10:00
Benjamin Tissoires
85acab4642 hidpp20: fix correct number of resolutions in the profile
There are only 5 resolutions per profile.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-02 10:54:13 +01:00
Benjamin Tissoires
0b5ccfeaed hidpp20: re-enable G303
The G303 shares the same profile than the G402/G502. The only difference
is an extension at the end of the profile, but they are compatible.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-02 10:50:19 +01:00
Benjamin Tissoires
f108ffa9ff hidpp20: remove internal project name
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-02 10:49:07 +01:00
Peter Hutterer
f233b6bf77 data: add a README on how do do the data files
And update all devices to match the the requirements

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-02 09:09:17 +01:00
Peter Hutterer
c95d8e05ca lur: Add a note that we're not doing OOM
Mostly inherited from our zalloc() implementation. But really,
if you have no memory left, should you be trying to talk to receivers?

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-02 09:08:58 +01:00
Peter Hutterer
b3237fd001 lur: change new_from_hidraw to return an errno
Makes debugging slightly more useful than just returning NULL.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-02 09:08:13 +01:00
Benjamin Tissoires
7704c102e4 lur: add lur_device_get/set_userdata
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
BT: move the functions in LIBLUR_0.4.0
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-02 09:08:05 +01:00
Peter Hutterer
55cb7447f9 lur: capitalize use of Logitech Unifying Receiver
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-03-02 14:40:41 +10:00
bentiss
77eaa70f77 Merge pull request #63 from phomes/G9-set-profile
hidpp10: write profile for G9
2016-03-01 23:45:30 +01:00
Thomas Hindoe Paaboel Andersen
70d864af09 hidpp10: write profile for G9 2016-03-01 23:37:54 +01:00
bentiss
15968c8e39 Merge pull request #62 from phomes/g9-features
hidpp10: Add button mappings for G9
2016-03-01 23:12:00 +01:00
bentiss
0558bd4101 Merge pull request #61 from phomes/cleanup
Follow up for #56
2016-03-01 23:10:56 +01:00
Thomas Hindoe Paaboel Andersen
3962ec7745 hidpp10: Add button mappings for G9
There are 10 buttons defined in the profile but only 9 physical
buttons. LGS also only allows to configure 9 of them. Button 5
is the "dead" button in the list.
2016-03-01 22:11:47 +01:00
Thomas Hindoe Paaboel Andersen
2164a3b549 libratbag: remove unused variable 2016-03-01 21:20:23 +01:00
Thomas Hindoe Paaboel Andersen
f0417a9535 hidpp10: Add definition for buttons on the G9 2016-03-01 21:20:18 +01:00
Thomas Hindoe Paaboel Andersen
9d9c42cc6a hidpp10: rename structs wrt _sym vs. _dual
Follow up for review in #56
2016-03-01 21:20:13 +01:00
Benjamin Tissoires
efd15a33fb configure.ac: libratbag 0.3
Soname bump for liblur and libratbag as they were not properly versioned.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-01 17:04:39 +01:00
Benjamin Tissoires
b804e117c1 hidpp20drv: fix segfault when a device is not powered on
We were returning 0 and libratbag tried to access the profiles afterward.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-01 16:59:32 +01:00
Benjamin Tissoires
b54d28ec1a tools: add a man for ratbag-command
Just duplicated the help.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-01 16:46:31 +01:00
Benjamin Tissoires
388dae76bc libratbag.sym: bump symbols version
0.1.0 never existed, the first release was 0.2 :)

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-01 15:35:33 +01:00
Benjamin Tissoires
19f87790bb Add liblur versioning and pkg-config
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2016-03-01 15:34:46 +01:00
Benjamin Tissoires
9aa8938eb8 hidpp20: profiles: make sure the mouse is in the profiles mode, not HID
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-01 15:05:50 +01:00
Benjamin Tissoires
25094daaed hidpp20: profiles: make sure device is compatible with our implementation
Get_Profile_Info gives valuable information to know if we are compatible
with a given device. Use it and bail out if we are not able to support
the device.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-01 14:57:19 +01:00
Benjamin Tissoires
39069623f2 hidpp20drv: bail out if the features are not working as expected
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-01 14:36:03 +01:00
Benjamin Tissoires
d5c74682f1 Add missing carriage return when the driver does not match
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-01 14:32:42 +01:00
Benjamin Tissoires
6cba2abd8e hidpp20: reorder profile initialization
Spec says the profile count is stored in byte 3 of
CMD_ONBOARD_PROFILES_GET_PROFILES_DESCR, so use this to allocate the proper
number of profiles.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-03-01 14:15:15 +01:00
Peter Hutterer
6c285fa2a1 button: emit property change events when we update a button
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-29 13:55:58 +10:00
Peter Hutterer
d6116e00bc ratbagd 0.0.1
This reverts the version number but ratbagd is nowhere near stable (because
libratbag itself isn't stable yet) and the dbus API can change at any time.
I'm not committing to keeping the current dbus API around for backwards
compatibility reasons.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-29 10:24:47 +10:00
Peter Hutterer
c54b382775 Require libratbag 0.2
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-29 09:59:51 +10:00
Peter Hutterer
f58b91a9b2 When setting a button action, update our internal state on success
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
2016-02-29 09:58:18 +10:00
Peter Hutterer
184e2b646c Add a systemd service file
We don't have dbus-activation yet, so just define it as a normal dbus service
for now.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-29 09:54:00 +10:00
Peter Hutterer
8a8379c7eb button: return -1 for invalid special values
Otherwise it looks like the function doesn't exist

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-25 15:23:58 +10:00
Peter Hutterer
8e8d827e00 Export button mappings over dbus
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-24 12:40:49 +10:00
Peter Hutterer
de08323609 Fix return for ratbag_button_get_special when we don't have a special action
Do what the doc says.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-24 10:51:28 +10:00
Peter Hutterer
5fcdfab2a6 Fix a doxygen ref
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-24 10:51:07 +10:00
Peter Hutterer
2fad2e3ccd resolution: mark the dpi/rate as changable
And make sure we actually save the new value too

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-22 13:29:02 +10:00
Peter Hutterer
4c66d7f5ba dbus: allow normal users to talk to ratbagd
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-22 09:42:47 +10:00
Peter Hutterer
124385fc31 ratbagctl: print all properties
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-19 15:32:33 +10:00
Peter Hutterer
5fe39f94ab ratbagctl: fix show-profile to handle the resolution objects
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-19 15:32:33 +10:00
Peter Hutterer
e8d1645150 ratbagctl: Switch ActiveProfile parsing to an integer
Fallout from c04f1b57aa

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-19 15:32:33 +10:00
Peter Hutterer
d1ea517f9a ratbagctl: drop DefaultProfile parsing
Dropped in d049c13062

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-19 15:32:33 +10:00
Peter Hutterer
c6627d6266 device: if we can't fetch the description or svg, return an empty string
Less exception handling needed on the client side.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-19 15:32:33 +10:00
Peter Hutterer
2138ab05e5 Replace fprintfs with a new log_error helper
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-19 15:32:33 +10:00
Peter Hutterer
ce3038fd8e Use streq over strcmp
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-19 15:32:33 +10:00
Peter Hutterer
555d00c9a8 ratbagd: if --verbose is specified, set libratbag to verbose too
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-19 15:32:33 +10:00
Peter Hutterer
12f013681a Add rudimentary for buttons
This just exports the button index so far and makes sure we can write clients
that deal with button objects instead of making stuff up to test for buttons.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-19 15:32:03 +10:00
Benjamin Tissoires
e07839bcee tools: remove no-install flag for lur-command
The resulting RPM complains about RPATH.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2016-02-18 14:25:18 +01:00
Peter Hutterer
7a498310e2 resolution: remove unused variables
Copy/paste leftover from the profile.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-18 15:14:15 +10:00
Peter Hutterer
9af58224fa resolution: export capabilities as dbus properties
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-18 11:14:13 +10:00
Peter Hutterer
61abdb57a7 device: export all capabilities as dbus properties
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-18 11:12:00 +10:00
Peter Hutterer
e0cb0c70e1 README: fix typo, this is ratbagd, not libratbag
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-18 09:12:36 +10:00
Benjamin Tissoires
420a05cabf README: Add instructions to have libsystemd >= 227
Works on F23. There are few dependencies pulled by configure that I did
not put there.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-18 09:12:33 +10:00
Peter Hutterer
262dd532e6 Add a log_verbose logging function for debug output
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-17 10:33:20 +10:00
Peter Hutterer
80ed6d1399 resolution: switch to an index for ActiveResolution/DefaultResolution
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-16 17:00:27 +10:00
Peter Hutterer
c04f1b57aa device: switch active profile over to return an index only
Returning the object path makes it harder for the client to identify which
object is the active one. A simple index into the devices array is easier to
handle.

Note: another approach was to have an IsActive property on the profile
directly. This would require updating all properties when the active profile
is changed, it's simpler to just have one index.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-16 17:00:27 +10:00
Peter Hutterer
7aca92f2b3 resolution: add support for setting resolutions and report rate
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-16 09:56:12 +10:00
Peter Hutterer
e199278d6f profile: switch DefaultResolution/ActiveResolution over to an object path
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-15 15:17:36 +10:00
Peter Hutterer
f2a55bca55 profile: add GetResolutionByIndex
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-15 15:17:35 +10:00
Peter Hutterer
bcd62aec6b resolutions: switch to using objects for resolutions
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-15 15:17:23 +10:00
Peter Hutterer
c4e4330bd1 profile: prefix the profile object path with a 'p'
sd_bus_encode_many() escaps elements starting with a digit so our 0 profile
has an object path of /.../_30. This is quite confusing during debugging.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-15 15:17:12 +10:00
Peter Hutterer
c70b53a162 profile: add support for setting the active profile
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-15 15:17:11 +10:00
Peter Hutterer
3262def07c device: add support for get-svg
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-15 15:17:11 +10:00
Peter Hutterer
d049c13062 device: drop default profile fetching, removed from libratbag
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2016-02-15 15:17:01 +10:00
Peter Hutterer
5ffed21e0e Remove cleanup func define for sd_bus_message_unref
Added to systemd in 4afd3348c7506dd1d36305b7bcb9feb8952b9d6b
2016-02-11 17:16:58 +10:00
David Herrmann
92ffda48ee Merge pull request #2 from whot/master
Add missing attributes.m4
2016-01-16 20:34:42 +01:00
Peter Hutterer
91a4a325ae Add missing attributes.m4
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2015-11-20 09:06:17 +10:00
David Herrmann
7cff5e58f0 Merge pull request #1 from bentiss/master
Add Readme and the policy file required
2015-10-15 11:01:52 +02:00
Benjamin Tissoires
17f0fc41d1 Add Readme and the policy file required
Or running ratbagd (as root) raises a "Permission denied".

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2015-10-14 11:34:27 -04:00
David Herrmann
958e86f67e ratbagctl: pick default device if none given for show-device
Lets use the new get_device_path() helper to allow calling show-device
without any argument. This follows what show-profile does already.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 19:02:12 +02:00
David Herrmann
acb0118897 ratbagctl: split get_profile_path()
Split get_profile_path() and provide an helper for get_device_path() on
its own.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 19:00:27 +02:00
David Herrmann
85a045c538 ratbagctl: move get_profile_path()
Move get_profile_path() up to make it available to other verbs as well.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 18:57:42 +02:00
David Herrmann
fbffe241e6 ratbagctl: add show-profile verb
New verb: show-profile

This simply prints a profile of a given device by querying ratbagd for
all information on the selected profile.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 18:56:25 +02:00
David Herrmann
765fd1565a ratbagd: expose resolutions on the bus
Add a new property to profiles that exposes resolutions on the bus. We
simply expose a dictionary entry for each resolution, hence, we can
easily extend it in the future. However, we avoid the full overhead of
real dbus objects, as there is no real need for it.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 18:55:03 +02:00
David Herrmann
1a677c0b1d ratbagd: add DBus methods to retrieve object paths
Add two new helpers to query ratbagd for the object-paths of a specific
object:
  GetDeviceByName: Return device given a device name
  GetProfileByIndex: Return a profile given a profile index

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 18:53:54 +02:00
David Herrmann
eeb8788413 ratbagctl: print the default profile
Include the default profile in 'show-device' output.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 16:35:00 +02:00
David Herrmann
c24ca68d63 ratbagd: expose 'DefaultProfile'
Add a new property that exposes the default profile.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 16:34:32 +02:00
David Herrmann
e189dc3b1f ratbagd: never return ENODATA for Get()
We should always provide valid data if requested via Get().
Unfortunately, DBus still does not include 'maybe' types, so we just
return something that is clearly invalid data.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 16:33:30 +02:00
David Herrmann
ff8f391e91 ratbagctl: implement list-devices and show-device
Two new verbs for ratbagctl:
 - list-devices: list all available devices
 - show-device: show detailed information about a single device

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 16:23:51 +02:00
David Herrmann
0a9c2824ba ratbagd: expose profile information
Expose a list of profiles, including a tag for the active profile, from
within each ratbagd device via DBus.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 16:23:51 +02:00
David Herrmann
dc37d3bf62 ratbagd: ignore NULL profiles
Never return information about non-existant profiles. Just skip NULL
profiles silently, when iterating.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 16:23:51 +02:00
David Herrmann
2780e6f3d7 ratbagd: simplify profile DBus paths
Refrain from hierarchical paths and instead use object prefixes with
unique paths each. This avoids several nasty DBus-enforced behaviors
regarding deep hierarchies. Furthermore, it keeps the names shorter,
which is always a nice side-effect.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 16:23:51 +02:00
David Herrmann
260df58856 ratbagd: profile profile accessors
Provide two new profile accessors that return the path and index of a
profile. This also requires us to cache the dbus path in the profile
itself.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 16:23:51 +02:00
David Herrmann
cba28a97a0 ratbagd: add ratbagd_device_get_name()
New helper to return the name of a device. This will be required to build
hierarchical paths in the sub-classes, like profiles.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 16:23:51 +02:00
David Herrmann
eca2203603 ratbagd: add manager interface
Instead of relying on the cricket ObjectManager interface, use our own
manager interface. This gets rid of ridiculous recursive behavior of the
ObjectManager interface and instead allows us to control the behavior.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 14:09:23 +02:00
David Herrmann
e926212554 ratbagd: remove ratbagd_device_list()
This is no longer needed. Use the device-iterator in ratbagd.c directly
and then drop ratbagd_device_list().

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 13:25:05 +02:00
David Herrmann
65463b6efb ratbagd: add device iterator
Add iterators for ratbagd_device objects.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 13:22:13 +02:00
David Herrmann
9ac799bc95 ratbagd: add ratbagd_device_get_path()
Add helper to retrieve the device path from outside of ratbagd-device.c.
This will be needed for the non-object-manager based enumerators and
signals introduced to org.freedesktop.ratbag1.Manager.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 13:21:27 +02:00
David Herrmann
7e4d757bdf ratbagd: cache device path
Cache device path in device->path and avoid re-calculating it all the
time.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-29 13:09:45 +02:00
David Herrmann
1a8037cd6c ratbagd: expose profiles on the bus
Profiles are static children of devices. Expose them as 2nd-level objects
underneath their parent devices on the bus.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-26 18:53:55 +02:00
David Herrmann
dbc8a37b56 ratbagd: move dbus device management
Make sure the device management (lookup and enumeration and registration)
is done from the parent, not the child. Hence, move it into ratbagd.c over
ratbagd-device.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-26 18:07:18 +02:00
David Herrmann
b1851ada93 ratbagd: split dbus handling from object handling
Make sure our dbus callbacks to not contain any logic themselves. They
should be simple wrappers around our internal object handling.

This also makes use of the new sd-bus pat helpers, to-be-released with
systemd-v227 next week.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-26 18:02:00 +02:00
David Herrmann
c0799b0c1d ratbagd: drop UNPRIVILEGED from properties
This does not work, we cannot flag properties as UNPRIVILEGED. Drop it.
Lets figure out later what exactly we want as privilege level to
enumerate and query device properties.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-26 17:59:58 +02:00
David Herrmann
e461710811 ratbagd: add basic device properties
Add two device properties:
  - Id: Returns the string ID to identify the device
  - Description: Returns the ratbag-device name

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-25 16:27:45 +02:00
David Herrmann
bea8e89cc7 ratbagd: create libratbag_device contexts
Make sure to create libratbag_device contexts for each device we add. If
libratbag returns NULL, we just ignore the devices. Otherwise, we keep
the context around and manage the device.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-25 16:05:26 +02:00
David Herrmann
17d66153b0 ratbagd: ignore anything but event devices
We are only interested in evdev, so ignore anything but input-event
devices.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-25 15:57:14 +02:00
David Herrmann
8ca0c11154 ratbagd: keep libratbag context around
Make sure the ratbagd context has a reference to a valid libratbag
context at all times. We need this to access libratbag devices, so lets
reference on in our own context.

This also adds a dependency for libratbag via autotools.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-25 12:51:11 +02:00
David Herrmann
c4afc3d5c0 shared: add safe_close()
Add safe_close() helper that makes close(2) a bit nicer to use. This also
introduces PROTECT_ERRNO, which makes sure a function does not clutter
errno if called.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-25 12:49:44 +02:00
David Herrmann
c22d51e439 build: be more consistent in variable names
Rename UDEV_* to LIBUDEV_* to be more consistent regarding other
pkg-build dependencies.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-25 12:35:43 +02:00
David Herrmann
aa052ae005 Import initial components
This adds two new components:

1) ratbagd
   The ratbagd daemon is a very basic dbus daemon that provides an
   org.freedesktop.DBus.ObjectManager based enumeration and modification
   API for libratbag devices. So far, all it does is enumerate input
   devices on the bus. However, this will be extended to advertise full
   libratbag hierarchies on the bus and allow clients to modify
   properties via DBus.

   So far, it is root-only, but the DBus policy can be losened and PolKit
   support can be added.

2) ratbagctl
   A very basic command-line tool to manage ratbagd. It's basically a
   command-line interface for the DBus API of ratbagd.

   So far, it has no hookup for anything. This will be added once ratbagd
   gains further DBus APIs.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-24 20:05:27 +02:00
David Herrmann
3f0b41ec3c Initialize ratbagd repository
Initial commit for the ratbagd project, a dbus daemon to manage libratbag
devices and proxy user accesses.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2015-09-24 16:45:22 +02:00
117 changed files with 25513 additions and 12735 deletions

3
.flake8 Normal file
View File

@@ -0,0 +1,3 @@
[flake8]
ignore = E402,E501
exclude = .git,__pycache__,build,data,piper/piper.py

41
.gitignore vendored
View File

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

View File

@@ -1,4 +1,5 @@
Copyright © 2015 Red Hat, Inc.
Copyright © 2015-2017 Red Hat, Inc.
Copyright © 2015 David Herrmann <dh.herrmann@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),

303
DBusInterface.md Normal file
View File

@@ -0,0 +1,303 @@
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

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

View File

@@ -1,5 +1,3 @@
/*!@mainpage
libratbag
=========
@@ -7,11 +5,66 @@ libratbag is a configuration library for gaming mice. It provides a generic
way to access the various features exposed by these mice and abstracts away
hardware-specific and kernel-specific quirks.
Architecture
------------
libratbag exports the devices over a DBus daemon called **ratbagd**, see the
[ratbagd DBus Interface description](DBusInterface.md).
libratbag has two main components, the front-end API and wrapper code, and
the back-end HW-specific drivers:
Compiling libratbag
-------------------
libratbag uses the [meson build system](http://mesonbuild.com) which in
turn uses ninja to invoke the compiler. Run the following commands to clone
libratbag and initialize the build:
git clone https://github.com/libratbag/libratbag.git
cd libratbag
meson builddir --prefix=/usr/
And to build or re-build after code-changes, run:
ninja -C builddir
sudo ninja -C builddir install
Note: `builddir` is the build output directory and can be changed to any
other directory name. To set configure-time options, use e.g.
mesonconf builddir -Denable-documentation=false
Run `mesonconf builddir` to list the options.
Running ratbagd as DBus-activated systemd service
-------------------------------------------------
To run ratbagd, simply run it as root `sudo ratbagd`. However,
ratbagd is intended to run as dbus-activated systemd service and installs
the following files:
/usr/share/dbus-1/system.d/org.freedesktop.ratbag1.conf
/usr/share/dbus-1/system-services/org.freedesktop.ratbag1.conf
/usr/share/systemd/system/ratbagd.service
These files are installed into the prefix by `ninja install`, see also the
configure-time options `-Dsystemd-unit-dir` and `-Ddbus-root-dir`.
Developers are encouraged to simply symlink to the files in the git
repository.
For the files to take effect, you should run
sudo systemctl daemon-reload
sudo systemctl reload dbus.service
And finally, to enable the service:
sudo systemctl enable ratbagd.service
This places the required symlink into the systemd directory so that dbus
activation is possible.
libratbag Internal Architecture
-------------------------------
libratbag has two main components, libratbag and ratbagd.
The former is a library to access the devices. It consists of
the front-end API and wrapper code, and the back-end HW-specific drivers:
+-----+ +-----+ +-----------+ +----------+
| app | -> | API | -> | hw-driver | -> | protocol | -> device
@@ -24,8 +77,8 @@ implemented. The HW driver then only accesses the bits required for
libratbag. This allows us to optionally export the protocol as separate
library in the future, if other projects require it.
Adding Devices
--------------
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
@@ -92,6 +145,4 @@ libratbag is licensed under the MIT license.
See the COPYING file for the full license information.
[![Build Status](https://semaphoreci.com/api/v1/projects/7905244a-c0b5-468b-9071-d846de3ce9f1/545192/badge.svg)](https://semaphoreci.com/libratbag/libratbag)
*/
[![Build Status](https://circleci.com/gh/libratbag/libratbag.svg?style=shield&circle-token=d7c782e10d2d934b176da754f11b5105ea074f4a)](https://circleci.com/gh/libratbag/libratbag)

View File

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

135
circle.yml Normal file
View File

@@ -0,0 +1,135 @@
libratbag_references:
default_settings: &default_settings
working_directory: ~/libratbag
build_default: &build_default
name: Build
command: |
rm -rf build
meson build ${MESON_PARAMS}
mesonconf build
ninja -v -C build ${NINJA_ARGS}
environment:
MESON_PARAMS: --prefix=/usr
build_and_test: &build_and_test
run:
<<: *build_default
name: Build and test
environment:
NINJA_ARGS: test
install: &install
run:
name: Installing
command: ninja -C build install
check_uninstall: &check_uninstall
run:
name: Checking if any files are left after uninstall
command: |
PREFIX=/root/test_install
# workaround until https://github.com/mesonbuild/meson/pull/2033 is merged
# and a new release appears
git clone https://github.com/whot/meson -b wip/remove-directories-on-uninstall
./meson/meson.py build_install --prefix=$PREFIX
ninja -C build_install install
ninja -C build_install uninstall
if [ -d $PREFIX ]
then
tree $PREFIX
exit 1
fi
export_logs: &export_logs
store_artifacts:
path: ~/libratbag/build/meson-logs
ninja_scan_build: &ninja_scan_build
run:
<<: *build_default
name: Ninja scan-build
environment:
NINJA_ARGS: scan-build
start_dbus: &start_dbus
run:
name: Start dbus daemon
command: |
mkdir /run/dbus
/usr/bin/dbus-daemon --system --fork
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
fedora_settings: &fedora_settings
<<: *default_settings
steps:
- *fedora_install
- checkout
- *start_dbus
- *build_and_test
- *install
- *check_uninstall
- *export_logs
ubuntu_settings: &ubuntu_settings
<<: *default_settings
steps:
- run:
name: Install prerequisites
command: |
apt-get update
apt-get install -y software-properties-common
add-apt-repository universe
apt-get update
apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev doxygen graphviz valgrind python3-gi python3-evdev
- checkout
- *start_dbus
- *build_and_test
- *install
- *check_uninstall
- *export_logs
scan_build_run: &scan_build_run
<<: *default_settings
steps:
- *fedora_install
- run:
name: Install clang and find
command: dnf install -y clang-analyzer findutils
- checkout
- *ninja_scan_build
- *export_logs
- 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)
version: 2
jobs:
fedora_rawhide:
<<: *fedora_settings
docker:
- image: fedora:rawhide
fedora_latest:
<<: *fedora_settings
docker:
- image: fedora:latest
ubuntu_17_04:
<<: *ubuntu_settings
docker:
- image: ubuntu:zesty
scan_build:
<<: *scan_build_run
docker:
- image: fedora:latest
workflows:
version: 2
compile_and_test:
jobs:
# - fedora_rawhide
- scan_build
- ubuntu_17_04
- fedora_latest

View File

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

View File

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

17
data/README.md Normal file
View File

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

15
data/default/README.md Normal file
View File

@@ -0,0 +1,15 @@
libratbag default theme
====================
Requirements
------------
- Canvas size: 750x750 pixels
- Two layers in the final SVG: a lower layer named "Device" with the device
itself, "Leaders" with the buttons and leader-lines
- The device should be approximately centered on the canvas and occupy a
meaningful portion of the canvas without making it look cramped
- Button labels must be in 12pt Sans, starting with Button0
- Wheel buttons may be labeled separately if required (look at the Etekcity
for an example)

436
data/default/etekcity.svg Normal file
View File

@@ -0,0 +1,436 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg2"
viewBox="0 0 750.00005 750"
height="750"
width="750"
sodipodi:docname="etekcity.svg"
inkscape:version="0.92+devel 15739 custom">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
inkscape:document-rotation="0"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1053"
id="namedview75"
showgrid="false"
inkscape:snap-global="false"
inkscape:zoom="0.90533333"
inkscape:cx="249.19702"
inkscape:cy="368.25832"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs4">
<linearGradient
id="linearGradient4449">
<stop
style="stop-color:#a6a6a6;stop-opacity:1"
offset="0"
id="stop4451" />
<stop
style="stop-color:#484848;stop-opacity:1"
offset="1"
id="stop4453" />
</linearGradient>
<linearGradient
id="linearGradient4336">
<stop
style="stop-color:#898989;stop-opacity:1;"
offset="0"
id="stop4338" />
<stop
style="stop-color:#898989;stop-opacity:0;"
offset="1"
id="stop4340" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient4449"
id="linearGradient3237-3-3"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.5735897,0,0,1.5735897,515.00096,570.33442)"
x1="31.968746"
y1="18.546629"
x2="31.968746"
y2="29.577879" />
<linearGradient
y2="29.577879"
x2="31.968746"
y1="18.546629"
x1="31.968746"
gradientTransform="matrix(1.5735898,0,0,-1.5735898,515.00096,691.37898)"
gradientUnits="userSpaceOnUse"
id="linearGradient4593-2-5"
xlink:href="#linearGradient4449" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4336"
id="linearGradient4784"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.79176032,0,0,0.79176032,-529.7793,277.79557)"
x1="97.085503"
y1="74.08123"
x2="95.471596"
y2="200.83122" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4449"
id="linearGradient4786"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.344964,0,0,1.344964,-441.46063,314.40367)"
x1="31.968746"
y1="18.546629"
x2="31.968746"
y2="29.577879" />
</defs>
<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></dc:title>
<cc:license
rdf:resource="https://opensource.org/licenses/MIT" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Device">
<g
transform="matrix(1.0968619,0,0,1.0968619,-53.374562,-35.91136)"
style="display:inline;stroke-width:0.91169178"
id="layer6">
<g
id="layer2-9"
style="display:inline;stroke-width:0.91169178"
transform="translate(-0.549249,-6.732224)">
<g
style="display:inline;stroke-width:0.91169178"
transform="matrix(1.066667,0,0,1.066667,706.52353,-41.851899)"
id="layer5">
<g
style="display:inline;stroke-width:0.91169178"
transform="translate(102.62379,-59.6133)"
id="g3893">
<g
id="g4271"
style="display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.91169178"
transform="matrix(0.79176032,0,0,0.79176032,-628.41671,192.05523)">
<path
inkscape:connector-curvature="0"
id="path4249"
d="m 421.23361,409.14757 c 4.4349,4.09496 9.16721,10.86185 32.57742,27.27412 4.08118,24.47294 2.40765,48.35999 1.01015,67.42768 -1.3975,19.06769 -9.52288,85.7367 -30.55711,85.7367 l -41.92133,-6.18719 8.9651,-171.72593 32.38801,-150.26019 27.05315,-2.27284 -2.70689,27.21098 -26.19294,53.47495 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.91169178" />
<use
height="1013.322"
width="1469.2054"
transform="matrix(-1,0,0,1,580.95786,0)"
id="use4266"
xlink:href="#path4249"
y="0"
x="0"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.91169178" />
</g>
<path
inkscape:connector-curvature="0"
style="display:inline;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.91169178px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -268.43146,403.53037 c -1.07689,19.01234 -1.64984,38.40264 -3.69907,56.68573 -2.36798,4.75208 -4.96718,7.8857 -7.59809,10.79728 h -13.59657 l 5.59859,-67.18309 z"
id="path4251" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.91169178px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -281.12826,473.7127 -0.29993,6.19844 7.09822,5.79854 -2.19945,30.1924 -8.99773,9.59758 -0.29993,28.99271 -2.3994,3.59909 h -14.59632 v -84.47873 z"
id="path4253" />
<use
style="display:inline;stroke-width:0.91169178"
x="0"
y="0"
xlink:href="#path4251"
id="use4275"
transform="matrix(-1,0,0,1,-698.21665,-85.740335)"
width="1469.2054"
height="1013.322" />
<ellipse
transform="matrix(0.79176032,0,0,0.79176032,-628.41671,124.43258)"
style="display:inline;fill:#000000;fill-opacity:1;stroke:#ff0000;stroke-width:1.15147448;stroke-opacity:1"
id="path4243"
cx="290.47894"
cy="231.07376"
rx="57.5"
ry="36.875" />
<use
style="display:inline;stroke-width:0.91169178"
x="0"
y="0"
xlink:href="#path4253"
id="use4277"
transform="matrix(-1,0,0,1,-698.21665,-85.740335)"
width="1469.2054"
height="1013.322" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:#282828;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.91169178px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -401.08313,328.79009 0.26444,-40.70143 c -39.18883,7.37119 -44.1411,4.0242 -51.69484,0.84743 -19.57977,14.34842 -45.06471,30.13362 -73.61979,38.63234 -2.47282,19.58198 -2.71663,46.75085 -2.09075,66.56663 19.0204,10.56139 17.6053,33.92046 19.25741,48.34377 1.65211,14.4233 7.20626,94.24576 7.20626,94.24576 l -2.59797,21.72393 23.30745,43.04888 c -0.16213,7.00604 -0.46719,14.87323 -3.03251,37.40758 -7.15647,2.04323 -13.81042,1.56652 -20.02442,-1.10259 0.0912,10.91499 -0.68305,21.82736 0.56908,32.68021 11.24444,13.4205 16.39645,27.09393 56.20571,56.18715 l 6.498,11.63207 c 13.0739,7.46592 23.16933,11.65186 42.40866,14.95221 19.23933,-3.30035 29.33476,-7.48629 42.40866,-14.95221 l 6.498,-11.63207 c 39.80926,-29.09322 44.96127,-42.76665 56.2057,-56.18715 1.25213,-10.85285 0.47635,-21.76522 0.56754,-32.68021 -6.214,2.66911 -12.86641,3.14582 -20.02288,1.10259 -2.56532,-22.53435 -2.87037,-30.40154 -3.0325,-37.40758 l 23.3059,-43.04888 -2.59642,-21.72393 c 0,0 5.55415,-79.82246 7.20625,-94.24576 1.65211,-14.42331 0.23546,-37.78238 19.25586,-48.34377 0.62589,-19.81578 0.38362,-46.98465 -2.08919,-66.56663 -28.55509,-8.49872 -54.04157,-24.28392 -73.62134,-38.63234 -7.55373,3.17677 -12.50447,6.52376 -51.69329,-0.84743 l 0.26444,40.70143 z"
id="path3441" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:none;stroke:#ff0000;stroke-width:0.91169178px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -398.45459,328.52925 V 446.37959"
id="path4289" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.91169178px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -390.51793,495.35976 13.14911,20.077 h 24.31724 l -19.22802,-64.1898 -26.14665,-13.56354 -26.14819,13.56354 -19.22802,64.1898 h 24.31879 l 13.14909,-20.077 z"
id="path4247" />
<rect
style="display:inline;fill:#000000;fill-opacity:1;stroke:#ff0000;stroke-width:0.91169178;stroke-opacity:1"
id="rect4245"
width="25.025282"
height="59.523411"
x="-410.93967"
y="334.18024"
rx="3.2500002"
ry="3.2500002" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:url(#linearGradient4784);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.91169178"
d="m -408.76059,297.49061 c -14.10207,2.39851 -28.95919,4.88556 -42.94836,0.51186 -20.52977,14.9081 -42.9383,27.51676 -67.08004,35.5287 -1.79569,18.70085 -2.21232,37.56904 -1.4629,56.27529 14.36611,10.46486 18.14376,29.44442 18.67596,46.17572 1.42879,19.10333 3.31403,38.21183 4.78767,57.33922 l 94.06329,0.0603 v -99.67829 h -5.64129 c -1.42556,0 -2.57322,-1.14766 -2.57322,-2.57323 v -54.37631 c 0,-1.42556 1.14766,-2.57322 2.57322,-2.57322 h 2.21136 c -2.89117,-5.43908 -2.75569,-17.98166 -2.75569,-23.06002 0,-6.57742 0.14421,-9.08623 0.15,-13.63002 z"
id="path4311" />
<use
style="display:inline;stroke-width:0.91169178"
x="0"
y="0"
xlink:href="#path4311"
id="use4348"
transform="matrix(-1,0,0,1,-796.64968,2.010815e-6)"
width="1469.2054"
height="1013.322" />
<circle
transform="matrix(0.79176032,0,0,0.79176032,-529.7793,210.17291)"
style="display:inline;fill:#898989;fill-opacity:1;stroke:none;stroke-width:0.91169178"
id="path4352"
cx="165.89903"
cy="194.21156"
r="13.13577" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.91169178"
d="m -420.59834,454.5406 c -5.60675,18.71514 -11.21351,37.43027 -16.82027,56.1454 5.1217,0 10.2434,0 15.3651,0 4.38485,-6.69216 8.76766,-13.38566 13.15375,-20.077 6.98254,0 13.96508,0 20.94762,0 4.38609,6.69134 8.76889,13.38484 13.15374,20.077 5.12118,0 10.24237,0 15.36355,0 -5.60676,-18.71565 -11.21352,-37.43129 -16.82027,-56.14694 -7.39028,-3.83406 -14.78056,-7.66812 -22.17083,-11.50218 -7.39079,3.83457 -14.78159,7.66915 -22.17239,11.50372 z"
id="path4357" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.91169178"
d="m -521.43644,406.56488 c -1.77374,0.0227 -3.15566,0.4222 -3.55518,1.76754 l 0.11752,2.1727 c 1.24058,16.62081 0.75876,33.65011 3.93561,49.99997 1.98359,2.87617 3.63732,8.09685 7.96709,7.20935 1.8046,-0.22471 4.69928,1.07511 6.16243,-0.0881 -0.0876,-1.12531 -0.18047,-2.37689 -0.26598,-3.46086 -1.38363,-12.51596 -2.3414,-25.11574 -3.33096,-37.70913 -0.77697,-6.57243 -2.15369,-13.39954 -5.30263,-19.41823 -1.9185,-0.16721 -4.00601,-0.49523 -5.7279,-0.4732 z"
id="use4396" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.91169178"
d="m -506.09917,476.83515 c -1.97837,0.009 -3.95674,0.0188 -5.93511,0.0279 0.85216,4.85149 -4.04309,7.59388 -6.85213,10.37484 -0.6345,6.02214 1.09923,12.03757 0.98197,18.13626 0.3064,4.54843 -0.35793,10.2138 4.18921,12.95425 3.89072,2.9778 6.76537,7.60716 5.65521,12.64652 0.43285,7.84497 -0.8119,15.99277 0.9062,23.38167 1.01515,0.57035 2.08809,0.77358 3.18869,0.80413 l 2.20518,-18.43626 c 0,0 -2.26899,-32.57611 -4.33922,-59.88925 z"
id="use4398" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.91169178"
d="m -271.77677,406.75044 c -3.09643,0.0485 -6.19285,0.0969 -9.28928,0.14537 -3.15953,5.97283 -4.55856,12.75131 -5.34747,19.29452 l -3.45931,41.48855 c 3.94759,0.0294 9.07538,1.78551 11.24547,-2.81911 5.80456,-6.57393 3.74439,-15.87475 4.98871,-23.78837 0.76732,-11.43639 1.23856,-22.86533 1.86188,-34.32096 z"
id="path4404" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.91169178"
d="m -288.21508,476.67123 c -0.84134,0.006 -1.68687,0.0599 -2.53457,0.11907 -2.07117,27.32261 -4.3423,59.9341 -4.3423,59.9341 l 2.20826,18.47337 c 1.29581,-0.023 2.52331,-0.3133 3.58612,-1.2263 1.08603,-9.86486 -0.19804,-20.31797 0.95414,-30.09153 3.59307,-4.65204 10.46727,-8.17451 9.15627,-15.08365 0.31411,-7.28513 1.50719,-14.93066 1.3129,-22.00692 -3.27092,-2.21915 -7.90201,-5.21182 -6.84904,-9.77793 -1.14795,-0.27038 -2.31544,-0.34912 -3.49178,-0.34021 z"
id="path4406" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:url(#linearGradient4786);fill-opacity:1;stroke:none;stroke-width:0.91169178"
d="m -398.42702,334.8881 -2.54284,4.40528 -2.54281,4.40266 h 2.90008 v 9.31229 l 2.18557,-1.2609 2.18556,1.2609 v -9.31229 h 2.90008 l -2.54283,-4.40266 z"
id="path4441" />
<use
style="display:inline;stroke-width:0.91169178"
x="0"
y="0"
xlink:href="#path4441"
id="use4460"
transform="matrix(1,0,0,-1,-9.1690072e-7,727.78645)"
width="1469.2054"
height="1013.322" />
</g>
</g>
</g>
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Leaders">
<path
id="path4211"
d="M 292.70532,245.09234 251.78243,107.31863"
style="display:inline;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
id="path4215"
d="M 459.12507,234.17958 530.45112,110.63926"
style="display:inline;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
id="path4227"
d="m 136.73079,278.9931 99.36454,68.40648"
style="display:inline;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
id="path4229"
d="M 134.68465,353.33635 242.9158,425.83512"
style="display:inline;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
id="path4546"
d="M 373.8779,389.96445 480.48362,283.35873 h 88.33698"
style="display:inline;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
id="path4221"
d="M 568.78,356.23193 H 511.64278"
style="display:inline;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
id="path4225"
d="M 568.89967,423.74332 H 509.44904"
style="display:inline;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:url(#linearGradient3237-3-3);fill-opacity:1;stroke:none;stroke-width:1.06666708"
d="m 565.3497,594.30093 -2.97509,5.1541 -2.97505,5.15104 h 3.39305 v 10.89527 l 2.55709,-1.47523 2.55708,1.47523 v -10.89527 h 3.39306 l -2.97509,-5.15104 z"
id="path4556"
inkscape:connector-curvature="0" />
<path
id="use4558"
d="m 565.3497,667.41248 -2.97509,-5.15413 -2.97505,-5.15105 h 3.39305 v -10.89527 l 2.55709,1.47523 2.55708,-1.47523 v 10.89527 h 3.39305 l -2.97508,5.15105 z"
style="display:inline;fill:url(#linearGradient4593-2-5);fill-opacity:1;stroke:none;stroke-width:1.06666708"
inkscape:connector-curvature="0" />
<text
id="text4473"
y="92.597137"
x="200.24596"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666708"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666708"
id="tspan4217"
y="92.597137"
x="200.24596">Button0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666708"
x="519.96796"
y="103.0135"
id="text4477"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666708"
id="tspan4479"
x="519.96796"
y="103.0135">Button1</tspan></text>
<text
id="text4481"
y="36.971676"
x="375.0303"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666708"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666708"
y="36.971676"
x="375.0303"
id="tspan4483">Button2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666708"
x="81.078667"
y="268.62354"
id="text4485"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666708"
id="tspan4487"
x="81.078667"
y="268.62354">Button3</tspan></text>
<text
id="text4489"
y="347.68259"
x="77.313698"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666708"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666708"
y="347.68259"
x="77.313698"
id="tspan4491">Button4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666708"
x="571.02142"
y="290.06454"
id="text4493"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666708"
id="tspan4495"
x="571.02142"
y="290.06454">Button5</tspan></text>
<text
id="text4497"
y="359.25067"
x="580.3692"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666708"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666708"
y="359.25067"
x="580.3692"
id="tspan4499">Button6</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666708"
x="574.50854"
y="428.69608"
id="text4501"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666708"
id="tspan4503"
x="574.50854"
y="428.69608">Button7</tspan></text>
<text
id="text4505"
y="613.29852"
x="584.23242"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666708"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666708"
y="613.29852"
x="584.23242"
id="tspan4507">Button8</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666708"
x="584.95984"
y="662.38556"
id="text4509"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.54979134px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666708"
id="tspan4511"
x="584.95984"
y="662.38556">Button9</tspan></text>
<path
id="path4213"
d="m 374.55111,266.91789 h 30.01011 V 45.934283"
style="display:inline;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -0,0 +1,754 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg8"
version="1.1"
viewBox="0 0 198.4375 198.43751"
height="750"
width="750"
sodipodi:docname="logitech-g300.svg"
inkscape:version="0.92+devel 15739 custom">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
inkscape:document-rotation="0"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1053"
id="namedview127"
showgrid="false"
inkscape:zoom="0.90533333"
inkscape:cx="375"
inkscape:cy="375"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" />
<defs
id="defs2">
<linearGradient
id="linearGradient7649">
<stop
style="stop-color:#f5f5f5;stop-opacity:1"
offset="0"
id="stop7643" />
<stop
id="stop7645"
offset="0.42190266"
style="stop-color:#030303;stop-opacity:1" />
<stop
style="stop-color:#ededed;stop-opacity:1"
offset="1"
id="stop7647" />
</linearGradient>
<linearGradient
id="linearGradient5366-6">
<stop
id="stop5360"
offset="0"
style="stop-color:#f5f5f5;stop-opacity:1" />
<stop
style="stop-color:#030303;stop-opacity:1"
offset="0.51176381"
id="stop5362" />
<stop
id="stop5364"
offset="1"
style="stop-color:#ededed;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient4649">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4645" />
<stop
id="stop4651"
offset="0.49026167"
style="stop-color:#a2a2a2;stop-opacity:1" />
<stop
style="stop-color:#000000;stop-opacity:1"
offset="1"
id="stop4647" />
</linearGradient>
<linearGradient
id="linearGradient4581-3">
<stop
id="stop4577"
offset="0"
style="stop-color:#000000;stop-opacity:1;" />
<stop
id="stop4579"
offset="1"
style="stop-color:#ececec;stop-opacity:1" />
</linearGradient>
<linearGradient
gradientUnits="userSpaceOnUse"
y2="43.886501"
x2="111.31824"
y1="50.580582"
x1="106.2162"
id="linearGradient6815-1"
xlink:href="#linearGradient4581-3"
gradientTransform="matrix(0.88930424,0,0,0.88930424,24.034848,15.217114)" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="35.112171"
x2="100.22734"
y1="42.556988"
x1="96.357521"
id="linearGradient6645-6"
xlink:href="#linearGradient4581-3"
gradientTransform="matrix(0.88930424,0,0,0.88930424,24.034848,15.217114)" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="73.771301"
x2="50.455471"
y1="83.415108"
x1="40.34473"
id="linearGradient5376-4"
xlink:href="#linearGradient4581-3"
gradientTransform="matrix(0.88930424,0,0,0.88930424,24.034848,15.217114)" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="71.838379"
x2="45.254807"
y1="76.173668"
x1="35.011879"
id="linearGradient5354-5"
xlink:href="#linearGradient4581-3"
gradientTransform="matrix(0.88930424,0,0,0.88930424,24.034848,15.217114)" />
<linearGradient
y2="67.095848"
x2="91.018227"
y1="75.713417"
x1="82.07766"
gradientTransform="matrix(1.1187372,0,0,1.1567117,-4.7672285,-6.6331718)"
gradientUnits="userSpaceOnUse"
id="linearGradient5326-4"
xlink:href="#linearGradient5366-6" />
<linearGradient
gradientTransform="matrix(0.99489774,0,0,1.0286686,19.795332,9.3182056)"
y2="64.980942"
x2="82.750046"
y1="75.018211"
x1="84.70034"
gradientUnits="userSpaceOnUse"
id="linearGradient5320-4"
xlink:href="#linearGradient4581-3" />
<linearGradient
y2="63.717323"
x2="77.788185"
y1="73.272537"
x1="79.893219"
gradientUnits="userSpaceOnUse"
id="linearGradient5336-7"
xlink:href="#linearGradient4581-3"
gradientTransform="matrix(0.88930424,0,0,0.88930424,24.034848,15.217114)" />
<linearGradient
gradientTransform="matrix(0.88930424,0,0,0.88930424,24.07419,15.177773)"
gradientUnits="userSpaceOnUse"
y2="43.694389"
x2="69.186096"
y1="185.06635"
x1="148.15651"
id="linearGradient4583-3"
xlink:href="#linearGradient4581-3" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="151.89273"
x2="35.842487"
y1="116.10757"
x1="78.975082"
id="linearGradient4608-6"
xlink:href="#linearGradient4581-3"
gradientTransform="matrix(0.88930424,0,0,0.88930424,24.034848,15.217114)" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="105.68479"
x2="160.22058"
y1="113.20518"
x1="149.38902"
id="linearGradient4597-8"
xlink:href="#linearGradient4581-3"
gradientTransform="matrix(0.88930424,0,0,0.88930424,24.034848,15.217114)" />
<linearGradient
gradientTransform="translate(5.7351308,-2.1927033)"
y2="57.257454"
x2="79.269432"
y1="64.032951"
x1="70.218117"
gradientUnits="userSpaceOnUse"
id="linearGradient9255"
xlink:href="#linearGradient4649" />
<linearGradient
gradientTransform="translate(5.7351308,-2.1927033)"
y2="67.40316"
x2="91.913757"
y1="75.838074"
x1="83.709564"
gradientUnits="userSpaceOnUse"
id="linearGradient9257"
xlink:href="#linearGradient5366-6" />
<linearGradient
y2="76.867943"
x2="49.128342"
y1="84.263214"
x1="40.247364"
gradientTransform="translate(5.6466558,-1.9272773)"
gradientUnits="userSpaceOnUse"
id="linearGradient9259"
xlink:href="#linearGradient7649" />
<linearGradient
gradientTransform="translate(5.7351308,-2.1927033)"
y2="34.04863"
x2="99.476608"
y1="44.193058"
x1="95.572289"
gradientUnits="userSpaceOnUse"
id="linearGradient9261"
xlink:href="#linearGradient5366-6" />
<linearGradient
gradientTransform="translate(5.7351308,-2.1927033)"
y2="43.886501"
x2="111.31824"
y1="50.580582"
x1="106.2162"
gradientUnits="userSpaceOnUse"
id="linearGradient9263"
xlink:href="#linearGradient5366-6" />
<filter
height="1.2728308"
y="-0.13641544"
width="1.1993906"
x="-0.09969531"
id="filter5257-2-0"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
id="feGaussianBlur5259-0-3"
stdDeviation="0.67565569" />
</filter>
<filter
height="1.0871989"
y="-0.043599416"
width="1.0766768"
x="-0.038338386"
id="filter8545-8-0"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
id="feGaussianBlur8547-4-9"
stdDeviation="1.1009866" />
</filter>
<filter
height="1.0289921"
y="-0.014496"
width="1.0568471"
x="-0.028423529"
id="filter8931-8-2"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
id="feGaussianBlur8933-6-5"
stdDeviation="0.45344554" />
</filter>
<filter
height="1.1508775"
y="-0.075438753"
width="1.1029657"
x="-0.05148283"
id="filter6058-3-9"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
id="feGaussianBlur6060-0-4"
stdDeviation="0.210429" />
</filter>
<filter
height="1.2033294"
y="-0.10166474"
width="1.1502416"
x="-0.075120777"
id="filter6114-7-6"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
id="feGaussianBlur6116-4-9"
stdDeviation="0.36423808" />
</filter>
<linearGradient
y2="66.854225"
x2="48.142838"
y1="77.964264"
x1="30.665777"
gradientTransform="matrix(0.70966437,0,0,0.65096738,8.52826,24.237011)"
gradientUnits="userSpaceOnUse"
id="linearGradient9265-2"
xlink:href="#linearGradient5366-6" />
<filter
height="1.3214939"
y="-0.16074698"
width="1.2348779"
x="-0.11743895"
id="filter7639-5-2"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
id="feGaussianBlur7641-2-4"
stdDeviation="0.487782" />
</filter>
<filter
height="1.2302096"
y="-0.11510481"
width="1.1831484"
x="-0.091574177"
id="filter7247-3-4"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
id="feGaussianBlur7249-7-8"
stdDeviation="0.41036213" />
</filter>
<filter
height="1.2783537"
y="-0.13917683"
width="1.1930734"
x="-0.096536696"
id="filter8043-4-1"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
id="feGaussianBlur8045-7-2"
stdDeviation="0.40736955" />
</filter>
<filter
height="1.2202032"
y="-0.11010158"
width="1.1900181"
x="-0.095008977"
id="filter7107-0-8"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
id="feGaussianBlur7109-6-9"
stdDeviation="0.60580599" />
</filter>
<marker
style="overflow:visible"
id="Arrow2Mstart-3"
refX="0"
refY="0"
orient="auto">
<path
inkscape:connector-curvature="0"
transform="scale(0.6)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path9385-6" />
</marker>
<marker
orient="auto"
refY="0"
refX="0"
id="marker9769-8"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path9767-0"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="scale(0.6)" />
</marker>
<marker
style="overflow:visible"
id="marker10285-2"
refX="0"
refY="0"
orient="auto">
<path
inkscape:connector-curvature="0"
transform="scale(0.6)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path10283-1" />
</marker>
<marker
orient="auto"
refY="0"
refX="0"
id="marker10813-0"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path10811-5"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="scale(0.6)" />
</marker>
<marker
style="overflow:visible"
id="marker11197-1"
refX="0"
refY="0"
orient="auto">
<path
inkscape:connector-curvature="0"
transform="scale(0.6)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path11195-1" />
</marker>
<marker
orient="auto"
refY="0"
refX="0"
id="marker11803-0"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path11801-8"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="scale(0.6)" />
</marker>
<marker
style="overflow:visible"
id="marker12601-5"
refX="0"
refY="0"
orient="auto">
<path
inkscape:connector-curvature="0"
transform="scale(0.6)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path12599-0" />
</marker>
<marker
orient="auto"
refY="0"
refX="0"
id="marker13567-6"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path13565-4"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="scale(0.6)" />
</marker>
<marker
style="overflow:visible"
id="marker14005-6"
refX="0"
refY="0"
orient="auto">
<path
inkscape:connector-curvature="0"
transform="scale(0.6)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path14003-2" />
</marker>
</defs>
<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></dc:title>
<cc:license
rdf:resource="https://opensource.org/licenses/MIT" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Device">
<g
transform="translate(-3.0873728,3.6519886)"
style="display:inline"
id="layer8">
<path
inkscape:connector-curvature="0"
id="path4589-9"
d="m 149.10287,101.50117 c 0,0 -1.05786,0.16638 4.0148,1.53332 5.07266,1.36693 10.15226,5.82516 11.98533,8.1296 2.51392,2.40918 6.52051,10.23903 1.3879,14.00992 -5.13261,3.7709 -17.38803,-23.67284 -17.38803,-23.67284 z"
style="display:inline;fill:url(#linearGradient4597-8);fill-opacity:1;stroke:none;stroke-width:0.23529507px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4586-2"
d="m 47.656799,72.245042 c 1.942356,-3.777983 8.589985,-9.41686 13.813269,-13.065406 1.567266,-1.09476 3.79326,0.642353 5.704413,0.594209 1.127681,-0.0284 10.73636,-5.504113 11.017493,-6.243426 0.368443,-0.968912 -1.255371,-2.707871 -0.294117,-3.095862 6.37104,-2.571549 13.117079,-5.313039 20.888587,-5.872484 7.771506,-0.559444 17.767586,3.500592 25.075636,9.269673 10.30308,8.133397 18.06398,20.559662 27.69019,44.80342 9.6262,24.243764 12.77467,23.651204 14.20078,23.532374 1.4261,-0.11884 3.83901,-6.01224 -1.15235,-11.47896 -4.99136,-5.46674 -10.07738,-7.18089 -10.07738,-7.18089 0,0 17.91418,5.69939 18.50838,29.46779 0.59422,23.76838 -8.00245,28.28016 -12.54438,31.28987 -6.51013,4.31394 -4.71386,3.59056 -17.00662,9.69133 -15.94321,7.91246 -51.426816,-6.03833 -58.795018,-14.4761 -7.368201,-8.43778 -3.725533,-14.73474 -8.122685,-26.2624 -4.397154,-11.52768 -20.55966,-35.414905 -20.55966,-35.414905 0,0 -7.654168,-11.693722 -9.136414,-18.370041 -0.343475,-1.547078 -1.15248,-3.410207 0.789876,-7.188192 z"
style="display:inline;opacity:1;fill:#050501;fill-opacity:1;stroke:none;stroke-width:0.23529507px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4600-6"
d="m 103.98404,157.58344 c 5.34109,-2.28109 8.14446,-5.10628 7.17707,-8.95743 C 108.41973,137.71258 96.185717,130.62094 87.905188,122.36574 76.932121,111.42625 56.762426,97.598091 53.011756,91.654563 c 5.289391,8.443197 17.264869,25.694437 21.193431,34.101017 3.92856,8.40659 5.326392,10.83456 7.357476,22.20279 2.031081,11.36824 13.649194,13.51119 22.421377,9.62507 z"
style="display:inline;fill:url(#linearGradient4608-6);fill-opacity:1;stroke:none;stroke-width:0.23529507px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
transform="matrix(0.88930424,0,0,0.88930424,18.934572,17.167095)"
id="path4630-6"
d="m 71.205713,51.477546 c 1.791534,-0.716614 4.879796,-1.532756 8.88373,1.532757 4.003934,3.065513 5.458341,4.46734 5.067478,6.037182 -0.436294,1.752311 -4.660828,4.035215 -7.413534,3.597284 -2.752704,-0.43793 -8.10171,-4.348022 -8.602201,-6.162304 -0.500494,-1.814282 -0.06576,-4.152802 2.064527,-5.004919 z"
style="display:inline;fill:url(#linearGradient9255);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter5257-2-0)" />
<path
inkscape:connector-curvature="0"
style="display:inline;opacity:1;fill:url(#linearGradient4583-3);fill-opacity:1;stroke:none;stroke-width:0.23529507px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 129.57912,64.421701 c -4.02561,0.627933 -7.50264,0.19394 -8.2263,-0.188069 -1.94545,-1.026962 -3.22816,-2.689304 -5.06731,-4.939595 -1.10426,-1.351113 -2.69039,-2.790981 -3.85765,-3.578251 -4.13658,-2.789973 -7.8692,-2.240708 -9.34964,-3.598119 -1.94376,-2.24103 2.30053,-6.127193 0.76519,-6.334074 -8.32641,-1.121952 -20.979676,3.315328 -22.783881,4.987519 -1.113028,1.031586 0.668263,4.430866 0.719191,5.697487 0.02332,0.580255 -1.705527,1.631637 -4.074122,2.882151 0.39358,0.162424 1.963716,1.31009 4.273294,3.132684 1.867629,-0.936312 4.414217,-0.433059 6.027162,0.154724 1.612943,0.587782 3.57968,1.73307 5.57957,3.798942 1.746528,1.804149 2.745617,3.586443 -0.372413,5.404756 1.010513,0.879362 2.037503,1.783111 3.070206,2.703609 2.578615,-1.314173 4.364533,-1.85557 5.641363,-0.707269 1.8246,1.640927 6.60886,6.409596 8.33059,8.081954 1.48441,1.44184 1.21288,3.210236 -1.31383,4.690652 3.82651,3.99094 6.90726,7.688805 8.37744,10.479682 0.13647,0.259046 -0.30588,0.242884 -0.38261,0.131916 -2.31626,-3.349976 -5.31611,-6.868647 -8.63094,-10.352564 -3.10741,2.202274 -5.34212,1.300996 -6.76368,-0.08526 -1.757879,-1.714218 -6.204391,-5.920059 -7.999085,-7.549923 -1.020934,-0.927168 -1.8553,-2.63953 2.172742,-4.305226 -1.095093,-0.962247 -2.148632,-1.91287 -3.209145,-2.816149 -1.193148,0.947677 -4.702692,1.417889 -6.165911,0.59061 -1.625289,-0.91891 -4.270282,-2.809047 -5.46815,-3.763177 -1.267292,-1.009429 -2.167667,-4.180695 0.275401,-6.048903 -2.488867,-1.9263 -4.051939,-3.072117 -4.148205,-3.138823 -0.825302,0.417497 -3.558598,1.774096 -4.587447,2.30064 -1.503726,0.769578 -4.317148,-0.128829 -5.848823,-0.433786 -1.905003,-0.379286 -4.371397,-0.893915 -5.171614,-0.541384 -2.942512,1.296312 -6.074862,4.787062 -8.627105,7.278119 -2.27056,2.216124 -3.521763,3.746314 -3.736697,6.714802 -0.0876,1.210002 9.706223,-0.641827 10.471747,1.794503 0.36497,1.161541 1.709726,4.925493 2.760199,6.256188 1.025689,1.2993 6.656463,5.179118 9.561507,7.524861 2.055757,1.659965 -2.9966,6.803736 -1.497747,7.784044 17.995826,11.769988 32.861033,21.509038 46.998083,34.211858 4.61386,4.14577 8.60721,8.94008 12.57325,13.71822 3.6281,3.8645 8.87124,2.06461 13.42541,0.24678 4.55417,-1.81783 9.50566,-5.04099 12.09212,-10.05164 1.09196,-4.90777 -0.92741,-9.90219 -2.05352,-14.73013 -2.66586,-11.42928 -6.6914,-22.533831 -11.06818,-33.423235 -2.55172,-6.348681 -6.66426,-14.637972 -8.82766,-18.531751 -2.16338,-3.89378 -2.18061,-4.119987 -3.8828,-5.44937 z"
id="path3770-4" />
<path
inkscape:connector-curvature="0"
transform="matrix(0.88930424,0,0,0.88930424,18.934572,17.167095)"
id="path8359-9"
d="m 40.682868,81.757779 c 3.273586,5.750891 24.13353,21.123631 33.089756,28.400561 25.480883,20.70322 25.657831,20.88018 35.832486,32.20501 C 102.5271,133.51581 92.647763,123.86601 76.072982,110.51225 59.4982,97.15848 51.445211,93.523393 40.682868,81.757779 Z"
style="display:inline;fill:#f5f5f5;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter8545-8-0)" />
<path
inkscape:connector-curvature="0"
transform="matrix(0.88930424,0,0,0.88930424,18.934572,17.167095)"
id="path8549-5"
d="m 129.82819,55.373092 c -2.58325,-3.826262 -5.14432,-6.939375 -7.56369,-9.475619 -2.28354,-2.393862 -3.98129,-3.804147 -3.98129,-3.804147 0,0 1.65448,1.460826 3.88456,3.894587 2.38121,2.598695 4.8504,5.697508 7.4408,9.533472 3.54891,5.255969 6.40972,10.684166 8.63649,15.530661 1.81559,3.951587 4.40778,10.574633 7.03637,17.434214 1.93022,5.037132 3.49502,9.128949 5.15642,13.3596 2.21222,5.63347 3.94334,9.56558 3.94334,9.56558 0,0 -1.64429,-3.96377 -3.7867,-9.62597 -1.4885,-3.934163 -3.25603,-8.671722 -5.06561,-13.394034 -2.6288,-6.860149 -5.22395,-13.490882 -7.04302,-17.450028 -2.23109,-4.855899 -5.09901,-10.297918 -8.65767,-15.568316 z"
style="display:inline;fill:#ffffff;fill-rule:nonzero;stroke:none;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter8931-8-2)" />
<path
inkscape:connector-curvature="0"
style="display:inline;fill:url(#linearGradient5336-7);fill-opacity:1;stroke:none;stroke-width:0.23529507px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 97.053387,81.615705 c 0,0 5.472913,-3.257201 6.423383,-3.887711 0.203,-0.596085 0.15434,-0.700778 0.30827,-1.223197 -0.58011,-0.541244 -1.8937,-1.724806 -2.70251,-2.395301 -0.46484,-0.385345 -1.04865,-0.369782 -1.82756,-0.147241 -0.778907,0.222548 -2.622613,0.958746 -3.727624,1.780359 -0.522664,0.38862 -1.358448,3.035358 -1.045328,3.413581 1.125243,1.359188 2.571369,2.45951 2.571369,2.45951 z"
id="path5296-0" />
<path
inkscape:connector-curvature="0"
transform="matrix(0.88930424,0,0,0.88930424,18.934572,17.167095)"
id="path5278-4"
d="m 88.506185,70.391657 6.906011,-3.667918 c 0,0 -1.40906,-1.457741 -2.873019,-2.671336 -0.522702,-0.433311 -1.345071,-0.437931 -2.220932,-0.187686 -0.875861,0.250246 -2.949063,1.078084 -4.191619,2.001967 -0.587723,0.436992 -0.664906,1.2013 -0.312809,1.626601 1.265305,1.528374 2.692368,2.898372 2.692368,2.898372 z"
style="display:inline;fill:url(#linearGradient9257);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter6058-3-9)" />
<path
inkscape:connector-curvature="0"
id="path5318-8"
d="m 104.5717,87.70787 c 1.74059,0.303403 4.4547,-1.31763 5.32227,-2.271515 0.48844,-0.537038 0.18246,-2.498079 -0.0446,-3.083973 -0.32583,-0.84082 -2.70273,-3.386009 -3.60759,-4.161578 -0.52003,-0.445733 -1.72952,-0.344279 -2.60091,-0.08686 -0.8714,0.257421 -3.12875,1.665353 -4.364962,2.615721 -0.584724,0.449522 -1.019022,2.120132 -0.66872,2.557623 1.258848,1.572192 3.832542,4.058955 5.964492,4.43058 z"
style="display:inline;fill:url(#linearGradient5320-4);fill-opacity:1;stroke:none;stroke-width:0.26766366px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
transform="matrix(0.85398815,0,0,0.85065967,27.22295,18.060489)"
style="display:inline;fill:url(#linearGradient5326-4);fill-opacity:1;stroke:none;stroke-width:0.30098099px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter6114-7-6)"
d="m 89.122725,78.448498 c 2.074321,1.469692 5.596417,-0.677646 6.766776,-2.178891 1.170358,-1.501246 -2.030052,-4.474198 -3.449872,-5.457294 -1.898288,-1.314392 -4.503703,0.444651 -5.768422,1.404729 -0.657508,0.505474 -2.665663,1.436447 -2.05057,2.215942 2.274973,2.372244 2.427767,2.545822 4.502088,4.015514 z"
id="path5322-7" />
<path
inkscape:connector-curvature="0"
id="path5338-1"
d="m 50.157089,76.970875 c 0.550769,1.967036 4.406159,8.025506 5.507701,8.261554 1.101541,0.236041 4.956931,-1.57363 5.271657,-2.203085 0.314726,-0.629451 -1.337585,-5.58638 -2.596488,-6.687919 -1.258902,-1.10154 -8.654959,-0.39341 -8.18287,0.62945 z"
style="display:inline;fill:url(#linearGradient5354-5);fill-opacity:1;stroke:none;stroke-width:0.23529507px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
transform="matrix(0.85287128,0,0,0.84325571,25.137301,18.710901)"
style="display:inline;fill:url(#linearGradient9265-2);fill-opacity:1;stroke:none;stroke-width:0.17983267px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7639-5-2)"
d="m 30.96635,68.909681 c 0.439514,1.439863 3.781539,6.228542 4.660569,6.401327 0.879029,0.172783 5.017331,-1.328841 5.268482,-1.789598 0.251151,-0.460756 -1.509768,-4.266162 -2.514372,-5.072484 -1.004603,-0.806324 -7.791405,-0.287974 -7.414679,0.460755 z"
id="path5356-7" />
<path
inkscape:connector-curvature="0"
id="path5368-2"
d="m 56.608968,86.648691 c 2.989894,3.304623 11.566172,11.487492 12.667713,11.330131 1.101538,-0.157363 2.832531,-4.327479 2.596486,-6.058474 -0.236044,-1.73099 -10.149905,-8.733638 -10.700675,-8.418915 -0.550771,0.314727 -4.406161,2.20308 -4.563524,3.147258 z"
style="display:inline;opacity:1;fill:url(#linearGradient5376-4);fill-opacity:1;stroke:none;stroke-width:0.23529507px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
transform="matrix(0.88930424,0,0,0.88930424,18.934572,17.167095)"
style="display:inline;fill:url(#linearGradient9259);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7247-3-4)"
d="m 44.773741,76.938558 c 4.034828,3.618886 10.678674,13.084069 11.917329,12.907119 1.238652,-0.176951 -1.03912,-9.347722 -1.341852,-9.671101 -0.806336,-0.861329 -7.3229,-5.671771 -7.942226,-5.31787 -0.619328,0.353901 -3.434524,1.363181 -2.633251,2.081852 z"
id="path5378-7" />
<path
inkscape:connector-curvature="0"
id="path6637-2"
d="m 105.26368,45.64344 c 2.83744,-0.222544 12.75903,5.610037 11.73921,6.286889 -1.26669,0.840713 -3.82162,2.985881 -4.50651,2.614899 -4.0058,-2.169811 -8.93308,-1.938998 -9.13998,-3.167128 -0.2069,-1.228129 0.73892,-5.345207 1.90728,-5.73466 z"
style="display:inline;fill:url(#linearGradient6645-6);fill-opacity:1;stroke:none;stroke-width:0.23529507px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
transform="matrix(0.88930424,0,0,0.88930424,18.934572,17.167095)"
style="display:inline;fill:url(#linearGradient9261);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter8043-4-1)"
d="m 97.700498,32.490138 c 3.066172,0.574471 9.403852,4.394314 8.383242,5.317726 -1.31379,1.188668 -1.16927,2.043757 -1.93941,1.626597 -4.504426,-2.439897 -6.166216,-0.866562 -6.398866,-2.247563 -0.232651,-1.381 -0.341868,-2.321618 -0.170093,-3.695777 z"
id="path6647-2" />
<path
inkscape:connector-curvature="0"
id="path6807-6"
d="m 113.68441,54.868923 c 0.46393,-0.382114 3.70382,-2.910924 4.31772,-2.487335 0.61389,0.42359 8.53125,6.90077 10.74,10.564704 1.28208,2.126748 -6.39815,1.223998 -7.28834,0.611998 -0.89017,-0.611996 -8.87722,-7.776899 -7.76938,-8.689367 z"
style="display:inline;fill:url(#linearGradient6815-1);fill-opacity:1;stroke:none;stroke-width:0.23529507px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
transform="matrix(0.88930424,0,0,0.88930424,18.934572,17.167095)"
style="display:inline;fill:url(#linearGradient9263);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7107-0-8)"
d="m 107.9815,42.040847 c -0.5421,-0.403601 2.72714,-2.919358 3.41745,-2.443043 0.69031,0.476316 9.17292,7.516433 11.6566,11.636435 1.44167,2.391474 -6.81845,1.493981 -7.24444,0.356394 -0.58072,-1.550808 -6.33261,-8.435263 -7.82961,-9.549786 z"
id="path6817-1" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Leaders">
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="23.282806"
y="56.571674"
id="text9269"><tspan
id="tspan9267"
x="23.282806"
y="56.571674"
style="stroke-width:0.26458332">Button 0</tspan></text>
<text
id="text9273"
y="29.795361"
x="86.34478"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="stroke-width:0.26458332"
y="29.795361"
x="86.34478"
id="tspan9271">Button 1</tspan><tspan
style="stroke-width:0.26458332"
y="35.087029"
x="86.34478"
id="tspan9275" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="57.316246"
y="31.296835"
id="text9281"><tspan
id="tspan9279"
x="57.316246"
y="31.296835"
style="stroke-width:0.26458332">Button 2</tspan><tspan
x="57.316246"
y="36.588501"
style="stroke-width:0.26458332"
id="tspan9285" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="119.37722"
y="33.549049"
id="text9327"><tspan
id="tspan9325"
x="119.37722"
y="33.549049"
style="stroke-width:0.26458332">Button 7</tspan></text>
<text
id="text9333"
y="47.813061"
x="140.64815"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="stroke-width:0.26458332"
y="47.813061"
x="140.64815"
id="tspan9329">Button 6</tspan><tspan
style="stroke-width:0.26458332"
y="53.104733"
x="140.64815"
id="tspan9331" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="9.5192785"
y="98.612984"
id="text9339"><tspan
id="tspan9335"
x="9.5192785"
y="98.612984"
style="stroke-width:0.26458332">Button 5</tspan><tspan
id="tspan9337"
x="9.5192785"
y="103.90463"
style="stroke-width:0.26458332" /></text>
<text
id="text9345"
y="114.12823"
x="16.025673"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="stroke-width:0.26458332"
y="114.12823"
x="16.025673"
id="tspan9341">Button 4</tspan><tspan
style="stroke-width:0.26458332"
y="119.4199"
x="16.025673"
id="tspan9343" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="145.15257"
y="67.832733"
id="text9351"><tspan
id="tspan9347"
x="145.15257"
y="67.832733"
style="stroke-width:0.26458332">Button 3</tspan><tspan
id="tspan9349"
x="145.15257"
y="73.124397"
style="stroke-width:0.26458332" /></text>
<text
id="text9357"
y="82.847488"
x="152.15945"
style="font-style:normal;font-weight:normal;font-size:4.23333311px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
xml:space="preserve"><tspan
style="stroke-width:0.26458332"
y="82.847488"
x="152.15945"
id="tspan9353">Button 8</tspan><tspan
style="stroke-width:0.26458332"
y="88.139153"
x="152.15945"
id="tspan9355" /></text>
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:0.76500005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow2Mstart-3)"
d="m 108.98515,81.306499 h 41.04031"
id="path9359" />
<path
inkscape:connector-curvature="0"
id="path9765"
d="m 103.47974,76.051335 39.28859,-9.259097"
style="fill:none;stroke:#000000;stroke-width:0.76500005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker9769-8)" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:0.76500005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker10285-2)"
d="m 126.00187,57.783384 17.5172,-7.006883"
id="path10281" />
<path
inkscape:connector-curvature="0"
id="path10809"
d="m 110.48663,48.023795 11.26105,-9.759589"
style="fill:none;stroke:#000000;stroke-width:0.76500005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker10813-0)" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:0.76500005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker11197-1)"
d="m 91.628348,53.885635 3.25318,-19.769426"
id="path11193" />
<path
inkscape:connector-curvature="0"
id="path11799"
d="M 58.095398,70.902355 37.324968,59.140797"
style="fill:none;stroke:#000000;stroke-width:0.76500005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker11803-0)" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:0.76500005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker12601-5)"
d="m 42.079658,87.668829 -12.26207,8.508363"
id="path12597" />
<path
inkscape:connector-curvature="0"
id="path13563"
d="m 49.587038,100.68162 -12.26207,8.50836"
style="fill:none;stroke:#000000;stroke-width:0.76500005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker13567-6)" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:0.76500005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker14005-6)"
d="M 83.620478,62.393995 69.856938,34.6167"
id="path14001" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 38 KiB

View File

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

After

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 69 KiB

View File

@@ -0,0 +1,564 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg2"
viewBox="0 0 750.00004 749.99998"
height="750"
width="750"
sodipodi:docname="logitech-g502.svg"
inkscape:version="0.92+devel 15739 custom">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
inkscape:document-rotation="0"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1053"
id="namedview105"
showgrid="false"
inkscape:zoom="0.90533333"
inkscape:cx="346.71574"
inkscape:cy="388.7347"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" />
<defs
id="defs4">
<linearGradient
id="linearGradient4449">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4451" />
<stop
id="stop4059"
offset="0.24691147"
style="stop-color:#3b3b3b;stop-opacity:1;" />
<stop
id="stop4057"
offset="0.84132797"
style="stop-color:#777777;stop-opacity:1;" />
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="1"
id="stop4453" />
</linearGradient>
<linearGradient
id="linearGradient4372">
<stop
style="stop-color:#3d444c;stop-opacity:1"
offset="0"
id="stop4374" />
<stop
style="stop-color:#8f9aaa;stop-opacity:1"
offset="1"
id="stop4376" />
</linearGradient>
<linearGradient
id="linearGradient4327">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4329" />
<stop
style="stop-color:#969696;stop-opacity:1"
offset="1"
id="stop4331" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient4372"
id="linearGradient4378-3"
x1="476.00003"
y1="513.00006"
x2="559.50006"
y2="463.50003"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient4327"
id="linearGradient4333-7"
x1="-258.85849"
y1="636.73883"
x2="-210.58292"
y2="669.61383"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient4449"
id="linearGradient4562-2"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,-1.672119,1.672119,0,512.07567,646.09377)"
x1="31.968746"
y1="18.546629"
x2="31.968746"
y2="29.577879" />
<linearGradient
xlink:href="#linearGradient4449"
id="linearGradient4623-2"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,-1.6721192,-1.6721192,0,585.53788,701.25516)"
x1="31.968746"
y1="18.546629"
x2="31.968746"
y2="29.577879" />
</defs>
<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></dc:title>
<cc:license
rdf:resource="https://opensource.org/licenses/MIT" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Device">
<g
id="layer1-2"
transform="matrix(1.066667,0,0,1.066667,543.92842,-223.56879)"
style="display:inline">
<g
transform="matrix(0.92282933,0,0,0.92282933,9.1598029,28.961215)"
id="g4390"
style="display:inline;stroke-width:1.08362401">
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -226.16095,683.58782 c -6.01535,-13.02364 -13,-16.5 -13,-16.5 l -11.25,-3.375 -116.45795,-88.57547 11.4905,-82.02437 7.62132,1.97703 6.89061,-13.15685 c -18.38478,2.65165 -27.23986,-1.01777 -28.47729,-2.60876 -1.23744,-1.59099 0.17677,-3.18198 0.17677,-3.18198 0,0 7.43505,-10.39747 47.55294,-23.33452 117.72759,-37.96429 194.75285,-32.63933 310.328127,14.65494 101.699787,41.61632 127.995243,117.50993 130.641353,159.47011 0,0 -2.38649,0.17677 -5.74524,5.03813 -3.35876,4.86136 6.0104,17.05896 3.53553,28.72622 -2.47487,11.66726 -28.637827,53.29817 -45.166448,48.70198 -16.528622,-4.5962 -39.067651,-6.98268 -55.154331,-3.00521 -16.08667977,3.97748 -95.017477,43.75224 -100.144001,46.66905 -5.126525,2.91682 -19.26866,10.42983 -25.36746,10.87177 -6.09879,0.44194 -18.29639,-4.06586 -45.25483,-14.05375 -26.95845,-9.98788 -54.09367,-22.98097 -61.25313,-32.88046 -7.15946,-9.8995 -4.95112,-20.38922 -10.96647,-33.41286 z"
id="path4435"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient4378-3);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 564.2793,394.82812 c -3.92575,-0.10626 -7.28936,3.75042 -13.08985,11.8711 -8.83883,12.37437 -39.24401,61.16451 -77.07422,75.30664 -37.83021,14.14214 -97.93406,2.12147 -130.10742,-1.41406 -32.07532,-3.52476 -80.61133,-16.61719 -80.61133,-16.61719 l -30.16601,4.00391 39.14453,29.77148 11.25,3.375 c 0,0 6.98465,3.47636 13,16.5 6.01535,13.02364 3.80734,23.51261 10.9668,33.41211 7.15946,9.89949 34.2935,22.89298 61.25195,32.88086 26.95844,9.98789 39.15707,14.49663 45.25586,14.05469 6.0988,-0.44194 20.24066,-7.95623 25.36719,-10.87305 5.12652,-2.91681 84.05785,-42.69049 100.14453,-46.66797 16.08668,-3.97747 38.62567,-1.59229 55.15429,3.00391 16.52863,4.59619 42.69115,-37.03392 45.16602,-48.70117 0.81145,-3.82542 0.34164,-7.70307 -0.52734,-11.36524 -4.70923,-11.20032 -11.76875,-25.18057 -21.7461,-38.13281 -23.82425,-30.92783 -38.18467,-40.65753 -46.3164,-47.02149 -2.79529,-2.18761 -5.00616,-3.33105 -7.0625,-3.38672 z"
transform="translate(-522.78594,165.96281)"
id="path4362"
inkscape:connector-curvature="0" />
<g
id="g4356"
style="stroke-width:1.08362401">
<path
id="path4417"
transform="translate(-522.78594,165.96281)"
d="m 130.91406,379.34375 c -0.46995,0.14042 -0.93877,0.27563 -1.41015,0.42187 0,10e-6 -1.6703,5.53928 -1.51758,6.25196 l 51.97265,21.91992 23.86524,-17.50195 2.72461,-3.55664 c -0.22885,-0.14243 -0.45999,-0.27609 -0.68945,-0.41602 -27.39877,-3.31358 -52.27682,-5.25289 -75.04493,-6.2793 l 0.0996,-0.83984 z"
style="fill:#2e2d2b;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4422"
transform="translate(-522.78594,165.96281)"
d="m 128.01367,386.0293 4.48633,9.0957 66,39.375 -18.53906,-26.56445 -0.002,0.002 -51.94531,-21.9082 z"
style="fill:#13130f;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4427"
transform="translate(-522.78594,165.96281)"
d="m 203.82422,390.61328 c 0,0 -0.57509,0.25215 -0.60156,0.26367 l -23.26172,17.0586 18.38281,26.33984 -1.14844,-4.50586 0.79492,-17.32422 z"
style="fill:#7c7a79;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:#3d4144;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 411.19922,269.32812 C 389.02158,276.55183 382.23256,283.7763 371.75,291 l 25.375,6.25 c 18.2282,-19.41084 23.88453,-20.47312 31.20117,-23.95898 -5.76229,-1.45746 -11.46778,-2.78139 -17.12695,-3.9629 z"
transform="translate(-522.78594,165.96281)"
id="path4216"
inkscape:connector-curvature="0" />
<path
style="fill:#3c4247;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -170.91095,464.08782 -8.5,16.125 35.75,3.25 1.625,-16.25 z"
id="path4220"
inkscape:connector-curvature="0" />
<path
style="fill:#363c41;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 411.17969,269.33398 c -24.50302,-5.11518 -48.18402,-7.65405 -72.22657,-7.57031 -42.56105,0.14824 -86.27544,8.47266 -137.78124,25.08203 -40.11789,12.93705 -47.55274,23.33399 -47.55274,23.33399 0,0 -0.48629,0.56241 -0.63476,1.3457 l 0.10351,0.0684 c 65.05383,-30.40559 116.42376,-36.77027 218.33008,-20.63869 11.09267,-7.17661 17.60011,-14.39959 39.76172,-21.62112 z"
transform="translate(-522.78594,165.96281)"
id="path4226"
inkscape:connector-curvature="0" />
<path
style="fill:#10f5fa;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 296.87305,518.20312 c 5.68645,12.75421 3.66653,23.08278 10.71875,32.83399 7.15946,9.89949 34.2935,22.89298 61.25195,32.88086 26.95844,9.98789 39.15707,14.49663 45.25586,14.05469 5.82656,-0.42222 18.90685,-7.25764 24.54101,-10.41407 -0.55038,-0.0177 -1.10725,-0.03 -1.64062,-0.0586 -7,-0.375 -17.75,-3.625 -26.375,-6.5 -8.625,-2.875 -67.14947,-20.67049 -76.375,-33.125 -7.10507,-9.59187 -5.33684,-16.54135 -8.83008,-19.27148 -2.49111,-1.94692 -8.34261,-3.73063 -12.5039,-5.15625 -3.08671,-1.05747 -11.18273,-3.67568 -16.04297,-5.24415 z"
transform="translate(-522.78594,165.96281)"
id="path4263"
inkscape:connector-curvature="0" />
<g
id="g4348"
style="stroke-width:1.08362401">
<path
id="path4228"
transform="translate(-522.78594,165.96281)"
d="m 244.0293,281.64062 c -11.87933,0.21005 -21.28719,4.70945 -25.45899,14.41407 l -7.76367,15.2207 c 4.63005,-8.00151 13.38623,-11.7657 24.20703,-11.95703 13.84985,-0.24489 31.05521,5.35529 47.24414,15.31641 4.24517,-2.44738 6.87171,-7.03619 7.65625,-8.34375 0.83684,-1.39473 3.09281,-4.54246 4.70703,-7.20118 -17.13681,-11.29215 -35.78244,-17.71107 -50.59179,-17.44922 z m -33.22266,29.63477 c -0.45292,0.78273 -0.88022,1.59229 -1.25195,2.45703 z"
style="fill:#a6a09b;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
transform="translate(-522.78594,165.96281)"
id="path4247"
d="m 210.77539,311.25976 -1.12305,2.28126 c -0.45554,2.37144 2.75076,4.2831 5.21875,3.9746 2.82843,-0.35354 20.06602,-2.56346 32.97071,-2.74023 12.9047,-0.17678 22.09591,1.76761 29.34375,1.41406 1.88682,-0.092 3.57816,-0.69332 5.07226,-1.55468 -16.18893,-9.96112 -33.39429,-15.5613 -47.24414,-15.31641 -10.8208,0.19133 -19.60823,3.93989 -24.23828,11.9414 z"
style="fill:#0c0d08;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:#111419;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -179.35432,480.17165 -2.95971,5.51668 39.24443,5.56847 -0.88388,-7.86656 z"
id="path4258"
inkscape:connector-curvature="0" />
<path
style="fill:#282d32;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -143.07496,491.2041 -1.00352,-7.90761 2.03293,-16.10721 1.45082,6.56663 z"
id="path4260"
inkscape:connector-curvature="0" />
<g
id="g4352"
style="stroke-width:1.08362401">
<path
id="path4262"
d="m -313.91094,553.83781 -2.375,3.5 -3.875,14.125 93.375,-1.625 c -21.95804,-4.72268 -46.10785,-9.6343 -87.125,-16 z"
style="fill:#2f333a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4264"
d="m -320.16094,571.44718 -3.125,23.01563 3.25,7.125 99.5,-15.375 -6.29687,-16.40625 z"
style="fill:#585958;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
<g
id="g4335"
style="stroke-width:1.08362401">
<path
id="path4288"
d="m -295.16094,608.46281 6.625,27.125 27.125,5.875 c 0,0 38.75,14.5 45.75,29.125 l 5.5,0.875 2.375,-4.625 c 0,0 -13.875,-23.75 -49.625,-33.375 l -1,-1.25 2.25,-11.25 z"
style="fill:url(#linearGradient4333-7);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient4333-7);stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4290"
transform="translate(-522.78594,165.96281)"
d="M 235.18359,469.82617 272.125,497.75 l 35,6.875 C 300.125,490 261.375,475.5 261.375,475.5 Z"
style="fill:#1f2021;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
<g
id="g4297"
style="stroke-width:1.08362401">
<path
id="path4266"
d="m -271.28594,618.46281 4,7 85.38128,13.44194 9.61872,-8.44194 z"
style="fill:#5e5d5c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4268"
d="m -181.07659,638.77695 c 0,0 22.18548,12.55115 -5.48008,8.04334 -27.66555,-4.5078 -67.97064,-12.55114 -67.97064,-12.55114 l -12.72792,-8.75045 z"
style="fill:#242422;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4270"
d="m -172.41453,630.38006 -9.45755,8.3085 9.28077,8.3969 9.54594,-5.3033 z"
style="fill:#5e6570;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4280"
d="m -271.48416,618.56636 2.19822,-8.60355 103.25,9.875 6,18.125 -2.625,3.875 -9.60983,-10.91789 z"
style="fill:#181719;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
<g
id="g4305"
style="stroke-width:1.08362401">
<path
id="path4272"
d="m -164.98991,632.50138 6.18718,9.72272 75.041708,8.22011 8.131734,-6.54073 1.155328,-4.55469 4.153795,-8.23574 -93.255535,-9.12988 z"
style="fill:#676763;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4274"
d="m -70.32599,631.08717 5.48008,5.39169 -5.74524,12.90469 -5.204655,-5.54257 c 0.08832,-4.98178 3.353501,-8.62593 5.469815,-12.75381 z"
style="fill:#2f343b;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4276"
d="m -83.58424,650.17905 3.8007,4.06586 -77.87014,-6.89429 -1.15104,-5.14215 z"
style="fill:#202328;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
style="fill:#97a6b5;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -70.59115,649.38355 -9.1397,4.91406 -3.894905,-4.1034 7.819685,-6.37912 z"
id="path4278"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:#676769;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -153.03036,495.18342 44.96255,6.76837 1.76776,125.51145 -32.51219,-3.18198 z"
id="path4292"
inkscape:connector-curvature="0" />
<path
id="path4303"
d="m -164.98991,632.50138 89.360622,11.4021 1.155328,-4.55469 4.153795,-8.23574 -93.255535,-9.12988 z"
style="fill:#181719;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<g
id="g4282"
style="stroke-width:1.08362401">
<path
id="path4244"
d="m -283.96063,605.18939 1.59099,4.06586 144.42656,16.26346 -6.01041,-45.07806 z"
style="fill:#9b9994;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4246"
transform="translate(-522.78594,165.96281)"
d="m 312.64258,435.04297 -5.01172,0.64453 8.00195,8.8418 5.88281,0.41015 z"
style="fill:#2cbfff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4248"
transform="translate(-522.78594,165.96281)"
d="m 331.68945,432.58984 -4.78515,0.61524 11.86718,12.93945 6.72461,0.4707 z"
style="fill:#2cbfff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4250"
transform="translate(-522.78594,165.96281)"
d="m 351.97266,429.97852 -6.13282,0.78906 16.58594,17.0293 7.58399,0.52929 z"
style="fill:#2cbfff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
<g
id="g4339"
style="stroke-width:1.08362401">
<path
id="path3608"
d="m -386.31433,498.06271 -5.65686,48.08326 c 45.99186,2.07336 100.55399,7.85718 167.23075,21.21321 l 1.06066,3.0052 c 23.24045,-1.15617 46.44002,-2.32824 77.95852,-0.26516 l 3.35876,3.62392 -10.6066,-78.48885 c -119.63944,-15.8417 -179.55343,-5.16228 -233.34523,2.82842 z"
style="fill:#4c5053;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4412"
transform="translate(-522.78594,165.96281)"
d="m 344.67578,403.00977 c -16.50546,-0.0586 -30.99426,0.6673 -45.49414,1.38867 l 6.64258,15.02929 74.77539,-10.96093 -0.94727,-1.53907 -2.58789,-2.79101 c -11.81943,-0.77366 -22.46808,-1.09174 -32.38867,-1.12695 z"
style="fill:#413d44;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="path4242"
d="m -296.22345,497.71282 4.375,6.6875 6.875,-6.8125 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
<g
id="g4344"
style="stroke:none;stroke-width:1.08362401">
<path
id="path4234"
transform="translate(-522.78594,165.96281)"
d="m 428.32422,273.29297 c -7.29616,3.47575 -12.94858,4.55152 -31.06055,23.8125 0.0479,0.0864 0.0803,0.17102 0.13086,0.25781 6.62912,19.7106 10.60547,165.375 10.60547,165.375 0,0 41.19295,7.2655 67.125,1.38672 22.33947,-5.06434 38.625,-29.625 51.125,-42.625 12.5,-13 24.89365,-35.29164 33.5,-37.875 6.89763,-2.07046 15.8234,4.4109 29.125,12.375 33.4418,20.02269 42.89394,39.55603 52.79297,65.09375 0.30214,-0.10531 0.47265,-0.12305 0.47265,-0.12305 C 639.49453,419.01054 613.19977,343.11632 511.5,301.5 481.65769,289.2883 454.39027,279.88532 428.32422,273.29297 Z"
style="fill:#6d747e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<ellipse
ry="29.875"
rx="32.25"
cy="561.08783"
cx="-58.535938"
id="path4261"
style="opacity:1;fill:#2cbfff;fill-opacity:1;stroke:none;stroke-width:1.62543607;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<path
style="fill:#202124;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -205.53595,454.96282 -11.375,19 21.5,-0.875 10.875,-18.875 z"
id="path4218"
inkscape:connector-curvature="0" />
<path
style="fill:#121518;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -222.88428,483.39024 6.01577,-9.47808 21.47301,-0.86336 1.23744,11.49049 z"
id="path4254"
inkscape:connector-curvature="0" />
<path
style="fill:#1a1e22;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08362401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -195.50442,473.05324 10.88157,-18.79455 2.39721,10.39322 -12.00029,19.91863 z"
id="path4256"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Leaders">
<path
style="display:inline;fill:url(#linearGradient4562-2);fill-opacity:1;stroke:none;stroke-width:1.06666696"
d="m 537.54283,592.59247 5.47684,3.16138 5.47357,3.16133 v -3.60551 h 11.57746 l -1.5676,-2.7172 1.5676,-2.7172 h -11.57746 v -3.60549 l -5.47357,3.16135 z"
id="path4556"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient4623-2);fill-opacity:1;stroke:none;stroke-width:1.06666696"
d="m 560.07072,647.75385 -5.47685,3.16137 -5.47357,3.16132 v -3.60549 h -11.57747 l 1.56762,-2.7172 -1.56762,-2.71719 h 11.57747 v -3.60551 l 5.47357,3.16136 z"
id="use4558"
inkscape:connector-curvature="0" />
<text
id="text4489"
y="326.48422"
x="70.888794"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
y="326.48422"
x="70.888794"
id="tspan4491">Button0</tspan></text>
<text
id="text4505"
y="598.14478"
x="570.61731"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
y="598.14478"
x="570.61731"
id="tspan4507">Button10</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="570.65088"
y="653.30615"
id="text4509"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
id="tspan4511"
x="570.65088"
y="653.30615">Button9</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="68.277954"
y="388.80585"
id="text4440"><tspan
id="tspan4442"
x="68.277954"
y="388.80585"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button7</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 136.79589,322.56395 h 77.95666"
id="path4448"
inkscape:connector-curvature="0" />
<path
id="path4450"
d="M 344.21631,234.86269 381.80255,118.62372"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="180.61809"
y="131.11789"
id="text4452"><tspan
id="tspan4454"
x="180.61809"
y="131.11789"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button2</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 280.18048,255.0479 214.05652,136.72082"
id="path4456"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 332.38361,561.30626 424.95716,449.24354"
id="path4466"
inkscape:connector-curvature="0" />
<text
id="text4468"
y="438.99133"
x="76.613747"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
y="438.99133"
x="76.613747"
id="tspan4470">Button6</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="124.46452"
y="486.74979"
id="text4472"><tspan
id="tspan4474"
x="124.46452"
y="486.74979"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button4</tspan></text>
<text
id="text4476"
y="535.11047"
x="152.34004"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
y="535.11047"
x="152.34004"
id="tspan4478">Button5</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="299.23001"
y="578.30023"
id="text4480"><tspan
id="tspan4482"
x="299.23001"
y="578.30023"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button3</tspan></text>
<text
id="text4484"
y="117.37291"
x="382.20294"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
y="117.37291"
x="382.20294"
id="tspan4486">Button1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="528.19232"
y="164.23615"
id="text4488"><tspan
id="tspan4490"
x="528.19232"
y="164.23615"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button8</tspan></text>
<path
id="path4492"
d="M 136.09985,377.55125 195.26339,365.0225"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 141.66819,424.88208 266.25965,381.72751"
id="path4494"
inkscape:connector-curvature="0" />
<path
id="path4496"
d="m 191.78319,474.30105 138.5123,-43.15458"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 198.04756,519.54376 98.83792,-60.55562"
id="path4498"
inkscape:connector-curvature="0" />
<path
id="path4500"
d="M 401.98779,271.75291 523.79509,166.65062"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 38 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,594 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg8"
version="1.1"
viewBox="0 0 198.4375 198.43751"
height="750"
width="750"
sodipodi:docname="logitech-g9.svg"
inkscape:version="0.92+devel 15739 custom">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1053"
id="namedview94"
showgrid="false"
inkscape:snap-global="false"
inkscape:zoom="0.62933333"
inkscape:cx="285.67971"
inkscape:cy="405.53127"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer3"
inkscape:document-rotation="0" />
<defs
id="defs2">
<linearGradient
id="linearGradient4505">
<stop
id="stop4501"
offset="0"
style="stop-color:#989899;stop-opacity:1" />
<stop
id="stop4503"
offset="1"
style="stop-color:#423f43;stop-opacity:0" />
</linearGradient>
<linearGradient
id="linearGradient4330">
<stop
id="stop4326"
offset="0"
style="stop-color:#636166;stop-opacity:1" />
<stop
id="stop4328"
offset="1"
style="stop-color:#413f43;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient4308">
<stop
id="stop4304"
offset="0"
style="stop-color:#444248;stop-opacity:1" />
<stop
style="stop-color:#48474b;stop-opacity:1"
offset="0.33914274"
id="stop4312" />
<stop
id="stop4306"
offset="1"
style="stop-color:#b2b2b4;stop-opacity:0" />
</linearGradient>
<linearGradient
id="linearGradient4379">
<stop
id="stop4375"
offset="0"
style="stop-color:#45494d;stop-opacity:1" />
<stop
id="stop4377"
offset="1"
style="stop-color:#25262a;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient4351">
<stop
id="stop4347"
offset="0"
style="stop-color:#9b9b9f;stop-opacity:1" />
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="0.49072835"
id="stop4355" />
<stop
id="stop4349"
offset="1"
style="stop-color:#8e8d90;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient4341">
<stop
id="stop4337"
offset="0"
style="stop-color:#120b11;stop-opacity:1" />
<stop
id="stop4339"
offset="1"
style="stop-color:#27282d;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient4327">
<stop
id="stop4323"
offset="0"
style="stop-color:#100f15;stop-opacity:1" />
<stop
id="stop4325"
offset="1"
style="stop-color:#595d62;stop-opacity:1" />
</linearGradient>
<linearGradient
gradientTransform="matrix(1.1742912,0,0,1.1742912,-9.3841221,-5.8762942)"
y2="22.688826"
x2="105.51226"
y1="188.74203"
x1="103.14564"
gradientUnits="userSpaceOnUse"
id="linearGradient4331"
xlink:href="#linearGradient4327" />
<linearGradient
gradientTransform="matrix(1.1742912,0,0,1.1742912,-9.3841221,-5.8762942)"
gradientUnits="userSpaceOnUse"
y2="111.9789"
x2="101.38398"
y1="47.607136"
x1="101.79689"
id="linearGradient4343"
xlink:href="#linearGradient4341" />
<linearGradient
gradientTransform="matrix(1.1742912,0,0,1.1683052,-9.536801,-5.7076406)"
gradientUnits="userSpaceOnUse"
y2="78.818336"
x2="106.56455"
y1="58.741718"
x1="106.52042"
id="linearGradient4353"
xlink:href="#linearGradient4351" />
<linearGradient
gradientTransform="matrix(1.1742912,0,0,1.1742912,-9.3841221,-5.8762942)"
gradientUnits="userSpaceOnUse"
y2="104.87594"
x2="54.881187"
y1="106.28905"
x1="65.710442"
id="linearGradient4381"
xlink:href="#linearGradient4379" />
<linearGradient
gradientTransform="matrix(1.1742912,0,0,1.1742912,-9.3841221,-5.8762942)"
gradientUnits="userSpaceOnUse"
y2="32.342125"
x2="105.20406"
y1="50.405422"
x1="105.60937"
id="linearGradient4310"
xlink:href="#linearGradient4308" />
<linearGradient
gradientTransform="matrix(1.1742912,0,0,1.1742912,-310.58744,-20.716767)"
gradientUnits="userSpaceOnUse"
y2="92.680016"
x2="396.11249"
y1="83.560425"
x1="368.85941"
id="linearGradient4332"
xlink:href="#linearGradient4330" />
<linearGradient
gradientTransform="matrix(1.1730673,-0.05360072,0.05360072,1.1730673,-272.43538,-5.5525684)"
gradientUnits="userSpaceOnUse"
y2="82.570068"
x2="312.97284"
y1="83.391426"
x1="324.6431"
id="linearGradient4507"
xlink:href="#linearGradient4505" />
<linearGradient
y2="101.28305"
x2="306.84976"
y1="104.65936"
x1="307.03534"
gradientTransform="matrix(1.1730673,-0.05360072,0.05360072,1.1730673,-272.43538,-5.5525684)"
gradientUnits="userSpaceOnUse"
id="linearGradient4511"
xlink:href="#linearGradient4505" />
<linearGradient
xlink:href="#linearGradient4449"
id="linearGradient4562-2"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.44241483,0,0,0.44241483,141.87301,147.82897)"
x1="31.968746"
y1="18.546629"
x2="31.968746"
y2="29.577879" />
<linearGradient
id="linearGradient4449">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4451" />
<stop
id="stop4059"
offset="0.24691147"
style="stop-color:#3b3b3b;stop-opacity:1;" />
<stop
id="stop4057"
offset="0.84132797"
style="stop-color:#777777;stop-opacity:1;" />
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="1"
id="stop4453" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient4449"
id="linearGradient4623-2"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.44241488,0,0,-0.44241488,141.87299,181.86062)"
x1="31.968746"
y1="18.546629"
x2="31.968746"
y2="29.577879" />
</defs>
<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></dc:title>
<cc:license
rdf:resource="https://opensource.org/licenses/MIT" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Device"
style="display:inline">
<g
transform="translate(-1.0583334,1.5875001)"
style="display:inline;opacity:1"
id="layer2-0">
<g
transform="translate(-3.4704866,-7.0405558)"
id="g4594">
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#3a373a;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
d="m 77.177399,97.902294 6.104879,-45.161826 c 0,0 23.584792,-4.178672 24.946992,-4.230997 1.36216,-0.05233 2.90708,0.284061 2.90708,0.284061 l -1.7858,46.32423 c -12.77599,0.205474 -29.351306,3.51043 -32.173151,2.784532 z"
id="path4497" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#2c282c;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
d="m 77.484288,103.8942 0.485337,-6.617983 c 8.461322,-0.795948 19.932848,-1.405469 31.644865,-2.832276 l -0.13147,6.093759 c -2.00828,2.01378 -25.087068,3.75167 -31.998732,3.35649 z"
id="path4492" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:url(#linearGradient4310);fill-opacity:1;fill-rule:evenodd;stroke-width:1.32291675;stroke-miterlimit:2;paint-order:markers stroke fill"
d="m 112.45854,48.673649 -0.15185,-14.682308 4.10547,-0.275969 0.10898,15.201736 z"
id="path4294" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:url(#linearGradient4381);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.74083328;stroke-linecap:round;paint-order:markers stroke fill"
d="m 77.876085,56.839675 c 0,0 -1.471527,0.83341 -1.810925,1.7849 -0.339393,0.951491 -16.168851,35.787062 -19.676329,50.819095 -3.507477,15.03202 -2.371756,19.94152 -2.371756,19.94152 0,0 3.861947,10.45097 8.329276,18.52531 4.467326,8.07435 4.961694,8.89575 4.961694,8.89575 l -0.213903,-19.5262 1.703903,-0.46404 2.744193,-39.166022 z"
id="path4365" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:url(#linearGradient4331);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.74083328;stroke-linecap:round;paint-order:markers stroke fill"
d="m 78.27115,54.434668 -7.48213,43.568266 c 0,0 0.476579,1.029381 0.324073,1.990126 -0.152506,0.96074 -2.420477,36.18422 -2.420477,36.18422 -0.355121,0.15828 -1.325027,0.33622 -2.042764,-0.16606 l 0.383815,20.3658 c 0,0 16.260517,24.29325 21.731528,26.4292 5.471007,2.13595 33.712475,5.76449 45.045255,-5.04393 11.33279,-10.80844 18.13817,-40.40294 19.0246,-42.62467 0.88643,-2.22174 0.97221,-22.60336 0.63861,-27.0554 -0.33359,-4.45204 -5.24224,-53.518879 -5.24224,-53.518879 l -0.75298,-0.574736 -0.34314,-1.818558 -3.28832,-0.446063 -1.05801,1.612684 c 0,0 0.65473,55.280832 0.0828,57.236642 -3.50339,8.99181 -62.826338,16.12105 -65.71582,10.5305 0,0 -0.343145,-19.51523 0.171552,-27.69875 0.514695,-8.183518 6.46227,-40.694595 6.46227,-40.694595 l -1.477365,-0.386014 z"
id="path4299" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#0a0309;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.74083328;stroke-linecap:round;paint-order:markers stroke fill"
d="m 143.68572,52.718279 -1.08758,-0.105064 -0.7152,58.124735 c -25.40904,10.09051 -46.263283,10.35133 -64.709748,5.41595 -0.569129,-6.64228 2.723755,-40.412798 6.551495,-63.38927 -1.329573,2.096014 -11.038066,45.857103 -6.432098,72.44326 31.026281,-1.3617 55.581991,-4.72092 67.571961,-13.01971 z"
id="path4363" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#100f15;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.74083328;stroke-linecap:round;paint-order:markers stroke fill"
d="m 147.48209,53.990659 c 0,0 3.60862,62.216321 3.18362,73.553921 -0.42504,11.3376 -3.08536,24.73061 -3.08536,24.73061 0.17938,-0.42088 3.57331,-10.50586 5.40745,-17.70147 0.71802,-5.20464 0.79094,-21.82933 0.50887,-26.13904 -0.35573,-5.43493 -4.00333,-41.108137 -5.26548,-53.873474 z"
id="path4333" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:url(#linearGradient4343);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.74083328;stroke-linecap:round;paint-order:markers stroke fill"
d="m 83.824604,52.738709 -1.250972,-0.245922 c 0,0 -8.059607,43.356337 -8.166174,56.967173 -0.106625,13.61084 -5.055488,24.88381 34.888512,19.75206 39.94401,-5.13176 36.61892,-20.58117 36.61892,-20.58117 l -1.76914,-56.190652 -0.85293,0.06392 c 0,0 1.96242,55.618052 1.12783,57.633402 -0.90305,5.11422 -29.75059,11.23217 -37.2287,12.06204 C 98.789172,123.0647 77.702414,124.85494 76.780689,120.5 75.468184,93.62462 83.824604,52.738707 83.824604,52.738707 Z"
id="path4335" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#0a0309;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.74083328;stroke-linecap:round;paint-order:markers stroke fill"
d="m 110.34556,64.349805 -0.11449,21.523245 10.83825,-0.04674 0.4938,-21.169092 z"
id="path4361" />
<rect
style="color:#000000;overflow:visible;vector-effect:none;fill:url(#linearGradient4353);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.74083334;stroke-linecap:round;paint-order:markers stroke fill"
id="rect4345"
width="8.5005722"
height="19.579855"
x="111.05955"
y="65.467842"
ry="2.078804" />
<rect
style="color:#000000;overflow:visible;vector-effect:none;fill:#313237;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.74083328;stroke-linecap:round;paint-order:markers stroke fill"
id="rect4357"
width="4.8539634"
height="19.804958"
x="112.85114"
y="65.303093"
ry="0.51506227" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#525054;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.74083328;stroke-linecap:round;paint-order:markers stroke fill"
d="m 110.93569,49.34497 c 0.17831,-0.317361 0.29848,-0.818988 0.53493,-0.952083 l 11.85679,0.387209 c 3.57996,0.438472 11.48189,1.107237 18.90241,3.168225 l 0.58904,0.628053 c 0.4516,18.61888 0.74216,35.257569 0.26336,57.894286 0,0 -0.62934,5.72246 -26.94491,9.97842 -26.315559,4.25595 -37.605807,1.37998 -37.605807,1.37998 -0.623452,-0.24916 -1.115754,-0.79276 -1.30372,-1.94276 -0.149634,-0.91548 -0.142638,-6.91509 -0.142638,-6.91509 10.089039,-2.41284 25.379115,-1.9056 28.541445,-3.69477 4.79494,-1.81161 4.35855,-13.193339 4.35855,-13.193339 l 0.22511,-10.433667 c 2.01048,-0.120202 9.88363,1.14486 9.98353,-2.014334 l -0.12176,-17.186068 c -0.13242,-2.593108 -6.39547,-1.529455 -9.45682,-1.614711 z"
id="path4359" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#020007;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.32291675;stroke-miterlimit:2;paint-order:markers stroke fill"
d="m 71.570973,98.461729 c 0,0 -1.488872,-0.259002 -1.858879,0.108387 -0.370008,0.367365 -1.855921,8.803524 -2.087914,10.561054 -0.232004,1.75753 -0.317164,4.00401 -0.21439,4.57487 -0.0101,0.69988 0.368241,0.94326 0.425821,0.87746 l 2.28173,0.0661 z"
id="path4314" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#949698;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.32291675;stroke-miterlimit:2;paint-order:markers stroke fill"
d="m 70.569596,98.755149 c -0.01773,0.296004 -2.199529,10.870271 -2.261203,12.170581 -0.06165,1.30032 0.31072,3.21606 -0.04462,3.3799 -0.355341,0.16388 -0.58306,0.25519 -0.735312,-0.10509 -0.108622,-0.33037 -0.368848,-3.11368 -0.195605,-4.29506 0.173278,-1.18137 2.158786,-10.80624 2.308544,-11.237029 0.1758,-0.417689 0.943147,-0.300638 0.928197,0.08671 z"
id="path4316" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#030007;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.32291675;stroke-miterlimit:2;paint-order:markers stroke fill"
d="m 67.575413,114.75396 c 0.299185,-0.26682 2.82104,0.0248 2.353274,0.28766 0.120388,-0.26164 -0.966148,21.16968 -0.966148,21.16968 -0.321588,0.36161 -2.280894,0.44599 -2.635973,-0.47617 0,0 -1.282502,-15.46808 1.248847,-20.98117 z"
id="path4318" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#b1b2b4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.32291675;stroke-miterlimit:2;paint-order:markers stroke fill"
d="m 68.272455,115.00255 -0.70333,-0.21676 c 0,0 -1.300892,3.31288 -1.377256,5.4827 -0.328426,2.27648 -0.20179,14.32772 0.160056,15.10817 0.273845,0.59063 0.484536,0.54577 0.731208,0.47308 -0.132143,-3.43844 0.03675,-14.49501 0.374434,-15.6526 0.153609,-1.62495 0.68015,-3.65239 0.814888,-5.19459 z"
id="path4320" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:url(#linearGradient4507);fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
d="m 107.5756,94.192672 c 0,0 0.90781,-43.268534 0.68191,-45.181422 -1.23023,0.241078 -22.726413,4.189946 -22.726413,4.189946 l -4.467392,43.50272 z"
id="path4499" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#09070f;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.32291663;stroke-miterlimit:2;paint-order:markers stroke fill"
d="m 109.94226,48.671364 -0.56089,45.497671 c 0,0 -30.495458,2.831185 -31.186916,3.006854 -0.69148,0.175669 -0.330722,1.017048 -0.330722,1.017048 0,0 3.202242,-0.321591 9.419245,-0.885974 6.217003,-0.564383 17.969613,-1.84224 19.607953,-2.010433 1.63836,-0.168196 2.42368,-0.2397 2.42368,-0.239697 0,0 -0.0771,4.872482 -0.0771,5.238777 0,0.0959 -0.13927,0.17936 -0.33983,0.25184 -10.390275,0.59447 -26.633856,1.69783 -31.4436,2.93281 -0.231992,0.29072 -0.491949,10.20583 -0.491949,10.20583 0.281625,0.15497 5.423843,-1.26531 11.011807,-1.74462 15.335252,-1.64639 19.804012,-1.00499 21.344882,-5.71529 0.91571,-5.42145 1.34517,-20.494631 1.34517,-20.494631 l 0.35136,-36.858539 z"
id="path4322" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#6f7073;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.32291675;stroke-miterlimit:2;paint-order:markers stroke fill"
d="m 77.411868,103.91325 c 0,0 1.063145,-0.6709 1.25419,-0.22612 0.191046,0.44478 -0.197257,7.59674 -0.176508,8.09945 0.02079,0.50271 0.147433,0.95122 -0.09758,1.2222 -0.245028,0.27098 -0.766213,0.31957 -1.042395,0.35134 -0.276182,0.0318 -0.05402,-8.63206 0.06224,-9.44687 z"
id="path4324" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#46484a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.32291675;stroke-miterlimit:2;paint-order:markers stroke fill"
d="m 78.97546,112.67054 c 0,0 0.328086,-8.68441 0.894951,-9.05631 0.250018,-0.26677 20.531119,-2.23686 29.085419,-2.70407 0,0 0.67902,0.79611 0.26789,3.34145 -0.28087,1.89737 -1.32214,3.85795 -2.26857,4.46833 0,0 2.35786,-2.02018 2.04222,-7.09216 -0.24294,-1.24276 -16.11566,1.25585 -16.11566,1.25585 -0.9444,0.13002 -3.9837,8.44222 -4.182038,8.64141 -7.716949,0.30636 -9.975475,2.07833 -9.724212,1.1455 z"
id="path4326" />
<ellipse
style="color:#000000;overflow:visible;vector-effect:none;fill:#d95340;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.32291675;stroke-miterlimit:2;paint-order:markers stroke fill"
id="path4328"
cx="83.578178"
cy="105.82099"
rx="0.56089103"
ry="0.5533585" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#d95340;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.32291675;stroke-miterlimit:2;paint-order:markers stroke fill"
d="m 82.958575,106.18016 c -0.609008,-0.63796 -1.992334,0.66651 -1.190515,0.73933 0.241749,0.007 0.367914,-0.30147 0.667075,-0.36694 0.218751,-0.0493 0.405768,0.18468 0.304921,0.35634 -0.450027,0.60808 -0.52823,0.84153 -0.844167,1.0729 -0.342452,0.21496 -0.898548,-0.16695 -0.819023,0.5888 0.03007,0.27606 1.131107,0.33876 1.658155,-0.54686 0.210475,-0.32253 0.80175,0.0775 0.588739,0.34142 -0.19038,0.31417 -0.567977,0.48775 -0.515963,0.88076 -0.0052,0.25699 0.426881,0.2478 0.558629,0.0624 0.657588,-0.72321 1.318084,-1.43322 0.407796,-1.74455 0.561929,-1.11103 0.816182,-0.17457 1.415261,-1.56037 0.157536,-0.3471 -0.41929,-0.48303 -0.450941,-0.26601 -0.46683,0.97779 -1.363227,0.8736 -1.779967,0.4428 z"
id="path4330" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:url(#linearGradient4332);fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
d="m 123.49758,49.067522 c 0,0 -0.0705,53.468648 -0.75764,56.936138 -1.16535,6.57928 -2.63078,11.73021 -8.70444,14.39287 7.92002,-1.50118 27.53396,-6.01221 28.28571,-9.65942 0.55208,-11.401498 -0.27021,-58.432029 -0.27021,-58.432029 0,0 -14.7388,-3.295701 -18.55342,-3.237559 z"
id="path4309" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#4e5457;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
d="M 78.30836,54.454564 70.432643,98.331526 71.2757,98.383852 78.666579,54.29057 Z"
id="path4334" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#70757c;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
d="m 124.8123,72.186612 0.85382,1.931306 -0.85382,2.370025 1.91393,-2.370025 z"
id="path4488" />
<path
inkscape:connector-curvature="0"
id="path4490"
d="m 107.41661,73.119557 -0.85382,1.931306 0.85382,2.370025 -1.91393,-2.370025 z"
style="color:#000000;overflow:visible;vector-effect:none;fill:#70757c;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill" />
<path
inkscape:connector-curvature="0"
id="path4509"
d="m 107.37833,95.267241 -26.511892,2.511244 c -0.135763,0.721764 -0.424341,1.755042 1.875312,1.852584 3.500617,0.148482 18.61654,-0.846935 23.27085,-2.164032 1.36306,-0.385724 1.46754,-1.608614 1.36573,-2.199796 z"
style="color:#000000;overflow:visible;vector-effect:none;fill:url(#linearGradient4511);fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#5b585b;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
d="m 78.284745,97.100947 c 0.680588,-14.4694 3.333553,-29.668628 5.470204,-44.347429 l 0.889389,-0.176348 c -2.502774,14.715793 -5.552664,34.507894 -5.612737,44.100552 z"
id="path4513" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#5b585b;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
d="M 80.725112,96.438914 C 81.357027,81.702915 83.4597,67.618893 84.828255,53.20944 83.659178,67.637017 81.570622,81.500782 81.343904,96.506198 Z"
id="path4515" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#52565e;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
d="m 106.78688,97.624749 v 0.980036 h -0.98004 v 0.489104 h 0.98004 v 0.980031 h 0.48913 v -0.980031 h 0.98002 v -0.489104 h -0.98002 v -0.980036 z"
id="rect4517" />
<rect
style="color:#000000;overflow:visible;vector-effect:none;fill:#52565e;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
id="rect4522"
width="2.7589374"
height="0.51933014"
x="78.916763"
y="100.49229" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#d7252a;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
d="m 90.251984,108.80352 -1.294175,-0.0345 1.566199,-3.31249 1.344012,-0.0542 z"
id="path4524" />
<path
inkscape:connector-curvature="0"
id="path4528"
d="m 96.80332,107.91957 -1.294174,-0.0345 1.566199,-3.31249 1.344011,-0.0542 z"
style="color:#000000;overflow:visible;vector-effect:none;fill:#d7252a;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;vector-effect:none;fill:#d7252a;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.54999999, 3.29999996;paint-order:markers stroke fill"
d="m 103.35466,107.21316 -1.29418,-0.0345 1.5662,-3.31248 1.34401,-0.0543 z"
id="path4530" />
</g>
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Leaders"
style="display:inline">
<text
xml:space="preserve"
style="color:#000000;font-weight:900;font-size:4.23333359px;line-height:3.17500019px;font-family:Lato;-inkscape-font-specification:'Lato, Heavy';text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;overflow:visible;vector-effect:none;fill:#d7252a;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.55000001, 3.30000008;paint-order:markers stroke fill"
x="54.122028"
y="28.876167"
id="text4534"><tspan
id="tspan4532"
x="54.122028"
y="28.876167"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;stroke-width:0.55000001">Button 0</tspan></text>
<text
id="text4538"
y="30.384899"
x="151.17892"
style="color:#000000;font-weight:900;font-size:4.23333359px;line-height:3.17500019px;font-family:Lato;-inkscape-font-specification:'Lato, Heavy';text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;overflow:visible;vector-effect:none;fill:#d7252a;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.55000001, 3.30000008;paint-order:markers stroke fill"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;stroke-width:0.55000001"
y="30.384899"
x="151.17892"
id="tspan4536">Button 1</tspan></text>
<text
xml:space="preserve"
style="color:#000000;font-weight:900;font-size:4.23333359px;line-height:3.17500019px;font-family:Lato;-inkscape-font-specification:'Lato, Heavy';text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;overflow:visible;vector-effect:none;fill:#d7252a;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.55000001, 3.30000008;paint-order:markers stroke fill"
x="130.88269"
y="18.324669"
id="text4542"><tspan
id="tspan4540"
x="130.88269"
y="18.324669"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;stroke-width:0.55000001">Button 2</tspan></text>
<text
id="text4546"
y="80.354729"
x="31.912523"
style="color:#000000;font-weight:900;font-size:4.23333359px;line-height:3.17500019px;font-family:Lato;-inkscape-font-specification:'Lato, Heavy';text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;overflow:visible;vector-effect:none;fill:#d7252a;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.55000001, 3.30000008;paint-order:markers stroke fill"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;stroke-width:0.55000001"
y="80.354729"
x="31.912523"
id="tspan4544">Button 3</tspan></text>
<text
xml:space="preserve"
style="color:#000000;font-weight:900;font-size:4.23333359px;line-height:3.17500019px;font-family:Lato;-inkscape-font-specification:'Lato, Heavy';text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;overflow:visible;vector-effect:none;fill:#d7252a;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.55000001, 3.30000008;paint-order:markers stroke fill"
x="29.855825"
y="99.495857"
id="text4550"><tspan
id="tspan4548"
x="29.855825"
y="99.495857"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;stroke-width:0.55000001">Button 5</tspan></text>
<text
id="text4554"
y="111.76219"
x="29.400412"
style="color:#000000;font-weight:900;font-size:4.23333359px;line-height:3.17500019px;font-family:Lato;-inkscape-font-specification:'Lato, Heavy';text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;overflow:visible;vector-effect:none;fill:#d7252a;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.55000001, 3.30000008;paint-order:markers stroke fill"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;stroke-width:0.55000001"
y="111.76219"
x="29.400412"
id="tspan4552">Button 6</tspan></text>
<text
xml:space="preserve"
style="color:#000000;font-weight:900;font-size:4.23333359px;line-height:3.17500019px;font-family:Lato;-inkscape-font-specification:'Lato, Heavy';text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;overflow:visible;vector-effect:none;fill:#d7252a;fill-opacity:1;stroke-width:0.55000001;stroke-dasharray:0.55000001, 3.30000008;paint-order:markers stroke fill"
x="173.11214"
y="85.690308"
id="text4558"><tspan
id="tspan4556"
x="173.11214"
y="85.690308"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;stroke-width:0.55000001">Button 4</tspan></text>
<path
style="color:#000000;overflow:visible;vector-effect:none;fill:none;stroke:#000000;stroke-width:0.5291667;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;paint-order:markers stroke fill"
d="M 63.124178,31.772128 94.291591,60.577023"
id="path4345"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;overflow:visible;vector-effect:none;fill:none;stroke:#000000;stroke-width:0.5291667;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;paint-order:markers stroke fill"
d="M 127.00716,22.400186 113.88429,65.198573"
id="path4347"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;overflow:visible;vector-effect:none;fill:none;stroke:#000000;stroke-width:0.5291667;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;paint-order:markers stroke fill"
d="M 146.57607,34.065047 128.58942,63.193353"
id="path4349"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;overflow:visible;vector-effect:none;fill:none;stroke:#000000;stroke-width:0.5291667;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;paint-order:markers stroke fill"
d="m 160.61332,85.397431 -56.88611,8.243402"
id="path4351"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;overflow:visible;vector-effect:none;fill:none;stroke:#000000;stroke-width:0.5291667;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;paint-order:markers stroke fill"
d="m 44.225079,81.803153 30.802392,13.92351"
id="path4353"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;overflow:visible;vector-effect:none;fill:none;stroke:#000000;stroke-width:0.5291667;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;paint-order:markers stroke fill"
d="M 41.803519,98.888778 63.738687,103.0855"
id="path4355"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;overflow:visible;vector-effect:none;fill:none;stroke:#000000;stroke-width:0.5291667;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;paint-order:markers stroke fill"
d="m 41.540072,110.21938 20.868867,4.82739"
id="path4357"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="display:inline;fill:url(#linearGradient4562-2);fill-opacity:1;stroke:none;stroke-width:0.2822223"
d="m 156.02856,154.56715 -0.83645,1.44908 -0.83644,1.44822 h 0.95396 v 3.0632 l 0.71893,-0.41476 0.71892,0.41476 v -3.0632 h 0.95396 l -0.83644,-1.44822 z"
id="path4556"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient4623-2);fill-opacity:1;stroke:none;stroke-width:0.2822223"
d="m 156.02855,175.12243 -0.83645,-1.44908 -0.83643,-1.44822 h 0.95394 v -3.06321 l 0.71893,0.41477 0.71892,-0.41477 v 3.06321 h 0.95396 l -0.83645,1.44822 z"
id="use4558"
inkscape:connector-curvature="0" />
<text
id="text4505"
y="159.06049"
x="161.79926"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333359px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2822223"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333359px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.2822223"
y="159.06049"
x="161.79926"
id="tspan4507">Button7</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333359px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2822223"
x="161.80814"
y="173.68317"
id="text4509"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333359px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.2822223"
id="tspan4511"
x="161.80814"
y="173.68317">Button8</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,335 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg2"
viewBox="0 0 750.00004 749.99998"
height="750"
width="750"
sodipodi:docname="logitech-mx_master.svg"
inkscape:version="0.92+devel 15739 custom">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
inkscape:document-rotation="0"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1053"
id="namedview55"
showgrid="false"
inkscape:zoom="0.90533333"
inkscape:cx="394.54153"
inkscape:cy="346.39586"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" />
<defs
id="defs4">
<linearGradient
id="linearGradient5204">
<stop
style="stop-color:#868686;stop-opacity:1;"
offset="0"
id="stop5206" />
<stop
style="stop-color:#868686;stop-opacity:0;"
offset="1"
id="stop5208" />
</linearGradient>
<linearGradient
id="linearGradient5128">
<stop
style="stop-color:#3a3a3a;stop-opacity:1"
offset="0"
id="stop5130" />
<stop
style="stop-color:#aaaaaa;stop-opacity:1"
offset="1"
id="stop5132" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient5128"
id="linearGradient3078"
gradientUnits="userSpaceOnUse"
spreadMethod="reflect"
x1="230.63638"
y1="432.30179"
x2="238.061"
y2="447.85815" />
<linearGradient
xlink:href="#linearGradient5204"
id="linearGradient3080"
gradientUnits="userSpaceOnUse"
x1="196.63423"
y1="468.17209"
x2="170.13423"
y2="402.67206" />
</defs>
<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></dc:title>
<cc:license
rdf:resource="https://opensource.org/licenses/MIT" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Device">
<g
id="layer1-0"
transform="matrix(1.066667,0,0,1.066667,557.63834,-227.02703)"
style="display:inline">
<g
style="stroke-width:1.03775251"
id="g3058"
transform="matrix(0.6738802,0,0,0.67794969,-190.55606,295.44166)">
<path
inkscape:connector-curvature="0"
id="path5084"
transform="translate(-522.78594,-97.359255)"
d="m 921.14258,351.92188 c -10.71472,0.3561 17.48242,40.65039 17.48242,40.65039 l -0.99805,38.58007 c -0.0632,-0.10181 -0.1163,-0.2052 -0.18164,-0.30664 -0.41273,0.4635 -0.8827,0.9276 -1.47656,1.39063 C 883.92622,472.81347 836.5,478.32226 756.5,516.32227 c -80,37.99997 -316.69239,159.29807 -343.5,147.5 -0.19217,-0.0846 -0.3462,-0.18445 -0.50195,-0.28321 -0.14711,0.0864 -0.35538,0.19871 -0.49805,0.28321 -0.0138,-0.0118 -0.0272,-0.0234 -0.041,-0.0352 l -0.0273,0.0156 c 20.65212,21.99316 41.34778,28.42752 134.68945,6.01954 h 67.87891 c 0,0 31.685,6.41255 73,41.25 56.21397,47.40048 70.15272,37.94645 213.83008,7.3789 120.72766,-25.68496 98.22343,-76.56575 107.07816,-168.1289 5.6329,-58.24749 15.6725,-114.94915 -35.90824,-161 -31.61176,-28.22275 -46.12465,-37.57431 -51.35742,-37.40039 z"
style="fill:#433737;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.47948277px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path5077"
transform="translate(-522.78594,-97.359255)"
d="m 766.6582,304.5 c -179.15881,0.002 -367.84961,155.86523 -367.84961,155.86523 -21.93235,32.98189 -41.23548,69.54864 -57.98242,109.60157 7.5036,28.04106 30.67382,59.21512 71.17383,93.85547 59.9519,-35.50497 512.63903,-236.02476 512.5,-244 95.7156,51.99999 -269.006,189 -269,189 -12.27104,5.04673 -14.55606,11.0677 -7.5,18 l 2.06445,1.76953 h 0.002 l 11.89062,10.1875 c -0.62249,0.15805 -1.26128,0.30706 -1.87305,0.48242 -47.26127,13.54738 -84.55325,23.64795 -114.41015,30.79101 L 615,669.82227 l 63.40625,-16.95508 38.59961,33.06445 c 38.18376,34.64823 37.84255,32.06806 141.42187,14.14258 C 964.16532,681.77523 962.3307,680.80807 974.39258,545.2168 984.45817,432.06647 1011.3318,377.81074 858.42773,320.35742 828.82733,309.23515 797.88772,304.49969 766.6582,304.5 Z"
style="fill:#3d3d3d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.47948277px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path5079"
transform="translate(-522.78594,-97.359255)"
d="m 924.5,419.82227 c 0.13868,7.95534 -450.25292,207.4789 -512.00586,243.71484 0.15695,0.0995 0.31224,0.19994 0.50586,0.28516 26.80761,11.79809 263.5,-109.50001 343.5,-147.5 80,-38.00001 127.42622,-43.50878 179.46875,-84.08594 0.5964,-0.46501 1.06842,-0.93101 1.48242,-1.39649 -2.56726,-3.98332 -6.80221,-7.67699 -12.95117,-11.01757 z"
style="fill:#826c5e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.03775251" />
<path
inkscape:connector-curvature="0"
id="path5101"
transform="translate(-522.78594,-97.359255)"
d="m 633.2832,328.9668 c -128.50884,43.87332 -234.47461,131.39843 -234.47461,131.39843 -21.86064,32.87406 -41.10756,69.31169 -57.8164,109.21094 l 0.008,-0.004 61.25,-46.25 c -0.31467,-30.27892 8.67871,-33.61166 108.5,-91 105.67745,-60.75512 109.87649,-54.6167 115.74609,-80.49805 1.83541,-8.09301 4.52898,-16.4514 6.78711,-22.85742 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.47948277px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path5107"
transform="translate(-522.78594,-97.359255)"
d="m 640.70703,428.57422 c -3.04651,0.0756 -6.31444,0.73835 -9.96875,2.0918 -19.09188,7.07104 -141.89935,74.20682 -175.71484,98.99414 -48.59449,35.62053 -48.4375,14.14257 -48.4375,14.14257 l -4.24219,-20.85937 c 0,0 -0.0901,0.0489 -0.0937,0.0508 9.1e-4,0.11168 -10e-4,0.2157 0,0.32813 l -61.25,46.25 -0.008,0.004 c -0.0548,0.13093 -0.11124,0.25961 -0.16602,0.39063 7.5036,28.04104 30.67382,59.21512 71.17383,93.85547 0.14966,-0.0886 0.33959,-0.19447 0.49414,-0.28516 0.0169,-0.01 0.0397,-0.0213 0.0566,-0.0312 1.02546,-0.60112 2.16407,-1.25057 3.39844,-1.93945 0.2066,-0.11531 0.43415,-0.23971 0.64648,-0.35743 1.08032,-0.59888 2.2254,-1.22479 3.45118,-1.88476 0.32998,-0.17769 0.67334,-0.36095 1.01367,-0.54297 1.22538,-0.65535 2.51457,-1.33647 3.87109,-2.04687 0.32591,-0.17069 0.65104,-0.34183 0.98438,-0.51563 0.98003,-0.51099 2.03888,-1.05627 3.08203,-1.59375 1.50441,-0.77516 3.19823,-1.63034 4.83007,-2.45898 2.6812,-1.36148 5.48308,-2.77245 8.49219,-4.27149 1.45002,-0.72234 2.95192,-1.46789 4.47461,-2.2207 3.14575,-1.55527 6.43566,-3.16851 9.87696,-4.84766 1.7505,-0.85415 3.50304,-1.70917 5.32617,-2.59375 3.69907,-1.79476 7.57491,-3.665 11.55664,-5.57812 1.62203,-0.77935 3.19102,-1.53607 4.85742,-2.33399 4.85137,-2.32297 9.91021,-4.73306 15.11328,-7.20312 1.05862,-0.50256 2.06054,-0.98002 3.13281,-1.48828 6.27234,-2.97314 12.74159,-6.02841 19.44532,-9.18164 0.19493,-0.0917 0.37504,-0.1777 0.57031,-0.26954 0.88374,-0.4156 1.84183,-0.86083 2.73242,-1.27929 8.89106,-4.17765 18.33343,-8.58591 27.83008,-13.01563 9.48674,-4.42512 19.20352,-8.94432 29.15625,-13.5625 5.63004,-2.6124 11.17539,-5.18811 16.91992,-7.84765 11.03955,-5.11098 22.24511,-10.28776 33.57227,-15.51563 4.59686,-2.12162 9.06381,-4.18813 13.6875,-6.32031 31.79772,-14.66333 64.04275,-29.4992 94.89648,-43.72656 0.64347,-0.29672 1.23671,-0.57103 1.87891,-0.86719 -0.0661,-2.523 -0.5325,-5.27289 -1.4043,-8.3125 -2.95973,-10.3194 -36.14133,-35.93502 -56.06055,-50.55859 C 663.08118,438.83704 654.18972,428.94451 642,428.57812 c -0.42664,-0.0128 -0.85775,-0.0147 -1.29297,-0.004 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.47948277px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
transform="matrix(0.89707316,-0.44188205,0.39545007,0.91848747,0,0)"
ry="4.5092516"
rx="4.7671914"
y="299.94894"
x="-63.730179"
height="22.140448"
width="42.955582"
id="rect5118"
style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:1.48045075;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path5120"
d="m 275.08906,417.33778 4.375,22.875 80.75,-60.625 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.47948277px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path5122"
d="m 281.58906,443.33778 91.75,-68.625 c 10.87423,-8.13345 34.03844,-10.26653 -9.25,21.375 -37.38734,27.32814 -82.5,47.25 -82.5,47.25 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.47948277px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path5124"
d="m 197.58906,448.96278 c 6.125,13 10.625,29.125 10.625,29.125 0,0 57.34269,-25.16319 68.25,-30.75 -0.13843,-9.48681 -3.52264,-22.72856 -8.375,-27.875 -27.125,3.75 -70.5,29.5 -70.5,29.5 z"
style="fill:url(#linearGradient3078);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.47948301;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path5138"
d="m 3.47828,341.26173 c 0,0 6.886406,9.46214 7.95495,30.22881 -9.7125082,0.38797 -22.45064,-1.59098 -48.96714,10.78339 -26.5165,12.37437 -35.70889,37.29989 -35.70889,37.29989 l -6.84404,-0.46967 -8.1731,-22.75242 c 0,0 5.196794,-22.7334 30.485095,-40.41753 C -32.486544,338.25007 3.47828,341.26173 3.47828,341.26173 Z"
style="fill:#797979;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.47948277px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path5140"
transform="translate(-522.78594,-97.359255)"
d="m 527.89648,441.94336 c -11.21543,-6.8e-4 -35.91089,1.15173 -54.02148,10.37891 -20.66776,10.53002 -34.76604,36.66892 -38.41016,44.00195 l 7.17383,19.9707 c 2.6388,-7.65898 11.92913,-28.82418 37.36133,-42.34765 27.46537,-14.60461 48.83613,-10.8705 53.86328,-9.67383 -1.11415,-11.22385 -3.86953,-18.48609 -5.74414,-22.32617 -0.0929,-1.7e-4 -0.12786,-0.004 -0.22266,-0.004 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.03775251" />
<path
inkscape:connector-curvature="0"
id="path5145"
transform="translate(-522.78594,-97.359255)"
d="m 485.25195,479.63281 c -26.5165,12.37437 -35.70886,37.34486 -35.70886,37.34486 9.75656,-6.29912 84.5483,-48.37571 84.5483,-48.37571 -6.04962,0.7682 -22.32294,-1.34352 -48.83944,11.03085 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.03775251" />
<path
inkscape:connector-curvature="0"
id="path5151"
d="m -6.1035156,230.91992 c -19.3024674,3.60682 -35.0488854,16.31894 -52.6556354,24.21599 -51.094769,27.0536 -101.437299,55.94388 -149.051399,88.65901 -9.95415,5.51243 -22.54214,11.99271 -33.83984,6.61914 -6.38959,-3.90926 -7.96324,-11.94005 -8.66992,-18.82617 -15.28799,12.40039 -32.51253,23.07504 -46.6875,36.30469 11.43671,32.54466 37.53518,57.07166 61.90234,80.5625 1.79409,1.58773 4.14015,-3.28155 6.15485,-2.80815 C -144.1825,402.09598 -56.798635,363.92186 29.448769,323.50255 46.66114,315.61589 63.86767,307.71041 80.912109,299.46484 75.326356,287.31278 62.865961,280.7519 53.502952,271.87742 35.5688,257.98999 18.22755,242.94827 -1.484375,231.63672 c -1.4916064,-0.46547 -3.0527704,-0.75113 -4.6191406,-0.7168 z"
style="display:inline;fill:#868686;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.03775251"
transform="translate(124.57988,108.29077)" />
<path
inkscape:connector-curvature="0"
id="path5162"
d="m -28.414062,137.02148 c -76.907488,29.50803 -148.819988,71.80459 -213.308758,122.89206 -13.55128,17.43946 -23.11685,37.54603 -33.88323,56.82187 -3.7754,7.32186 -7.34192,14.58222 -10.84707,22.04584 11.21464,-8.37958 22.86793,-16.47415 33.20898,-25.8125 -0.25698,-17.631 11.68533,-33.06792 26.42383,-41.59375 31.52229,-22.08766 66.11652,-39.23093 99.04413,-59.05073 30.395219,-17.37588 61.961588,-32.92482 91.295711,-52.0313 6.692064,-6.28955 7.910236,-15.49613 9.814453,-23.93359 l -0.935164,0.35421 z"
style="display:inline;fill:#868686;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.03775251"
transform="translate(124.57988,108.29077)" />
<path
inkscape:connector-curvature="0"
id="path5173"
d="m 152.77364,310.2307 c 1.1582,6.05599 2.3164,12.11198 3.4746,18.16797 21.37631,-16.04948 42.75261,-32.09896 64.12891,-48.14843 -22.5345,9.99349 -45.06901,19.98697 -67.60351,29.98046 z"
style="display:inline;fill:#868686;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.03775251"
transform="translate(124.57988,108.29077)" />
<path
inkscape:connector-curvature="0"
id="path5187"
d="m 261.2834,263.28539 c -12.21657,2.16029 -20.42211,12.86093 -30.56476,19.12846 -18.40968,13.8199 -38.36778,28.59535 -55.81414,41.82076 29.28272,-15.22709 58.48771,-31.62916 82.88671,-54.0625 1.70896,-1.96865 4.73868,-4.48833 4.69532,-6.85547 -0.39767,-0.0641 -0.80252,-0.0596 -1.20313,-0.0312 z"
style="display:inline;fill:#868686;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.03775251"
transform="translate(124.57988,108.29077)" />
<path
inkscape:connector-curvature="0"
id="path5192"
d="m 42.546875,449.04102 c 19.339052,15.73498 36.723013,34.10436 57.603515,47.67578 20.60808,9.36671 43.55773,2.08926 64.83959,-0.49809 35.57938,-6.15258 71.49952,-11.18454 106.39931,-20.59787 15.04923,-4.60526 29.72058,-14.93837 34.46618,-30.81811 9.43121,-25.52623 9.54274,-53.05503 12.87313,-79.81517 -0.62149,-4.24546 3.55982,-14.72991 -0.52003,-14.51534 -91.88723,32.85626 -183.77446,65.71253 -275.661695,98.5688 z"
style="display:inline;fill:url(#linearGradient3080);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.03775251"
transform="translate(124.57988,108.29077)" />
<path
inkscape:connector-curvature="0"
id="rect5212"
d="m -27.103516,182.32422 c -10.397754,4.71799 -21.409505,9.91406 -30.929687,15.4375 2.508683,3.97971 3.88786,8.5168 5.707031,12.72266 10.007293,-5.84796 21.032198,-9.89858 31.039063,-15.81641 -1.598593,-4.02995 -3.118058,-9.77197 -5.816407,-12.34375 z"
style="display:inline;fill:#868686;fill-opacity:1;stroke:none;stroke-width:1.03775251"
transform="translate(124.57988,108.29077)" />
</g>
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Leaders">
<text
id="text4473"
y="565.3598"
x="102.38671"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"
transform="scale(0.99699417,1.0030149)"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
y="565.3598"
x="102.38671"
id="tspan4475">Button0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="108.10234"
y="242.15002"
id="text4477"
transform="scale(0.99699417,1.0030149)"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
id="tspan4479"
x="108.10234"
y="242.15002">Button1</tspan></text>
<text
id="text4481"
y="371.8588"
x="100.09487"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"
transform="scale(0.99699417,1.0030149)"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
y="371.8588"
x="100.09487"
id="tspan4483">Button2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="653.47821"
y="157.51355"
id="text4485"
transform="scale(0.99699417,1.0030149)"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
id="tspan4487"
x="653.47821"
y="157.51355">Button3</tspan></text>
<text
id="text4489"
y="93.571732"
x="449.42313"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"
transform="scale(0.99699417,1.0030149)"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
y="93.571732"
x="449.42313"
id="tspan4491">Button4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="448.09875"
y="620.54236"
id="text4493"
transform="scale(0.99699417,1.0030149)"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
id="tspan4495"
x="448.09875"
y="620.54236">Button5</tspan></text>
<text
id="text4497"
y="123.03238"
x="209.7597"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"
transform="scale(0.99699417,1.0030149)"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696"
y="123.03238"
x="209.7597"
id="tspan4499">Button6</tspan></text>
<path
inkscape:connector-curvature="0"
id="path3082"
d="m 162.38897,251.08006 123.99755,90.72462"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" />
<path
inkscape:connector-curvature="0"
id="path3084"
d="m 166.48807,366.54776 151.6664,-7.21673"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" />
<path
inkscape:connector-curvature="0"
id="path3086"
d="M 154.19079,550.05892 287.41129,438.71507"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" />
<path
inkscape:connector-curvature="0"
id="path3088"
d="M 274.08923,125.30276 410.38405,310.87584"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" />
<path
inkscape:connector-curvature="0"
id="path3092"
d="m 487.24203,102.62161 83.00662,283.51442"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" />
<path
inkscape:connector-curvature="0"
id="path3094"
d="M 694.24619,166.54122 625.5864,361.39295"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" />
<path
inkscape:connector-curvature="0"
id="path3096"
d="m 502.61363,600.57604 87.10572,-89.69366"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,445 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
version="1.1"
id="svg2"
viewBox="0 0 750.00004 749.99998"
height="750"
width="750"
sodipodi:docname="roccat-kone-xtd.svg"
inkscape:version="0.92+devel 15739 custom">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
inkscape:document-rotation="0"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1053"
id="namedview91"
showgrid="false"
inkscape:zoom="1.2803347"
inkscape:cx="378.62971"
inkscape:cy="438.51164"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" />
<defs
id="defs4" />
<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></dc:title>
<cc:license
rdf:resource="https://opensource.org/licenses/MIT" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Device"
style="display:inline">
<g
id="layer1-5"
transform="matrix(1.066667,0,0,1.066667,557.63834,-227.02703)"
style="display:inline">
<g
style="stroke-width:1.08492351"
id="g3136"
transform="matrix(0.63485207,0,0,0.63485207,-238.79655,274.87627)">
<path
inkscape:connector-curvature="0"
id="path4770"
d="m -6.8766439,519.77381 c -32.5046101,10.85914 -64.2932331,9.07935 -55.9542041,-23.77308 18.883288,-74.39257 -57.845532,2.41084 -29.233145,-133.3763"
style="fill:none;stroke:#000000;stroke-width:32.00106812;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3661"
d="m -33.934887,422.04568 10.14038,-25.10953 C 52.122953,323.19727 160.74504,269.07207 269.83981,256.48052 c 114.24665,-13.18617 232.89306,-1.04277 284.3675,72.85307 67.68352,97.16539 16.07476,170.11753 -8.69176,195.56454 -20.05502,20.60604 -39.26023,38.76732 -57.46217,50.70193 -38.08063,24.96863 -99.02413,29.12078 -118.30443,27.5239 -56.28478,-4.66179 -227.65266,41.307 -235.6432,46.83891 L 32.219046,607.9527 C 4.8894737,559.61921 -21.751345,509.50644 -33.934887,422.04568 Z"
style="fill:#293843;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4463"
d="m 520.63342,372.91486 c -22.02158,0.11026 -45.15654,9.22867 -63.99943,19.07328 -68.64532,35.8642 -269.2094,153.12888 -338.02986,180.28255 l -87.122352,34.37512 c 0.245863,0.43536 0.49112,0.87206 0.737105,1.30711 l 39.216027,16.16965 c 44.83999,-15.41316 98.86516,-40.41176 154.72423,-67.21674 90.48008,-43.41854 177.45369,-92.45774 236.96271,-121.89569 60.39622,-29.87684 105.87888,-24.18751 112.83284,44.85426 6.15884,-13.91293 10.75607,-30.17964 11.63778,-48.45529 -13.44827,-44.58801 -39.32739,-58.63259 -66.95905,-58.49425 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4468"
d="m 460.17444,405.06363 c -25.33816,12.5814 -164.20311,89.16529 -247.36725,132.94909 -31.24494,16.55348 -62.91668,32.44366 -95.5461,46.08015 l 3.13236,9.65662 c 54.85379,-22.99389 108.07344,-49.78006 161.39646,-76.09146 64.31558,-32.64428 127.12882,-68.2385 192.32459,-99.13545 18.32983,-7.64259 38.30614,-12.8303 58.27155,-10.58417 14.47996,1.85097 28.06901,9.59531 37.24885,20.96153 6.66994,-53.74538 -76.10417,-40.33748 -109.46046,-23.83631 z"
style="fill:#00ff3a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4545"
d="m 270.88842,559.80819 26.12051,4.09732 62.14289,-30.90074 c 11.97455,-20.17142 5.93562,-23.53024 5.29239,-31.92505 l -91.50713,47.4608 c -3.16993,2.58552 -6.42443,5.13123 -2.04866,11.26767 z"
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4547"
d="m 392.10122,486.05615 c 1.23298,4.69552 4.04803,7.598 -1.19506,19.63307 l -9.73117,17.58438 80.75159,-31.4129 c 5.77716,-10.78358 11.4074,-21.61616 11.4384,-34.31516 -2.7867,-11.40006 -8.20083,-9.22506 -13.65779,-6.82888 z"
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4549"
d="M 20.837165,461.33753 26.414193,480.9345 62.94875,466.25238 55.307676,431.63187 Z"
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4551"
d="m 178.69842,365.35554 12.29201,31.24217 56.50906,-25.09617 -12.46274,-27.48628 z"
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4553"
d="m 261.4987,335.64987 11.95056,26.63267 49.85089,-20.31594 12.80416,-3.41445 -6.82888,-14.16994 -12.97489,-3.41445 z"
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
d="m 204.43745,364.0435 2.09895,5.60714 5.81901,-2.56255 0.64062,1.71134 -5.81901,2.56254 2.09895,5.60714 -1.75511,0.77292 -2.09894,-5.60715 -5.81902,2.56255 -0.64059,-1.71135 5.81898,-2.56254 -2.09892,-5.60714 z"
id="path4560"
style="font-style:normal;font-weight:normal;font-size:medium;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;writing-mode:tb-rl;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
style="font-style:normal;font-weight:normal;font-size:medium;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1.08492351"
id="path4567"
d="m 305.42303,329.94388 4.03768,8.95391 -2.72436,1.22853 -4.03769,-8.95391 z" />
<path
inkscape:connector-curvature="0"
d="m 46.989112,448.7916 2.557622,11.39542 -2.682847,1.56354 -2.557622,-11.39545 z"
id="path4569"
style="font-style:normal;font-weight:normal;font-size:medium;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
style="font-style:normal;font-weight:normal;font-size:medium;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1.08492351"
id="path4577"
d="m 42.643233,451.50777 2.557622,11.39545 -2.682847,1.56351 -2.557622,-11.39545 z" />
<path
inkscape:connector-curvature="0"
d="m 38.478433,454.34466 2.557622,11.39543 -2.682847,1.56354 -2.557622,-11.39545 z"
id="path4579"
style="font-style:normal;font-weight:normal;font-size:medium;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4581"
d="m 119.43841,465.4441 5.37199,3.74229 1.87114,-8.26926 z"
style="fill:#838d96;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4583"
d="m 99.218,376.23286 -2.29366,6.51884 6.33774,-3.25942 z"
style="fill:#838d96;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4585"
d="m 370.13999,299.12951 c -0.43414,-0.004 -0.88599,0.004 -1.35658,0.0243 -114.61991,18.29248 -262.7613,57.8235 -362.6396132,162.97056 l -0.2413441,18.34924 -23.0804397,18.75305 c 0.295394,0.96107 0.592605,1.91666 0.892085,2.8672 L 6.8682998,485.78508 11.938416,498.5815 c 3.022522,1.91455 4.542353,4.99753 13.03731,2.65575 0,0 35.049508,-13.87527 66.878294,-31.62836 25.86269,-14.4254 169.13513,-92.29377 297.45122,-135.68783 3.53168,-1.85807 6.92052,-3.82383 2.89753,-11.3479 l -11.34789,-17.38339 c -1.68431,-3.27833 -4.20267,-6.0005 -10.71489,-6.06024 z m 0.85055,7.18994 c 0.76127,-0.0183 1.49621,0.14715 2.1413,0.68389 2.28062,2.60105 3.95393,5.73706 5.90572,8.6044 2.36789,3.85426 5.13602,7.51375 7.31692,11.43239 -2.00889,0.76437 -4.02217,1.5172 -6.0383,2.2622 -74.38121,26.47497 -146.51346,58.91354 -216.93155,94.53357 -23.7859,11.80119 -47.13201,24.45131 -70.49007,37.07193 -21.87976,12.03816 -44.368571,22.99172 -67.494965,32.39674 -2.178032,0.79387 -4.91012,2.49014 -7.139522,0.97798 -2.462342,-3.62775 -3.065109,-8.18739 -5.10326,-12.05744 -0.191754,-5.72806 0.125694,-11.45575 0.125574,-17.18406 19.24401,-19.99827 40.662677,-37.85032 63.501803,-53.60899 56.88977,-39.14613 121.81546,-65.28367 188.25625,-83.14833 34.45174,-9.02055 69.30996,-16.66573 104.56505,-21.77335 0.46215,-0.10291 0.9283,-0.17995 1.38505,-0.19093 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4590"
d="m 69.56866,436.43193 c 2.34022,-18.39325 11.0874,-31.41629 35.56456,-46.25604 24.47717,-14.83973 51.02774,-12.17822 62.53237,-11.10613 4.19739,0.39116 6.14741,2.1271 9.65751,8.20888 l 9.65751,18.10785 c -9.72905,-3.24303 -45.31306,0.55135 -63.36379,8.82104 -23.93977,10.96767 -32.63451,13.417 -49.78556,44.90031 z"
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<text
transform="matrix(0.81792144,-0.30782972,0,1.2226113,0,0)"
id="text4751"
y="544.12469"
x="541.47949"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1.08492351"
xml:space="preserve"><tspan
y="544.12469"
x="541.47949"
id="tspan4753"
style="font-size:19.26995659px;line-height:1.25;stroke-width:1.08492351">&gt;</tspan></text>
<path
inkscape:connector-curvature="0"
id="path4759"
d="m 300.25264,556.5858 10.24335,-10.8622 0.29876,2.6035 -4.75887,5.05767 6.35939,-0.96032 0.72556,2.19804 z"
style="fill:#838d96;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<path
inkscape:connector-curvature="0"
id="path4761"
d="m 175.51398,404.23374 c -14.95857,0.0867 -38.3591,3.7713 -51.89715,9.97357 -23.8618,10.93194 -32.57995,13.40905 -49.62048,44.60172 l 112.98222,-53.41898 -0.0122,-0.008 c -2.4337,-0.80767 -6.47543,-1.17722 -11.45245,-1.14838 z"
style="fill:#151d23;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.08492351" />
<ellipse
ry="11.985341"
rx="11.98534"
cy="294.12094"
cx="-12.758862"
id="path4352"
style="display:inline;fill:#898989;fill-opacity:1;stroke:none;stroke-width:1.08492351"
transform="translate(124.57988,108.29077)" />
</g>
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Leaders"
style="display:inline">
<text
id="text4473"
y="573.11182"
x="147.83389"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
id="tspan4217"
y="573.11182"
x="147.83389"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="147.83389"
y="593.82111"
id="text4212"><tspan
x="147.83389"
y="593.82111"
id="tspan4214"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button12</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="425.59845"
y="619.85443"
id="text4216"><tspan
x="425.59845"
y="619.85443"
id="tspan4218"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button3</tspan></text>
<text
id="text4220"
y="642.56366"
x="425.59845"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
id="tspan4222"
y="642.56366"
x="425.59845"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button15</tspan></text>
<text
id="text4238"
y="623.47388"
x="612.12598"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
id="tspan4240"
y="623.47388"
x="612.12598"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="612.12598"
y="644.18311"
id="text4242"><tspan
x="612.12598"
y="644.18311"
id="tspan4244"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button16</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 436.11779,601.86162 71.66472,-160.7027"
id="path3163"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 635.91034,602.58551 593.92495,397.00187"
id="path3165"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="578.06567"
y="124.26476"
id="text4258"><tspan
x="578.06567"
y="124.26476"
id="tspan4260"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button10</tspan></text>
<text
id="text4262"
y="145.71872"
x="578.06567"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
id="tspan4264"
y="145.71872"
x="578.06567"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button22</tspan></text>
<text
id="text4268"
y="80.422646"
x="374.27548"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
id="tspan4270"
y="80.422646"
x="374.27548"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button9</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="374.27548"
y="101.5351"
id="text4272"><tspan
x="374.27548"
y="101.5351"
id="tspan4274"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button21</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="233.21674"
y="657.15039"
id="text4278"><tspan
x="233.21674"
y="657.15039"
id="tspan4280"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button5</tspan></text>
<text
id="text4282"
y="677.85962"
x="233.21674"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
id="tspan4284"
y="677.85962"
x="233.21674"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button17</tspan></text>
<text
id="text4288"
y="459.87857"
x="113.53596"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
id="tspan4290"
y="459.87857"
x="113.53596"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button11</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="113.53596"
y="480.58783"
id="text4292"><tspan
x="113.53596"
y="480.58783"
id="tspan4294"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button23</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="97.079071"
y="317.79367"
id="text4340"><tspan
x="97.079071"
y="317.79367"
id="tspan4342"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button1</tspan></text>
<text
id="text4344"
y="339.46393"
x="97.079071"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
id="tspan4346"
y="339.46393"
x="97.079071"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button13</tspan></text>
<text
id="text4350"
y="203.12646"
x="170.02243"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
id="tspan4352"
y="203.12646"
x="170.02243"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="170.02243"
y="225.13828"
id="text4354"><tspan
x="170.02243"
y="225.13828"
id="tspan4356"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button14</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
x="234.68954"
y="108.61727"
id="text4360"><tspan
x="234.68954"
y="108.61727"
id="tspan4362"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button6</tspan></text>
<text
id="text4364"
y="130.57028"
x="234.68954"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666696"
xml:space="preserve"><tspan
id="tspan4366"
y="130.57028"
x="234.68954"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:1.06666696">Button18</tspan></text>
<path
inkscape:connector-curvature="0"
id="path3167"
d="M 263.10902,638.05592 383.99798,381.07636"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3171"
d="M 201.5787,551.18959 325.36322,445.50222"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3173"
d="M 184.20544,444.77834 329.70653,382.52413"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3175"
d="M 597.54438,152.32838 510.67805,292.76226"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3177"
d="m 413.67732,110.34298 33.29875,204.13587"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3179"
d="m 270.34788,142.91784 97.00073,180.24763"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3181"
d="M 221.12363,235.57527 376.03525,337.64321"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path3183"
d="m 176.96658,323.16547 131.02338,28.95545"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -1,553 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="414.6424mm"
height="285.98199mm"
viewBox="0 0 1469.2054 1013.322"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="etekcity.svg">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient4449">
<stop
style="stop-color:#a6a6a6;stop-opacity:1"
offset="0"
id="stop4451" />
<stop
style="stop-color:#484848;stop-opacity:1"
offset="1"
id="stop4453" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient4336">
<stop
style="stop-color:#898989;stop-opacity:1;"
offset="0"
id="stop4338" />
<stop
style="stop-color:#898989;stop-opacity:0;"
offset="1"
id="stop4340" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4449"
id="linearGradient4562"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.698701,0,0,1.698701,340.78058,475.26804)"
x1="31.968746"
y1="18.546629"
x2="31.968746"
y2="29.577879" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4449"
id="linearGradient4215"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.698701,0,0,1.698701,111.54723,131.64433)"
x1="31.968746"
y1="18.546629"
x2="31.968746"
y2="29.577879" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4336"
id="linearGradient4219"
gradientUnits="userSpaceOnUse"
x1="97.085503"
y1="74.08123"
x2="95.471596"
y2="200.83122"
gradientTransform="translate(0,85.407996)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.67909341"
inkscape:cx="650.93837"
inkscape:cy="527.80453"
inkscape:document-units="px"
inkscape:current-layer="layer6"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
fit-margin-top="50"
fit-margin-left="50"
fit-margin-right="50"
fit-margin-bottom="50"
inkscape:window-width="1920"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<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:label="Mouse"
inkscape:groupmode="layer"
id="layer1"
transform="translate(522.78594,97.359194)"
style="display:inline">
<g
id="g4271"
style="fill:#000000;fill-opacity:1;stroke:none"
transform="translate(0,85.407996)">
<path
sodipodi:nodetypes="cczcccccccc"
inkscape:connector-curvature="0"
id="path4249"
d="m 421.23361,409.14757 c 4.4349,4.09496 9.16721,10.86185 32.57742,27.27412 4.08118,24.47294 2.40765,48.35999 1.01015,67.42768 -1.3975,19.06769 -9.52288,85.7367 -30.55711,85.7367 l -41.92133,-6.18719 8.9651,-171.72593 32.38801,-150.26019 27.05315,-2.27284 -2.70689,27.21098 -26.19294,53.47495 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<use
height="100%"
width="100%"
transform="matrix(-1,0,0,1,580.95786,0)"
id="use4266"
xlink:href="#path4249"
y="0"
x="0"
style="fill:#000000;fill-opacity:1;stroke:none" />
</g>
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 454.66443,352.50287 c -1.36013,24.01275 -2.08377,48.50286 -4.67196,71.59456 -2.99078,6.00192 -6.27359,9.95971 -9.59645,13.63706 l -17.17259,0 7.07107,-84.85281 z"
id="path4251"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 438.62826,441.14375 -0.37881,7.82869 8.9651,7.3236 -2.77792,38.13326 -11.36421,12.12183 -0.37881,36.61803 -3.03046,4.54569 -18.43528,0 0,-106.69736 z"
id="path4253"
inkscape:connector-curvature="0" />
<use
x="0"
y="0"
xlink:href="#path4251"
id="use4275"
transform="matrix(-1,0,0,1,580.95786,4.8707099e-7)"
width="100%"
height="100%" />
<ellipse
style="fill:#000000;fill-opacity:1;stroke:#ff0000;stroke-opacity:1"
id="path4243"
cx="290.47894"
cy="231.07376"
rx="57.5"
ry="36.875" />
<use
x="0"
y="0"
xlink:href="#path4253"
id="use4277"
transform="matrix(-1,0,0,1,580.95786,4.8707099e-7)"
width="100%"
height="100%" />
<path
style="fill:#282828;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 287.12424,258.10527 0.33399,-51.40625 c -49.49582,9.30987 -55.75059,5.0826 -65.29102,1.07031 -24.72942,18.12217 -56.91711,38.05902 -92.98242,48.79297 -3.12319,24.73221 -3.43113,59.04672 -2.64063,84.07422 24.02292,13.33912 22.23564,42.84183 24.32227,61.05859 2.08662,18.21675 9.10156,119.0332 9.10156,119.0332 l -3.28125,27.4375 29.4375,54.3711 c -0.20477,8.84869 -0.59006,18.78502 -3.83008,47.24609 -9.03868,2.58062 -17.44268,1.97853 -25.29101,-1.39258 0.11517,13.78573 -0.8627,27.56815 0.71875,41.27539 14.20182,16.9502 20.70885,34.21986 70.98828,70.96485 l 8.20703,14.6914 c 16.51245,9.42952 29.26306,14.71639 53.5625,18.88477 24.29944,-4.16838 37.05005,-9.45525 53.5625,-18.88477 l 8.20703,-14.6914 c 50.27943,-36.74499 56.78646,-54.01465 70.98828,-70.96485 1.58145,-13.70724 0.60163,-27.48966 0.7168,-41.27539 -7.84833,3.37111 -16.25038,3.9732 -25.28906,1.39258 -3.24002,-28.46107 -3.62531,-38.3974 -3.83008,-47.24609 l 29.43555,-54.3711 -3.2793,-27.4375 c 0,0 7.01494,-100.81645 9.10156,-119.0332 2.08663,-18.21676 0.29739,-47.71947 24.32031,-61.05859 0.7905,-25.0275 0.48452,-59.34201 -2.63867,-84.07422 -36.06531,-10.73395 -68.25495,-30.6708 -92.98437,-48.79297 -9.54043,4.01229 -15.79325,8.23956 -65.28907,-1.07031 l 0.33399,51.40625 z"
id="path3441"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccscccccccccccccccccscccccc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 290.44411,257.77582 0,148.84598"
id="path4289"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 300.46818,468.48417 16.60743,25.35742 30.71289,0 -24.28516,-81.07226 -33.02344,-17.13086 -33.02539,17.13086 -24.28515,81.07226 30.71484,0 16.60742,-25.35742 z"
id="path4247"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccc" />
<rect
style="fill:#000000;fill-opacity:1;stroke:#ff0000;stroke-opacity:1"
id="rect4245"
width="31.607143"
height="75.178574"
x="274.67535"
y="264.91306"
rx="3.25"
ry="3.25" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Actions"
style="display:inline"
transform="translate(647.36582,205.64997)">
<path
style="fill:url(#linearGradient4219);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 152.84766,110.283 c -17.81104,3.02933 -36.57571,6.1705 -54.244144,0.64648 -25.929275,18.82906 -54.231433,34.7539 -84.722657,44.87305 -2.267978,23.61933 -2.794179,47.45001 -1.847656,71.07617 18.144518,13.21721 22.915716,37.18855 23.587891,58.32032 1.804576,24.12767 4.185653,48.26187 6.046875,72.41992 l 118.802731,0.0762 0,-125.89453 -7.125,0 c -1.8005,0 -3.25,-1.4495 -3.25,-3.25 l 0,-68.67774 c 0,-1.8005 1.4495,-3.25 3.25,-3.25 l 2.79297,0 c -3.65158,-6.86961 -3.48047,-22.71099 -3.48047,-29.125 0,-8.30734 0.18214,-11.47599 0.18946,-17.21484 z"
id="path4311"
inkscape:connector-curvature="0" />
<use
x="0"
y="0"
xlink:href="#path4311"
id="use4348"
transform="matrix(-1,0,0,1,332.05619,4.8707099e-7)"
width="100%"
height="100%" />
<circle
style="fill:#898989;fill-opacity:1;stroke:none;stroke-opacity:1"
id="path4352"
cx="165.89903"
cy="194.21156"
r="13.13577" />
<path
style="fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 137.89648,308.63847 c -7.08138,23.63737 -14.16276,47.27474 -21.24414,70.91211 6.46875,0 12.9375,0 19.40625,0 5.5381,-8.45225 11.07362,-16.9062 16.61329,-25.35742 8.81901,0 17.63802,0 26.45703,0 5.53967,8.45122 11.07518,16.90517 16.61328,25.35742 6.4681,0 12.9362,0 19.40429,0 -7.08138,-23.63802 -14.16276,-47.27604 -21.24414,-70.91406 -9.33398,-4.84245 -18.66797,-9.6849 -28.00195,-14.52735 -9.33463,4.8431 -18.66927,9.6862 -28.00391,14.5293 z"
id="path4357"
inkscape:connector-curvature="0" />
<path
style="fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 10.537109,248.04472 c -2.2402592,0.0287 -3.9856276,0.53324 -4.490234,2.23242 l 0.1484375,2.74414 c 1.566854,20.99222 0.9583123,42.50038 4.9707035,63.15039 2.505291,3.63263 4.593974,10.22639 10.0625,9.10547 2.27923,-0.28382 5.935234,1.35787 7.783203,-0.11133 -0.110687,-1.42127 -0.227935,-3.00203 -0.335938,-4.37109 -1.747534,-15.80777 -2.957208,-31.72139 -4.207031,-47.62695 -0.981314,-8.30104 -2.720121,-16.92373 -6.697266,-24.52539 -2.423075,-0.21119 -5.059619,-0.62548 -7.234375,-0.59766 z"
id="use4396"
inkscape:connector-curvature="0" />
<path
style="fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 29.908203,336.79667 c -2.498699,0.0115 -4.997394,0.0237 -7.496094,0.0352 1.076291,6.12748 -5.106457,9.59114 -8.654297,13.10351 -0.801383,7.60601 1.388334,15.20355 1.240235,22.90625 0.386982,5.74471 -0.452066,12.90012 5.291015,16.36133 4.914007,3.76099 8.544715,9.60791 7.142579,15.97266 0.546689,9.90826 -1.025444,20.199 1.144531,29.53125 1.282147,0.72035 2.637282,0.97704 4.027344,1.01562 l 2.785156,-23.28515 c 0,0 -2.865754,-41.1439 -5.480469,-75.64063 z"
id="use4398"
inkscape:connector-curvature="0" />
<path
style="fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 325.85938,248.27909 c -3.91081,0.0612 -7.82162,0.12234 -11.73243,0.1836 -3.99052,7.54373 -5.75751,16.10502 -6.7539,24.36914 l -4.36914,52.40039 c 4.98584,0.0372 11.46228,2.25512 14.20312,-3.56055 7.33121,-8.30294 4.7292,-20.04995 6.30078,-30.04492 0.96913,-14.44425 1.56431,-28.8791 2.35157,-43.34766 z"
id="path4404"
inkscape:connector-curvature="0" />
<path
style="fill:#898989;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 305.09766,336.58964 c -1.06262,0.008 -2.13053,0.0756 -3.20118,0.15039 -2.61591,34.50868 -5.48437,75.69727 -5.48437,75.69727 l 2.78906,23.33203 c 1.63662,-0.029 3.18696,-0.39571 4.5293,-1.54883 1.37166,-12.4594 -0.25013,-25.66177 1.20508,-38.00586 4.53808,-5.87556 13.22025,-10.32447 11.56445,-19.05078 0.39672,-9.20118 1.90359,-18.85755 1.6582,-27.79492 -4.1312,-2.80281 -9.9803,-6.58258 -8.65039,-12.34961 -1.44987,-0.34149 -2.92443,-0.44095 -4.41015,-0.42969 z"
id="path4406"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient4215);fill-opacity:1;stroke:none;stroke-opacity:1"
d="m 165.89904,157.51634 -3.21162,5.56391 -3.21159,5.56059 3.66282,0 0,11.76151 2.76039,-1.59253 2.76039,1.59253 0,-11.76151 3.66282,0 -3.21161,-5.56059 -3.2116,-5.56391 z"
id="path4441"
inkscape:connector-curvature="0" />
<use
x="0"
y="0"
xlink:href="#path4441"
id="use4460"
transform="matrix(1,0,0,-1,-4.295044e-6,388.30011)"
width="100%"
height="100%" />
</g>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="Leaders"
transform="translate(647.36582,205.64997)"
style="display:inline">
<path
inkscape:connector-curvature="0"
id="path4556"
d="m 395.13239,501.14005 -3.21162,5.56391 -3.21159,5.56059 3.66282,0 0,11.76151 2.76039,-1.59253 2.76039,1.59253 0,-11.76151 3.66282,0 -3.21161,-5.56059 -3.2116,-5.56391 z"
style="display:inline;fill:url(#linearGradient4562);fill-opacity:1;stroke:none;stroke-opacity:1" />
<use
style="display:inline"
height="100%"
width="100%"
transform="matrix(1.0000001,0,0,-1.0000001,229.23333,737.5807)"
id="use4558"
xlink:href="#path4441"
y="0"
x="0" />
<path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 164.75586,327.92021 115.08162,-115.08161 95.36037,0"
id="path4546"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 77.129499,171.52978 32.952959,22.802079"
id="path4211"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 165.48259,195.09061 32.39613,0 0,-238.553341"
id="path4213"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 256.78078,159.74937 333.77774,26.386719"
id="path4215"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 375.15404,291.50573 -61.68003,0"
id="path4221"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 375.28322,364.38474 -64.17738,0"
id="path4225"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -175.41308,446.16063 16.018609,281.97114"
id="path4227"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -177.6219,526.41468 23.381369,366.64285"
id="path4229"
inkscape:connector-curvature="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="Controls"
transform="translate(647.36582,205.64997)"
style="display:inline">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="27.097548"
y="16.66757"
id="text4473"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="27.097548"
y="16.66757"
id="tspan4217">Button0</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4477"
y="21.853914"
x="336.33337"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="21.853914"
x="336.33337"
id="tspan4479"
sodipodi:role="line">Button1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="196.44098"
y="-51.004951"
id="text4481"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4483"
x="196.44098"
y="-51.004951">Button2</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4485"
y="446.01364"
x="-176.11456"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="446.01364"
x="-176.11456"
id="tspan4487"
sodipodi:role="line">Button3</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-177.07159"
y="526.30017"
id="text4489"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4491"
x="-177.07159"
y="526.30017">Button4</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4493"
y="212.67909"
x="375.72412"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="212.67909"
x="375.72412"
id="tspan4495"
sodipodi:role="line">Button5</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="375.72412"
y="288.81161"
id="text4497"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4499"
x="375.72412"
y="288.81161">Button6</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4501"
y="364.41678"
x="375.72412"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="364.41678"
x="375.72412"
id="tspan4503"
sodipodi:role="line">Button7</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="417.32373"
y="526.42987"
id="text4505"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4507"
x="417.32373"
y="526.42987">Button8</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4509"
y="583.15552"
x="417.32373"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="583.15552"
x="417.32373"
id="tspan4511"
sodipodi:role="line">Button9</tspan></text>
<g
id="g4602"
transform="translate(-140.12576,-153.50496)">
<text
sodipodi:linespacing="125%"
id="text4572"
y="109.05057"
x="-431.3205"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4576"
y="109.05057"
x="-431.3205"
sodipodi:role="line">Resolution0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-431.3205"
y="182.02396"
id="text4580"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-431.3205"
y="182.02396"
id="tspan4582">Resolution1</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4584"
y="254.99734"
x="-431.3205"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4586"
y="254.99734"
x="-431.3205"
sodipodi:role="line">Resolution2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-431.3205"
y="327.97079"
id="text4588"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-431.3205"
y="327.97079"
id="tspan4590">Resolution3</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4592"
y="400.94421"
x="-431.3205"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4594"
y="400.94421"
x="-431.3205"
sodipodi:role="line">Resolution4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-431.3205"
y="473.91763"
id="text4596"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-431.3205"
y="473.91763"
id="tspan4598">Resolution5</tspan></text>
</g>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="rect4600"
width="323.85489"
height="479.4184"
x="-615.48309"
y="-116.64217"
rx="10.321068"
ry="10.321068" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-591.40881"
y="625.81189"
id="text4211"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4213"
x="-591.40881"
y="625.81189">Current Profile</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer6"
inkscape:label="Title">
<text
transform="translate(647.36582,205.64997)"
xml:space="preserve"
style="display:inline;font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-271.95749"
y="-131.81583"
id="text4223"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4225"
x="-271.95749"
y="-131.81583"
style="font-size:70.00000763px">Etekcity Scroll Alpha</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 27 KiB

66
data/gnome/README.md Normal file
View File

@@ -0,0 +1,66 @@
libratbag gnome theme
====================
Requirements
------------
See the Logitech G403 for an example of a device with a small amount of buttons,
and the Logitech G700 for an example of a device with a large amount of buttons.
- Canvas size should be between 400x400 and 500x500 pixels.
- Three layers in the final SVG:
1. a lower layer named "Device" with the device itself. Each button on the
device should have an id `buttonX`, with `X` being the number of the button
(so for button 0 the id would be `button0`). Similarly, each LED on the device
should have an id `ledX`.
2. a middle layer named "Buttons" with the button leaders (see below for
leaders).
3. an upper layer named "LEDs" with the LED leaders.
- A leader line is a path that extends from the button or LED to the left or the
right of the device (see below). Each leader line requires the following:
- It should start with a 7x7 square placed on or close to the button or LED
that it maps with.
- From this square, a path should extend left or right (see below).
- Each path should end with a 1x1 pixel with identifier `buttonX-leader` (or
`ledX-leader`), where `X` is the number of the button (or LED) with
which the leader maps. For button 0, this would be `button0-leader`.
- All these elements should be grouped and given the identifier `buttonX-path`
(or `ledX-path` for LEDs)
- Leader lines should have a vertical spacing of at least 40 pixels. When there
are several leader lines above and below each other, make the spacing between
them equal.
- If the device's scroll wheel supports horizontal tilting, add two small arrows
left and right of the scroll wheel with the respective button identifiers (see
the Logitech G700 for an example). Do not cut the scroll wheel in half
vertically to map these buttons.
- If there aren't too many buttons, preferably make the leaders point to the
right with the device itself placed on the left. If the buttons would extend
below or above the device, make some point to the left instead with the device
itself centered in the middle. In this case, half of the leaders should extend
to the left and the other half to the right.
- When a leader points to the right, its 1x1 pixel should have a style property
`text-align:start`. When a leader points to the left, its 1x1 pixel should
have a style property `text-align:end`.
- The canvas should be resized so that there is a 20px gap between the device
and the edge of the canvas and no gap between the 1x1 pixels and the canvas.
Please note that due to the way the SVG is drawn, you cannot rely on the
z-ordering of elements to line up or cover elements (as noted in [this
issue](https://github.com/libratbag/piper/issues/48), which includes links to
examples). As such, please make sure that you align the elements appropriately;
[this
comment](https://github.com/libratbag/piper/issues/48#issuecomment-315979109)
includes some helpful tips.
Technique
---------
The simplest approach is to find a photo of the device and import it into
inkscape. Put it on the lowest layer, create a new layer "Device" above it
and start tracing the outlines and edges of the device. Fill in the shapes
and your device should resemble the underlying photo. Delete the photo
layer, add leaders in their respective layers and you're done.
Make sure the image looks ''toned-down'' and not realistic. Do not use dark or
bright colors.

View File

@@ -0,0 +1,410 @@
<?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="417.20001"
height="407.25"
viewBox="0 0 417.20001 407.24999"
version="1.1"
id="svg8"
inkscape:version="0.91 r13725"
sodipodi:docname="logitech-g303.svg"
inkscape:export-filename="/home/jimmac/logitech-g403.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2">
<inkscape:path-effect
effect="spiro"
id="path-effect4193"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect4185"
is_visible="true" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="21.208975"
inkscape:cy="195.5613"
inkscape:document-units="px"
inkscape:current-layer="Device"
inkscape:document-rotation="0"
showgrid="false"
units="px"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:pagecheckerboard="false"
showguides="true"
inkscape:window-width="1920"
inkscape:window-height="1101"
inkscape:window-x="1920"
inkscape:window-y="28"
inkscape:window-maximized="1"
fit-margin-top="20"
fit-margin-left="20"
fit-margin-bottom="20"
fit-margin-right="0"
inkscape:guide-bbox="true"
guidetolerance="10"
objecttolerance="10"
gridtolerance="10">
<inkscape:grid
type="xygrid"
id="grid956"
originx="-68.791593"
originy="1.2767161" />
</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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="Device"
inkscape:label="Device"
transform="translate(-68.791597,-394.02675)"
style="display:inline">
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:3;stroke-linecap:butt;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 183.34499,416.02062 c -24.661,4.08226 -49.92333,21.30746 -57.18502,32.68484 -8.18178,12.81894 -33.473053,116.65587 -34.496301,163.9888 15.773271,86.16472 55.738681,146.5572 61.556861,154.99421 4.70624,6.82458 9.70825,11.92177 42.30188,12.39158 32.59363,0.46981 37.71496,-5.68647 43.38814,-11.32031 4.79276,-4.75952 54.85052,-92.05678 61.98662,-153.22394 -1.55176,-67.0395 -18.56389,-150.61293 -30.38133,-163.10122 -11.79119,-12.46055 -51.83653,-36.40671 -54.76015,-36.45817 l 0,7.19061 -32.4107,0 z"
id="path870"
sodipodi:nodetypes="cscssscscccc" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path5040"
d="m 92.50792,592.62443 c 17.91963,82.02052 35.18878,118.28381 72.23883,184.44829"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#bfc2bb;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" />
<path
style="color:#000000;overflow:visible;opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;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 216.16981,422.85766 -2.60225,201.6236 c -0.0907,7.03035 -6.07063,10.44123 -13.61129,10.44123 -7.54065,0 -13.50652,-3.41107 -13.61128,-10.44123 l -3,-201.31424 z"
id="rect913"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssscc" />
<rect
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"
id="button2"
width="23.15593"
height="57.717773"
x="188.55382"
y="469.01868"
rx="11.577965"
ry="11.577967"
inkscape:label="#rect932" />
<path
sodipodi:nodetypes="cssscc"
inkscape:connector-curvature="0"
id="button5"
d="m 209.20722,547.47605 -0.65676,37.92859 c -0.0779,4.49803 -3.83301,8.12045 -8.59419,8.12045 -4.76117,0 -8.51628,-3.62242 -8.59417,-8.12045 l -0.65677,-37.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" />
<path
style="color:#000000;display:inline;overflow:visible;fill:none;fill-opacity:1;stroke:#babdb6;stroke-width:10;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="led0"
sodipodi:type="arc"
sodipodi:cx="-729.17651"
sodipodi:cy="199.82953"
sodipodi:rx="13.376111"
sodipodi:ry="13.525835"
sodipodi:start="0.78539816"
sodipodi:end="5.4977871"
sodipodi:arc-type="arc"
d="m -719.71817,209.39374 a 13.376111,13.525835 0 0 1 -18.91668,0 13.376111,13.525835 0 0 1 0,-19.12842 13.376111,13.525835 0 0 1 18.91667,0"
sodipodi:open="true"
transform="matrix(0,-1,1,0,0,0)"
inkscape:label="#path883" />
<path
id="button1"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#bfc2bb;stroke-width:3;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 298.92308,580.98137 C 293.8094,522.57046 280.3602,462.83778 270.51584,452.43458 258.72465,439.97403 218.67931,416.02787 215.75569,415.97641 l -0.003,7.2085 -0.50645,110.06055 83.95322,47.95787"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cscccc"
inkscape:label="#path4278" />
<path
id="button0"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:3;stroke-linecap:butt;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 115.69423,138.97706 -1.12358,-110.662125 -0.0378,-6.147082 C 89.8643,26.095287 64.626024,43.307658 57.368373,54.67871 52.0464,63.017 39.485291,109.86559 31.072821,154.624 c -1.978688,10.52757 -3.72786,20.93951 -5.142655,31.38156 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccsccc"
transform="translate(68.791597,394.02675)"
inkscape:label="#path4256" />
<path
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#bfc2bb;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 299.98341,585.82953 C 288.7813,671.38558 244.14684,741.20584 228.31,776.96473"
id="path5042"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
sodipodi:nodetypes="cscssscscccc"
id="path5016"
d="m 183.34499,416.02062 c -24.661,4.08226 -49.92333,21.30746 -57.18502,32.68484 -8.18178,12.81894 -33.47305,116.65587 -34.4963,163.9888 15.77327,86.16472 55.73868,146.5572 61.55686,154.99421 4.70624,6.82458 9.70825,11.92177 42.30188,12.39158 32.59363,0.46981 37.71496,-5.68647 43.38814,-11.32031 4.79276,-4.75952 54.85052,-92.05678 61.98662,-153.22394 -1.55176,-67.0395 -18.56389,-150.61293 -30.38133,-163.10122 -11.79119,-12.46055 -51.83653,-36.40671 -54.76015,-36.45817 l 0,7.19061 -32.4107,0 z"
style="color:#000000;overflow:visible;opacity:1;fill:none;fill-opacity:1;stroke:#babdb6;stroke-width:3;stroke-linecap:butt;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:connector-curvature="0" />
<path
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 102.08895,531.68418 c 0.53723,0.0143 -0.15214,3.8319 -0.54934,6.21419 l -7.140506,42.82642 -5.16583,0.42115 8.76037,-44.50491 c 0.466456,-2.3697 1.681005,-5.02125 4.095306,-4.95685 z"
id="button4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssccss"
inkscape:label="#rect921" />
<path
inkscape:label="#path924"
style="color:#000000;display:inline;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 92.206722,591.3329 c 2.40195,-0.25239 2.836696,1.11858 3.023443,3.52652 l 3.57032,46.03607 c 0.186747,2.40794 -0.771034,3.65494 -3.172974,3.90732 -2.40195,0.25238 -6.087456,-7.10638 -6.124623,-9.52127 l -0.504216,-32.76168 c -0.03717,-2.41488 0.8061,-10.93458 3.20805,-11.18696 z"
id="button3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssss" />
<path
id="led1"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#babdb6;fill-opacity:1;stroke:none;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 29.707031,242.95703 c 17.947503,72.58734 50.782189,122.34942 55.957031,129.85352 2.115971,3.06841 4.009169,5.50137 8.642579,7.49023 C 59.190137,317.56953 43.428144,284.03672 27.683594,216.87891 l 2.023437,26.07812 z M 229.75195,201.85742 c -14.69885,84.15847 -52.92757,142.415 -68.74414,178.1543 3.74134,-1.87015 5.74151,-4.04661 8.05469,-6.34375 0.24888,-0.24715 1.70524,-2.23579 3.5293,-5.16992 1.82406,-2.93413 4.19179,-6.95551 6.92187,-11.84375 5.46015,-9.77649 12.37582,-23.01815 19.36133,-38.01368 13.95635,-29.95956 28.17703,-66.96234 31.72656,-97.20898 -0.14948,-6.39067 -0.43956,-12.93267 -0.84961,-19.57422 z"
inkscape:label="#path5630"
inkscape:connector-curvature="0"
transform="translate(68.791597,394.02675)" />
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
style="display:inline"
transform="translate(-68.791597,5.973336)">
<g
id="button1-path"
inkscape:label="#g131"
transform="translate(8,-8)">
<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 242.94764,50.505764 234.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="237"
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="477"
y="50.005764" />
</g>
<g
id="button0-path"
inkscape:label="#g141"
transform="translate(8,-8)">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path960"
d="m 477,130.50576 -327.65602,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" />
<rect
y="127.00576"
x="143"
height="6.999999"
width="6.999999"
id="rect970"
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
inkscape:label="#rect871"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button0-leader"
width="1"
height="1"
x="477"
y="130.00577" />
</g>
<g
id="button4-path"
inkscape:label="#g151"
transform="translate(8,-8)">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path964"
d="M 89.31232,181.51438 117.74787,210.44326 477,210.50576"
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" />
<rect
inkscape:label="#rect875"
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="477"
y="210.00577" />
<rect
y="177"
x="85"
height="6.999999"
width="6.999999"
id="rect974"
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" />
</g>
<g
id="button2-path"
inkscape:label="#g136"
transform="translate(8,-8)">
<rect
inkscape:label="#rect869"
y="90.00576"
x="477"
height="1"
width="1"
id="button2-leader"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
<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"
d="m 195.97852,108.49147 17.9857,-17.98571 263.03578,0"
id="path908"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<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="rect910"
width="6.999999"
height="6.999999"
x="194"
y="104" />
</g>
<g
id="button3-path"
inkscape:label="#g156"
transform="translate(8,-8)">
<path
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 88.786361,231.47803 18.999029,19.02773 369.21461,0"
id="path966"
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="rect976"
width="6.999999"
height="6.999999"
x="82.883881"
y="225.00577" />
<rect
y="250.00575"
x="477"
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="#rect875" />
</g>
<g
id="button5-path"
inkscape:label="#g146"
transform="translate(8,-8)">
<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 476.55132,170.50615 -283.20734,0"
id="path51"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<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="rect53"
width="6.999999"
height="6.999999"
x="189"
y="167" />
<rect
y="170"
x="477"
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="#rect871" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
style="display:inline"
transform="translate(-68.791597,5.973336)">
<g
id="led0-path"
inkscape:label="#g161"
transform="translate(8,56)">
<path
inkscape:connector-curvature="0"
id="path877"
d="m 477,290.50577 -268.35562,0 -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="cc" />
<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="477"
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
id="led1-path"
inkscape:label="#g126"
transform="translate(8,274)">
<path
sodipodi:nodetypes="ccc"
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"
d="m 476.99745,10.505764 -192.31824,0 -23.37131,23.307989"
id="path4525"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;text-align:start;display:inline;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="led1-leader"
width="1"
height="1"
x="477"
y="10.005764"
inkscape:label="#rect881" />
<rect
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"
id="rect4544"
width="6.999999"
height="6.999999"
x="257.69327"
y="30.345425" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,380 @@
<?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="409.2084"
height="411.271"
viewBox="0 0 409.2084 411.27099"
version="1.1"
id="svg8"
inkscape:version="0.92.1 r"
sodipodi:docname="logitech-g403.svg"
inkscape:export-filename="/home/jimmac/logitech-g403.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4142135"
inkscape:cx="223.25242"
inkscape:cy="156.77583"
inkscape:document-units="px"
inkscape:current-layer="LEDs"
inkscape:document-rotation="0"
showgrid="false"
units="px"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:pagecheckerboard="false"
showguides="true"
inkscape:window-width="1366"
inkscape:window-height="704"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
fit-margin-top="20"
fit-margin-left="20"
fit-margin-bottom="20"
fit-margin-right="0">
<inkscape:grid
type="xygrid"
id="grid956"
originx="-68.791593"
originy="1.2767161" />
</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"
transform="translate(-68.791597,-390.00576)">
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:3;stroke-linecap:butt;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 175.34499,424.02062 c -24.661,4.08226 -60.18056,22.01925 -69.18502,28.18484 -7.932853,5.43184 -10.801193,13.70196 -10.925233,20.94538 -0.72438,42.30084 10.273313,88.0482 7.561093,130.50252 -1.21026,18.94414 -7.437413,51.01218 -9.969593,64.24176 -1.65073,6.85296 -2.50133,13.87513 -2.53464,20.92508 2.7e-4,50.2338 40.610433,90.95628 90.705683,90.95655 45.46938,-0.0495 82.39643,-34.07041 89.82748,-79.05254 10.66083,-64.53278 10.42629,-136.39723 11.98541,-204.91567 0.28464,-12.50912 -1.94334,-23.29335 -8.32377,-31.80289 -13.26086,-21.57098 -66.73071,-40.02924 -66.73071,-40.02924 v 7.19061 h -32.4107 z"
id="path870"
sodipodi:nodetypes="cssscccssccccc" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;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.56756,431.16702 -3,141.31424 c -0.14923,7.02936 -6.07063,12.69123 -13.61129,12.69123 -7.54065,0 -13.46205,-5.66187 -13.61128,-12.69123 l -3,-141.31424 z"
id="rect913"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssscc" />
<path
sodipodi:nodetypes="cscsc"
inkscape:connector-curvature="0"
id="path950"
d="m 95.373251,486.43435 c 38.955159,101.12102 48.667459,140.67087 51.422819,177.84397 2.03646,27.47435 -36.96151,79.62933 -36.96151,79.62933 0,0 -24.033873,-37.15986 -19.463976,-61.45137 10.258966,-54.53208 19.996276,-75.99616 5.002667,-196.02193 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#bfc2bb;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" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 104.9358,539.80779 c 2.40195,-0.25238 2.7176,0.9048 2.96999,3.30675 l 5.69164,54.1678 c 0.25238,2.40194 -1.47813,4.53882 -3.88008,4.7912 -2.40195,0.25239 -5.44876,-8.75375 -5.55682,-11.1665 l -2.02953,-45.3135 c -0.10806,-2.41275 0.40286,-5.53337 2.8048,-5.78575 z"
id="button4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssss"
inkscape:label="#rect921" />
<path
sodipodi:nodetypes="sssssss"
inkscape:connector-curvature="0"
id="button3"
d="m 109.77779,602.64876 c 2.40195,-0.25239 4.53882,1.47813 4.79121,3.88007 l 5.69164,54.1678 c 0.25238,2.40195 -1.47814,4.53883 -3.88008,4.79121 -2.40195,0.25238 -7.77772,-8.87659 -7.89239,-11.28904 l -1.91843,-40.36308 c -0.11466,-2.41244 0.8061,-10.93458 3.20805,-11.18696 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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="#path924" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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"
id="button2"
width="23.155933"
height="69.467796"
x="180.55382"
y="454.99139"
rx="11.577967"
ry="11.577967"
inkscape:label="#rect932" />
<path
sodipodi:nodetypes="cssscc"
inkscape:connector-curvature="0"
id="button5"
d="m 201.20722,551.97605 -0.65676,17.92859 c -0.16469,4.49569 -3.83301,8.12045 -8.59419,8.12045 -4.76117,0 -8.49995,-3.62273 -8.59417,-8.12045 l -0.65677,-17.92859 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#bfc2bb;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 281.45725,491.14612 c -30.15796,102.59778 -42.11413,137.63368 -50.74442,173.1322 -6.50823,26.76995 22.13723,79.62933 22.13723,79.62933 0,0 13.58979,-21.97214 16.0745,-32.60727 16.80373,-71.92371 12.53269,-220.15426 12.53269,-220.15426 z"
id="path946"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cscsc" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:#bfc2bb;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"
d="m 207.75569,423.97641 -1.77476,128.93066 47.96839,25.54626 27.50793,-87.30721 c 4.86457,-36.68335 -44.6715,-55.30429 -73.70156,-67.16971 z"
id="button1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#path944" />
<path
sodipodi:nodetypes="ccccsc"
inkscape:connector-curvature="0"
id="button0"
d="m 174.78525,423.6101 3.03798,129.76808 -49.23161,25.07515 -33.218369,-92.01898 c -1.039426,-19.85252 -0.81982,-28.05969 15.888469,-37.87541 17.53477,-10.30125 42.4825,-20.7564 63.52353,-24.94884 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:#bfc2bb;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"
inkscape:label="#path948" />
<path
style="color:#000000;display:inline;overflow:visible;vector-effect:none;fill:none;fill-opacity:1;stroke:#babdb6;stroke-width:10;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="led0"
sodipodi:type="arc"
sodipodi:cx="-656.78052"
sodipodi:cy="189.57951"
sodipodi:rx="19.936752"
sodipodi:ry="19.936752"
sodipodi:start="0.78539816"
sodipodi:end="5.4977871"
sodipodi:arc-type="arc"
d="m -642.6831,203.67693 a 19.936752,19.936752 0 0 1 -28.19483,0 19.936752,19.936752 0 0 1 0,-28.19483 19.936752,19.936752 0 0 1 28.19482,0"
sodipodi:open="true"
transform="rotate(-90)"
inkscape:label="#path883" />
<path
style="fill:#babdb6;fill-opacity:1;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 192.13178,454.61765 c 0,70.18035 0,70.18035 0,70.18035"
id="led1"
inkscape:connector-curvature="0"
inkscape:label="#path42" />
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
style="display:inline"
transform="translate(-68.791597,9.994236)">
<g
id="button1-path"
inkscape:label="#g131">
<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 242.94764,50.505764 H 477"
id="path958"
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="rect968"
width="6.999999"
height="6.999999"
x="237"
y="47.005764" />
<rect
inkscape:label="#rect867"
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="477"
y="50.005764" />
</g>
<g
id="button0-path"
inkscape:label="#g141">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path960"
d="M 477,130.50576 H 161.34398"
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:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button0-leader"
width="1"
height="1"
x="477"
y="130.00577" />
</g>
<g
id="button4-path"
inkscape:label="#g151">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path964"
d="M 109.31232,181.51438 137.74787,210.44326 477,210.50576"
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="button4-leader"
width="1"
height="1"
x="477"
y="210.00577" />
<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
id="button2-path"
inkscape:label="#g136">
<rect
inkscape:label="#rect869"
y="90.00576"
x="477"
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" />
<path
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 195.97852,108.49147 17.9857,-17.98571 H 477"
id="path908"
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="rect910"
width="6.999999"
height="6.999999"
x="194"
y="104" />
</g>
<g
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 H 477"
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="477"
height="1"
width="1"
id="button3-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect875" />
</g>
<g
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 476.55132,170.50615 H 193.34398"
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="170"
x="477"
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>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
style="display:inline"
transform="translate(-68.791597,9.994236)">
<g
id="led0-path"
inkscape:label="#g161">
<path
inkscape:connector-curvature="0"
id="path877"
d="M 477,290.50577 H 208.64438 l -18.97696,-18.97696"
style="color:#000000;display:inline;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"
sodipodi:nodetypes="cc" />
<rect
y="271"
x="186"
height="6.999999"
width="6.999999"
id="rect879"
style="color:#000000;display:inline;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="#rect881"
y="290.00577"
x="477"
height="1"
width="1"
id="led0-leader"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
</g>
<g
id="led1-path"
inkscape:label="#g126">
<path
sodipodi:nodetypes="ccc"
style="color:#000000;display:inline;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 476.99745,10.505764 H 247.7728 l -51.03686,51.503873"
id="path4525"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="led1-leader"
width="1"
height="1"
x="477"
y="10.005764"
inkscape:label="#rect881" />
<rect
style="color:#000000;display:inline;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="rect4544"
width="6.999999"
height="6.999999"
x="190.67345"
y="59.51358" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,484 @@
<?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="461.86667"
height="437.33334"
id="svg2"
version="1.1"
inkscape:version="0.92+devel unknown"
sodipodi:docname="logitech-g500.svg"
viewBox="0 0 433 410">
<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="4.0000004"
inkscape:cx="184.69456"
inkscape:cy="292.7786"
inkscape:document-units="px"
inkscape:current-layer="Device"
showgrid="false"
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">
<sodipodi:guide
orientation="-0.98480775,-0.17364818"
position="118.1781,220.98096"
id="guide4656"
inkscape:locked="false" />
<sodipodi:guide
orientation="-0.98480775,-0.17364818"
position="124.47548,146.18232"
id="guide4658"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-115.05694,410.68446"
id="guide5340"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-338.2349,374.49999"
id="guide5342"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-411.98489,294.5"
id="guide5344"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-447.39711,334.5"
id="guide5346"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-458.85989,254.5"
id="guide5348"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-443.23489,214.5"
id="guide5350"
inkscape:locked="false" />
<inkscape:grid
type="xygrid"
id="grid5352"
empspacing="4"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
originx="0"
originy="0"
spacingx="1"
spacingy="1" />
<sodipodi:guide
orientation="0,1"
position="-305.47013,174.5"
id="guide5354"
inkscape:locked="false" />
</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:label="Device"
id="Device"
transform="translate(-39.484902,-592.98552)"
style="display:inline"
inkscape:groupmode="layer">
<path
style="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 256.36569,614.54742 c 62.08413,0.89801 96.39946,43.63605 97.13045,100.21395 0.55781,43.17356 -0.71866,53.46001 -1.54175,67.83715 -2.26656,39.59053 13.49465,51.18855 14.64665,88.65081 1.15816,37.66265 -45.5866,110.51959 -94.81782,110.23536 -65.62864,-0.3789 -122.38269,-97.81594 -122.56938,-158.02971 -0.0782,-25.23502 4.46146,-43.80053 13.87577,-79.40029 -9.23186,-76.55096 33.02958,-130.37871 93.27608,-129.50727 z"
id="path3154"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssscs" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 256.8125,620.48588 c 65.06567,0.698 86.46352,49.92709 87.43313,85.02498 1.40678,50.92328 -31.00828,141.5762 -14.64667,177.30163 8.51955,18.60234 18.50105,43.3618 18.50105,43.3618 0,0 -32.07332,55.55238 -76.12407,55.3104 -47.75882,-0.26237 -83.25468,-52.61233 -83.25468,-52.61233 0,0 18.17404,-39.75596 19.0792,-60.70654 2.21242,-51.20828 -34.1177,-72.31129 -37.00208,-127.19463 -2.92063,-55.57291 17.50781,-121.18975 83.17598,-120.48531 v 47.86067 h -5.96621 c -1.88766,0 -4.0051,2.17484 -4.0051,4.0625 v 39.96094 c 0,1.86907 2.13603,3.98438 4.0051,3.98438 h 5.96621 v 28.98005 h -4.03271 c -0.93786,0 -1.9335,1.10612 -1.9335,2.04398 v 16.95951 c 0,0.93786 0.99564,2.04398 1.9335,2.04398 h 4.03271 v 46.07243 h 2.83814 v -46.07243 h 3.98434 c 0.95705,0 2.01636,-1.08693 2.01636,-2.04398 V 747.3784 c 0,-0.95705 -1.05931,-2.04398 -2.01636,-2.04398 h -3.98434 v -28.98005 h 6.0007 c 1.88748,0 4.0051,-2.0969 4.0051,-3.98438 v -39.96094 c 0,-1.9059 -2.0992,-4.0625 -4.0051,-4.0625 h -6.0007 z"
id="path3156"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscscssccssssccssssccccssssccsssscc" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2.13333344;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 228.78906,209.52734 v -24.57031 h -4.30273 c -1.00039,0 -2.0625,-1.18126 -2.0625,-2.18164 v -18.08984 c 0,-1.00039 1.06211,-2.17969 2.0625,-2.17969 h 4.30273 v -30.91211 h -6.36523 c -1.99368,0 -4.27149,-2.25633 -4.27149,-4.25 v -42.625 c 0,-2.013504 2.25798,-4.333984 4.27149,-4.333984 h 6.36523 V 29.333984 C 191.43799,28.933309 167.81063,48.64998 154.36133,76.128906 l 6.78125,13.011719 c 0.88314,1.694805 1.15123,3.956361 0.41211,5.71875 -4.4888,10.703155 -7.116,17.605295 -7.34571,28.384765 -0.037,1.73622 -0.81139,4.42706 -1.86718,4.68164 l -8.53907,1.96094 c -2.34577,34.22551 0.78777,54.03726 19.46485,92.625 l 8.17383,0.56055 c 0.9494,0.0651 1.95013,0.3512 2.86523,0.80273"
transform="matrix(0.93749999,0,0,0.93749999,39.484902,592.98552)"
id="button0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccssssccssssccccccccccc" />
<path
sodipodi:nodetypes="ccssssccssssccsc"
inkscape:connector-curvature="0"
id="button1"
d="m 256.8125,789.41811 v -23.03622 h 3.98434 c 0.95705,0 2.01636,-1.08693 2.01636,-2.04398 V 747.3784 c 0,-0.95705 -1.05931,-2.04398 -2.01636,-2.04398 h -3.98434 v -28.98005 h 6.0007 c 1.88748,0 4.0051,-2.0969 4.0051,-3.98438 v -39.96094 c 0,-1.9059 -2.0992,-4.0625 -4.0051,-4.0625 h -6.0007 v -47.86067 c 65.06567,0.698 86.46353,49.92709 87.43313,85.02498 0.70339,25.46164 -7.04868,60.85569 -12.9314,94.35001"
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path5337" />
<path
style="fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:2.13333344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 154.36133,76.128906 c -11.77275,24.053464 -15.7471,54.053924 -14.29297,81.722654 2.85731,54.3682 36.47683,77.66427 39.36719,124.46094 l 10.40625,-11.33594 c 1.8935,-2.06288 1.8635,-5.57497 1.24804,-8.30664 -3.12898,-13.88786 -7.08345,-21.76245 -13.9707,-36.16406 -0.95424,-1.99536 -3.47112,-3.28232 -5.67773,-3.43359 l -8.17383,-0.56055 C 144.5905,183.92398 141.45767,164.11281 143.80344,129.8873 l 8.53836,-1.96152 c 1.05579,-0.25458 1.83017,-2.94542 1.86718,-4.68164 0.22971,-10.77947 2.85691,-17.68161 7.34571,-28.384765 0.73912,-1.762389 0.47103,-4.023945 -0.41211,-5.71875 z"
transform="matrix(0.93749999,0,0,0.93749999,39.484902,592.98552)"
id="path4640"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cscccccccccccc" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:2;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="button2"
width="16"
height="40"
x="247.44788"
y="672.31464"
rx="4"
ry="4"
inkscape:label="#rect4642" />
<rect
style="fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:2;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4644"
width="8"
height="15"
x="251.34375"
y="748.26843"
rx="2"
ry="2" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:bevel;stroke-opacity:1"
d="m 184.45535,669.33767 c -2.23196,5.12412 -4.80484,11.95291 -6.46149,18.61989 l 7.35715,1.95705 c 1.0372,-3.57697 2.03018,-5.75654 2.91825,-7.91351 0.52031,-1.26376 0.20456,-3.91971 -0.38116,-5.19416 z"
id="button8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssc"
inkscape:label="#path4646" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:bevel;stroke-opacity:1"
d="m 174.03907,712.2528 c 0.52083,-6.80199 1.39322,-13.37092 2.65623,-18.35938 l 6.99498,1.53292 c -1.25795,4.82258 -1.66687,8.88981 -1.774,12.75056 -0.0294,1.05941 -0.59464,2.39181 -1.62721,2.6306 z"
id="button9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssc"
inkscape:label="#path4648" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 159.55083,740.00544 c -1.24751,0.22588 -2.18342,2.00899 -1.96327,3.25754 l 2.97337,16.86281 4.53764,-0.94696 3.46041,18.3604 1.8943,-0.39559 -6.68317,-37.90213 z"
id="button4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sscccccs"
inkscape:label="#path4650" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#bdbab6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 169.00092,779.7656 1.83239,-0.46666 6.87338,38.98084 -4.17452,1.07194 c -1.15216,0.29585 -2.68927,-0.91399 -2.89583,-2.08545 l -3.45729,-19.60726 4.8521,-1.10345 z"
id="button3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssccc"
inkscape:label="#path4652" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 162.19674,761.50057 6.01776,34.31296 2.99355,-0.71424 -3.77818,-20.44043 -2.54859,-13.7678 z"
id="button5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc"
inkscape:label="#path4654" />
<path
inkscape:label="#path1148"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.9375;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 238.49468,688.57611 v 7.47706 l -3.59361,-3.59364 z"
id="button6"
inkscape:connector-curvature="0" />
<path
inkscape:label="#path1146"
inkscape:connector-curvature="0"
id="button7"
d="m 272.40108,688.57611 v 7.47706 l 3.5936,-3.59364 z"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.9375;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" />
</g>
<g
id="Buttons"
inkscape:label="Buttons"
style="display:inline"
transform="translate(-39.484902,-52.623025)"
inkscape:groupmode="layer">
<g
id="button8-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path5328"
d="M 183.46993,142.07026 170.4849,128.12301 H 40.484902"
style="fill:none;stroke:#888a85;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="color:#000000;display:inline;overflow:visible;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;marker:none"
id="rect968-0"
width="6.999999"
height="6.999999"
x="180.25464"
y="138.46088" />
<rect
inkscape:label="#rect5374"
y="127.62303"
x="39.484901"
height="1"
width="1"
id="button8-leader"
style="text-align:end;fill:#888a85;fill-opacity:1;stroke:none" />
</g>
<g
id="button3-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path5338"
d="m 171.875,267.3125 -20.3901,20.81051 H 40.484902"
style="fill:none;stroke:#888a85;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="color:#000000;display:inline;overflow:visible;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;marker:none"
id="rect968-4"
width="6.999999"
height="6.999999"
x="168.53668"
y="263.65982" />
<rect
inkscape:label="#rect5376"
transform="translate(39.484902,52.674496)"
y="234.94852"
x="0"
height="1"
width="1"
id="button3-leader"
style="text-align:end;fill:#888a85;fill-opacity:1;stroke:none" />
</g>
<g
id="button5-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path5334"
d="m 166.54255,238.69695 -9.05765,9.42606 H 40.484902"
style="fill:none;stroke:#888a85;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="color:#000000;display:inline;overflow:visible;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;marker:none"
id="rect968-3"
width="6.999999"
height="6.999999"
x="163.02351"
y="235.40237" />
<rect
inkscape:label="#rect5378"
transform="translate(39.484902,52.674496)"
y="194.94852"
x="0"
height="1"
width="1"
id="button5-leader"
style="text-align:end;fill:#888a85;fill-opacity:1;stroke:none" />
</g>
<g
id="button4-path">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path5332"
d="M 161.4849,208.12301 H 40.484902"
style="fill:none;stroke:#888a85;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="color:#000000;display:inline;overflow:visible;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;marker:none"
id="rect968-68"
width="6.999999"
height="6.999999"
x="158.37202"
y="204.55347" />
<rect
inkscape:label="#rect5380"
transform="translate(39.484902,52.674496)"
y="154.94852"
x="0"
height="1"
width="1"
id="button4-leader"
style="text-align:end;fill:#888a85;fill-opacity:1;stroke:none" />
</g>
<g
id="button9-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path5330"
d="m 178.4278,162.96866 -4.9429,5.15435 H 40.484902"
style="fill:none;stroke:#888a85;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="color:#000000;display:inline;overflow:visible;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;marker:none"
id="rect968-99"
width="6.999999"
height="6.999999"
x="175.10759"
y="159.39299" />
<rect
inkscape:label="#rect5382"
transform="translate(39.484902,52.674496)"
y="114.94852"
x="0"
height="1"
width="1"
id="button9-leader"
style="text-align:end;fill:#888a85;fill-opacity:1;stroke:none" />
</g>
<g
id="button6-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path5324"
d="m 244.4849,152.62303 95,95.5 132,-2e-5"
style="fill:none;stroke:#888a85;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="color:#000000;display:inline;overflow:visible;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;marker:none"
id="rect968-5"
width="6.999999"
height="6.999999"
x="240.37251"
y="148.45215" />
<rect
inkscape:label="#rect5384"
transform="translate(39.484902,52.623025)"
y="195"
x="432"
height="1"
width="1"
id="button6-leader"
style="text-align:start;fill:#888a85;fill-opacity:1;stroke:none" />
</g>
<g
id="button7-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path5322"
d="m 267.4849,152.62303 55,55.5 149,-1e-5"
style="fill:none;stroke:#888a85;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="color:#000000;display:inline;overflow:visible;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;marker:none"
id="rect968-1"
width="6.999999"
height="6.999999"
x="263.49133"
y="148.45215" />
<rect
inkscape:label="#rect5386"
transform="translate(39.484902,52.623025)"
y="155"
x="432"
height="1"
width="1"
id="button7-leader"
style="text-align:start;fill:#888a85;fill-opacity:1;stroke:none" />
</g>
<g
id="button2-path">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path5326"
d="m 255.4849,141.62303 h 39 l 27,26.5 150,-1e-5"
style="fill:none;stroke:#888a85;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="color:#000000;display:inline;overflow:visible;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;marker:none"
id="rect968-6"
width="6.999999"
height="6.999999"
x="251.93195"
y="138.13458" />
<rect
inkscape:label="#rect5388"
transform="translate(39.484902,52.623025)"
y="115"
x="432"
height="1"
width="1"
id="button2-leader"
style="text-align:start;fill:#888a85;fill-opacity:1;stroke:none" />
</g>
<g
id="button1-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path5320"
d="m 290.625,110.67188 16.8599,17.45114 h 164"
style="fill:none;stroke:#888a85;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="color:#000000;display:inline;overflow:visible;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;marker:none"
id="rect968-9"
width="6.999999"
height="6.999999"
x="287.23676"
y="107.42233" />
<rect
inkscape:label="#rect5390"
transform="translate(39.484902,52.623025)"
y="75"
x="432"
height="1"
width="1"
id="button1-leader"
style="text-align:start;fill:#888a85;fill-opacity:1;stroke:none" />
</g>
<g
id="button0-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path5318"
d="M 226.27417,106.9604 246.4849,88.123025 h 225"
style="fill:none;stroke:#888a85;stroke-width:1px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="color:#000000;display:inline;overflow:visible;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;marker:none"
id="rect968"
width="6.999999"
height="6.999999"
x="222.83652"
y="103.6103" />
<rect
inkscape:label="#rect5392"
transform="translate(39.484902,52.623025)"
y="35"
x="432"
height="1"
width="1"
id="button0-leader"
style="text-align:start;fill:#888a85;fill-opacity:1;stroke:none" />
</g>
</g>
<g
id="LEDs"
inkscape:label="LEDs"
inkscape:groupmode="layer" />
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

View File

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

View File

@@ -0,0 +1,570 @@
<?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="467.83105"
height="398.87393"
viewBox="0 0 467.83105 398.87393"
version="1.1"
id="svg8"
inkscape:version="0.92.1 r"
sodipodi:docname="logitech-g502.svg"
inkscape:export-filename="/home/jimmac/logitech-g403.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:cx="345.48859"
inkscape:cy="341.91565"
inkscape:document-units="px"
inkscape:current-layer="Buttons"
inkscape:document-rotation="0"
showgrid="false"
units="px"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:pagecheckerboard="false"
showguides="true"
inkscape:window-width="1366"
inkscape:window-height="704"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-global="false"
fit-margin-top="20"
fit-margin-bottom="20"
fit-margin-left="0"
fit-margin-right="0">
<inkscape:grid
type="xygrid"
id="grid956"
originx="-16.168953"
originy="-1.1260801" />
</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"
transform="translate(-16.168953,-400)"
style="display:inline">
<g
transform="translate(-374,-4.864278e-6)"
id="g1170">
<path
inkscape:connector-curvature="0"
id="path981"
d="m 617.52893,453.41998 -49.99065,26.66899 c -2.22449,1.86937 -2.81832,3.35837 -3.6652,5.83317 l -15.9563,53.34684 c -0.49748,2.25523 -0.34218,3.74811 0.48513,6.46679 l 3.99946,10.9325 -3.53186,0.74244 -1.35901,12.37091 -1.50378,2.8507 2.09912,18.55549 c 0,0 -13.03638,4.81768 -18.22998,15.00854 -5.19361,10.19086 -9.29541,53.53534 -6.03613,59.82001 3.25927,6.28467 25.6018,33.9057 28.70703,40.07849 3.10522,6.17279 24.79553,45.22613 30.93054,51.77106 6.13501,6.54492 35.79895,19.27618 35.79895,19.27618 6.39358,-5.0822 15.98738,-6.80155 23.61682,-0.45557 0,0 31.72192,-10.76812 43.56091,-26.60644 11.83899,-15.83832 24.57691,-52.0235 24.57691,-52.0235 0,0 -2.04663,-1.98694 -2.04663,-9.06775 0,-7.08081 3.24377,-9.90655 3.24377,-9.90655 l -7.41565,-77.33204 c 0,0 0.90478,-68.51501 -1.01514,-76.49939 -1.57946,-6.56849 -5.90589,-29.81348 -7.88675,-38.02183 -0.42699,-1.76936 -0.95122,-1.65743 -2.07882,-3.29921 l -52.65222,-36.341 4.00378,32.1344 h -28.47082 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:3;stroke-linecap:butt;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="ccccccccccsccsccscsccssccccc" />
<path
sodipodi:nodetypes="cccccscssccc"
inkscape:connector-curvature="0"
id="path1014"
d="m 648.06347,618.15601 34.51828,10.05586 c 2.62858,0.8078 3.9256,5.47034 3.9256,5.47034 l -0.44831,35.46611 23.5331,26.29168 c 0,0 -9.4414,38.41029 -27.23181,57.70209 -11.60967,12.58946 -39.46726,23.54443 -39.46726,23.54443 0,0 -4.87562,-48.51623 -9.30469,-56.255 -4.42908,-7.73877 -35.48734,-18.11903 -50.81543,-30.61249 -5.82123,-4.7447 -15.27906,-16.55719 -15.27906,-16.55719 l -4.00329,-23.86047 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:#bfc2bb;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" />
<path
sodipodi:nodetypes="cccsc"
inkscape:connector-curvature="0"
id="path1144"
d="m 620.3461,775.42623 3.91771,-44.34657 c 1.3829,-6.28113 -0.99643,-10.76025 -6.48071,-12.64329 0,0 -33.9759,-17.94313 -41.16454,-25.32697 -7.18863,-7.38383 -16.89702,-24.84664 -16.89702,-24.84664"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;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" />
<path
style="color:#000000;display:inline;overflow:visible;vector-effect:none;fill:none;fill-opacity:1;stroke:#babdb6;stroke-width:8;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="led1"
sodipodi:type="arc"
sodipodi:cx="-664.05457"
sodipodi:cy="598.10364"
sodipodi:rx="12.162673"
sodipodi:ry="12.162673"
sodipodi:start="0.78539816"
sodipodi:end="5.4977871"
sodipodi:arc-type="arc"
d="m -655.45426,606.70395 a 12.162673,12.162673 0 0 1 -17.20061,0 12.162673,12.162673 0 0 1 0,-17.20062 12.162673,12.162673 0 0 1 17.20061,0"
sodipodi:open="true"
transform="rotate(-90)"
inkscape:label="#path1012" />
<path
sodipodi:nodetypes="ccsccsc"
inkscape:connector-curvature="0"
id="path1030"
d="m 548.10584,591.18781 -1.39021,19.03606 c 0,0 -6.51942,1.07248 -9.54983,9.44719 -3.03041,8.37471 -8.01844,25.6823 -7.19278,55.16199 -3.80454,-3.92381 -8.20043,-7.54825 -7.93855,-14.11229 0.0565,-12.69733 0.50177,-31.56063 5.66468,-49.88965 3.26585,-11.59423 10.35698,-16.87164 20.40669,-19.6433 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:2;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" />
<path
inkscape:connector-curvature="0"
id="button0"
d="m 616.72979,453.87813 -43.21475,23.25557 -2.30905,111.85791 -3.27924,2.29913 8.95587,40.68384 43.49634,-20.81641 c 0,0 -6.29272,-57.3948 -6.04248,-82.96182 0.25025,-25.56702 2.39331,-74.31822 2.39331,-74.31822 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;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"
sodipodi:nodetypes="ccccccsc"
inkscape:label="#path1060" />
<path
inkscape:connector-curvature="0"
id="button1"
d="m 641.35924,447.84438 c 0,0 6.82136,40.56938 7.71833,77.06871 0.89697,36.49932 0.60175,77.20425 0.60175,77.20425 l 43.91543,12.95245 c 0,0 2.9472,-62.03698 1.5412,-81.15454 -1.4061,-19.11755 -6.51723,-50.97735 -8.90483,-54.50845 -2.3876,-3.5311 -44.87188,-31.56242 -44.87188,-31.56242 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;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"
sodipodi:nodetypes="cscccsc"
inkscape:label="#path1062" />
<path
inkscape:connector-curvature="0"
id="button8"
d="m 626.36054,592.91458 3.41284,20.82315 12.69293,-3.75027 1.10974,-17.00943 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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="#path1064" />
<path
inkscape:connector-curvature="0"
id="path1066"
d="m 624.73297,570.54019 2.51941,13.45651 14.86756,-0.0507 1.26654,-13.4599 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#eeeeec;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" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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"
id="button2"
width="17.152637"
height="51.954956"
x="623.8761"
y="503.84464"
rx="8.5763187"
ry="8.5763187"
inkscape:label="#rect1068" />
<path
inkscape:connector-curvature="0"
id="button7"
d="m 569.97595,534.59994 -16.73181,-6.63336 13.09595,-44.4487 3.95538,-3.11892 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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="#path1098" />
<path
inkscape:connector-curvature="0"
id="button6"
d="m 569.97595,534.59994 -15.81811,4.41446 13.12378,48.26233 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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="#path1100" />
<path
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;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 550.97571,556.55099 -1.73137,22.23376 c 0,0 -8.07466,-0.0385 -6.10341,-6.67814 0.79661,-2.68321 1.88213,-11.10134 3.54027,-13.80998 2.44498,-3.99399 4.29451,-1.74564 4.29451,-1.74564 z"
id="button5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccsc"
inkscape:label="#path1473" />
<path
inkscape:connector-curvature="0"
id="button4"
d="m 553.91685,576.77039 3.78851,43.20785 -3.52893,1.40018 -4.18952,-2.67337 -1.94029,-38.13895 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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"
sodipodi:nodetypes="cccccc"
inkscape:label="#path1118" />
<path
inkscape:connector-curvature="0"
id="button3"
d="m 557.29072,626.42287 -4.13718,-0.69442 -1.90659,6.11048 8.04212,38.1661 3.30713,3.0868 4.6499,-1.78876 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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="#path1120" />
<path
inkscape:connector-curvature="0"
id="led0"
d="m 554.07316,558.40614 18.36886,76.63506 -10.78213,5.31523 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;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="#path1122" />
<path
inkscape:connector-curvature="0"
id="path1138"
d="m 561.0357,599.31803 1.68542,4.21409 -3.19735,5.27031 -0.72068,-4.52978 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;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" />
<path
inkscape:connector-curvature="0"
id="path1140"
d="m 564.53845,610.12329 0.75,2.84987 -3.50226,8.03142 -0.75,-4.13156 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;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" />
<path
inkscape:connector-curvature="0"
id="path1142"
d="m 566.94537,619.35785 1.45849,3.46056 -4.29748,11.03294 -1.04318,-4.82295 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;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" />
<path
inkscape:connector-curvature="0"
id="button9"
d="m 654.80262,526.99557 v 7.97553 l 3.83318,-3.83321 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;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="#path1146" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;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 608.6358,526.99557 v 7.97553 l -3.83318,-3.83321 z"
id="path1148"
inkscape:connector-curvature="0" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
style="display:inline"
transform="translate(-16.168953)">
<g
id="button0-path"
inkscape:label="#g442">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path954"
d="m 234.68702,62.49186 41.9857,-41.98571 H 483"
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="62"
x="230"
height="6.999999"
width="6.999999"
id="rect952"
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="#rect865"
y="20"
x="483"
height="1"
width="1"
id="button0-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
</g>
<g
id="button1-path"
inkscape:label="#g437">
<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 275.03741,60.50615 H 483.05562"
id="path958"
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="rect968"
width="6.999999"
height="6.999999"
x="273"
y="57" />
<rect
inkscape:label="#rect867"
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="483"
y="60" />
</g>
<g
id="button2-path"
inkscape:label="#g432">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path960"
d="M 483.15129,100.50615 H 266.97374 l -7.07445,7.07445"
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="106.05299"
x="254.86905"
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="#rect869"
y="100"
x="483"
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" />
</g>
<g
id="button6-path"
inkscape:label="#g427">
<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 483,140.50615 H 189.32589"
id="path962"
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="rect972"
width="6.999999"
height="6.999999"
x="188"
y="137" />
<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="button6-leader"
width="1"
height="1"
x="483"
y="140" />
</g>
<g
id="button9-path"
inkscape:label="#g305">
<rect
inkscape:label="button7"
y="-106.99028"
x="-17.168953"
height="1"
width="1"
id="button9-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
transform="scale(-1)" />
<path
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 17.16895,106.48415 h 196.02823 l 12.07418,12.07418 h 32.523 l 6.63159,11.48624"
id="path902"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<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="rect904"
width="6.999999"
height="6.999999"
x="262.93826"
y="128.04514" />
</g>
<g
id="button10-path"
inkscape:label="#g310">
<rect
transform="scale(-1)"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button10-leader"
width="1"
height="1"
x="-17.168953"
y="-146.9903"
inkscape:label="#rect873" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path900"
d="m 17.16895,146.48415 h 136.64539 l 16.12282,-23.87718 h 74.32285 l 4.99222,8.64678"
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="128.03354"
x="247.00359"
height="6.999999"
width="6.999999"
id="rect906"
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
id="button4-path"
inkscape:label="#g402">
<path
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 17.16895,226.39549 151.60272,0.0884 8.22787,-9.92565"
id="path896"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
y="-219.98177"
x="-181.00188"
height="6.999999"
width="6.999999"
id="rect1297"
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"
transform="scale(-1)" />
<rect
inkscape:label="#rect873"
y="-226.98393"
x="-17.168953"
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"
transform="scale(-1)" />
</g>
<g
id="button8-path"
inkscape:label="#g397">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path1259"
d="m 17.16895,186.48415 h 90.82321 l 16.12282,16.12282 h 132.34309"
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="-207.00615"
x="-262"
height="6.999999"
width="6.999999"
id="rect1261"
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"
transform="scale(-1)" />
<rect
inkscape:label="#rect873"
y="-186.98393"
x="-17.168953"
height="1"
width="1"
id="button8-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
transform="scale(-1)" />
</g>
<g
id="button7-path"
inkscape:label="#g300">
<rect
inkscape:label="#rect873"
y="-62.912788"
x="-17.168953"
height="1"
width="1"
id="button7-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
transform="scale(-1)" />
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path914"
d="M 17.317611,62.412787 H 178.90504 l 15.75447,23.87718"
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
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="rect912"
width="6.999999"
height="6.999999"
x="191.28058"
y="85.438072" />
</g>
<g
id="button3-path"
inkscape:label="#g407">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path1295"
d="m 17.294405,266.46958 168.568675,0.0407"
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
transform="scale(-1)"
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="rect1301"
width="6.999999"
height="6.999999"
x="-191.68813"
y="-269.40173" />
<rect
transform="scale(-1)"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round"
id="button3-leader"
width="1.1351269"
height="1"
x="-17.294409"
y="-266.98993"
inkscape:label="#rect873" />
</g>
<g
id="button5-path"
inkscape:label="#g422">
<path
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 483.13134,180.50551 -295.64539,-0.25 -14.35505,-20.22553"
id="path890"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
y="-163.30751"
x="-175.13875"
height="6.999999"
width="6.999999"
id="rect894"
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"
transform="scale(-1)" />
<rect
transform="scale(-1)"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button5-leader"
width="1"
height="1"
x="-484.00677"
y="-181.00594"
inkscape:label="#rect873" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
style="display:inline"
transform="translate(-16.168953)">
<g
id="led0-path"
inkscape:label="#g417">
<path
sodipodi:nodetypes="cc"
style="color:#000000;display:inline;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 191.93858,220.50615 H 483.10224"
id="path966"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;display:inline;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="188"
y="217" />
<rect
inkscape:label="#rect875"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="led0-leader"
width="1"
height="1"
x="483"
y="220" />
</g>
<g
id="led1-path"
inkscape:label="#g412">
<path
inkscape:connector-curvature="0"
id="path877"
d="M 214.64438,260.50615 H 483"
style="color:#000000;display:inline;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"
sodipodi:nodetypes="cc" />
<rect
y="257"
x="210"
height="6.999999"
width="6.999999"
id="rect879"
style="color:#000000;display:inline;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="#rect881"
y="260"
x="483"
height="1"
width="1"
id="led1-leader"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,576 @@
<?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="467.83105"
height="401.98727"
viewBox="0 0 467.83105 401.98727"
version="1.1"
id="svg8"
inkscape:version="0.92+devel unknown"
sodipodi:docname="logitech-g700.svg"
inkscape:export-filename="/home/jimmac/logitech-g403.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="187.43137"
inkscape:cy="258.73512"
inkscape:document-units="px"
inkscape:current-layer="Device"
inkscape:document-rotation="0"
showgrid="false"
units="px"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:pagecheckerboard="false"
showguides="true"
inkscape:window-width="1853"
inkscape:window-height="1016"
inkscape:window-x="1507"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:guide-bbox="true"
fit-margin-top="20"
fit-margin-bottom="20"
fit-margin-left="0"
fit-margin-right="0">
<inkscape:grid
type="xygrid"
id="grid956"
originx="-16.168953"
originy="1.9872821" />
<sodipodi:guide
position="566.49402,250.15413"
orientation="0.70710678,0.70710678"
id="guide1516"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
position="345.16261,205.63403"
orientation="1,0"
id="guide1518"
inkscape:locked="false" />
<sodipodi:guide
position="-91.863214,42.700431"
orientation="-0.70710678,0.70710678"
id="guide1524"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
</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"
transform="translate(-16.168953,-400)"
style="display:inline">
<g
id="g372">
<g
transform="translate(-407.70972)"
id="g1433">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:3;stroke-linecap:butt;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 637.21276,442.13269 -7.36914,-7.47852 c 0,0 -28.8291,0.88227 -39.76196,5.88251 -10.93286,5.00025 -25.81714,43.44409 -27.22974,60.10694 -1.41259,16.66284 5.59131,79.27301 5.59131,79.27301 0,0 -24.91552,52.53601 -25.32446,75.27893 -0.76329,42.44959 68.62109,128.5404 134.36865,125.19647 65.74756,-3.34394 84.87379,-61.25208 83.01639,-96.18292 -1.85745,-34.93085 -24.0347,-104.81464 -24.0347,-104.81464 0,0 3.22168,-38.68249 0.51367,-55.62091 -2.708,-16.93842 -11.71606,-56.80554 -18.39038,-64.55786 -6.67431,-7.75232 -37.8457,-17.78882 -37.8457,-17.78882 l -6.53735,7.85638 c -12.1313,-4.07352 -24.5562,-5.43098 -36.99659,-7.15057 z"
id="path1375"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccsscssccssccc" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;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 612.41098,758.14269 c 0,0 26.76334,-11.80523 23.71829,-39.47179 -3.04504,-27.66657 -32.98144,-79.07678 -45.64611,-110.1203 -12.66468,-31.04352 -15.74874,-51.61667 -15.74874,-51.61667 l 23.05147,-8.93851 c 0,0 -7.96118,-20.18219 -6.17273,-34.80102 1.78845,-14.61884 8.3562,-33.93274 8.3562,-33.93274 l -21.43054,-2.36243 c 0,0 5.83261,-22.58934 16.60092,-28.15233 10.76831,-5.56298 19.23914,-5.81466 28.58237,-6.42699 7.5605,-0.4955 15.97198,18.16221 15.97198,18.16221 l 16.26808,2.17394 -0.32704,18.44984 -5.25454,-0.5654 c -3.67431,-0.56751 -4.4221,1.65708 -4.4841,3.99801 l 0.59236,44.46703 c 0.17877,3.31112 2.72713,6.15312 5.36572,6.27812 l 5.67576,0.87203 1.14008,17.91322 -11.71232,-0.0529 c 0,0 0.46941,10.73577 1.62541,15.95522 0.58121,2.62422 2.64643,7.61678 2.64643,7.61678 l -5.41105,14.14438 c -0.3143,4.44983 3.4652,8.4858 6.87092,8.70703 l 5.74669,0.1166 0.0229,11.90123 4.43947,0.27181 0.57583,-12.38929 6.31564,-0.49459 c 3.86404,0 5.05122,-6.48394 4.39517,-9.41658 l -4.08726,-12.49046 c 0,0 2.54921,-4.39682 3.08963,-6.83936 1.19851,-5.41691 0.18619,-16.64269 0.18619,-16.64269 l -10.87259,-0.44848 -0.60938,-17.27195 6.24524,-0.76363 c 4.06456,0.38511 6.77965,-4.10902 6.66305,-6.86494 l -0.33252,-39.01044 c 0.28048,-3.6366 -2.07788,-7.62472 -5.83705,-7.98954 l -8.47677,-0.49546 0.62854,-18.36184 13.79884,0.89971 c 0,0 6.34522,-14.40167 10.99085,-13.74957 4.64563,0.6521 24.41467,6.6986 27.51233,10.86999 3.09765,4.17138 18.99438,49.84112 18.07202,75.55481 -0.92237,25.71368 -9.57337,116.68359 -9.57337,139.14441 0,22.46081 21.64884,69.89563 21.64884,69.89563 -10.48167,14.90232 -31.78702,34.56021 -69.45713,34.56021 -21.63533,0 -42.34124,-8.87714 -61.36398,-22.28231 z"
id="path1382"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssccsccssccccccccccsccccccccccsccccccccccsssscsc" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 142.66406 158.2168 L 130.64258 163.35547 C 130.64258 163.35547 129.76172 163.91431 128.76172 167.41406 C 127.76172 170.91382 137.07617 201.45703 137.07617 201.45703 L 149.06055 210.76562 L 152.07422 210.875 L 152.07227 210.86523 L 149.33594 210.76562 L 144.33008 201.45703 C 144.33008 201.45703 133.88281 169.16187 134.88281 165.66211 C 135.88281 162.16236 137.75195 160.85547 137.75195 160.85547 L 142.72266 158.54492 L 142.66406 158.2168 z "
transform="translate(423.87867,400)"
id="button4" />
<path
inkscape:label="#path1389"
sodipodi:nodetypes="ccccscc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 567.30901,558.21712 8.91914,52.65846 -3.01355,-0.10922 -5.00602,-9.30922 c 0,0 -10.4463,-32.29576 -9.4463,-35.79552 1,-3.49975 2.86811,-4.80649 2.86811,-4.80649 z"
id="button6"
inkscape:connector-curvature="0" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 150.2168 212.87695 C 150.2168 212.87695 142.38943 214.63751 142.22656 219.33398 C 142.06369 224.03045 149.80475 253.7071 153.02734 256.68164 C 156.24994 259.65617 168.61133 259.91016 168.61133 259.91016 L 168.60352 259.88281 C 167.54083 259.83165 160.96579 259.42094 157.99805 256.68164 C 154.77546 253.7071 147.0344 224.03045 147.19727 219.33398 C 147.35939 214.65909 150.57195 212.89894 150.60156 212.88281 L 150.2168 212.87695 z "
transform="translate(423.87867,400)"
id="button3" />
<path
inkscape:label="#path1396"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 578.07507,612.93874 -3.58529,-0.0618 c 0,0 -3.25129,1.76049 -3.41416,6.45696 -0.16287,4.69647 7.57883,34.3726 10.80142,37.34714 3.2226,2.97453 11.00647,3.22825 11.00647,3.22825 z"
id="button5"
inkscape:connector-curvature="0" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 571.80821,529.2887 16.75168,-1.61745 2.26709,2.47905 2.96588,16.7467 -19.4389,7.16674 -3.05292,-21.38677 z"
id="path1398"
inkscape:connector-curvature="0" />
<path
inkscape:label="#path1400"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 572.64727,529.77417 1.93371,12.46355 c 0.17435,1.34405 1.04887,1.97653 2.60016,1.83535 l 12.20529,-1.49705 c 0.75646,-0.10483 2.09687,-1.30513 1.66048,-2.69895 l -1.85475,-11.61027 z"
id="button7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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"
id="button2"
width="19.65448"
height="46.726868"
x="650.44757"
y="485.83923"
rx="9.82724"
ry="9.82724"
inkscape:label="#rect932" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 590.00589,504.70809 c 0,0 -1.03484,5.10473 -1.28213,12.10321 -0.16329,4.62125 0.88242,10.235 0.88242,10.235 l -16.1009,0.81035 c -1.3428,0.0627 -2.62237,-0.55688 -2.44997,-2.19533 l -0.0873,-14.82945 1.66055,-5.2578 z"
id="path1408"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cscccccc" />
<path
inkscape:label="#path1410"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 573.0899,505.40318 0.20623,15.85869 c -0.0713,1.05397 -0.0531,1.75142 1.13489,1.61532 l 12.98495,-0.46789 c 0,0 -0.15978,-5.93198 0.18903,-8.06114 0.34882,-2.12917 2.24487,-9.58433 2.24487,-9.58433 z"
id="button8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 572.95934,499.38009 c -0.23891,1.10591 0.96351,2.11782 2.59219,2.20621 l 11.93492,0.43018 c 1.44453,-0.125 3.244,-1.82045 3.95047,-4.53981 l 3.64306,-15.04301 c 0.17855,-1.27163 -0.13707,-2.71932 -1.86683,-2.77546 l -13.72539,-1.45781 c -1.4456,-0.0958 -2.68982,1.02632 -2.93832,2.65451 z"
id="path1412"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
<path
inkscape:label="#path1414"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 581.62245,478.87309 c -1.41933,-0.125 -3.23079,0.15096 -3.56732,1.46211 l -2.1321,13.50026 c -0.54179,1.66011 0.55078,2.48769 2.41358,2.75904 l 8.73161,1.00413 c 2.00999,0.16125 2.98232,-1.37377 3.2488,-2.60043 l 3.35525,-12.3699 c 0.31909,-1.42149 0.0372,-2.70382 -1.81709,-2.80852 z"
id="button9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
<path
inkscape:label="#path1416"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 667.01457,578.34297 c 0,0 2.43437,5.37905 2.21672,7.83622 -0.21765,2.45717 0.057,8.12536 0.057,8.12536 -0.084,0.82971 -0.54395,1.59363 -2.16234,1.87243 l -13.39023,-0.0774 c -0.78452,-0.28984 -1.84803,-0.88996 -1.76233,-2.03309 0,0 -0.61952,-8.35025 0.0674,-10.32185 0.68693,-1.97161 1.63297,-5.64161 1.63297,-5.64161 0,0 8.03972,0.11155 13.34081,0.23994 z"
id="button10"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csccccscc" />
<path
inkscape:label="#path1418"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;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 653.91725,575.19687 13.46749,0.34741 c 0,0 3.20643,-8.97326 3.20643,-11.33956 0,-2.36631 -0.24059,-5.29814 -0.24059,-5.29814 0.14618,-1.05058 -1.20836,-1.93243 -2.12238,-1.98332 h -15.76925 c -1.30503,-0.0224 -1.73401,1.14136 -1.93921,2.31117 0,0 0.0841,7.54347 0.60793,9.21094 0.52379,1.66747 2.78958,6.7515 2.78958,6.7515 z"
id="path1418"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscccccc" />
<path
sodipodi:nodetypes="ccsccccccccccsssc"
inkscape:connector-curvature="0"
id="button1"
d="m 674.1855,590.4286 -4.08726,-12.49046 c 0,0 2.54921,-4.39682 3.08963,-6.83936 1.19851,-5.41691 0.18619,-16.64269 0.18619,-16.64269 l -10.87259,-0.44848 -0.60938,-17.27195 6.24524,-0.76363 c 4.06456,0.38511 6.77965,-4.10902 6.66305,-6.86494 l -0.33252,-39.01044 c 0.28048,-3.6366 -2.07788,-7.62472 -5.83705,-7.98954 l -8.47677,-0.49546 0.62854,-18.36184 13.79884,0.89971 c 0,0 6.34522,-14.40167 10.99085,-13.74957 4.64563,0.6521 24.41467,6.6986 27.51233,10.86999 3.09765,4.17138 18.99439,49.84112 18.07202,75.55481 -0.46118,12.85684 -2.85453,42.02774 -5.13257,70.79203"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;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" />
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;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 590.48316,608.5506 c -12.66468,-31.04352 -15.74874,-51.61667 -15.74874,-51.61667 l 23.05147,-8.93851 c 0,0 -7.96118,-20.18219 -6.17273,-34.80102 1.78845,-14.61884 8.3562,-33.93274 8.3562,-33.93274 l -21.43054,-2.36243 c 0,0 5.83261,-22.58934 16.60092,-28.15233 10.76831,-5.56298 19.23914,-5.81466 28.58237,-6.42699 7.5605,-0.4955 15.97198,18.16221 15.97198,18.16221 l 16.26808,2.17394 -0.32704,18.44984 -5.25454,-0.5654 c -3.67431,-0.56751 -4.4221,1.65708 -4.4841,3.99801 l 0.59236,44.46703 c 0.17877,3.31112 2.72713,6.15312 5.36572,6.27812 l 5.67576,0.87203 1.14008,17.91322 -11.71232,-0.0529 c 0,0 0.46941,10.73577 1.62541,15.95522 0.58121,2.62422 2.64643,7.61678 2.64643,7.61678 l -5.41105,14.14438"
id="button0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccsccssccccccccccscc" />
</g>
<path
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;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 274.29495,505.2149 v 7.97553 l 3.83318,-3.83321 z"
id="button12"
inkscape:connector-curvature="0"
inkscape:label="#path1146" />
<path
inkscape:connector-curvature="0"
id="button11"
d="m 230.83554,505.2149 v 7.97553 l -3.83318,-3.83321 z"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;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="#path1148" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
style="display:inline"
transform="translate(-16.168953)">
<g
id="button0-path"
inkscape:label="#g226">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path954"
d="m 234.68702,62.49186 41.9857,-41.98571 H 483"
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="62"
x="230"
height="6.999999"
width="6.999999"
id="rect952"
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="#rect865"
y="20"
x="483"
height="1"
width="1"
id="button0-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
</g>
<g
id="button1-path"
inkscape:label="#g221">
<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 275.03741,60.50615 H 483.05562"
id="path958"
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="rect968"
width="6.999999"
height="6.999999"
x="273"
y="57" />
<rect
inkscape:label="#rect867"
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="483"
y="60" />
</g>
<g
id="button9-path"
inkscape:label="#g216">
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path960"
d="M 483,100.50615 H 361.43709 L 336.37251,75.396706 H 189.62772 l -8.6513,10"
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="82.008446"
x="177.07306"
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="#rect869"
y="100"
x="483"
height="1"
width="1"
id="button9-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
</g>
<g
id="button12-path"
inkscape:label="#g206">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path964"
d="M 483.5,180.00615 H 360.68314 l -59.54024,-65.8551 -33.2008,0.0451"
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="109.90022"
x="264.5123"
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" />
<rect
inkscape:label="#rect873"
y="179.50615"
x="483"
height="1"
width="1"
id="button12-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
</g>
<g
id="button7-path"
inkscape:label="#g171">
<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 17.151054,140.50027 H 182.33834"
id="path76"
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="rect972"
width="6.999999"
height="6.999999"
x="176.04955"
y="136.99055" />
<rect
transform="scale(-1)"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button7-leader"
width="1"
height="1"
x="-17.168953"
y="-141.00027"
inkscape:label="#rect873" />
</g>
<g
id="button10-path"
inkscape:label="#g176">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path1259"
d="M 17.16895,180.50027 H 246.82196"
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="-184.26186"
x="-252.73283"
height="6.999999"
width="6.999999"
id="rect1261"
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"
transform="scale(-1)" />
<rect
inkscape:label="#rect873"
y="-181.00027"
x="-17.168953"
height="1"
width="1"
id="button10-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
transform="scale(-1)" />
</g>
<g
id="button6-path"
inkscape:label="#g181">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path1295"
d="m 17.16895,220.50027 h 33.151095 l 30.12282,-29.87718 h 81.709115"
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="-194.0271"
x="-167.2599"
height="6.999999"
width="6.999999"
id="rect1297"
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"
transform="scale(-1)" />
<rect
transform="scale(-1)"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button6-leader"
width="1"
height="1"
x="-17.168953"
y="-221.00027"
inkscape:label="#rect873" />
</g>
<g
id="button4-path"
inkscape:label="#g186">
<path
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 17.16895,260.50027 h 42.566708 l 60.284632,-61.94843 h 31.82975"
id="path1299"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
transform="scale(-1)"
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="rect1301"
width="6.999999"
height="6.999999"
x="-156.24265"
y="-202.01924" />
<rect
inkscape:label="#rect873"
y="-261.00027"
x="-17.168953"
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"
transform="scale(-1)" />
</g>
<g
id="button11-path"
inkscape:label="#g211">
<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="button11-leader"
width="1"
height="1"
x="483"
y="140" />
<path
inkscape:connector-curvature="0"
id="path1512"
d="M 482.98501,140.50615 H 359.31376 l -34.84984,-39.6734 -82.25306,-0.004"
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"
sodipodi:nodetypes="cccc" />
<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="rect1514"
width="6.999999"
height="6.999999"
x="235.83218"
y="99.10099" />
</g>
<g
id="button3-path"
inkscape:label="#g191">
<path
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 17.16895,300.50027 h 51.327469 l 81.344411,-79.98695 h 10.85261"
id="path1502"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
transform="scale(-1)"
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="rect1504"
width="6.999999"
height="6.999999"
x="-164.00285"
y="-223.98463" />
<rect
transform="scale(-1)"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button3-leader"
width="1"
height="1"
x="-17.168953"
y="-301.00027"
inkscape:label="#rect873" />
</g>
<g
id="button5-path"
inkscape:label="#g196">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path1506"
d="M 17.16895,340.50027 H 97.369601 L 183.06798,252.94705"
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="-253.98224"
x="-185.04956"
height="6.999999"
width="6.999999"
id="rect1508"
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"
transform="scale(-1)" />
<rect
inkscape:label="#rect873"
y="-341.00027"
x="-17.168953"
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"
transform="scale(-1)" />
</g>
<g
id="button2-path"
inkscape:label="#g201">
<path
sodipodi:nodetypes="ccc"
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 482.7753,220.37357 -139.89827,-0.0147 -91.18642,-98.70563"
id="path962"
inkscape:connector-curvature="0" />
<rect
y="219.87363"
x="483"
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" />
<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="rect436"
width="6.999999"
height="6.999999"
x="246.30431"
y="116.44096" />
</g>
<g
id="button8-path"
inkscape:label="#g166">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path438"
d="m 17.282046,100.50073 138.615124,0.125 20.88991,20.36764"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999988;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="116.02772"
x="174.8121"
height="6.999999"
width="6.999999"
id="rect1435"
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="#rect873"
y="-101.00027"
x="-17.168953"
height="1"
width="1"
id="button8-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
transform="scale(-1)" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
style="display:inline"
transform="translate(-16.168953)" />
</svg>

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,516 @@
<?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="482.65375"
height="409.75723"
viewBox="0 0 482.65374 409.75722"
version="1.1"
id="svg8"
inkscape:version="0.91 r13725"
sodipodi:docname="roccat-kone-xtd.svg"
inkscape:export-filename="/home/jimmac/logitech-g403.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2">
<inkscape:path-effect
effect="spiro"
id="path-effect4426"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect4416"
is_visible="true" />
<inkscape:path-effect
is_visible="true"
id="path-effect4412"
effect="spiro" />
<inkscape:path-effect
effect="spiro"
id="path-effect4397"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect4384"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect4380"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect4368"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect4360"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect4193"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect4185"
is_visible="true" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.70710678"
inkscape:cx="-64.59026"
inkscape:cy="199.95686"
inkscape:document-units="px"
inkscape:current-layer="Buttons"
inkscape:document-rotation="0"
showgrid="false"
units="px"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:pagecheckerboard="false"
showguides="true"
inkscape:window-width="1920"
inkscape:window-height="1101"
inkscape:window-x="1920"
inkscape:window-y="28"
inkscape:window-maximized="1"
fit-margin-top="20"
fit-margin-left="0"
fit-margin-bottom="20"
fit-margin-right="0"
inkscape:guide-bbox="true"
guidetolerance="10"
objecttolerance="10"
gridtolerance="10">
<inkscape:grid
type="xygrid"
id="grid956"
originx="36.653759"
originy="3.4686305" />
<sodipodi:guide
position="125.24434,292.81279"
orientation="1,0"
id="guide4350" />
<sodipodi:guide
position="232.01745,389.68642"
orientation="0,1"
id="guide4352" />
<sodipodi:guide
position="339.49769,179.67571"
orientation="1,0"
id="guide4354" />
<sodipodi:guide
position="262.42305,21.283798"
orientation="0,1"
id="guide4356" />
<sodipodi:guide
position="61.51829,157.33126"
orientation="1,0"
id="guide5867" />
</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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="Device"
inkscape:label="Device"
transform="translate(36.653757,-393.71139)"
style="display:inline">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 122.82453,740.37034 c -19.02281,-23.41625 -31.616134,-52.28805 -34.233943,-82.34359 -2.096443,-24.06963 2.128869,-48.32241 8.95101,-71.5 2.158331,-7.33273 4.577033,-14.58882 7.250003,-21.75 -1.3982,-18.81093 -1.06298,-37.75033 1,-56.5 2.52814,-22.97724 7.65448,-45.66761 15.25,-67.5 l 13.25,-11.25 141.5,-5.25 c 12.02953,26.7963 18.37607,56.12763 18.5,85.5 0.12121,28.72967 -5.62197,57.27681 -5,86 0.24638,11.37797 1.49774,22.75148 4.0739,33.8367 3.20755,13.80209 8.47842,27.26326 9.22845,41.4133 0.66161,12.48182 -2.27756,24.99565 -7.36927,36.41091 -5.0917,11.41525 -12.28663,21.79231 -20.30154,31.38369 -9.49466,11.36219 -20.28557,21.78699 -32.84623,29.62777 -12.56066,7.84078 -26.98545,13.02513 -41.78531,13.48575 -15.18013,0.47246 -30.29151,-4.05272 -43.44909,-11.63807 -13.15758,-7.58536 -24.44174,-18.13853 -34.01798,-29.92646 z"
id="path4382"
inkscape:path-effect="#path-effect4384"
inkscape:original-d="m 122.82453,740.37034 c 0,0 -34.233943,-46.59359 -34.233943,-82.34359 0,-35.75 5.95101,-62.75 8.95101,-71.5 3.000003,-8.75 7.250003,-21.75 7.250003,-21.75 25.62768,0 -0.25,-44 1,-56.5 1.25,-12.5 15.25,-67.5 15.25,-67.5 l 13.25,-11.25 141.5,-5.25 c 0,0 18.25,53.25 18.5,85.5 0.25,32.25 -5.25,74.75 -5,86 0.125,5.625 0.81574,20.14113 4.0739,33.8367 3.25816,13.69557 6.83375,29.07057 9.22845,41.4133 0.84882,4.375 -22.22676,62.14156 -27.67081,67.7946 -24.05895,24.98254 -60.00654,43.19257 -74.63154,43.11352 -48.05458,-0.25976 -77.46707,-41.56453 -77.46707,-41.56453 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscscccssssssc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 134.2916,429.52675 -12.75,12.5 c -12.31411,36.57641 -16.0253,76.01857 -10.75,114.25 6.1427,44.5177 24.24594,87.57778 23,132.5 -0.49267,17.76334 -4.06145,35.43727 -10.5,52 8.39201,13.20649 20.3144,24.14654 34.19206,31.37481 13.87766,7.22828 29.67603,10.72678 45.30794,10.03331 29.20955,-1.29581 57.30887,-17.926 72.5,-42.90812 -6.99047,-28.36406 -8.86284,-57.98142 -5.5,-87 5.139,-44.34544 22.31262,-86.91141 24.5,-131.5 1.63092,-33.24546 -5.32115,-66.87607 -20,-96.75 l -14,-9.49452 c -20.53616,-0.82089 -41.13517,-0.0647 -61.55586,2.25985 -21.78053,2.4793 -43.35766,6.74311 -64.44414,12.73467 z"
id="path4378"
inkscape:original-d="m 134.2916,429.52675 -12.75,12.5 c 0,0 -18.75,71.75 -10.75,114.25 8,42.5 23.42774,101.24777 23,132.5 -0.42774,31.25223 -10.5,52 -10.5,52 0,0 43.5,41.40812 79.5,41.40812 36,0 72.5,-42.90812 72.5,-42.90812 0,0 -20,-25.5 -5.5,-87 14.5,-61.5 23,-104.5 24.5,-131.5 1.5,-27 -20,-96.75 -20,-96.75 l -14,-9.49452 c -22.40781,-0.31655 -36.15781,-0.75 -61.55586,2.25985 -25.99748,3.08089 -64.44414,12.73467 -64.44414,12.73467 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccszcscssccsc"
inkscape:path-effect="#path-effect4380" />
<path
style="fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 102.95061,567.96501 c 0.70002,22.44793 2.65772,43.37669 3.30638,62.88708 0.52948,15.92576 7.34749,40.38934 -4.69813,38.78754 -4.929835,-0.65556 -12.883112,-9.26574 -13.017313,-24.73788"
id="path4401"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssc" />
<path
inkscape:label="#path924"
style="color:#000000;display:inline;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 104.14503,591.78576 c 2.70152,2.04148 8.34731,2.78189 8.46463,3.52635 l 7.53906,47.84087 c 0.37596,2.38573 -1.6214,1.86287 -1.6214,1.86287 -2.38894,0.35504 -11.76758,-6.66231 -11.90814,-9.07339 l -1.90715,-32.71 c -0.14062,-2.41107 -0.567,-11.4467 -0.567,-11.4467 z"
id="button4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscssc" />
<path
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 105.59036,538.91245 c 0.52253,-0.12564 0.84775,3.74005 1.08254,6.14378 l 4.20424,41.21202 -6.91297,-2.23542 -3.04198,-39.25359 c -0.1866,-2.40794 2.31991,-5.30229 4.66817,-5.86679 z"
id="button3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssccss"
inkscape:label="#rect921" />
<path
style="color:#000000;overflow:visible;opacity:1;fill:#eeeeec;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 216.66981,463.35766 c 7.7882,31.51628 -3.10225,161.1236 -3.10225,161.1236 -0.13535,7.02963 -6.07063,10.44123 -13.61129,10.44123 -7.54065,0 -13.50233,-3.41113 -13.61128,-10.44123 0,0 -10.54298,-129.82998 -2.5,-161.31424 8.04298,-31.48426 11.005,-29.3064 16.58919,-29.26346 5.58419,0.0429 8.44743,-2.06218 16.23563,29.4541 z"
id="rect913"
inkscape:connector-curvature="0"
sodipodi:nodetypes="zssszzz" />
<rect
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"
id="button2"
width="23.15593"
height="57.717773"
x="188.55382"
y="461.01868"
rx="11.577965"
ry="11.577967"
inkscape:label="#rect932" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="button9"
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" />
<path
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"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssscc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 199.58066,416.69667 c -0.28142,0.0314 -0.56434,0.0637 -0.8457,0.0957 -21.78053,2.47931 -43.35688,6.74282 -64.44336,12.73438 l -5.24219,5.13867 c -4.63188,14.23762 -6.48452,22.56972 -10.85742,51.73047 -2.31322,15.42571 0.0333,42.97083 4.37695,70.53516 20.18393,0.10496 59.33211,-0.93056 59.53906,0.375 l 0.0371,-0.30274 c -1.67671,-36.75209 -2.37374,-77.8923 1.69922,-93.83594 6.49241,-25.41455 9.67504,-28.8908 13.59766,-29.26562 l 2.25586,-0.10352 z"
id="button0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccscccsccc" />
<path
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.55097,552.1948 -5e-4,13.20984 -17.18836,0 -5.2e-4,-13.17859 z"
id="path4391"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="button11"
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" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#d3d3ce;fill-opacity:1;fill-rule:nonzero;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 245.09433,414.2123 c -15.20199,-0.0324 -30.40556,0.79698 -45.51367,2.48437 l 0.33399,17.16016 1.96093,0.0351 c 4.68444,-0.0473 7.71457,0.81299 14.79493,29.46485 3.87599,15.68486 3.12444,55.66092 1.39257,91.86914 l 0.23829,1.79297 60.07421,0.69336 c 8.82289,-52.62093 4.27435,-90.98774 -13.62695,-139.62696 l -0.13281,-0.62109 -4.32422,-2.93164 c -5.0633,-0.2024 -10.12994,-0.30953 -15.19727,-0.32031 z"
id="button1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccscccccccc" />
<path
id="led0"
style="fill:#babdb6;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 290.24244,554.46605 c -5.26976,49.56099 -36.23031,99.47705 -15.28225,184.71113 -3.16837,4.44852 -3.173,4.56365 -8.05566,10.86971 -17.84401,-50.57656 -7.8566,-125.64401 4.04428,-156.62518 17.6011,-72.50699 15.50543,-116.35777 -6.20089,-175.33551 l 9.29158,5.5835 c 10.82003,25.11299 27.5667,64.49596 16.20294,130.79635 z m -180.19239,-2.47487 c 7.61079,68.82965 34.53563,125.64 13.2841,188.42344 3.24708,3.97977 8.53092,12.44446 12.34811,15.28913 18.99515,-41.57656 13.57812,-98.28741 2.86138,-129.26858 -11.32886,-31.75516 -24.92286,-109.55879 -20.35212,-140.03876 4.57073,-30.47997 6.3548,-38.13399 11.4734,-53.63774 l -10.11812,10.8868 c -9.96888,31.30753 -13.36232,66.4405 -9.49675,108.34571 z"
inkscape:label="#path5753"
inkscape:connector-curvature="0" />
<path
inkscape:label="#path1146"
inkscape:connector-curvature="0"
id="button6"
d="m 221.59975,489.94486 0,7.97552 3.83318,-3.83321 z"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#888a85;fill-opacity:1;stroke:none;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" />
<path
inkscape:label="#path1148"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#888a85;fill-opacity:1;stroke:none;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 178.14034,489.94486 0,7.97552 -3.83318,-3.83321 z"
id="button5"
inkscape:connector-curvature="0" />
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
style="display:inline"
transform="translate(36.653757,6.2887068)">
<g
id="button1-path"
inkscape:label="#g131"
transform="translate(8,72)">
<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 242.94764,50.505764 194.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="237"
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
id="button11-path"
inkscape:label="#g141"
transform="translate(8,-88)">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path960"
d="m 437,130.50576 -237.65602,0 -3.89602,3.89602"
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="#path960" />
<rect
y="133.00577"
x="189"
height="6.999999"
width="6.999999"
id="rect970"
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"
inkscape:label="#rect970" />
<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"
width="1"
height="1"
x="437"
y="130.00577" />
</g>
<g
id="button2-path"
inkscape:label="#g136"
transform="translate(8,-8)">
<rect
inkscape:label="#rect869"
y="90.00576"
x="437"
height="1"
width="1"
id="button2-leader"
style="color:#000000;text-align:start;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
<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"
d="m 195.97852,94.49147 3.9857,-3.98571 237.03578,0"
id="path908"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<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="rect910"
width="6.999999"
height="6.999999"
x="190"
y="92" />
</g>
<g
id="button10-path"
inkscape:label="#g146"
transform="translate(8,-8)">
<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"
id="path51"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<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="rect53"
width="6.999999"
height="6.999999"
x="189"
y="187" />
<rect
y="170"
x="437"
height="1"
width="1"
id="button10-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"
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"
d="m 37.16895,300.50027 59.327469,0 79.344411,-79.98695 96.85261,0"
id="path1502"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
transform="scale(-1,-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="rect1504"
width="6.999999"
height="6.999999"
x="-276.00287"
y="-223.98463" />
<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"
width="1"
height="1"
x="-37.168953"
y="-301.00027"
inkscape:label="#rect873" />
</g>
<g
transform="translate(-72.82271,-38.03545)"
style="display:inline"
id="button4-path"
inkscape:label="#g196">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path1506"
d="m 37.16895,340.50027 60.200651,0 87.698379,-87.55322"
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" />
<rect
y="-257.98224"
x="-187.04956"
height="6.999999"
width="6.999999"
id="rect1508"
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)" />
<rect
inkscape:label="#rect873"
y="-341.00027"
x="-37.168953"
height="1"
width="1"
id="button4-leader"
style="color:#000000;text-align:end;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
transform="scale(-1,-1)" />
</g>
<g
inkscape:label="#g196"
id="button3-path"
style="display:inline"
transform="translate(-72.82271,-78.03545)">
<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"
d="m 37.16895,340.50027 60.200651,0 83.698379,-83.55322"
id="path5817"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
transform="scale(-1,-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="rect5819"
width="6.999999"
height="6.999999"
x="-183.04956"
y="-261.98224" />
<rect
transform="scale(-1,-1)"
style="color:#000000;text-align:end;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button3-leader"
width="1"
height="1"
x="-37.168953"
y="-341.00027"
inkscape:label="#rect873" />
</g>
<g
inkscape:label="#g191"
id="button0-path"
style="display:inline"
transform="translate(-72.0872,-137.95323)">
<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"
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
y="-223.98463"
x="-226.00287"
height="6.999999"
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)"
inkscape:label="#rect5843" />
<rect
inkscape:label="#rect873"
y="-321.00027"
x="-37.168953"
height="1"
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)" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
style="display:inline"
transform="translate(36.653757,6.2887068)">
<g
id="led0-path"
inkscape:label="#g161"
transform="translate(8,56)">
<path
inkscape:connector-curvature="0"
id="path877"
d="m 437,290.50577 -158.35562,0 -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="256.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="437"
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: 29 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 64 KiB

View File

@@ -1,389 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="414.6424mm"
height="285.98199mm"
viewBox="0 0 1469.2054 1013.322"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="logitech-mx_master.svg">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient5204">
<stop
style="stop-color:#868686;stop-opacity:1;"
offset="0"
id="stop5206" />
<stop
style="stop-color:#868686;stop-opacity:0;"
offset="1"
id="stop5208" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient5128">
<stop
style="stop-color:#3a3a3a;stop-opacity:1"
offset="0"
id="stop5130" />
<stop
style="stop-color:#aaaaaa;stop-opacity:1"
offset="1"
id="stop5132" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5128"
id="linearGradient5134"
x1="230.63638"
y1="432.30179"
x2="238.061"
y2="447.85815"
gradientUnits="userSpaceOnUse"
spreadMethod="reflect" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5204"
id="linearGradient5210"
x1="196.63423"
y1="468.17209"
x2="170.13423"
y2="402.67206"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.70710678"
inkscape:cx="672.09896"
inkscape:cy="504.57351"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
fit-margin-top="50"
fit-margin-left="50"
fit-margin-right="50"
fit-margin-bottom="50"
inkscape:window-width="1920"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<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:label="Mouse"
inkscape:groupmode="layer"
id="layer1"
transform="translate(522.78594,97.359194)"
style="display:inline"
sodipodi:insensitive="true">
<path
style="fill:#433737;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 921.14258,351.92188 c -10.71472,0.3561 17.48242,40.65039 17.48242,40.65039 l -0.99805,38.58007 c -0.0632,-0.10181 -0.1163,-0.2052 -0.18164,-0.30664 -0.41273,0.4635 -0.8827,0.9276 -1.47656,1.39063 C 883.92622,472.81347 836.5,478.32226 756.5,516.32227 c -80,37.99997 -316.69239,159.29807 -343.5,147.5 -0.19217,-0.0846 -0.3462,-0.18445 -0.50195,-0.28321 -0.14711,0.0864 -0.35538,0.19871 -0.49805,0.28321 -0.0138,-0.0118 -0.0272,-0.0234 -0.041,-0.0352 l -0.0273,0.0156 c 20.65212,21.99316 41.34778,28.42752 134.68945,6.01954 l 67.87891,0 c 0,0 31.685,6.41255 73,41.25 56.21397,47.40048 70.15272,37.94645 213.83008,7.3789 120.72766,-25.68496 98.22343,-76.56575 107.07816,-168.1289 5.6329,-58.24749 15.6725,-114.94915 -35.90824,-161 -31.61176,-28.22275 -46.12465,-37.57431 -51.35742,-37.40039 z"
transform="translate(-522.78594,-97.359255)"
id="path5084"
inkscape:connector-curvature="0" />
<path
style="fill:#3d3d3d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 766.6582,304.5 c -179.15881,0.002 -367.84961,155.86523 -367.84961,155.86523 -21.93235,32.98189 -41.23548,69.54864 -57.98242,109.60157 7.5036,28.04106 30.67382,59.21512 71.17383,93.85547 59.9519,-35.50497 512.63903,-236.02476 512.5,-244 95.7156,51.99999 -269.006,189 -269,189 -12.27104,5.04673 -14.55606,11.0677 -7.5,18 l 2.06445,1.76953 0.002,0 11.89062,10.1875 c -0.62249,0.15805 -1.26128,0.30706 -1.87305,0.48242 -47.26127,13.54738 -84.55325,23.64795 -114.41015,30.79101 L 615,669.82227 l 63.40625,-16.95508 38.59961,33.06445 c 38.18376,34.64823 37.84255,32.06806 141.42187,14.14258 C 964.16532,681.77523 962.3307,680.80807 974.39258,545.2168 984.45817,432.06647 1011.3318,377.81074 858.42773,320.35742 828.82733,309.23515 797.88772,304.49969 766.6582,304.5 Z"
transform="translate(-522.78594,-97.359255)"
id="path5077"
inkscape:connector-curvature="0"
sodipodi:nodetypes="scccccccccsccccssss" />
<path
style="fill:#826c5e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 924.5,419.82227 c 0.13868,7.95534 -450.25292,207.4789 -512.00586,243.71484 0.15695,0.0995 0.31224,0.19994 0.50586,0.28516 26.80761,11.79809 263.5,-109.50001 343.5,-147.5 80,-38.00001 127.42622,-43.50878 179.46875,-84.08594 0.5964,-0.46501 1.06842,-0.93101 1.48242,-1.39649 -2.56726,-3.98332 -6.80221,-7.67699 -12.95117,-11.01757 z"
transform="translate(-522.78594,-97.359255)"
id="path5079"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 633.2832,328.9668 c -128.50884,43.87332 -234.47461,131.39843 -234.47461,131.39843 -21.86064,32.87406 -41.10756,69.31169 -57.8164,109.21094 l 0.008,-0.004 61.25,-46.25 c -0.31467,-30.27892 8.67871,-33.61166 108.5,-91 105.67745,-60.75512 109.87649,-54.6167 115.74609,-80.49805 1.83541,-8.09301 4.52898,-16.4514 6.78711,-22.85742 z"
transform="translate(-522.78594,-97.359255)"
id="path5101"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 640.70703,428.57422 c -3.04651,0.0756 -6.31444,0.73835 -9.96875,2.0918 -19.09188,7.07104 -141.89935,74.20682 -175.71484,98.99414 -48.59449,35.62053 -48.4375,14.14257 -48.4375,14.14257 l -4.24219,-20.85937 c 0,0 -0.0901,0.0489 -0.0937,0.0508 9.1e-4,0.11168 -10e-4,0.2157 0,0.32813 l -61.25,46.25 -0.008,0.004 c -0.0548,0.13093 -0.11124,0.25961 -0.16602,0.39063 7.5036,28.04104 30.67382,59.21512 71.17383,93.85547 0.14966,-0.0886 0.33959,-0.19447 0.49414,-0.28516 0.0169,-0.01 0.0397,-0.0213 0.0566,-0.0312 1.02546,-0.60112 2.16407,-1.25057 3.39844,-1.93945 0.2066,-0.11531 0.43415,-0.23971 0.64648,-0.35743 1.08032,-0.59888 2.2254,-1.22479 3.45118,-1.88476 0.32998,-0.17769 0.67334,-0.36095 1.01367,-0.54297 1.22538,-0.65535 2.51457,-1.33647 3.87109,-2.04687 0.32591,-0.17069 0.65104,-0.34183 0.98438,-0.51563 0.98003,-0.51099 2.03888,-1.05627 3.08203,-1.59375 1.50441,-0.77516 3.19823,-1.63034 4.83007,-2.45898 2.6812,-1.36148 5.48308,-2.77245 8.49219,-4.27149 1.45002,-0.72234 2.95192,-1.46789 4.47461,-2.2207 3.14575,-1.55527 6.43566,-3.16851 9.87696,-4.84766 1.7505,-0.85415 3.50304,-1.70917 5.32617,-2.59375 3.69907,-1.79476 7.57491,-3.665 11.55664,-5.57812 1.62203,-0.77935 3.19102,-1.53607 4.85742,-2.33399 4.85137,-2.32297 9.91021,-4.73306 15.11328,-7.20312 1.05862,-0.50256 2.06054,-0.98002 3.13281,-1.48828 6.27234,-2.97314 12.74159,-6.02841 19.44532,-9.18164 0.19493,-0.0917 0.37504,-0.1777 0.57031,-0.26954 0.88374,-0.4156 1.84183,-0.86083 2.73242,-1.27929 8.89106,-4.17765 18.33343,-8.58591 27.83008,-13.01563 9.48674,-4.42512 19.20352,-8.94432 29.15625,-13.5625 5.63004,-2.6124 11.17539,-5.18811 16.91992,-7.84765 11.03955,-5.11098 22.24511,-10.28776 33.57227,-15.51563 4.59686,-2.12162 9.06381,-4.18813 13.6875,-6.32031 31.79772,-14.66333 64.04275,-29.4992 94.89648,-43.72656 0.64347,-0.29672 1.23671,-0.57103 1.87891,-0.86719 -0.0661,-2.523 -0.5325,-5.27289 -1.4043,-8.3125 -2.95973,-10.3194 -36.14133,-35.93502 -56.06055,-50.55859 C 663.08118,438.83704 654.18972,428.94451 642,428.57812 c -0.42664,-0.0128 -0.85775,-0.0147 -1.29297,-0.004 z"
transform="translate(-522.78594,-97.359255)"
id="path5107"
inkscape:connector-curvature="0" />
<rect
style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:1.00065422;stroke-opacity:1"
id="rect5118"
width="42.955582"
height="22.140448"
x="-63.730179"
y="299.94894"
rx="3.3438475"
ry="3.162921"
transform="matrix(0.89707316,-0.44188205,0.39545007,0.91848747,0,0)" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 275.08906,417.33778 4.375,22.875 80.75,-60.625 z"
id="path5120"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 281.58906,443.33778 91.75,-68.625 c 10.87423,-8.13345 34.03844,-10.26653 -9.25,21.375 -37.38734,27.32814 -82.5,47.25 -82.5,47.25 z"
id="path5122"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssc" />
<path
style="fill:url(#linearGradient5134);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 197.58906,448.96278 c 6.125,13 10.625,29.125 10.625,29.125 0,0 57.34269,-25.16319 68.25,-30.75 -0.13843,-9.48681 -3.52264,-22.72856 -8.375,-27.875 -27.125,3.75 -70.5,29.5 -70.5,29.5 z"
id="path5124"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#797979;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.47828,341.26173 c 0,0 6.886406,9.46214 7.95495,30.22881 -9.7125082,0.38797 -22.45064,-1.59098 -48.96714,10.78339 -26.5165,12.37437 -35.70889,37.29989 -35.70889,37.29989 l -6.84404,-0.46967 -8.1731,-22.75242 c 0,0 5.196794,-22.7334 30.485095,-40.41753 C -32.486544,338.25007 3.47828,341.26173 3.47828,341.26173 Z"
id="path5138"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccsccczc" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 527.89648,441.94336 c -11.21543,-6.8e-4 -35.91089,1.15173 -54.02148,10.37891 -20.66776,10.53002 -34.76604,36.66892 -38.41016,44.00195 l 7.17383,19.9707 c 2.6388,-7.65898 11.92913,-28.82418 37.36133,-42.34765 27.46537,-14.60461 48.83613,-10.8705 53.86328,-9.67383 -1.11415,-11.22385 -3.86953,-18.48609 -5.74414,-22.32617 -0.0929,-1.7e-4 -0.12786,-0.004 -0.22266,-0.004 z"
transform="translate(-522.78594,-97.359255)"
id="path5140"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 485.25195,479.63281 c -26.5165,12.37437 -35.70886,37.34486 -35.70886,37.34486 9.75656,-6.29912 84.5483,-48.37571 84.5483,-48.37571 -6.04962,0.7682 -22.32294,-1.34352 -48.83944,11.03085 z"
transform="translate(-522.78594,-97.359255)"
id="path5145"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sccs" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Actions"
style="display:inline"
transform="translate(647.36582,205.64997)"
sodipodi:insensitive="true">
<path
style="display:inline;fill:#868686;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -6.1035156,230.91992 c -19.3024674,3.60682 -35.0488854,16.31894 -52.6556354,24.21599 -51.094769,27.0536 -101.437299,55.94388 -149.051399,88.65901 -9.95415,5.51243 -22.54214,11.99271 -33.83984,6.61914 -6.38959,-3.90926 -7.96324,-11.94005 -8.66992,-18.82617 -15.28799,12.40039 -32.51253,23.07504 -46.6875,36.30469 11.43671,32.54466 37.53518,57.07166 61.90234,80.5625 1.79409,1.58773 4.14015,-3.28155 6.15485,-2.80815 C -144.1825,402.09598 -56.798635,363.92186 29.448769,323.50255 46.66114,315.61589 63.86767,307.71041 80.912109,299.46484 75.326356,287.31278 62.865961,280.7519 53.502952,271.87742 35.5688,257.98999 18.22755,242.94827 -1.484375,231.63672 c -1.4916064,-0.46547 -3.0527704,-0.75113 -4.6191406,-0.7168 z"
id="path5151"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:#868686;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -28.414062,137.02148 c -76.907488,29.50803 -148.819988,71.80459 -213.308758,122.89206 -13.55128,17.43946 -23.11685,37.54603 -33.88323,56.82187 -3.7754,7.32186 -7.34192,14.58222 -10.84707,22.04584 11.21464,-8.37958 22.86793,-16.47415 33.20898,-25.8125 -0.25698,-17.631 11.68533,-33.06792 26.42383,-41.59375 31.52229,-22.08766 66.11652,-39.23093 99.04413,-59.05073 30.395219,-17.37588 61.961588,-32.92482 91.295711,-52.0313 6.692064,-6.28955 7.910236,-15.49613 9.814453,-23.93359 l -0.935164,0.35421 -0.812882,0.30789 z"
id="path5162"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:#868686;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 152.77364,310.2307 c 1.1582,6.05599 2.3164,12.11198 3.4746,18.16797 21.37631,-16.04948 42.75261,-32.09896 64.12891,-48.14843 -22.5345,9.99349 -45.06901,19.98697 -67.60351,29.98046 z"
id="path5173"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:#868686;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 261.2834,263.28539 c -12.21657,2.16029 -20.42211,12.86093 -30.56476,19.12846 -18.40968,13.8199 -38.36778,28.59535 -55.81414,41.82076 29.28272,-15.22709 58.48771,-31.62916 82.88671,-54.0625 1.70896,-1.96865 4.73868,-4.48833 4.69532,-6.85547 -0.39767,-0.0641 -0.80252,-0.0596 -1.20313,-0.0312 z"
id="path5187"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:url(#linearGradient5210);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 42.546875,449.04102 c 19.339052,15.73498 36.723013,34.10436 57.603515,47.67578 20.60808,9.36671 43.55773,2.08926 64.83959,-0.49809 35.57938,-6.15258 71.49952,-11.18454 106.39931,-20.59787 15.04923,-4.60526 29.72058,-14.93837 34.46618,-30.81811 9.43121,-25.52623 9.54274,-53.05503 12.87313,-79.81517 -0.62149,-4.24546 3.55982,-14.72991 -0.52003,-14.51534 -91.88723,32.85626 -183.77446,65.71253 -275.661695,98.5688 z"
id="path5192"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:#868686;fill-opacity:1;stroke:none;stroke-width:1.00065422;stroke-opacity:1"
d="m -27.103516,182.32422 c -10.397754,4.71799 -21.409505,9.91406 -30.929687,15.4375 2.508683,3.97971 3.88786,8.5168 5.707031,12.72266 10.007293,-5.84796 21.032198,-9.89858 31.039063,-15.81641 -1.598593,-4.02995 -3.118058,-9.77197 -5.816407,-12.34375 z"
id="rect5212"
inkscape:connector-curvature="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="Leaders"
transform="translate(647.36582,205.64997)"
style="display:inline">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -345.36582,473.672 139,-91"
id="path5269"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -367.36582,346.672 192,-87"
id="path5271"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -336.36582,223.672 152,5"
id="path5273"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 22.63418,39.672 -60,152"
id="path5275"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 296.63418,105.672 -132,205"
id="path5277"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 431.63418,222.672 -187,54"
id="path5279"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 398.63418,512.672 -163,-63"
id="path5281"
inkscape:connector-curvature="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="Controls"
transform="translate(647.36582,205.64997)"
style="display:inline">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-352.13092"
y="486.22949"
id="text4473"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4475"
x="-352.13092"
y="486.22949">Button0</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4477"
y="225.95509"
x="-348.13092"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="225.95509"
x="-348.13092"
id="tspan4479"
sodipodi:role="line">Button1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-376.13092"
y="351.68076"
id="text4481"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4483"
x="-376.13092"
y="351.68076">Button2</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4485"
y="218.54855"
x="442.71985"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="218.54855"
x="442.71985"
id="tspan4487"
sodipodi:role="line">Button3</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="296.71985"
y="96.274208"
id="text4489"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4491"
x="296.71985"
y="96.274208">Button4</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4493"
y="524.99994"
x="402.71985"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="524.99994"
x="402.71985"
id="tspan4495"
sodipodi:role="line">Button5</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-29.28018"
y="29.72551"
id="text4497"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4499"
x="-29.28018"
y="29.72551">Button6</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-425.66364"
y="83.6576"
id="text4572"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-425.66364"
y="83.6576"
id="tspan4576">Resolution0</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="rect4600"
width="323.85489"
height="120.2081"
x="-469.70047"
y="11.469827"
rx="10.321068"
ry="10.321068" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Title">
<text
transform="translate(647.36582,205.64997)"
xml:space="preserve"
style="display:inline;font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-261.24213"
y="-97.912132"
id="text4223"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4225"
x="-261.24213"
y="-97.912132"
style="font-size:70.00000763px">Logitech MX Master</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -1,621 +0,0 @@
<?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="414.6424mm"
height="285.98199mm"
viewBox="0 0 1469.2054 1013.322"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="roccat-kone-xtd.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.3581868"
inkscape:cx="554.68333"
inkscape:cy="461.59649"
inkscape:document-units="px"
inkscape:current-layer="layer5"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
fit-margin-top="50"
fit-margin-left="50"
fit-margin-right="50"
fit-margin-bottom="50"
inkscape:window-width="2560"
inkscape:window-height="1571"
inkscape:window-x="0"
inkscape:window-y="55"
inkscape:window-maximized="1" />
<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:label="Mouse"
inkscape:groupmode="layer"
id="layer1"
transform="translate(522.78594,97.359194)"
style="display:inline">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:20.31594276;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -6.8766439,519.77381 c -32.5046101,10.85914 -64.2932331,9.07935 -55.9542041,-23.77308 18.883288,-74.39257 -57.845532,2.41084 -29.233145,-133.3763"
id="path4770"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc" />
<path
style="fill:#293843;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -33.934887,422.04568 10.14038,-25.10953 C 52.122953,323.19727 160.74504,269.07207 269.83981,256.48052 c 114.24665,-13.18617 232.89306,-1.04277 284.3675,72.85307 67.68352,97.16539 16.07476,170.11753 -8.69176,195.56454 -20.05502,20.60604 -39.26023,38.76732 -57.46217,50.70193 -38.08063,24.96863 -99.02413,29.12078 -118.30443,27.5239 -56.28478,-4.66179 -227.65266,41.307 -235.6432,46.83891 L 32.219046,607.9527 C 4.8894737,559.61921 -21.751345,509.50644 -33.934887,422.04568 Z"
id="path3661"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccsssssccc" />
<path
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 520.63342,372.91486 c -22.02158,0.11026 -45.15654,9.22867 -63.99943,19.07328 -68.64532,35.8642 -269.2094,153.12888 -338.02986,180.28255 l -87.122352,34.37512 c 0.245863,0.43536 0.49112,0.87206 0.737105,1.30711 l 39.216027,16.16965 c 44.83999,-15.41316 98.86516,-40.41176 154.72423,-67.21674 90.48008,-43.41854 177.45369,-92.45774 236.96271,-121.89569 60.39622,-29.87684 105.87888,-24.18751 112.83284,44.85426 6.15884,-13.91293 10.75607,-30.17964 11.63778,-48.45529 -13.44827,-44.58801 -39.32739,-58.63259 -66.95905,-58.49425 z"
id="path4463" />
<path
style="fill:#00ff3a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 460.17444,405.06363 c -25.33816,12.5814 -164.20311,89.16529 -247.36725,132.94909 -31.24494,16.55348 -62.91668,32.44366 -95.5461,46.08015 l 3.13236,9.65662 c 54.85379,-22.99389 108.07344,-49.78006 161.39646,-76.09146 64.31558,-32.64428 127.12882,-68.2385 192.32459,-99.13545 18.32983,-7.64259 38.30614,-12.8303 58.27155,-10.58417 14.47996,1.85097 28.06901,9.59531 37.24885,20.96153 6.66994,-53.74538 -76.10417,-40.33748 -109.46046,-23.83631 z"
id="path4468"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 270.88842,559.80819 26.12051,4.09732 62.14289,-30.90074 c 11.97455,-20.17142 5.93562,-23.53024 5.29239,-31.92505 l -91.50713,47.4608 c -3.16993,2.58552 -6.42443,5.13123 -2.04866,11.26767 z"
id="path4545"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 392.10122,486.05615 c 1.23298,4.69552 4.04803,7.598 -1.19506,19.63307 l -9.73117,17.58438 80.75159,-31.4129 c 5.77716,-10.78358 11.4074,-21.61616 11.4384,-34.31516 -2.7867,-11.40006 -8.20083,-9.22506 -13.65779,-6.82888 z"
id="path4547"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 20.837165,461.33753 26.414193,480.9345 62.94875,466.25238 55.307676,431.63187 Z"
id="path4549"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 178.69842,365.35554 12.29201,31.24217 56.50906,-25.09617 -12.46274,-27.48628 z"
id="path4551"
inkscape:connector-curvature="0" />
<path
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 261.4987,335.64987 11.95056,26.63267 49.85089,-20.31594 12.80416,-3.41445 -6.82888,-14.16994 -12.97489,-3.41445 z"
id="path4553"
inkscape:connector-curvature="0" />
<path
style="font-style:normal;font-weight:normal;font-size:medium;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;writing-mode:tb-rl;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0"
id="path4560"
d="m 204.43745,364.0435 2.09895,5.60714 5.81901,-2.56255 0.64062,1.71134 -5.81901,2.56254 2.09895,5.60714 -1.75511,0.77292 -2.09894,-5.60715 -5.81902,2.56255 -0.64059,-1.71135 5.81898,-2.56254 -2.09892,-5.60714 1.75508,-0.7729 z" />
<path
d="m 305.42303,329.94388 4.03768,8.95391 -2.72436,1.22853 -4.03769,-8.95391 2.72437,-1.22853 z"
id="path4567"
inkscape:connector-curvature="0"
style="font-style:normal;font-weight:normal;font-size:medium;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="font-style:normal;font-weight:normal;font-size:medium;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0"
id="path4569"
d="m 46.989112,448.7916 2.557622,11.39542 -2.682847,1.56354 -2.557622,-11.39545 2.682847,-1.56351 z" />
<path
d="m 42.643233,451.50777 2.557622,11.39545 -2.682847,1.56351 -2.557622,-11.39545 2.682847,-1.56351 z"
id="path4577"
inkscape:connector-curvature="0"
style="font-style:normal;font-weight:normal;font-size:medium;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="font-style:normal;font-weight:normal;font-size:medium;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0"
id="path4579"
d="m 38.478433,454.34466 2.557622,11.39543 -2.682847,1.56354 -2.557622,-11.39545 2.682847,-1.56352 z" />
<path
style="fill:#838d96;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 119.43841,465.4441 5.37199,3.74229 1.87114,-8.26926 z"
id="path4581"
inkscape:connector-curvature="0" />
<path
style="fill:#838d96;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 99.218,376.23286 -2.29366,6.51884 6.33774,-3.25942 z"
id="path4583"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 370.13999,299.12951 c -0.43414,-0.004 -0.88599,0.004 -1.35658,0.0243 -114.61991,18.29248 -262.7613,57.8235 -362.6396132,162.97056 l -0.2413441,18.34924 -23.0804397,18.75305 c 0.295394,0.96107 0.592605,1.91666 0.892085,2.8672 L 6.8682998,485.78508 11.938416,498.5815 c 3.022522,1.91455 4.542353,4.99753 13.03731,2.65575 0,0 35.049508,-13.87527 66.878294,-31.62836 25.86269,-14.4254 169.13513,-92.29377 297.45122,-135.68783 3.53168,-1.85807 6.92052,-3.82383 2.89753,-11.3479 l -11.34789,-17.38339 c -1.68431,-3.27833 -4.20267,-6.0005 -10.71489,-6.06024 z m 0.85055,7.18994 c 0.76127,-0.0183 1.49621,0.14715 2.1413,0.68389 2.28062,2.60105 3.95393,5.73706 5.90572,8.6044 2.36789,3.85426 5.13602,7.51375 7.31692,11.43239 -2.00889,0.76437 -4.02217,1.5172 -6.0383,2.2622 -74.38121,26.47497 -146.51346,58.91354 -216.93155,94.53357 -23.7859,11.80119 -47.13201,24.45131 -70.49007,37.07193 -21.87976,12.03816 -44.368571,22.99172 -67.494965,32.39674 -2.178032,0.79387 -4.91012,2.49014 -7.139522,0.97798 -2.462342,-3.62775 -3.065109,-8.18739 -5.10326,-12.05744 -0.191754,-5.72806 0.125694,-11.45575 0.125574,-17.18406 19.24401,-19.99827 40.662677,-37.85032 63.501803,-53.60899 56.88977,-39.14613 121.81546,-65.28367 188.25625,-83.14833 34.45174,-9.02055 69.30996,-16.66573 104.56505,-21.77335 0.46215,-0.10291 0.9283,-0.17995 1.38505,-0.19093 z"
id="path4585" />
<path
style="fill:#202427;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 69.56866,436.43193 c 2.34022,-18.39325 11.0874,-31.41629 35.56456,-46.25604 24.47717,-14.83973 51.02774,-12.17822 62.53237,-11.10613 4.19739,0.39116 6.14741,2.1271 9.65751,8.20888 l 9.65751,18.10785 c -9.72905,-3.24303 -45.31306,0.55135 -63.36379,8.82104 -23.93977,10.96767 -32.63451,13.417 -49.78556,44.90031 z"
id="path4590"
inkscape:connector-curvature="0"
sodipodi:nodetypes="czsccscc" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:19.26995659px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#838d96;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="541.47949"
y="544.12469"
id="text4751"
sodipodi:linespacing="125%"
transform="matrix(0.81792144,-0.30782972,0,1.2226113,0,0)"><tspan
sodipodi:role="line"
id="tspan4753"
x="541.47949"
y="544.12469">&gt;</tspan></text>
<path
style="fill:#838d96;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 300.25264,556.5858 10.24335,-10.8622 0.29876,2.6035 -4.75887,5.05767 6.35939,-0.96032 0.72556,2.19804 z"
id="path4759"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:#151d23;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 175.51398,404.23374 c -14.95857,0.0867 -38.3591,3.7713 -51.89715,9.97357 -23.8618,10.93194 -32.57995,13.40905 -49.62048,44.60172 l 112.98222,-53.41898 -0.0122,-0.008 c -2.4337,-0.80767 -6.47543,-1.17722 -11.45245,-1.14838 z"
id="path4761" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Actions"
style="display:inline"
transform="translate(647.36582,205.64997)">
<ellipse
style="display:inline;fill:#898989;fill-opacity:1;stroke:none;stroke-opacity:1"
id="path4352"
cx="-12.758862"
cy="294.12094"
rx="11.98534"
ry="11.985341" />
</g>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="Leaders"
transform="translate(647.36582,205.64997)"
style="display:inline">
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -216.45721,492.54598 -83.92758,424.07234"
id="path4368"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -148.71984,592.67948 0.00785948,357.80752"
id="path4370"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -231.919,376.21442 -92.76289,355.5987"
id="path4372"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -237.07293,234.84948 113.38646,72.89129"
id="path4374"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -177.52663,109.77463 -12.600865,294.57984"
id="path4376"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -30.915721,22.80206 6.62648,247.38865"
id="path4378"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 107.50411,-32.41862 96.45998,252.52009"
id="path4380"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 258.44064,52.98937 162.7248,230.43182"
id="path4382"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 130.32866,662.62568 199.53858,430.69882"
id="path4384"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 192.9121,662.62567 295.9907,385.786"
id="path4386"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
<g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="Controls"
transform="translate(647.36582,205.64997)"
style="display:inline">
<g
id="g4200"
transform="translate(1110.0988,277.2489)">
<text
sodipodi:linespacing="125%"
id="text4572"
y="-44.454391"
x="-571.44623"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4576"
y="-44.454391"
x="-571.44623"
sodipodi:role="line">Resolution0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-571.44623"
y="28.518997"
id="text4580"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-571.44623"
y="28.518997"
id="tspan4582">Resolution1</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4584"
y="101.49239"
x="-571.44623"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4586"
y="101.49239"
x="-571.44623"
sodipodi:role="line">Resolution2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-571.44623"
y="174.46584"
id="text4588"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-571.44623"
y="174.46584"
id="tspan4590">Resolution3</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4592"
y="247.43925"
x="-571.44623"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4594"
y="247.43925"
x="-571.44623"
sodipodi:role="line">Resolution4</tspan></text>
</g>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-opacity:1"
id="rect4600"
width="323.85489"
height="410.20847"
x="494.82077"
y="158.72498"
rx="10.321068"
ry="10.321068" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-629.95673"
y="789.13135"
id="text4211"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4213"
x="-629.95673"
y="789.13135">Current Profile</tspan></text>
<g
id="g4230"
transform="translate(-73.440271,-63.319721)">
<text
sodipodi:linespacing="125%"
id="text4473"
y="547.70569"
x="-157.20392"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4217"
y="547.70569"
x="-157.20392"
sodipodi:role="line">Button0</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-157.20392"
y="591.14594"
id="text4212"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-157.20392"
y="591.14594"
id="tspan4214">Button12</tspan></text>
</g>
<g
id="g4224"
transform="translate(112.10123,30.187304)">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="6.9855804"
y="647.10291"
id="text4216"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="6.9855804"
y="647.10291"
id="tspan4218">Button3</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4220"
y="690.54315"
x="6.9855804"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4222"
y="690.54315"
x="6.9855781"
sodipodi:role="line">Button15</tspan></text>
</g>
<g
id="g4250"
transform="translate(14.912823,34.604968)">
<text
sodipodi:linespacing="125%"
id="text4238"
y="642.68524"
x="195.12758"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4240"
y="642.68524"
x="195.12758"
sodipodi:role="line">Button4</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="195.12758"
y="686.12549"
id="text4242"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="195.12758"
y="686.12549"
id="tspan4244">Button16</tspan></text>
</g>
<g
id="g4256"
transform="translate(86.33157,-599.32852)">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="195.12758"
y="642.68524"
id="text4258"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="195.12758"
y="642.68524"
id="tspan4260">Button10</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4262"
y="686.12549"
x="195.12758"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4264"
y="686.12549"
x="195.12758"
sodipodi:role="line">Button22</tspan></text>
</g>
<g
transform="translate(-76.385377,-689.15417)"
id="g4266">
<text
sodipodi:linespacing="125%"
id="text4268"
y="642.68524"
x="195.12758"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4270"
y="642.68524"
x="195.12758"
sodipodi:role="line">Button9</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="195.12758"
y="686.12549"
id="text4272"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="195.12758"
y="686.12549"
id="tspan4274">Button21</tspan></text>
</g>
<g
transform="translate(0.18730648,35.341239)"
id="g4276">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-157.20392"
y="547.70569"
id="text4278"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-157.20392"
y="547.70569"
id="tspan4280">Button5</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4282"
y="591.14594"
x="-157.20392"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4284"
y="591.14594"
x="-157.20392"
sodipodi:role="line">Button17</tspan></text>
</g>
<g
id="g4286"
transform="translate(-73.440271,-166.39834)">
<text
sodipodi:linespacing="125%"
id="text4288"
y="547.70569"
x="-157.20392"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4290"
y="547.70569"
x="-157.20392"
sodipodi:role="line">Button11</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-157.20392"
y="591.14594"
id="text4292"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-157.20392"
y="591.14594"
id="tspan4294">Button23</tspan></text>
</g>
<g
transform="translate(-86.693235,-300.40054)"
id="g4338">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-157.20392"
y="547.70569"
id="text4340"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-157.20392"
y="547.70569"
id="tspan4342">Button1</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4344"
y="591.14594"
x="-157.20392"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4346"
y="591.14594"
x="-157.20392"
sodipodi:role="line">Button13</tspan></text>
</g>
<g
id="g4348"
transform="translate(-31.472552,-438.82039)">
<text
sodipodi:linespacing="125%"
id="text4350"
y="547.70569"
x="-157.20392"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4352"
y="547.70569"
x="-157.20392"
sodipodi:role="line">Button2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-157.20392"
y="591.14594"
id="text4354"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-157.20392"
y="591.14594"
id="tspan4356">Button14</tspan></text>
</g>
<g
transform="translate(115.04633,-541.16273)"
id="g4358">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-157.20392"
y="547.70569"
id="text4360"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-157.20392"
y="547.70569"
id="tspan4362">Button6</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4364"
y="591.14594"
x="-157.20392"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40.00000381px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4366"
y="591.14594"
x="-157.20392"
sodipodi:role="line">Button18</tspan></text>
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer6"
inkscape:label="Title"
sodipodi:insensitive="true">
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="733.21844"
y="73.834145"
id="text4223"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4225"
x="733.21844"
y="73.834145"
style="font-size:70.00000763px;text-align:center;text-anchor:middle">Roccat Kone XTD</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0"?> <!--*-nxml-*-->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="org.freedesktop.ratbag1"/>
<allow send_destination="org.freedesktop.ratbag1"/>
<allow receive_sender="org.freedesktop.ratbag1"/>
</policy>
<!-- allow everyone to talk to the daemon -->
<policy context="default">
<allow send_destination="org.freedesktop.ratbag1"/>
<allow receive_sender="org.freedesktop.ratbag1"/>
</policy>
</busconfig>

View File

@@ -0,0 +1,5 @@
[D-BUS Service]
Name=org.freedesktop.ratbag1
Exec=@bindir@/ratbagd
User=root
SystemdService=dbus-org.freedesktop.ratbag1.service

View File

@@ -0,0 +1,26 @@
<?xml version="1.0"?> <!--*-nxml-*-->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<!--
WARNING: this file is for testing of libratbag during development.
If you don't know why this file is in your dbus configuration
folder, remove it.
-->
<busconfig>
<policy user="root">
<allow own="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow send_destination="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow receive_sender="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
</policy>
<!-- allow everyone to talk to the daemon -->
<policy context="default">
<allow send_destination="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow receive_sender="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
</policy>
</busconfig>

View File

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

View File

@@ -1,26 +0,0 @@
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
INPUT = @top_srcdir@/src/libratbag.h \
@top_srcdir@/README.txt
GENERATE_HTML = YES
HTML_TIMESTAMP = YES
USE_MATHJAX = YES
GENERATE_LATEX = NO
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = LIBRATBAG_ATTRIBUTE_PRINTF(f, \
a)= \
LIBRATBAG_ATTRIBUTE_DEPRECATED
HTML_HEADER = @top_srcdir@/doc/style/header.html
HTML_FOOTER = @top_srcdir@/doc/style/footer.html
HTML_EXTRA_STYLESHEET = @top_srcdir@/doc/style/customdoxygen.css \
@top_srcdir@/doc/style/bootstrap.css

View File

@@ -1,229 +0,0 @@
These licenses apply to the doxygen documentation HTML style only. They do
not apply or affect libratbag itself.
Apache: https://github.com/Velron/doxygen-bootstrapped/
MIT: https://bootswatch.com/paper/bootstrap.css
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
The MIT License (MIT)
Copyright (c) 2011-2015 Twitter, 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 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.

7500
doc/style/bootstrap.css vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,255 +0,0 @@
h1, .h1, h2, .h2, h3, .h3{
font-weight: 200 !important;
}
#navrow1, #navrow2, #navrow3, #navrow4, #navrow5{
border-bottom: 1px solid #EEEEEE;
}
.adjust-right {
margin-left: 30px !important;
font-size: 1.15em !important;
}
.navbar{
border: 0px solid #222 !important;
}
/* Sticky footer styles
-------------------------------------------------- */
html,
body {
height: 100%;
/* The html and body elements cannot have any padding or margin. */
}
/* Wrapper for page content to push down footer */
#wrap {
min-height: 100%;
height: auto;
/* Negative indent footer by its height */
margin: 0 auto -60px;
/* Pad bottom by footer height */
padding: 0 0 60px;
}
/* Set the fixed height of the footer here */
#footer {
font-size: 0.9em;
padding: 8px 0px;
background-color: #f5f5f5;
}
.footer-row {
line-height: 44px;
}
#footer > .container {
padding-left: 15px;
padding-right: 15px;
}
.footer-follow-icon {
margin-left: 3px;
text-decoration: none !important;
}
.footer-follow-icon img {
width: 20px;
}
.footer-link {
padding-top: 5px;
display: inline-block;
color: #999999;
text-decoration: none;
}
.footer-copyright {
text-align: center;
}
@media (min-width: 992px) {
.footer-row {
text-align: left;
}
.footer-icons {
text-align: right;
}
}
@media (max-width: 991px) {
.footer-row {
text-align: center;
}
.footer-icons {
text-align: center;
}
}
/* DOXYGEN Code Styles
----------------------------------- */
a.qindex {
font-weight: bold;
}
a.qindexHL {
font-weight: bold;
background-color: #9CAFD4;
color: #ffffff;
border: 1px double #869DCA;
}
.contents a.qindexHL:visited {
color: #ffffff;
}
a.code, a.code:visited, a.line, a.line:visited {
color: #4665A2;
}
a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited {
color: #4665A2;
}
/* @end */
dl.el {
margin-left: -1cm;
}
pre.fragment {
border: 1px solid #C4CFE5;
background-color: #FBFCFD;
padding: 4px 6px;
margin: 4px 8px 4px 2px;
overflow: auto;
word-wrap: break-word;
font-size: 9pt;
line-height: 125%;
font-family: monospace, fixed;
font-size: 105%;
}
div.fragment {
padding: 4px 6px;
margin: 4px 8px 4px 2px;
border: 1px solid #C4CFE5;
}
div.line {
font-family: monospace, fixed;
font-size: 13px;
min-height: 13px;
line-height: 1.0;
text-wrap: unrestricted;
white-space: -moz-pre-wrap; /* Moz */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
white-space: pre-wrap; /* CSS3 */
word-wrap: break-word; /* IE 5.5+ */
text-indent: -53px;
padding-left: 53px;
padding-bottom: 0px;
margin: 0px;
-webkit-transition-property: background-color, box-shadow;
-webkit-transition-duration: 0.5s;
-moz-transition-property: background-color, box-shadow;
-moz-transition-duration: 0.5s;
-ms-transition-property: background-color, box-shadow;
-ms-transition-duration: 0.5s;
-o-transition-property: background-color, box-shadow;
-o-transition-duration: 0.5s;
transition-property: background-color, box-shadow;
transition-duration: 0.5s;
}
div.line.glow {
background-color: cyan;
box-shadow: 0 0 10px cyan;
}
span.lineno {
padding-right: 4px;
text-align: right;
border-right: 2px solid #0F0;
background-color: #E8E8E8;
white-space: pre;
}
span.lineno a {
background-color: #D8D8D8;
}
span.lineno a:hover {
background-color: #C8C8C8;
}
div.groupHeader {
margin-left: 16px;
margin-top: 12px;
font-weight: bold;
}
div.groupText {
margin-left: 16px;
font-style: italic;
}
/* @group Code Colorization */
span.keyword {
color: #008000
}
span.keywordtype {
color: #604020
}
span.keywordflow {
color: #e08000
}
span.comment {
color: #800000
}
span.preprocessor {
color: #806020
}
span.stringliteral {
color: #002080
}
span.charliteral {
color: #008080
}
span.vhdldigit {
color: #ff00ff
}
span.vhdlchar {
color: #000000
}
span.vhdlkeyword {
color: #700070
}
span.vhdllogic {
color: #ff0000
}
blockquote {
background-color: #F7F8FB;
border-left: 2px solid #9CAFD4;
margin: 0 24px 0 4px;
padding: 0 12px 0 16px;
}

View File

@@ -1,26 +0,0 @@
<!-- HTML footer for doxygen 1.8.8-->
<!-- start footer part -->
<!--BEGIN GENERATE_TREEVIEW-->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
$navpath
<li class="footer">$generatedby
<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
</ul>
</div>
<!--END GENERATE_TREEVIEW-->
</div>
</div>
</div>
</div>
</div>
<!--BEGIN !GENERATE_TREEVIEW-->
<hr class="footer"/><address class="footer"><small>
$generatedby &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/>
</a> $doxygenversion
</small></address>
<!--END !GENERATE_TREEVIEW-->
</body>
</html>

View File

@@ -1,42 +0,0 @@
<!-- HTML header for doxygen 1.8.8-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- For Mobile Devices -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<!--<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>-->
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
<link href="bootstrap.css" rel="stylesheet" type="text/css" />
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="doxy-boot.js"></script>
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand">$projectname $projectnumber</a>
</div>
</div>
</nav>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div class="content" id="content">
<div class="container">
<div class="row">
<div class="col-sm-12 panel panel-default" style="padding-bottom: 15px;">
<div style="margin-bottom: 15px;">
<!-- end header part -->

View File

@@ -69,61 +69,116 @@ mouse:usb:v046dpc041:name:*:
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 over USB
# G502 Proteus Core over USB
mouse:usb:v046dpc07d:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g502.svg
# G502 Proteus Spectrum over USB
mouse:usb:v046dpc332:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g502.svg
# G700 over USB
# 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:*:
@@ -143,6 +198,26 @@ mouse:usb:v046dp400a:name:*:
# G303 over USB
mouse:usb:v046dpc080:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g303.svg
# Wireless Touchpad (Unifying)
mouse:usb:v046dp4011:name:*:
RATBAG_DRIVER=hidpp20
# G602 over wireless USB
mouse:usb:v046dpc537:name:*:
RATBAG_HIDPP20_INDEX=1
# G900 over USB
mouse:usb:v046dpc081:name:*:
RATBAG_SVG=logitech-g900.svg
# G900, G403 over wireless USB
mouse:usb:v046dpc539:name:*:
RATBAG_DRIVER=hidpp20
RATBAG_SVG=logitech-g900.svg
RATBAG_HIDPP20_INDEX=1
##########################################
# Roccat
@@ -151,3 +226,12 @@ mouse:usb:v046dpc080:name:*:
mouse:usb:v1e7dp2e22:name:*:
RATBAG_DRIVER=roccat
RATBAG_SVG=roccat-kone-xtd.svg
##########################################
# G.Skill
##########################################
# MX-780
mouse:usb:v28dap3101:name:*:
RATBAG_DRIVER=gskill

View File

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

485
meson.build Normal file
View File

@@ -0,0 +1,485 @@
project('libratbag', 'c', 'cpp',
version : '0.9.900',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ])
libratbag_version = meson.project_version().split('.')
git = find_program('git')
r = run_command('tools/ratbag-git-sha.sh')
ratbagd_sha = r.stdout().strip()
# We use libtool-version numbers because it's easier to understand.
# Before making a release, the libratbag_so_* and liblur_so_*
# numbers should be modified. The components are of the form C:R:A.
# a) If binary compatibility has been broken (eg removed or changed interfaces)
# change to C+1:0:0.
# b) If interfaces have been changed or added, but binary compatibility has
# been preserved, change to C+1:0:A+1
# c) If the interface is the same as the previous version, change to C:R+1:A
liblur_so_c=3
liblur_so_r=3
liblur_so_a=0
# convert to sonames
liblur_so_version = '@0@.@1@.@2@'.format((liblur_so_c-liblur_so_a),
liblur_so_a, liblur_so_r)
# Compiler setup
cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
cppflags = ['-Wno-unused-parameter', '-fvisibility=hidden']
cflags = cppflags + ['-Wmissing-prototypes', '-Wstrict-prototypes']
add_project_arguments(cflags, language: 'c')
add_project_arguments(cppflags, language: 'cpp')
# Initialize config.h, to be added to in the various options below, config.h
# is generated at the end of this file
config_h = configuration_data()
config_h.set('_GNU_SOURCE', '1')
libratbag_data_dir = join_paths(get_option('prefix'),
get_option('datadir'),
'libratbag')
config_h.set_quoted('LIBRATBAG_DATA_DIR', libratbag_data_dir)
# dependencies
pkgconfig = import('pkgconfig')
dep_udev = dependency('libudev')
dep_libevdev = dependency('libevdev')
dep_libsystemd = dependency('libsystemd', version : '>=227')
dep_systemd = dependency('systemd')
dep_lm = cc.find_library('m')
#### libutil.a ####
src_libutil = [
'src/libratbag-util.c',
'src/libratbag-util.h'
]
deps_libutil = [
dep_udev,
]
lib_libutil = static_library('util',
src_libutil,
dependencies : deps_libutil
)
dep_libutil = declare_dependency(link_with: lib_libutil)
### libhidpp.a ####
src_libhidpp = [
'src/hidpp-generic.h',
'src/hidpp-generic.c',
'src/hidpp10.h',
'src/hidpp10.c',
'src/hidpp20.h',
'src/hidpp20.c',
'src/usb-ids.h'
]
deps_libhidpp = [ dep_lm ]
lib_libhidpp = static_library('hidpp',
src_libhidpp,
dependencies : deps_libhidpp)
dep_libhidpp = declare_dependency(link_with: lib_libhidpp)
### liblur.a ####
install_headers('src/liblur.h')
src_liblur = [
'src/liblur.c',
'src/liblur.h'
]
deps_liblur = [
dep_libutil,
dep_libhidpp,
]
lur_mapfile = 'src/liblur.sym'
lur_version_flag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), lur_mapfile)
lib_liblur = shared_library('lur',
src_liblur,
include_directories : include_directories('.'),
dependencies : deps_liblur,
version : liblur_so_version,
link_args : lur_version_flag,
link_depends : lur_mapfile,
install : true,
)
dep_liblur = declare_dependency(link_with: lib_liblur)
pkgconfig.generate (
filebase: 'liblur',
name: 'Liblur',
description: 'Logitech Unifying Receiver configuration library',
version: meson.project_version(),
libraries: lib_liblur
)
#### libratbag.so ####
src_libratbag = [
'src/libratbag.h',
'src/driver-etekcity.c',
'src/driver-hidpp20.c',
'src/driver-hidpp10.c',
'src/driver-logitech-g300.c',
'src/driver-roccat.c',
'src/driver-gskill.c',
'src/driver-test.c',
'src/libratbag.c',
'src/libratbag.h',
'src/libratbag-hidraw.c',
'src/libratbag-hidraw.h',
'src/libratbag-private.h',
'src/libratbag-test.c',
'src/libratbag-test.h',
'src/usb-ids.h'
]
deps_libratbag = [
dep_udev,
dep_libevdev,
dep_libutil,
dep_libhidpp,
]
libratbag_version_h_config = configuration_data()
libratbag_version_h_config.set_quoted('LIBRATBAG_VERSION', meson.project_version())
libratbag_version_h = configure_file(
output : 'libratbag-version.h',
configuration : libratbag_version_h_config,
install: false,
)
lib_libratbag = static_library('ratbag',
src_libratbag,
include_directories : include_directories('.'),
dependencies : deps_libratbag,
)
dep_libratbag = declare_dependency(
link_with : lib_libratbag,
dependencies : deps_libratbag
)
#### libshared.a ####
src_libshared = [
'tools/shared.c',
'tools/shared.h'
]
deps_libshared = [
dep_udev,
dep_libevdev,
]
lib_libshared = static_library('shared',
src_libshared,
dependencies : deps_libshared,
include_directories : include_directories('src')
)
dep_libshared = declare_dependency(link_with: lib_libshared)
#### ratbag-command ####
src_ratbag_command = [ 'tools/ratbag-command.c' ]
deps_ratbag_command = [
dep_libshared,
dep_libratbag,
dep_libevdev
]
executable('ratbag-command',
src_ratbag_command,
dependencies : deps_ratbag_command,
include_directories : include_directories('src'),
install : 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',
src_hidpp10_dump_page,
dependencies : [ dep_libhidpp ],
include_directories : include_directories('src'),
install : false,
)
#### hidpp20-dump-page ####
src_hidpp20_dump_page = [ 'tools/hidpp20-dump-page.c' ]
executable('hidpp20-dump-page',
src_hidpp20_dump_page,
dependencies : [ dep_libhidpp ],
include_directories : include_directories('src'),
install : false,
)
#### lur-command ####
src_lur_command = [ 'tools/lur-command.c' ]
executable('lur-command',
src_lur_command,
dependencies : [ dep_libshared, dep_liblur ],
include_directories : include_directories('src'),
install : true,
)
man_lur_command = configure_file (
input: 'tools/lur-command.man',
output: 'lur-command.1',
configuration: man_config,
install : true,
install_dir : join_paths(get_option('mandir'), 'man1')
)
#### hwdb ####
udev_dir = get_option('udev-dir')
if udev_dir == ''
udev_dir = '@0@/lib/udev'.format(get_option('prefix'))
endif
udev_rules_dir = '@0@/rules.d'.format(udev_dir)
udev_hwdb_dir = '@0@/hwdb.d'.format(udev_dir)
install_data('hwdb/70-libratbag-mouse.rules',
install_dir : udev_rules_dir)
install_data('hwdb/70-libratbag-mouse.hwdb',
install_dir : udev_hwdb_dir)
#### svg files ####
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 = [
'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/roccat-kone-xtd.svg',
]
install_data(gnome_svg_files,
install_dir : join_paths(get_option('datadir'), 'libratbag', 'gnome'))
#### 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'],
dependencies : [ dep_libratbag, dep_check ],
include_directories : include_directories('src'),
install : false)
test_device = executable('test-device',
['test/test-device.c'],
dependencies : [ dep_libratbag, dep_check ],
include_directories : include_directories('src'),
install : false)
test_iconv_helper = executable('test-iconv-helper',
['test/test-iconv-helper.c'],
dependencies : [ dep_libratbag,
dep_check,
dep_libutil],
include_directories : include_directories('src'),
install : false)
test('test-context', test_context, env : env_test)
test('test-device', test_device, env : env_test)
test('test-iconv-helper', test_iconv_helper, env : env_test)
valgrind = find_program('valgrind')
valgrind_suppressions_file = join_paths(meson.source_root(), 'test', 'valgrind.suppressions')
add_test_setup('valgrind',
exe_wrapper : [
valgrind,
'--leak-check=full',
'--quiet',
'--error-exitcode=3',
'--suppressions=' + valgrind_suppressions_file ],
env : env_test,
timeout_multiplier: 5)
executable('test-build-cxx',
['test/build-cxx.cc'],
dependencies : [ dep_libratbag ],
include_directories : include_directories('src'),
cpp_args : ['-Wall', '-Wextra', '-Wno-unused-parameter'],
install : false)
executable('test-build-linker',
['test/build-pedantic.c'],
dependencies : [ dep_libratbag ],
include_directories : include_directories('src'),
install : false)
executable('test-build-gnuc90',
['test/build-pedantic.c'],
dependencies : [ dep_libratbag ],
include_directories : include_directories('src'),
c_args : ['-std=gnu90', '-Werror'],
install : false)
executable('test-build-pedantic',
['test/build-pedantic.c'],
dependencies : [ dep_libratbag ],
include_directories : include_directories('src'),
c_args : ['-std=c99', '-pedantic', '-Werror'],
install : false)
endif
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',
'src/shared-rbtree.h',
'src/shared-rbtree.c',
'ratbagd/ratbagd.h',
'ratbagd/ratbagd.c',
'ratbagd/ratbagd-led.c',
'ratbagd/ratbagd-button.c',
'ratbagd/ratbagd-device.c',
'ratbagd/ratbagd-profile.c',
'ratbagd/ratbagd-resolution.c',
'src/libratbag-util.h',
'src/libratbag-util.c',
]
deps_ratbagd = [
dep_udev,
dep_libsystemd,
dep_libratbag,
]
executable('ratbagd',
src_ratbagd,
dependencies : deps_ratbagd,
include_directories : include_directories('src'),
install : true,
)
install_man('ratbagd/ratbagd.8')
#### ratbagd_devel ####
executable('ratbagd.devel',
src_ratbagd,
dependencies : deps_ratbagd,
include_directories : include_directories('src'),
install : false,
c_args : '-DRATBAG_DBUS_INTERFACE="ratbag_devel1_@0@"'.format(ratbagd_sha),
)
config_ratbagd_devel = configuration_data()
config_ratbagd_devel.set('ratbagd_sha', ratbagd_sha)
configure_file(input : 'dbus/org.freedesktop.ratbag_devel1.conf.in',
output : 'org.freedesktop.ratbag_devel1.conf',
configuration : config_ratbagd_devel)
#### unit file ####
unitdir = get_option('systemd-unit-dir')
if unitdir == ''
libdir = get_option('libdir')
default_unitdir = dep_systemd.get_pkgconfig_variable('systemdsystemunitdir')
# Fedora uses lib64 but systemd is in lib. Hack around this so it
# works out of the box.
intended_unitdir = join_paths(get_option('prefix'), get_option('libdir'), 'systemd')
if get_option('prefix') == '/usr' and intended_unitdir != default_unitdir
message('
systemd unitdir libdir mismatch detected, changing unitdir to
@0@
or specify with
mesonconf -Dsystemd-unit-dir=<path>
See https://github.com/libratbag/libratbag/issues/188
'.format(default_unitdir))
unitdir = default_unitdir
else
unitdir = intended_unitdir
endif
endif
config_bindir = configuration_data()
config_bindir.set('bindir', join_paths(get_option('prefix'), get_option('bindir')))
configure_file(input : 'ratbagd/ratbagd.service.in',
output : 'ratbagd.service',
configuration : config_bindir,
install_dir : unitdir)
dbusdir = get_option('dbus-root-dir')
if dbusdir == ''
dbusdir = join_paths(get_option('datadir'), 'dbus-1')
endif
configure_file(input : 'dbus/org.freedesktop.ratbag1.service.in',
output : 'org.freedesktop.ratbag1.service',
configuration : config_bindir,
install_dir : join_paths(dbusdir, 'system-services'))
install_data('dbus/org.freedesktop.ratbag1.conf',
install_dir : join_paths(dbusdir, 'system.d'))
#### tools ####
python = import('python3')
py3 = python.find_python()
custom_target('ratbagctl',
output : 'ratbagctl',
input : ['tools/ratbagctl.in', 'tools/ratbagd.py'],
build_by_default : true,
command : [py3, join_paths(meson.source_root(), 'tools', 'merge_ratbagd.py'),
'@INPUT@',
'--output', '@OUTPUT@',
'--version', meson.project_version()],
install: true,
install_dir: get_option('bindir'))
install_man('tools/ratbagctl.1')
config_ratbagctl_devel = configuration_data()
config_ratbagctl_devel.set('ratbagd_sha', ratbagd_sha)
config_ratbagctl_devel.set('MESON_BUILD_ROOT', meson.build_root())
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)
ratbagctl_test = find_program(join_paths(meson.build_root(), 'ratbagctl.test'))
test('ratbagctl-test', ratbagctl_test, args: ['-v'])
configure_file(input : 'tools/toolbox.py',
output : 'toolbox.py',
configuration : config_ratbagctl_devel)
#### output files ####
configure_file(output: 'config.h', install: false, configuration: config_h)

19
meson_options.txt Normal file
View File

@@ -0,0 +1,19 @@
option('udev-dir',
type: 'string',
default: '',
description: 'udev base directory [default=$prefix/lib/udev]')
option('enable-tests',
type: 'boolean',
value: true,
description: 'Build the tests (default=yes)')
option('systemd-unit-dir',
type : 'string',
default : '',
description : 'systemd unit directory [default=$libdir/systemd/system]')
option('dbus-root-dir',
type : 'string',
default : '',
description : 'dbus service directory [default=$datadir/dbus-1]')

12
ratbagd/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,14 @@
<?xml version="1.0"?> <!--*-nxml-*-->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="org.freedesktop.ratbag1"/>
<allow send_destination="org.freedesktop.ratbag1"/>
<allow receive_sender="org.freedesktop.ratbag1"/>
</policy>
</busconfig>

513
ratbagd/ratbagd-button.c Normal file
View File

@@ -0,0 +1,513 @@
/***
This file is part of ratbagd.
Copyright 2016 Red Hat, Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
***/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <libratbag.h>
#include <libudev.h>
#include <stdio.h>
#include <stdlib.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include <linux/input.h>
#include "ratbagd.h"
#include "shared-macro.h"
#include "libratbag-util.h"
struct ratbagd_button {
struct ratbag_button *lib_button;
unsigned int index;
char *path;
};
static int ratbagd_button_get_type(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;
enum ratbag_button_type t;
t = ratbag_button_get_type(button->lib_button);
return sd_bus_message_append(reply, "u", t);
}
static int ratbagd_button_get_button(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 b;
b = ratbag_button_get_button(button->lib_button);
return sd_bus_message_append(reply, "u", b);
}
static int ratbagd_button_set_button(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 map;
int r;
r = sd_bus_message_read(m, "u", &map);
if (r < 0)
return r;
if (map == 0 || map > 30)
return 0;
r = ratbag_button_set_button(button->lib_button, map);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"ButtonMapping",
NULL);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"ActionType",
NULL);
}
return r;
}
static int ratbagd_button_get_special(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;
enum ratbag_button_action_special special;
special = ratbag_button_get_special(button->lib_button);
return sd_bus_message_append(reply, "u", special);
}
static int ratbagd_button_set_special(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;
enum ratbag_button_action_special special;
int r;
r = sd_bus_message_read(m, "u", &special);
if (r < 0)
return r;
r = ratbag_button_set_special(button->lib_button, special);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"SpecialMapping",
NULL);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"ActionType",
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;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbag_button_macro *, ratbag_button_macro_unref);
static int ratbagd_button_get_macro(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;
_cleanup_(ratbag_button_macro_unrefp) struct ratbag_button_macro *macro = NULL;
int r;
unsigned int idx;
r = sd_bus_message_open_container(reply, 'a', "(uu)");
if (r < 0)
return r;
macro = ratbag_button_get_macro(button->lib_button);
if (!macro)
goto out;
for (idx = 0; idx < ratbag_button_macro_get_num_events(macro); idx++) {
enum ratbag_macro_event_type type;
int value;
type = ratbag_button_macro_get_event_type(macro, idx);
switch (type) {
case RATBAG_MACRO_EVENT_INVALID:
abort();
break;
case RATBAG_MACRO_EVENT_NONE:
goto out;
case RATBAG_MACRO_EVENT_KEY_PRESSED:
case RATBAG_MACRO_EVENT_KEY_RELEASED:
value = ratbag_button_macro_get_event_key(macro, idx);
break;
case RATBAG_MACRO_EVENT_WAIT:
value = ratbag_button_macro_get_event_timeout(macro, idx);
break;
}
r = sd_bus_message_append(reply, "(uu)", type, value);
if (r < 0)
return r;
}
out:
return sd_bus_message_close_container(reply);
}
static int ratbagd_button_set_macro(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 type, value;
int r, idx = 0;
_cleanup_(ratbag_button_macro_unrefp) struct ratbag_button_macro *macro = NULL;
r = sd_bus_message_enter_container(m, 'a', "(uu)");
if (r < 0)
return r;
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)
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
r = ratbag_button_set_macro(button->lib_button, macro);
if (r < 0)
return r;
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"Macro",
NULL);
sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"ActionType",
NULL);
}
return r;
}
static int ratbagd_button_get_action_type(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;
enum ratbag_button_action_type type;
type = ratbag_button_get_action_type(button->lib_button);
return sd_bus_message_append(reply, "u", type);
}
static int ratbagd_button_get_action_types(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;
int r;
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;
r = sd_bus_message_open_container(reply, 'a', "u");
if (r < 0)
return r;
ARRAY_FOR_EACH(types, t) {
if (!ratbag_button_has_action_type(button->lib_button, *t))
continue;
r = sd_bus_message_append(reply, "u", *t);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int ratbagd_button_disable(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_button *button = userdata;
int r;
r = sd_bus_message_read(m, "");
if (r < 0)
return r;
r = ratbag_button_disable(button->lib_button);
return sd_bus_reply_method_return(m, "u", r);
}
const sd_bus_vtable ratbagd_button_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_button, index), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Type", "u", ratbagd_button_get_type, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_WRITABLE_PROPERTY("ButtonMapping", "u",
ratbagd_button_get_button,
ratbagd_button_set_button,
0, SD_BUS_VTABLE_UNPRIVILEGED | SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("SpecialMapping", "u",
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,
0, SD_BUS_VTABLE_UNPRIVILEGED | SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ActionType", "u", ratbagd_button_get_action_type, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ActionTypes", "au", ratbagd_button_get_action_types, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD("Disable", "", "u", ratbagd_button_disable, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
int ratbagd_button_new(struct ratbagd_button **out,
struct ratbagd_device *device,
struct ratbagd_profile *profile,
struct ratbag_button *lib_button,
unsigned int index)
{
_cleanup_(ratbagd_button_freep) struct ratbagd_button *button = NULL;
char profile_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1],
button_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
int r;
assert(out);
assert(lib_button);
button = zalloc(sizeof(*button));
button->lib_button = lib_button;
button->index = index;
sprintf(profile_buffer, "p%u", ratbagd_profile_get_index(profile));
sprintf(button_buffer, "b%u", index);
r = sd_bus_path_encode_many(&button->path,
RATBAGD_OBJ_ROOT "/button/%/%/%",
ratbagd_device_get_name(device),
profile_buffer,
button_buffer);
if (r < 0)
return r;
*out = button;
button = NULL;
return 0;
}
const char *ratbagd_button_get_path(struct ratbagd_button *button)
{
assert(button);
return button->path;
}
struct ratbagd_button *ratbagd_button_free(struct ratbagd_button *button)
{
if (!button)
return NULL;
button->path = mfree(button->path);
button->lib_button = ratbag_button_unref(button->lib_button);
return mfree(button);
}

548
ratbagd/ratbagd-device.c Normal file
View File

@@ -0,0 +1,548 @@
/***
This file is part of ratbagd.
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
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 <limits.h>
#include <libratbag.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include "ratbagd.h"
#include "shared-macro.h"
#include "shared-rbtree.h"
#include "libratbag-util.h"
struct ratbagd_device {
struct ratbagd *ctx;
RBNode node;
char *name;
char *path;
struct ratbag_device *lib_device;
sd_bus_slot *profile_vtable_slot;
sd_bus_slot *profile_enum_slot;
unsigned int n_profiles;
struct ratbagd_profile **profiles;
};
#define ratbagd_device_from_node(_ptr) \
rbnode_of((_ptr), struct ratbagd_device, node)
static int ratbagd_device_find_profile(sd_bus *bus,
const char *path,
const char *interface,
void *userdata,
void **found,
sd_bus_error *error)
{
_cleanup_(freep) char *name = NULL;
struct ratbagd_device *device = userdata;
unsigned int index = 0;
int r;
r = sd_bus_path_decode_many(path,
RATBAGD_OBJ_ROOT "/profile/%/p%",
NULL,
&name);
if (r <= 0)
return r;
r = safe_atou(name, &index);
if (r < 0)
return 0;
if (index >= device->n_profiles || !device->profiles[index])
return 0;
*found = device->profiles[index];
return 1;
}
static int ratbagd_device_list_profiles(sd_bus *bus,
const char *path,
void *userdata,
char ***paths,
sd_bus_error *error)
{
struct ratbagd_device *device = userdata;
struct ratbagd_profile *profile;
char **profiles;
unsigned int i;
profiles = zalloc((device->n_profiles + 1) * sizeof(char *));
for (i = 0; i < device->n_profiles; ++i) {
profile = device->profiles[i];
if (!profile)
continue;
profiles[i] = strdup_safe(ratbagd_profile_get_path(profile));
}
profiles[i] = NULL;
*paths = profiles;
return 1;
}
static int ratbagd_device_get_device_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_device *device = userdata;
const char *name;
name = ratbag_device_get_name(device->lib_device);
if (!name) {
log_error("Unable to fetch name for %s\n",
ratbagd_device_get_name(device));
name = "";
}
return sd_bus_message_append(reply, "s", name);
}
static int ratbagd_device_get_svg(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_device *device = userdata;
const char *svg;
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));
svg = "";
}
return sd_bus_message_append(reply, "s", svg);
}
static int ratbagd_device_get_theme_svg(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_device *device = userdata;
char svg_path[PATH_MAX] = {0};
const char *theme;
const char *svg;
int r;
r = sd_bus_message_read(m, "s", &theme);
if (r < 0)
return r;
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;
}
sprintf(svg_path, "%s/%s/%s", LIBRATBAG_DATA_DIR, theme, svg);
out:
return sd_bus_reply_method_return(m, "s", svg_path);
}
static int ratbagd_device_get_profiles(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_device *device = userdata;
struct ratbagd_profile *profile;
unsigned int i;
int r;
r = sd_bus_message_open_container(reply, 'a', "o");
if (r < 0)
return r;
for (i = 0; i < device->n_profiles; ++i) {
profile = device->profiles[i];
if (!profile)
continue;
r = sd_bus_message_append(reply,
"o",
ratbagd_profile_get_path(profile));
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int ratbagd_device_commit(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_device *device = userdata;
int r;
r = ratbag_device_commit(device->lib_device);
return sd_bus_reply_method_return(m, "u", r);
}
static int
ratbagd_device_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_device *device = userdata;
struct ratbag_device *lib_device = device->lib_device;
enum ratbag_device_capability cap;
enum ratbag_device_capability caps[] = {
RATBAG_DEVICE_CAP_QUERY_CONFIGURATION,
RATBAG_DEVICE_CAP_RESOLUTION,
RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION,
RATBAG_DEVICE_CAP_PROFILE,
RATBAG_DEVICE_CAP_SWITCHABLE_PROFILE,
RATBAG_DEVICE_CAP_DISABLE_PROFILE,
RATBAG_DEVICE_CAP_DEFAULT_PROFILE,
RATBAG_DEVICE_CAP_BUTTON,
RATBAG_DEVICE_CAP_BUTTON_KEY,
RATBAG_DEVICE_CAP_BUTTON_MACROS,
RATBAG_DEVICE_CAP_LED,
};
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_device_has_capability(lib_device, 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_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("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_VTABLE_END,
};
int ratbagd_device_new(struct ratbagd_device **out,
struct ratbagd *ctx,
const char *name,
struct ratbag_device *lib_device)
{
_cleanup_(ratbagd_device_freep) struct ratbagd_device *device = NULL;
struct ratbag_profile *profile;
unsigned int i;
int r;
assert(out);
assert(ctx);
assert(name);
device = zalloc(sizeof(*device));
device->ctx = ctx;
rbnode_init(&device->node);
device->lib_device = ratbag_device_ref(lib_device);
device->name = strdup_safe(name);
r = sd_bus_path_encode(RATBAGD_OBJ_ROOT "/device",
device->name,
&device->path);
if (r < 0)
return r;
device->n_profiles = ratbag_device_get_num_profiles(device->lib_device);
device->profiles = zalloc(device->n_profiles * sizeof(*device->profiles));
log_verbose("%s: \"%s\", %d profiles\n",
name,
ratbag_device_get_name(lib_device),
device->n_profiles);
for (i = 0; i < device->n_profiles; ++i) {
profile = ratbag_device_get_profile(device->lib_device, i);
if (!profile)
continue;
r = ratbagd_profile_new(&device->profiles[i],
device,
profile,
i);
if (r < 0) {
errno = -r;
log_error("Cannot allocate profile for '%s': %m\n",
device->name);
}
}
*out = device;
device = NULL;
return 0;
}
struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device)
{
unsigned int i;
if (!device)
return NULL;
assert(!ratbagd_device_linked(device));
for (i = 0; i < device->n_profiles; ++i)
device->profiles[i] = ratbagd_profile_free(device->profiles[i]);
device->profiles = mfree(device->profiles);
device->lib_device = ratbag_device_unref(device->lib_device);
device->path = mfree(device->path);
device->name = mfree(device->name);
assert(!device->lib_device); /* ratbag yields !NULL if still pinned */
return mfree(device);
}
const char *ratbagd_device_get_name(struct ratbagd_device *device)
{
assert(device);
return device->name;
}
const char *ratbagd_device_get_path(struct ratbagd_device *device)
{
assert(device);
return device->path;
}
unsigned int ratbagd_device_get_num_buttons(struct ratbagd_device *device)
{
assert(device);
return ratbag_device_get_num_buttons(device->lib_device);
}
unsigned int ratbagd_device_get_num_leds(struct ratbagd_device *device)
{
assert(device);
return ratbag_device_get_num_leds(device->lib_device);
}
bool ratbagd_device_linked(struct ratbagd_device *device)
{
return device && rbnode_linked(&device->node);
}
void ratbagd_device_link(struct ratbagd_device *device)
{
_cleanup_(freep) char *prefix = NULL;
struct ratbagd_device *iter;
RBNode **node, *parent;
int r, v;
unsigned int i;
assert(device);
assert(!ratbagd_device_linked(device));
/* find place to link it to */
parent = NULL;
node = &device->ctx->device_map.root;
while (*node) {
parent = *node;
iter = ratbagd_device_from_node(parent);
v = strcmp(device->name, iter->name);
/* if there's a duplicate, the caller screwed up */
assert(v != 0);
if (v < 0)
node = &parent->left;
else /* if (v > 0) */
node = &parent->right;
}
/* link into context */
rbtree_add(&device->ctx->device_map, parent, node, &device->node);
++device->ctx->n_devices;
/* register profile interfaces */
r = sd_bus_path_encode_many(&prefix,
RATBAGD_OBJ_ROOT "/profile/%",
device->name);
if (r >= 0) {
r = sd_bus_add_fallback_vtable(device->ctx->bus,
&device->profile_vtable_slot,
prefix,
RATBAGD_NAME_ROOT ".Profile",
ratbagd_profile_vtable,
ratbagd_device_find_profile,
device);
if (r >= 0)
r = sd_bus_add_node_enumerator(device->ctx->bus,
&device->profile_enum_slot,
prefix,
ratbagd_device_list_profiles,
device);
}
if (r < 0) {
errno = -r;
log_error("Cannot register profile interfaces for '%s': %m\n",
device->name);
return;
}
for (i = 0; i < device->n_profiles; i++) {
r = ratbagd_profile_register_resolutions(device->ctx->bus,
device,
device->profiles[i]);
if (r < 0) {
log_error("Cannot register resolutions for '%s': %m\n",
device->name);
}
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);
}
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);
}
}
}
void ratbagd_device_unlink(struct ratbagd_device *device)
{
if (!ratbagd_device_linked(device))
return;
device->profile_enum_slot = sd_bus_slot_unref(device->profile_enum_slot);
device->profile_vtable_slot = sd_bus_slot_unref(device->profile_vtable_slot);
/* unlink from context */
--device->ctx->n_devices;
rbtree_remove(&device->ctx->device_map, &device->node);
rbnode_init(&device->node);
}
struct ratbagd_device *ratbagd_device_lookup(struct ratbagd *ctx,
const char *name)
{
struct ratbagd_device *device;
RBNode *node;
int v;
assert(ctx);
assert(name);
node = ctx->device_map.root;
while (node) {
device = ratbagd_device_from_node(node);
v = strcmp(name, device->name);
if (!v)
return device;
else if (v < 0)
node = node->left;
else /* if (v > 0) */
node = node->right;
}
return NULL;
}
struct ratbagd_device *ratbagd_device_first(struct ratbagd *ctx)
{
struct RBNode *node;
assert(ctx);
node = rbtree_first(&ctx->device_map);
return node ? ratbagd_device_from_node(node) : NULL;
}
struct ratbagd_device *ratbagd_device_next(struct ratbagd_device *device)
{
struct RBNode *node;
assert(device);
node = rbnode_next(&device->node);
return node ? ratbagd_device_from_node(node) : NULL;
}
int ratbagd_for_each_profile_signal(sd_bus *bus,
struct ratbagd_device *device,
int (*func)(sd_bus *bus,
struct ratbagd_profile *profile))
{
int rc = 0;
for (size_t i = 0; rc == 0 && i < device->n_profiles; i++)
rc = func(bus, device->profiles[i]);
return rc;
}

316
ratbagd/ratbagd-led.c Normal file
View File

@@ -0,0 +1,316 @@
/***
This file is part of ratbagd.
Copyright 2017 Jente Hidskes <hjdskes@gmail.com>.
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 <libratbag.h>
#include <systemd/sd-bus.h>
#include "ratbagd.h"
#include "shared-macro.h"
#include "libratbag-util.h"
struct ratbagd_led {
struct ratbag_led *lib_led;
unsigned int index;
char *path;
};
static int ratbagd_led_get_mode(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;
mode = ratbag_led_get_mode(led->lib_led);
return sd_bus_message_append(reply, "u", mode);
}
static int ratbagd_led_set_mode(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;
enum ratbag_led_mode mode;
int r;
r = sd_bus_message_read(m, "u", &mode);
if (r < 0)
return r;
r = ratbag_led_set_mode(led->lib_led, mode);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
led->path,
RATBAGD_NAME_ROOT ".Led",
"Mode",
NULL);
}
return r;
}
static int ratbagd_led_get_type(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_type type;
type = ratbag_led_get_type(led->lib_led);
return sd_bus_message_append(reply, "u", type);
}
static int ratbagd_led_get_color(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;
struct ratbag_color c;
c = ratbag_led_get_color(led->lib_led);
return sd_bus_message_append(reply, "(uuu)", c.red, c.green, c.blue);
}
static int ratbagd_led_set_color(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;
struct ratbag_color c;
int r;
r = sd_bus_message_read(m, "(uuu)", &c.red, &c.green, &c.blue);
if (r < 0)
return r;
if (c.red > 255 || c.green > 255 || c.blue > 255)
return RATBAG_ERROR_VALUE;
r = ratbag_led_set_color(led->lib_led, c);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
led->path,
RATBAGD_NAME_ROOT ".Led",
"Color",
NULL);
}
return r;
}
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)
{
struct ratbagd_led *led = userdata;
int rate;
rate = ratbag_led_get_effect_rate(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)
{
struct ratbagd_led *led = userdata;
unsigned int rate;
int r;
r = sd_bus_message_read(m, "u", &rate);
if (r < 0)
return r;
if (rate < 100 || rate > 20000)
return 0;
r = ratbag_led_set_effect_rate(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",
NULL);
}
return r;
}
static int ratbagd_led_get_brightness(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;
unsigned int brightness;
brightness = ratbag_led_get_brightness(led->lib_led);
return sd_bus_message_append(reply, "u", brightness);
}
static int ratbagd_led_set_brightness(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 brightness;
int r;
r = sd_bus_message_read(m, "u", &brightness);
if (r < 0)
return r;
if (brightness > 255)
return 0;
r = ratbag_led_set_brightness(led->lib_led, brightness);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
led->path,
RATBAGD_NAME_ROOT ".Led",
"Brightness",
NULL);
}
return r;
}
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_WRITABLE_PROPERTY("Mode", "u",
ratbagd_led_get_mode,
ratbagd_led_set_mode, 0,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Type", "u", ratbagd_led_get_type, 0, SD_BUS_VTABLE_PROPERTY_CONST),
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_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("Brightness", "u",
ratbagd_led_get_brightness, ratbagd_led_set_brightness, 0,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_VTABLE_END,
};
int ratbagd_led_new(struct ratbagd_led **out,
struct ratbagd_device *device,
struct ratbagd_profile *profile,
struct ratbag_led *lib_led,
unsigned int index)
{
_cleanup_(ratbagd_led_freep) struct ratbagd_led *led = NULL;
char profile_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1],
led_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
int r;
assert(out);
assert(lib_led);
led = zalloc(sizeof(*led));
led->lib_led = lib_led;
led->index = index;
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),
profile_buffer,
led_buffer);
if (r < 0)
return r;
*out = led;
led = NULL;
return 0;
}
const char *ratbagd_led_get_path(struct ratbagd_led *led)
{
assert(led);
return led->path;
}
struct ratbagd_led *ratbagd_led_free(struct ratbagd_led *led)
{
if (!led)
return NULL;
led->path = mfree(led->path);
led->lib_led = ratbag_led_unref(led->lib_led);
return mfree(led);
}

713
ratbagd/ratbagd-profile.c Normal file
View File

@@ -0,0 +1,713 @@
/***
This file is part of ratbagd.
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
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 <libratbag.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include "ratbagd.h"
#include "libratbag-util.h"
#include "shared-macro.h"
#include "libratbag-util.h"
struct ratbagd_profile {
struct ratbagd_device *device;
struct ratbag_profile *lib_profile;
unsigned int index;
char *path;
sd_bus_slot *resolution_vtable_slot;
sd_bus_slot *resolution_enum_slot;
unsigned int n_resolutions;
struct ratbagd_resolution **resolutions;
sd_bus_slot *button_vtable_slot;
sd_bus_slot *button_enum_slot;
unsigned int n_buttons;
struct ratbagd_button **buttons;
sd_bus_slot *led_vtable_slot;
sd_bus_slot *led_enum_slot;
unsigned int n_leds;
struct ratbagd_led **leds;
};
static int ratbagd_profile_find_resolution(sd_bus *bus,
const char *path,
const char *interface,
void *userdata,
void **found,
sd_bus_error *error)
{
_cleanup_(freep) char *name = NULL;
struct ratbagd_profile *profile = userdata;
unsigned int index = 0;
int r;
r = sd_bus_path_decode_many(path,
RATBAGD_OBJ_ROOT "/resolution/%/p%/r%",
NULL,
NULL,
&name);
if (r <= 0)
return r;
r = safe_atou(name, &index);
if (r < 0)
return 0;
if (index >= profile->n_resolutions || !profile->resolutions[index])
return 0;
*found = profile->resolutions[index];
return 1;
}
static int ratbagd_profile_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_profile *profile = userdata;
struct ratbagd_resolution *resolution;
unsigned int i;
int r;
r = sd_bus_message_open_container(reply, 'a', "o");
if (r < 0)
return r;
for (i = 0; i < profile->n_resolutions; ++i) {
resolution = profile->resolutions[i];
if (!resolution)
continue;
r = sd_bus_message_append(reply,
"o",
ratbagd_resolution_get_path(resolution));
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int ratbagd_profile_get_buttons(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 ratbagd_button *button;
unsigned int i;
int r;
r = sd_bus_message_open_container(reply, 'a', "o");
if (r < 0)
return r;
for (i = 0; i < profile->n_buttons; ++i) {
button = profile->buttons[i];
if (!button)
continue;
r = sd_bus_message_append(reply,
"o",
ratbagd_button_get_path(button));
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int ratbagd_profile_get_leds(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 ratbagd_led *led;
unsigned int i;
int r;
r = sd_bus_message_open_container(reply, 'a', "o");
if (r < 0)
return r;
for (i = 0; i < profile->n_leds; ++i) {
led = profile->leds[i];
if (!led)
continue;
r = sd_bus_message_append(reply,
"o",
ratbagd_led_get_path(led));
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int ratbagd_profile_is_active(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;
bool is_active;
is_active = !!ratbag_profile_is_active(profile->lib_profile);
return sd_bus_message_append(reply, "b", is_active);
}
static int ratbagd_profile_find_button(sd_bus *bus,
const char *path,
const char *interface,
void *userdata,
void **found,
sd_bus_error *error)
{
_cleanup_(freep) char *name = NULL;
struct ratbagd_profile *profile = userdata;
unsigned int index = 0;
int r;
r = sd_bus_path_decode_many(path,
RATBAGD_OBJ_ROOT "/button/%/p%/b%",
NULL,
NULL,
&name);
if (r <= 0)
return r;
r = safe_atou(name, &index);
if (r < 0)
return 0;
if (index >= profile->n_buttons || !profile->buttons[index])
return 0;
*found = profile->buttons[index];
return 1;
}
static int ratbagd_profile_find_led(sd_bus *bus,
const char *path,
const char *interface,
void *userdata,
void **found,
sd_bus_error *error)
{
_cleanup_(freep) char *name = NULL;
struct ratbagd_profile *profile = userdata;
unsigned int index = 0;
int r;
r = sd_bus_path_decode_many(path,
RATBAGD_OBJ_ROOT "/led/%/p%/l%",
NULL,
NULL,
&name);
if (r <= 0)
return r;
r = safe_atou(name, &index);
if (r < 0)
return 0;
if (index >= profile->n_leds || !profile->leds[index])
return 0;
*found = profile->leds[index];
return 1;
}
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));
return 0;
}
static int ratbagd_profile_set_active(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_profile *profile = userdata;
int r;
r = sd_bus_message_read(m, "");
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));
}
static int
ratbagd_profile_set_enabled(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;
bool enabled;
int r;
r = sd_bus_message_read(m, "b", &enabled);
if (r < 0)
return r;
r = ratbag_profile_set_enabled(profile->lib_profile, enabled);
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
profile->path,
RATBAGD_NAME_ROOT ".Profile",
"Enabled",
NULL);
}
return r;
}
static int
ratbagd_profile_is_enabled(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;
bool enabled = ratbag_profile_is_enabled(profile->lib_profile) != 0;
return sd_bus_message_append(reply, "b", enabled);
}
const sd_bus_vtable ratbagd_profile_vtable[] = {
SD_BUS_VTABLE_START(0),
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("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),
SD_BUS_PROPERTY("IsActive", "b", ratbagd_profile_is_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_METHOD("SetActive", "", "u", ratbagd_profile_set_active, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
int ratbagd_profile_new(struct ratbagd_profile **out,
struct ratbagd_device *device,
struct ratbag_profile *lib_profile,
unsigned int index)
{
_cleanup_(ratbagd_profile_freep) struct ratbagd_profile *profile = NULL;
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct ratbag_led *led;
char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
unsigned int i;
int r;
assert(out);
assert(lib_profile);
profile = zalloc(sizeof(*profile));
profile->device = device;
profile->lib_profile = lib_profile;
profile->index = index;
sprintf(index_buffer, "p%u", index);
r = sd_bus_path_encode_many(&profile->path,
RATBAGD_OBJ_ROOT "/profile/%/%",
ratbagd_device_get_name(device),
index_buffer);
if (r < 0)
return r;
profile->n_resolutions = ratbag_profile_get_num_resolutions(profile->lib_profile);
profile->resolutions = zalloc(profile->n_resolutions * sizeof(*profile->resolutions));
profile->n_buttons = ratbagd_device_get_num_buttons(device);
profile->buttons = zalloc(profile->n_buttons * sizeof(*profile->buttons));
profile->n_leds = ratbagd_device_get_num_leds(device);
profile->leds = zalloc(profile->n_leds * sizeof(*profile->leds));
for (i = 0; i < profile->n_resolutions; ++i) {
resolution = ratbag_profile_get_resolution(profile->lib_profile, i);
if (!resolution)
continue;
r = ratbagd_resolution_new(&profile->resolutions[i],
device,
profile,
resolution,
i);
if (r < 0) {
errno = -r;
log_error("Cannot allocate resolution for '%s': %m\n",
ratbagd_device_get_name(device));
}
}
for (i = 0; i < profile->n_buttons; ++i) {
button = ratbag_profile_get_button(profile->lib_profile, i);
if (!button)
continue;
r = ratbagd_button_new(&profile->buttons[i],
device,
profile,
button,
i);
if (r < 0) {
errno = -r;
log_error("Cannot allocate button for '%s': %m\n",
ratbagd_device_get_name(device));
}
}
for (i = 0; i < profile->n_leds; ++i) {
led = ratbag_profile_get_led(profile->lib_profile, i);
if (!led)
continue;
r = ratbagd_led_new(&profile->leds[i],
device,
profile,
led,
i);
if (r < 0) {
errno = -r;
log_error("Cannot allocate led for '%s': %m\n",
ratbagd_device_get_name(device));
}
}
*out = profile;
profile = NULL;
return 0;
}
struct ratbagd_profile *ratbagd_profile_free(struct ratbagd_profile *profile)
{
unsigned int i;
if (!profile)
return NULL;
profile->resolution_vtable_slot = sd_bus_slot_unref(profile->resolution_vtable_slot);
profile->resolution_enum_slot = sd_bus_slot_unref(profile->resolution_enum_slot);
profile->button_vtable_slot = sd_bus_slot_unref(profile->button_vtable_slot);
profile->button_enum_slot = sd_bus_slot_unref(profile->button_enum_slot);
profile->led_vtable_slot = sd_bus_slot_unref(profile->led_vtable_slot);
profile->led_enum_slot = sd_bus_slot_unref(profile->led_enum_slot);
for (i = 0; i< profile->n_leds; ++i)
ratbagd_led_free(profile->leds[i]);
for (i = 0; i< profile->n_buttons; ++i)
ratbagd_button_free(profile->buttons[i]);
for (i = 0; i< profile->n_resolutions; ++i)
ratbagd_resolution_free(profile->resolutions[i]);
mfree(profile->leds);
mfree(profile->buttons);
mfree(profile->resolutions);
profile->path = mfree(profile->path);
profile->lib_profile = ratbag_profile_unref(profile->lib_profile);
return mfree(profile);
}
const char *ratbagd_profile_get_path(struct ratbagd_profile *profile)
{
assert(profile);
return profile->path;
}
unsigned int ratbagd_profile_get_index(struct ratbagd_profile *profile)
{
assert(profile);
return profile->index;
}
static int ratbagd_profile_list_resolutions(sd_bus *bus,
const char *path,
void *userdata,
char ***paths,
sd_bus_error *error)
{
struct ratbagd_profile *profile = userdata;
struct ratbagd_resolution *resolution;
char **resolutions;
unsigned int i;
resolutions = zalloc((profile->n_resolutions + 1) * sizeof(char *));
for (i = 0; i < profile->n_resolutions; ++i) {
resolution = profile->resolutions[i];
if (!resolution)
continue;
resolutions[i] = strdup_safe(ratbagd_resolution_get_path(resolution));
}
resolutions[i] = NULL;
*paths = resolutions;
return 1;
}
int ratbagd_profile_register_resolutions(struct sd_bus *bus,
struct ratbagd_device *device,
struct ratbagd_profile *profile)
{
_cleanup_(freep) char *prefix = NULL;
char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
int r;
sprintf(index_buffer, "p%u", profile->index);
/* register resolution interfaces */
r = sd_bus_path_encode_many(&prefix,
RATBAGD_OBJ_ROOT "/resolution/%/%",
ratbagd_device_get_name(device),
index_buffer);
if (r >= 0) {
r = sd_bus_add_fallback_vtable(bus,
&profile->resolution_vtable_slot,
prefix,
RATBAGD_NAME_ROOT ".Resolution",
ratbagd_resolution_vtable,
ratbagd_profile_find_resolution,
profile);
if (r >= 0)
r = sd_bus_add_node_enumerator(bus,
&profile->resolution_enum_slot,
prefix,
ratbagd_profile_list_resolutions,
profile);
}
if (r < 0) {
errno = -r;
log_error("Cannot register resolutions for '%s': %m\n",
ratbagd_device_get_name(device));
}
return 0;
}
static int ratbagd_profile_list_buttons(sd_bus *bus,
const char *path,
void *userdata,
char ***paths,
sd_bus_error *error)
{
struct ratbagd_profile *profile = userdata;
struct ratbagd_button *button;
char **buttons;
unsigned int i;
buttons = zalloc((profile->n_buttons + 1) * sizeof(char *));
for (i = 0; i < profile->n_buttons; ++i) {
button = profile->buttons[i];
if (!button)
continue;
buttons[i] = strdup_safe(ratbagd_button_get_path(button));
}
buttons[i] = NULL;
*paths = buttons;
return 1;
}
int ratbagd_profile_register_buttons(struct sd_bus *bus,
struct ratbagd_device *device,
struct ratbagd_profile *profile)
{
_cleanup_(freep) char *prefix = NULL;
char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
int r;
sprintf(index_buffer, "p%u", profile->index);
/* register button interfaces */
r = sd_bus_path_encode_many(&prefix,
RATBAGD_OBJ_ROOT "/button/%/%",
ratbagd_device_get_name(device),
index_buffer);
if (r >= 0) {
r = sd_bus_add_fallback_vtable(bus,
&profile->button_vtable_slot,
prefix,
RATBAGD_NAME_ROOT ".Button",
ratbagd_button_vtable,
ratbagd_profile_find_button,
profile);
if (r >= 0)
r = sd_bus_add_node_enumerator(bus,
&profile->button_enum_slot,
prefix,
ratbagd_profile_list_buttons,
profile);
}
if (r < 0) {
errno = -r;
log_error("Cannot register buttons for '%s': %m\n",
ratbagd_device_get_name(device));
}
return 0;
}
static int ratbagd_profile_list_leds(sd_bus *bus,
const char *path,
void *userdata,
char ***paths,
sd_bus_error *error)
{
struct ratbagd_profile *profile = userdata;
struct ratbagd_led *led;
char **leds;
unsigned int i;
leds = zalloc((profile->n_leds + 1) * sizeof(char *));
for (i = 0; i < profile->n_leds; ++i) {
led = profile->leds[i];
if (!led)
continue;
leds[i] = strdup_safe(ratbagd_led_get_path(led));
}
leds[i] = NULL;
*paths = leds;
return 1;
}
int ratbagd_profile_register_leds(struct sd_bus *bus,
struct ratbagd_device *device,
struct ratbagd_profile *profile)
{
_cleanup_(freep) char *prefix = NULL;
char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
int r;
sprintf(index_buffer, "p%u", profile->index);
/* register led interfaces */
r = sd_bus_path_encode_many(&prefix,
RATBAGD_OBJ_ROOT "/led/%/%",
ratbagd_device_get_name(device),
index_buffer);
if (r >= 0) {
r = sd_bus_add_fallback_vtable(bus,
&profile->led_vtable_slot,
prefix,
RATBAGD_NAME_ROOT ".Led",
ratbagd_led_vtable,
ratbagd_profile_find_led,
profile);
if (r >= 0)
r = sd_bus_add_node_enumerator(bus,
&profile->led_enum_slot,
prefix,
ratbagd_profile_list_leds,
profile);
}
if (r < 0) {
errno = -r;
log_error("Cannot register leds for '%s': %m\n",
ratbagd_device_get_name(device));
}
return 0;
}
int ratbagd_for_each_resolution_signal(sd_bus *bus,
struct ratbagd_profile *profile,
int (*func)(sd_bus *bus,
struct ratbagd_resolution *resolution))
{
int rc = 0;
for (size_t i = 0; rc == 0 && i < profile->n_resolutions; i++)
rc = func(bus, profile->resolutions[i]);
return rc;
}

View File

@@ -0,0 +1,418 @@
/***
This file is part of ratbagd.
Copyright 2016 Red Hat, Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
***/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <libratbag.h>
#include <libudev.h>
#include <stdio.h>
#include <stdlib.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include "ratbagd.h"
#include "shared-macro.h"
#include "libratbag-util.h"
struct ratbagd_resolution {
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",
"IsActive",
NULL);
return 0;
}
static int ratbagd_resolution_set_active(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_resolution *resolution = userdata;
int r;
r = ratbag_resolution_set_active(resolution->lib_resolution);
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);
}
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",
"IsDefault",
NULL);
return 0;
}
static int ratbagd_resolution_set_default(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_resolution *resolution = userdata;
int r;
r = ratbag_resolution_set_default(resolution->lib_resolution);
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);
}
static int
ratbagd_resolution_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_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
enum ratbag_device_capability cap;
int r;
r = sd_bus_message_open_container(reply, 'a', "u");
if (r < 0)
return r;
cap = RATBAG_RESOLUTION_CAP_INDIVIDUAL_REPORT_RATE;
if (ratbag_resolution_has_capability(lib_resolution, cap)) {
r = sd_bus_message_append(reply, "u", cap);
if (r < 0)
return r;
}
cap = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION;
if (ratbag_resolution_has_capability(lib_resolution, cap)) {
r = sd_bus_message_append(reply, "u", cap);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int
ratbagd_resolution_is_active(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;
bool is_active;
is_active = !!ratbag_resolution_is_active(lib_resolution);
return sd_bus_message_append(reply, "b", is_active);
}
static int
ratbagd_resolution_is_default(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;
bool is_default;
is_default = !!ratbag_resolution_is_default(lib_resolution);
return sd_bus_message_append(reply, "b", is_default);
}
static int
ratbagd_resolution_get_resolution(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 xres, yres;
xres = ratbag_resolution_get_dpi_x(lib_resolution);
yres = ratbag_resolution_get_dpi_y(lib_resolution);
return sd_bus_message_append(reply, "(uu)", xres, yres);
}
static int
ratbagd_resolution_set_resolution(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;
const enum ratbag_device_capability cap = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION;
int xres, yres;
int r;
r = sd_bus_message_read(m, "(uu)", &xres, &yres);
if (r < 0)
return r;
if (!ratbag_resolution_has_capability(lib_resolution, cap)) {
r = ratbag_resolution_set_dpi(resolution->lib_resolution, xres);
} else {
r = ratbag_resolution_set_dpi_xy(resolution->lib_resolution,
xres, yres);
}
if (r == 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
sd_bus_emit_properties_changed(bus,
resolution->path,
RATBAGD_NAME_ROOT ".Resolution",
"Resolution",
NULL);
}
return r;
}
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)
{
struct ratbagd_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
int rate;
rate = ratbag_resolution_get_report_rate(lib_resolution);
return sd_bus_message_append(reply, "u", rate);
}
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)
{
struct ratbagd_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
int min;
min = ratbag_resolution_get_dpi_minimum(lib_resolution);
return sd_bus_message_append(reply, "u", min);
}
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)
{
struct ratbagd_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
int max;
max = ratbag_resolution_get_dpi_maximum(lib_resolution);
return sd_bus_message_append(reply, "u", max);
}
const sd_bus_vtable ratbagd_resolution_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_resolution, index), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Capabilities", "au", ratbagd_resolution_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IsActive", "b", ratbagd_resolution_is_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IsDefault", "b", ratbagd_resolution_is_default, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("Resolution", "(uu)",
ratbagd_resolution_get_resolution,
ratbagd_resolution_set_resolution, 0,
SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("ReportRate", "u",
ratbagd_resolution_get_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_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,
};
int ratbagd_resolution_new(struct ratbagd_resolution **out,
struct ratbagd_device *device,
struct ratbagd_profile *profile,
struct ratbag_resolution *lib_resolution,
unsigned int index)
{
_cleanup_(ratbagd_resolution_freep) struct ratbagd_resolution *resolution = NULL;
char profile_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1],
resolution_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
int r;
assert(out);
assert(lib_resolution);
resolution = calloc(1, sizeof(*resolution));
if (!resolution)
return -ENOMEM;
resolution->profile = profile;
resolution->lib_resolution = lib_resolution;
resolution->index = index;
sprintf(profile_buffer, "p%u", ratbagd_profile_get_index(profile));
sprintf(resolution_buffer, "r%u", index);
r = sd_bus_path_encode_many(&resolution->path,
RATBAGD_OBJ_ROOT "/resolution/%/%/%",
ratbagd_device_get_name(device),
profile_buffer,
resolution_buffer);
if (r < 0)
return r;
*out = resolution;
resolution = NULL;
return 0;
}
const char *ratbagd_resolution_get_path(struct ratbagd_resolution *resolution)
{
assert(resolution);
return resolution->path;
}
struct ratbagd_resolution *ratbagd_resolution_free(struct ratbagd_resolution *resolution)
{
if (!resolution)
return NULL;
resolution->path = mfree(resolution->path);
resolution->lib_resolution = ratbag_resolution_unref(resolution->lib_resolution);
return mfree(resolution);
}

19
ratbagd/ratbagd.8 Normal file
View File

@@ -0,0 +1,19 @@
.TH ratbagd 8 "Apr 13, 2016" ratbagd
.SH NAME
ratbagd \- system daemon to introspect and modify configurable mice
.SH SYNOPSIS
.B ratbagd
.RB [ \-\-verbose ]
.SH DESCRIPTION
.B ratbagd
starts the daemon. It shouldn't be invoked directly;
.B ratbagd
is normally started through DBus activation.
.SH SEE ALSO
.BR ratbagctl (1)
.SH AUTHORS
.B ratbagd
was written by David Herrmann, Peter Hutterer and Benjamin Tissoires.
.PP
This manual page was written by Stephen Kitt <skitt@debian.org> for
the Debian GNU/Linux system (but may be used by others).

738
ratbagd/ratbagd.c Normal file
View File

@@ -0,0 +1,738 @@
/***
This file is part of ratbagd.
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
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-event-codes.h>
#include <assert.h>
#include <errno.h>
#include <libratbag.h>
#include <libudev.h>
#include <stdio.h>
#include <stdlib.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include "ratbagd.h"
#include "shared-macro.h"
#include "libratbag-util.h"
static bool verbose = false;
static const char *SVG_THEMES[] = {
"default",
"gnome",
NULL
};
void log_verbose(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (verbose)
vprintf(fmt, args);
va_end(args);
}
void log_error(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "%s error: ", program_invocation_short_name);
vfprintf(stderr, fmt, args);
va_end(args);
}
static int ratbagd_find_device(sd_bus *bus,
const char *path,
const char *interface,
void *userdata,
void **found,
sd_bus_error *error)
{
_cleanup_(freep) char *name = NULL;
struct ratbagd *ctx = userdata;
struct ratbagd_device *device;
int r;
r = sd_bus_path_decode_many(path,
RATBAGD_OBJ_ROOT "/device/%",
&name);
if (r <= 0)
return r;
device = ratbagd_device_lookup(ctx, name);
if (!device)
return 0;
*found = device;
return 1;
}
static int ratbagd_list_devices(sd_bus *bus,
const char *path,
void *userdata,
char ***paths,
sd_bus_error *error)
{
struct ratbagd *ctx = userdata;
struct ratbagd_device *device;
char **devices, **pos;
devices = calloc(ctx->n_devices + 1, sizeof(char *));
if (!devices)
return -ENOMEM;
pos = devices;
RATBAGD_DEVICE_FOREACH(device, ctx) {
*pos = strdup(ratbagd_device_get_path(device));
if (!*pos)
goto error;
++pos;
}
*pos = NULL;
*paths = devices;
return 1;
error:
for (pos = devices; *pos; ++pos)
free(*pos);
free(devices);
return -ENOMEM;
}
static int ratbagd_get_devices(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd *ctx = userdata;
struct ratbagd_device *device;
int r;
r = sd_bus_message_open_container(reply, 'a', "o");
if (r < 0)
return r;
RATBAGD_DEVICE_FOREACH(device, ctx) {
r = sd_bus_message_append(reply,
"o",
ratbagd_device_get_path(device));
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int ratbagd_get_themes(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd *ctx = userdata;
const char **theme;
int r;
r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0)
return r;
theme = ctx->themes;
while(*theme) {
r = sd_bus_message_append(reply,
"s",
*theme);
if (r < 0)
return r;
theme++;
}
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),
SD_BUS_PROPERTY("Themes", "as", ratbagd_get_themes, 0, 0),
#ifdef RATBAG_DEVELOPER_EDITION
SD_BUS_METHOD("ResetTestDevice", "", "u", ratbagd_reset_test_device, SD_BUS_VTABLE_UNPRIVILEGED),
#endif /* RATBAG_DEVELOPER_EDITION */
SD_BUS_VTABLE_END,
};
static void ratbagd_process_device(struct ratbagd *ctx,
struct udev_device *udevice)
{
struct ratbag_device *lib_device;
struct ratbagd_device *device;
const char *name;
int r;
/*
* TODO: libratbag should provide some mechanism to allow
* device-grouping, just like libinput does. If multiple input
* devices belong to the same virtual device, we should not add
* it multiple times. Instead, libratbag should group them and
* provide *us* a unique name that identifies the group, rather
* than taking a random input-device as tag.
*/
name = udev_device_get_sysname(udevice);
if (!name || !startswith(name, "event"))
return;
device = ratbagd_device_lookup(ctx, name);
if (streq_ptr("remove", udev_device_get_action(udevice))) {
/* device was removed, unlink it and destroy our context */
if (device) {
ratbagd_device_unlink(device);
ratbagd_device_free(device);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
RATBAGD_NAME_ROOT ".Manager",
"Devices",
NULL);
}
} else if (device) {
/* device already known, refresh our view of the device */
} else {
enum ratbag_error_code error;
/* device unknown, create new one and link it */
error = ratbag_device_new_from_udev_device(ctx->lib_ctx,
udevice,
&lib_device);
if (error != RATBAG_SUCCESS)
return; /* unsupported device */
r = ratbagd_device_new(&device, ctx, name, lib_device);
/* the ratbagd_device takes its own reference, drop ours */
ratbag_device_unref(lib_device);
if (r < 0) {
log_error("Cannot track device '%s'\n", name);
return;
}
ratbagd_device_link(device);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
RATBAGD_NAME_ROOT ".Manager",
"Devices",
NULL);
}
}
static int ratbagd_monitor_event(sd_event_source *source,
int fd,
uint32_t mask,
void *userdata)
{
struct ratbagd *ctx = userdata;
struct udev_device *udevice;
udevice = udev_monitor_receive_device(ctx->monitor);
if (!udevice)
return 0;
ratbagd_process_device(ctx, udevice);
udev_device_unref(udevice);
return 0;
}
static int ratbagd_lib_open_restricted(const char *path,
int flags,
void *userdata)
{
return open(path, flags, 0);
}
static void ratbagd_lib_close_restricted(int fd, void *userdata)
{
safe_close(fd);
}
static const struct ratbag_interface ratbagd_lib_interface = {
.open_restricted = ratbagd_lib_open_restricted,
.close_restricted = ratbagd_lib_close_restricted,
};
static struct ratbagd *ratbagd_free(struct ratbagd *ctx)
{
struct ratbagd_device *device;
if (!ctx)
return NULL;
RATBAGD_DEVICE_FOREACH(device, ctx)
ratbagd_device_unlink(device);
ctx->bus = sd_bus_flush_close_unref(ctx->bus);
ctx->monitor_source = sd_event_source_unref(ctx->monitor_source);
ctx->monitor = udev_monitor_unref(ctx->monitor);
ctx->lib_ctx = ratbag_unref(ctx->lib_ctx);
ctx->event = sd_event_unref(ctx->event);
assert(!ctx->device_map.root);
assert(!ctx->lib_ctx); /* ratbag returns non-NULL if still pinned */
return mfree(ctx);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd *, ratbagd_free);
static int ratbagd_init_monitor(struct ratbagd *ctx)
{
struct udev *udev;
int r;
udev = udev_new();
if (!udev)
return -ENOMEM;
ctx->monitor = udev_monitor_new_from_netlink(udev, "udev");
/* we don't need the context to stay around; drop it */
udev_unref(udev);
if (!ctx->monitor)
return -ENOMEM;
r = udev_monitor_filter_add_match_subsystem_devtype(ctx->monitor,
"input",
NULL);
if (r < 0)
return r;
r = udev_monitor_enable_receiving(ctx->monitor);
if (r < 0)
return r;
r = sd_event_add_io(ctx->event,
&ctx->monitor_source,
udev_monitor_get_fd(ctx->monitor),
EPOLLIN,
ratbagd_monitor_event,
ctx);
if (r < 0)
return r;
return 0;
}
static int ratbagd_new(struct ratbagd **out)
{
_cleanup_(ratbagd_freep) struct ratbagd *ctx = NULL;
int r;
ctx = zalloc(sizeof(*ctx));
ctx->themes = SVG_THEMES;
r = sd_event_default(&ctx->event);
if (r < 0)
return r;
r = sd_event_set_watchdog(ctx->event, true);
if (r < 0)
return r;
ctx->lib_ctx = ratbag_create_context(&ratbagd_lib_interface, ctx);
if (!ctx->lib_ctx)
return -ENOMEM;
if (verbose)
ratbag_log_set_priority(ctx->lib_ctx,
RATBAG_LOG_PRIORITY_DEBUG);
r = ratbagd_init_monitor(ctx);
if (r < 0)
return r;
r = sd_bus_open_system(&ctx->bus);
if (r < 0)
return r;
r = sd_bus_add_object_vtable(ctx->bus,
NULL,
RATBAGD_OBJ_ROOT,
RATBAGD_NAME_ROOT ".Manager",
ratbagd_vtable,
ctx);
if (r < 0)
return r;
r = sd_bus_add_fallback_vtable(ctx->bus,
NULL,
RATBAGD_OBJ_ROOT "/device",
RATBAGD_NAME_ROOT ".Device",
ratbagd_device_vtable,
ratbagd_find_device,
ctx);
if (r < 0)
return r;
r = sd_bus_add_node_enumerator(ctx->bus,
NULL,
RATBAGD_OBJ_ROOT "/device",
ratbagd_list_devices,
ctx);
if (r < 0)
return r;
r = sd_bus_request_name(ctx->bus, RATBAGD_NAME_ROOT, 0);
if (r < 0)
return r;
r = sd_bus_attach_event(ctx->bus, ctx->event, 0);
if (r < 0)
return r;
*out = ctx;
ctx = NULL;
return 0;
}
static int ratbagd_run_enumerate(struct ratbagd *ctx)
{
struct udev_list_entry *list, *iter;
struct udev_enumerate *e;
struct udev *udev;
int r;
udev = udev_monitor_get_udev(ctx->monitor);
e = udev_enumerate_new(udev);
if (!e)
return -ENOMEM;
r = udev_enumerate_add_match_subsystem(e, "input");
if (r < 0)
goto exit;
r = udev_enumerate_add_match_is_initialized(e);
if (r < 0)
goto exit;
r = udev_enumerate_scan_devices(e);
if (r < 0)
goto exit;
list = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(iter, list) {
struct udev_device *udevice;
const char *p;
p = udev_list_entry_get_name(iter);
udevice = udev_device_new_from_syspath(udev, p);
if (udevice)
ratbagd_process_device(ctx, udevice);
udev_device_unref(udevice);
}
r = 0;
exit:
udev_enumerate_unref(e);
return r;
}
static int sighandler(sd_event_source *source,
const struct signalfd_siginfo *si,
void *userdata)
{
sd_event *event = sd_event_source_get_event(source);
sd_event_exit(event, 0);
return 0;
}
static int ratbagd_run(struct ratbagd *ctx)
{
int r;
/*
* TODO: We should support exit-on-idle and bus-activation. Note that
* we don't store any state on our own, hence, all we have to do
* is to make sure we advertise device add/remove events via the
* bus (in case there is a listener).
* To track such events, we should store devices we already
* advertised in /run/ratbagd/devices/ and read it out on
* activation.
* Note that this feature requires udev-activation, which might
* not be possible, yet.
*/
r = ratbagd_run_enumerate(ctx);
if (r < 0)
return r;
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigprocmask(SIG_BLOCK, &sigset, NULL);
sd_event_add_signal(ctx->event, NULL, SIGINT, sighandler, NULL);
return sd_event_loop(ctx->event);
}
int main(int argc, char *argv[])
{
struct ratbagd *ctx = NULL;
int r;
if (argc > 1) {
if (streq(argv[1], "--verbose")) {
verbose = true;
} else {
fprintf(stderr, "Usage: %s [--verbose]\n",
program_invocation_short_name);
r = -EINVAL;
goto exit;
}
}
r = ratbagd_new(&ctx);
if (r < 0)
goto exit;
ratbagd_init_test_device(ctx);
r = ratbagd_run(ctx);
exit:
ratbagd_free(ctx);
if (r < 0) {
errno = -r;
log_error("Failed to start ratbagd: %m\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

187
ratbagd/ratbagd.h Normal file
View File

@@ -0,0 +1,187 @@
#pragma once
/***
This file is part of ratbagd.
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
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 <libratbag.h>
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include "shared-macro.h"
#include "shared-rbtree.h"
#ifndef RATBAG_DBUS_INTERFACE
#define RATBAG_DBUS_INTERFACE "ratbag1"
#else
#define RATBAG_DEVELOPER_EDITION
#endif
#define RATBAGD_OBJ_ROOT "/org/freedesktop/" RATBAG_DBUS_INTERFACE
#define RATBAGD_NAME_ROOT "org.freedesktop." RATBAG_DBUS_INTERFACE
struct ratbagd;
struct ratbagd_device;
struct ratbagd_profile;
struct ratbagd_resolution;
struct ratbagd_button;
struct ratbagd_led;
void log_verbose(const char *fmt, ...) _printf_(1, 2);
void log_error(const char *fmt, ...) _printf_(1, 2);
/*
* Profiles
*/
extern const sd_bus_vtable ratbagd_profile_vtable[];
int ratbagd_profile_new(struct ratbagd_profile **out,
struct ratbagd_device *device,
struct ratbag_profile *lib_profile,
unsigned int index);
struct ratbagd_profile *ratbagd_profile_free(struct ratbagd_profile *profile);
const char *ratbagd_profile_get_path(struct ratbagd_profile *profile);
bool ratbagd_profile_is_default(struct ratbagd_profile *profile);
unsigned int ratbagd_profile_get_index(struct ratbagd_profile *profile);
int ratbagd_profile_register_resolutions(struct sd_bus *bus,
struct ratbagd_device *device,
struct ratbagd_profile *profile);
int ratbagd_profile_register_buttons(struct sd_bus *bus,
struct ratbagd_device *device,
struct ratbagd_profile *profile);
int ratbagd_profile_register_leds(struct sd_bus *bus,
struct ratbagd_device *device,
struct ratbagd_profile *profile);
int ratbagd_for_each_profile_signal(sd_bus *bus,
struct ratbagd_device *device,
int (*func)(sd_bus *bus,
struct ratbagd_profile *profile));
int ratbagd_for_each_resolution_signal(sd_bus *bus,
struct ratbagd_profile *profile,
int (*func)(sd_bus *bus,
struct ratbagd_resolution *resolution));
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_profile *, ratbagd_profile_free);
/*
* Resolutions
*/
extern const sd_bus_vtable ratbagd_resolution_vtable[];
int ratbagd_resolution_new(struct ratbagd_resolution **out,
struct ratbagd_device *device,
struct ratbagd_profile *profile,
struct ratbag_resolution *lib_resolution,
unsigned int index);
struct ratbagd_resolution *ratbagd_resolution_free(struct ratbagd_resolution *resolution);
const char *ratbagd_resolution_get_path(struct ratbagd_resolution *resolution);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_resolution *, ratbagd_resolution_free);
/*
* Buttons
*/
extern const sd_bus_vtable ratbagd_button_vtable[];
int ratbagd_button_new(struct ratbagd_button **out,
struct ratbagd_device *device,
struct ratbagd_profile *profile,
struct ratbag_button *lib_button,
unsigned int index);
struct ratbagd_button *ratbagd_button_free(struct ratbagd_button *button);
const char *ratbagd_button_get_path(struct ratbagd_button *button);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_button *, ratbagd_button_free);
/*
* Leds
*/
extern const sd_bus_vtable ratbagd_led_vtable[];
int ratbagd_led_new(struct ratbagd_led **out,
struct ratbagd_device *device,
struct ratbagd_profile *profile,
struct ratbag_led *lib_led,
unsigned int index);
struct ratbagd_led *ratbagd_led_free(struct ratbagd_led *led);
const char *ratbagd_led_get_path(struct ratbagd_led *led);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_led *, ratbagd_led_free);
/*
* Devices
*/
extern const sd_bus_vtable ratbagd_device_vtable[];
int ratbagd_device_new(struct ratbagd_device **out,
struct ratbagd *ctx,
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_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);
bool ratbagd_device_linked(struct ratbagd_device *device);
void ratbagd_device_link(struct ratbagd_device *device);
void ratbagd_device_unlink(struct ratbagd_device *device);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_device *, ratbagd_device_free);
struct ratbagd_device *ratbagd_device_lookup(struct ratbagd *ctx,
const char *name);
struct ratbagd_device *ratbagd_device_first(struct ratbagd *ctx);
struct ratbagd_device *ratbagd_device_next(struct ratbagd_device *device);
#define RATBAGD_DEVICE_FOREACH(_device, _ctx) \
for ((_device) = ratbagd_device_first(_ctx); \
(_device); \
(_device) = ratbagd_device_next(_device))
/*
* Context
*/
struct ratbagd {
sd_event *event;
struct ratbag *lib_ctx;
struct udev_monitor *monitor;
sd_event_source *monitor_source;
sd_bus *bus;
RBTree device_map;
size_t n_devices;
const char **themes; /* NULL-terminated */
};

View File

@@ -0,0 +1,11 @@
[Unit]
Description=Daemon to introspect and modify configurable mice
[Service]
Type=dbus
BusName=org.freedesktop.ratbag1
ExecStart=@bindir@/ratbagd
Restart=on-abort
[Install]
Alias=dbus-org.freedesktop.ratbag1.service

541
release.sh Executable file
View File

@@ -0,0 +1,541 @@
#!/bin/bash
#
# Generate the announce template
#
# Completely copy/paste of Xorg/util/modular/release.sh:
#
# Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
export LC_ALL=C
#------------------------------------------------------------------------------
# Function: check_local_changes
#------------------------------------------------------------------------------
#
check_local_changes() {
git diff --quiet HEAD > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo ""
echo "Uncommitted changes found. Did you forget to commit? Aborting."
echo ""
echo "You can perform a 'git stash' to save your local changes and"
echo "a 'git stash apply' to recover them after the tarball release."
echo "Make sure to rebuild and run 'make distcheck' again."
echo ""
echo "Alternatively, you can clone the module in another directory"
echo "and run ./configure. No need to build if testing was finished."
echo ""
return 1
fi
return 0
}
#------------------------------------------------------------------------------
# Function: check_option_args
#------------------------------------------------------------------------------
#
# perform sanity checks on cmdline args which require arguments
# arguments:
# $1 - the option being examined
# $2 - the argument to the option
# returns:
# if it returns, everything is good
# otherwise it exit's
check_option_args() {
option=$1
arg=$2
# check for an argument
if [ x"$arg" = x ]; then
echo ""
echo "Error: the '$option' option is missing its required argument."
echo ""
usage
exit 1
fi
# does the argument look like an option?
echo $arg | $GREP "^-" > /dev/null
if [ $? -eq 0 ]; then
echo ""
echo "Error: the argument '$arg' of option '$option' looks like an option itself."
echo ""
usage
exit 1
fi
}
#------------------------------------------------------------------------------
# Function: check_modules_specification
#------------------------------------------------------------------------------
#
check_modules_specification() {
if [ x"$MODFILE" = x ]; then
if [ x"${INPUT_MODULES}" = x ]; then
echo ""
echo "Error: no modules specified (blank command line)."
usage
exit 1
fi
fi
}
#------------------------------------------------------------------------------
# Function: generate_announce
#------------------------------------------------------------------------------
#
generate_announce()
{
cat <<RELEASE
Subject: [ANNOUNCE] $pkg_name $pkg_version
To: $list_to
Cc: $list_cc
libratbag $tag_name is out.
The list of interesting changes are:
See the full log at the end if you are interested in the details.
`git log --no-merges "$tag_range" | git shortlog`
git tag: $tag_name
RELEASE
for tarball in $tarbz2 $targz $tarxz; do
cat <<RELEASE
The libratbag project does not generate tarballs for releases, you can
grab one directly from github:
https://github.com/libratbag/libratbag/archive/$tag_name/$tarball
RELEASE
done
}
#------------------------------------------------------------------------------
# Function: read_modfile
#------------------------------------------------------------------------------
#
# Read the module names from the file and set a variable to hold them
# This will be the same interface as cmd line supplied modules
#
read_modfile() {
if [ x"$MODFILE" != x ]; then
# Make sure the file is sane
if [ ! -r "$MODFILE" ]; then
echo "Error: module file '$MODFILE' is not readable or does not exist."
exit 1
fi
# read from input file, skipping blank and comment lines
while read line; do
# skip blank lines
if [ x"$line" = x ]; then
continue
fi
# skip comment lines
if echo "$line" | $GREP -q "^#" ; then
continue;
fi
INPUT_MODULES="$INPUT_MODULES $line"
done <"$MODFILE"
fi
return 0
}
#------------------------------------------------------------------------------
# Function: print_epilog
#------------------------------------------------------------------------------
#
print_epilog() {
epilog="======== Successful Completion"
if [ x"$NO_QUIT" != x ]; then
if [ x"$failed_modules" != x ]; then
epilog="======== Partial Completion"
fi
elif [ x"$failed_modules" != x ]; then
epilog="======== Stopped on Error"
fi
echo ""
echo "$epilog `date`"
# Report about modules that failed for one reason or another
if [ x"$failed_modules" != x ]; then
echo " List of failed modules:"
for mod in $failed_modules; do
echo " $mod"
done
echo "========"
echo ""
fi
}
#------------------------------------------------------------------------------
# Function: process_modules
#------------------------------------------------------------------------------
#
# Loop through each module to release
# Exit on error if --no-quit was not specified
#
process_modules() {
for MODULE_RPATH in ${INPUT_MODULES}; do
if ! process_module ; then
echo "Error: processing module \"$MODULE_RPATH\" failed."
failed_modules="$failed_modules $MODULE_RPATH"
if [ x"$NO_QUIT" = x ]; then
print_epilog
exit 1
fi
fi
done
}
#------------------------------------------------------------------------------
# Function: get_section
#------------------------------------------------------------------------------
# Code 'return 0' on success
# Code 'return 1' on error
# Sets global variable $section
get_section() {
local module_url
local full_module_url
# Obtain the git url in order to find the section to which this module belongs
full_module_url=`git config --get remote.$remote_name.url | sed 's:\.git$::'`
if [ $? -ne 0 ]; then
echo "Error: unable to obtain git url for remote \"$remote_name\"."
return 1
fi
# The last part of the git url will tell us the section. Look for xorg first
echo "$full_module_url"
module_url=`echo "$full_module_url" | $GREP -o "libratbag/.*"`
if [ $? -eq 0 ]; then
module_url=`echo $module_url | cut -d'/' -f2,3`
else
echo "Error: unable to locate a valid project url from \"$full_module_url\"."
echo "Cannot establish url as one of libratbag"
cd $top_src
return 1
fi
# Find the section (subdirs) where the tarballs are to be uploaded
# The module relative path can be app/xfs, xserver, or mesa/drm for example
section=`echo $module_url | cut -d'/' -f1`
if [ $? -ne 0 ]; then
echo "Error: unable to extract section from $module_url first field."
return 1
fi
return 0
}
#------------------------------------------------------------------------------
# Function: process_module
#------------------------------------------------------------------------------
# Code 'return 0' on success to process the next module
# Code 'return 1' on error to process next module if invoked with --no-quit
#
process_module() {
top_src=`pwd`
echo ""
echo "======== Processing \"$top_src/$MODULE_RPATH\""
# This is the location where the script has been invoked
if [ ! -d $MODULE_RPATH ] ; then
echo "Error: $MODULE_RPATH cannot be found under $top_src."
return 1
fi
# Change directory to be in the git module
cd $MODULE_RPATH
if [ $? -ne 0 ]; then
echo "Error: failed to cd to $MODULE_RPATH."
return 1
fi
# ----- Now in the git module *root* directory ----- #
# Check that this is indeed a git module
if [ ! -d .git ]; then
echo "Error: there is no git module here: `pwd`"
return 1
fi
# Check for uncommitted/queued changes.
check_local_changes
if [ $? -ne 0 ]; then
cd $top_src
return 1
fi
# Determine what is the current branch and the remote name
current_branch=`git branch | $GREP "\*" | sed -e "s/\* //"`
remote_name=`git config --get branch.$current_branch.remote`
remote_branch=`git config --get branch.$current_branch.merge | cut -d'/' -f3,4`
remote_url_root="https://github.com/libratbag/libratbag/archive"
echo "Info: working off the \"$current_branch\" branch tracking the remote \"$remote_name/$remote_branch\"."
# Obtain the section
get_section
if [ $? -ne 0 ]; then
cd $top_src
return 1
fi
# Find out the tarname from meson.build
pkg_name=$($GREP '^project(' meson.build | sed 's|project(\([^\s,]*\).*|\1|' | sed "s/'//g")
pkg_version=$($GREP -m 1 '^ version : ' meson.build | sed 's| version : \([^\s,]*\).*|\1|' | sed "s/'//g")
tar_name="$pkg_name-$pkg_version"
targz=$tar_name.tar.gz
tag_name=$(echo "v$pkg_version" | sed 's|.0$||')
# Now get the tarballs from github directly to compute their checksum
remote_targz_url=$remote_url_root/$tag_name/$targz
echo "downloading the tarbal from $remote_targz_url"
curl $remote_targz_url > $targz
# Obtain the top commit SHA which should be the version bump
# It should not have been tagged yet (the script will do it later)
local_top_commit_sha=`git rev-list --max-count=1 HEAD`
if [ $? -ne 0 ]; then
echo "Error: unable to obtain the local top commit id."
cd $top_src
return 1
fi
# Check that the top commit looks like a version bump
git diff --unified=0 HEAD^ | $GREP -F $pkg_version >/dev/null 2>&1
if [ $? -ne 0 ]; then
# Wayland repos use m4_define([wayland_major_version], [0])
git diff --unified=0 HEAD^ | $GREP -E "(major|minor|micro)_version" >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Error: the local top commit does not look like a version bump."
echo " the diff does not contain the string \"$pkg_version\"."
local_top_commit_descr=`git log --oneline --max-count=1 $local_top_commit_sha`
echo " the local top commit is: \"$local_top_commit_descr\""
cd $top_src
return 1
fi
fi
# Check that the top commit has been pushed to remote
remote_top_commit_sha=`git rev-list --max-count=1 $remote_name/$remote_branch`
if [ $? -ne 0 ]; then
echo "Error: unable to obtain top commit from the remote repository."
cd $top_src
return 1
fi
if [ x"$remote_top_commit_sha" != x"$local_top_commit_sha" ]; then
echo "Error: the local top commit has not been pushed to the remote."
local_top_commit_descr=`git log --oneline --max-count=1 $local_top_commit_sha`
echo " the local top commit is: \"$local_top_commit_descr\""
cd $top_src
return 1
fi
# If a tag exists with the the tar name, ensure it is tagging the top commit
# It may happen if the version set in configure.ac has been previously released
tagged_commit_sha=`git rev-list --max-count=1 $tag_name 2>/dev/null`
if [ $? -eq 0 ]; then
# Check if the tag is pointing to the top commit
if [ x"$tagged_commit_sha" != x"$remote_top_commit_sha" ]; then
echo "Error: the \"$tag_name\" already exists."
echo " this tag is not tagging the top commit."
remote_top_commit_descr=`git log --oneline --max-count=1 $remote_top_commit_sha`
echo " the top commit is: \"$remote_top_commit_descr\""
local_tag_commit_descr=`git log --oneline --max-count=1 $tagged_commit_sha`
echo " tag \"$tag_name\" is tagging some other commit: \"$local_tag_commit_descr\""
cd $top_src
return 1
else
echo "Info: module already tagged with \"$tag_name\"."
fi
else
# Tag the top commit with the tar name
if [ x"$DRY_RUN" = x ]; then
git tag -s -m $tag_name $tag_name
if [ $? -ne 0 ]; then
echo "Error: unable to tag module with \"$tag_name\"."
cd $top_src
return 1
else
echo "Info: module tagged with \"$tag_name\"."
fi
else
echo "Info: skipping the commit tagging in dry-run mode."
fi
fi
# Mailing lists where to post the all [Announce] e-mails
list_to="input-tools@lists.freedesktop.org"
# Pushing the top commit tag to the remote repository
if [ x$DRY_RUN = x ]; then
echo "Info: pushing tag \"$tag_name\" to remote \"$remote_name\":"
git push $remote_name $tag_name
if [ $? -ne 0 ]; then
echo "Error: unable to push tag \"$tag_name\" to the remote repository."
echo " it is recommended you fix this manually and not run the script again"
cd $top_src
return 1
fi
else
echo "Info: skipped pushing tag \"$tag_name\" to the remote repository in dry-run mode."
fi
# --------- Generate the announce e-mail ------------------
# Failing to generate the announce is not considered a fatal error
# Git-describe returns only "the most recent tag", it may not be the expected one
# However, we only use it for the commit history which will be the same anyway.
tag_previous=`git describe --abbrev=0 HEAD^ 2>/dev/null`
# Git fails with rc=128 if no tags can be found prior to HEAD^
if [ $? -ne 0 ]; then
if [ $? -ne 0 ]; then
echo "Warning: unable to find a previous tag."
echo " perhaps a first release on this branch."
echo " Please check the commit history in the announce."
fi
fi
if [ x"$tag_previous" != x ]; then
# The top commit may not have been tagged in dry-run mode. Use commit.
tag_range=$tag_previous..$local_top_commit_sha
else
tag_range=$tag_name
fi
generate_announce > "$tar_name.announce"
echo "Info: [ANNOUNCE] template generated in \"$tar_name.announce\" file."
echo " Please pgp sign and send it."
# --------- Successful completion --------------------------
cd $top_src
return 0
}
#------------------------------------------------------------------------------
# Function: usage
#------------------------------------------------------------------------------
# Displays the script usage and exits successfully
#
usage() {
basename="`expr "//$0" : '.*/\([^/]*\)'`"
cat <<HELP
Usage: $basename [options] path...
Where "path" is a relative path to a git module, including '.'.
Options:
--dry-run Does everything except tagging and uploading tarballs
--force Force overwriting an existing release
--help Display this help and exit successfully
--no-quit Do not quit after error; just print error message
HELP
}
#------------------------------------------------------------------------------
# Script main line
#------------------------------------------------------------------------------
#
# Choose which grep program to use (on Solaris, must be gnu grep)
if [ "x$GREP" = "x" ] ; then
if [ -x /usr/gnu/bin/grep ] ; then
GREP=/usr/gnu/bin/grep
else
GREP=grep
fi
fi
# Process command line args
while [ $# != 0 ]
do
case $1 in
# Does everything except uploading tarball
--dry-run)
DRY_RUN=yes
;;
# Force overwriting an existing release
# Use only if nothing changed in the git repo
--force)
FORCE=yes
;;
# Display this help and exit successfully
--help)
usage
exit 0
;;
# Do not quit after error; just print error message
--no-quit)
NO_QUIT=yes
;;
--*)
echo ""
echo "Error: unknown option: $1"
echo ""
usage
exit 1
;;
-*)
echo ""
echo "Error: unknown option: $1"
echo ""
usage
exit 1
;;
*)
if [ x"${MODFILE}" != x ]; then
echo ""
echo "Error: specifying both modules and --modfile is not permitted"
echo ""
usage
exit 1
fi
INPUT_MODULES="${INPUT_MODULES} $1"
;;
esac
shift
done
# If no modules specified (blank cmd line) display help
check_modules_specification
# Read the module file and normalize input in INPUT_MODULES
read_modfile
# Loop through each module to release
# Exit on error if --no-quit no specified
process_modules
# Print the epilog with final status
print_epilog

View File

@@ -1,13 +0,0 @@
# This is the build script used for
# https://semaphoreci.com/libratbag/libratbag/
#
# semaphore uses this script directly, use the following two commands as
# semaphore's build script:
# curl https://raw.githubusercontent.com/libratbag/libratbag/master/semaphore/semaphore-build-script.sh > build-script.sh
# sh ./build-script.sh
sudo apt-get update
sudo apt-get install -y valgrind check libevdev-dev libudev-dev doxygen graphviz
autoreconf -ivf
./configure --prefix=$PWD/_build --with-udev-base-dir=$PWD/_build
DISTCHECK_CONFIGURE_FLAGS="--with-udev-base-dir=$PWD/_build" make distcheck

View File

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

View File

@@ -36,6 +36,7 @@
#define ETEKCITY_PROFILE_MAX 4
#define ETEKCITY_BUTTON_MAX 10
#define ETEKCITY_NUM_DPI 6
#define ETEKCITY_LED 0
#define ETEKCITY_REPORT_ID_CONFIGURE_PROFILE 4
#define ETEKCITY_REPORT_ID_PROFILE 5
@@ -430,6 +431,7 @@ etekcity_read_button(struct ratbag_button *button)
{
const struct ratbag_button_action *action;
struct ratbag_device *device;
struct ratbag_button_macro *m;
struct etekcity_macro *macro;
struct etekcity_data *drv_data;
uint8_t *buf;
@@ -464,7 +466,7 @@ etekcity_read_button(struct ratbag_button *button)
button->index, button->profile->index,
rc < 0 ? strerror(-rc) : "not read enough", rc);
} else {
ratbag_button_set_macro(button, macro->name);
m = ratbag_button_macro_new(macro->name);
log_raw(device->ratbag,
"macro on button %d of profile %d is named '%s', and contains %d events:\n",
button->index, button->profile->index,
@@ -472,7 +474,7 @@ etekcity_read_button(struct ratbag_button *button)
for (j = 0; j < macro->length; j++) {
unsigned int keycode = ratbag_hidraw_get_keycode_from_keyboard_usage(device,
macro->keys[j].keycode);
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
j,
macro->keys[j].flag & 0x80 ? RATBAG_MACRO_EVENT_KEY_RELEASED : RATBAG_MACRO_EVENT_KEY_PRESSED,
keycode);
@@ -481,6 +483,8 @@ etekcity_read_button(struct ratbag_button *button)
libevdev_event_code_get_name(EV_KEY, keycode),
macro->keys[j].flag & 0x80 ? "released" : "pressed");
}
ratbag_button_copy_macro(button, m);
ratbag_button_macro_unref(m);
}
msleep(10);
}
@@ -633,13 +637,8 @@ etekcity_probe(struct ratbag_device *device)
int active_idx;
rc = ratbag_open_hidraw(device);
if (rc) {
log_error(device->ratbag,
"Can't open corresponding hidraw node: '%s' (%d)\n",
strerror(-rc),
rc);
return -ENODEV;
}
if (rc)
return rc;
if (!ratbag_hidraw_has_report(device, ETEKCITY_REPORT_ID_KEY_MAPPING)) {
ratbag_close_hidraw(device);
@@ -653,22 +652,27 @@ etekcity_probe(struct ratbag_device *device)
rc = ratbag_hidraw_raw_request(device, ETEKCITY_REPORT_ID_SPEED_SETTING,
drv_data->speed_setting, ETEKCITY_REPORT_SIZE_SPEED_SETTING,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (rc)
return rc;
log_debug(device->ratbag, "device is at %d ms of latency\n", drv_data->speed_setting[2]);
/* profiles are 0-indexed */
ratbag_device_init_profiles(device,
ETEKCITY_PROFILE_MAX + 1,
ETEKCITY_NUM_DPI,
ETEKCITY_BUTTON_MAX + 1);
ETEKCITY_BUTTON_MAX + 1,
ETEKCITY_LED);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
active_idx = etekcity_current_profile(device);
if (active_idx < 0) {
log_error(device->ratbag,
"Can't talk to the mouse: '%s' (%d)\n",
strerror(-rc),
rc);
strerror(-active_idx),
active_idx);
rc = -ENODEV;
goto err;
}

1513
src/driver-gskill.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -35,8 +35,6 @@
#include "config.h"
#define USB_VENDOR_ID_LOGITECH 0x046d
#include <linux/types.h>
#include <errno.h>
#include <stdbool.h>
@@ -45,6 +43,7 @@
#include <unistd.h>
#include "hidpp10.h"
#include "usb-ids.h"
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
@@ -76,6 +75,7 @@ hidpp10drv_read_macro(struct ratbag_button *button,
union hidpp10_button *binding)
{
struct ratbag_device *device = button->profile->device;
struct ratbag_button_macro *m;
const char *name;
union hidpp10_macro_data *macro;
unsigned int i, keycode;
@@ -84,13 +84,13 @@ hidpp10drv_read_macro(struct ratbag_button *button,
macro = profile->macros[binding->macro.address];
name = binding->macro.address > 1 ? (const char *)profile->macro_names[binding->macro.address - 2] : "";
ratbag_button_set_macro(button, name);
i = 0;
m = ratbag_button_macro_new(name);
while (macro && macro->any.type != HIDPP10_MACRO_END && i < MAX_MACRO_EVENTS) {
switch (macro->any.type) {
case HIDPP10_MACRO_DELAY:
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_WAIT,
macro->delay.time);
@@ -99,11 +99,11 @@ hidpp10drv_read_macro(struct ratbag_button *button,
case HIDPP10_MACRO_KEY_PRESS:
keycode = ratbag_hidraw_get_keycode_from_keyboard_usage(device, macro->key.key);
if (!delay)
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_WAIT,
1);
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_KEY_PRESSED,
keycode);
@@ -112,11 +112,11 @@ hidpp10drv_read_macro(struct ratbag_button *button,
case HIDPP10_MACRO_KEY_RELEASE:
keycode = ratbag_hidraw_get_keycode_from_keyboard_usage(device, macro->key.key);
if (!delay)
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_WAIT,
1);
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_KEY_RELEASED,
keycode);
@@ -125,11 +125,11 @@ hidpp10drv_read_macro(struct ratbag_button *button,
case HIDPP10_MACRO_MOD_PRESS:
keycode = hidpp10drv_read_macro_modifier(device, macro);
if (!delay)
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_WAIT,
1);
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_KEY_PRESSED,
keycode);
@@ -138,11 +138,11 @@ hidpp10drv_read_macro(struct ratbag_button *button,
case HIDPP10_MACRO_MOD_RELEASE:
keycode = hidpp10drv_read_macro_modifier(device, macro);
if (!delay)
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_WAIT,
1);
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_KEY_RELEASED,
keycode);
@@ -151,6 +151,9 @@ hidpp10drv_read_macro(struct ratbag_button *button,
}
macro++;
}
ratbag_button_copy_macro(button, m);
ratbag_button_macro_unref(m);
}
static void
@@ -248,6 +251,27 @@ hidpp10drv_read_button(struct ratbag_button *button)
}
hidpp10drv_map_button(device, hidpp10, button);
break;
case HIDPP10_PROFILE_G9:
switch (button->index) {
case 0: type = RATBAG_BUTTON_TYPE_LEFT; break;
case 1: type = RATBAG_BUTTON_TYPE_RIGHT; break;
case 2: type = RATBAG_BUTTON_TYPE_MIDDLE; break;
case 3: type = RATBAG_BUTTON_TYPE_THUMB; break;
case 4: type = RATBAG_BUTTON_TYPE_THUMB2; break;
case 5: type = RATBAG_BUTTON_TYPE_UNKNOWN; break;
case 6: type = RATBAG_BUTTON_TYPE_WHEEL_LEFT; break;
case 7: type = RATBAG_BUTTON_TYPE_WHEEL_RIGHT; break;
case 8: type = RATBAG_BUTTON_TYPE_RESOLUTION_UP; break;
case 9: type = RATBAG_BUTTON_TYPE_RESOLUTION_DOWN; break;
case 10:
case 11:
case 12: /* these don't actually exist on the device */
type = RATBAG_BUTTON_TYPE_UNKNOWN; break;
default:
break;
}
hidpp10drv_map_button(device, hidpp10, button);
break;
default:
switch (button->index) {
case 0: type = RATBAG_BUTTON_TYPE_LEFT; break;
@@ -266,27 +290,22 @@ hidpp10drv_read_button(struct ratbag_button *button)
}
static int
hidpp10drv_write_button(struct ratbag_button *button,
hidpp10drv_write_button(struct hidpp10_profile *profile,
struct ratbag_button *button,
const struct ratbag_button_action *action)
{
struct ratbag_device *device = button->profile->device;
struct hidpp10drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp10_device *hidpp10 = drv_data->dev;
struct hidpp10_profile profile;
uint8_t code;
int ret;
if (hidpp10->profile_type == HIDPP10_PROFILE_UNKNOWN)
return -ENOTSUP;
ret = hidpp10_get_profile(hidpp10, button->profile->index, &profile);
if (ret)
return ret;
switch (action->type) {
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
profile.buttons[button->index].button.type = PROFILE_BUTTON_TYPE_BUTTON;
profile.buttons[button->index].button.button = action->action.button;
profile->buttons[button->index].button.type = PROFILE_BUTTON_TYPE_BUTTON;
profile->buttons[button->index].button.button = action->action.button;
break;
case RATBAG_BUTTON_ACTION_TYPE_KEY:
code = ratbag_hidraw_get_keyboard_usage_from_keycode(device, action->action.key.key);
@@ -295,26 +314,26 @@ hidpp10drv_write_button(struct ratbag_button *button,
if (code == 0)
return -EINVAL;
profile.buttons[button->index].consumer_control.type = PROFILE_BUTTON_TYPE_CONSUMER_CONTROL;
profile.buttons[button->index].consumer_control.consumer_control = code;
profile->buttons[button->index].consumer_control.type = PROFILE_BUTTON_TYPE_CONSUMER_CONTROL;
profile->buttons[button->index].consumer_control.consumer_control = code;
} else {
profile.buttons[button->index].keys.type = PROFILE_BUTTON_TYPE_KEYS;
profile.buttons[button->index].keys.key = code;
profile->buttons[button->index].keys.type = PROFILE_BUTTON_TYPE_KEYS;
profile->buttons[button->index].keys.key = code;
}
break;
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
code = hidpp10_onboard_profiles_get_code_from_special(action->action.special);
if (code == 0)
return -EINVAL;
profile.buttons[button->index].special.type = PROFILE_BUTTON_TYPE_SPECIAL;
profile.buttons[button->index].special.special = code;
profile->buttons[button->index].special.type = PROFILE_BUTTON_TYPE_SPECIAL;
profile->buttons[button->index].special.special = code;
break;
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
default:
return -ENOTSUP;
}
return hidpp10_set_profile(drv_data->dev, button->profile->index, &profile);
return 0;
}
static int
@@ -332,12 +351,12 @@ hidpp10drv_read_profile(struct ratbag_profile *profile, unsigned int index)
struct ratbag_device *device = profile->device;
struct hidpp10drv_data *drv_data;
struct hidpp10_device *hidpp10;
struct hidpp10_profile p;
struct hidpp10_profile p = {0};
struct ratbag_resolution *res;
int rc;
unsigned int i;
uint16_t xres, yres;
int8_t idx;
uint8_t idx;
drv_data = ratbag_get_drv_data(device);
hidpp10 = drv_data->dev;
@@ -345,6 +364,8 @@ hidpp10drv_read_profile(struct ratbag_profile *profile, unsigned int index)
if (rc)
return;
profile->is_enabled = p.enabled;
rc = hidpp10_get_current_profile(hidpp10, &idx);
if (rc == 0 && (unsigned int)idx == profile->index)
profile->is_active = true;
@@ -353,7 +374,12 @@ hidpp10drv_read_profile(struct ratbag_profile *profile, unsigned int index)
if (rc)
xres = 0xffff;
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;
res = ratbag_resolution_init(profile, i,
p.dpi_modes[i].xres,
p.dpi_modes[i].yres,
@@ -364,8 +390,15 @@ hidpp10drv_read_profile(struct ratbag_profile *profile, unsigned int index)
res->dpi_x == xres &&
res->dpi_y == yres)
res->is_active = true;
if (i == p.default_dpi_mode)
if (i == 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);
}
}
@@ -376,22 +409,18 @@ hidpp10drv_write_profile(struct ratbag_profile *profile)
}
static int
hidpp10drv_write_resolution_dpi(struct ratbag_resolution *resolution,
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;
struct hidpp10_profile p;
unsigned int index;
uint16_t cur_dpi_x, cur_dpi_y;
int rc;
rc = hidpp10_get_profile(hidpp10, profile->index, &p);
if (rc)
return rc;
/* store the current resolution */
rc = hidpp10_get_current_resolution(hidpp10, &cur_dpi_x, &cur_dpi_y);
if (rc)
@@ -406,13 +435,8 @@ hidpp10drv_write_resolution_dpi(struct ratbag_resolution *resolution,
/* retrieve which resolution is asked to be changed */
index = resolution - profile->resolution.modes;
p.dpi_modes[index].xres = dpi_x;
p.dpi_modes[index].yres = dpi_y;
/* this effectively switches the resolution to the default in the profile */
rc = hidpp10_set_profile(drv_data->dev, profile->index, &p);
if (rc)
return rc;
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);
@@ -422,29 +446,32 @@ static int
hidpp10drv_fill_from_profile(struct ratbag_device *device, struct hidpp10_device *dev)
{
int rc;
struct hidpp10_profile profile;
struct hidpp10_directory directory[16];
int count;
count = hidpp10_get_profile_directory(dev, directory, ARRAY_LENGTH(directory));
if (count < 0)
return count;
struct hidpp10_profile profile = {0};
unsigned int i;
/* We don't know the HID++1.0 requests to query for buttons, etc.
* Simply get the first profile and fill the device information in
* from that.
* Simply get the first enabled profile and fill in the device
* information from that.
*/
rc = hidpp10_get_profile(dev, 0, &profile);
if (rc)
return rc;
for (i = 0; i < dev->profile_count; i++) {
rc = hidpp10_get_profile(dev, i, &profile);
if (rc)
return rc;
if (profile.enabled)
break;
}
ratbag_device_init_profiles(device,
count,
dev->profile_count,
profile.num_dpi_modes,
profile.num_buttons);
profile.num_buttons,
profile.num_leds);
if (dev->profile_type != HIDPP10_PROFILE_UNKNOWN) {
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_PROFILE);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_PROFILE);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_DISABLE_PROFILE);
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);
}
@@ -466,6 +493,60 @@ hidpp10_log(void *userdata, enum hidpp_log_priority priority, const char *format
log_msg_va(device->ratbag, priority, format, args);
}
static int
hidpp10drv_commit(struct ratbag_device *device)
{
struct hidpp10drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp10_device *dev = drv_data->dev;
struct ratbag_profile *profile;
struct ratbag_button *button;
struct ratbag_resolution *resolution;
struct hidpp10_profile p;
int rc;
unsigned int i;
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
continue;
rc = hidpp10_get_profile(dev, profile->index, &p);
if (rc)
return rc;
p.enabled = profile->is_enabled;
for (i = 0; i < profile->resolution.num_modes; i++) {
resolution = &profile->resolution.modes[i];
rc = hidpp10drv_write_resolution_dpi(&p,
resolution,
resolution->dpi_x,
resolution->dpi_y);
if (rc)
return RATBAG_ERROR_DEVICE;
}
list_for_each(button, &profile->buttons, link) {
struct ratbag_button_action action = button->action;
if (!button->dirty)
continue;
rc = hidpp10drv_write_button(&p, button, &action);
if (rc)
return RATBAG_ERROR_DEVICE;
}
if (dev->profile_type != HIDPP10_PROFILE_UNKNOWN) {
rc = hidpp10_set_profile(dev, profile->index, &p);
if (rc)
return RATBAG_ERROR_DEVICE;
}
}
return RATBAG_SUCCESS;
}
static int
hidpp10drv_probe(struct ratbag_device *device)
{
@@ -477,18 +558,11 @@ hidpp10drv_probe(struct ratbag_device *device)
const char *prop;
int device_idx = HIDPP_WIRED_DEVICE_IDX;
int nread = 0;
unsigned int profile_count = 1;
rc = ratbag_find_hidraw(device, hidpp10drv_test_hidraw);
if (rc == -ENODEV) {
return rc;
} else if (rc) {
log_error(device->ratbag,
"Can't open corresponding hidraw node: '%s' (%d)\n",
strerror(-rc),
rc);
rc = -ENODEV;
if (rc)
goto err;
}
drv_data = zalloc(sizeof(*drv_data));
hidpp_device_init(&base, device->hidraw.fd);
@@ -502,6 +576,10 @@ hidpp10drv_probe(struct ratbag_device *device)
type = HIDPP10_PROFILE_G700;
else if (strcasecmp("G9", prop) == 0)
type = HIDPP10_PROFILE_G9;
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_PROFILE_COUNT");
if (prop)
profile_count = atoi(prop);
}
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_INDEX");
@@ -523,7 +601,7 @@ hidpp10drv_probe(struct ratbag_device *device)
* If there is a special need like for G700(s), we can pass a
* udev prop RATBAG_HIDPP10_INDEX.
*/
dev = hidpp10_device_new(&base, device_idx, type);
dev = hidpp10_device_new(&base, device_idx, type, profile_count);
if (!dev) {
log_error(device->ratbag,
@@ -561,6 +639,10 @@ hidpp10drv_probe(struct ratbag_device *device)
device->name);
}
rc = hidpp10_device_read_profiles(dev);
if (rc)
goto err;
drv_data->dev = dev;
ratbag_set_drv_data(device, drv_data);
@@ -568,7 +650,7 @@ hidpp10drv_probe(struct ratbag_device *device)
/* Fall back to something that every mouse has */
struct ratbag_profile *profile;
ratbag_device_init_profiles(device, 1, 1, 3);
ratbag_device_init_profiles(device, 1, 1, 3, 0);
profile = ratbag_device_get_profile(device, 0);
profile->is_active = true;
ratbag_profile_unref(profile);
@@ -609,6 +691,5 @@ struct ratbag_driver hidpp10_driver = {
.write_profile = hidpp10drv_write_profile,
.set_active_profile = hidpp10drv_set_current_profile,
.read_button = hidpp10drv_read_button,
.write_button = hidpp10drv_write_button,
.write_resolution_dpi = hidpp10drv_write_resolution_dpi,
.commit = hidpp10drv_commit,
};

View File

@@ -51,7 +51,8 @@
#define HIDPP_CAP_BUTTON_KEY_1b04 (1 << 2)
#define HIDPP_CAP_BATTERY_LEVEL_1000 (1 << 3)
#define HIDPP_CAP_KBD_REPROGRAMMABLE_KEYS_1b00 (1 << 4)
#define HIDPP_CAP_ONBOARD_PROFILES_8100 (1 << 5)
#define HIDPP_CAP_COLOR_LED_EFFECTS_8070 (1 << 5)
#define HIDPP_CAP_ONBOARD_PROFILES_8100 (1 << 6)
struct hidpp20drv_data {
struct hidpp20_device *dev;
@@ -61,10 +62,12 @@ struct hidpp20drv_data {
unsigned num_controls;
struct hidpp20_control_id *controls;
struct hidpp20_profiles *profiles;
struct hidpp20_color_led_zone_info *led_infos;
unsigned int num_profiles;
unsigned int num_resolutions;
unsigned int num_buttons;
unsigned int num_leds;
};
static void
@@ -117,38 +120,43 @@ hidpp20drv_read_macro_key_8100(struct ratbag_device *device, union hidpp20_macro
return ratbag_hidraw_get_keycode_from_keyboard_usage(device, macro->key.key);
}
static void
static int
hidpp20drv_read_macro_8100(struct ratbag_button *button,
struct hidpp20_profile *profile,
union hidpp20_button_binding *binding)
{
struct ratbag_device *device = button->profile->device;
struct ratbag_button_macro *m;
union hidpp20_macro_data *macro;
unsigned int i, keycode;
bool delay = true;
macro = profile->macros[binding->macro.page];
ratbag_button_set_macro(button, "macro");
if (!macro)
return -EINVAL;
i = 0;
m = ratbag_button_macro_new("macro");
while (macro && macro->any.type != HIDPP20_MACRO_END && i < MAX_MACRO_EVENTS) {
switch (macro->any.type) {
case HIDPP20_MACRO_DELAY:
ratbag_button_set_macro_event(button,
i++,
RATBAG_MACRO_EVENT_WAIT,
macro->delay.time);
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_WAIT,
macro->delay.time);
delay = true;
break;
case HIDPP20_MACRO_KEY_PRESS:
keycode = hidpp20drv_read_macro_key_8100(device, macro);
if (!delay)
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_WAIT,
1);
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_KEY_PRESSED,
keycode);
@@ -157,11 +165,11 @@ hidpp20drv_read_macro_8100(struct ratbag_button *button,
case HIDPP20_MACRO_KEY_RELEASE:
keycode = hidpp20drv_read_macro_key_8100(device, macro);
if (!delay)
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_WAIT,
1);
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
i++,
RATBAG_MACRO_EVENT_KEY_RELEASED,
keycode);
@@ -170,6 +178,11 @@ hidpp20drv_read_macro_8100(struct ratbag_button *button,
}
macro++;
}
ratbag_button_copy_macro(button, m);
ratbag_button_macro_unref(m);
return 0;
}
static void
@@ -178,6 +191,7 @@ hidpp20drv_read_button_8100(struct ratbag_button *button)
struct ratbag_device *device = button->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_profile *profile;
int rc;
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100))
return;
@@ -208,10 +222,13 @@ hidpp20drv_read_button_8100(struct ratbag_button *button)
button->action.action.special = hidpp20_onboard_profiles_get_special(profile->buttons[button->index].special.special);
break;
case HIDPP20_BUTTON_MACRO:
hidpp20drv_read_macro_8100(button, profile, &profile->buttons[button->index]);
rc = hidpp20drv_read_macro_8100(button, profile, &profile->buttons[button->index]);
if (rc)
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
break;
default:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
break;
}
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
@@ -227,6 +244,44 @@ hidpp20drv_read_button(struct ratbag_button *button)
hidpp20drv_read_button_8100(button);
}
static void
hidpp20drv_read_led(struct ratbag_led *led)
{
struct ratbag_device *device = led->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_profile *profile;
struct hidpp20_color_led_zone_info *led_info;
struct hidpp20_led *h_led;
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return;
led_info = &drv_data->led_infos[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
switch (h_led->mode) {
case HIDPP20_LED_ON:
led->mode = RATBAG_LED_ON;
break;
case HIDPP20_LED_CYCLE:
led->mode = RATBAG_LED_CYCLE;
break;
case HIDPP20_LED_BREATHING:
led->mode = RATBAG_LED_BREATHING;
break;
default:
led->mode = RATBAG_LED_OFF;
break;
}
led->type = hidpp20_8070_get_location_mapping(led_info->location);
led->color.red = h_led->color.red;
led->color.green = h_led->color.green;
led->color.blue = h_led->color.blue;
led->hz = h_led->period; /* FIXME: should be ceil(1000.0 / h_led->period), but that would be very small */
led->brightness = h_led->brightness * 255 / 100;
}
static int
hidpp20drv_write_button_1b04(struct ratbag_button *button,
const struct ratbag_button_action *action)
@@ -245,12 +300,12 @@ hidpp20drv_write_button_1b04(struct ratbag_button *button,
if (!mapping)
return -EINVAL;
control->reporting.divert = 1;
control->reporting.divert = 0;
control->reporting.remapped = mapping;
control->reporting.updated = 1;
rc = hidpp20_special_key_mouse_set_control(drv_data->dev, control);
if (rc == ERR_INVALID_ADDRESS)
if (rc == HIDPP20_ERR_INVALID_ARGUMENT)
return -EINVAL;
if (rc)
@@ -263,8 +318,8 @@ hidpp20drv_write_button_1b04(struct ratbag_button *button,
}
static int
hidpp20drv_write_button_8100(struct ratbag_button *button,
const struct ratbag_button_action *action)
hidpp20drv_update_button_8100(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
struct ratbag_device *device = button->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
@@ -315,14 +370,14 @@ hidpp20drv_write_button_8100(struct ratbag_button *button,
}
static int
hidpp20drv_write_button(struct ratbag_button *button,
hidpp20drv_update_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
struct ratbag_device *device = button->profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
return hidpp20drv_write_button_8100(button, action);
return hidpp20drv_update_button_8100(button, action);
if (drv_data->capabilities & HIDPP_CAP_BUTTON_KEY_1b04)
return hidpp20drv_write_button_1b04(button, action);
@@ -330,6 +385,48 @@ hidpp20drv_write_button(struct ratbag_button *button,
return -ENOTSUP;
}
static int
hidpp20drv_update_led(struct ratbag_led *led)
{
struct ratbag_profile *profile = led->profile;
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_profile *h_profile;
struct hidpp20_led *h_led;
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return -ENOTSUP;
h_profile = &drv_data->profiles->profiles[profile->index];
h_led = &(h_profile->leds[led->index]);
if (!h_led)
return -EINVAL;
switch (led->mode) {
case RATBAG_LED_ON:
h_led->mode = HIDPP20_LED_ON;
break;
case RATBAG_LED_CYCLE:
h_led->mode = HIDPP20_LED_CYCLE;
break;
case RATBAG_LED_BREATHING:
h_led->mode = HIDPP20_LED_BREATHING;
break;
default:
h_led->mode = HIDPP20_LED_OFF;
break;
}
h_led->color.red = led->color.red;
h_led->color.green = led->color.green;
h_led->color.blue = led->color.blue;
h_led->period = led->hz; /* FIXME: should be 1000 / led->hz, but that would be very small */
h_led->brightness = led->brightness * 100 / 255;
return RATBAG_SUCCESS;
}
static int
hidpp20drv_current_profile(struct ratbag_device *device)
{
@@ -430,10 +527,17 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
rc = hidpp20drv_read_resolution_dpi_2201(device);
if (rc < 0)
return rc;
for (i = 0; i < profile->resolution.num_modes; i++) {
int dpi = drv_data->sensors[i].dpi;
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];
/* FIXME: retrieve the refresh rate */
res = ratbag_resolution_init(profile, i, dpi, dpi, 0);
res = ratbag_resolution_init(profile, i, sensor->dpi, sensor->dpi, 0);
ratbag_resolution_set_range(res, sensor->dpi_min, sensor->dpi_max);
/* FIXME: we mark all resolutions as active because
* they are from different sensors */
@@ -447,8 +551,8 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
}
static int
hidpp20drv_write_resolution_dpi_8100(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
hidpp20drv_update_resolution_dpi_8100(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_device *device = profile->device;
@@ -463,12 +567,12 @@ hidpp20drv_write_resolution_dpi_8100(struct ratbag_resolution *resolution,
h_profile = &drv_data->profiles->profiles[profile->index];
h_profile->dpi[index] = dpi;
return hidpp20_onboard_profiles_write(drv_data->dev, profile->index, drv_data->profiles);
return RATBAG_SUCCESS;
}
static int
hidpp20drv_write_resolution_dpi(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_device *device = profile->device;
@@ -478,7 +582,7 @@ hidpp20drv_write_resolution_dpi(struct ratbag_resolution *resolution,
int dpi = dpi_x; /* dpi_x == dpi_y if we don't have the individual resolution cap */
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
return hidpp20drv_write_resolution_dpi_8100(resolution, dpi_x, dpi_y);
return hidpp20drv_update_resolution_dpi_8100(resolution, dpi_x, dpi_y);
if (!(drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201))
return -ENOTSUP;
@@ -556,6 +660,29 @@ hidpp20drv_read_kbd_reprogrammable_key(struct ratbag_device *device)
return rc;
}
static int
hidpp20drv_read_color_leds(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
int rc;
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return 0;
free(drv_data->led_infos);
drv_data->led_infos = NULL;
drv_data->num_leds = 0;
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos);
if (rc > 0) {
drv_data->num_leds = rc;
rc = 0;
}
return rc;
}
static int
hidpp20drv_read_onboard_profile(struct ratbag_device *device, unsigned index)
{
@@ -579,7 +706,10 @@ hidpp20drv_read_profile_8100(struct ratbag_profile *profile, unsigned int index)
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag_resolution *res;
struct hidpp20_profile *p;
unsigned i, dpi;
unsigned int i;
int dpi_index = 0xff;
profile->is_enabled = drv_data->profiles->profiles[index].enabled;
hidpp20drv_read_onboard_profile(device, profile->index);
@@ -587,21 +717,36 @@ hidpp20drv_read_profile_8100(struct ratbag_profile *profile, unsigned int index)
if ((int)index == hidpp20drv_current_profile(device))
profile->is_active = true;
dpi = ratbag_resolution_get_dpi(profile->resolution.modes);
if (profile->is_active)
dpi_index = hidpp20_onboard_profiles_get_current_dpi_index(drv_data->dev);
if (dpi_index < 0)
dpi_index = 0xff;
p = &drv_data->profiles->profiles[index];
for (i = 0; i < profile->resolution.num_modes; i++) {
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);
if (profile->is_active &&
res->dpi_x == dpi)
i == (unsigned int)dpi_index)
res->is_active = true;
if (i == p->default_dpi)
if (i == 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);
}
}
@@ -620,12 +765,6 @@ hidpp20drv_read_profile(struct ratbag_profile *profile, unsigned int index)
profile->is_active = (index == 0);
}
static int
hidpp20drv_write_profile(struct ratbag_profile *profile)
{
return 0;
}
static int
hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
{
@@ -639,6 +778,8 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
&feature_index,
&feature_type,
&feature_version);
if (rc < 0)
return rc;
switch (feature) {
case HIDPP_PAGE_ROOT:
@@ -647,22 +788,25 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
break;
case HIDPP_PAGE_MOUSE_POINTER_BASIC: {
drv_data->capabilities |= HIDPP_CAP_RESOLUTION_2200;
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_RESOLUTION);
break;
}
case HIDPP_PAGE_ADJUSTABLE_DPI: {
log_debug(ratbag, "device has adjustable dpi\n");
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION);
drv_data->capabilities |= HIDPP_CAP_SWITCHABLE_RESOLUTION_2201;
/* we read the profile once to get the correct number of
* supported resolutions. */
rc = hidpp20drv_read_resolution_dpi_2201(device);
if (rc < 0)
return rc;
return 0; /* this is not a hard failure */
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_RESOLUTION);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION);
drv_data->capabilities |= HIDPP_CAP_SWITCHABLE_RESOLUTION_2201;
break;
}
case HIDPP_PAGE_SPECIAL_KEYS_BUTTONS: {
log_debug(ratbag, "device has programmable keys/buttons\n");
drv_data->capabilities |= HIDPP_CAP_BUTTON_KEY_1b04;
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_KEY);
/* we read the profile once to get the correct number of
* supported buttons. */
@@ -701,11 +845,22 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
}
case HIDPP_PAGE_COLOR_LED_EFFECTS: {
log_debug(ratbag, "device has color effects\n");
drv_data->capabilities |= HIDPP_CAP_COLOR_LED_EFFECTS_8070;
/* we read the profile once to get the correct number of
* supported leds. */
if (hidpp20drv_read_color_leds(device))
return 0;
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_LED);
device->num_leds = drv_data->num_leds;
break;
}
case HIDPP_PAGE_ONBOARD_PROFILES: {
log_debug(ratbag, "device has onboard profiles\n");
drv_data->capabilities |= HIDPP_CAP_ONBOARD_PROFILES_8100;
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_PROFILE);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_DISABLE_PROFILE);
rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles);
if (rc < 0)
@@ -714,6 +869,7 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
drv_data->num_profiles = drv_data->profiles->num_profiles;
drv_data->num_resolutions = drv_data->profiles->num_modes;
drv_data->num_buttons = drv_data->profiles->num_buttons;
drv_data->num_leds = drv_data->profiles->num_leds;
break;
}
@@ -727,6 +883,68 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
return 0;
}
static int
hidpp20drv_commit(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag_profile *profile;
struct ratbag_button *button;
struct ratbag_led *led;
struct ratbag_resolution *resolution;
int rc;
unsigned int i, dpi_index = 0;
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
continue;
drv_data->profiles->profiles[profile->index].enabled = profile->is_enabled;
for (i = 0; i < profile->resolution.num_modes; i++) {
resolution = &profile->resolution.modes[i];
if (resolution->is_active)
dpi_index = i;
rc = hidpp20drv_update_resolution_dpi(resolution,
resolution->dpi_x,
resolution->dpi_y);
if (rc)
return RATBAG_ERROR_DEVICE;
}
list_for_each(button, &profile->buttons, link) {
struct ratbag_button_action action = button->action;
if (!button->dirty)
continue;
rc = hidpp20drv_update_button(button, &action);
if (rc)
return RATBAG_ERROR_DEVICE;
}
list_for_each(led, &profile->leds, link) {
if (!led->dirty)
continue;
rc = hidpp20drv_update_led(led);
if (rc)
return RATBAG_ERROR_DEVICE;
}
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
hidpp20_onboard_profiles_write(drv_data->dev,
profile->index,
drv_data->profiles);
if (profile->is_active)
hidpp20_onboard_profiles_set_current_dpi_index(drv_data->dev,
dpi_index);
}
}
return RATBAG_SUCCESS;
}
static int
hidpp20drv_20_probe(struct ratbag_device *device)
{
@@ -734,6 +952,7 @@ hidpp20drv_20_probe(struct ratbag_device *device)
struct hidpp20_device *dev = drv_data->dev;
struct hidpp20_feature *feature_list = dev->feature_list;
unsigned int i;
int rc;
log_raw(device->ratbag,
"'%s' has %d features\n",
@@ -744,7 +963,9 @@ hidpp20drv_20_probe(struct ratbag_device *device)
log_raw(device->ratbag, "Init feature %s (0x%04x) \n",
hidpp20_feature_get_name(feature_list[i].feature),
feature_list[i].feature);
hidpp20drv_init_feature(device, feature_list[i].feature);
rc = hidpp20drv_init_feature(device, feature_list[i].feature);
if (rc < 0)
return rc;
}
return 0;
@@ -765,6 +986,27 @@ hidpp20_log(void *userdata, enum hidpp_log_priority priority, const char *format
log_msg_va(device->ratbag, priority, format, args);
}
static void
hidpp20drv_remove(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data;
if (!device)
return;
drv_data = ratbag_get_drv_data(device);
ratbag_close_hidraw(device);
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(drv_data->profiles);
free(drv_data->controls);
free(drv_data->sensors);
if (drv_data->dev)
hidpp20_device_destroy(drv_data->dev);
free(drv_data);
}
static int
hidpp20drv_probe(struct ratbag_device *device)
{
@@ -772,28 +1014,42 @@ hidpp20drv_probe(struct ratbag_device *device)
struct hidpp20drv_data *drv_data;
struct hidpp_device base;
struct hidpp20_device *dev;
const char *prop;
int device_idx = HIDPP_RECEIVER_IDX;
int nread = 0;
rc = ratbag_find_hidraw(device, hidpp20drv_test_hidraw);
if (rc == -ENODEV) {
if (rc)
return rc;
} else if (rc) {
log_error(device->ratbag,
"Can't open corresponding hidraw node: '%s' (%d)\n",
strerror(-rc),
rc);
return -ENODEV;
}
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
hidpp_device_init(&base, device->hidraw.fd);
hidpp_device_set_log_handler(&base, hidpp20_log, HIDPP_LOG_PRIORITY_RAW, device);
dev = hidpp20_device_new(&base, HIDPP_RECEIVER_IDX);
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP20_INDEX");
if (prop) {
sscanf(prop, "%d%n", &device_idx, &nread);
if (!nread || (prop[nread]) != '\0' || device_idx < 0) {
log_error(device->ratbag,
"Error parsing RATBAG_HIDPP20_INDEX: '%s' for %s\n",
prop,
device->name);
device_idx = HIDPP_RECEIVER_IDX;
}
}
/* In the general case, we can treat all devices as wired devices
* here. If we talk to the correct hidraw device the kernel adjusts
* the device index for us, so even for unifying receiver devices
* we can just use 0xff as device index.
*
* If there is a special need like for G900, we can pass a
* udev prop RATBAG_HIDPP20_INDEX.
*/
dev = hidpp20_device_new(&base, device_idx);
if (!dev) {
log_error(device->ratbag,
"Failed to get HID++2.0 device for %s\n",
device->name);
rc = -ENODEV;
goto err;
}
@@ -805,50 +1061,32 @@ hidpp20drv_probe(struct ratbag_device *device)
drv_data->num_profiles = 1;
drv_data->num_resolutions = 1;
drv_data->num_buttons = 8;
drv_data->num_leds = 2;
if (dev->proto_major >= 2) {
rc = hidpp20drv_20_probe(device);
if (rc)
goto err;
}
rc = hidpp20drv_20_probe(device);
if (rc)
goto err;
ratbag_device_init_profiles(device,
drv_data->num_profiles,
drv_data->num_resolutions,
drv_data->num_buttons);
drv_data->num_buttons,
drv_data->num_leds);
return rc;
err:
free(drv_data);
ratbag_set_drv_data(device, NULL);
hidpp20drv_remove(device);
return rc;
}
static void
hidpp20drv_remove(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_device *dev = drv_data->dev;
ratbag_close_hidraw(device);
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(dev, drv_data->profiles);
free(drv_data->controls);
free(drv_data->sensors);
hidpp20_device_destroy(drv_data->dev);
free(drv_data);
}
struct ratbag_driver hidpp20_driver = {
.name = "Logitech HID++2.0",
.id = "hidpp20",
.probe = hidpp20drv_probe,
.remove = hidpp20drv_remove,
.commit = hidpp20drv_commit,
.read_profile = hidpp20drv_read_profile,
.write_profile = hidpp20drv_write_profile,
.set_active_profile = hidpp20drv_set_current_profile,
.read_button = hidpp20drv_read_button,
.write_button = hidpp20drv_write_button,
.write_resolution_dpi = hidpp20drv_write_resolution_dpi,
.read_led = hidpp20drv_read_led,
};

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

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

View File

@@ -36,6 +36,7 @@
#define ROCCAT_PROFILE_MAX 4
#define ROCCAT_BUTTON_MAX 23
#define ROCCAT_NUM_DPI 5
#define ROCCAT_LED_MAX 0
#define ROCCAT_MAX_RETRY_READY 10
@@ -302,6 +303,7 @@ roccat_wait_ready(struct ratbag_device *device)
return 2;
msleep(10);
count++;
}
return -ETIMEDOUT;
@@ -532,6 +534,7 @@ roccat_read_button(struct ratbag_button *button)
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
if (action && action->type == RATBAG_BUTTON_ACTION_TYPE_MACRO) {
struct ratbag_button_macro *m = NULL;
roccat_set_config_profile(device,
button->profile->index,
@@ -566,7 +569,7 @@ roccat_read_button(struct ratbag_button *button)
goto out_macro;
}
ratbag_button_set_macro(button, macro->name);
m = ratbag_button_macro_new(macro->name);
log_raw(device->ratbag,
"macro on button %d of profile %d is named '%s', and contains %d events:\n",
button->index, button->profile->index,
@@ -574,7 +577,7 @@ roccat_read_button(struct ratbag_button *button)
for (j = 0; j < macro->length; j++) {
unsigned int keycode = ratbag_hidraw_get_keycode_from_keyboard_usage(device,
macro->keys[j].keycode);
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
j * 2,
macro->keys[j].flag & 0x01 ? RATBAG_MACRO_EVENT_KEY_PRESSED : RATBAG_MACRO_EVENT_KEY_RELEASED,
keycode);
@@ -582,7 +585,7 @@ roccat_read_button(struct ratbag_button *button)
time = macro->keys[j].time;
else
time = macro->keys[j].flag & 0x01 ? 10 : 50;
ratbag_button_set_macro_event(button,
ratbag_button_macro_set_event(m,
j * 2 + 1,
RATBAG_MACRO_EVENT_WAIT,
time);
@@ -592,9 +595,11 @@ roccat_read_button(struct ratbag_button *button)
libevdev_event_code_get_name(EV_KEY, keycode),
macro->keys[j].flag & 0x80 ? "released" : "pressed");
}
ratbag_button_copy_macro(button, m);
}
out_macro:
msleep(10);
ratbag_button_macro_unref(m);
}
}
@@ -777,13 +782,8 @@ roccat_probe(struct ratbag_device *device)
int active_idx;
rc = ratbag_open_hidraw(device);
if (rc) {
log_error(device->ratbag,
"Can't open corresponding hidraw node: '%s' (%d)\n",
strerror(-rc),
rc);
return -ENODEV;
}
if (rc)
return rc;
if (!ratbag_hidraw_has_report(device, ROCCAT_REPORT_ID_KEY_MAPPING)) {
ratbag_close_hidraw(device);
@@ -797,16 +797,18 @@ roccat_probe(struct ratbag_device *device)
ratbag_device_init_profiles(device,
ROCCAT_PROFILE_MAX + 1,
ROCCAT_NUM_DPI,
ROCCAT_BUTTON_MAX + 1);
ROCCAT_BUTTON_MAX + 1,
ROCCAT_LED_MAX);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
active_idx = roccat_current_profile(device);
if (active_idx < 0) {
log_error(device->ratbag,
"Can't talk to the mouse: '%s' (%d)\n",
strerror(-rc),
rc);
strerror(-active_idx),
active_idx);
rc = -ENODEV;
goto err;
}

View File

@@ -24,6 +24,7 @@
#include "config.h"
#include <errno.h>
#include <libevdev/libevdev.h>
#include "libratbag-private.h"
#include "libratbag-test.h"
@@ -34,24 +35,41 @@ 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++) {
struct ratbag_resolution *res;
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;
res->capabilities = r->caps;
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
@@ -73,6 +91,111 @@ test_set_active_profile(struct ratbag_device *device, unsigned int index)
return 0;
}
static void
test_read_button(struct ratbag_button *button)
{
struct ratbag_device *device = button->profile->device;
struct ratbag_test_device *d = ratbag_get_drv_data(device);
struct ratbag_test_profile *p = &d->profiles[button->profile->index];
struct ratbag_test_button *b = &p->buttons[button->index];
struct ratbag_button_macro *m;
struct ratbag_test_macro_event *e;
int idx;
/* Only take the button types from the first profile */
button->type = d->profiles[0].buttons[button->index].button_type;
switch (b->action_type) {
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_BUTTON;
button->action.action.button = p->buttons[button->index].button;
break;
case RATBAG_BUTTON_ACTION_TYPE_KEY:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
button->action.action.key.key = p->buttons[button->index].key;
break;
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_MACRO;
m = ratbag_button_macro_new("test macro");
idx = 0;
ARRAY_FOR_EACH(b->macro, e) {
if (e->type == RATBAG_MACRO_EVENT_NONE)
break;
ratbag_button_macro_set_event(m, idx++, e->type, e->value);
}
ratbag_button_copy_macro(button, m);
ratbag_button_macro_unref(m);
break;
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL;
button->action.action.special = p->buttons[button->index].special;
break;
default:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
}
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
}
static void
test_read_led(struct ratbag_led *led)
{
struct ratbag_device *device = led->profile->device;
struct ratbag_test_device *d = ratbag_get_drv_data(device);
struct ratbag_test_profile *p = &d->profiles[led->profile->index];
struct ratbag_test_led t_led = p->leds[led->index];
switch (t_led.mode) {
case RATBAG_LED_ON:
led->mode = RATBAG_LED_ON;
break;
case RATBAG_LED_CYCLE:
led->mode = RATBAG_LED_CYCLE;
break;
case RATBAG_LED_BREATHING:
led->mode = RATBAG_LED_BREATHING;
break;
default:
led->mode = RATBAG_LED_OFF;
}
led->color.red = t_led.color.red;
led->color.green = t_led.color.green;
led->color.blue = t_led.color.blue;
led->hz = t_led.hz;
led->brightness = t_led.brightness;
}
static int
test_write_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
/* check if the device is still valid */
assert(ratbag_get_drv_data(button->profile->device) != NULL);
return 0;
}
static int
test_write_led(struct ratbag_led *led,
enum ratbag_led_mode mode,
struct ratbag_color color, unsigned int hz,
unsigned int brightness)
{
/* check if the device is still valid */
assert(ratbag_get_drv_data(led->profile->device) != NULL);
return 0;
}
static const char* test_get_svg_name(const struct ratbag_device *device)
{
struct ratbag_test_device *d = ratbag_get_drv_data(device);
return d->svg;
}
static int
test_fake_probe(struct ratbag_device *device)
{
@@ -80,17 +203,23 @@ test_fake_probe(struct ratbag_device *device)
}
static int
test_probe(struct ratbag_device *device, void *data)
test_probe(struct ratbag_device *device, const void *data)
{
struct ratbag_test_device *test_device = data;
struct ratbag_test_device *test_device;
test_device = zalloc(sizeof(*test_device));
memcpy(test_device, data, sizeof(*test_device));
ratbag_set_drv_data(device, test_device);
ratbag_device_init_profiles(device,
test_device->num_profiles,
test_device->num_resolutions,
test_device->num_buttons);
if (test_device->num_profiles > 1)
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);
}
return 0;
}
@@ -105,6 +234,7 @@ test_remove(struct ratbag_device *device)
if (d->destroyed)
d->destroyed(device, d->destroyed_data);
ratbag_set_drv_data(device, NULL);
free(d);
}
struct ratbag_driver test_driver = {
@@ -113,10 +243,13 @@ struct ratbag_driver test_driver = {
.probe = test_fake_probe,
.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 = NULL,
.write_button = NULL,
.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

@@ -36,7 +36,7 @@
#include "libratbag-private.h"
const char *hidpp_errors[0xFF] = {
const char *hidpp10_errors[0x100] = {
[0x00] = "ERR_SUCCESS",
[0x01] = "ERR_INVALID_SUBID",
[0x02] = "ERR_INVALID_ADDRESS",
@@ -50,7 +50,21 @@ const char *hidpp_errors[0xFF] = {
[0x0A] = "ERR_REQUEST_UNAVAILABLE",
[0x0B] = "ERR_INVALID_PARAM_VALUE",
[0x0C] = "ERR_WRONG_PIN_CODE",
[0x0D ... 0xFE] = NULL,
[0x0D ... 0xFF] = NULL,
};
const char *hidpp20_errors[0x100] = {
[0x00] = "ERR_NO_ERROR",
[0x01] = "ERR_UNKNOWN",
[0x02] = "ERR_INVALID_ARGUMENT",
[0x03] = "ERR_OUT_OF_RANGE",
[0x04] = "ERR_HARDWARE_ERROR",
[0x05] = "ERR_LOGITECH_INTERNAL",
[0x06] = "ERR_INVALID_FEATURE_INDEX",
[0x07] = "ERR_INVALID_FUNCTION_ID",
[0x08] = "ERR_BUSY",
[0x09] = "ERR_UNSUPPORTED",
[0x0A ... 0xFF] = NULL,
};
struct hidpp20_1b04_action_mapping {
@@ -138,6 +152,21 @@ static const struct hidpp20_1b04_physical_mapping hidpp20_1b04_physical_mapping[
BUTTON_PHYS_MAPPING(221, "LedToggle" , RATBAG_BUTTON_TYPE_UNKNOWN),
};
struct hidpp20_8070_location_mapping {
uint16_t value;
const char *name;
enum ratbag_led_type type;
};
#define LED_LOC_MAPPING(v_, n_, t_) \
{ v_, n_, t_ }
static const struct hidpp20_8070_location_mapping hidpp20_8070_location_mapping[] = {
LED_LOC_MAPPING(0, "None", RATBAG_LED_TYPE_UNKNOWN),
LED_LOC_MAPPING(1, "Logo LED", RATBAG_LED_TYPE_LOGO),
LED_LOC_MAPPING(2, "Side LED", RATBAG_LED_TYPE_SIDE),
};
const struct ratbag_button_action *
hidpp20_1b04_get_logical_mapping(uint16_t value)
{
@@ -203,6 +232,32 @@ hidpp20_1b04_get_physical_mapping_name(uint16_t value)
return "UNKNOWN";
}
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;
ARRAY_FOR_EACH(hidpp20_8070_location_mapping, map) {
if (map->value == value)
return map->type;
}
return RATBAG_LED_TYPE_UNKNOWN;
}
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;
ARRAY_FOR_EACH(hidpp20_8070_location_mapping, map) {
if (map->value == value)
return map->name;
}
return "UNKNOWN";
}
int
hidpp_write_command(struct hidpp_device *dev, uint8_t *cmd, int size)
{

View File

@@ -26,13 +26,12 @@
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
*/
#pragma once
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
#ifndef HIDPP_GENERIC_H
#define HIDPP_GENERIC_H
#define HIDPP_RECEIVER_IDX 0xFF
#define HIDPP_WIRED_DEVICE_IDX 0x00
@@ -52,19 +51,30 @@
#define GET_LONG_REGISTER_RSP 0x83
#define __ERROR_MSG 0x8F
#define ERR_SUCCESS 0x00
#define ERR_INVALID_SUBID 0x01
#define ERR_INVALID_ADDRESS 0x02
#define ERR_INVALID_VALUE 0x03
#define ERR_CONNECT_FAIL 0x04
#define ERR_TOO_MANY_DEVICES 0x05
#define ERR_ALREADY_EXISTS 0x06
#define ERR_BUSY 0x07
#define ERR_UNKNOWN_DEVICE 0x08
#define ERR_RESOURCE_ERROR 0x09
#define ERR_REQUEST_UNAVAILABLE 0x0A
#define ERR_INVALID_PARAM_VALUE 0x0B
#define ERR_WRONG_PIN_CODE 0x0C
#define HIDPP10_ERR_SUCCESS 0x00
#define HIDPP10_ERR_INVALID_SUBID 0x01
#define HIDPP10_ERR_INVALID_ADDRESS 0x02
#define HIDPP10_ERR_INVALID_VALUE 0x03
#define HIDPP10_ERR_CONNECT_FAIL 0x04
#define HIDPP10_ERR_TOO_MANY_DEVICES 0x05
#define HIDPP10_ERR_ALREADY_EXISTS 0x06
#define HIDPP10_ERR_BUSY 0x07
#define HIDPP10_ERR_UNKNOWN_DEVICE 0x08
#define HIDPP10_ERR_RESOURCE_ERROR 0x09
#define HIDPP10_ERR_REQUEST_UNAVAILABLE 0x0A
#define HIDPP10_ERR_INVALID_PARAM_VALUE 0x0B
#define HIDPP10_ERR_WRONG_PIN_CODE 0x0C
#define HIDPP20_ERR_NO_ERROR 0x00
#define HIDPP20_ERR_UNKNOWN 0x01
#define HIDPP20_ERR_INVALID_ARGUMENT 0x02
#define HIDPP20_ERR_OUT_OF_RANGE 0x03
#define HIDPP20_ERR_HARDWARE_ERROR 0x04
#define HIDPP20_ERR_LOGITECH_INTERNAL 0x05
#define HIDPP20_ERR_INVALID_FEATURE_INDEX 0x06
#define HIDPP20_ERR_INVALID_FUNCTION_ID 0x07
#define HIDPP20_ERR_BUSY 0x08
#define HIDPP20_ERR_UNSUPPORTED 0x09
/* Keep this in sync with ratbag_log_priority */
enum hidpp_log_priority {
@@ -98,7 +108,8 @@ hidpp_device_set_log_handler(struct hidpp_device *dev,
enum hidpp_log_priority priority,
void *userdata);
extern const char *hidpp_errors[0xFF];
extern const char *hidpp10_errors[0x100];
extern const char *hidpp20_errors[0x100];
const char *
hidpp20_1b04_get_physical_mapping_name(uint16_t value);
@@ -106,6 +117,12 @@ hidpp20_1b04_get_physical_mapping_name(uint16_t value);
enum ratbag_button_type
hidpp20_1b04_get_physical_mapping(uint16_t value);
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value);
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value);
const char *
hidpp20_1b04_get_logical_mapping_name(uint16_t value);
@@ -200,4 +217,8 @@ hidpp_cpu_to_le_u16(uint16_t data)
return result;
}
#endif /* HIDPP_GENERIC_H */
static inline uint32_t
hidpp_get_unaligned_be_u32(uint8_t *buf)
{
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}

View File

@@ -31,6 +31,7 @@
#include "config.h"
#include <assert.h>
#include <linux/types.h>
#include <errno.h>
#include <stdio.h>
@@ -85,6 +86,32 @@ const char *device_types[0xFF] = {
[0x0A ... 0xFE] = NULL,
};
unsigned int
hidpp10_dpi_table_get_max_dpi(struct hidpp10_device *dev)
{
struct hidpp10_dpi_mapping *dpi;
assert(dev->dpi_count > 0);
/* We assume a sorted list */
dpi = &dev->dpi_table[dev->dpi_count - 1];
return dpi->dpi;
}
unsigned int
hidpp10_dpi_table_get_min_dpi(struct hidpp10_device *dev)
{
struct hidpp10_dpi_mapping *dpi;
assert(dev->dpi_count > 0);
/* We assume a sorted list, index 0 is always 0 */
dpi = &dev->dpi_table[1];
return dpi->dpi;
}
int
hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev,
const char *str_list)
@@ -136,9 +163,6 @@ hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev,
if (index != count)
goto err;
/* mark the state as invalid */
dev->profile_count = 0;
return 0;
err:
@@ -176,9 +200,6 @@ hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev,
dev->dpi_table[i].dpi = round((min + step * i) / 25.0f) * 25;
}
/* mark the state as invalid */
dev->profile_count = 0;
return 0;
}
@@ -247,7 +268,7 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
" HID++ error from the %s (%d): %s (%02x)\n",
read_buffer.msg.device_idx == HIDPP_RECEIVER_IDX ? "receiver" : "device",
read_buffer.msg.device_idx,
hidpp_errors[hidpp_err] ? hidpp_errors[hidpp_err] : "Undocumented error code",
hidpp10_errors[hidpp_err] ? hidpp10_errors[hidpp_err] : "Undocumented error code",
hidpp_err);
break;
}
@@ -305,7 +326,7 @@ hidpp10_get_hidpp_notifications(struct hidpp10_device *dev,
return res;
*reporting_flags = notifications.msg.parameters[0];
*reporting_flags |= (notifications.msg.parameters[0] & 0x1F) << 8;
*reporting_flags |= (notifications.msg.parameters[1] & 0x1F) << 8;
*reporting_flags |= (notifications.msg.parameters[2] & 0x7 ) << 16;
return res;
@@ -520,16 +541,16 @@ hidpp10_get_battery_mileage(struct hidpp10_device *dev,
} \
}
struct _hidpp10_dpi_mode_8_sym {
struct _hidpp10_dpi_mode_8 {
uint8_t res;
uint8_t led1:4;
uint8_t led2:4;
uint8_t led3:4;
uint8_t led4:4;
} __attribute__((packed));
_Static_assert(sizeof(struct _hidpp10_dpi_mode_8_sym) == 3, "Invalid size");
_Static_assert(sizeof(struct _hidpp10_dpi_mode_8) == 3, "Invalid size");
struct _hidpp10_dpi_mode_8 {
struct _hidpp10_dpi_mode_8_dual {
uint8_t xres;
uint8_t yres;
uint8_t led1:4;
@@ -537,7 +558,7 @@ struct _hidpp10_dpi_mode_8 {
uint8_t led3:4;
uint8_t led4:4;
} __attribute__((packed));
_Static_assert(sizeof(struct _hidpp10_dpi_mode_8) == 4, "Invalid size");
_Static_assert(sizeof(struct _hidpp10_dpi_mode_8_dual) == 4, "Invalid size");
struct _hidpp10_dpi_mode_16 {
uint16_t xres;
@@ -616,7 +637,7 @@ static const uint8_t _hidpp10_profile_700_unknown1[3] = { 0x80, 0x01, 0x10 };
static const uint8_t _hidpp10_profile_700_unknown2[10] = { 0x01, 0x2c, 0x02, 0x58, 0x64, 0xff, 0xbc, 0x00, 0x09, 0x31 };
struct _hidpp10_profile_700 {
struct _hidpp10_dpi_mode_8 dpi_modes[PROFILE_NUM_DPI_MODES];
struct _hidpp10_dpi_mode_8_dual dpi_modes[PROFILE_NUM_DPI_MODES];
uint8_t default_dpi_mode;
uint8_t unknown1[3];
uint8_t usb_refresh_rate;
@@ -631,11 +652,11 @@ struct _hidpp10_profile_9 {
uint8_t green;
uint8_t blue;
uint8_t unknown1;
struct _hidpp10_dpi_mode_8_sym dpi_modes[PROFILE_NUM_DPI_MODES];
struct _hidpp10_dpi_mode_8 dpi_modes[PROFILE_NUM_DPI_MODES];
uint8_t default_dpi_mode;
uint8_t unknown2[2];
uint8_t usb_refresh_rate;
union _hidpp10_button_binding buttons[10];
union _hidpp10_button_binding buttons[PROFILE_NUM_BUTTONS_G9];
uint8_t unknown3[3];
union _hidpp10_profile_metadata metadata;
} __attribute__((packed));
@@ -693,29 +714,83 @@ hidpp10_get_dpi_value(struct hidpp10_device *dev, uint8_t raw_value)
return 0;
}
int
hidpp10_get_profile_directory(struct hidpp10_device *dev,
struct hidpp10_directory *out,
size_t nelems)
static int
hidpp10_write_profile_directory(struct hidpp10_device *dev)
{
unsigned int i;
unsigned int i, index;
int res;
uint8_t bytes[HIDPP10_PAGE_SIZE] = { 0 };
uint8_t bytes[HIDPP10_PAGE_SIZE];
struct hidpp10_directory *directory = (struct hidpp10_directory *)bytes;
size_t count;
uint16_t crc;
if (dev->profile_type == HIDPP10_PROFILE_UNKNOWN) {
hidpp_log_debug(&dev->base, "no profile type given\n");
return 0;
}
if (dev->profile_directory) {
count = 0;
while (dev->profile_directory[count].page) {
count++;
}
memset(bytes, 0xff, sizeof(bytes));
goto out;
index = 0;
for (i = 0; i < dev->profile_count; i++) {
if (!dev->profiles[i].enabled)
continue;
directory[index].page = dev->profiles[i].page;
directory[index].offset = dev->profiles[i].offset;
directory[index].led_mask = ((0b111 << index) >> 2) & 0b111;
++index;
}
crc = hidpp_crc_ccitt(bytes, HIDPP10_PAGE_SIZE - 2);
hidpp_set_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2], crc);
res = hidpp10_send_hot_payload(dev,
0x00, 0x0000, /* destination: RAM */
bytes,
HIDPP10_PAGE_SIZE / 2);
if (res < 0)
return res;
res = hidpp10_erase_memory(dev, 0x01);
if (res < 0)
return res;
res = hidpp10_write_flash(dev,
0x00, 0x0000,
0x01, 0x0000,
HIDPP10_PAGE_SIZE / 2);
if (res < 0)
return res;
res = hidpp10_send_hot_payload(dev,
0x00, 0x0000, /* destination: RAM */
bytes + HIDPP10_PAGE_SIZE / 2,
HIDPP10_PAGE_SIZE / 2);
if (res < 0)
return res;
res = hidpp10_write_flash(dev,
0x00, 0x0000,
0x01, HIDPP10_PAGE_SIZE / 2,
HIDPP10_PAGE_SIZE / 2);
if (res < 0)
return res;
return 0;
}
static int
hidpp10_read_profile_directory(struct hidpp10_device *dev)
{
unsigned int i;
int res;
uint8_t bytes[HIDPP10_PAGE_SIZE] = { 0 };
struct hidpp10_directory *directory = (struct hidpp10_directory *)bytes;
unsigned int count;
if (dev->profile_type == HIDPP10_PROFILE_UNKNOWN) {
hidpp_log_debug(&dev->base, "no profile type given\n");
return 0;
}
hidpp_log_raw(&dev->base, "Fetching the profiles' directory\n");
@@ -725,46 +800,29 @@ hidpp10_get_profile_directory(struct hidpp10_device *dev,
return res;
count = 0;
/* assume 16 profiles max */
for (i = 0; i < 16; i++) {
for (i = 0; i < dev->profile_count; i++) {
if (directory[i].page == 0xFF)
break;
dev->profiles[i].page = directory[i].page;
dev->profiles[i].offset = directory[i].offset;
dev->profiles[i].enabled = true;
count++;
}
dev->profile_directory = zalloc((count + 1) * sizeof(struct hidpp10_directory));
memcpy(dev->profile_directory, directory, count * sizeof(struct hidpp10_directory));
out:
if (dev->profile_count != count) {
if (dev->profiles)
free(dev->profiles);
dev->profiles = zalloc(count * sizeof(struct hidpp10_profile));
dev->profile_count = count;
}
count = min(count, nelems);
memcpy(out, dev->profile_directory, count * sizeof(out[0]));
for (i = count; i < dev->profile_count; i++)
dev->profiles[i].enabled = false;
return count;
}
int
hidpp10_get_current_profile(struct hidpp10_device *dev, int8_t *current_profile)
hidpp10_get_current_profile(struct hidpp10_device *dev, uint8_t *current_profile)
{
unsigned idx = dev->index;
union hidpp10_message profile = CMD_PROFILE(idx, GET_REGISTER_REQ);
int res;
unsigned i;
int8_t type, page, offset;
struct hidpp10_directory directory[16]; /* completely random profile count */
int count = 0;
hidpp_log_raw(&dev->base, "Fetching the profiles' directory\n");
count = hidpp10_get_profile_directory(dev, directory,
ARRAY_LENGTH(directory));
if (count < 0)
return count;
uint8_t type, page, offset;
hidpp_log_raw(&dev->base, "Fetching current profile\n");
@@ -779,14 +837,14 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, int8_t *current_profile)
*current_profile = page;
/* If the profile exceeds the directory length, default to
* the first */
if (*current_profile > count)
if (*current_profile > dev->profile_count)
*current_profile = 0;
return 0;
case PROFILE_TYPE_ADDRESS:
offset = profile.msg.parameters[2];
for (i = 0; i < ARRAY_LENGTH(directory) && directory[i].page < 32; i++) {
if (page == directory[i].page &&
offset == directory[i].offset) {
for (i = 0; i < dev->profile_count; i++) {
if (page == dev->profiles[i].page &&
offset == dev->profiles[i].offset) {
*current_profile = i;
return 0;
}
@@ -806,21 +864,12 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, int8_t *current_profile)
static int
hidpp10_set_internal_current_profile(struct hidpp10_device *dev,
int16_t current_profile,
uint16_t current_profile,
uint8_t profile_type)
{
unsigned idx = dev->index;
union hidpp10_message profile = CMD_PROFILE(idx, SET_REGISTER_REQ);
int8_t page, offset;
struct hidpp10_directory directory[16]; /* completely random profile count */
int count = 0;
hidpp_log_raw(&dev->base, "Fetching the profiles' directory\n");
count = hidpp10_get_profile_directory(dev, directory,
ARRAY_LENGTH(directory));
if (count < 0)
return count;
hidpp_log_raw(&dev->base, "Setting current profile\n");
@@ -828,7 +877,7 @@ hidpp10_set_internal_current_profile(struct hidpp10_device *dev,
switch (profile_type) {
case PROFILE_TYPE_INDEX:
if (current_profile > count)
if (current_profile > dev->profile_count)
return -EINVAL;
profile.msg.parameters[1] = current_profile & 0xFF;
break;
@@ -851,35 +900,13 @@ hidpp10_set_internal_current_profile(struct hidpp10_device *dev,
}
int
hidpp10_set_current_profile(struct hidpp10_device *dev, int16_t current_profile)
hidpp10_set_current_profile(struct hidpp10_device *dev, uint16_t current_profile)
{
return hidpp10_set_internal_current_profile(dev,
current_profile,
PROFILE_TYPE_INDEX);
}
static void
hidpp10_fill_dpi_modes_8_sym(struct hidpp10_device *dev,
struct hidpp10_profile *profile,
struct _hidpp10_dpi_mode_8_sym *dpi_list,
unsigned int count)
{
unsigned int i;
profile->num_dpi_modes = count;
for (i = 0; i < count; i++) {
struct _hidpp10_dpi_mode_8_sym *dpi = &dpi_list[i];
profile->dpi_modes[i].xres = hidpp10_get_dpi_value(dev, dpi->res);
profile->dpi_modes[i].yres = hidpp10_get_dpi_value(dev, dpi->res);
profile->dpi_modes[i].led[0] = dpi->led1 == 0x2;
profile->dpi_modes[i].led[1] = dpi->led2 == 0x2;
profile->dpi_modes[i].led[2] = dpi->led3 == 0x2;
profile->dpi_modes[i].led[3] = dpi->led4 == 0x2;
}
}
static void
hidpp10_fill_dpi_modes_8(struct hidpp10_device *dev,
struct hidpp10_profile *profile,
@@ -892,8 +919,8 @@ hidpp10_fill_dpi_modes_8(struct hidpp10_device *dev,
for (i = 0; i < count; i++) {
struct _hidpp10_dpi_mode_8 *dpi = &dpi_list[i];
profile->dpi_modes[i].xres = hidpp10_get_dpi_value(dev, dpi->xres);
profile->dpi_modes[i].yres = hidpp10_get_dpi_value(dev, dpi->yres);
profile->dpi_modes[i].xres = hidpp10_get_dpi_value(dev, dpi->res);
profile->dpi_modes[i].yres = hidpp10_get_dpi_value(dev, dpi->res);
profile->dpi_modes[i].led[0] = dpi->led1 == 0x2;
profile->dpi_modes[i].led[1] = dpi->led2 == 0x2;
@@ -913,6 +940,48 @@ hidpp10_write_dpi_modes_8(struct hidpp10_device *dev,
for (i = 0; i < count; i++) {
struct _hidpp10_dpi_mode_8 *dpi = &dpi_list[i];
dpi->res = hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].xres);
dpi->led1 = profile->dpi_modes[i].led[0] ? 0x02 : 0x01;
dpi->led2 = profile->dpi_modes[i].led[1] ? 0x02 : 0x01;
dpi->led3 = profile->dpi_modes[i].led[2] ? 0x02 : 0x01;
dpi->led4 = profile->dpi_modes[i].led[3] ? 0x02 : 0x01;
}
}
static void
hidpp10_fill_dpi_modes_8_dual(struct hidpp10_device *dev,
struct hidpp10_profile *profile,
struct _hidpp10_dpi_mode_8_dual *dpi_list,
unsigned int count)
{
unsigned int i;
profile->num_dpi_modes = count;
for (i = 0; i < count; i++) {
struct _hidpp10_dpi_mode_8_dual *dpi = &dpi_list[i];
profile->dpi_modes[i].xres = hidpp10_get_dpi_value(dev, dpi->xres);
profile->dpi_modes[i].yres = hidpp10_get_dpi_value(dev, dpi->yres);
profile->dpi_modes[i].led[0] = dpi->led1 == 0x2;
profile->dpi_modes[i].led[1] = dpi->led2 == 0x2;
profile->dpi_modes[i].led[2] = dpi->led3 == 0x2;
profile->dpi_modes[i].led[3] = dpi->led4 == 0x2;
}
}
static void
hidpp10_write_dpi_modes_8_dual(struct hidpp10_device *dev,
struct hidpp10_profile *profile,
struct _hidpp10_dpi_mode_8_dual *dpi_list,
unsigned int count)
{
unsigned int i;
for (i = 0; i < count; i++) {
struct _hidpp10_dpi_mode_8_dual *dpi = &dpi_list[i];
dpi->xres = hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].xres);
dpi->yres = hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].yres);
@@ -1033,7 +1102,6 @@ hidpp10_onboard_profiles_macro_next(struct hidpp10_device *device,
break;
case HIDPP10_MACRO_END:
step = 1;
return 0;
default:
if (macro->any.type >= 0x80 && macro->any.type <= 0xFE) {
@@ -1377,7 +1445,7 @@ hidpp10_profile_set_names(struct hidpp10_device *dev, struct hidpp10_profile *pr
memcpy(metadata->lgs02.marker, "LGS02", sizeof(metadata->lgs02.marker));
hidpp10_uchar8_to_uchar16(metadata->lgs02.name,
profile->name,
sizeof(metadata->lgs02.name));
ARRAY_LENGTH(metadata->lgs02.name));
for (i = 0; i < ARRAY_LENGTH(metadata->lgs02.macro_names); i++) {
hidpp10_uchar8_to_uchar16(metadata->lgs02.macro_names[i],
profile->macro_names[i],
@@ -1385,8 +1453,8 @@ hidpp10_profile_set_names(struct hidpp10_device *dev, struct hidpp10_profile *pr
}
}
int
hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_profile *profile_return)
static int
hidpp10_read_profile(struct hidpp10_device *dev, uint8_t number)
{
uint8_t page_data[HIDPP10_PAGE_SIZE];
union _hidpp10_profile_data *data = (union _hidpp10_profile_data *)page_data;
@@ -1396,9 +1464,7 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
size_t i;
int res;
struct hidpp10_profile *profile;
struct hidpp10_directory directory[16]; /* completely random profile count */
union _hidpp10_button_binding *buttons;
int count = 0;
uint8_t page;
/* Page 0 is RAM
@@ -1412,19 +1478,30 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
hidpp_log_raw(&dev->base, "Fetching profile %d\n", number);
count = hidpp10_get_profile_directory(dev, directory,
ARRAY_LENGTH(directory));
if (count < 0)
return count;
if (count == 0 || dev->profile_type == HIDPP10_PROFILE_UNKNOWN)
if (dev->profile_type == HIDPP10_PROFILE_UNKNOWN)
return -ENOTSUP;
if (number >= count) {
hidpp_log_error(&dev->base, "Profile number %d not in the directory.\n", number);
if (number >= dev->profile_count) {
hidpp_log_error(&dev->base, "Profile number %d is not supported.\n", number);
return -EINVAL;
}
profile = &dev->profiles[number];
if (!profile->page) {
unsigned long pages = 0xffff;
unsigned int i;
/* pages 0 and 1 are ROM and directory so they are reserved */
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);
page = ffsl(pages) - 1;
profile->page = page;
}
switch (dev->profile_type) {
case HIDPP10_PROFILE_G500:
buttons = p500->buttons;
@@ -1437,21 +1514,21 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
break;
default:
hidpp_log_error(&dev->base, "This should never happen, complain to your maintainer.\n");
return -EINVAL;
}
profile = &dev->profiles[number];
if (!profile->initialized) {
page = directory[number].page;
page = profile->page;
res = hidpp10_read_page(dev, page, page_data);
if (res == -EILSEQ) {
/*
* if the CRC is wrong, the mouse still handles the
* profile. Warn the user.
*/
hidpp_log_info(&dev->base,
"Profile %d has a wrong CRC, assuming valid.\n",
number);
if (profile->enabled)
hidpp_log_info(&dev->base,
"Profile %d has a wrong CRC, assuming valid.\n",
number);
res = 0;
}
if (res)
@@ -1474,7 +1551,7 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
profile->default_dpi_mode = p700->default_dpi_mode;
profile->refresh_rate = p700->usb_refresh_rate ? 1000/p700->usb_refresh_rate : 0;
hidpp10_fill_dpi_modes_8(dev, profile, p700->dpi_modes, PROFILE_NUM_DPI_MODES);
hidpp10_fill_dpi_modes_8_dual(dev, profile, p700->dpi_modes, PROFILE_NUM_DPI_MODES);
hidpp10_profile_parse_names(dev, profile, number, &p700->metadata);
hidpp10_fill_buttons(dev, profile, buttons, PROFILE_NUM_BUTTONS);
break;
@@ -1482,9 +1559,9 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
profile->default_dpi_mode = p9->default_dpi_mode;
profile->refresh_rate = p9->usb_refresh_rate ? 1000/p9->usb_refresh_rate : 0;
hidpp10_fill_dpi_modes_8_sym(dev, profile, p9->dpi_modes, PROFILE_NUM_DPI_MODES);
hidpp10_fill_dpi_modes_8(dev, profile, p9->dpi_modes, PROFILE_NUM_DPI_MODES);
hidpp10_profile_parse_names(dev, profile, number, &p9->metadata);
hidpp10_fill_buttons(dev, profile, buttons, 10);
hidpp10_fill_buttons(dev, profile, buttons, PROFILE_NUM_BUTTONS_G9);
break;
default:
hidpp_log_error(&dev->base, "This should never happen, complain to your maintainer.\n");
@@ -1504,10 +1581,10 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
profile->dpi_modes[i].yres);
hidpp_log_raw(&dev->base,
"LED status: 1:%s 2:%s 3:%s 4:%s\n",
(profile->dpi_modes[i].led[0] & 0x2) ? "on" : "off",
(profile->dpi_modes[i].led[1] & 0x2) ? "on" : "off",
(profile->dpi_modes[i].led[2] & 0x2) ? "on" : "off",
(profile->dpi_modes[i].led[3] & 0x2) ? "on" : "off");
profile->dpi_modes[i].led[0] ? "on" : "off",
profile->dpi_modes[i].led[1] ? "on" : "off",
profile->dpi_modes[i].led[2] ? "on" : "off",
profile->dpi_modes[i].led[3] ? "on" : "off");
}
hidpp_log_raw(&dev->base, "Angle correction: %d\n", profile->angle_correction);
hidpp_log_raw(&dev->base, "Default DPI mode: %d\n", profile->default_dpi_mode);
@@ -1550,8 +1627,21 @@ hidpp10_get_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
}
}
*profile_return = *profile;
return 0;
}
int
hidpp10_get_profile(struct hidpp10_device *dev, uint8_t number, struct hidpp10_profile *profile_return)
{
if (dev->profile_type == HIDPP10_PROFILE_UNKNOWN)
return -ENOTSUP;
if (number >= dev->profile_count) {
hidpp_log_error(&dev->base, "Profile number %d is not supported.\n", number);
return -EINVAL;
}
*profile_return = dev->profiles[number];
return 0;
}
@@ -1561,11 +1651,12 @@ static const enum ratbag_button_action_special hidpp10_profiles_specials[] = {
[0x02] = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT,
[0x03] = RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL,
[0x04] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP,
[0x05] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP,
[0x05 ... 0x07] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x06 ... 0x07] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x08] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN,
[0x09] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP,
[0x09] = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN,
[0x0a ... 0x0f] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
@@ -1575,8 +1666,9 @@ static const enum ratbag_button_action_special hidpp10_profiles_specials[] = {
[0x12 ... 0x1f] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x20] = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN,
[0x21] = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_DOWN,
[0x21 ... 0xff] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x22 ... 0xff] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
};
enum ratbag_button_action_special
@@ -1599,34 +1691,32 @@ hidpp10_onboard_profiles_get_code_from_special(enum ratbag_button_action_special
}
int
hidpp10_set_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_profile *profile)
hidpp10_set_profile(struct hidpp10_device *dev, uint8_t number, struct hidpp10_profile *profile)
{
uint8_t page_data[HIDPP10_PAGE_SIZE];
union _hidpp10_profile_data *data = (union _hidpp10_profile_data *)page_data;
struct _hidpp10_profile_500 *p500 = &data->profile_500;
struct _hidpp10_profile_700 *p700 = &data->profile_700;
struct _hidpp10_profile_9 *p9 = &data->profile_9;
int res;
struct hidpp10_directory directory[16]; /* completely random profile count */
union _hidpp10_button_binding *buttons;
int count = 0;
uint16_t crc;
uint8_t page;
hidpp_log_raw(&dev->base, "Fetching profile %d\n", number);
count = hidpp10_get_profile_directory(dev, directory,
ARRAY_LENGTH(directory));
if (count < 0)
return count;
if (count == 0 || dev->profile_type == HIDPP10_PROFILE_UNKNOWN)
if (dev->profile_type == HIDPP10_PROFILE_UNKNOWN)
return -ENOTSUP;
if (number >= count) {
hidpp_log_error(&dev->base, "Profile number %d not in the directory.\n", number);
if (number >= dev->profile_count) {
hidpp_log_error(&dev->base, "Profile number %d is incorrect.\n", number);
return -EINVAL;
}
/* something went wrong */
if (!profile->page)
return -ENOTSUP;
memset(page_data, 0xff, sizeof(page_data));
switch (dev->profile_type) {
@@ -1636,8 +1726,12 @@ hidpp10_set_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
case HIDPP10_PROFILE_G700:
buttons = p700->buttons;
break;
case HIDPP10_PROFILE_G9:
buttons = p9->buttons;
break;
default:
hidpp_log_error(&dev->base, "This should never happen, complain to your maintainer.\n");
return -ENOTSUP;
}
@@ -1645,9 +1739,10 @@ hidpp10_set_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
* values when we are not sure. */
switch (dev->profile_type) {
case HIDPP10_PROFILE_G500:
case HIDPP10_PROFILE_G9:
/* we do not know the actual values of the remaining field right now
* so pre-fill with the current data */
res = hidpp10_read_page(dev, directory[number].page, page_data);
res = hidpp10_read_page(dev, profile->page, page_data);
if (res)
return res;
break;
@@ -1657,6 +1752,7 @@ hidpp10_set_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
break;
default:
hidpp_log_error(&dev->base, "This should never happen, complain to your maintainer.\n");
return -ENOTSUP;
}
switch (dev->profile_type) {
@@ -1674,14 +1770,26 @@ hidpp10_set_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
break;
case HIDPP10_PROFILE_G700:
p700->default_dpi_mode = profile->default_dpi_mode;
p700->usb_refresh_rate = 1000 / profile->refresh_rate;
p700->usb_refresh_rate = profile->refresh_rate ? 1000 / profile->refresh_rate : 0;
hidpp10_write_dpi_modes_8(dev, profile, p700->dpi_modes, PROFILE_NUM_DPI_MODES);
hidpp10_write_dpi_modes_8_dual(dev, profile, p700->dpi_modes, PROFILE_NUM_DPI_MODES);
hidpp10_write_buttons(dev, profile, buttons, PROFILE_NUM_BUTTONS);
hidpp10_profile_set_names(dev, profile, number, &p700->metadata);
break;
case HIDPP10_PROFILE_G9:
p9->red = profile->red;
p9->green = profile->green;
p9->blue = profile->blue;
p9->default_dpi_mode = profile->default_dpi_mode;
p9->usb_refresh_rate = 1000 / profile->refresh_rate;
hidpp10_write_dpi_modes_8(dev, profile, p9->dpi_modes, PROFILE_NUM_DPI_MODES);
hidpp10_write_buttons(dev, profile, buttons, PROFILE_NUM_BUTTONS);
hidpp10_profile_set_names(dev, profile, number, &p9->metadata);
break;
default:
hidpp_log_error(&dev->base, "This should never happen, complain to your maintainer.\n");
return -ENOTSUP;
}
crc = hidpp_crc_ccitt(page_data, HIDPP10_PAGE_SIZE - 2);
@@ -1704,6 +1812,13 @@ hidpp10_set_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
if (res < 0)
return res;
if (profile->enabled != dev->profiles[number].enabled) {
dev->profiles[number].enabled = profile->enabled;
res = hidpp10_write_profile_directory(dev);
if (res < 0)
return res;
}
res = hidpp10_send_hot_payload(dev,
0x00, 0x0000, /* destination: RAM */
page_data,
@@ -1711,7 +1826,7 @@ hidpp10_set_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
if (res < 0)
return res;
page = directory[number].page;
page = profile->page;
/* according to the spec, a profile can have an offset.
* For all the devices we know, they all start at 0x0000 */
res = hidpp10_erase_memory(dev, page);
@@ -1743,6 +1858,7 @@ hidpp10_set_profile(struct hidpp10_device *dev, int8_t number, struct hidpp10_pr
if (res < 0)
return res;
dev->profiles[number] = *profile;
return res;
}
@@ -2002,18 +2118,28 @@ hidpp10_get_current_resolution(struct hidpp10_device *dev,
uint16_t *yres)
{
unsigned idx = dev->index;
union hidpp10_message resolution = CMD_CURRENT_RESOLUTION(REPORT_ID_SHORT, idx, GET_LONG_REGISTER_REQ);
union hidpp10_message resolution = CMD_CURRENT_RESOLUTION(REPORT_ID_SHORT, idx, GET_REGISTER_REQ);
union hidpp10_message resolution_long = CMD_CURRENT_RESOLUTION(REPORT_ID_SHORT, idx, GET_LONG_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching current resolution\n");
res = hidpp10_request_command(dev, &resolution);
if (res)
return res;
/* resolution is in 50dpi multiples */
*xres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution.data[4]));
*yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution.data[6]));
switch (dev->profile_type) {
case HIDPP10_PROFILE_G9:
res = hidpp10_request_command(dev, &resolution);
if (res)
return res;
/* resolution is in 50dpi multiples */
*xres = *yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution.data[4]));
break;
default:
res = hidpp10_request_command(dev, &resolution_long);
if (res)
return res;
/* resolution is in 50dpi multiples */
*xres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution_long.data[4]));
*yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution_long.data[6]));
}
return 0;
}
@@ -2061,6 +2187,9 @@ hidpp10_get_usb_refresh_rate(struct hidpp10_device *dev,
if (res)
return res;
if (!refresh.msg.parameters[0])
return -EINVAL;
*rate = 1000/refresh.msg.parameters[0];
return 0;
@@ -2508,8 +2637,7 @@ hidpp10_get_extended_pairing_information(struct hidpp10_device *dev,
if (res)
return -1;
*serial = hidpp_get_unaligned_be_u16(&info.msg.string[2]) << 16;
*serial |= hidpp_get_unaligned_be_u16(&info.msg.string[4]);
*serial = hidpp_get_unaligned_be_u32(&info.msg.string[1]);
return 0;
}
@@ -2578,14 +2706,10 @@ hidpp10_get_device_info(struct hidpp10_device *dev)
{
uint32_t feature_mask, notifications;
uint8_t reflect;
int i;
uint16_t xres, yres;
uint16_t refresh_rate;
enum hidpp10_led_status led[6];
int8_t current_profile;
struct hidpp10_profile profiles[HIDPP10_NUM_PROFILES];
int count;
struct hidpp10_directory directory[16];
uint8_t current_profile;
hidpp10_get_individual_features(dev, &feature_mask);
hidpp10_get_hidpp_notifications(dev, &notifications);
@@ -2597,18 +2721,14 @@ hidpp10_get_device_info(struct hidpp10_device *dev)
hidpp10_get_optical_sensor_settings(dev, &reflect);
hidpp10_get_current_profile(dev, &current_profile);
count = hidpp10_get_profile_directory(dev, directory,
ARRAY_LENGTH(directory));
for (i = 0; i < count && i < HIDPP10_NUM_PROFILES; i++)
hidpp10_get_profile(dev, i, &profiles[i]);
return 0;
}
struct hidpp10_device*
hidpp10_device_new(const struct hidpp_device *base, int idx,
enum hidpp10_profile_type type)
hidpp10_device_new(const struct hidpp_device *base,
int idx,
enum hidpp10_profile_type type,
unsigned int profile_count)
{
struct hidpp10_device *dev;
@@ -2617,6 +2737,8 @@ hidpp10_device_new(const struct hidpp_device *base, int idx,
dev->index = idx;
dev->base = *base;
dev->profile_type = type;
dev->profile_count = profile_count;
dev->profiles = zalloc(dev->profile_count * sizeof(struct hidpp10_profile));
if (hidpp10_get_device_info(dev) != 0) {
hidpp10_device_destroy(dev);
@@ -2626,6 +2748,19 @@ hidpp10_device_new(const struct hidpp_device *base, int idx,
return dev;
}
int
hidpp10_device_read_profiles(struct hidpp10_device *dev)
{
unsigned int i;
hidpp10_read_profile_directory(dev);
for (i = 0; i < dev->profile_count && i < HIDPP10_NUM_PROFILES; i++)
hidpp10_read_profile(dev, i);
return 0;
}
void
hidpp10_device_destroy(struct hidpp10_device *dev)
{
@@ -2634,19 +2769,15 @@ hidpp10_device_destroy(struct hidpp10_device *dev)
if (dev->dpi_table)
free(dev->dpi_table);
if (dev->profile_directory)
free(dev->profile_directory);
if (dev->profiles) {
for (i = 0; i < dev->profile_count; i++) {
ARRAY_FOR_EACH(dev->profiles[i].macros, macro) {
if (*macro) {
free(*macro);
*macro = NULL;
}
for (i = 0; i < dev->profile_count; i++) {
ARRAY_FOR_EACH(dev->profiles[i].macros, macro) {
if (*macro) {
free(*macro);
*macro = NULL;
}
}
free(dev->profiles);
}
free(dev->profiles);
free(dev);
}

View File

@@ -26,8 +26,7 @@
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
*/
#ifndef HIDPP_10_H
#define HIDPP_10_H
#pragma once
#include <stdint.h>
#include <stdbool.h>
@@ -59,16 +58,20 @@ struct hidpp10_device {
struct hidpp_device base;
unsigned index;
uint8_t dpi_count;
struct hidpp10_dpi_mapping *dpi_table; /* must be null terminated */
struct hidpp10_directory *profile_directory; /* must be null terminated */
struct hidpp10_dpi_mapping *dpi_table;
enum hidpp10_profile_type profile_type;
struct hidpp10_profile *profiles;
unsigned int profile_count;
};
struct hidpp10_device*
hidpp10_device_new(const struct hidpp_device *base, int idx,
enum hidpp10_profile_type type);
hidpp10_device_new(const struct hidpp_device *base,
int idx,
enum hidpp10_profile_type type,
unsigned int profile_count);
int
hidpp10_device_read_profiles(struct hidpp10_device *dev);
void
hidpp10_device_destroy(struct hidpp10_device *dev);
@@ -102,6 +105,22 @@ int
hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev,
const char *str_dpi);
/**
* Returns the lowest dpi stored in the dpi mapping table generated by
* hidpp10_build_dpi_table_from_list() or
* hidpp10_build_dpi_table_from_dpi_info()
*/
unsigned int
hidpp10_dpi_table_get_min_dpi(struct hidpp10_device *dev);
/**
* Returns the highest dpi stored in the dpi mapping table generated by
* hidpp10_build_dpi_table_from_list() or
* hidpp10_build_dpi_table_from_dpi_info()
*/
unsigned int
hidpp10_dpi_table_get_max_dpi(struct hidpp10_device *dev);
/* -------------------------------------------------------------------------- */
/* 0x00: Enable HID++ Notifications */
/* -------------------------------------------------------------------------- */
@@ -365,6 +384,7 @@ hidpp10_get_battery_mileage(struct hidpp10_device *dev,
/* 0x0F: Profile queries */
/* -------------------------------------------------------------------------- */
#define PROFILE_NUM_BUTTONS 13
#define PROFILE_NUM_BUTTONS_G9 10
#define PROFILE_NUM_DPI_MODES 5
#define PROFILE_BUTTON_TYPE_BUTTON 0x81
#define PROFILE_BUTTON_TYPE_KEYS 0x82
@@ -447,6 +467,8 @@ union hidpp10_macro_data {
_Static_assert(sizeof(union hidpp10_macro_data) == 5, "Invalid size");
struct hidpp10_profile {
uint8_t page;
uint8_t offset;
struct {
uint16_t xres;
uint16_t yres;
@@ -493,8 +515,10 @@ struct hidpp10_profile {
} buttons[PROFILE_NUM_BUTTONS];
union hidpp10_macro_data *macros[PROFILE_NUM_BUTTONS];
size_t num_buttons;
size_t num_leds;
unsigned int initialized;
bool enabled;
};
struct hidpp10_directory {
@@ -504,22 +528,17 @@ struct hidpp10_directory {
} __attribute__((packed));
int
hidpp10_get_profile_directory(struct hidpp10_device *dev,
struct hidpp10_directory *out,
size_t nelems);
hidpp10_get_current_profile(struct hidpp10_device *dev, uint8_t *current_profile);
int
hidpp10_get_current_profile(struct hidpp10_device *dev, int8_t *current_profile);
hidpp10_set_current_profile(struct hidpp10_device *dev, uint16_t current_profile);
int
hidpp10_set_current_profile(struct hidpp10_device *dev, int16_t current_profile);
int
hidpp10_get_profile(struct hidpp10_device *dev, int8_t number,
hidpp10_get_profile(struct hidpp10_device *dev, uint8_t number,
struct hidpp10_profile *profile);
int
hidpp10_set_profile(struct hidpp10_device *dev, int8_t number,
hidpp10_set_profile(struct hidpp10_device *dev, uint8_t number,
struct hidpp10_profile *profile);
enum ratbag_button_action_special
@@ -693,5 +712,3 @@ hidpp10_get_firmare_information(struct hidpp10_device *dev,
uint8_t *major,
uint8_t *minor,
uint8_t *build_number);
#endif /* HIDPP_10_H */

View File

@@ -130,13 +130,13 @@ hidpp20_request_command_allow_error(struct hidpp20_device *device, union hidpp20
hidpp_log_debug(&device->base,
" HID++ error from the device (%d): %s (%02x)\n",
read_buffer.msg.device_idx,
hidpp_errors[hidpp_err] ? hidpp_errors[hidpp_err] : "Undocumented error code",
hidpp20_errors[hidpp_err] ? hidpp20_errors[hidpp_err] : "Undocumented error code",
hidpp_err);
else
hidpp_log_error(&device->base,
" HID++ error from the device (%d): %s (%02x)\n",
read_buffer.msg.device_idx,
hidpp_errors[hidpp_err] ? hidpp_errors[hidpp_err] : "Undocumented error code",
hidpp20_errors[hidpp_err] ? hidpp20_errors[hidpp_err] : "Undocumented error code",
hidpp_err);
break;
}
@@ -241,7 +241,7 @@ hidpp20_root_get_protocol_version(struct hidpp20_device *device,
rc = hidpp20_request_command_allow_error(device, &msg, true);
if (rc == ERR_INVALID_SUBID) {
if (rc == HIDPP10_ERR_INVALID_SUBID) {
*major = 1;
*minor = 0;
return 0;
@@ -500,6 +500,118 @@ err:
return rc;
}
/* -------------------------------------------------------------------------- */
/* 0x8070 - Color LED effects */
/* -------------------------------------------------------------------------- */
#define CMD_COLOR_LED_EFFECTS_GET_INFO 0x00
#define CMD_COLOR_LED_EFFECTS_GET_ZONE_INFO 0x10
static int
hidpp20_color_led_zones_get_count(struct hidpp20_device *device, uint8_t reg)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_COLOR_LED_EFFECTS_GET_INFO,
};
struct hidpp20_color_led_info *info;
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_COLOR_LED_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info = (struct hidpp20_color_led_info *)msg.msg.parameters;
return info->zone_count;
}
int
hidpp20_color_led_effects_get_zone_info(struct hidpp20_device *device,
uint8_t reg,
struct hidpp20_color_led_zone_info *info)
{
int rc;
struct hidpp20_color_led_zone_info *msg_info;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_COLOR_LED_EFFECTS_GET_ZONE_INFO,
.msg.parameters[0] = info->index,
};
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
msg_info = (struct hidpp20_color_led_zone_info *)msg.msg.parameters;
info->location = hidpp_be_u16_to_cpu(msg_info->location);
info->num_effects = msg_info->num_effects;
info->persistency_caps = msg_info->persistency_caps;
return 0;
}
int
hidpp20_color_led_effects_get_zone_infos(struct hidpp20_device *device,
struct hidpp20_color_led_zone_info **infos_list)
{
uint8_t feature_index;
struct hidpp20_color_led_zone_info *i_list, *info;
uint8_t num_infos;
unsigned i;
int rc;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_COLOR_LED_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
rc = hidpp20_color_led_zones_get_count(device, feature_index);
if (rc < 0)
return rc;
num_infos = rc;
if (num_infos == 0) {
*infos_list = NULL;
return 0;
}
i_list = zalloc(num_infos * sizeof(struct hidpp20_color_led_zone_info));
for (i = 0; i < num_infos; i++) {
info = &i_list[i];
info->index = i;
rc = hidpp20_color_led_effects_get_zone_info(device, feature_index, info);
if (rc)
goto err;
hidpp_log_raw(&device->base,
"led_info %d: location: %d type %s num_effects: %d persistency_caps: 0x%02x\n",
info->index,
info->location,
hidpp20_8070_get_location_mapping_name(info->location),
info->num_effects,
info->persistency_caps);
}
*infos_list = i_list;
return num_infos;
err:
free(i_list);
return rc;
}
/* -------------------------------------------------------------------------- */
/* 0x1b04: Special keys and mouse buttons */
/* -------------------------------------------------------------------------- */
@@ -673,12 +785,15 @@ hidpp20_special_key_mouse_set_control(struct hidpp20_device *device,
return -ENOTSUP;
msg.msg.sub_id = feature_index;
msg.msg.parameters[2] |= 0x02;
if (control->reporting.divert)
msg.msg.parameters[2] |= 0x03;
msg.msg.parameters[2] |= 0x01;
msg.msg.parameters[2] |= 0x08;
if (control->reporting.persist)
msg.msg.parameters[2] |= 0x0c;
msg.msg.parameters[2] |= 0x04;
msg.msg.parameters[2] |= 0x20;
if (control->reporting.raw_XY)
msg.msg.parameters[2] |= 0x20;
msg.msg.parameters[2] |= 0x10;
return hidpp20_request_command(device, &msg);
}
@@ -775,8 +890,8 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
if (value > 0xe000) {
sensor->dpi_steps = value - 0xe000;
} else {
sensor->dpi_min = value < sensor->dpi_min ? value : sensor->dpi_min;
sensor->dpi_max = value > sensor->dpi_max ? value : sensor->dpi_max;
sensor->dpi_min = min(value, sensor->dpi_min);
sensor->dpi_max = max(value, sensor->dpi_max);
sensor->dpi_list[dpi_index++] = value;
}
assert(sensor->dpi_list[dpi_index] == 0x0000);
@@ -864,7 +979,7 @@ int hidpp20_adjustable_dpi_get_sensors(struct hidpp20_device *device,
return num_sensors;
err:
free(s_list);
return rc;
return rc > 0 ? -EPROTO : rc;
}
int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
@@ -907,6 +1022,8 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
/* -------------------------------------------------------------------------- */
#define CMD_ONBOARD_PROFILES_GET_PROFILES_DESCR 0x00
#define CMD_ONBOARD_PROFILES_SET_ONBOARD_MODE 0x10
#define CMD_ONBOARD_PROFILES_GET_ONBOARD_MODE 0x20
#define CMD_ONBOARD_PROFILES_SET_CURRENT_PROFILE 0x30
#define CMD_ONBOARD_PROFILES_GET_CURRENT_PROFILE 0x40
#define CMD_ONBOARD_PROFILES_MEMORY_READ 0x50
@@ -920,11 +1037,36 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
#define HIDPP20_PROFILE_SIZE HIDPP20_PAGE_SIZE
#define HIDPP20_BUTTON_HID 0x80
#define HIDPP20_MODE_NO_CHANGE 0x00
#define HIDPP20_ONBOARD_MODE 0x01
#define HIDPP20_HOST_MODE 0x02
#define HIDPP20_ONBOARD_PROFILES_MEMORY_TYPE_G402 0x01
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G402 0x01
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303 0x02
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900 0x03
#define HIDPP20_ONBOARD_PROFILES_MACRO_TYPE_G402 0x01
struct hidpp20_onboard_profiles_info {
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");
int
hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
uint8_t read_rom,
uint8_t page,
uint8_t section,
uint16_t section,
uint8_t result[16])
{
uint8_t feature_index;
@@ -935,13 +1077,13 @@ hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
.msg.address = CMD_ONBOARD_PROFILES_MEMORY_READ,
.msg.parameters[0] = read_rom,
.msg.parameters[1] = page,
.msg.parameters[2] = 0,
.msg.parameters[3] = section,
};
if (read_rom > 1)
return -EINVAL;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[2], section);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
if (feature_index == 0)
@@ -961,8 +1103,9 @@ hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
static int
hidpp20_onboard_profiles_write_start(struct hidpp20_device *device,
uint8_t page,
uint8_t section)
uint16_t page,
uint16_t section,
uint16_t size)
{
uint8_t feature_index;
int rc;
@@ -970,13 +1113,12 @@ hidpp20_onboard_profiles_write_start(struct hidpp20_device *device,
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_ONBOARD_PROFILES_MEMORY_ADDR_WRITE,
.msg.parameters[0] = 0x00,
.msg.parameters[1] = page,
.msg.parameters[2] = 0,
.msg.parameters[3] = section,
.msg.parameters[4] = 0x01,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], page);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[2], section);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[4], size);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
if (feature_index == 0)
@@ -1018,8 +1160,8 @@ hidpp20_onboard_profiles_write_end(struct hidpp20_device *device)
static int
hidpp20_onboard_profiles_write_data(struct hidpp20_device *device,
uint8_t page,
uint8_t section,
uint16_t page,
uint16_t section,
uint8_t *data,
size_t len)
{
@@ -1039,7 +1181,10 @@ hidpp20_onboard_profiles_write_data(struct hidpp20_device *device,
msg.msg.sub_id = feature_index;
rc = hidpp20_onboard_profiles_write_start(device, page, section);
rc = hidpp20_onboard_profiles_write_start(device,
page,
section,
HIDPP20_PAGE_SIZE);
if (rc)
return rc;
@@ -1065,7 +1210,7 @@ hidpp20_onboard_profiles_write_data(struct hidpp20_device *device,
static int
hidpp20_onboard_profiles_write_page(struct hidpp20_device *device,
uint8_t page,
uint16_t page,
uint8_t data[HIDPP20_PAGE_SIZE])
{
uint16_t crc;
@@ -1076,6 +1221,58 @@ hidpp20_onboard_profiles_write_page(struct hidpp20_device *device,
return hidpp20_onboard_profiles_write_data(device, page, 0x00, data, HIDPP20_PAGE_SIZE);
}
static int
hidpp20_onboard_profiles_get_onboard_mode(struct hidpp20_device *device)
{
uint8_t feature_index;
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_ONBOARD_PROFILES_GET_ONBOARD_MODE,
};
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
return msg.msg.parameters[0];
}
static int
hidpp20_onboard_profiles_set_onboard_mode(struct hidpp20_device *device,
uint8_t onboard_mode)
{
uint8_t feature_index;
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_ONBOARD_PROFILES_SET_ONBOARD_MODE,
.msg.parameters[1] = onboard_mode,
};
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
return 0;
}
int
hidpp20_onboard_profiles_get_current_profile(struct hidpp20_device *device)
{
@@ -1101,6 +1298,61 @@ hidpp20_onboard_profiles_get_current_profile(struct hidpp20_device *device)
return msg.msg.parameters[1];
}
int
hidpp20_onboard_profiles_set_current_dpi_index(struct hidpp20_device *device,
uint8_t index)
{
uint8_t feature_index;
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_ONBOARD_PROFILES_SET_CURRENT_DPI_INDEX,
.msg.parameters[0] = index,
};
if (index > 4)
return -EINVAL;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
return 0;
}
int
hidpp20_onboard_profiles_get_current_dpi_index(struct hidpp20_device *device)
{
uint8_t feature_index;
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_ONBOARD_PROFILES_GET_CURRENT_DPI_INDEX,
};
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
return msg.msg.parameters[0];
}
int
hidpp20_onboard_profiles_set_current_profile(struct hidpp20_device *device,
uint8_t index)
@@ -1130,16 +1382,18 @@ hidpp20_onboard_profiles_set_current_profile(struct hidpp20_device *device,
static int
hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
unsigned profile_count,
struct hidpp20_profiles *profiles_list)
struct hidpp20_profiles **profiles_list)
{
uint8_t feature_index;
int rc;
struct hidpp20_profiles *profiles;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_ONBOARD_PROFILES_GET_PROFILES_DESCR,
};
struct hidpp20_onboard_profiles_info *info;
int onboard_mode;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
@@ -1152,10 +1406,78 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
if (rc)
return rc;
profiles_list->num_buttons = msg.msg.parameters[5] <= 16 ? msg.msg.parameters[5] : 16;
profiles_list->num_profiles = profile_count;
/* FIXME: actually retrieve the correct values */
profiles_list->num_modes = 5;
info = (struct hidpp20_onboard_profiles_info *)msg.msg.parameters;
if (info->memory_model_id != HIDPP20_ONBOARD_PROFILES_MEMORY_TYPE_G402) {
hidpp_log_error(&device->base,
"Memory layout not supported: 0x%02x.\n",
info->memory_model_id);
return -ENOTSUP;
}
if ((info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G402) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900)) {
hidpp_log_error(&device->base,
"Profile layout not supported: 0x%02x.\n",
info->profile_format_id);
return -ENOTSUP;
}
if (info->macro_format_id != HIDPP20_ONBOARD_PROFILES_MACRO_TYPE_G402) {
hidpp_log_error(&device->base,
"Macro format not supported: 0x%02x.\n",
info->macro_format_id);
return -ENOTSUP;
}
info->sector_size = hidpp_be_u16_to_cpu(info->sector_size);
if (info->sector_size != HIDPP20_PAGE_SIZE) {
hidpp_log_error(&device->base,
"Unsupported sector size: %d.\n",
info->sector_size);
return -ENOTSUP;
}
rc = hidpp20_onboard_profiles_get_onboard_mode(device);
if (rc < 0)
return rc;
onboard_mode = rc;
if (onboard_mode != HIDPP20_ONBOARD_MODE) {
hidpp_log_raw(&device->base,
"not on the correct mode: %d.\n",
onboard_mode);
rc = hidpp20_onboard_profiles_set_onboard_mode(device,
HIDPP20_ONBOARD_MODE);
if (rc < 0)
return rc;
}
profiles = zalloc(sizeof(struct hidpp20_profiles) +
info->profile_count * sizeof(struct hidpp20_profile));
profiles->num_profiles = info->profile_count;
profiles->num_rom_profiles = info->profile_count_oob;
profiles->num_buttons = msg.msg.parameters[5] <= 16 ? msg.msg.parameters[5] : 16;
profiles->num_modes = HIDPP20_DPI_COUNT;
profiles->num_leds = HIDPP20_LED_COUNT;
profiles->has_g_shift = (info->mechanical_layout & 0x03) == 2;
profiles->has_dpi_shift = ((info->mechanical_layout & 0x0c) >> 2) == 2;
switch(info->various_info & 0x07) {
case 1:
profiles->corded = 1;
break;
case 2:
profiles->wireless = 1;
break;
case 4:
profiles->corded = 1;
profiles->wireless = 1;
break;
}
*profiles_list = profiles;
return 0;
}
@@ -1192,7 +1514,6 @@ hidpp20_onboard_profiles_macro_next(struct hidpp20_device *device,
rc = -EAGAIN;
break;
case HIDPP20_MACRO_END:
step = 1;
return 0;
default:
hidpp_log_error(&device->base, "unknown tag: 0x%02x\n", macro->any.type);
@@ -1289,7 +1610,7 @@ hidpp20_onboard_profiles_parse_macro(struct hidpp20_device *device,
int rc;
rc = hidpp20_onboard_profiles_read_macro(device, page, offset, &macro);
if (rc < 0)
if (rc)
return rc;
count = rc;
@@ -1320,49 +1641,70 @@ hidpp20_onboard_profiles_parse_macro(struct hidpp20_device *device,
return 0;
}
static unsigned int
hidpp20_onboard_profiles_compute_dict_size(const struct hidpp20_device *device,
const struct hidpp20_profiles *profiles)
{
unsigned p, num_offset;
num_offset = 0;
p = profiles->num_profiles;
while (p) {
p >>= 2;
num_offset += 16;
}
return num_offset;
}
int
hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
struct hidpp20_profiles **profiles_list)
{
unsigned i;
unsigned i, offset, num_offset;
int rc;
uint8_t data[16] = {0};
struct hidpp20_profiles *profiles;
unsigned profile_count = 0;
uint8_t data[HIDPP20_PAGE_SIZE] = {0};
struct hidpp20_profiles *profiles = NULL;
rc = hidpp20_onboard_profiles_read_memory(device,
0x00,
0x00,
0x00,
data);
rc = hidpp20_onboard_profiles_initialize(device,
&profiles);
if (rc < 0)
return rc;
profiles = zalloc(sizeof(struct hidpp20_profiles));
assert(profiles);
for (i = 0; i < 3; i++) {
num_offset = hidpp20_onboard_profiles_compute_dict_size(device,
profiles);
for (offset = 0; offset < num_offset; offset += 16) {
rc = hidpp20_onboard_profiles_read_memory(device,
0x00,
0x00,
offset,
data + offset);
if (rc < 0) {
hidpp20_onboard_profiles_destroy(profiles);
return rc;
}
}
for (i = 0; i < profiles->num_profiles; i++) {
uint8_t *d = data + 4 * i;
if (d[0] == 0xFF && d[1] == 0xFF)
break;
profile_count++;
profiles->profiles[i].index = d[1];
profiles->profiles[i].enabled = d[2];
}
hidpp20_onboard_profiles_initialize(device,
profile_count,
profiles);
*profiles_list = profiles;
return profile_count;
return profiles->num_profiles;
}
void
hidpp20_onboard_profiles_destroy(struct hidpp20_device *device,
struct hidpp20_profiles *profiles_list)
hidpp20_onboard_profiles_destroy(struct hidpp20_profiles *profiles_list)
{
struct hidpp20_profile *profile;
union hidpp20_macro_data **macro;
@@ -1386,7 +1728,8 @@ hidpp20_onboard_profiles_destroy(struct hidpp20_device *device,
static int
hidpp20_onboard_profiles_find_and_read_profile(struct hidpp20_device *device,
unsigned int index,
uint8_t *data)
uint8_t *data,
unsigned int num_rom_profiles)
{
int rc;
unsigned i;
@@ -1419,13 +1762,19 @@ hidpp20_onboard_profiles_find_and_read_profile(struct hidpp20_device *device,
}
/* something went wrong, the mouse is using the factory profile in ROM */
if (num_rom_profiles == 0)
return -EINVAL;
if (index >= num_rom_profiles)
index = num_rom_profiles - 1;
for (i = 0; i < HIDPP20_PROFILE_SIZE / 0x10; i++) {
rc = hidpp20_onboard_profiles_read_memory(device,
1,
index + 1,
i * 0x10,
data + i * 0x10);
if (rc < 0)
if (rc != 0)
return rc;
}
@@ -1433,20 +1782,16 @@ hidpp20_onboard_profiles_find_and_read_profile(struct hidpp20_device *device,
}
static int
hidpp20_onboard_profiles_enable_profile(struct hidpp20_device *device,
unsigned int index,
struct hidpp20_profiles *profiles_list)
hidpp20_onboard_profiles_set_enable_profile(struct hidpp20_device *device,
unsigned int index,
struct hidpp20_profiles *profiles_list)
{
unsigned int i, buffer_index = 0;
uint8_t data[HIDPP20_PROFILE_SIZE] = {0};
if (profiles_list->profiles[index].enabled)
return 0;
profiles_list->profiles[index].index = index + 1;
profiles_list->profiles[index].enabled = 0x01;
for (i = 0; i < 3; i++) {
for (i = 0; i < profiles_list->num_profiles; i++) {
data[buffer_index++] = 0x00;
data[buffer_index++] = i + 1;
data[buffer_index++] = profiles_list->profiles[i].enabled;
@@ -1461,9 +1806,13 @@ hidpp20_onboard_profiles_enable_profile(struct hidpp20_device *device,
memset(data + buffer_index, 0xff, sizeof(data) - buffer_index);
hidpp_log_buf_error(&device->base, "dictionary: ", data, 16);
hidpp_log_buf_debug(&device->base,
"dictionary: ",
data,
hidpp20_onboard_profiles_compute_dict_size(device,
profiles_list));
return hidpp20_onboard_profiles_write_page(device, 0x00, data);
return hidpp20_onboard_profiles_write_page(device, 0x0000, data);
}
union hidpp20_internal_profile {
@@ -1472,16 +1821,19 @@ union hidpp20_internal_profile {
uint8_t report_rate;
uint8_t default_dpi;
uint8_t switched_dpi;
uint16_t dpi[7];
uint8_t zero;
uint8_t padding[14];
uint16_t dpi[5];
struct hidpp20_color profile_color;
uint8_t power_mode;
uint8_t angle_snapping;
uint8_t reserved[14];
union hidpp20_button_binding buttons[16];
union hidpp20_button_binding alternate_buttons[16];
union {
char txt[16 * 3];
uint8_t raw[16 * 3];
} name;
uint8_t padding1[46];
struct hidpp20_internal_led leds[2]; /* G303, g502, g900 only */
uint8_t free[24];
uint16_t crc;
} __attribute__((packed)) profile;
};
@@ -1593,6 +1945,40 @@ hidpp20_buttons_from_cpu(struct hidpp20_profile *profile,
}
}
static void
hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
struct hidpp20_internal_led internal_led)
{
uint16_t period = 0;
uint8_t brightness = 0;
led->mode = (enum hidpp20_led_mode)internal_led.mode;
switch (led->mode) {
case HIDPP20_LED_CYCLE:
period = hidpp_be_u16_to_cpu(internal_led.effect.cycle.period_or_speed);
brightness = internal_led.effect.cycle.intensity;
if (brightness == 0)
brightness = 100;
break;
case HIDPP20_LED_BREATHING:
period = hidpp_be_u16_to_cpu(internal_led.effect.breath.period_or_speed);
brightness = internal_led.effect.breath.intensity;
if (brightness == 0)
brightness = 100;
led->color = internal_led.effect.breath.color;
break;
case HIDPP20_LED_ON:
led->color = internal_led.effect.fixed.color;
break;
case HIDPP20_LED_OFF:
break;
}
led->period = period;
led->brightness = brightness;
}
int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
unsigned int index,
struct hidpp20_profiles *profiles_list)
@@ -1606,8 +1992,11 @@ int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
if (index >= profiles_list->num_profiles)
return -EINVAL;
rc = hidpp20_onboard_profiles_find_and_read_profile(device, index, data);
if (rc < 0)
rc = hidpp20_onboard_profiles_find_and_read_profile(device,
index,
data,
profiles_list->num_rom_profiles);
if (rc != 0)
return rc;
profile->report_rate = 1000 / max(1, pdata.profile.report_rate);
@@ -1618,6 +2007,9 @@ int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
profile->dpi[i] = hidpp_get_unaligned_le_u16(&data[2 * i + 3]);
}
for (i = 0; i < profiles_list->num_leds; i++)
hidpp20_onboard_profiles_read_led(&profile->leds[i], pdata.profile.leds[i]);
hidpp20_buttons_to_cpu(device, profile, pdata.profile.buttons, profiles_list->num_buttons);
memcpy(profile->name, pdata.profile.name.txt, sizeof(profile->name));
@@ -1635,6 +2027,46 @@ int hidpp20_onboard_profiles_read(struct hidpp20_device *device,
return 0;
}
static void
hidpp20_onboard_profiles_write_led(struct hidpp20_internal_led *internal_led,
struct hidpp20_led *led)
{
uint16_t period = led->period;
uint8_t brightness = led->brightness;
memset(internal_led, 0, sizeof(*internal_led));
internal_led->mode = (uint8_t)led->mode;
switch (led->mode) {
case HIDPP20_LED_CYCLE:
internal_led->effect.cycle.period_or_speed = hidpp_cpu_to_be_u16(period);
if (brightness < 100)
internal_led->effect.cycle.intensity = brightness;
else
internal_led->effect.cycle.intensity = 0;
break;
case HIDPP20_LED_BREATHING:
internal_led->effect.breath.color.red = led->color.red;
internal_led->effect.breath.color.blue = led->color.blue;
internal_led->effect.breath.color.green = led->color.green;
internal_led->effect.breath.period_or_speed = hidpp_cpu_to_be_u16(period);
if (brightness < 100)
internal_led->effect.breath.intensity = brightness;
else
internal_led->effect.breath.intensity = 0;
break;
case HIDPP20_LED_ON:
internal_led->effect.fixed.color.red = led->color.red;
internal_led->effect.fixed.color.blue = led->color.blue;
internal_led->effect.fixed.color.green = led->color.green;
internal_led->effect.fixed.effect = 0;
break;
case HIDPP20_LED_OFF:
break;
}
}
int hidpp20_onboard_profiles_write(struct hidpp20_device *device,
unsigned int index,
struct hidpp20_profiles *profiles_list)
@@ -1648,11 +2080,14 @@ int hidpp20_onboard_profiles_write(struct hidpp20_device *device,
if (index >= profiles_list->num_profiles)
return -EINVAL;
rc = hidpp20_onboard_profiles_find_and_read_profile(device, index, data);
rc = hidpp20_onboard_profiles_find_and_read_profile(device,
index,
data,
profiles_list->num_rom_profiles);
if (rc < 0)
return rc;
rc = hidpp20_onboard_profiles_enable_profile(device, index, profiles_list);
rc = hidpp20_onboard_profiles_set_enable_profile(device, index, profiles_list);
if (rc < 0)
return rc;
@@ -1664,6 +2099,9 @@ int hidpp20_onboard_profiles_write(struct hidpp20_device *device,
pdata.profile.dpi[i] = hidpp_cpu_to_le_u16(profile->dpi[i]);
}
for (i = 0; i < profiles_list->num_leds; i++)
hidpp20_onboard_profiles_write_led(&pdata.profile.leds[i], &profile->leds[i]);
hidpp20_buttons_from_cpu(profile, pdata.profile.buttons, profiles_list->num_buttons);
memcpy(pdata.profile.name.txt, profile->name, sizeof(profile->name));
@@ -1691,8 +2129,9 @@ static const enum ratbag_button_action_special hidpp20_profiles_specials[] = {
[0x09] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x0a] = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP,
[0x0b] = RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE,
[0x0c] = RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL,
[0x0c ... 0xff] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
[0x0d ... 0xff] = RATBAG_BUTTON_ACTION_SPECIAL_INVALID,
};
enum ratbag_button_action_special
@@ -1738,6 +2177,9 @@ hidpp20_device_new(const struct hidpp_device *base, unsigned int idx)
goto err;
}
if (dev->proto_major < 2)
goto err;
rc = hidpp20_feature_set_get(dev);
if (rc < 0)
goto err;

View File

@@ -26,8 +26,7 @@
* https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=sharing
*/
#ifndef HIDPP_20_H
#define HIDPP_20_H
#pragma once
#include <stdint.h>
@@ -258,6 +257,41 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
#define HIDPP_PAGE_COLOR_LED_EFFECTS 0x8070
struct hidpp20_color_led_info;
struct hidpp20_color_led_zone_info;
int
hidpp20_color_led_effects_get_zone_info(struct hidpp20_device *device,
uint8_t reg, struct hidpp20_color_led_zone_info *info);
int
hidpp20_color_led_effects_get_zone_infos(struct hidpp20_device *device,
struct hidpp20_color_led_zone_info **infos_list);
enum hidpp20_color_led_location {
HIDPP20_COLOR_LED_LOCATION_UNDEFINED = 0,
HIDPP20_COLOR_LED_LOCATION_PRIMARY,
HIDPP20_COLOR_LED_LOCATION_LOGO,
HIDPP20_COLOR_LED_LOCATION_LEFT,
HIDPP20_COLOR_LED_LOCATION_RIGHT,
HIDPP20_COLOR_LED_LOCATION_COMBINED,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_1,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_2,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_3,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_4,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_5,
HIDPP20_COLOR_LED_LOCATION_PRIMARY_6,
};
enum hidpp20_color_led_persistency {
HIDPP20_COLOR_LED_PERSISTENCY_UNSUPPORTED,
HIDPP20_COLOR_LED_PERSISTENCY_ON,
HIDPP20_COLOR_LED_PERSISTENCY_OFF,
HIDPP20_COLOR_LED_PERSISTENCY_ON_OFF,
};
/* -------------------------------------------------------------------------- */
/* 0x8100 - Onboard Profiles */
/* -------------------------------------------------------------------------- */
@@ -275,6 +309,9 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
#define HIDPP20_MODIFIER_KEY_CTRL 0x01
#define HIDPP20_MODIFIER_KEY_SHIFT 0x02
#define HIDPP20_DPI_COUNT 5
#define HIDPP20_LED_COUNT 2
union hidpp20_button_binding {
struct {
uint8_t type;
@@ -315,6 +352,88 @@ union hidpp20_button_binding {
} __attribute__((packed));
_Static_assert(sizeof(union hidpp20_button_binding) == 4, "Invalid size");
struct hidpp20_color_led_info {
uint8_t zone_count;
/* we don't care about NV capabilities for libratbag, they just
* indicate sale demo effects */
uint16_t nv_caps;
uint16_t ext_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_info) == 5, "Invalid size");
struct hidpp20_color_led_zone_info {
uint8_t index;
uint16_t location;
uint8_t num_effects;
uint8_t persistency_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_info) == 5, "Invalid size");
struct hidpp20_color {
uint8_t red;
uint8_t green;
uint8_t blue;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color) == 3, "Invalid size");
enum hidpp20_led_type {
HIDPP20_LED_UNKNOWN = -1,
HIDPP20_LED_LOGO = 0,
HIDPP20_LED_SIDE,
};
enum hidpp20_led_mode {
HIDPP20_LED_OFF = 0x00,
HIDPP20_LED_ON = 0x01,
HIDPP20_LED_CYCLE = 0x03,
HIDPP20_LED_BREATHING = 0x0a,
};
enum hidpp20_led_waveform {
HIDPP20_LED_WF_DEFAULT = 0x00,
HIDPP20_LED_WF_SINE = 0x01,
HIDPP20_LED_WF_SQUARE = 0x02,
HIDPP20_LED_WF_TRIANGLE = 0x03,
HIDPP20_LED_WF_SAWTOOTH = 0x04,
HIDPP20_LED_WF_SHARKFIN = 0x05,
HIDPP20_LED_WF_EXPONENTIAL = 0x06,
};
struct hidpp20_internal_led {
uint8_t mode; /* enum hidpp20_led_mode */
union {
struct hidpp20_led_fixed {
struct hidpp20_color color;
uint8_t effect;
} __attribute__((packed)) fixed;
struct hidpp20_led_cycle {
uint8_t unused[5];
uint16_t period_or_speed; /* period in ms, speed is device dependent */
uint8_t intensity; /* 1 - 100 percent, 0 means 100 */
} __attribute__((packed)) cycle;
struct hidpp20_led_breath {
struct hidpp20_color color;
uint16_t period_or_speed; /* period in ms, speed is device dependent */
uint8_t waveform; /* enum hidpp20_led_waveform */
uint8_t intensity; /* 1 - 100 percent, 0 means 100 */
} __attribute__((packed)) breath;
uint8_t padding[10];
} __attribute__((packed)) effect;
};
_Static_assert(sizeof(struct hidpp20_led_fixed) == 4, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_cycle) == 8, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_breath) == 7, "Invalid size");
_Static_assert(sizeof(struct hidpp20_internal_led) == 11, "Invalid size");
typedef uint8_t percent_t;
struct hidpp20_led {
enum hidpp20_led_mode mode;
struct hidpp20_color color;
uint16_t period;
percent_t brightness;
};
#define HIDPP20_MACRO_NOOP 0x01
#define HIDPP20_MACRO_DELAY 0x40
#define HIDPP20_MACRO_KEY_PRESS 0x43
@@ -353,16 +472,24 @@ struct hidpp20_profile {
unsigned report_rate;
unsigned default_dpi;
unsigned switched_dpi;
uint16_t dpi[5];
unsigned current_dpi;
uint16_t dpi[HIDPP20_DPI_COUNT];
union hidpp20_button_binding buttons[32];
union hidpp20_macro_data *macros[32];
struct hidpp20_led leds[HIDPP20_LED_COUNT];
};
struct hidpp20_profiles {
uint8_t num_profiles;
uint8_t num_rom_profiles;
uint8_t num_buttons;
uint8_t num_modes;
struct hidpp20_profile profiles[5];
uint8_t num_leds;
uint8_t has_g_shift;
uint8_t has_dpi_shift;
uint8_t corded;
uint8_t wireless;
struct hidpp20_profile profiles[0];
};
/**
@@ -378,8 +505,7 @@ int hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
* free a list of profiles allocated by hidpp20_onboard_profiles_allocate()
*/
void
hidpp20_onboard_profiles_destroy(struct hidpp20_device *device,
struct hidpp20_profiles *profiles_list);
hidpp20_onboard_profiles_destroy(struct hidpp20_profiles *profiles_list);
/**
* return the current profile index or a negative error.
@@ -396,6 +522,22 @@ int
hidpp20_onboard_profiles_set_current_profile(struct hidpp20_device *device,
uint8_t index);
/**
* return the current dpi index of the current active profile
* or a negative error.
*/
int hidpp20_onboard_profiles_get_current_dpi_index(struct hidpp20_device *device);
/**
* Sets the current dpi index on the current active profile.
* Indexes are 0-indexed.
*
* return 0 or a negative error.
*/
int
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.
@@ -420,7 +562,7 @@ int
hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
uint8_t read_rom,
uint8_t page,
uint8_t section,
uint16_t section,
uint8_t result[16]);
/* -------------------------------------------------------------------------- */
@@ -430,5 +572,3 @@ hidpp20_onboard_profiles_read_memory(struct hidpp20_device *device,
#define HIDPP_PAGE_MOUSE_BUTTON_SPY 0x8110
#endif /* HIDPP_20_H */

View File

@@ -24,18 +24,26 @@
*/
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/hidraw.h>
#include "usb-ids.h"
#include "hidpp10.h"
#include "libratbag-util.h"
#include "liblur.h"
#define _EXPORT_ __attribute__ ((visibility("default")))
#define MAX_DEVICES 6
#define VENDOR_ID_LOGITECH 0x046d
static inline void
cleanup_hidpp10_device_destroy(struct hidpp10_device **hidpp10_device) {
if (*hidpp10_device)
hidpp10_device_destroy(*hidpp10_device);
}
#define _cleanup_hidpp10_device_destroy_ _cleanup_(cleanup_hidpp10_device_destroy)
struct lur_receiver {
int refcount;
@@ -50,6 +58,7 @@ struct lur_receiver {
struct lur_device {
struct lur_receiver *receiver;
int refcount;
void *userdata;
char *name;
uint16_t vid, pid;
@@ -111,7 +120,7 @@ lur_device_disconnect(struct lur_device *dev)
_EXPORT_ int
lur_is_receiver(uint16_t vid, uint16_t pid)
{
return (vid == VENDOR_ID_LOGITECH && (pid == 0xc52b || pid == 0xc532));
return (vid == USB_VENDOR_ID_LOGITECH && (pid == 0xc52b || pid == 0xc532));
}
static bool
@@ -137,16 +146,16 @@ hidpp10_init(int fd)
hidpp_device_init(&base, fd);
return hidpp10_device_new(&base, HIDPP_RECEIVER_IDX,
HIDPP10_PROFILE_UNKNOWN);
HIDPP10_PROFILE_UNKNOWN, 1);
}
_EXPORT_ struct lur_receiver*
lur_receiver_new_from_hidraw(int fd, void *userdata)
_EXPORT_ int
lur_receiver_new_from_hidraw(int fd, void *userdata, struct lur_receiver **out)
{
struct lur_receiver *receiver;
if (!hidraw_is_receiver(fd))
return NULL;
return -EINVAL;
receiver = zalloc(sizeof(*receiver));
receiver->refcount = 1;
@@ -158,11 +167,12 @@ lur_receiver_new_from_hidraw(int fd, void *userdata)
if (!receiver->hidppdev)
goto error;
return receiver;
*out = receiver;
return 0;
error:
free(receiver);
return NULL;
return -errno;
}
@@ -183,7 +193,7 @@ lur_receiver_enumerate(struct lur_receiver *lur,
dev->present = false;
for (i = 0; i < MAX_DEVICES; i++) {
struct hidpp10_device *d;
_cleanup_hidpp10_device_destroy_ struct hidpp10_device *d;
size_t name_size = 64;
char name[name_size];
uint8_t interval, type;
@@ -191,8 +201,7 @@ lur_receiver_enumerate(struct lur_receiver *lur,
uint32_t serial;
bool is_new_device = true;
d = hidpp10_device_new(&base, i,
HIDPP10_PROFILE_UNKNOWN);
d = hidpp10_device_new(&base, i, HIDPP10_PROFILE_UNKNOWN, 1);
if (!d)
continue;
@@ -229,7 +238,7 @@ lur_receiver_enumerate(struct lur_receiver *lur,
lur_receiver_ref(lur);
dev->refcount = 1;
dev->name = strdup_safe(name);
dev->vid = VENDOR_ID_LOGITECH;
dev->vid = USB_VENDOR_ID_LOGITECH;
dev->pid = wpid;
dev->type = type;
dev->serial = serial;
@@ -237,8 +246,6 @@ lur_receiver_enumerate(struct lur_receiver *lur,
dev->present = true;
list_insert(&lur->devices, &dev->node);
}
hidpp10_device_destroy(d);
}
devices = zalloc(MAX_DEVICES * sizeof(*devices));
@@ -347,3 +354,15 @@ lur_device_unref(struct lur_device *dev)
return NULL;
}
_EXPORT_ void
lur_device_set_user_data(struct lur_device *dev, void *userdata)
{
dev->userdata = userdata;
}
_EXPORT_ void*
lur_device_get_user_data(const struct lur_device *dev)
{
return dev->userdata;
}

View File

@@ -23,8 +23,7 @@
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBLUR_H_
#define _LIBLUR_H_ 1
#pragma once
#ifdef __cplusplus
extern "C" {
@@ -85,8 +84,8 @@ int
lur_device_disconnect(struct lur_device *dev);
/**
* Returns non-zero if a device with the given vid/pid is a logitech
* unifying receiver device.
* Returns non-zero if a device with the given vid/pid is a Logitech
* Unifying Receiver device.
*
* @param vid The vendor ID
* @param pid The product ID
@@ -107,11 +106,16 @@ lur_is_receiver(uint16_t vid, uint16_t pid);
*
* @param fd An O_RDWR file descriptor pointing to a /dev/hidraw node
* @param userdata Caller-specific data
* @param lur Set to the lur object on success, otherwise unmodified
*
* @return A lur object, or NULL on error.
* @return 0 on success or a negative errno on error
* @retval -EINVAL The fd does not point to a lur receiver
*
* @note liblur does not have OOM handling. If an allocation fails, liblur
* will simply abort()
*/
struct lur_receiver*
lur_receiver_new_from_hidraw(int fd, void *userdata);
int
lur_receiver_new_from_hidraw(int fd, void *userdata, struct lur_receiver **lur);
/**
* Enumerate devices currently paired with the given receiver.
@@ -239,7 +243,27 @@ lur_device_ref(struct lur_device *dev);
struct lur_device *
lur_device_unref(struct lur_device *dev);
/**
* Set caller-specific data associated with this object. liblur does
* not manage, look at, or modify this data. The caller must ensure the
* data is valid.
*
* @param dev A valid device object
* @param userdata Caller-specific data
*/
void
lur_device_set_user_data(struct lur_device *dev, void *userdata);
/**
* Get the caller-specific data associated with this object, if any.
*
* @param dev A valid device object
* @return The caller-specific data previously assigned in
* lur_device_set_user_data().
*/
void*
lur_device_get_user_data(const struct lur_device *dev);
#ifdef __cplusplus
}
#endif
#endif

28
src/liblur.sym Normal file
View File

@@ -0,0 +1,28 @@
/* in alphabetical order! */
LIBLUR_0.4.0 {
global:
lur_device_disconnect;
lur_device_get_name;
lur_device_get_product_id;
lur_device_get_serial;
lur_device_get_type;
lur_device_get_user_data;
lur_device_get_vendor_id;
lur_device_ref;
lur_device_set_user_data;
lur_device_unref;
lur_is_receiver;
lur_receiver_close;
lur_receiver_enumerate;
lur_receiver_get_fd;
lur_receiver_get_user_data;
lur_receiver_new_from_hidraw;
lur_receiver_open;
lur_receiver_ref;
lur_receiver_set_user_data;
lur_receiver_unref;
local:
*;
};

View File

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

View File

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

View File

@@ -21,8 +21,7 @@
* DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRATBAG_PRIVATE_H
#define LIBRATBAG_PRIVATE_H
#pragma once
#include <linux/input.h>
#include <stdint.h>
@@ -79,6 +78,8 @@ struct ratbag {
enum ratbag_log_priority log_priority;
};
#define MAX_CAP 1000
struct ratbag_device {
char *name;
void *userdata;
@@ -89,12 +90,13 @@ struct ratbag_device {
struct input_id ids;
struct ratbag_driver *driver;
struct ratbag *ratbag;
unsigned long capabilities;
unsigned long capabilities[NLONGS(MAX_CAP)];
unsigned num_profiles;
struct list profiles;
unsigned num_buttons;
unsigned num_leds;
void *drv_data;
@@ -130,6 +132,27 @@ struct ratbag_driver {
*/
void (*remove)(struct ratbag_device *device);
/**
* Callback called when the driver should write any profiles that
* were modified back to the device.
*
* Both profile and button structs have a dirty variable that can
* be used to tell whether or not they've actually changed since
* the last commit. In order to reduce the amount of time
* committing takes, drivers should use this information to avoid
* writing back profiles and buttons that haven't actually changed.
*/
int (*commit)(struct ratbag_device *device);
/**
* Callback called when the device doesn't have the udev property
* RATBAG_SVG.
*
* 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.
@@ -141,9 +164,10 @@ struct ratbag_driver {
*/
void (*read_profile)(struct ratbag_profile *profile, unsigned int index);
/**
* Here, the driver should actually write the profile to the
* device.
/*
* FIXME: This function is deprecated and should not be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_profile)(struct ratbag_profile *profile);
@@ -168,32 +192,49 @@ struct ratbag_driver {
*/
void (*read_button)(struct ratbag_button *button);
/**
* For the given button, store in the profile and in the device
* the given struct ratbag_button.
/*
* FIXME: This function is deprecated and should not be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_button)(struct ratbag_button *button,
const struct ratbag_button_action *action);
/**
* For the given profile, overwrite the current resolution
* of the sensor expressed in DPI, and commit it to the hardware.
*
* Mandatory if the driver exports RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION.
/*
* FIXME: This function is deprecated and should not be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_resolution_dpi)(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y);
/**
* For the given led, fill in the struct ratbag_led
* with the available information.
*/
void (*read_led)(struct ratbag_led *led);
/*
* FIXME: This function is deprecated and should be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_led)(struct ratbag_led *led, enum ratbag_led_mode mode,
struct ratbag_color color, unsigned int hz,
unsigned int brightness);
/* private */
int (*test_probe)(struct ratbag_device *device, void *data);
int (*test_probe)(struct ratbag_device *device, const void *data);
struct list link;
};
struct ratbag_resolution{
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 */
unsigned int dpi_x; /**< x resolution in dpi */
unsigned int dpi_y; /**< y resolution in dpi */
unsigned int hz; /**< report rate in Hz */
@@ -202,6 +243,20 @@ struct ratbag_resolution{
uint32_t capabilities;
};
struct ratbag_led {
int refcount;
void *userdata;
struct list link;
struct ratbag_profile *profile;
unsigned index;
enum ratbag_led_type type;
enum ratbag_led_mode mode;
struct ratbag_color color;
unsigned int hz; /**< rate of action in hz */
unsigned int brightness; /**< brightness of the LED */
bool dirty;
};
struct ratbag_profile {
int refcount;
void *userdata;
@@ -216,8 +271,11 @@ struct ratbag_profile {
struct ratbag_resolution *modes;
unsigned int num_modes;
} resolution;
struct list leds;
bool is_active; /**< profile is the currently active one */
bool is_enabled;
bool dirty; /**< profile changed since last commit */
};
#define BUTTON_ACTION_NONE \
@@ -250,6 +308,11 @@ struct ratbag_macro {
struct ratbag_macro_event events[MAX_MACRO_EVENTS];
};
struct ratbag_button_macro {
int refcount;
struct ratbag_macro macro;
};
struct ratbag_button_action {
enum ratbag_button_action_type type;
union ratbag_btn_action {
@@ -272,6 +335,7 @@ struct ratbag_button {
enum ratbag_button_type type;
struct ratbag_button_action action;
uint32_t action_caps;
bool dirty; /* changed since last commit to device */
};
static inline void
@@ -304,7 +368,7 @@ ratbag_set_drv_data(struct ratbag_device *device, void *drv_data)
}
static inline void *
ratbag_get_drv_data(struct ratbag_device *device)
ratbag_get_drv_data(const struct ratbag_device *device)
{
return device->drv_data;
}
@@ -313,7 +377,8 @@ int
ratbag_device_init_profiles(struct ratbag_device *device,
unsigned int num_profiles,
unsigned int num_resolutions,
unsigned int num_buttons);
unsigned int num_buttons,
unsigned int num_leds);
void
ratbag_device_set_capability(struct ratbag_device *device,
@@ -367,10 +432,19 @@ ratbag_resolution_init(struct ratbag_profile *profile, int index,
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)
{
res->dpi_min = min;
res->dpi_max = max;
}
static inline void
ratbag_resolution_set_cap(struct ratbag_resolution *res,
enum ratbag_resolution_capability cap)
@@ -412,7 +486,9 @@ log_buffer(struct ratbag *ratbag,
struct ratbag_driver etekcity_driver;
struct ratbag_driver hidpp20_driver;
struct ratbag_driver hidpp10_driver;
struct ratbag_driver logitech_g300_driver;
struct ratbag_driver roccat_driver;
struct ratbag_driver gskill_driver;
struct ratbag_device*
ratbag_device_new(struct ratbag *ratbag, struct udev_device *udev_device,
@@ -424,13 +500,15 @@ const char *
ratbag_device_get_udev_property(const struct ratbag_device* device,
const char *name);
struct ratbag_driver *
ratbag_find_driver(struct ratbag_device *device,
const struct input_id *dev_id,
struct ratbag_test_device *test_device);
bool
ratbag_assign_driver(struct ratbag_device *device,
const struct input_id *dev_id,
const struct ratbag_test_device *test_device);
void
ratbag_register_driver(struct ratbag *ratbag, struct ratbag_driver *driver);
#endif /* LIBRATBAG_PRIVATE_H */
void
ratbag_button_copy_macro(struct ratbag_button *button,
const struct ratbag_button_macro *macro);

View File

@@ -50,7 +50,7 @@ ratbag_register_test_drivers(struct ratbag *ratbag)
LIBRATBAG_EXPORT struct ratbag_device*
ratbag_device_new_test_device(struct ratbag *ratbag,
struct ratbag_test_device *test_device)
const struct ratbag_test_device *test_device)
{
struct ratbag_device* device = NULL;
#if BUILD_TESTS
@@ -71,7 +71,7 @@ ratbag_device_new_test_device(struct ratbag *ratbag,
device = ratbag_device_new(ratbag, NULL, "Test device", &id);
if (!ratbag_find_driver(device, &device->ids, test_device)) {
if (!ratbag_assign_driver(device, &device->ids, test_device)) {
ratbag_device_destroy(device);
return NULL;
}

View File

@@ -22,16 +22,29 @@
*/
#ifndef LIBRATBAG_TEST_H
#define LIBRATBAG_TEST_H
#pragma once
#include "libratbag.h"
#define RATBAG_TEST_MAX_PROFILES 12
#define RATBAG_TEST_MAX_BUTTONS 25
#define RATBAG_TEST_MAX_RESOLUTIONS 8
#define RATBAG_TEST_MAX_LEDS 8
struct ratbag_test_macro_event {
enum ratbag_macro_event_type type;
unsigned int value;
};
struct ratbag_test_button {
enum ratbag_button_type button_type;
enum ratbag_button_action_type action_type;
union {
int button;
int key;
enum ratbag_button_action_special special;
struct ratbag_test_macro_event macro[24];
};
};
struct ratbag_test_resolution {
@@ -42,23 +55,40 @@ struct ratbag_test_resolution {
uint32_t caps;
};
struct ratbag_test_color {
unsigned short red;
unsigned short green;
unsigned short blue;
};
struct ratbag_test_led {
enum ratbag_led_type type;
enum ratbag_led_mode mode;
struct ratbag_test_color color;
unsigned int hz;
unsigned int brightness;
};
struct ratbag_test_profile {
struct ratbag_test_button buttons[RATBAG_TEST_MAX_BUTTONS];
struct ratbag_test_resolution resolutions[RATBAG_TEST_MAX_RESOLUTIONS];
struct ratbag_test_led leds[RATBAG_TEST_MAX_LEDS];
bool active;
bool dflt;
bool disabled;
};
struct ratbag_test_device {
unsigned int num_profiles;
unsigned int num_resolutions;
unsigned int num_buttons;
unsigned int num_leds;
const char *svg;
struct ratbag_test_profile profiles[RATBAG_TEST_MAX_PROFILES];
void (*destroyed)(struct ratbag_device *device, void *data);
void *destroyed_data;
};
struct ratbag_device* ratbag_device_new_test_device(struct ratbag *ratbag,
struct ratbag_test_device *test_device);
const struct ratbag_test_device *test_device);
#endif /* LIBRATBAG_TEST_H */

View File

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

View File

@@ -21,8 +21,7 @@
* OF THIS SOFTWARE.
*/
#ifndef LIBRATBAG_UTIL_H
#define LIBRATBAG_UTIL_H
#pragma once
#include <stdlib.h>
#include <stdio.h>
@@ -30,6 +29,8 @@
#include <unistd.h>
#include <libudev.h>
#include "shared-macro.h"
/*
* This list data structure is a verbatim copy from wayland-util.h from the
* Wayland project; except that wl_ prefix has been removed.
@@ -45,16 +46,6 @@ void list_insert(struct list *list, struct list *elm);
void list_remove(struct list *elm);
int list_empty(const struct list *list);
#ifdef __GNUC__
#define container_of(ptr, sample, member) \
(__typeof__(sample))((char *)(ptr) - \
((char *)&(sample)->member - (char *)(sample)))
#else
#define container_of(ptr, sample, member) \
(void *)((char *)(ptr) - \
((char *)&(sample)->member - (char *)(sample)))
#endif
#define list_for_each(pos, head, member) \
for (pos = 0, pos = container_of((head)->next, pos, member); \
&pos->member != (head); \
@@ -68,45 +59,6 @@ int list_empty(const struct list *list);
pos = tmp, \
tmp = container_of(pos->member.next, tmp, member))
#define LONG_BITS (sizeof(long) * 8)
#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
#define ARRAY_FOR_EACH(_arr, _elem) \
for (size_t _i = 0; _i < ARRAY_LENGTH(_arr) && (_elem = &_arr[_i]); _i++)
#define AS_MASK(v) (1 << (v))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define streq(s1, s2) (strcmp((s1), (s2)) == 0)
#define strneq(s1, s2, n) (strncmp((s1), (s2), (n)) == 0)
static inline void
cleanup_free(void *p) {
free(*(void**)p);
}
static inline void
cleanup_close(int *p) {
close(*p);
}
static inline void
cleanup_udev_unref(struct udev **udev) {
if (*udev)
udev_unref(*udev);
}
static inline void
cleanup_udev_device_unref(struct udev_device **udev_device) {
if (*udev_device)
udev_device_unref(*udev_device);
}
#define _cleanup_(x) __attribute__((cleanup(x)))
#define _cleanup_free_ _cleanup_(cleanup_free)
#define _cleanup_close_ _cleanup_(cleanup_close)
#define _cleanup_udev_unref_ _cleanup_(cleanup_udev_unref)
#define _cleanup_udev_device_unref_ _cleanup_(cleanup_udev_device_unref)
static inline char*
strncpy_safe(char *dest, const char *src, size_t n)
{
@@ -200,4 +152,20 @@ long_set_bit_state(unsigned long *array, int bit, int state)
const char *
udev_prop_value(struct udev_device *device,
const char *property_name);
#endif /* LIBRATBAG_UTIL_H */
/**
* Converts a string from UTF-8 to the encoding specified. Returns the number
* of bytes written to buf on success, or negative errno value on failure.
*/
ssize_t
ratbag_utf8_to_enc(char *buf, size_t buf_len, const char *to_enc,
const char *format, ...) __attribute__((format(printf, 4, 5)));
/**
* Converts a string from the given encoding into UTF-8. The memory for the
* result is allocated and a pointer to the result is placed in *out. Returns
* the number of bytes in the UTF-8 version of the string on success, negative
* errno value on failure.
*/
ssize_t
ratbag_utf8_from_enc(char *in_buf, size_t in_len, const char *from_enc,
char **out);

View File

@@ -1,32 +0,0 @@
/*
* Copyright © 2013 Jonas Ådahl
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRATBAG_VERSION_H
#define LIBRATBAG_VERSION_H
#define LIBRATBAG_VERSION_MAJOR @LIBRATBAG_VERSION_MAJOR@
#define LIBRATBAG_VERSION_MINOR @LIBRATBAG_VERSION_MINOR@
#define LIBRATBAG_VERSION_MICRO @LIBRATBAG_VERSION_MICRO@
#define LIBRATBAG_VERSION "@LIBRATBAG_VERSION@"
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -21,8 +21,7 @@
* DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRATBAG_H
#define LIBRATBAG_H
#pragma once
#ifdef __cplusplus
extern "C" {
@@ -30,6 +29,7 @@ extern "C" {
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <libudev.h>
#define LIBRATBAG_ATTRIBUTE_PRINTF(_format, _args) \
@@ -54,6 +54,8 @@ extern "C" {
*
* @defgroup button Button configuration
*
* @defgroup led LED configuration
*
* @defgroup resolution Resolution and frequency mappings
*
* A device's sensor resolution and report rate can be configured per
@@ -125,6 +127,81 @@ struct ratbag_button;
*/
struct ratbag_resolution;
/**
* @ingroup led
* @struct ratbag_color
*
* Represents LED color in RGB format.
* each color component is integer 0 - 255
*/
struct ratbag_color {
unsigned int red;
unsigned int green;
unsigned int blue;
};
/**
* @ingroup led
* @struct ratbag_led
*
* Represents a led on the device.
*/
struct ratbag_led;
/**
* @ingroup button
* @struct ratbag_macro
*
* Represents a macro that can be assigned to a button.
*
* This struct is refcounted, use ratbag_button_macro_ref() and
* ratbag_button_macro_unref().
*/
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
@@ -325,12 +402,18 @@ ratbag_unref(struct ratbag *ratbag);
* The device is refcounted with an initial value of at least 1.
* Use ratbag_device_unref() to release the device.
*
* @return A new device based on the udev device, or NULL in case of
* failure.
* @param ratbag A previously initialized ratbag context
* @param udev_device The udev device that points at the device
* @param device Set to a new device based on the udev device.
*
* @return 0 on success or the error.
* @retval RATBAG_ERROR_DEVICE The given device does not exist or is not
* supported by libratbag.
*/
struct ratbag_device*
enum ratbag_error_code
ratbag_device_new_from_udev_device(struct ratbag *ratbag,
struct udev_device *device);
struct udev_device *udev_device,
struct ratbag_device **device);
/**
* @ingroup device
@@ -407,6 +490,29 @@ ratbag_device_get_svg_name(const struct ratbag_device* 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.
@@ -416,6 +522,13 @@ enum ratbag_device_capability {
* 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
@@ -425,6 +538,29 @@ enum ratbag_device_capability {
*/
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.
@@ -437,12 +573,9 @@ enum ratbag_device_capability {
RATBAG_DEVICE_CAP_BUTTON_MACROS,
/**
* 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).
* The device supports assigning LED colors and effects
*/
RATBAG_DEVICE_CAP_DEFAULT_PROFILE,
RATBAG_DEVICE_CAP_LED = 400,
};
/**
@@ -461,6 +594,18 @@ int
ratbag_device_has_capability(const struct ratbag_device *device,
enum ratbag_device_capability cap);
/**
* @ingroup device
*
* Write any changes to the device. Depending on the device, this may take
* a couple of seconds.
*
* @param device A previously initialized ratbag device
* @return 0 on success or an error code otherwise
*/
enum ratbag_error_code
ratbag_device_commit(struct ratbag_device *device);
/**
* @ingroup device
*
@@ -490,6 +635,17 @@ ratbag_device_get_num_profiles(struct ratbag_device *device);
unsigned int
ratbag_device_get_num_buttons(struct ratbag_device *device);
/**
* @ingroup device
*
* Return the number of LEDs available on this device.
*
* @param device A previously initialized ratbag device
* @return The number of LEDs available on this device.
*/
unsigned int
ratbag_device_get_num_leds(struct ratbag_device *device);
/**
* @ingroup profile
*
@@ -515,6 +671,34 @@ ratbag_profile_ref(struct ratbag_profile *profile);
struct ratbag_profile *
ratbag_profile_unref(struct ratbag_profile *profile);
/**
* @ingroup profile
*
* Enable/disable the ratbag profile. For this to work, the device must support
* @ref RATBAG_DEVICE_CAP_DISABLE_PROFILE.
*
* @param profile A previously initialized ratbag profile
* @param enabled Whether to enable or disable the profile
*
* @return 0 on success or an error code otherwise
*/
enum ratbag_error_code
ratbag_profile_set_enabled(struct ratbag_profile *profile, bool enabled);
/**
* @ingroup profile
*
* Check whether the ratbag profile is enabled or not. For devices that don't
* support @ref RATBAG_DEVICE_CAP_DISABLE_PROFILE the profile will always be
* set to enabled.
*
* @param profile A previously initialized ratbag profile
*
* @return Whether the profile is enabled or not.
*/
bool
ratbag_profile_is_enabled(const struct ratbag_profile *profile);
/**
* @ingroup profile
*
@@ -583,9 +767,9 @@ ratbag_profile_is_active(struct ratbag_profile *profile);
*
* @param profile The profile to make the active profile.
*
* @return 0 on success or nonzero otherwise.
* @return 0 on success or an error code otherwise
*/
int
enum ratbag_error_code
ratbag_profile_set_active(struct ratbag_profile *profile);
/**
@@ -593,7 +777,7 @@ ratbag_profile_set_active(struct ratbag_profile *profile);
*
* Get the number of @ref ratbag_resolution available in this profile. A
* resolution mode is a tuple of (resolution, report rate), each mode can be
* fetched with ratbag_profile_get_resolution_by_idx().
* fetched with ratbag_profile_get_resolution().
*
* The returned value is the maximum number of modes available and thus
* identical for all profiles. However, some of the modes may not be
@@ -601,9 +785,9 @@ ratbag_profile_set_active(struct ratbag_profile *profile);
*
* @param profile A previously initialized ratbag profile
*
* @return The number of profiles available.
* @return The number of resolutions available.
*/
int
unsigned int
ratbag_profile_get_num_resolutions(struct ratbag_profile *profile);
/**
@@ -723,9 +907,9 @@ ratbag_resolution_has_capability(struct ratbag_resolution *resolution,
* @param resolution A previously initialized ratbag resolution
* @param dpi Set to the resolution in dpi, 0 to disable
*
* @return zero on success, non-zero otherwise
* @return zero on success or an error code on failure
*/
int
enum ratbag_error_code
ratbag_resolution_set_dpi(struct ratbag_resolution *resolution,
unsigned int dpi);
@@ -749,9 +933,9 @@ ratbag_resolution_set_dpi(struct ratbag_resolution *resolution,
* @param x The x resolution in dpi
* @param y The y resolution in dpi
*
* @return zero on success, non-zero otherwise
* @return zero on success or an error code on failure
*/
int
enum ratbag_error_code
ratbag_resolution_set_dpi_xy(struct ratbag_resolution *resolution,
unsigned int x, unsigned int y);
@@ -772,6 +956,32 @@ ratbag_resolution_set_dpi_xy(struct ratbag_resolution *resolution,
int
ratbag_resolution_get_dpi(struct ratbag_resolution *resolution);
/**
* @ingroup resolution
*
* Get the maximum allowed resolution in DPI for the resolution mode.
*
* @param resolution A previously initialized ratbag resolution
*
* @return The maximum resolution in dpi
* @retval 0 The maximum resolution is unknown to libratbag
*/
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);
/**
* @ingroup resolution
*
@@ -809,37 +1019,37 @@ ratbag_resolution_get_dpi_y(struct ratbag_resolution *resolution);
/**
* @ingroup resolution
*
* Set the the report rate in Hz for the resolution mode.
* Set the report rate in Hz for the resolution mode.
*
* A value of 0 hz disables the mode.
*
* If the resolution mode is the currently active mode and the profile is
* the currently active profile, the change takes effect immediately.
*
* @param resolution A previously initialized ratbag resolution
* @param hz Set to the report rate in Hz, may be 0
*
* @return zero on success, non-zero otherwise
*/
int
ratbag_resolution_set_report_rate(struct ratbag_resolution *resolution,
unsigned int hz);
/**
* @ingroup resolution
*
* Get the the report rate in Hz for the resolution mode.
*
* A value of 0 hz indicates the mode is disabled.
*
* If the resolution does not have the @ref
* RATBAG_RESOLUTION_CAP_INDIVIDUAL_REPORT_RATE capability, changing the
* report rate on one resolution changes the report rate for all resolutions
* in this profile.
*
* @param resolution A previously initialized ratbag resolution
* @param hz Set to the report rate in Hz, may be 0
*
* @return zero on success, non-zero otherwise
* @return zero on success or an error code on failure
*/
enum ratbag_error_code
ratbag_resolution_set_report_rate(struct ratbag_resolution *resolution,
unsigned int hz);
/**
* @ingroup resolution
*
* Get the report rate in Hz for the resolution mode.
*
* A value of 0 hz indicates the mode is disabled.
*
* @param resolution A previously initialized ratbag resolution
*
* @return The report rate for this resolution in Hz
*/
int
ratbag_resolution_get_report_rate(struct ratbag_resolution *resolution);
@@ -855,9 +1065,9 @@ ratbag_resolution_get_report_rate(struct ratbag_resolution *resolution);
*
* @param resolution A previously initialized ratbag resolution
*
* @return zero on success, non-zero otherwise
* @return zero on success or an error code on failure
*/
int
enum ratbag_error_code
ratbag_resolution_set_active(struct ratbag_resolution *resolution);
/**
@@ -889,9 +1099,9 @@ ratbag_resolution_is_active(const struct ratbag_resolution *resolution);
*
* @param resolution A previously initialized ratbag resolution
*
* @return zero on success, non-zero otherwise
* @return zero on success or an error code on failure
*/
int
enum ratbag_error_code
ratbag_resolution_set_default(struct ratbag_resolution *resolution);
/**
@@ -927,7 +1137,7 @@ ratbag_resolution_is_default(const struct ratbag_resolution *resolution);
*
* @return A button context, or NULL if the button does not exist.
*
* @see ratbag_get_num_buttons
* @see ratbag_device_get_num_buttons
*/
struct ratbag_button*
ratbag_profile_get_button(struct ratbag_profile *profile, unsigned int index);
@@ -1024,10 +1234,6 @@ ratbag_button_get_type(struct ratbag_button *button);
* The type assigned to a button.
*/
enum ratbag_button_action_type {
/**
* Button action is unknown
*/
RATBAG_BUTTON_ACTION_TYPE_UNKNOWN = -1,
/**
* Button is disabled
*/
@@ -1049,6 +1255,10 @@ enum ratbag_button_action_type {
* Button sends a user-defined key or button sequence
*/
RATBAG_BUTTON_ACTION_TYPE_MACRO,
/**
* Button action is unknown
*/
RATBAG_BUTTON_ACTION_TYPE_UNKNOWN = 1000,
};
/**
@@ -1101,6 +1311,7 @@ enum ratbag_button_action_special {
/* DPI switch */
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP,
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN,
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP,
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN,
RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE,
@@ -1108,6 +1319,7 @@ enum ratbag_button_action_special {
/* Profile */
RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP,
RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_DOWN,
RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP,
RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN,
@@ -1148,12 +1360,12 @@ ratbag_button_get_button(struct ratbag_button *button);
*
* @param button A previously initialized ratbag button
* @param btn The logical button number to assign to this button.
* @return 0 on success or nonzero otherwise. On success, the button's
* @return 0 on success or an error code otherwise. On success, the button's
* action is set to @ref RATBAG_BUTTON_ACTION_TYPE_BUTTON.
*
* @see ratbag_button_get_button
*/
int
enum ratbag_error_code
ratbag_button_set_button(struct ratbag_button *button,
unsigned int btn);
@@ -1173,6 +1385,172 @@ ratbag_button_set_button(struct ratbag_button *button,
enum ratbag_button_action_special
ratbag_button_get_special(struct ratbag_button *button);
/**
* @ingroup led
*
* RATBAG_LED_OFF - led is now off,
* RATBAG_LED_ON - led is on with static color,
* RATBAG_LED_CYCLE - led is cycling between all colors.
* RATBAG_LED_BREATHING - led is pulsating with static color
*
* Each LED mode has different properties, e.g. the brightness and rate are only
* available in modes @ref RATBAG_LED_CYCLE and @ref RATBAG_LED_BREATHING modes
*/
enum ratbag_led_mode {
RATBAG_LED_OFF = 0,
RATBAG_LED_ON,
RATBAG_LED_CYCLE,
RATBAG_LED_BREATHING,
};
/**
* @ingroup led
*
* LED types, usually based on their physical location
*/
enum ratbag_led_type {
RATBAG_LED_TYPE_UNKNOWN = -1,
RATBAG_LED_TYPE_LOGO = 0,
RATBAG_LED_TYPE_SIDE
};
/**
* @ingroup led
*
* Return a reference to the LED given by the index. The order of the
* LEDs is device-specific though.
*
* The LED is refcounted with an initial value of at least 1.
* Use ratbag_led_unref() to release the LED.
*
* @param profile A previously initialized ratbag profile
* @param index The index of the LED
*
* @return A LED context, or NULL if the LED does not exist.
*
* @see ratbag_device_get_profile
*/
struct ratbag_led *
ratbag_profile_get_led(struct ratbag_profile *profile, unsigned int index);
/**
* @ingroup led
*
* This function returns the type for ratbag_led.
*
* @param led A previously initialized ratbag LED
* @return The LED type @ref ratbag_led_type
*
* @see ratbag_led_set_mode
*/
enum ratbag_led_type
ratbag_led_get_type(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the mode for ratbag_led.
*
* @param led A previously initialized ratbag LED
* @return The LED mod @ref ratbag_led_mode
*
* @see ratbag_led_set_mode
*/
enum ratbag_led_mode
ratbag_led_get_mode(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the led color.
*
* @param led A previously initialized ratbag LED
* @return The LED color in @ref ratbag_led_mode
*
* @see ratbag_led_set_color
*/
struct ratbag_color
ratbag_led_get_color(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the LED effect rate.
*
* @param led A previously initialized ratbag LED
* @return The LED rate in Hz, can be 100 - 20000
*
* @see ratbag_led_set_effect_rate
*/
int
ratbag_led_get_effect_rate(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the LED brightness.
*
* @param led A previously initialized ratbag LED
* @return The LED brightness 0 - 255
*
* @see ratbag_led_get_brightness
*/
unsigned int
ratbag_led_get_brightness(struct ratbag_led *led);
/**
* @ingroup led
*
* this function sets the LED mode.
*
* @param led A previously initialized ratbag LED
* @param mode LED mode @ref ratbag_led_mode.
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_mode
*/
enum ratbag_error_code
ratbag_led_set_mode(struct ratbag_led *led, enum ratbag_led_mode mode);
/**
* @ingroup led
*
* If the LED's mode is @ref RATBAG_LED_ON or @ref RATBAG_LED_BREATHING
* then this function sets the LED color, otherwise it has no effect.
*
* @param led A previously initialized ratbag LED
* @param color A LED color.
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_color
*/
enum ratbag_error_code
ratbag_led_set_color(struct ratbag_led *led, struct ratbag_color color);
/**
* @ingroup led
*
* If the LED's mode is @ref RATBAG_LED_CYCLE or @ref RATBAG_LED_BREATHING
* then this function sets the LED rate in Hz
*
* @param led A previously initialized ratbag LED
* @param rate Effect rate in hz, 100 - 20000
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_effect_rate
*/
enum ratbag_error_code
ratbag_led_set_effect_rate(struct ratbag_led *led, unsigned int rate);
/**
* @ingroup led
*
* If the LED's mode is @ref RATBAG_LED_CYCLE or @ref RATBAG_LED_BREATHING
* then this function sets the LED brightness, otherwise it has no effect.
*
* @param led A previously initialized ratbag LED
* @param brightness Effect brightness 0 - 255
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_brightness
*/
enum ratbag_error_code
ratbag_led_set_brightness(struct ratbag_led *led, unsigned int brightness);
/**
* @ingroup button
*
@@ -1180,12 +1558,12 @@ ratbag_button_get_special(struct ratbag_button *button);
*
* @param button A previously initialized ratbag button
* @param action The special action to assign to this button.
* @return 0 on success or nonzero otherwise. On success, the button's
* @return 0 on success or an error code otherwise. On success, the button's
* action is set to @ref RATBAG_BUTTON_ACTION_TYPE_SPECIAL.
*
* @see ratbag_button_get_button
*/
int
enum ratbag_error_code
ratbag_button_set_special(struct ratbag_button *button,
enum ratbag_button_action_special action);
@@ -1226,10 +1604,10 @@ ratbag_button_get_key(struct ratbag_button *button,
* @param sz The size of the modifiers array. sz may be 0 if no modifiers
* are required.
*
* @return 0 on success or nonzero otherwise. On success, the button's
* @return 0 on success or an error code otherwise. On success, the button's
* action is set to @ref RATBAG_BUTTON_ACTION_TYPE_KEY.
*/
int
enum ratbag_error_code
ratbag_button_set_key(struct ratbag_button *button,
unsigned int key,
unsigned int *modifiers,
@@ -1240,10 +1618,10 @@ ratbag_button_set_key(struct ratbag_button *button,
*
* @param button A previously initialized ratbag button
*
* @return 0 on success or nonzero otherwise. On success, the button's
* @return 0 on success or an error code otherwise. On success, the button's
* action is set to @ref RATBAG_BUTTON_ACTION_TYPE_NONE.
*/
int
enum ratbag_error_code
ratbag_button_disable(struct ratbag_button *button);
/**
@@ -1262,101 +1640,175 @@ enum ratbag_macro_event_type {
/**
* @ingroup button
*
* If a button's action is @ref RATBAG_BUTTON_ACTION_TYPE_MACRO,
* this function returns the macro name.
* @param macro A previously initialized ratbag button macro
*
* If the button's action type is not @ref RATBAG_BUTTON_ACTION_TYPE_MACRO,
* this function returns NULL.
*
* @param button A previously initialized ratbag button
*
* @return The name of the macro associated to the button.
* @return The name of the macro
*/
const char *
ratbag_button_get_macro_name(struct ratbag_button *button);
ratbag_button_macro_get_name(struct ratbag_button_macro *macro);
/**
* @ingroup button
*
* If a button's action is @ref RATBAG_BUTTON_ACTION_TYPE_MACRO,
* this function returns the macro event type configured for the event at the
* @param macro A previously initialized ratbag button macro
*
* @return The maximum number of events that can be assigned to this macro
*/
unsigned int
ratbag_button_macro_get_num_events(struct ratbag_button_macro *macro);
/**
* @ingroup button
*
* Returns the macro event type configured for the event at the
* given index.
*
* If the button's action type is not @ref RATBAG_BUTTON_ACTION_TYPE_MACRO,
* this function returns @ref RATBAG_MACRO_EVENT_INVALID.
* The behavior of this function for an index equal to or greater than the
* return value of ratbag_button_macro_get_num_events() is undefined.
*
* @param button A previously initialized ratbag button
* @param macro A previously initialized ratbag button macro
* @param index An index of the event within the macro we are interested in.
*
* @return The type of the event at the given index
*/
enum ratbag_macro_event_type
ratbag_button_get_macro_event_type(struct ratbag_button *button, unsigned int index);
ratbag_button_macro_get_event_type(struct ratbag_button_macro *macro,
unsigned int index);
/**
* @ingroup button
*
* If a button's action is @ref RATBAG_BUTTON_ACTION_TYPE_MACRO, and if the
* event stored at the given index is @ref RATBAG_MACRO_EVENT_KEY_PRESSED or
* @ref RATBAG_MACRO_EVENT_KEY_RELEASED, this function returns the key code
* configured for the event at the given index.
* If the event stored at the given index is @ref
* RATBAG_MACRO_EVENT_KEY_PRESSED or @ref RATBAG_MACRO_EVENT_KEY_RELEASED,
* this function returns the key code configured for the event at the given
* index.
*
* If the button's action type is not @ref RATBAG_BUTTON_ACTION_TYPE_MACRO,
* or if the event stored at the given index is not
* @ref RATBAG_MACRO_EVENT_KEY_PRESSED or @ref RATBAG_MACRO_EVENT_KEY_RELEASED,
* this function returns -EINVAL.
* The behavior of this function for an index equal to or greater than the
* return value of ratbag_button_macro_get_num_events() is undefined.
*
* @param button A previously initialized ratbag button
* @param macro A previously initialized ratbag button macro
* @param index An index of the event within the macro we are interested in.
*
* @return The key of the event at the given index
*/
int
ratbag_button_get_macro_event_key(struct ratbag_button *button, unsigned int index);
ratbag_button_macro_get_event_key(struct ratbag_button_macro*macro,
unsigned int index);
/**
* @ingroup button
*
* If a button's action is @ref RATBAG_BUTTON_ACTION_TYPE_MACRO, and if the
* event stored at the given index is @ref RATBAG_MACRO_EVENT_WAIT, this
* function returns the timeout configured for the event at the given index.
* If the event stored at the given index is @ref RATBAG_MACRO_EVENT_WAIT,
* this function returns the timeout configured for the event at the given
* index.
*
* If the button's action type is not @ref RATBAG_BUTTON_ACTION_TYPE_MACRO,
* or if the event stored at the given index is not
* @ref RATBAG_MACRO_EVENT_WAIT, this function returns -EINVAL.
* The behavior of this function for an index equal to or greater than the
* return value of ratbag_button_macro_get_num_events() is undefined.
*
* @param button A previously initialized ratbag button
* @param macro A previously initialized ratbag button macro
* @param index An index of the event within the macro we are interested in.
*
* @return The tiemout of the event at the given index
* @return The timeout of the event at the given index
*/
int
ratbag_button_get_macro_event_timeout(struct ratbag_button *button, unsigned int index);
ratbag_button_macro_get_event_timeout(struct ratbag_button_macro *macro,
unsigned int index);
/**
* @ingroup button
*
* Sets the button's action to @ref RATBAG_BUTTON_ACTION_TYPE_MACRO and
* allocates the required memory to store the macro.
* assigns the given macro to this button.
*
* libratbag does not use the macro struct passed in, it extracts the
* required information from the struct. Changes to the macro after a call
* to ratbag_button_set_macro() are not reflected in the device until a
* subsequent call to ratbag_button_set_macro().
*
* @param button A previously intialized ratbag button
* @param macro A fully initialized macro
*
* @return 0 on success or nonzero otherwise
*/
int
ratbag_button_set_macro(struct ratbag_button *button, const char *name);
enum ratbag_error_code
ratbag_button_set_macro(struct ratbag_button *button,
const struct ratbag_button_macro *macro);
int
ratbag_button_write_macro(struct ratbag_button *button);
/**
* @ingroup button
*
* Initialize a new button macro.
*
* The macro is refcounted with an initial value of at least 1.
* Use ratbag_button_macro_unref() to release the macro.
*
* Note that some devices have limited storage for the macro names.
* libratbag silently shortens macro names to the longest string the device
* is capable of storing.
*
* @param name The name to assign to this macro.
*
* @return An "empty" button macro
*/
struct ratbag_button_macro *
ratbag_button_macro_new(const char *name);
/**
* @ingroup button
*
* If a button's action is @ref RATBAG_BUTTON_ACTION_TYPE_MACRO,
* this function returns the current button macro. The macro is a copy of
* the one used on the device, changes to the macro are not reflected on the
* device until a subsequent call to ratbag_button_set_macro().
*
* If a button's action is not @ref RATBAG_BUTTON_ACTION_TYPE_MACRO,
* this function returns NULL.
*
* @param button A previously initialized ratbag button
*/
struct ratbag_button_macro *
ratbag_button_get_macro(struct ratbag_button *button);
/**
* @ingroup button
*
* Sets the macro's event at the given index to the given type with the
* key code or timeout given.
*
* The behavior of this function for an index equal to or greater than the
* return value of ratbag_button_macro_get_num_events() is undefined.
*/
int
ratbag_button_set_macro_event(struct ratbag_button *button,
enum ratbag_error_code
ratbag_button_macro_set_event(struct ratbag_button_macro *macro,
unsigned int index,
enum ratbag_macro_event_type type,
unsigned int data);
/**
* @ingroup button
*
* Add a reference to the macro. A macro is destroyed whenever the
* reference count reaches 0. See @ref ratbag_button_macro_unref.
*
* @param macro A previously initialized valid ratbag button macro
* @return The passed ratbag macro
*/
struct ratbag_button_macro *
ratbag_button_macro_ref(struct ratbag_button_macro *macro);
/**
* @ingroup button
*
* Dereference the ratbag button macro. When the internal refcount reaches
* zero, all resources associated with this object are released. The object
* must be considered invalid once unref is called.
*
* @param macro A previously initialized ratbag button macro
* @return Always NULL
*/
struct ratbag_button_macro *
ratbag_button_macro_unref(struct ratbag_button_macro *macro);
/**
* @ingroup button
*
@@ -1382,7 +1834,31 @@ ratbag_button_ref(struct ratbag_button *button);
struct ratbag_button *
ratbag_button_unref(struct ratbag_button *button);
/**
* @ingroup led
*
* Add a reference to the led. A led is destroyed whenever the
* reference count reaches 0. See @ref ratbag_led_unref.
*
* @param led A previously initialized valid ratbag led
* @return The passed ratbag led
*/
struct ratbag_led *
ratbag_led_ref(struct ratbag_led *led);
/**
* @ingroup led
*
* Dereference the ratbag led. When the internal refcount reaches
* zero, all resources associated with this object are released. The object
* must be considered invalid once unref is called.
*
* @param led A previously initialized ratbag led
* @return Always NULL
*/
struct ratbag_led *
ratbag_led_unref(struct ratbag_led *led);
#ifdef __cplusplus
}
#endif
#endif /* LIBRATBAG_H */

View File

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

View File

@@ -1,6 +1,6 @@
/* in alphabetical order! */
LIBRATBAG_0.1.0 {
LIBRATBAG_0.5.0 {
global:
ratbag_create_context;
ratbag_button_disable;
@@ -8,22 +8,27 @@ global:
ratbag_button_get_type;
ratbag_button_get_button;
ratbag_button_get_key;
ratbag_button_get_macro_event_type;
ratbag_button_get_macro_event_key;
ratbag_button_get_macro_event_timeout;
ratbag_button_get_macro_name;
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_macro_event;
ratbag_button_set_special;
ratbag_button_set_user_data;
ratbag_button_unref;
ratbag_button_write_macro;
ratbag_device_commit;
ratbag_device_get_name;
ratbag_device_get_num_buttons;
ratbag_device_get_num_profiles;
@@ -40,16 +45,33 @@ global:
ratbag_log_get_priority;
ratbag_log_set_handler;
ratbag_log_set_priority;
ratbag_profile_get_led;
ratbag_led_get_mode;
ratbag_led_get_type;
ratbag_led_get_color;
ratbag_led_get_effect_rate;
ratbag_led_get_brightness;
ratbag_led_ref;
ratbag_led_set_mode;
ratbag_led_set_color;
ratbag_led_set_effect_rate;
ratbag_led_set_brightness;
ratbag_led_unref;
ratbag_device_get_num_leds;
ratbag_profile_get_button;
ratbag_profile_get_num_resolutions;
ratbag_profile_get_resolution;
ratbag_profile_get_user_data;
ratbag_profile_is_active;
ratbag_profile_is_enabled;
ratbag_profile_ref;
ratbag_profile_set_user_data;
ratbag_profile_set_active;
ratbag_profile_set_enabled;
ratbag_profile_unref;
ratbag_resolution_get_dpi;
ratbag_resolution_get_dpi_maximum;
ratbag_resolution_get_dpi_minimum;
ratbag_resolution_get_dpi_x;
ratbag_resolution_get_dpi_y;
ratbag_resolution_get_report_rate;

287
src/shared-macro.h Normal file
View File

@@ -0,0 +1,287 @@
#pragma once
/***
This file is part of ratbagd.
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
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 <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
/*
* We require:
* sizeof(void*) == sizeof(long)
* sizeof(long) == 4 || sizeof(long) == 8
* sizeof(int) == 4
* The linux kernel requires the same from the toolchain, so this should work
* just fine.
*/
#if __SIZEOF_POINTER__ != __SIZEOF_LONG__
# error "sizeof(void*) != sizeof(long)"
#elif __SIZEOF_LONG__ != 4 && __SIZEOF_LONG__ != 8
# error "sizeof(long) != 4 && sizeof(long) != 8"
#elif __SIZEOF_INT__ != 4
# error "sizeof(int) != 4"
#endif
/*
* Shortcuts for gcc attributes. See GCC manual for details.
*/
#define _alignas_(_x) __attribute__((aligned(__alignof(_x))))
#define _alloc_(...) __attribute__((alloc_size(__VA_ARGS__)))
#define _cleanup_(_x) __attribute__((cleanup(_x)))
#define _const_ __attribute__((const))
#define _deprecated_ __attribute__((deprecated))
#define _hidden_ __attribute__((visibility("hidden")))
#define _likely_(_x) (__builtin_expect(!!(_x), 1))
#define _malloc_ __attribute__((malloc))
#define _packed_ __attribute__((packed))
#define _printf_(_a, _b) __attribute__((format (printf, _a, _b)))
#define _public_ __attribute__((visibility("default")))
#define _pure_ __attribute__((pure))
#define _sentinel_ __attribute__((sentinel))
#define _unlikely_(_x) (__builtin_expect(!!(_x), 0))
#define _unused_ __attribute__((unused))
#define _weak_ __attribute__((weak))
#define _weakref_(_x) __attribute__((weakref(#_x)))
/* container_of macro */
#ifdef __GNUC__
#define container_of(ptr, sample, member) \
(__typeof__(sample))((char *)(ptr) - \
((char *)&(sample)->member - (char *)(sample)))
#else
#define container_of(ptr, sample, member) \
(void *)((char *)(ptr) - \
((char *)&(sample)->member - (char *)(sample)))
#endif
#define LONG_BITS (sizeof(long) * 8)
#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
#define ARRAY_FOR_EACH(_arr, _elem) \
for (size_t _i = 0; _i < ARRAY_LENGTH(_arr) && (_elem = &_arr[_i]); _i++)
#define AS_MASK(v) (1 << (v))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
/*
* DECIMAL_TOKEN_MAX() - calculate maximum length of the decimal representation
* of an integer
* @_type: type of integer
*/
#define DECIMAL_TOKEN_MAX(_type) \
(1 + (sizeof(_type) <= 1 ? 3 : \
sizeof(_type) <= 2 ? 5 : \
sizeof(_type) <= 4 ? 10 : \
sizeof(_type) <= 8 ? 20 : \
sizeof(int[-2 * (sizeof(_type) > 8)])))
/*
* PROTECT_ERRNO: make sure a function protects errno
*/
static inline void reset_errno(int *saved_errno)
{
errno = *saved_errno;
}
#define PROTECT_ERRNO _cleanup_(reset_errno) _unused_ int saved_errno = errno
#define _cleanup_free_ _cleanup_(freep)
#define _cleanup_close_ _cleanup_(safe_closep)
/*
* DEFINE_TRIVIAL_CLEANUP_FUNC() - define helper suitable for _cleanup_()
* @_type: type of object to cleanup
* @_func: function to call on cleanup
*/
#define DEFINE_TRIVIAL_CLEANUP_FUNC(_type, _func) \
static inline void _func##p(_type *_p) \
{ \
if (*_p) \
_func(*_p); \
} \
struct __useless_struct_to_allow_trailing_semicolon__
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev*, udev_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_device*, udev_device_unref);
/*
* ELEMENTSOF() - number of array elements
* @_array: C array
*
* This calculates the number of elements (compared to the byte size) of
* the given C array. This returns (void) if the passed argument is not
* actually a valid C array (decided at compile time).
*/
#define ELEMENTSOF(_array) \
__extension__ (__builtin_choose_expr( \
!__builtin_types_compatible_p(typeof(_array), \
typeof(&*(_array))), \
sizeof(_array) / sizeof((_array)[0]), \
(void)0))
/*
* negative_errno() - return negative errno
*
* This helper should be used to shut up gcc if you know 'errno' is
* negative. Instead of "return -errno;", use "return negative_errno();"
* It will suppress bogus gcc warnings in case it assumes 'errno' might
* be 0 and thus the caller's error-handling might not be triggered.
*/
static inline int negative_errno(void)
{
return (errno <= 0) ? -EINVAL : -errno;
}
/*
* mfree() - free memory
* @mem: memory to free
*
* This is basically the same as free(), but returns NULL. This makes free()
* follow the same style as all our other destructors.
*/
static inline void *mfree(void *mem)
{
free(mem);
return NULL;
}
/*
* safe_close() - safe variant of close(2)
* @fd: fd to close
*
* This is the same as close(2), but allows passing negative FDs, which makes
* it a no-op. Furthermore, this always returns -1.
*/
static inline int safe_close(int fd)
{
if (fd >= 0) {
PROTECT_ERRNO;
close(fd);
}
return -1;
}
/*
* Miscellaneous cleanup helpers
* All these helpers are suitable for use with _cleanup_(). They call the helper
* they're named after on the variable marked for cleanup.
*/
static inline void freep(void *p)
{
free(*(void**)p);
}
static inline void safe_closep(int *fd)
{
safe_close(*fd);
}
/*
* streq() - test whether two strings are equal
* @_a: string A
* @_b: string B
*/
#define streq(_a, _b) (strcmp((_a), (_b)) == 0)
#define strneq(s1, s2, n) (strncmp((s1), (s2), (n)) == 0)
/*
* streq_ptr() - test whether two strings are equal, considering NULL valid
* @a: string A or NULL
* @b: string B or NULL
*/
static inline bool streq_ptr(const char *a, const char *b)
{
return (a && b) ? streq(a, b) : (!a && !b);
}
/*
* startswith() - test prefix of a string
* @s: string to test
* @prefix: prefix to test for
*
* This returns a pointer to the first character in @s that follows @prefix. If
* @s does not start with @prefix, NULL is returned.
*/
static inline char *startswith(const char *s, const char *prefix)
{
size_t l;
l = strlen(prefix);
if (strncmp(s, prefix, l) == 0)
return (char*) s + l;
return NULL;
}
/*
* safe_atou() - safe variant of strtoul()
* @s: string to parse
* @ret_u: output storage for parsed integer
*
* This is a sane and safe variant of strtoul(), which rejects any invalid
* input, or trailing garbage.
*
* Return: 0 on success, negative error code on failure.
*/
static inline int safe_atou(const char *s, unsigned int *ret_u)
{
char *x = NULL;
unsigned long l;
assert(s);
assert(ret_u);
errno = 0;
l = strtoul(s, &x, 0);
if (!x || x == s || *x || errno)
return errno > 0 ? -errno : -EINVAL;
if ((unsigned long)(unsigned int)l != l)
return -ERANGE;
*ret_u = (unsigned int)l;
return 0;
}
/*
* now() - returns current time in nano-seconds
* @clock: clock to use
*/
static inline uint64_t now(clockid_t clock)
{
struct timespec spec = {};
clock_gettime(clock, &spec);
return spec.tv_sec * 1000ULL * 1000ULL * 1000ULL + spec.tv_nsec;
}

455
src/shared-rbtree.c Normal file
View File

@@ -0,0 +1,455 @@
/***
This file is part of ratbagd.
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
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.
***/
/*
* Open Red-Black-Tree Implementation
* You're highly recommended to read a paper on rb-trees before reading this
* code. The reader is expected to be familiar with the different cases that
* might occur during insertion and removal of elements. The comments in this
* file do not contain a full prove for correctness.
*/
#include "config.h"
#include "shared-rbtree.h"
RBNode *rbnode_leftmost(RBNode *n) {
if (n)
while (n->left)
n = n->left;
return n;
}
RBNode *rbnode_rightmost(RBNode *n) {
if (n)
while (n->right)
n = n->right;
return n;
}
RBNode *rbtree_first(RBTree *t) {
return rbnode_leftmost(t->root);
}
RBNode *rbtree_last(RBTree *t) {
return rbnode_rightmost(t->root);
}
RBNode *rbnode_next(RBNode *n) {
RBNode *p;
if (!rbnode_linked(n))
return NULL;
if (n->right)
return rbnode_leftmost(n->right);
while ((p = rbnode_parent(n)) && n == p->right)
n = p;
return p;
}
RBNode *rbnode_prev(RBNode *n) {
RBNode *p;
if (!rbnode_linked(n))
return NULL;
if (n->left)
return rbnode_rightmost(n->left);
while ((p = rbnode_parent(n)) && n == p->left)
n = p;
return p;
}
static inline void rbnode_reparent(RBNode *n, RBNode *p, unsigned long c) {
/* change color and/or parent of a node */
rbtree_assert(!((unsigned long)p & 1));
rbtree_assert(c < 2);
n->__parent_and_color = (RBNode*)((unsigned long)p | c);
}
static inline void rbtree_reparent(RBTree *t, RBNode *p, RBNode *old, RBNode *new) {
/* change previous parent/root from old to new node */
if (p) {
if (p->left == old)
p->left = new;
else
p->right = new;
} else {
t->root = new;
}
}
static inline RBNode *rbtree_paint_one(RBTree *t, RBNode *n) {
RBNode *p, *g, *gg, *u, *x;
/*
* Paint a single node according to RB-Tree rules. The node must
* already be linked into the tree and painted red.
* We repaint the node or rotate the tree, if required. In case a
* recursive repaint is required, the next node to be re-painted
* is returned.
* p: parent
* g: grandparent
* gg: grandgrandparent
* u: uncle
* x: temporary
*/
/* node is red, so we can access the parent directly */
p = n->__parent_and_color;
if (!p) {
/* Case 1:
* We reached the root. Mark it black and be done. As all
* leaf-paths share the root, the ratio of black nodes on each
* path stays the same. */
rbnode_reparent(n, p, RBNODE_BLACK);
n = NULL;
} else if (rbnode_black(p)) {
/* Case 2:
* The parent is already black. As our node is red, we did not
* change the number of black nodes on any path, nor do we have
* multiple consecutive red nodes. */
n = NULL;
} else if (p == p->__parent_and_color->left) { /* parent is red, so grandparent exists */
g = p->__parent_and_color;
gg = rbnode_parent(g);
u = g->right;
if (u && rbnode_red(u)) {
/* Case 3:
* Parent and uncle are both red. We know the
* grandparent must be black then. Repaint parent and
* uncle black, the grandparent red and recurse into
* the grandparent. */
rbnode_reparent(p, g, RBNODE_BLACK);
rbnode_reparent(u, g, RBNODE_BLACK);
rbnode_reparent(g, gg, RBNODE_RED);
n = g;
} else {
/* parent is red, uncle is black */
if (n == p->right) {
/* Case 4:
* We're the right child. Rotate on parent to
* become left child, so we can handle it the
* same as case 5. */
x = n->left;
p->right = n->left;
n->left = p;
if (x)
rbnode_reparent(x, p, RBNODE_BLACK);
rbnode_reparent(p, n, RBNODE_RED);
p = n;
}
/* 'n' is invalid from here on! */
n = NULL;
/* Case 5:
* We're the red left child or a red parent, black
* grandparent and uncle. Rotate on grandparent and
* switch color with parent. Number of black nodes on
* each path stays the same, but we got rid of the
* double red path. As the grandparent is still black,
* we're done. */
x = p->right;
g->left = x;
p->right = g;
if (x)
rbnode_reparent(x, g, RBNODE_BLACK);
rbnode_reparent(p, gg, RBNODE_BLACK);
rbnode_reparent(g, p, RBNODE_RED);
rbtree_reparent(t, gg, g, p);
}
} else /* if (p == p->__parent_and_color->left) */ { /* same as above, but mirrored */
g = p->__parent_and_color;
gg = rbnode_parent(g);
u = g->left;
if (u && rbnode_red(u)) {
rbnode_reparent(p, g, RBNODE_BLACK);
rbnode_reparent(u, g, RBNODE_BLACK);
rbnode_reparent(g, gg, RBNODE_RED);
n = g;
} else {
if (n == p->left) {
x = n->right;
p->left = n->right;
n->right = p;
if (x)
rbnode_reparent(x, p, RBNODE_BLACK);
rbnode_reparent(p, n, RBNODE_RED);
p = n;
}
n = NULL;
x = p->left;
g->right = x;
p->left = g;
if (x)
rbnode_reparent(x, g, RBNODE_BLACK);
rbnode_reparent(p, gg, RBNODE_BLACK);
rbnode_reparent(g, p, RBNODE_RED);
rbtree_reparent(t, gg, g, p);
}
}
return n;
}
static inline void rbtree_paint(RBTree *t, RBNode *n) {
while (n)
n = rbtree_paint_one(t, n);
}
void rbtree_add(RBTree *t, RBNode *p, RBNode **l, RBNode *n) {
n->__parent_and_color = p;
n->left = n->right = NULL;
*l = n;
rbtree_paint(t, n);
}
static inline RBNode *rbtree_rebalance_one(RBTree *t, RBNode *p, RBNode *n) {
RBNode *s, *x, *y, *g;
/*
* Rebalance tree after a node was removed. This happens only if you
* remove a black node and one path is now left with an unbalanced
* number or black nodes.
* This function assumes all paths through p and n have one black node
* less than all other paths. If recursive fixup is required, the
* current node is returned.
*/
if (n == p->left) {
s = p->right;
if (rbnode_red(s)) {
/* Case 3:
* We have a red node as sibling. Rotate it onto our
* side so we can later on turn it black. This way, we
* gain the additional black node in our path. */
g = rbnode_parent(p);
x = s->left;
p->right = x;
s->left = p;
rbnode_reparent(x, p, RBNODE_BLACK);
rbnode_reparent(s, g, rbnode_color(p));
rbnode_reparent(p, s, RBNODE_RED);
rbtree_reparent(t, g, p, s);
s = x;
}
x = s->right;
if (!x || rbnode_black(x)) {
y = s->left;
if (!y || rbnode_black(y)) {
/* Case 4:
* Our sibling is black and has only black
* children. Flip it red and turn parent black.
* This way we gained a black node in our path,
* or we fix it recursively one layer up, which
* will rotate the red sibling as parent. */
rbnode_reparent(s, p, RBNODE_RED);
if (rbnode_black(p))
return p;
rbnode_reparent(p, rbnode_parent(p), RBNODE_BLACK);
return NULL;
}
/* Case 5:
* Left child of our sibling is red, right one is black.
* Rotate on parent so the right child of our sibling is
* now red, and we can fall through to case 6. */
x = y->right;
s->left = y->right;
y->right = s;
p->right = y;
if (x)
rbnode_reparent(x, s, RBNODE_BLACK);
x = s;
s = y;
}
/* Case 6:
* The right child of our sibling is red. Rotate left and flip
* colors, which gains us an additional black node in our path,
* that was previously on our sibling. */
g = rbnode_parent(p);
y = s->left;
p->right = y;
s->left = p;
rbnode_reparent(x, s, RBNODE_BLACK);
if (y)
rbnode_reparent(y, p, rbnode_color(y));
rbnode_reparent(s, g, rbnode_color(p));
rbnode_reparent(p, s, RBNODE_BLACK);
rbtree_reparent(t, g, p, s);
} else /* if (!n || n == p->right) */ { /* same as above, but mirrored */
s = p->left;
if (rbnode_red(s)) {
g = rbnode_parent(p);
x = s->right;
p->left = x;
s->right = p;
rbnode_reparent(x, p, RBNODE_BLACK);
rbnode_reparent(s, g, RBNODE_BLACK);
rbnode_reparent(p, s, RBNODE_RED);
rbtree_reparent(t, g, p, s);
s = x;
}
x = s->left;
if (!x || rbnode_black(x)) {
y = s->right;
if (!y || rbnode_black(y)) {
rbnode_reparent(s, p, RBNODE_RED);
if (rbnode_black(p))
return p;
rbnode_reparent(p, rbnode_parent(p), RBNODE_BLACK);
return NULL;
}
x = y->left;
s->right = y->left;
y->left = s;
p->left = y;
if (x)
rbnode_reparent(x, s, RBNODE_BLACK);
x = s;
s = y;
}
g = rbnode_parent(p);
y = s->right;
p->left = y;
s->right = p;
rbnode_reparent(x, s, RBNODE_BLACK);
if (y)
rbnode_reparent(y, p, rbnode_color(y));
rbnode_reparent(s, g, rbnode_color(p));
rbnode_reparent(p, s, RBNODE_BLACK);
rbtree_reparent(t, g, p, s);
}
return NULL;
}
static inline void rbtree_rebalance(RBTree *t, RBNode *p) {
RBNode *n = NULL;
while (p) {
n = rbtree_rebalance_one(t, p, n);
p = n ? rbnode_parent(n) : NULL;
}
}
void rbtree_remove(RBTree *t, RBNode *n) {
RBNode *p, *s, *gc, *x, *next = NULL;
unsigned long c;
/*
* To remove an interior node from a binary tree, we simply find its
* successor, swap both nodes and then remove the node. Therefore, the
* only interesting case is were the node to be removed has at most one
* child.
* p: parent
* s: successor
* gc: grand-...-child
* x: temporary
* next: next node to rebalance on
*/
if (!n->left) {
/* Case 1:
* We have at most one child, which must be red. We're
* guaranteed to be black then. Replace our node with the child
* (in case it exists), but turn it black.
* If no child exists, we need to rebalance, in case we were
* black as our path now lost a black node. */
p = rbnode_parent(n);
c = rbnode_color(n);
rbtree_reparent(t, p, n, n->right);
if (n->right)
rbnode_reparent(n->right, p, c);
else
next = (c == RBNODE_BLACK) ? p : NULL;
} else if (!n->right) {
/* case 1 mirrored (but n->left guaranteed non-NULL) */
p = rbnode_parent(n);
c = rbnode_color(n);
rbnode_reparent(n->left, p, c);
rbtree_reparent(t, p, n, n->left);
} else {
/* Case 2:
* We're an interior node. Find our successor and swap it with
* our node. Then remove our node. For performance reasons we
* don't perform the full swap, but skip links that are about to
* be removed, anyway. */
s = n->right;
if (!s->left) {
/* right child is next, no need to touch grandchild */
p = s;
gc = s->right;
} else {
/* find successor and swap partially */
s = rbnode_leftmost(s);
p = rbnode_parent(s);
gc = s->right;
p->left = s->right;
s->right = n->right;
rbnode_reparent(n->right, s, rbnode_color(n->right));
}
/* node is partially swapped, now remove as in case 1 */
s->left = n->left;
rbnode_reparent(n->left, s, rbnode_color(n->left));
x = rbnode_parent(n);
c = rbnode_color(n);
rbtree_reparent(t, x, n, s);
if (gc) {
rbnode_reparent(s, x, c);
rbnode_reparent(gc, p, RBNODE_BLACK);
} else {
next = rbnode_black(s) ? p : NULL;
rbnode_reparent(s, x, c);
}
}
if (next)
rbtree_rebalance(t, next);
}

107
src/shared-rbtree.h Normal file
View File

@@ -0,0 +1,107 @@
#pragma once
/***
This file is part of ratbagd.
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
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"
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#ifdef RBTREE_DEBUG
# include <assert.h>
# define rbtree_assert(_x) assert(_x)
#else
# define rbtree_assert(_x)
#endif
typedef struct RBTree RBTree;
typedef struct RBNode RBNode;
enum {
RBNODE_RED = 0,
RBNODE_BLACK = 1,
};
struct RBTree {
RBNode *root;
};
struct RBNode {
RBNode *__parent_and_color;
RBNode *left;
RBNode *right;
};
#define rbnode_of(_ptr, _type, _member) \
__extension__ ({ \
const typeof( ((_type*)0)->_member ) *__ptr = (_ptr); \
__ptr ? (_type*)( (char*)__ptr - offsetof(_type, _member) ) : NULL; \
})
#define RBNODE_INIT(var) ((RBNode){ .__parent_and_color = (&var) })
static inline RBNode *rbnode_init(RBNode *n) {
*n = RBNODE_INIT(*n);
return n;
}
static inline RBNode *rbnode_parent(RBNode *n) {
return (RBNode*)((unsigned long)n->__parent_and_color & ~1UL);
}
static inline int rbnode_linked(RBNode *n) {
return n && n->__parent_and_color != n;
}
static inline unsigned long rbnode_color(RBNode *n) {
return (unsigned long)n->__parent_and_color & 1UL;
}
static inline int rbnode_red(RBNode *n) {
return rbnode_color(n) == RBNODE_RED;
}
static inline int rbnode_black(RBNode *n) {
return rbnode_color(n) == RBNODE_BLACK;
}
RBNode *rbnode_leftmost(RBNode *n);
RBNode *rbnode_rightmost(RBNode *n);
RBNode *rbtree_first(RBTree *t);
RBNode *rbtree_last(RBTree *t);
RBNode *rbnode_next(RBNode *n);
RBNode *rbnode_prev(RBNode *n);
void rbtree_add(RBTree *t, RBNode *p, RBNode **l, RBNode *n);
void rbtree_remove(RBTree *t, RBNode *n);
#ifdef __cplusplus
}
#endif

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

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

View File

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

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

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

View File

@@ -26,12 +26,17 @@
#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.h"
#define _unused_ __attribute__ ((unused))
static int
open_restricted(const char *path, int flags, void *user_data)
{
@@ -65,23 +70,40 @@ END_TEST
START_TEST(context_init_bad_iface)
{
struct ratbag *lr;
struct ratbag *lr _unused_;
struct ratbag_interface iface = {
.open_restricted = NULL,
.close_restricted = NULL,
};
lr = ratbag_create_context(&iface, NULL);
ck_assert(lr == NULL);
/* abort */
}
END_TEST
iface.open_restricted = open_restricted;
lr = ratbag_create_context(&iface, NULL);
ck_assert(lr == NULL);
START_TEST(context_init_bad_iface_open)
{
struct ratbag *lr _unused_;
struct ratbag_interface iface = {
.open_restricted = open_restricted,
.close_restricted = NULL,
};
iface.open_restricted = NULL;
iface.close_restricted = close_restricted;
lr = ratbag_create_context(&iface, NULL);
ck_assert(lr == NULL);
/* abort */
}
END_TEST
START_TEST(context_init_bad_iface_close)
{
struct ratbag *lr _unused_;
struct ratbag_interface iface = {
.open_restricted = NULL,
.close_restricted = close_restricted,
};
lr = ratbag_create_context(&iface, NULL);
/* abort */
}
END_TEST
@@ -115,15 +137,19 @@ START_TEST(context_ref)
END_TEST
static Suite *
test_context_suite(void)
test_context_suite(bool using_valgrind)
{
TCase *tc;
Suite *s;
s = suite_create("context");
tc = tcase_create("init");
tcase_add_test(tc, context_init_NULL);
tcase_add_test(tc, context_init_bad_iface);
if (!using_valgrind) {
tcase_add_test_raise_signal(tc, context_init_NULL, SIGABRT);
tcase_add_test_raise_signal(tc, context_init_bad_iface, SIGABRT);
tcase_add_test_raise_signal(tc, context_init_bad_iface_open, SIGABRT);
tcase_add_test_raise_signal(tc, context_init_bad_iface_close, SIGABRT);
}
tcase_add_test(tc, context_init);
tcase_add_test(tc, context_ref);
suite_add_tcase(s, tc);
@@ -136,9 +162,17 @@ int main(void)
int nfailed;
Suite *s;
SRunner *sr;
bool using_valgrind;
const struct rlimit corelimit = { 0, 0 };
s = test_context_suite();
sr = srunner_create(s);
setrlimit(RLIMIT_CORE, &corelimit);
/* when running under valgrind we're using nofork mode, so a
* signal raised by a test will fail in valgrind */
using_valgrind = !!getenv("USING_VALGRIND");
s = test_context_suite(using_valgrind);
sr = srunner_create(s);
srunner_run_all(sr, CK_ENV);
nfailed = srunner_ntests_failed(sr);

View File

@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <sys/resource.h>
#include "libratbag.h"
#include "libratbag-test.h"
@@ -53,6 +54,7 @@ const struct ratbag_test_device sane_device = {
.num_profiles = 3,
.num_resolutions = 3,
.num_buttons = 1,
.num_leds = 2,
.profiles = {
{
.resolutions = {
@@ -78,6 +80,20 @@ const struct ratbag_test_device sane_device = {
{ .xres = 2200, .yres = 2300, .hz = 3000 },
{ .xres = 2300, .yres = 2400, .hz = 3000 },
},
.leds = {
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.hz = 1,
.brightness = 20
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.hz = 3,
.brightness = 40
}
},
.active = false,
.dflt = false,
},
@@ -110,7 +126,7 @@ START_TEST(device_init)
{
struct ratbag *r;
struct ratbag_device *d;
int nprofiles, nbuttons;
int nprofiles, nbuttons, nleds;
struct ratbag_test_device td = sane_device;
int device_freed_count = 0;
@@ -124,6 +140,8 @@ START_TEST(device_init)
ck_assert_int_eq(nprofiles, 3);
nbuttons = ratbag_device_get_num_buttons(d);
ck_assert_int_eq(nbuttons, 1);
nleds = ratbag_device_get_num_leds(d);
ck_assert_int_eq(nleds, 2);
ratbag_device_unref(d);
ratbag_unref(r);
@@ -471,7 +489,7 @@ START_TEST(device_freed_before_profile)
is_active = ratbag_profile_is_active(p);
ck_assert_int_eq(is_active, 1);
p = ratbag_profile_unref(p);
ratbag_profile_unref(p);
ratbag_unref(r);
ck_assert_int_eq(device_freed_count, 1);
}
@@ -649,6 +667,221 @@ START_TEST(device_and_profile_and_resolution_freed_before_button)
}
END_TEST
START_TEST(device_buttons)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
struct ratbag_button *b;
struct ratbag_button_macro *m;
int nprofiles, nbuttons;
int i, j;
int device_freed_count = 0;
struct ratbag_test_device td = sane_device;
td.num_buttons = 10;
td.profiles[0].buttons[8].action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO;
td.destroyed_data = &device_freed_count;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
nprofiles = ratbag_device_get_num_profiles(d);
ck_assert_int_eq(nprofiles, 3);
nbuttons = ratbag_device_get_num_buttons(d);
ck_assert_int_eq(nbuttons, 10);
for (i = 0; i < nprofiles; i++) {
p = ratbag_device_get_profile(d, i);
ck_assert(p != NULL);
for (j = 0; j < nbuttons; j++) {
b = ratbag_profile_get_button(p, j);
ck_assert(b != NULL);
if (ratbag_button_get_action_type(b) ==
RATBAG_BUTTON_ACTION_TYPE_MACRO) {
m = ratbag_button_get_macro(b);
ck_assert(m != NULL);
ratbag_button_macro_unref(m);
}
ratbag_button_unref(b);
}
ratbag_profile_unref(p);
}
ratbag_device_unref(d);
ratbag_unref(r);
ck_assert_int_eq(device_freed_count, 1);
}
END_TEST
START_TEST(device_buttons_ref_unref)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
struct ratbag_button *b;
struct ratbag_test_device td = sane_device;
td.num_buttons = 10;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
p = ratbag_device_get_profile(d, 1);
b = ratbag_profile_get_button(p, 0);
ratbag_unref(r);
ratbag_device_unref(d);
ratbag_profile_unref(p);
ref_unref_test(ratbag_button, b);
ratbag_button_unref(b);
}
END_TEST
START_TEST(device_buttons_set)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
struct ratbag_button *b;
struct ratbag_test_device td = sane_device;
td.num_buttons = 10;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
p = ratbag_device_get_profile(d, 1);
b = ratbag_profile_get_button(p, 0);
ratbag_button_set_button(b, 3);
ratbag_button_unref(b);
ratbag_profile_unref(p);
ratbag_device_unref(d);
ratbag_unref(r);
}
END_TEST
static void
assert_led_equals(struct ratbag_led *l, struct ratbag_test_led e_l)
{
enum ratbag_led_mode mode;
struct ratbag_color color;
int brightness, hz;
ck_assert(l != NULL);
mode = ratbag_led_get_mode(l);
color = ratbag_led_get_color(l);
hz = ratbag_led_get_effect_rate(l);
brightness = ratbag_led_get_brightness(l);
ck_assert_int_eq(mode, e_l.mode);
ck_assert_int_eq(color.red, e_l.color.red);
ck_assert_int_eq(color.green, e_l.color.green);
ck_assert_int_eq(color.blue, e_l.color.blue);
ck_assert_int_eq(hz, e_l.hz);
ck_assert_int_eq(brightness, e_l.brightness);
}
START_TEST(device_leds)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
struct ratbag_led *led_logo, *led_side;
int nprofiles;
int device_freed_count = 0;
struct ratbag_test_device td = sane_device;
td.destroyed_data = &device_freed_count;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
nprofiles = ratbag_device_get_num_profiles(d);
ck_assert_int_eq(nprofiles, 3);
p = ratbag_device_get_profile(d, 2);
ck_assert(p != NULL);
led_logo = ratbag_profile_get_led(p, RATBAG_LED_TYPE_LOGO);
assert_led_equals(led_logo, td.profiles[2].leds[RATBAG_LED_TYPE_LOGO]);
led_side = ratbag_profile_get_led(p, RATBAG_LED_TYPE_SIDE);
assert_led_equals(led_side, td.profiles[2].leds[RATBAG_LED_TYPE_SIDE]);
ratbag_led_unref(led_logo);
ratbag_led_unref(led_side);
ratbag_profile_unref(p);
ratbag_device_unref(d);
ratbag_unref(r);
ck_assert_int_eq(device_freed_count, 1);
}
END_TEST
START_TEST(device_leds_set)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
struct ratbag_led *l;
int nprofiles;
int device_freed_count = 0;
struct ratbag_test_device td = sane_device;
struct ratbag_color c = {
.red = 0,
.green = 111,
.blue = 222
};
td.destroyed_data = &device_freed_count;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
nprofiles = ratbag_device_get_num_profiles(d);
ck_assert_int_eq(nprofiles, 3);
p = ratbag_device_get_profile(d, 0);
ck_assert(p != NULL);
l = ratbag_profile_get_led(p, RATBAG_LED_TYPE_LOGO);
ratbag_led_set_mode(l, RATBAG_LED_BREATHING);
ratbag_led_set_color(l, c);
ratbag_led_set_effect_rate(l, 11);
ratbag_led_set_brightness(l, 22);
l = ratbag_profile_get_led(p, RATBAG_LED_TYPE_LOGO);
struct ratbag_test_led e_l = {
.mode = RATBAG_LED_BREATHING,
.color = {
.red = c.red,
.green = c.green,
.blue = c.blue
},
.hz = 11,
.brightness = 22
};
assert_led_equals(l, e_l);
ratbag_led_unref(l);
ratbag_led_unref(l);
ratbag_profile_unref(p);
ratbag_device_unref(d);
ratbag_unref(r);
ck_assert_int_eq(device_freed_count, 1);
}
END_TEST
static Suite *
test_context_suite(void)
{
@@ -681,6 +914,17 @@ test_context_suite(void)
tcase_add_test(tc, device_resolutions_num_0);
suite_add_tcase(s, tc);
tc = tcase_create("buttons");
tcase_add_test(tc, device_buttons);
tcase_add_test(tc, device_buttons_ref_unref);
tcase_add_test(tc, device_buttons_set);
suite_add_tcase(s, tc);
tc = tcase_create("led");
tcase_add_test(tc, device_leds);
tcase_add_test(tc, device_leds_set);
suite_add_tcase(s, tc);
return s;
}
@@ -689,6 +933,9 @@ int main(void)
int nfailed;
Suite *s;
SRunner *sr;
const struct rlimit corelimit = { 0, 0 };
setrlimit(RLIMIT_CORE, &corelimit);
s = test_context_suite();
sr = srunner_create(s);

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

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

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

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