121 Commits

Author SHA1 Message Date
Peter Hutterer
549a6c47b8 libratbag 0.9.904
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-01-24 13:38:26 +10:00
Đặng Đoàn Đức Trung
400451efe1 Change G102/203's symlink to G-Pro (#605)
* Change G102/203 symlink to G-Pro

* Fix the symbolic link breaking
2019-01-22 19:29:43 +01:00
Zachary
2ee88948f3 data: added Logitech G603 svg (#600)
* data: added Logitech G603 svg

* data: added Logitech G603 svg

* data: added Logitech G603 svg

* data: updated Logitech G603 svg

I fixed an issue with the right and left click buttons missing and the middle button being on the incorrect layer. I also removed the logo on the mouse as I was having some issues in Inkscape due to my unfamiliarity with the software. I figured properly configured buttons is good enough for the time being.
2019-01-12 14:06:14 +01:00
Jente Hidskes
ace60f76da data/gnome/logitech-g-pro-wireless: don't use Logitech LED logo
We don't want this for copyright reasons.
2019-01-10 08:52:18 +10:00
Daniele Laudani
451af331b9 Logitech G402 support (#602)
This brings initial support for the Logitech G402, as discussed in #471.
I used the G502 svg and modified it, without changing the graphics. The two mice are aesthetically similar.

* Added G402 device file
* Added G402 svg file
* Add G402 files to build

[bentiss] squashed the 3 patches as one
2019-01-08 16:16:20 +01:00
Peter Hutterer
b87a770970 data: add MX Anywhere 2S 2019-01-07 10:19:12 +01:00
Benjamin Tissoires
e59ef319b0 hidpp20: non-rgb leds: switch back to HW control once configured
As mentioned in the code:
We revert to HW control of the LEDs.
This effectively disable the capability of changing the DPI/profile
LEDs, but this allows to make them work with the internal DPI/profile
switches.

The net result is:
- DPI/profiles can't be controlled (but who wants this?)
- The logo can properly controlled

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2019-01-07 10:39:55 +10:00
Benjamin Tissoires
8a1487f6f4 hidpp20: use macros for key+modifiers
Since a0830a8aaa, we can re-add the simple
keybindings capabilities. It's time the HID++ 2.0 devices get this.

This used to be working with the KEY API, but this has been dropped in
favor of the macro one.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2019-01-02 15:57:36 +10:00
Justin Gottula
44d36d2bf0 tools: canonicalize path first in udev_device_from_path (#592)
* tools: canonicalize path first in udev_device_from_path

This fixes a problem in which running ratbag-command with the path to a
device node symlink (e.g. /dev/logi-g502 -> /dev/input/event4), such as
one created by a udev rule, would not work correctly, due to the code
failing to resolve the symlink prior to checking for the presence of the
"/dev/input/event" prefix.

This resulted in nonsensical error messages (shown below) and the
curious inability to run ratbag-command successfully with anything but
the actual raw path to the device node itself (e.g. /dev/input/event4
would work fine, but /dev/logi-g502 would not, despite both being valid
ways of getting to the same device node).

    Error: Can't open '/dev/logi-g502': Success
    Error: Device '/dev/logi-g502' is not supported

Note that this same problem also prevented the usage of autogenerated
symlinks by udev, i.e. '/dev/input/by-id/' and '/dev/input/by-path/'.

* Update tools/shared.c

Co-Authored-By: jgottula <justin@jgottula.com>
2018-12-24 19:04:04 +01:00
Peter Hutterer
635865efa5 hidpp20: if the button is remapped, use that
A non-zero value for the remapped field means the button is mapped to that
button.

"diverted" means to decouple it from normal HID reports and report it via
HID++ only. In our case here, we "map" it, which is different.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-21 15:53:10 +01:00
Peter Hutterer
38949013f7 circleci: check for missing .device or .svg files
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-20 06:55:33 +01:00
Peter Hutterer
e1879818b4 meson.build: add missing mx master.svg
Missing from cf9a3e48b7

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-20 06:55:33 +01:00
Peter Hutterer
9b2614109f tools: sync ratbagd.py from Piper - drop async commit
ratbagd now implements commit as normal always-successful call with a
potential Resync signal later if something failed on the device.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-20 10:34:19 +10:00
Haochen Tong
abe99fec6a data: add device file and SVG for MX Anywhere 2 2018-12-20 10:14:54 +10:00
Haochen Tong
21936db58b hidpp20: remove redundant rc check
Protocol error code is already handled in hidpp20_request_command.
2018-12-20 10:14:54 +10:00
Haochen Tong
74f01f3de4 hidpp-generic: add physical mapping for button 169
This is the "Gesture Button" found on MX Anywhere2.
2018-12-19 23:12:04 +01:00
Haochen Tong
53ba4d975d hidpp20: add 3 new feature constants
Add HIDPP_PAGE_RESET, HIDPP_PAGE_DEVICE_NAME, and
HIDPP_PAGE_WIRELESS_DEVICE_STATUS. These feature names are found in the
"features.xml" of an LogiOptions installation. Probably not very useful
for now.
2018-12-19 23:12:04 +01:00
Haochen Tong
97306ddd5a hidpp20: ignore error on single button when enumerating special keys
The MX Anywhere 2 mouse has a problematic special key (index=7). When
trying to get the reporting value of this key, the device returns an
"invalid argument" error. [1]

To avoid giving up all good information about other keys, ignore error
(and the cause) when enumerating special keys.

[1] https://github.com/libratbag/libratbag/issues/558#issuecomment-439732655
2018-12-19 23:12:04 +01:00
Peter Hutterer
16e052d846 tools: print a nice error when we don't have the required capabilities
Fixes #583

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-19 10:15:09 +01:00
Peter Hutterer
1586913fc7 tools: don't allow setting macros where not supported
Fixes #585

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-19 10:15:09 +01:00
Peter Hutterer
cf9a3e48b7 gnome: symlink the mx master to our existing mx master 2 SVG
Those two devices look identical

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-19 09:21:36 +01:00
Peter Hutterer
ec835e2e82 data: rename the MX master svg to drop the underscore
More consistent with the rest of the files

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-19 09:21:36 +01:00
Peter Hutterer
b86211204c tools: make ratbagctl bold in the man page
And a double-space to single-space change snuck in as well

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-19 11:51:52 +10:00
Lukas Werling
1d6995be72 Add Logitech MX Master 2S 2018-12-18 16:15:28 +10:00
Aleksandar Trifunovic
536576fe8b meson: conditional check to avoid use of depricated features after Meson v0.48.0 2018-12-17 18:04:37 +00:00
Peter Hutterer
b514f49b08 tools: sync ratbagd from Piper
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-13 11:58:49 +10:00
Aleksandar Trifunovic
46a466ffe1 Fix Logitech G Pro commit 2018-12-13 08:23:30 +10:00
Aleksandar Trifunovic
15f1667fa4 Add Logitech G Pro Wireless 2018-12-13 08:23:30 +10:00
Peter Hutterer
284c13b2fe ratbagd: update all properties before sending out Resync
Fixes #528

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-11 11:09:14 +10:00
Peter Hutterer
e9bb4f98ae ratbagd: make Commit() truly asynchronous
Schedule the actual operation as a callback to handle at the end of the event
loop and return success. The Resync signal will handle the rest.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-11 11:08:56 +10:00
Peter Hutterer
45b8d792cc meson.build: add the new device files to meson.build
A few of the device files didn't get installed, let's add them to the list.
This reshuffles the list a bit because the easiest way to make sure all files
are included is to run git ls-files, so let's stick to that.

Fixes #552

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-11 11:08:11 +10:00
Peter Hutterer
b93dfcfda4 data: add helper functions for checking list contents
Easier to print the lists instead of just failing

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-12-11 11:08:11 +10:00
Peter Hutterer
3ffdc8daa0 Revert "Add Logitech G Pro wired configuration (#555)"
This wasn't added to the meson.build file so the test didn't run on it. And
the test fails. Issues found include:

- DPIRange and DPIList must not be in the Device section
- Only one of those two, not both
- The svg file was in the 'default' theme rather than in gnome

This needs to be fixed first, not sure how this would've worked at all.

This reverts commit 2371960a1d.
2018-12-04 16:34:02 +10:00
Peter Hutterer
e38bc7a760 ratbagd: set up a simple exit-on-idle
ratbagd is not something that needs stay around forever just because we
configured a mouse once. We have DBus activation and we don't store anything
anyway, so we might as well exit after a period of inactivity (20 min).

There's a slight race condition where if something comes in at exactly the
20min mark, we may shut down anyway and the messages get lost. This is too
narrow to worry about.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-11-28 09:33:16 +01:00
Peter Hutterer
2dca516afa data: check for missing buttons
The Logitech G502 had a wrong button ID so it doesn't show up. We have a
correctly named leader and path though so let's add that to the checks.

https://github.com/libratbag/piper/issues/240

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-11-28 09:18:50 +01:00
Peter Hutterer
b489ac871d data: fix the Logitech G502 button 10 label
Fixes https://github.com/libratbag/piper/issues/240

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-11-28 09:18:50 +01:00
Peter Hutterer
50fb898138 roccat: set the dpi list
Turns out this driver never got updated for the 'new' DPI list provided by
libratbag and thus broke with bacdab2596 when we
enforced that DPI list.

Set a fixed DPI list based on the hardcoded values we use elsewhere in this
driver.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-11-28 09:08:24 +01:00
Peter Hutterer
c676fcefb1 README: minor improvements (#565)
Notable: parts of the instructions have been punted to the wiki now where it's
faster to edit and be verbose about the specific instructions.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-11-27 10:55:24 +10:00
Peter Hutterer
bb4dd2aae0 data: fix Logitech g600 button 6/7 assignment (#564)
Fixes #550

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-11-27 08:03:51 +10:00
Benjamin Tissoires
9b11340e91 Revert "circle CI: switch to ubuntu 18.10"
This reverts commit 874d7eccb8.

Looks like 18.10 fails at every pipeline build, so better
punting this for later.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2018-11-26 09:38:11 +01:00
Peter Hutterer
3cd5a1f986 hidpp20: if a command returns with error, return a neg errno
I'm pretty sure all callers rely on this command to return either 0 or a
negative errno. There's a quirky occasion where if we get a HID error (a
positive number) during hidpp20_special_keys_buttons_get_reporting, that
number eventually ends up as the 'num_controls' value in
hidpp20_special_key_mouse_get_controls().

Since the only time we care about HID++ errors is during
hidpp20_root_get_protocol_version, let's just always return -EPROTO on HID++
errors.

Fixes #558

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-11-26 09:27:35 +01:00
Peter Hutterer
874d7eccb8 circle CI: switch to ubuntu 18.10
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-11-26 09:25:08 +01:00
Erol Gorancic
44c4b901e2 Steelseries Rival 600 Support.
- New svg and device file. Updated meson.build.
- New device version (version 3).
- Refactored led cycle to be general for v2 and v3.
- New defines for v3.
2018-11-26 01:27:17 +00:00
mike391
2371960a1d Add Logitech G Pro wired configuration (#555) 2018-11-26 11:25:17 +10:00
Mateusz Maćkowski
874aa8e63e Add support for Logitech G502 HERO (#559)
* Add support for Logitech G502 HERO
2018-11-26 11:24:41 +10:00
Peter Hutterer
1225f421c0 circle CI: switch fedora latest to fedora 28
Some change in F29 breaks the CI runs, so lets switch back to what we had
before so we can merge patches.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-11-26 11:17:25 +10:00
Peter Hutterer
5f0f5d2843 driver-hidpp10: force the single profile to be active
If we can successfully read a profile but hidpp10_get_current_profile() fails
we end up with one profile, but not set to active. Fix this by always forcing
the single profile to be active.

Fixes #526
2018-11-26 11:11:42 +10:00
Peter Hutterer
1d464767e1 tools: fix error code names in ratbagd.py documentation 2018-09-27 09:44:05 +10:00
Peter Hutterer
9551dddbb8 tools: handle NULL svgs in ratbag-command
Showed up in #526
https://github.com/libratbag/libratbag/issues/526#issuecomment-420016451
2018-09-27 09:39:44 +10:00
Peter Hutterer
563c274705 tools: remove the unnecessary prefix from the error code enum
It's already encoded as part of the enum, no need to have it again for the
value
2018-09-26 13:17:36 +02:00
jit26
71ed64a843 Add Logitech G PowerPlay wireless configuration
Tested with a Logitech G903, should also work with a Logitech G703.
May also work with other Logitech mice that work with a G900 receiver.
2018-09-24 17:14:54 +01:00
Timofey Titovets
efb3efc828 Update Logitech G900 wireless config - add G903
Signed-off-by: Timofey Titovets <timofey.titovets@synesis.ru>
2018-09-18 14:51:11 +10:00
Timofey Titovets
982e6cc7fe Add Logitech G903 wired configuration
Signed-off-by: Timofey Titovets <timofey.titovets@synesis.ru>
2018-09-18 14:51:11 +10:00
Peter Hutterer
44dc754e31 meson.build: add hacks to be able to run coverity
Taken from libinput, we need to define a few gcc7 types that coverity doesn't
know about. To submit a coverity build, run meson with -Dcoverity=true

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-09-14 11:04:59 +02:00
Peter Hutterer
18887e4c86 meson_options: use a consistent style
Move the closing ) up to match the other options
2018-09-14 11:04:59 +02:00
Peter Hutterer
46d5fae2e5 tools: ratbagc - allow for the device to be just 'event7'
ratbagctl requires 'eventX' as argument, but ratbag-command requires
/dev/input/eventX. That's not smart, let's do better.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-09-14 10:23:11 +10:00
Peter Hutterer
4b31945e1d tools: change the exception to just RatbagdUnavailable
ratbag-command has an 'except RatbagdDBusUnavailable' line despite that class
never being defined. That's ok because we'll never throw it in this case but
it's still confusing (to flake8). So let's change the class name to something
more generic and define it in ratbagc.py

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-09-14 10:23:11 +10:00
Peter Hutterer
7e685f5196 tools: fix flake8 warnings about missing double empty lines 2018-09-13 10:51:15 +10:00
Andreas Heider
050db150f6 Add device match for MX Master 2018-09-10 17:54:03 +10:00
Peter Hutterer
a3de0e1ff7 ratbagd: provide a decent error message when the bus name is taken
sd-bus returns EEXISTS which is "File exists" and way too confusing. Let's
print out a decent error message for that case.

Relater #535
2018-09-10 04:15:01 +01:00
Peter Hutterer
acc0066f08 meson.build: add comments and tidy up a bit
Add some comments to make it slightly more obvious how the tools are being put
together.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-09-06 13:33:58 +02:00
Peter Hutterer
eb9f98e58d tools: ratbagc - set the LIBRATBAG_DATA_DIR environment before context start
This way we use the data files from the source directory, not the installed
ones.

Fixes #530

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-09-06 13:33:58 +02:00
Peter Hutterer
6d996bf1aa tools: add get_svg_fd() to ratbagc
Missing from 6000688c30

Fixes #31

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-09-06 13:33:58 +02:00
Peter Hutterer
34fbfe105c tools: return the full svg path for ratbagc get_svg()
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-09-06 13:33:58 +02:00
Peter Hutterer
dfeac1bcbc tools: generate ratbagc.py from configuration_data()
No functional changes, this is prep work only

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-09-06 13:33:58 +02:00
Peter Hutterer
3d345570eb meson.build: libratbag 0.9.903
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-09-05 08:41:26 +10:00
Peter Hutterer
6000688c30 ratbagd: add GetSvgFd() to return an fd to the SVG
If we're sandboxing the client we don't have access to the SVG files directly.
So pass a read-only file descriptor to the client over DBus and let them deal
with the SVG that way.

In ratbagd.py we pull the fd off but we convert the quirky dbus error into a
normal FileNotFoundError(), much easier to catch in the caller.

On the ratbagctl side we now have to search the SVG for it's name - stored in
the 'sodipodi:docname' attribute by Inkspace. This isn't guaranteed to exist
or be always correct, but meh, it's close enough. We could continue to use the
GetSvg() call here but I want to deprecate that and remove it in the near
future.

Fixes #521

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-09-05 08:31:35 +10:00
Rasmus Thomsen
9b409822c3 build: support building with elogind 2018-09-04 13:51:01 +10:00
FFY00
363eab80f0 steelseries: implement cycle struct 2018-09-01 22:49:32 +02:00
Peter Hutterer
66fc7b545e meson_options: fix indentation 2018-08-31 09:06:05 +10:00
Peter Hutterer
bb4b673df8 Drop the unused libratbag-version.h
Unused since 558f59b52c

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-30 08:20:23 +02:00
Peter Hutterer
e104eca02c ratbagd: add a --version commandline argument
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-30 08:20:23 +02:00
Peter Hutterer
01c362eadc ratbagd: log the device list by default
Without --verbose ratbagd just starts up with no message whatsoever. It's hard
to tell if any devices are supported or if the server just hangs. Let's log
the devices detected by default, at least that way we get some feedback.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-30 08:20:23 +02:00
Thomas Hindoe Paaboel Andersen
fff3589a20 steelseries: read settings from device
Open an extra hidraw to read current onboard settings.

Initially DPI and static LED color can be read from the rival
310. Other data/devices can be added as the protocol is better
understood.

The protocol used to extract the data seems to be both device
and firmware version specific. To be able to switch on both
cases we now store the firmware information in the drv_data.

Signed-off-by: Thomas Hindoe Paaboel Andersen <phomes@gmail.com>
2018-08-30 15:49:48 +10:00
Thomas Hindoe Paaboel Andersen
a4e1e1690d hidraw: allow opening multiple hidraw
For some devices we have to send reports on one endpoint
but read the reply on another. This commit allows us to
open and store multiple hidraws and adds API to specify
which hidraw to use for open, close, and read input report.

Signed-off-by: Thomas Hindoe Paaboel Andersen <phomes@gmail.com>
2018-08-30 15:49:48 +10:00
Peter Hutterer
38dce69838 ratbagd: remove some unused variables
Warnings introduced in 23ff4e753d

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-30 11:03:09 +10:00
Peter Hutterer
23ff4e753d ratbagd: wrap the various sd_bus functions into a macro
Make sure we log an error if any of these fail, because most really never
should, they're scaffolding we rely on to send the data. Some of these calls
(the *_read() ones) could fail if a client sends wrong data, but we might as
well log that.

This patch only wraps calls that reply on the bus, normal sd_bus_* calls that
we catch the error ourselves are handled as before.

Likewise, any errors from libratbag are left as-is. We somewhat rely on
libratbag to log something and such errors are not a failure of the actual
scaffolding.

This should make errors like https://github.com/libratbag/piper/issues/237
easier to debug.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-24 10:01:56 +02:00
Peter Hutterer
f24cb6dc66 ratbagd: always return an empty list for array-like properties []
In some cases we get Piper trying to use None as a list, see
https://github.com/libratbag/piper/issues/237

This can be reproduced by returning an error (e.g. -ENOMEM) from
ratbagd_get_devices(). That error isn't handled anywhere on the client side,
the effective result is that the property is None when calling
proxy.get_cached_property().

The error is visible on the bus:

method call time=1535084121.223284 sender=:1.1311 -> destination=:1.1310
  serial=7 path=/org/freedesktop/ratbag1;
  interface=org.freedesktop.DBus.Properties; member=GetAll
  string "org.freedesktop.ratbag1.Manager"
error time=1535084121.223433 sender=:1.1310 -> destination=:1.1311
  error_name=org.freedesktop.DBus.Error.NoMemory reply_serial=7
  string "Cannot allocate memory"

Make ratbagd.py more robust here and always return an empty list for those
properties where we expect an array. This papers over the real bug which is
that a) we need better logging in ratbagd on errors and b) something on the
other side needs to handle this error.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-24 10:01:56 +02:00
Thomas Hindoe Paaboel Andersen
b738136154 ratbagd: do not warn when converting invalid special action
Clients e.g. ratbagctl ask for all properties, so this getter gets
called often even if the button is not a special action.

We do not want to send the value for an invalid special action as
that is negative 1. Converting to unknown is good enough here so
let's just remove the error logging and convert in silence.
2018-08-23 08:04:57 +10:00
Peter Hutterer
447931d779 ratbagd: put in a check for invalid -1 sent down the wire
Before we send something as "u" down DBus, check that it's not -1, a commonly
used error code in libratbag. If so, return a dbus error instead and log where
this occured so we can fix it.

This should at least help spot these errors quickly while at the same time
hopefully not crashing piper anymore.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-22 20:21:55 +02:00
Peter Hutterer
920b71d94c ratbagd: if the action special type is invalid, change to unknown
This prevents us from sending -1 down the wire as unsigned int.

This may be the cause of https://github.com/libratbag/piper/issues/208

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-22 20:21:55 +02:00
Peter Hutterer
a786aaf390 libratbag: whitespace fixes
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-22 20:21:55 +02:00
Peter Hutterer
d395db1a11 libratbag: make the ratbag_*_has_* and _is_ functions return a bool
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-22 20:21:55 +02:00
Peter Hutterer
ad0b7e53d9 libratbag: use !! for the led modes
We don't want to leak the bitmask we use internally.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-22 20:21:55 +02:00
Peter Hutterer
0ed621db8f tools: make the dbus directory before trying to copy to it (#517)
Popped up in https://github.com/libratbag/libratbag/pull/515

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-22 04:08:10 +01:00
Peter Hutterer
5ae473dcd1 tools: make ratbagd a bit more robust regarding None object path lists
Not sure why this is happening exactly, but it's better to handle this case
without an immdiate exception.

Fixes https://github.com/libratbag/piper/issues/228

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-08-20 12:53:18 +10:00
maxice8
ac384137bd meson.build meson_options.txt: Add systemd switch (#510)
Allows systems that provide libelogind (which provides a portion of
libsystemd that libratbag uses) to also compile without having to patch
out the rest of the build to remove the full systemd dependency.

This commit:

- Adds a systemd swith on meson_options.txt, a boolean set to true as
default to keep expected behaivour
- Adds a check on meson.build for the systemd switch and adds
dep_systemd
- Guards sections meant to generate systemd service units under the
systemd switch
2018-08-16 23:33:42 +01:00
maxice8
e43cf7ab08 src/libratbag-data.c tools/lur-command.c: add missing limits.h includes (#507) 2018-08-16 12:46:36 +01:00
Thomas Hindoe Paaboel Andersen
acb65a1bc8 check-svg: only warn about non-consecutive buttons
Some devices like the logitech G9 have a button in its profile
data which is not a real physical button. This breaks the direct
mapping from button number to profile number.

This patch reduces this case from an error to a warning
2018-08-13 15:50:24 +10:00
Thomas Hindoe Paaboel Andersen
e5f4b49e36 update logitech G9 SVG
Adds wheel left/right clicks and adjusts button numberings

Button5 seems to be configurable in the profile, but no such button
exists on the mouse.
2018-08-13 15:50:24 +10:00
Thomas Hindoe Paaboel Andersen
31d0fa00d9 remove image layer in kinzu SVG 2018-07-30 09:43:47 +10:00
Thomas Hindoe Paaboel Andersen
d28f330d43 set profile dirty on name change 2018-07-30 09:42:25 +10:00
Thomas Hindoe Paaboel Andersen
a29adaf913 hidpp10: use profile name from device
Tested on G9
2018-07-30 09:42:25 +10:00
Thomas Hindoe Paaboel Andersen
ea3e68cf7d meson: bump min version to 0.40.0
Needed for build_by_default
2018-07-27 21:13:29 +10:00
gandalf3
158594ec20 Point buildsystem and piper at logitech-g900 svg 2018-07-27 10:19:09 +02:00
gandalf3
6e51849ae5 Add side view, space out leaders so Adwaita dropdowns don't overlap.
After some "user testing" (having a friend try it), I've put the side
view back in. This has the benefit of keeping all buttons visible
without overlap, which would be necessary for
https://github.com/libratbag/piper/issues/71.
2018-07-27 10:19:09 +02:00
gandalf3
29731aa578 Fix remaining missing leaders and add style properties 2018-07-27 10:19:09 +02:00
gandalf3
4d0ef4f2c1 Add buttons to device layer
Also set button ids for device layer button objects
2018-07-27 10:19:09 +02:00
gandalf3
e7d03448cc Add button ids to leader lines and add leader objects
Note: mouse up and mouse down ignored for now
2018-07-27 10:19:09 +02:00
gandalf3
2725a55770 Add LED and Button leads to logitech g600 svg 2018-07-27 10:19:09 +02:00
gandalf3
e5be1a70eb Add logitech G600 svg 2018-07-27 10:19:09 +02:00
Thomas Hindoe Paaboel Andersen
26493aceaf G600: fix setting resolution 2018-07-24 09:42:45 +02:00
Thomas Hindoe Paaboel Andersen
c4f5bd5218 Driver for logitech G600
A driver for the logitech G600. The behavior is similar to the
G300 but with enough differences that it seemed cleaner to implement
as a separate driver.
2018-07-24 09:42:45 +02:00
Thomas Hindoe Paaboel Andersen
7c65b0b4a9 increase max number of report rates to 8
This is needed for the logitech G600
2018-07-24 09:42:45 +02:00
Peter Hutterer
d81c68d94f meson: change to use "-Dtests=true" instead of "enable-tests"
This is more meson-like, the "enable-foo" is a autotools leftover.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-07-20 07:38:00 +02:00
Peter Hutterer
a5a2ea1a70 README: fix link to the DBus interface specification
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-07-18 14:59:03 +10:00
Peter Hutterer
507d10378a README: replace mesonconf with meson configure
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-07-18 08:12:18 +10:00
Gui Ambros
08f3f7daf2 Fix UTF-8 encoding on merge_ratbagd.py, per #493 2018-07-05 20:31:34 +02:00
Luke
9b848084fd data/gnome: added device entry for G203/G102, artwork needed (#490) 2018-07-05 03:01:35 +01:00
Peter Hutterer
e1ebf7ad1b Initialize a few _cleanup_ pointers to NULL
../src/liblur.c:46:3: warning: 'd' may be used uninitialized in this function [-Wmaybe-uninitialized]
   hidpp10_device_destroy(*hidpp10_device);
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/liblur.c:198:59: note: 'd' was declared here
   _cleanup_hidpp10_device_destroy_ struct hidpp10_device *d;

And similar messages

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-07-04 09:05:29 +02:00
Stephen Dawkins
38f46a9be8 updated sensei raw svg 2018-07-01 21:02:40 +02:00
Stephen Dawkins
d07b7582db removed sensei raw image from svg 2018-07-01 21:02:40 +02:00
Stephen Dawkins
af194a22f0 updated sensei raw image 2018-07-01 21:02:40 +02:00
Stephen Dawkins
d8c7050f6f disabled macros for steelseries if short_button is set 2018-07-01 21:02:40 +02:00
Stephen Dawkins
30e834b18b added svg for steelseries sensei raw 2018-07-01 21:02:40 +02:00
Stephen Dawkins
49770a22e7 added support for steelseries sensei raw 2018-07-01 21:02:40 +02:00
Thomas Hindoe Paaboel Andersen
de8cba13cf hidraw: use cleanup for udev enumerate 2018-06-29 11:25:03 +10:00
Benjamin Tissoires
1fab318dd3 hidpp20-drv: fix number of buttons for non-profiles mice
This was nasty. We correctly set the number of buttons but rely later
on the field set in drv_data.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2018-06-05 09:58:06 +10:00
Benjamin Tissoires
e2190964c2 hidpp20-drv: allow non configurable mice to work again
Given that there is a sanity check on the available resolution, we
need to provide default values. There might be a way to fetch the
actual values from the hardware but at least provide some sort of
default that re-enables the hardwares.

Fixes bacdab2596

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2018-06-05 09:58:06 +10:00
Benjamin Tissoires
8eddf99823 circle: allow to annotate clang static analyzer false positives
https://bugs.llvm.org/show_bug.cgi?id=3888

clang analyzer doesn't know how to handle __attribute__(cleanup()).

There are only 2 warnings raised because the analyzer only look at the
current compilation unit for checking the malloc call. So only those
2 are detected by clang and then marked as memory leak.

This also means our current continuous integration doesn't check for
memory leaks.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2018-05-25 08:45:47 +02:00
p4block
9fede0e68d Add Logitech G302 2018-05-22 09:04:27 +02:00
75 changed files with 7214 additions and 689 deletions

View File

@@ -6,7 +6,17 @@ way to access the various features exposed by these mice and abstracts away
hardware-specific and kernel-specific quirks.
libratbag exports the devices over a DBus daemon called **ratbagd**, see the
[ratbagd DBus Interface description](DBusInterface.md).
[ratbagd DBus Interface description](https://libratbag.github.io/).
Users interact through a GUI like
[Piper](https://github.com/libratbag/piper/). For developers, the
`ratbagctl` tool is the prime tool for debugging.
Installing libratbag from system packages
-----------------------------------------
libratbag is packaged for some distributions, you can use your system's
package manager to install it. See [the
wiki](https://github.com/libratbag/libratbag/wiki/Installation) for details.
Compiling libratbag
-------------------
@@ -17,7 +27,11 @@ libratbag and initialize the build:
git clone https://github.com/libratbag/libratbag.git
cd libratbag
meson builddir --prefix=/usr/
meson builddir
This will inititialize libratbag so that it installs under `/usr/local`,
i.e. it will not overwrite the system installation. For more information,
see [the wiki](https://github.com/libratbag/libratbag/wiki/Installation).
And to build or re-build after code-changes, run:
@@ -27,9 +41,9 @@ And to build or re-build after code-changes, run:
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
meson configure builddir -Denable-documentation=false
Run `mesonconf builddir` to list the options.
Run `meson configure builddir` to list the options.
Running ratbagd as DBus-activated systemd service
-------------------------------------------------

View File

@@ -29,6 +29,13 @@ libratbag_references:
run:
name: Installing
command: ninja -C build install
check_install: &check_install
run:
name: Checking installation
command: |
PREFIX=/usr/local/
diff -u <(cd data/devices; ls *.device) <(cd $PREFIX/share/libratbag/devices; ls *.device)
diff -u <(cd data/gnome; ls *.svg) <(cd $PREFIX/share/libratbag/gnome; ls *.svg)
check_uninstall: &check_uninstall
run:
name: Checking if any files are left after uninstall
@@ -94,6 +101,7 @@ fedora_settings: &fedora_settings
- *start_dbus
- *build_and_test
- *install
- *check_install
- *check_uninstall
- *export_logs
@@ -113,6 +121,7 @@ ubuntu_settings: &ubuntu_settings
- *start_dbus
- *build_and_test
- *install
- *check_install
- *check_uninstall
- *export_logs
@@ -124,13 +133,13 @@ scan_build_run: &scan_build_run
- *fedora_install
- run:
name: Install clang and find
command: dnf install -y clang-analyzer findutils
command: dnf install -y clang-analyzer findutils python3-beautifulsoup4
- 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)
command: python3 ~/libratbag/tools/check_scan_build.py ~/libratbag/build/meson-logs/scanbuild
doc_build: &doc_build
<<: *default_settings
@@ -197,11 +206,11 @@ jobs:
fedora_cache:
<<: *fedora_prep_cache
docker:
- image: fedora:latest
- image: fedora:28
fedora_latest:
<<: *fedora_settings
docker:
- image: fedora:latest
- image: fedora:28
ubuntu_17_10:
<<: *ubuntu_settings
docker:
@@ -209,15 +218,15 @@ jobs:
doc_build:
<<: *doc_build
docker:
- image: fedora:latest
- image: fedora:28
scan_build:
<<: *scan_build_run
docker:
- image: fedora:latest
- image: fedora:28
doc_deploy:
<<: *docs_deploy
docker:
- image: fedora:latest
- image: fedora:28
workflows:

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -32,6 +32,15 @@ import configparser
# Set on commandline with --svg-dir
svg_dirs = []
def assertIn(element, l):
if element not in l:
raise AssertionError('{} must be in {}'.format(element, l))
def assertNotIn(element, l):
if element in l:
raise AssertionError('{} must not be in {}'.format(element, l))
def check_svg_str(string):
assert(string.endswith('.svg'))
@@ -55,7 +64,7 @@ def check_match_str(string):
parts = match.split(':')
assert(len(parts) == 3)
assert(parts[0] in bustypes)
assertIn(parts[0], bustypes)
vid = parts[1]
assert(vid == '{:04x}'.format(int(vid, 16)))
pid = parts[2]
@@ -70,7 +79,7 @@ def check_ledtypes_str(string):
if not t: # emtpy string if trailing ;
continue
assert(t in permitted_types)
assertIn(t, permitted_types)
def check_section_device(section):
@@ -78,10 +87,10 @@ def check_section_device(section):
permitted_keys = required_keys + ['Svg', 'LedTypes']
for key in section.keys():
assert(key in permitted_keys)
assertIn(key, permitted_keys)
for r in required_keys:
assert(r in section)
assertIn(r, section)
try:
check_svg_str(section['Svg'])
@@ -132,13 +141,13 @@ def check_dpi_list_str(string):
def check_profile_type_str(string):
types = ['G9', 'G500', 'G700']
assert(string in types)
assertIn(string, types)
def check_section_hidpp10(section):
permitted = ['Profiles', 'ProfileType', 'DpiRange', 'DpiList', 'DeviceIndex', 'Leds']
for key in section.keys():
assert(key in permitted)
assertIn(key, permitted)
try:
nprofiles = int(section['Profiles'])
@@ -156,13 +165,13 @@ def check_section_hidpp10(section):
try:
check_dpi_range_str(section['DpiRange'])
assert('DpiList' not in section.keys())
assertNotIn('DpiList', section.keys())
except KeyError:
pass
try:
check_dpi_list_str(section['DpiList'])
assert('DpiRange' not in section.keys())
assertNotIn('DpiRange', section.keys())
except KeyError:
pass
@@ -182,7 +191,7 @@ def check_section_hidpp10(section):
def check_section_hidpp20(section):
permitted = ['DeviceIndex']
for key in section.keys():
assert(key in permitted)
assertIn(key, permitted)
try:
index = int(section['DeviceIndex'])
@@ -208,7 +217,7 @@ def parse_data_file(path):
data.optionxform = lambda option: option
data.read(path)
assert('Device' in data.sections())
assertIn('Device', data.sections())
check_section_device(data['Device'])
driver = data['Device']['Driver']
@@ -216,7 +225,7 @@ def parse_data_file(path):
permitted_sections = ['Device', driver_section]
for s in data.sections():
assert(s in permitted_sections)
assertIn(s, permitted_sections)
if data.has_section(driver_section):
check_section_driver(driver, data[driver_section])

View File

@@ -0,0 +1,5 @@
[Device]
Name=Logitech MX Anywhere 2
DeviceMatch=usb:046d:404a;bluetooth:046d:b013
Driver=hidpp20
Svg=logitech-mx-anywhere2.svg

View File

@@ -0,0 +1,5 @@
[Device]
Name=Logitech MX Anywhere 2S
DeviceMatch=usb:046d:406a
Driver=hidpp20
Svg=logitech-mx-anywhere2.svg

View File

@@ -0,0 +1,6 @@
# Logitech MX Master 2S
[Device]
Name=Logitech MX Master 2S
DeviceMatch=bluetooth:046d:b019;usb:046d:4069
Driver=hidpp20
Svg=logitech-mx-master-2s.svg

View File

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

View File

@@ -0,0 +1,9 @@
[Device]
Name=Logitech G Pro Wireless
DeviceMatch=usb:046d:c539;usb:046d:c088
Svg=logitech-g-pro-wireless.svg
LedTypes=logo;battery;dpi;
Driver=hidpp20
[Driver/hidpp20]
DeviceIndex=1

View File

@@ -0,0 +1,6 @@
[Device]
Name=Logitech Gaming Mouse G Pro
DeviceMatch=usb:046d:c085
Svg=logitech-g-pro.svg
Driver=hidpp20
LedTypes=logo;side

View File

@@ -0,0 +1,7 @@
# G102 and G203 (USB)
[Device]
Name=Logitech Gaming Mouse G102/G103
DeviceMatch=usb:046d:c084
Svg=logitech-g102-g203.svg
Driver=hidpp20
LedTypes=logo;side;

View File

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

View File

@@ -0,0 +1,6 @@
# Logitech G402 Gaming Mouse USB
[Device]
Name=Logitech G402 Gaming Mouse
DeviceMatch=usb:046d:c07e
Driver=hidpp20
Svg=logitech-g402.svg

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
# Logitech G PowerPlay mouse pad + wireless USB receiver.
[Device]
Name=Logitech G PowerPlay mouse pad.
DeviceMatch=usb:046d:c53a
Driver=hidpp20
Svg=logitech-g900.svg
[Driver/hidpp20]
DeviceIndex=1

View File

@@ -0,0 +1,12 @@
[Device]
Name=SteelSeries Rival 600
DeviceMatch=usb:1038:1724
Driver=steelseries
Svg=steelseries-rival600.svg
[Driver/steelseries]
DeviceVersion=3
Buttons=7
Leds=8
DpiRange=100:12000@100
MacroLength=1

View File

@@ -0,0 +1,15 @@
[Device]
Name=SteelSeries Sensei Raw
DeviceMatch=usb:1038:1369
Driver=steelseries
Svg=steelseries-senseiraw.svg
LedTypes=logo;dpi
[Driver/steelseries]
DeviceVersion=1
Buttons=8
Leds=1
DpiRange=90:5670@90
MacroLength=1
MonoLed=1
ShortButton=1

View File

@@ -49,7 +49,7 @@ def check_layers(root):
def check_elements(root, prefix, required=0):
"""
Checks for elements of the form 'prefixN' in the root tag. Any elements
found must be consecutive or an error is printed, i.e. if there's a
found must be consecutive or an warning is printed, i.e. if there's a
'button8' there has to be a 'button7'.
If required is nonzero, an error is logged for any missing element with
@@ -66,13 +66,14 @@ def check_elements(root, prefix, required=0):
highest = -1
for idx in range(0, 20):
e = '{}{}'.format(prefix, idx)
previous = '{}{}'.format(prefix, idx - 1)
leader = '{}{}-leader'.format(prefix, idx)
path = '{}{}-path'.format(prefix, idx)
if e in element_ids:
highest = idx
previous = '{}{}'.format(prefix, idx - 1)
if idx > 0 and previous not in element_ids:
logger.error("Non-consecutive {}: {}".format(prefix, e))
logger.warning("Non-consecutive {}: {}".format(prefix, e))
leader = '{}{}-leader'.format(prefix, idx)
if leader not in element_ids:
logger.error("Missing {} for {}".format(leader, e))
else:
@@ -80,9 +81,12 @@ def check_elements(root, prefix, required=0):
if element is None or len(element) != 1 or element[0] is None:
logger.error("Missing style property for {}".format(leader))
path = '{}{}-path'.format(prefix, idx)
if path not in element_ids:
logger.error("Missing {} for {}".format(path, e))
elif leader in element_ids:
logger.error("Have {} but not {}".format(leader, e))
elif path in element_ids:
logger.error("Have {} but not {}".format(path, e))
elif idx < required:
logger.error("Missing {}: {}".format(prefix, e))

View File

@@ -0,0 +1,358 @@
<?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.92.2 2405546, 2018-03-11"
sodipodi:docname="logitech-g-pro-wireless.svg"
inkscape:export-filename="/home/jimmac/logitech-g-pro.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.2804595"
inkscape:cx="68.346407"
inkscape:cy="156.12394"
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="1016"
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:guide-bbox="true"
guidetolerance="10"
objecttolerance="10"
gridtolerance="10"
inkscape:snap-object-midpoints="true"
inkscape:snap-others="true"
inkscape:snap-global="true">
<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,-394.02675)"
style="display:inline">
<path
sodipodi:nodetypes="cccccccccccccccccsccccsccccccccc"
inkscape:connector-curvature="0"
id="path1292"
d="m 360.53959,454.98488 c 3.74994,7.11792 5.22331,12.82285 5.80424,19.98939 1.3281,12.56244 0.9478,23.81846 0.96947,36.4814 l -3.17737,93.86839 c 2.16614,21.37627 5.56926,62.75825 5.56926,62.75825 0.65653,8.98996 -0.46847,26.48228 -0.46847,26.48228 -2.86905,23.33152 -10.5133,37.74934 -19.00067,50.59334 -18.20786,20.92677 -40.13338,36.77577 -73.78077,36.6144 -27.02249,-2.01448 -53.27969,-9.87305 -76.17266,-43.4221 -9.26342,-15.31303 -16.69901,-34.15568 -15.08733,-70.46891 0.62849,-14.11561 1.69038,-28.25238 4.18994,-49.46082 l -0.49311,-29.00325 1.18979,-1.57231 -0.0795,-4.45976 c -1.39215,-1.83047 -1.66233,-3.22446 -1.77535,-5.08282 -0.64341,-16.4181 -3.4946,-31.1865 -1.60176,-47.4595 l 1.47194,-0.0964 c -0.37849,-21.34319 -3.11342,-49.72121 2.66511,-66.67692 1.24485,-3.65274 1.80259,-6.37401 3.52312,-8.88212 l 1.05297,-0.01 c 0.54288,1.3278 1.67594,1.1345 2.10889,-0.024 v -5.52825 c 3.59201,-4.68158 7.50203,-9.54243 12.85097,-13.19967 15.55153,-10.63308 32.80347,-19.96687 65.99229,-22.56979 l -0.0387,6.48824 h 2.2079 l -0.01,-6.47842 c 23.50552,1.65098 57.68029,9.24243 79.04493,36.21112 l -0.0104,4.95885 0.5328,0.02 c -0.0202,1.38148 1.14549,1.60947 1.23348,-0.0306 z"
style="fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="button4"
d="m 188.09729,530.74646 c 1.24811,16.49039 3.84562,32.89212 4.67165,49.41274 -0.23358,2.47096 -1.75447,3.01485 -2.76648,3.22598 -1.40633,-1.85652 -1.95252,-3.40284 -2.12696,-4.9258 -0.5931,-13.32083 -2.99544,-30.31661 -1.59655,-47.61652 z"
style="fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path1308" />
<path
sodipodi:nodetypes="ccczzc"
inkscape:connector-curvature="0"
id="button3"
d="m 193.52072,585.08288 -4.67671,4.33437 c -0.0827,14.62518 0.50637,29.18789 1.55352,43.76306 0.38765,1.69742 1.72362,5.2371 2.9655,5.04628 1.24187,-0.19081 2.03429,-4.00811 2.05005,-4.80974 0.0158,-0.80162 -1.89236,-48.33397 -1.89236,-48.33397 z"
style="fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path1310" />
<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="button0"
width="21.618355"
height="54.844131"
x="-288.2897"
y="458.7883"
rx="10.20224"
ry="9.3806696"
inkscape:label="#rect932"
transform="scale(-1,1)" />
<path
sodipodi:nodetypes="ccccccccccc"
inkscape:connector-curvature="0"
id="button1"
d="m 348.93916,572.45034 c -0.55088,2.3786 -2.02369,4.23561 -5.30087,5.25105 l -65.10412,-0.0533 -0.056,-61.16607 c 6.69027,-0.62851 12.7931,-2.33598 13.40584,-12.8421 l 0.11678,-35.30516 c -0.67751,-4.76842 -1.92823,-11.08275 -13.43942,-12.70653 l -0.11153,-41.76267 c 28.32131,1.35252 61.44583,12.93152 79.04493,36.21112 -0.49018,45.87326 -3.70717,82.71393 -8.55561,122.37369 z"
style="fill:#e6e6e4;fill-opacity:1;stroke:#babdb6;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#path1314" />
<path
style="fill:#e6e6e4;fill-opacity:1;stroke:#babdb6;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 206.00299,572.3517 c 0.55088,2.3786 2.02369,4.23561 5.30087,5.25105 l 65.10412,-0.0533 -0.0153,-61.06743 c -6.69027,-0.62851 -12.72181,-2.43462 -13.33455,-12.94074 l -0.11678,-35.30516 c 0.67751,-4.76842 2.20116,-11.4932 13.43942,-12.70653 l 0.11153,-41.76267 c -28.7246,1.50375 -63.81513,13.58686 -79.04493,35.85825 0.49018,45.87326 3.70717,83.0668 8.55561,122.72656 z"
id="button2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccc"
inkscape:label="#path1316" />
<path
sodipodi:nodetypes="cccccccc"
inkscape:connector-curvature="0"
id="path1318"
d="m 360.53959,454.98488 c 0.43129,16.08426 -0.23059,32.16852 -1.31311,48.25278 -2.19193,26.2623 -4.67884,48.98479 -7.12913,72.1468 -1.46408,3.93001 -3.86084,5.76149 -6.84397,6.27364 -43.14472,-1.05018 -87.90934,-1.3804 -135.88124,-0.28516 -2.36085,-0.76628 -4.72268,-1.52623 -6.5588,-5.70331 -2.11905,-20.75413 -4.2381,-39.15076 -6.35715,-65.36609 -1.04644,-16.292 -1.64275,-37.04208 -2.17067,-55.11612"
style="fill:none;stroke:#babdb6;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<g
transform="translate(0.23449552,400.00009)"
style="display:inline"
id="led0">
<g
transform="translate(427.72943,-408.9487)"
id="g1304"
style="display:inline">
<ellipse
style="fill:#babdb6;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
id="ellipse1298"
cx="-150.51967"
cy="622.58923"
rx="1.8137393"
ry="1.9375004" />
<ellipse
ry="1.9375004"
rx="1.8137393"
cy="610.96423"
cx="-150.51967"
id="ellipse1300"
style="fill:#babdb6;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none" />
<ellipse
style="fill:#babdb6;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
id="ellipse1302"
cx="-150.51967"
cy="599.15173"
rx="1.8137393"
ry="1.9375004" />
</g>
<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-3"
sodipodi:type="arc"
sodipodi:cx="-284.0806"
sodipodi:cy="277.20975"
sodipodi:rx="19.936752"
sodipodi:ry="19.936752"
sodipodi:start="0.78539816"
sodipodi:end="5.4977871"
sodipodi:arc-type="arc"
d="m -269.98318,291.30716 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.19483,0"
sodipodi:open="true"
transform="rotate(-90)"
inkscape:label="#path883" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
style="display:inline"
transform="translate(-68.791597,5.973336)">
<g
id="button0-path"
inkscape:label="#button0-path">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path960"
d="m 485.19274,86.21028 -207.71221,10e-7"
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
y="82.710281"
x="273.98053"
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="485"
y="85.710289" />
</g>
<g
id="button4-path"
inkscape:label="#g979">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path964"
d="m 186.12905,161.04893 -116.990518,0.0625"
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
transform="scale(-1,1)"
inkscape:label="#rect875"
style="color:#000000;text-align:end;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.99999994;stroke-linecap:round"
id="button4-leader"
width="0.32565016"
height="1"
x="-69.138535"
y="160.58018" />
<rect
transform="scale(-1,1)"
y="157.58018"
x="-189.79312"
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="button3-path"
inkscape:label="#g984">
<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 187.9817,205.35644 H 69.129695"
id="path966"
inkscape:connector-curvature="0" />
<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="rect976"
width="6.999999"
height="6.999999"
x="-191.91962"
y="201.85645" />
<rect
transform="scale(-1,1)"
y="204.85645"
x="-69.1297"
height="1"
width="0.32190493"
id="button3-leader"
style="color:#000000;text-align:end;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round"
inkscape:label="#rect875" />
</g>
<g
id="button1-path"
inkscape:label="#button1-path">
<path
sodipodi:nodetypes="ccc"
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 316.65466,47.839954 25.8491,-28.270742 H 485"
id="path958"
inkscape:connector-curvature="0" />
<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="485"
y="19.069216" />
<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="313.15466"
y="44.339954" />
</g>
<g
id="button2-path"
inkscape:label="#g974">
<rect
transform="scale(-1,1)"
inkscape:label="#rect869"
y="19.069212"
x="-69.800262"
height="1"
width="1"
id="button2-leader"
style="color:#000000;text-align:end;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round" />
<path
inkscape:connector-curvature="0"
id="path1398"
d="M 237.81014,47.839954 211.96104,19.569212 H 69.464797"
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"
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="234.31013"
y="44.339954" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
style="display:inline"
transform="translate(-68.791597,5.973336)">
<g
id="led0-path">
<path
inkscape:connector-curvature="0"
id="path877"
d="m 484.99999,289.92309 -182.37295,10e-6"
style="color:#000000;display:inline;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"
sodipodi:nodetypes="cc" />
<rect
y="286.4231"
x="299.12704"
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="289.4231"
x="485"
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: 18 KiB

View File

@@ -0,0 +1,401 @@
<?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.92.2 2405546, 2018-03-11"
sodipodi:docname="logitech-g-pro.svg"
inkscape:export-filename="/home/jimmac/logitech-g-pro.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="1.9881757"
inkscape:cx="89.311243"
inkscape:cy="190.59507"
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="2560"
inkscape:window-height="1381"
inkscape:window-x="0"
inkscape:window-y="0"
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:snap-global="true"
inkscape:snap-object-midpoints="true">
<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" />
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
style="display:inline"
transform="translate(-68.791597,5.973336)">
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:1.5;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 266.16642,15.04496 c -22.92467,4.66104 -61.30054,24.9861 -69.63884,36.00461 -12.61957,47.68515 -14.80528,106.34213 -15.92423,155.87459 -2.00552,88.77814 32.07323,172.95939 100.58708,173.36506 46.62862,0.10701 67.422,-29.01299 84.50986,-68.69579 14.62944,-39.36304 14.98472,-73.16931 15.71414,-103.07446 0.40991,-66.33641 -5.20115,-105.262 -13.70975,-157.25734 C 354.76319,35.15715 299.72236,14.28047 297.61469,15.04496 v 5.05311 h -3.45953 v -5.05311 l -25.2862,3.5e-4 v 5.05276 l -2.70254,-10e-6 z"
id="path870"
sodipodi:nodetypes="ccscccccccccccc" />
<path
style="display:inline;fill:#989896;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 229.17383 21.009766 C 229.00748 20.992194 228.88813 20.993688 228.82227 21.017578 L 228.82227 26.072266 L 225.36328 26.072266 L 225.36328 21.017578 L 200.07812 21.019531 L 200.07812 26.072266 L 197.375 26.072266 L 197.375 21.017578 C 174.45033 25.678618 136.07463 46.004927 127.73633 57.023438 C 116.35922 100.01374 113.47533 151.86101 112.19531 197.96094 L 112.19727 197.96289 L 194.28711 159.88281 C 194.19011 159.92441 192.73412 179.58139 211.27148 179.11133 C 229.80884 178.64127 228.7988 160.0586 228.82227 160.05078 L 312.53125 197.98242 L 312.58008 197.88281 C 312.02168 141.36823 306.68242 104.71806 298.91211 57.234375 C 286.37504 41.63316 234.33067 21.554483 229.17383 21.009766 z "
id="path1006"
transform="translate(68.791597,-5.973336)" />
<rect
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:0.99999982;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="21.314863"
height="55.109035"
x="270.83594"
y="53.880791"
rx="10.657432"
ry="11.054664"
inkscape:label="#button2" />
<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="-328.60678"
sodipodi:cy="280.97754"
sodipodi:rx="13.376111"
sodipodi:ry="13.525835"
sodipodi:start="0.78539816"
sodipodi:end="5.4977871"
sodipodi:arc-type="arc"
d="m -319.14844,290.54175 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.91668,0"
sodipodi:open="true"
transform="rotate(-90)"
inkscape:label="#led0" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#bfc2bb;stroke-width:1.5;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 297.61497,15.045758 v 5.052735 h -0.004 c -0.072,38.851502 0.17641,91.903097 0.004,129.455077 l 76.24024,35.0039 C 371.4683,138.46428 370.62141,92.883297 359.46263,43.866071 348.69198,36.04897 332.00497,28.07624 318.64622,22.491071 307.62271,18.054789 298.7509,15.065752 297.61497,15.045758 Z"
id="button1" />
<path
inkscape:connector-curvature="0"
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 183.47044,134.90318 -3.23828,3.46679 -2.36328,40.83203 3.375,2.76368 c 0.0158,-0.0318 0.0292,-0.0639 0.0449,-0.0957 0.4971,-15.01188 1.2161,-30.49256 2.37305,-45.99218 -0.0654,-0.32133 -0.12562,-0.6554 -0.19141,-0.97461 z"
id="button4" />
<path
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 180.98802,191.98911 -3.65625,3.90235 0.70171,41.21448 3.55024,4.1781 c -0.0255,-0.32903 -0.0218,-0.78636 -0.0254,-1.21094 -0.88777,-10.87151 -1.20752,-21.97334 -0.95508,-33.14844 0.11017,-4.87659 0.24499,-9.90124 0.38477,-14.93555 z"
id="button3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccsc" />
<path
style="display:inline;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 267.69435,112.88968 25.82029,0.0837 0.40098,45.62246 c -3.09214,13.17174 -23.47755,15.50099 -27.35134,0 z"
id="rect1003"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<rect
inkscape:label="#button5"
ry="5.2884812"
rx="6.152226"
y="132.22084"
x="273.28256"
height="31.288317"
width="13.468106"
id="button5"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:0.99999988;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="display:inline;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect985"
width="25.286205"
height="35.886375"
x="268.86896"
y="15.044975"
rx="0"
ry="0" />
<path
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#d3d3ce;fill-opacity:1;stroke:#bfc2bb;stroke-width:1.5;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 266.16185,15.045758 c -18.53203,3.769004 -47.15067,17.769904 -61.84571,28.81836 -11.15926,49.01809 -14.18353,94.599442 -16.57028,140.693352 l 75.52927,-35.0039 2.89062,-129.455077 h -0.004 z"
id="button0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:label="#button0" />
<path
style="display:inline;fill:#d3d3ce;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 132.05273 52.619141 C 130.22394 54.195066 128.75358 55.679201 127.73633 57.023438 C 116.71194 98.680923 113.6505 148.70889 112.30664 193.71484 L 112.35938 193.70117 L 116.07812 191.96875 C 117.08353 147.52982 122.45967 94.477962 132.07227 52.640625 C 132.0735 52.635269 132.0547 52.625295 132.05273 52.619141 z "
transform="translate(68.791597,-5.973336)"
id="path1008" />
<path
style="display:inline;fill:#d3d3ce;stroke:#babdb6;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 294.34961 52.697266 C 302.02365 91.016721 306.47025 145.70637 307.9375 192.13477 L 312.22852 194.4668 L 312.54297 194.60547 C 311.81356 139.93715 306.53143 103.79539 298.91211 57.234375 C 297.72345 55.755196 296.17495 54.236496 294.34961 52.697266 z "
id="path1010"
transform="translate(68.791597,-5.973336)" />
<g
id="button4-path">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path964"
d="M 69.397827,158.3947 H 177.72623"
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:end;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button4-leader"
width="1"
height="1"
x="68.806679"
y="157.8947" />
<rect
y="154.8947"
x="174.22623"
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="button3-path">
<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 69.516354,216.62752 H 176.10094"
id="path966"
inkscape:connector-curvature="0" />
<rect
y="216.12752"
x="68.814278"
height="1"
width="1"
id="button3-leader"
style="color:#000000;text-align:end;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect875" />
<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="172.60094"
y="213.12752" />
</g>
<g
id="button5-path">
<rect
y="147.48535"
x="485"
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" />
<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 484.55132,147.9915 H 280.86242"
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="276.5166"
y="144.36501" />
</g>
<g
id="button2-path"
inkscape:label="#g997">
<rect
inkscape:label="#rect869"
y="80.93531"
x="485"
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 281.49336,81.435307 203.01408,2e-6"
id="path908"
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="rect910"
width="6.999999"
height="6.999999"
x="277.99338"
y="77.935318" />
</g>
<g
id="button1-path"
inkscape:label="#g1002">
<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="485"
y="52.581367" />
<path
sodipodi:nodetypes="cc"
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 329.18291,53.081369 H 485.00002"
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="325.29486"
y="49.581367" />
</g>
<g
id="button0-path"
inkscape:label="#button0-path">
<rect
inkscape:label="#rect871"
style="color:#000000;text-align:end;overflow:visible;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button0-leader"
width="1"
height="1"
x="68.718681"
y="52.581367" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path960"
d="m 236.34967,53.081367 -166.192195,2e-6"
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="49.581367"
x="232.84967"
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" />
</g>
<path
sodipodi:nodetypes="ccccsccsccccc"
id="led1"
d="m 381.38813,191.93864 c 0.0522,5.34015 0.0617,10.85826 0.0263,16.58033 -0.72942,29.90515 -1.0847,63.71142 -15.71414,103.07446 -17.08786,39.6828 -37.88124,68.8028 -84.50986,68.69579 -68.51385,-0.40567 -102.5926,-84.58692 -100.58708,-173.36506 0.11095,-4.91156 0.2425,-9.86517 0.38356,-14.93656 l 2.19839,0.0215 c -0.13796,4.95985 -0.26661,11.86142 -0.37513,16.66495 -1.96142,86.82554 31.36807,166.8792 98.37558,167.27594 45.60344,0.10466 65.93966,-26.09877 82.65183,-64.90878 14.30779,-38.49728 14.65526,-71.56001 15.36865,-100.80742 0.0346,-5.59622 0.0253,-13.00199 -0.0257,-18.22469 z"
style="color:#000000;display:inline;overflow:visible;opacity:1;fill:#babdb6;fill-opacity:1;stroke:#babdb6;stroke-width:1.5;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"
inkscape:label="#led1" />
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
style="display:inline"
transform="translate(-68.791597,5.973336)">
<g
id="led0-path">
<path
inkscape:connector-curvature="0"
id="path877"
d="m 485,329.52026 -203.94584,0"
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="326.02026"
x="277.47754"
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="329.02026"
x="485"
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">
<path
sodipodi:nodetypes="cc"
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 484.99745,272.95285 -105.75862,0"
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="485"
y="272.45285"
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="375.73883"
y="269.45285" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1 @@
logitech-g-pro.svg

View File

@@ -0,0 +1,479 @@
<?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="400.87393"
viewBox="0 0 467.83105 398.87393"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="logitech-g402.svg"
inkscape:export-filename="/home/daniele/Scrivania/logitech-g402.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="247.98208"
inkscape:cy="192.40531"
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="1280"
inkscape:window-height="992"
inkscape:window-x="0"
inkscape:window-y="0"
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="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
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;text-align:start;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="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="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: 28 KiB

View File

@@ -213,7 +213,7 @@
<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"
id="button10"
inkscape:connector-curvature="0" />
</g>
</g>

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

1190
data/gnome/logitech-g600.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 95 KiB

View File

@@ -0,0 +1,331 @@
<?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.3 (2405546, 2018-03-11)"
sodipodi:docname="logitech-g603.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="5.656854"
inkscape:cx="141.15425"
inkscape:cy="248.38223"
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="1017"
inkscape:window-x="-4"
inkscape:window-y="-4"
inkscape:window-maximized="1"
fit-margin-top="20"
fit-margin-left="20"
fit-margin-bottom="20"
fit-margin-right="0"
inkscape:snap-smooth-nodes="false"
inkscape:object-nodes="true"
inkscape:snap-nodes="true"
inkscape:guide-bbox="true">
<inkscape:grid
type="xygrid"
id="grid956"
originx="-68.791593"
originy="1.2767161" />
<sodipodi:guide
position="126.53125,259.03126"
orientation="0,1"
id="guide4772"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="Device"
inkscape:label="Device"
transform="translate(-68.791597,-390.00576)"
style="display:inline">
<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,545.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
sodipodi:nodetypes="ccccsc"
inkscape:connector-curvature="0"
id="button0"
d="m 174.91314,423.64614 3.0021,129.78071 -48.65044,25.07759 -32.826242,-92.02794 c -1.027151,-19.85446 -0.810142,-28.0624 15.700932,-37.87909 17.32775,-10.30224 41.981,-20.75843 62.77365,-24.95127 z"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:#bfc2bb;stroke-width:1.98825693;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;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.9684,25.54626 27.50793,-87.30721 c 4.86457,-36.68335 -44.6715,-55.30429 -73.70157,-67.16971 z"
id="button1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#path944" />
<g
style="display:inline"
id="g1304"
transform="translate(337.51617,-37.32483)">
<ellipse
ry="1.9375004"
rx="1.8137393"
cy="614.46423"
cx="-145.51083"
id="ellipse1298"
style="fill:#babdb6;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
</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"
transform="translate(0,-6)">
<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)" />
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -14,8 +14,8 @@
viewBox="0 0 129.64583 129.64583"
version="1.1"
id="svg8"
sodipodi:docname="G9.svg"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="logitech-g9.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
enable-background="new">
<defs
id="defs2">
@@ -38,16 +38,16 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4142136"
inkscape:cx="108.9024"
inkscape:cy="270.58583"
inkscape:zoom="8.0000004"
inkscape:cx="264.58829"
inkscape:cy="370.72481"
inkscape:document-units="px"
inkscape:current-layer="LEDs"
inkscape:current-layer="Device"
inkscape:document-rotation="0"
showgrid="false"
showguides="false"
showguides="true"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
@@ -55,8 +55,8 @@
inkscape:guide-bbox="true"
units="px"
inkscape:snap-global="false"
inkscape:measure-start="200.26,379.645"
inkscape:measure-end="199.294,334.274">
inkscape:measure-start="296.875,398.125"
inkscape:measure-end="288.375,379.75">
<inkscape:grid
type="xygrid"
id="grid78" />
@@ -71,12 +71,12 @@
id="guide81"
inkscape:locked="false" />
<sodipodi:guide
position="51.756014,88.614617"
position="-0.18708866,88.492936"
orientation="0,1"
id="guide83"
inkscape:locked="false" />
<sodipodi:guide
position="57.778386,76.729167"
position="13.758333,76.993746"
orientation="0,1"
id="guide85"
inkscape:locked="false" />
@@ -156,13 +156,13 @@
<path
style="fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61647916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 34.611402,214.21326 -0.467722,5.00462 16.18317,-1.07576 0.04677,-4.77076 z"
id="button3"
id="button9"
inkscape:connector-curvature="0"
inkscape:label="#path896" />
<path
style="fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61647916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 64.405272,212.43591 -14.066729,0.93544 0.02888,4.76741 13.98589,-0.84138 z"
id="button4"
id="button8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#path898" />
@@ -184,14 +184,14 @@
<path
style="fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61647916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 24.274755,248.26339 c -0.124726,-9.60388 -0.342996,-10.78877 0.748354,-17.86696 0.576857,0.0935 1.153713,0.18709 1.73057,0.28063 -0.826308,5.86211 -1.652616,11.72422 -2.478924,17.58633 z"
id="button6"
id="button3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
inkscape:label="#path4606" />
<path
style="fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:0.61647916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 27.104469,228.7594 c -0.561265,-0.0779 -1.122531,-0.15591 -1.683796,-0.23386 0.919852,-4.78635 2.120338,-8.77758 3.671614,-12.95589 -0.662606,4.39658 -1.325212,8.79317 -1.987818,13.18975 z"
id="button5"
id="button4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
inkscape:label="#path4608" />
@@ -207,6 +207,20 @@
id="button1"
inkscape:connector-curvature="0"
inkscape:label="#path4612" />
<path
transform="translate(0,164.70833)"
id="button6"
d="m 62.980849,30.452818 a 0.06589785,0.06589785 0 0 0 -0.046,0.02066 l -1.13998,1.23145 a 0.06589785,0.06589785 0 0 0 0.002,0.09147 l 1.13947,1.139465 a 0.06589785,0.06589785 0 0 0 0.10697,-0.07338 l -0.50385,-1.113628 0.50488,-1.205093 a 0.06589785,0.06589785 0 0 0 -0.0636,-0.09095 z"
style="display:inline;color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.25282338;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:connector-curvature="0"
inkscape:label="#path1016" />
<path
transform="translate(0,164.70833)"
id="button7"
d="m 75.28463,30.433444 a 0.06589785,0.06589785 0 0 1 0.046,0.02066 l 1.13998,1.23145 a 0.06589785,0.06589785 0 0 1 -0.002,0.09147 l -1.13947,1.139465 a 0.06589785,0.06589785 0 0 1 -0.10697,-0.07338 l 0.50385,-1.113628 -0.50488,-1.205093 a 0.06589785,0.06589785 0 0 1 0.0636,-0.09095 z"
style="display:inline;color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.25282338;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:connector-curvature="0"
inkscape:label="#path1016-3" />
</g>
<g
inkscape:groupmode="layer"
@@ -215,26 +229,26 @@
transform="translate(0,-2.6458338)"
style="display:inline">
<g
transform="matrix(0.26458333,0,0,0.26458333,40.502648,-13.272558)"
transform="matrix(0.26458333,0,0,0.26458333,40.502648,-1.1017221)"
style="display:inline"
id="button2-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 336.91912,170.14213 -225.57514,0.36405"
d="M 336.91912,169.83277 147.21994,169.67013 109.21898,124.50618"
id="path51"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
sodipodi:nodetypes="ccc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53"
width="6.999999"
height="6.999999"
x="107.41756"
y="166.91161" />
x="106.00334"
y="120.59612" />
<rect
y="169.75269"
x="336.91913"
y="169.31075"
x="336.56558"
height="1"
width="1"
id="button2-leader"
@@ -244,7 +258,7 @@
<g
transform="matrix(-0.26458333,0,0,-0.26458333,116.08398,94.455601)"
style="display:inline"
id="button3-path"
id="button9-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"
@@ -264,7 +278,7 @@
x="438.04599"
height="1"
width="1"
id="button3-leader"
id="button9-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
@@ -323,13 +337,13 @@
inkscape:label="#rect871" />
</g>
<g
transform="matrix(0.26458333,0,0,-0.26458333,13.573867,94.421678)"
transform="matrix(0.26458333,0,0,0.26458333,13.573867,4.9859563)"
style="display:inline"
id="button4-path"
id="button8-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 438.04598,191.7488 H 193.48839 l -29.95691,-26.43012"
d="m 438.04598,191.7488 -249.80759,-0.5 -23.58191,-19.43012"
id="path51-1-9-6-8-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
@@ -338,21 +352,21 @@
id="rect53-2-3-2-7-4"
width="6.999999"
height="6.999999"
x="160.13437"
y="161.68198" />
x="161.75937"
y="169.05698" />
<rect
y="191.3428"
x="438.04599"
height="1"
width="1"
id="button4-leader"
id="button8-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(-0.26458333,0,0,-0.26458333,116.07548,106.30868)"
style="display:inline"
id="button5-path"
id="button4-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
@@ -372,14 +386,14 @@
x="438.04599"
height="1"
width="1"
id="button5-leader"
id="button4-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(-0.26458333,0,0,-0.26458333,116.05287,118.29899)"
style="display:inline"
id="button6-path"
id="button3-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
@@ -399,8 +413,62 @@
x="438.04599"
height="1"
width="1"
id="button3-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
<g
transform="matrix(-0.29294531,0,0,0.26458333,70.05035,-34.688457)"
style="display:inline"
id="button6-path"
inkscape:label="#g156">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 33.59277,251.21287 205.11339,-0.0974"
id="path966-9-6"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect976-8-7"
width="6.999999"
height="6.999999"
x="29.446411"
y="247.53609" />
<rect
y="250.62447"
x="238.5843"
height="1"
width="1"
id="button6-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect875" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,40.372307,-13.288752)"
style="display:inline"
id="button7-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 336.91912,170.14213 -196.70014,0.23905"
id="path51-1-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-0-7"
width="6.999999"
height="6.999999"
x="137.97041"
y="166.84467" />
<rect
y="169.75269"
x="336.91913"
height="1"
width="1"
id="button7-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect871" />
</g>
</g>
@@ -411,13 +479,13 @@
transform="translate(0,-2.6458338)"
style="display:inline">
<g
transform="matrix(0.26458333,0,0,-0.26458333,13.686363,102.7808)"
transform="matrix(0.26458333,0,0,0.26458333,13.592814,20.269951)"
style="display:inline"
id="led0-path"
inkscape:label="#g146">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 438.04598,178.25732 H 151.75439 l -10.41041,-7.75114"
d="m 438.04598,178.25732 -267.54159,0.25 -31.41041,-32.75114"
id="path51-1-9-6-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
@@ -426,8 +494,8 @@
id="rect53-2-3-2-2"
width="6.999999"
height="6.999999"
x="137.02765"
y="165.93933" />
x="135.77765"
y="141.93933" />
<rect
y="177.79317"
x="438.04599"

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,349 @@
<?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"
version="1.1"
id="svg942"
width="450"
height="450"
viewBox="0 0 450 450.00001"
sodipodi:docname="logitech-mx-anywhere2.svg"
inkscape:version="0.92.2 2405546, 2018-03-11">
<metadata
id="metadata948">
<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>
<defs
id="defs946" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3200"
inkscape:window-height="1677"
id="namedview944"
showgrid="true"
inkscape:object-paths="true"
inkscape:object-nodes="false"
inkscape:snap-intersection-paths="true"
inkscape:zoom="5.6568542"
inkscape:cx="132.29246"
inkscape:cy="334.55769"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="Device"
inkscape:snap-object-midpoints="true"
inkscape:snap-grids="false"
inkscape:snap-to-guides="false"
inkscape:snap-others="true"
inkscape:snap-nodes="true"
inkscape:snap-bbox="true"
inkscape:snap-bbox-midpoints="true">
<inkscape:grid
type="xygrid"
id="grid880" />
</sodipodi:namedview>
<g
inkscape:groupmode="layer"
id="Device"
inkscape:label="Device"
style="display:inline"
transform="translate(0,321.03999)">
<path
style="opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:3.41371107px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 218.69405,-299.42426 c 23.35649,1.73474 46.18087,5.06587 68.35514,10.34733 23.75734,6.5434 34.95965,18.75684 40.44869,33.55046 14.75489,46.96997 14.92489,97.01791 12.09878,148.09014 -0.22053,69.160277 9.88471,151.952398 -41.25946,193.685563 C 257.44112,119.62007 184.10794,116.48359 139.99157,87.503464 100.63876,61.652557 96.65491,4.57244 95.843246,-38.897519 c -0.639505,-34.2499 -4.904947,-68.500891 -3.243152,-102.716411 1.744881,-35.9262 7.356301,-78.71605 13.841016,-107.01431 3.99526,-15.72475 12.19144,-29.89698 35.27502,-38.56736 23.0293,-5.08776 48.5148,-9.23085 76.97792,-12.22866 z"
id="path958"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccassaccc" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.04822683;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 219.40508,-294.00752 67.6238,14.41163 c 10.27506,3.52994 19.61317,7.79609 21.7283,17.7374 34.47843,139.54316 2.38187,235.179053 -22.17174,337.675524 -2.70961,6.974963 -4.75518,14.198956 -16.6288,17.737404 -33.62713,9.347182 -67.25427,11.307342 -100.8814,0 C 159.63341,88.719872 154.6317,82.405292 150.45098,75.817034 125.18112,-36.599477 94.771098,-127.54074 128.50096,-260.97161 c 1.682,-10.88963 9.98626,-14.60515 18.62426,-17.95912 z"
id="path970"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccc" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.38959765;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 191.52096,-288.19118 c 1.4779,64.07686 0.63073,131.09048 4.38208,189.294959 0.53834,8.352647 23.14409,12.010754 23.14409,12.010754 0,0 22.57181,-3.520747 23.2016,-11.833881 4.83394,-63.807432 1.5673,-125.647242 3.26713,-189.723552"
id="path960"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cscsc" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.04822683;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 300.42159,70.944985 C 339.27478,43.967826 336.69108,-80.871179 335.23506,-108.90368 325.41759,-45.455804 296.49373,32.142014 300.42159,70.944985 Z"
id="path974"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:3.41371107px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 91.703995,-152.38566 c -7.529254,25.22562 -5.874086,43.32648 -5.814066,51.60811 l 5.653792,1.441169 z"
id="button3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
inkscape:label="" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:3.41371107px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 86.425202,-87.164219 c 1.984551,13.294118 0.345072,23.115228 8.757835,42.569729 l -2.771465,-46.006346 z"
id="button4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc"
inkscape:label="" />
<path
style="opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.38959765;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 219.40508,-293.35803 V -87.543322"
id="path1908"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="display:inline;opacity:1;vector-effect:none;fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:3.07234001;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="rect972"
width="10.198998"
height="20.619715"
x="214.33723"
y="-122.47431"
ry="5.0994992" />
<rect
style="display:inline;opacity:1;vector-effect:none;fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:3.07234001;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="button2"
width="23.3599"
height="23.830233"
x="207.57869"
y="-163.18437"
ry="4.7033353"
inkscape:label="" />
<rect
style="display:inline;opacity:1;vector-effect:none;fill:#d9dad7;fill-opacity:1;stroke:#babdb6;stroke-width:3.07234001;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="rect966"
width="30.414902"
height="60.202698"
x="203.64337"
y="-248.47151"
ry="8.4660034" />
<path
style="opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.04822683;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 136.54246,-257.72138 -16.3049,112.56651 63.0247,-0.6271 1.25422,-131.0663 z"
id="button0"
inkscape:connector-curvature="0"
inkscape:label="" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.04822683;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 299.43465,-259.28913 16.30489,112.56648 -63.0247,-0.6271 -1.25422,-131.0663 z"
id="button1"
inkscape:connector-curvature="0"
inkscape:label="" />
<path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.56880355px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 199.80869,-221.82015 -3.90101,2.25227 3.90101,2.25225"
id="button5"
inkscape:connector-curvature="0"
inkscape:label="" />
<path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#babdb6;stroke-width:2.56880355px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 237.6341,-221.8517 3.90101,2.25227 -3.90101,2.25225"
id="button6"
inkscape:connector-curvature="0"
inkscape:label="" />
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
transform="translate(0,321.03999)"
style="display:inline">
<g
id="button5-path"
inkscape:label="">
<path
inkscape:connector-curvature="0"
id="path884"
d="m 198.00952,-210.78264 -29.43205,29.43205 H 0.35355339"
style="display:inline;fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
inkscape:label=""
y="-181.85059"
x="0.10401335"
height="1"
width="1"
id="button5-leader"
style="text-align:end;display:inline;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
<rect
y="-214.28264"
x="194.50952"
height="7"
width="7"
id="rect2047"
style="display:inline;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
</g>
<g
id="button3-path"
inkscape:label="">
<rect
inkscape:label=""
y="-125.62335"
x="0.05242718"
height="1"
width="1"
id="button3-leader"
style="text-align:end;display:inline;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
<path
inkscape:connector-curvature="0"
id="path2119"
d="M 80.53685,-125.12076 H 0.51959257"
style="display:inline;opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="-128.62076"
x="77.03685"
height="7"
width="7"
id="rect2047-9"
style="display:inline;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
</g>
<g
id="button4-path"
inkscape:label="">
<rect
inkscape:label=""
y="-74.184525"
x="0.0703125"
height="1"
width="1"
id="button4-leader"
style="text-align:end;display:inline;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
<path
inkscape:connector-curvature="0"
id="path2121"
d="M 81.835831,-73.68108 H 0.51959257"
style="display:inline;opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="-77.181084"
x="78.335831"
height="7"
width="7"
id="rect2047-87"
style="display:inline;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
</g>
<g
style="display:inline"
id="button0-path"
inkscape:label="">
<path
inkscape:connector-curvature="0"
id="path2117"
d="M 154.83858,-230.33824 96.384414,-288.7924 H 0.51959257"
style="display:inline;fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
inkscape:label=""
y="-289.29233"
x="0.25592589"
height="1"
width="1"
id="button0-leader"
style="text-align:end;display:inline;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
<rect
y="-233.83824"
x="151.33858"
height="7"
width="7"
id="rect2047-7"
style="display:inline;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
</g>
<g
id="button1-path"
inkscape:label="">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path2125"
d="m 284.47693,-230.07844 55.72631,-55.72631 h 109.63402"
style="display:inline;opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="-233.57845"
x="280.97693"
height="7"
width="7"
id="rect2047-0"
style="display:inline;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
<rect
y="-286.3056"
x="448.96484"
height="1"
width="1"
id="button1-leader"
style="text-align:start;display:inline;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
inkscape:label="" />
</g>
<g
id="button2-path"
inkscape:label="">
<path
inkscape:connector-curvature="0"
id="path2129"
d="m 219.25864,-151.26926 42.56584,42.56585 h 187.62309"
style="display:inline;opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="-154.76926"
x="215.75864"
height="7"
width="7"
id="rect2047-08"
style="display:inline;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
<rect
inkscape:label=""
y="-109.20406"
x="448.96875"
height="1"
width="1"
id="button2-leader"
style="text-align:start;display:inline;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
</g>
<g
id="button6-path"
inkscape:label="">
<path
inkscape:connector-curvature="0"
id="path891"
d="m 239.67007,-210.78999 29.97581,29.97581 h 180.25081"
style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
inkscape:label=""
y="-181.31418"
x="448.96875"
height="1"
width="1"
id="button6-leader"
style="text-align:start;display:inline;opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
<rect
y="-214.28999"
x="236.17007"
height="7"
width="7"
id="rect2047-8"
style="display:inline;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
style="display:inline" />
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1 @@
logitech-mx-anywhere2.svg

View File

@@ -0,0 +1,359 @@
<?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="485"
height="445"
viewBox="0 0 128.32296 117.73977"
version="1.1"
id="svg8"
sodipodi:docname="logitech-mx-master-2s.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="189.95565"
inkscape:cy="227.78986"
inkscape:document-units="px"
inkscape:current-layer="Buttons"
showgrid="true"
inkscape:window-width="2560"
inkscape:window-height="1376"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
units="px"
showguides="false"
inkscape:snap-global="false">
<inkscape:grid
type="xygrid"
id="grid5521"
spacingy="10.583333"
spacingx="264583.33"
originy="5.2916751" />
</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"
style="display:inline"
transform="translate(0,-179.26022)">
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 19.866021,223.53772 c 0,0 -0.785103,0.71829 -2.204974,1.68714 -1.419868,0.96885 -3.558026,2.2885 -4.877667,3.35758 -1.319644,1.06907 -2.004523,2.37202 -2.622585,3.77518 -0.6180575,1.40317 -1.4031635,5.06142 -2.121448,9.92238 -0.718287,4.86097 -1.102486,9.43796 -1.2194155,13.69757 -0.11693,4.2596 -0.451017,4.79414 0.868623,8.76978 1.5191735,3.4768 3.2046955,4.45805 4.2429035,5.41221 1.085782,0.83521 6.965712,5.3955 11.72645,8.30206 4.760738,2.90655 11.225936,6.8707 14.699824,8.16842 1.860286,0.69493 2.586897,1.04147 4.032189,1.46211 1.219513,0.35492 2.417778,0.58726 4.971452,0.8765 4.324451,0.48981 9.010044,-0.72432 11.943604,-2.20498 2.83974,-1.63702 7.75082,-4.94448 11.04158,-8.70296 3.29075,-3.75848 4.17608,-4.69392 4.91107,-7.76752 0.73499,-3.0736 1.23612,-9.6217 1.11919,-15.31788 -0.11693,-5.69619 -0.7851,-13.64745 -1.41987,-18.67546 -0.63476,-5.02801 -1.26952,-7.684 -1.26952,-7.684"
id="path5225"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csccsscccsssccsscc" />
<path
sodipodi:nodetypes="cscssscsc"
inkscape:connector-curvature="0"
id="button5"
d="m 19.676541,228.60285 c 0,0 -5.707153,4.07215 -6.676005,5.10782 -0.968853,1.03567 -1.5368,2.70611 -1.703845,3.74178 -0.167042,1.03567 -1.169302,7.58377 -1.5702065,12.56167 -0.4009035,4.97789 -0.7015815,6.98241 -0.233862,9.32102 0.467722,2.33861 1.4365735,3.64155 2.2383825,4.30972 0.801809,0.66817 13.229842,9.2208 15.969354,10.7576 0.342743,-3.52158 1.597747,-7.89241 -0.165219,-10.5636 -4.912482,-7.44322 -8.870526,-19.92797 -7.858599,-35.23601 z"
style="fill:#d3d3ce;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:label="#path5657" />
<path
style="fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 19.676541,228.60285 c 0,0 -5.707153,4.07215 -6.676005,5.10782 -0.968853,1.03567 -1.5368,2.70611 -1.703845,3.74178 -0.167042,1.03567 -1.169302,7.58377 -1.5702065,12.56167 -0.4009035,4.97789 -0.7015815,6.98241 -0.233862,9.32102 0.467722,2.33861 1.4365735,3.64155 2.2383825,4.30972 0.801809,0.66817 14.365737,9.78875 17.105249,11.32555 2.739514,1.5368 8.352173,4.57699 12.127356,5.34539 3.775181,0.7684 7.249684,1.20271 12.127357,0.93544 4.87767,-0.26727 11.35895,-4.00904 13.0962,-5.57925 1.73725,-1.57021 6.38106,-7.18287 7.38333,-10.08942 1.00226,-2.90656 1.77066,-11.42578 1.26952,-20.21226 -0.50113,-8.78649 -1.06907,-15.30119 -1.1693,-16.5039 -0.10022,-1.20271 -0.0334,-7.58377 -0.93544,-18.40819 -0.90204,-10.82441 -1.36976,-11.99372 -2.20498,-14.26551 -0.83522,-2.27179 -7.85104,-5.47902 -11.22532,-6.84878 -3.37427,-1.36976 -9.585632,-3.70469 -11.541371,-3.84016 -1.955737,-0.13547 -8.938152,2.40359 -11.944935,3.77334 -3.006783,1.36976 -8.073472,4.0577 -8.073472,4.0577 -4.468114,9.08817 -5.960983,22.63921 -8.068658,35.26804 z"
id="path78"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cscssssssscccsscsscc" />
<rect
id="button6"
width="5.315289"
height="5.3152866"
x="45.235897"
y="221.81316"
style="opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
rx="0.69999999"
ry="0.69999999"
inkscape:label="#rect5215" />
<rect
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="button2"
width="7.0870538"
height="14.552083"
x="44.220081"
y="199.01646"
rx="0.70000017"
ry="0.70000017"
inkscape:label="#rect5217" />
<path
id="button0"
style="fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 47.762508,195.6666 c -1.892311,0.0167 -3.873176,0.0377 -4.226442,0.0417 -0.709932,0.008 -1.720551,0.96885 -1.737251,1.72054 -0.0141,0.63483 -0.159624,28.13451 -0.171424,28.86684 -0.01181,0.73233 -0.563567,1.23439 -0.747315,1.40144 -0.183748,0.16704 -17.283331,6.12265 -17.708555,6.24077 -0.425221,0.11811 -0.761454,0.3511 -1.039434,-0.40751 -0.347226,-0.94758 -0.07087,-7.39416 0.87407,-17.9775 0.944941,-10.58333 4.739037,-22.21806 4.739037,-22.21806 0,0 5.066694,-2.68795 8.073477,-4.05771 3.006783,-1.36975 9.989198,-3.90881 11.944936,-3.77211 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccscccc"
inkscape:label="#path5627" />
<path
id="button1"
style="fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 47.763608,185.50377 v 10.08368 m -0.01842,0.0784 c 1.690105,-0.0156 3.321588,-0.0284 3.692045,-0.0244 0.944073,0.0103 2.438833,1.06072 2.488943,2.38872 0.0535,1.41886 0.18375,26.82718 0.23387,27.92966 0.0501,1.10249 0.14727,1.2276 0.71424,1.66464 0.56696,0.43703 13.97331,5.5279 14.74107,5.65783 0.76776,0.12993 0.88588,-0.14174 0.95675,-0.68508 0.0709,-0.54334 0.50944,-11.58187 0.27558,-14.7557 -0.23387,-3.17383 -1.089962,-16.77534 -2.799869,-24.09699 -2.565285,-1.75722 -6.479518,-3.48182 -8.74284,-4.4006 -3.37427,-1.36976 -9.585632,-3.70469 -11.541371,-3.84016"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccsccccc"
inkscape:label="#path5631" />
<path
style="opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 24.70574,258.91885 c 0,0 -2.944259,-4.70523 -3.525604,-7.81361 0.07137,0.16032 1.142052,1.76828 1.374962,1.88086"
id="button3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc"
inkscape:label="#path5636" />
<path
style="opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 20.347215,244.33435 c 0,0 0.503997,5.22172 0.804675,6.62488 0.300678,1.40317 1.186008,1.85418 1.186008,1.85418"
id="button4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc"
inkscape:label="#path5634" />
<path
style="opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 27.745199,193.33481 c 0,0 -1.681865,1.10395 -2.416856,2.12291 -0.734991,1.01897 -3.240643,8.4524 -4.510173,19.00955 -1.269531,10.55714 -0.985557,10.80771 -1.085783,14.51607 -0.100226,3.70837 0.06098,11.98844 0.627949,15.41385 0.566964,3.42541 2.69308,11.19754 4.086867,13.98512 1.393787,2.78757 2.944127,5.32102 3.061057,5.37113 0.116931,0.0501 0.175396,0.10858 0.400904,-0.0334 0.225509,-0.14198 1.03567,-0.92709 1.127544,-1.30294 0.09187,-0.37584 0.200452,-0.77675 0.04176,-1.17765 -0.158692,-0.40091 -2.029578,-3.25735 -2.38037,-4.14268 -0.350791,-0.88533 -2.15486,-4.86097 -2.873147,-8.4357 -0.718287,-3.57473 -1.503391,-7.51695 -1.753956,-9.83885 -0.250566,-2.32191 -0.183748,-4.09257 -0.183748,-5.04472 0,-0.95214 0.09187,-2.02958 0.09187,-2.02958 0.279608,-13.8572 2.014074,-26.7818 5.766082,-38.41311 z"
id="path5223"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csssccccccsssscc" />
<g
id="g1137"
transform="translate(-3.6416254,5.291675)">
<rect
y="193.85353"
x="48.915695"
height="14.312759"
width="1.8040693"
id="rect1131"
style="opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:none;stroke-width:0.26502112px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
style="opacity:1;vector-effect:none;fill:#d3d3ce;fill-opacity:1;stroke:none;stroke-width:0.26502112px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect1133"
width="1.8040693"
height="14.312759"
x="52.090698"
y="193.85353" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
transform="translate(0,-179.26022)">
<g
transform="matrix(0.26458333,0,0,0.26458333,2.1166624,183.016)"
style="display:inline"
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
inkscape:label="#g131"
id="button6-path"
style="display:inline"
transform="matrix(0.26458333,0,0,0.26458333,2.1166624,213.83995)">
<path
inkscape:connector-curvature="0"
id="path5469"
d="M 173.32645,40.025431 193.47816,54.00578 H 477.59978"
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"
sodipodi:nodetypes="ccc" />
<rect
y="36.559353"
x="169.85715"
height="6.999999"
width="6.999999"
id="rect5471"
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
y="53.505779"
x="477"
height="1"
width="1"
id="button6-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect867" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,2.1166624,204.05037)"
style="display:inline"
id="button0-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 132.23335,50.505764 H 477"
id="path5477"
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="rect5479"
width="6.999999"
height="6.999999"
x="128.42857"
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="button0-leader"
width="1"
height="1"
x="477"
y="50.005764" />
</g>
<g
inkscape:label="#g131"
id="button2-path"
style="display:inline"
transform="matrix(0.26458333,0,0,0.26458333,2.1166624,193.59933)">
<path
inkscape:connector-curvature="0"
id="path5485"
d="M 168.43093,50.505764 H 477"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.94604886;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
sodipodi:nodetypes="cc" />
<rect
y="47.005764"
x="167.35715"
height="6.999999"
width="6.999999"
id="rect5487"
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
y="50.005764"
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"
inkscape:label="#rect867" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,2.1166624,225.21704)"
style="display:inline"
id="button5-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.17770362;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 55.283671,50.505764 H 477"
id="path5493"
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="rect5495"
width="6.999999"
height="6.999999"
x="48.428574"
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="button5-leader"
width="1"
height="1"
x="477"
y="50.005764" />
</g>
<g
inkscape:label="#g131"
id="button4-path"
style="display:inline"
transform="matrix(0.26458333,0,0,0.26458333,2.1166624,235.80037)">
<path
inkscape:connector-curvature="0"
id="path5503"
d="M 69.460332,50.505764 H 477"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.13811326;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
sodipodi:nodetypes="cc" />
<rect
y="47.005764"
x="67.665703"
height="6.999999"
width="6.999999"
id="rect5505"
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
y="50.005764"
x="477"
height="1"
width="1"
id="button4-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect867" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,2.1166624,246.3837)"
style="display:inline"
id="button3-path"
inkscape:label="#g131">
<path
sodipodi:nodetypes="ccc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.12054801;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 78.817927,36.487907 105.14285,50.505764 H 477"
id="path5513"
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="rect5515"
width="6.999999"
height="6.999999"
x="75.454132"
y="33.075569" />
<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="button3-leader"
width="1"
height="1"
x="477"
y="50.005764" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
transform="translate(0,-14.552099)" />
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1 @@
logitech-mx-master-2s.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 739 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,637 @@
<?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="499"
height="499"
viewBox="0 0 132.02708 132.02709"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="steelseries-rival600.svg">
<defs
id="defs2">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath4634">
<rect
style="fill:#3d1400;fill-opacity:0.54140128;stroke:#000000;stroke-width:0.2117648;stroke-opacity:1"
id="rect4636"
width="107.04193"
height="199.41115"
x="30.877195"
y="155.32713" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4.0000002"
inkscape:cx="257.92153"
inkscape:cy="362.68056"
inkscape:document-units="px"
inkscape:current-layer="Device"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1012"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
units="px"
showguides="false"
inkscape:guide-bbox="true"
inkscape:snap-global="true"
inkscape:measure-start="305.625,430.5"
inkscape:measure-end="269.5,430.75">
<sodipodi:guide
position="66.14583,85.422621"
orientation="1,0"
id="guide4638"
inkscape:locked="false" />
<sodipodi:guide
position="115.99497,127"
orientation="0,1"
id="guide4640"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,0,255)" />
<sodipodi:guide
position="9.354433,5.2916666"
orientation="0,1"
id="guide4642"
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"
style="display:inline"
transform="translate(0,-0.26458333)">
<path
sodipodi:nodetypes="csccccccccsscccccccssssssscccccccccccscsssc"
inkscape:connector-curvature="0"
id="path4673"
d="m 40.02385,14.984554 c 1.580795,-2.51549 5.975285,-3.539984 9.385335,-4.965329 3.128814,-1.3077924 9.164225,-4.4175966 9.164225,-4.4175966 l 3.236676,-0.3099634 -0.0053,8.952208 1.214482,-5.705833 6.015962,-0.014436 1.88187,5.552463 -0.06133,-8.784402 2.895343,0.3674956 c 0,0 5.054486,2.5992473 7.544173,3.9686799 5.3036,2.9172035 11.440444,3.4069705 11.658567,6.9089765 l 2.434325,39.08341 2.530042,28.965015 c 0,0 0.286756,7.495044 -0.371079,10.115766 l -3.292458,-3.010664 -0.559771,0.835476 1.951257,2.541965 c 0.442734,0.62875 1.161707,2.907068 1.161707,2.907068 0,0 -0.1,2.252347 -0.334606,3.189677 -1.586764,6.33968 -6.10449,11.6792 -9.236308,15.53526 -2.544851,3.13335 -5.161521,5.64996 -8.518934,7.38326 -3.502718,1.80832 -7.474043,2.75126 -11.412536,2.91659 -4.30071,0.18053 -6.918836,0.23282 -12.770724,-1.91491 -4.175515,-1.53248 -7.367165,-5.08869 -9.121485,-7.10927 -9.051815,-10.4256 -9.37199,-18.857963 -8.769367,-19.894766 L 39.845444,91.905117 39.425142,90.803985 37.552215,94.320171 37.160224,93.903502 37.128144,92.93534 38.295133,81.010098 37.99214,64.407639 38.450135,63.977055 37.8061,63.559233 36.932233,47.808124 36.554461,47.459545 c 0,0 0.05032,-2.921717 0.06444,-3.330011 0.09797,-2.831675 0.826526,-3.731517 0.826526,-3.731517 0,0 0.08006,-10.762552 0.583863,-15.991305 0.174175,-1.807686 0.853434,-1.118453 0.93179,-1.886883 0.337789,-3.312656 -0.515822,-5.023298 1.062766,-7.535275 z"
style="display:inline;fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:1.32291663;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.57558382;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 232.34375,93.232422 v 60.628908 c 0.50061,1.14157 1.4678,2.62577 3.32617,4.04492 h 0.60742 V 93.232422 Z m 31.91406,0 v 64.673828 h 1.83985 c 0.83119,-0.78541 1.40627,-1.64516 1.81054,-2.55664 V 93.232422 Z"
transform="matrix(0.26458334,0,0,0.26458334,0,0.26458333)"
id="led0"
inkscape:connector-curvature="0" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:1.99999988;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="m 243.77734,85.634766 c -4.155,0 -7.5,3.345 -7.5,7.5 v 65.191404 c 2.23306,1.53273 5.55795,2.93721 10.59375,3.66211 l -0.0527,4.12695 h 6.42578 v -4.08593 c 5.24654,-0.45952 8.69408,-1.43412 10.98047,-2.7461 0.0194,-0.22077 0.0332,-0.44199 0.0332,-0.66797 V 93.134766 c 0,-4.155 -3.345,-7.5 -7.5,-7.5 z"
transform="matrix(0.26458334,0,0,0.26458334,0,0.26458333)"
id="button2"
inkscape:connector-curvature="0"
inkscape:label="#button2" />
<rect
style="fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:0.52916664;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="button6"
width="5.0028081"
height="14.072443"
x="63.525414"
y="49.707458"
ry="0.89731663"
inkscape:label="#button6" />
<path
style="fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:2;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="m 231.80196,19.999994 -10.42305,1.171881 c 0,0 -22.80933,11.752475 -34.63477,16.695313 -12.88838,5.38713 -29.498,9.260214 -35.47266,18.767578 -5.45964,8.687822 -3.23027,14.853779 -3.78906,25.429687 l 0.89286,110.357427 c 0.75974,6.5985 8.53258,13.62191 67.64583,28.30897 l 20.49636,0.59268 0.20962,-34.60642 c 1.20454,-5.7407 4.81879,-6.03597 9.92297,-6.35918 l 0.22147,-17.37007 c -14.80392,-2.13101 -15.06957,-10.10154 -15.06957,-10.10154 z"
transform="scale(0.26458333)"
id="button0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccssccccccccc"
inkscape:label="#button0" />
<path
style="fill:#d3d3ce;fill-opacity:1;stroke:#babdb6;stroke-width:2;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="m 268.57227,20.097656 0.34526,131.616644 c -0.47516,5.1884 -1.66448,10.08839 -15.67427,11.31543 l 6.2e-4,17.28677 c 4.75243,0.31401 8.60528,1.4133 9.36291,7.0537 l 0.15706,34.28927 23.61411,-0.28538 c 59.2439,-12.2777 65.79173,-21.56131 66.69622,-30.52428 V 90.634766 l -1.75195,-28.132813 c -0.82441,-13.235928 -24.01935,-15.08763 -44.06446,-26.113281 -9.40984,-5.175808 -28.51367,-15 -28.51367,-15 z"
id="button1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccscc"
inkscape:label="#button1"
transform="scale(0.26458333)" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 141.16016,153.26562 c -0.75439,1.32491 -2.46978,5.19815 -2.75782,13.52344 -0.0534,1.54316 -0.24414,12.58594 -0.24414,12.58594 l 1.42774,1.31641 v 0 l 2.08789,0.006 v -27.43148 z"
transform="scale(0.26458333)"
id="button5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc"
inkscape:label="#button5" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 139.70312,182.80273 3.17774,57.26563 h 4.10547 l -3.40926,-57.51817 z"
transform="scale(0.26458333)"
id="button4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#button4" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 143.60352,244.08398 2.65262,62.72071 4.65625,16.10854 -0.46289,-21.07144 -2.32143,-57.75782 z"
transform="scale(0.26458333)"
id="button3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc"
inkscape:label="#button3" />
<path
sodipodi:type="arc"
style="display:inline;opacity:1;fill:none;fill-opacity:0.39215686;stroke:#babdb6;stroke-width:3.62082291;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="led1"
sodipodi:cx="66.014069"
sodipodi:cy="113.27822"
sodipodi:rx="6.5174346"
sodipodi:ry="6.5174413"
d="m 70.622591,108.66969 a 6.5174346,6.5174413 0 0 1 0,9.21706 6.5174346,6.5174413 0 0 1 -9.217044,0 6.5174346,6.5174413 0 0 1 -10e-7,-9.21706"
sodipodi:start="5.4977871"
sodipodi:end="3.9269907"
sodipodi:open="true"
sodipodi:arc-type="arc"
inkscape:label="#led1" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.39687499;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 53.883876,61.201732 c 0.701684,-1.04199 1.713438,-1.252361 2.700948,-1.273296 -1.062711,6.124293 -2.23957,15.448337 -2.929315,17.714841 l -2.279083,-0.01181 c 0.921085,-4.981084 1.8723,-11.917817 2.50745,-16.429735 z"
id="led2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#led2" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.39687499;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 51.098164,78.550869 2.137416,0.0032 c -2.45902,3.08502 -5.645313,5.394855 -8.68164,7.870003 l 0.01274,-1.902901 c 2.26525,-2.119478 4.965592,-3.807868 6.531484,-5.970314 z"
id="led4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#led4" />
<path
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.38111177;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 39.904215,90.216504 c 2.946365,-4.10277 2.992207,-3.879322 3.87751,-4.907952 v 1.643592 c -1.069962,1.220999 -2.14185,2.446029 -3.652074,4.588366 -0.34675,-0.266319 -0.171896,-0.86875 -0.225436,-1.324006 z"
id="led6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:label="#led6" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="led3"
d="m 77.958921,61.201732 c -0.701684,-1.04199 -1.713438,-1.252361 -2.700948,-1.273296 1.062711,6.124293 2.23957,15.448337 2.929315,17.714841 l 2.279083,-0.01181 c -0.921085,-4.981084 -1.8723,-11.917817 -2.50745,-16.429735 z"
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.39687499;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#led3" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="led5"
d="m 80.744633,78.550869 -2.137416,0.0032 c 2.45902,3.08502 5.645313,5.394855 8.68164,7.870003 l -0.01274,-1.902901 c -2.26525,-2.119478 -4.965592,-3.807868 -6.531484,-5.970314 z"
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.39687499;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#led5" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="led7"
d="M 93.828039,91.01326 C 90.782971,87.489788 88.89285,86.252223 88.034855,85.182947 l 0.02219,1.740055 c 1.135212,1.088123 3.299661,3.204904 5.082908,5.26536 0.341405,-0.26364 0.635364,-0.724423 0.688079,-1.175102 z"
style="fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.37625623;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="#led7" />
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
transform="translate(0,-0.26458333)">
<g
id="button0-path"
inkscape:label="#button0-path">
<path
inkscape:connector-curvature="0"
id="path966"
d="m 50.609489,18.387576 -50.47151772,0.0019"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.26270351;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="17.462502"
x="-51.329166"
height="1.8520831"
width="1.8520833"
id="rect976"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50289863;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
transform="scale(-1,1)"
y="18.25625"
x="-0.26458332"
height="0.26458332"
width="0.26458332"
id="button0-leader"
style="color:#000000;text-align:end;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26458335;stroke-linecap:round"
inkscape:label="#button0-leader" />
</g>
<g
inkscape:label="#button5-path"
id="button5-path"
transform="matrix(1,0,0,0.99991172,-13.551005,26.460036)">
<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:0.26290539;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 50.998437,18.983827 37.196023,5.1572762 H 13.68371"
id="path8646"
inkscape:connector-curvature="0" />
<rect
transform="scale(-1,1)"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50289863;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="rect8648"
width="1.8520833"
height="1.8520831"
x="-51.329166"
y="17.462502" />
<rect
inkscape:label="#button5-leader"
style="color:#000000;text-align:end;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26458335;stroke-linecap:round"
id="button5-leader"
width="0.26458332"
height="0.26458332"
x="-13.815588"
y="5.0258474"
transform="scale(-1,1)" />
</g>
<g
transform="matrix(1,0,0,0.99991171,-13.551005,26.460037)"
id="button4-path"
inkscape:label="#button4-path">
<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:0.27385584;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 51.951929,30.017489 40.205394,18.387452 H 13.749442"
id="path8646-1"
inkscape:connector-curvature="0" />
<rect
y="28.52924"
x="-52.315731"
height="1.8520831"
width="1.8520833"
id="rect8648-0"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50289863;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
transform="scale(-1,1)"
y="18.256182"
x="-13.815588"
height="0.26458332"
width="0.26458332"
id="button4-leader"
style="color:#000000;text-align:end;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26458335;stroke-linecap:round"
inkscape:label="#button4-leader" />
</g>
<g
id="button3-path"
inkscape:label="#button3-path">
<path
style="fill:none;stroke:#888a85;stroke-width:0.26771936px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 0.13358648,58.076072 H 27.970024 l 10.933108,10.675053"
id="path8774"
inkscape:connector-curvature="0" />
<rect
transform="scale(-1,1)"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50287646;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="rect8648-0-7"
width="1.8520833"
height="1.8519195"
x="-39.559853"
y="67.568466" />
<rect
inkscape:label="#button3-leader"
style="color:#000000;text-align:end;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26457167;stroke-linecap:round"
id="button3-leader"
width="0.26458332"
height="0.26455995"
x="-0.26458332"
y="57.943771"
transform="scale(-1,1)" />
</g>
<g
id="button1-path"
inkscape:label="#button1-path">
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.26376939px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 131.97389,18.386474 H 80.896353"
id="path8957"
inkscape:connector-curvature="0" />
<rect
transform="scale(-1,1)"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50289863;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-6"
width="1.8520833"
height="1.8520831"
x="-81.801758"
y="17.4625" />
<rect
inkscape:label="#button1-leader"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26458335;stroke-linecap:round"
id="button1-leader"
width="0.26458332"
height="0.26458332"
x="-132.02708"
y="18.25625"
transform="scale(-1,1)" />
</g>
<g
id="button2-path"
inkscape:label="#button2-path">
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.26416048px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 131.95474,31.62391 H 66.14583"
id="path8993"
inkscape:connector-curvature="0" />
<rect
y="30.632051"
x="-67.080887"
height="1.8520831"
width="1.8520833"
id="rect976-6-9"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50289863;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
transform="scale(-1,1)"
y="31.485416"
x="-132.02708"
height="0.26458332"
width="0.26458332"
id="button2-leader"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26458335;stroke-linecap:round"
inkscape:label="#button2-leader" />
</g>
<g
id="button6-path"
inkscape:label="#button6-path">
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.26408577px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 131.91236,58.082243 H 66.14583"
id="path9075"
inkscape:connector-curvature="0" />
<rect
transform="scale(-1,1)"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50289863;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-6-9-2"
width="1.8520833"
height="1.8520831"
x="-67.0345"
y="57.148987" />
<rect
inkscape:label="#button6-leader"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26458335;stroke-linecap:round"
id="button6-leader"
width="0.26458332"
height="0.26458332"
x="-132.02708"
y="57.943748"
transform="scale(-1,1)" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
transform="translate(0,-0.26458333)">
<g
id="led2-path"
inkscape:label="#led2-path">
<path
style="fill:none;stroke:#888a85;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 0.12402344,71.315542 H 53.647675"
id="path8810"
inkscape:connector-curvature="0" />
<rect
y="70.379326"
x="-54.539886"
height="1.8519195"
width="1.8520833"
id="rect8648-0-7-5"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50287646;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
transform="scale(-1,1)"
y="71.172935"
x="-0.26458332"
height="0.26455995"
width="0.26458332"
id="led2-leader"
style="color:#000000;text-align:end;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26457167;stroke-linecap:round"
inkscape:label="#led2-leader" />
</g>
<g
id="led4-path"
inkscape:label="#led4-path">
<path
style="fill:none;stroke:#888a85;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 0.12862346,84.537282 H 27.648958 l 2.241759,-2.400562 h 18.612019"
id="path8848"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
transform="scale(-1,1)"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50287646;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="rect8648-0-7-5-7"
width="1.8520833"
height="1.8519195"
x="-49.503178"
y="81.204086" />
<rect
inkscape:label="#led4-leader"
style="color:#000000;text-align:end;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26457167;stroke-linecap:round"
id="led4-leader"
width="0.26458332"
height="0.26455995"
x="-0.26458332"
y="84.402107"
transform="scale(-1,1)" />
</g>
<g
id="led6-path"
inkscape:label="#led6-path">
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 0.12277694,97.767959 25.38364906,4e-5 9.304738,-9.437079 7.003154,-4e-5"
id="path8884"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
y="87.419861"
x="-42.752739"
height="1.8519195"
width="1.8520833"
id="rect8648-0-7-5-7-7"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50287646;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
transform="scale(-1,1)"
y="97.631271"
x="-0.26458332"
height="0.26455995"
width="0.26458332"
id="led6-leader"
style="color:#000000;text-align:end;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26457167;stroke-linecap:round"
inkscape:label="#led6-leader" />
</g>
<g
id="led1-path"
inkscape:label="#led1-path"
transform="matrix(-1,0,0,1,132.29166,0)">
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.26420629px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 0.31955082,110.99571 H 27.672239 l 2.158822,2.42958 H 66.14583"
id="path8920"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
transform="scale(-1,1)"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50287646;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="rect8648-0-7-5-7-7-6"
width="1.8520833"
height="1.8519195"
x="-67.071869"
y="112.48402" />
<rect
inkscape:label="#led1-leader"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26457167;stroke-linecap:round"
id="led1-leader"
width="0.26458332"
height="0.26455995"
x="-0.52915835"
y="110.86044"
transform="scale(-1,1)" />
</g>
<g
id="led0-path"
inkscape:label="#led0-path">
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.26413852px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 131.9564,44.847906 H 106.24218 L 100.20967,38.95381 H 70.511457"
id="path9031"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
y="38.02457"
x="-71.448746"
height="1.8519195"
width="1.8520833"
id="rect8648-0-7-5-7-7-6-4"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50287646;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
transform="scale(-1,1)"
y="44.714607"
x="-132.02708"
height="0.26455995"
width="0.26458332"
id="led0-leader"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26457167;stroke-linecap:round"
inkscape:label="#led0-leader" />
</g>
<g
inkscape:label="#led3-path"
id="led3-path"
transform="matrix(-1,0,0,1,131.90105,0)">
<path
inkscape:connector-curvature="0"
id="path9077"
d="M -0.02273763,71.315542 H 53.647675"
style="fill:none;stroke:#888a85;stroke-width:0.26494581px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
transform="scale(-1,1)"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50287646;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="rect9079"
width="1.8520833"
height="1.8519195"
x="-54.539886"
y="70.379326" />
<rect
inkscape:label="#led3-leader"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26457167;stroke-linecap:round"
id="led3-leader"
width="0.26458332"
height="0.26455995"
x="-0.13854833"
y="71.172935"
transform="scale(-1,1)" />
</g>
<g
inkscape:label="#led5-path"
id="led5-path"
transform="matrix(-1,0,0,1,131.90105,0)">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path9085"
d="M -0.08572876,84.537282 H 27.556552 l 2.251693,-2.400562 h 18.694491"
style="fill:none;stroke:#888a85;stroke-width:0.26516888px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
y="81.204086"
x="-49.503178"
height="1.8519195"
width="1.8520833"
id="rect9087"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50287646;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
transform="scale(-1,1)"
y="84.402107"
x="-0.13854833"
height="0.26455995"
width="0.26458332"
id="led5-leader"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26457167;stroke-linecap:round"
inkscape:label="#led5-leader" />
</g>
<g
inkscape:label="#led7-path"
id="led7-path"
transform="matrix(-1,0,0,1,131.90105,0)">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path9093"
d="m 0.02270835,97.767959 25.44457565,4e-5 9.327071,-9.437079 7.019963,-4e-5"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.26490065px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
transform="scale(-1,1)"
style="color:#000000;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.50287646;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="rect9095"
width="1.8520833"
height="1.8519195"
x="-42.752739"
y="87.419861" />
<rect
inkscape:label="#led7-leader"
style="color:#000000;text-align:start;display:inline;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:0.26457167;stroke-linecap:round"
id="led7-leader"
width="0.26458332"
height="0.26455995"
x="-0.13854833"
y="97.631271"
transform="scale(-1,1)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -0,0 +1,497 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="490"
height="490"
viewBox="0 0 129.64583 129.64583"
version="1.1"
id="svg8"
sodipodi:docname="steelseries-senseiraw.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4986862"
inkscape:cx="234.07051"
inkscape:cy="291.38143"
inkscape:document-units="px"
inkscape:current-layer="Device"
inkscape:document-rotation="0"
showgrid="false"
showguides="false"
inkscape:window-width="1920"
inkscape:window-height="1149"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
showborder="true"
inkscape:guide-bbox="true"
units="px"
inkscape:snap-global="false"
inkscape:measure-start="0,0"
inkscape:measure-end="0,0">
<inkscape:grid
type="xygrid"
id="grid78" />
<sodipodi:guide
position="58.722772,112.40709"
orientation="0,1"
id="guide79"
inkscape:locked="false" />
<sodipodi:guide
position="39.611923,100.54373"
orientation="0,1"
id="guide81"
inkscape:locked="false" />
<sodipodi:guide
position="51.756014,88.614617"
orientation="0,1"
id="guide83"
inkscape:locked="false" />
<sodipodi:guide
position="57.778386,76.729167"
orientation="0,1"
id="guide85"
inkscape:locked="false" />
<sodipodi:guide
position="45.047078,64.795712"
orientation="0,1"
inkscape:locked="false"
id="guide87" />
<sodipodi:guide
position="57.646696,52.899321"
orientation="0,1"
id="guide89"
inkscape:locked="false" />
<sodipodi:guide
position="64.732676,142.09384"
orientation="1,0"
id="guide913"
inkscape:locked="false" />
<sodipodi:guide
position="-45.462545,31.805073"
orientation="0,1"
inkscape:locked="false"
id="guide944" />
</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="layer1"
inkscape:label="Image"
style="display:none"
sodipodi:insensitive="true" />
<g
inkscape:groupmode="layer"
id="Device"
inkscape:label="Device"
style="display:inline;opacity:1"
transform="translate(0,-167.35416)">
<path
style="display:inline;fill:#d3d3ce;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 61.118815,173.57187 c -6.81302,0.0662 -11.972394,1.52136 -16.073435,3.37344 -2.88728,1.31189 -4.888218,3.54982 -6.680729,5.62239 -2.215885,3.43407 -3.375386,5.63618 -3.970698,8.86355 -0.919394,7.49385 -0.194949,14.47321 -0.225672,22.62187 -0.0441,2.92144 0.321327,3.9908 0.09533,6.28385 -0.132292,4.05143 -0.413412,7.82174 -0.79375,11.90625 l -0.859895,11.9724 -1.190625,16.27187 c -0.0248,0.94258 -1e-6,1.65364 0.198437,2.57969 0.171855,0.8399 0.352238,1.03153 0.463021,1.52135 0.595312,2.4033 0.529166,3.99631 1.785937,7.2099 2.116666,4.23333 3.604948,6.84609 6.746875,10.71562 3.858506,4.06797 6.928936,5.67685 11.575518,7.34219 4.293136,1.39044 7.919768,1.65835 12.633854,1.78594 0,0 8.340581,-0.39932 12.633718,-1.78976 4.646582,-1.66534 7.717012,-3.27422 11.575518,-7.34219 3.141927,-3.86953 4.630209,-6.48229 6.746875,-10.71562 1.256771,-3.21359 1.190625,-4.8066 1.785937,-7.2099 0.110783,-0.48982 0.291166,-0.68145 0.463021,-1.52135 0.198438,-0.92605 0.223237,-1.63711 0.198437,-2.57969 l -1.190625,-16.27187 -0.717876,-8.98999 c -0.380338,-4.08451 -0.803477,-10.83723 -0.935769,-14.88866 -0.225999,-2.29305 -0.28663,-3.36241 -0.33073,-6.28385 -0.03072,-8.14866 0.835741,-15.12802 -0.08365,-22.62187 -0.595312,-3.22737 -1.470774,-5.42948 -3.686659,-8.86355 -1.792511,-2.07257 -3.793449,-4.3105 -6.680729,-5.62239 -4.101041,-1.85208 -9.260416,-3.30724 -16.073436,-3.37344 -3.028432,-0.0919 -5.294794,0.0499 -7.4082,0.004 z"
id="path926"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccc" />
<path
id="path5205"
style="display:inline;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
d="m 68.894015,173.48626 c 0.01741,1.97468 0.845255,8.1474 1.684943,11.60161 1.369264,5.63272 0.266853,23.47034 0.266853,23.47034 0,0 -2.042025,17.37272 -6.022897,17.27166 -3.980871,0.16348 -6.022896,-17.20924 -6.022896,-17.20924 0,0 -1.10241,-17.83762 0.266854,-23.47034 0.839687,-3.45421 1.667532,-9.62693 1.684942,-11.60161 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cscccscc" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 64.695108,222.94948 c -0.606916,-0.009 -3.440676,-6.06643 -3.513997,-9.56014 -0.02982,-1.42071 0.176696,-1.66675 3.591511,-1.70532 3.503766,-0.0396 3.702586,0.22584 3.694867,1.62781 -0.01799,3.26716 -3.27008,9.64508 -3.772381,9.63765 z"
id="button7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssss"
mask="none"
clip-path="none" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 95.121771,209.18914 c 0,0 -0.197205,17.71196 -1.241986,33.44373 -0.483924,7.28668 -1.491683,12.97597 -2.130552,18.3934 -0.45519,3.85988 0.58105,6.73694 2.373267,7.24471 1.728139,0.48961 3.747262,-3.87217 3.747262,-3.87217"
id="path4742"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csssc" />
<path
sodipodi:nodetypes="csssc"
inkscape:connector-curvature="0"
id="path4744"
d="m 34.385075,208.66134 c 0,0 0.197205,17.71196 1.241986,33.44373 0.483924,7.28668 1.491683,12.97597 2.130552,18.3934 0.45519,3.85988 -0.58105,6.73694 -2.373267,7.24471 -1.728139,0.48961 -3.747262,-3.87217 -3.747262,-3.87217"
style="display:inline;opacity:1;vector-effect:none;fill:#eeeeec;fill-opacity:1;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 33.794256,226.79927 0.594666,-18.07871"
id="path58"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 35.720073,242.99898 c 0,0 -1.305894,-0.86732 -1.520495,-1.08633 -0.214601,-0.21901 -0.406023,-15.1316 -0.406023,-15.1316 l 1.116802,0.0102"
id="path56"
inkscape:connector-curvature="0"
sodipodi:nodetypes="czcc" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 95.452345,226.07744 -0.362533,-16.8175"
id="path54"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 93.827188,242.82115 c 0,0 1.385456,-0.49501 1.574101,-0.65851 0.188645,-0.16351 0.05639,-16.17317 0.05639,-16.17317 l -0.703725,-0.17593"
id="path52"
inkscape:connector-curvature="0"
sodipodi:nodetypes="czcc" />
<path
sodipodi:type="arc"
style="display:inline;opacity:1;fill:none;fill-opacity:0.39215686;stroke:#babdb6;stroke-width:3.62082291;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="led0"
sodipodi:cx="64.822914"
sodipodi:cy="280.68228"
sodipodi:rx="6.5174346"
sodipodi:ry="6.5174413"
d="m 69.431436,276.07375 a 6.5174346,6.5174413 0 0 1 10e-7,9.21706 6.5174346,6.5174413 0 0 1 -9.217045,0 6.5174346,6.5174413 0 0 1 -10e-7,-9.21705"
sodipodi:start="5.4977871"
sodipodi:end="3.9269907"
sodipodi:open="true"
sodipodi:arc-type="arc"
inkscape:label="#led1" />
<path
style="display:inline;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:#babdb6;stroke-width:0.98636663;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
d="m 62.181167,187.21067 -0.0053,13.99772 c 0.798868,1.60491 0.79411,1.60258 2.56045,1.60804 1.9255,-0.003 1.931661,-0.005 2.733645,-1.59929 l -0.0049,-14.01641 c -0.79057,-1.56574 -0.793119,-1.57885 -2.72281,-1.60756 -1.766557,0.0242 -1.762015,0.025 -2.561099,1.6175 z"
id="button2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<rect
id="button0"
width="18.360525"
height="26.481527"
x="37.983463"
y="180.65782"
style="display:inline;fill:none;stroke-width:0.26458332"
inkscape:label="#button0" />
<rect
id="button1"
width="19.0667"
height="26.834612"
x="72.056366"
y="179.95164"
style="display:inline;fill:none;stroke-width:0.26458332"
inkscape:label="#button1" />
<rect
id="button4"
width="3.8839571"
height="16.242002"
x="32.334072"
y="209.96404"
style="display:inline;fill:none;stroke-width:0.26458332"
inkscape:label="#button4" />
<rect
id="button3"
width="4.0605006"
height="15.006199"
x="32.334072"
y="227.2653"
style="display:inline;fill:none;stroke-width:0.26458332"
inkscape:label="#button3" />
<rect
id="button5"
width="3.177783"
height="14.653111"
x="93.594673"
y="210.67021"
style="display:inline;fill:none;stroke-width:0.26458332"
inkscape:label="#button5" />
<rect
id="button6"
width="3.3543267"
height="15.712373"
x="93.594673"
y="226.91222"
style="display:inline;fill:none;stroke-width:0.26458332"
inkscape:label="#button6" />
</g>
<g
inkscape:groupmode="layer"
id="Buttons"
inkscape:label="Buttons"
transform="translate(0,-2.6458338)"
style="display:inline">
<g
transform="matrix(-0.29294531,0,0,0.26458333,70.223399,-40.717196)"
style="display:inline"
id="button0-path"
inkscape:label="#button0-path">
<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 81.663449,250.50576 157.960771,0.0352"
id="path966"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect976"
width="6.999999"
height="6.999999"
x="79.083046"
y="247.00577" />
<rect
y="250.00575"
x="238.62422"
height="1"
width="1"
id="button0-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#button0-leader" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,40.529257,-19.549003)"
style="display:inline"
id="button1-path"
inkscape:label="#button1-path">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 336.91912,170.14213 -183.57514,0.36405"
id="path51"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53"
width="6.999999"
height="6.999999"
x="149"
y="167" />
<rect
y="169.75269"
x="336.91913"
height="1"
width="1"
id="button1-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#button1-leader" />
</g>
<g
transform="matrix(-0.2847136,0,0,0.26458333,87.395313,-3.7692874)"
style="display:inline;opacity:1"
id="button2-path"
inkscape:label="#button2-path">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path964-6"
d="m 79.210952,122.6933 62.691188,68.76371 165.40155,1e-5"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
<rect
inkscape:label="#button2-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
id="button2-leader"
width="1"
height="1"
x="306.40533"
y="190.93378" />
<rect
y="117.94263"
x="76.303352"
height="6.999999"
width="6.999999"
id="rect974-5"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" />
</g>
<g
transform="matrix(0.26458333,0,0,0.26458333,13.523106,5.8698781)"
style="display:inline"
id="button7-path"
inkscape:label="#button7-path">
<path
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="M 438.5178,161.34199 H 204.22621 l -10.41041,7.75114"
id="path51-1-9-6-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect53-2-3-2-2"
width="6.999999"
height="6.999999"
x="189"
y="167" />
<rect
y="160.86166"
x="437.87918"
height="1"
width="1"
id="button7-leader"
style="color:#000000;text-align:start;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#button7-leader" />
</g>
<g
inkscape:label="#button3-path"
id="button3-path"
style="display:inline"
transform="matrix(-0.29294531,0,0,0.26458333,58.364063,-10.132593)">
<path
inkscape:connector-curvature="0"
id="path64"
d="m 81.659897,250.49954 118.042313,0.0476"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.00572562;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="247.00577"
x="79.083046"
height="6.999999"
width="6.999999"
id="rect66"
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="#button3-leader"
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="199.22597"
y="250.00575" />
</g>
<g
transform="matrix(-0.29294531,0,0,0.26458333,58.488898,4.7227855)"
style="display:inline"
id="button4-path"
inkscape:label="#button4-path">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.00572562;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 81.659897,250.49954 118.042313,0.0476"
id="path72"
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="rect74"
width="6.999999"
height="6.999999"
x="79.083046"
y="247.00577" />
<rect
y="250.00575"
x="199.15065"
height="1"
width="1"
id="button4-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#button4-leader" />
</g>
<g
transform="matrix(0.29294531,0,0,0.26458333,71.117413,-9.779506)"
style="display:inline"
id="button5-path"
inkscape:label="#button5-path">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.00572562;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 81.659897,250.49954 118.042313,0.0476"
id="path80"
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="rect82"
width="6.999999"
height="6.999999"
x="79.083046"
y="247.00577" />
<rect
y="250.00575"
x="199.22597"
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="#button5-leader" />
</g>
<g
inkscape:label="#button6-path"
id="button6-path"
style="display:inline"
transform="matrix(0.29294531,0,0,0.26458333,70.992573,5.0758725)">
<path
inkscape:connector-curvature="0"
id="path88"
d="m 81.659897,250.49954 118.042313,0.0476"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1.00572562;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="247.00577"
x="79.083046"
height="6.999999"
width="6.999999"
id="rect90"
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="#button6-leader"
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="199.15065"
y="250.00575" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="LEDs"
inkscape:label="LEDs"
transform="translate(0,-2.6458338)"
style="display:inline">
<g
transform="matrix(-0.29294531,0,0,0.26458333,70.145744,50.590488)"
style="display:inline"
id="led0-path"
inkscape:label="#led0-path">
<path
sodipodi:nodetypes="cc"
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
d="m 19.28901,250.49471 219.01243,0.008"
id="path966-0-2"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;overflow:visible;opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
id="rect976-2-1"
width="6.999999"
height="6.999999"
x="16.834169"
y="246.63705" />
<rect
y="249.97328"
x="237.7849"
height="1"
width="1"
id="led0-leader"
style="color:#000000;text-align:end;overflow:visible;vector-effect:none;fill:#888a85;fill-opacity:1;stroke:none;stroke-linecap:round"
inkscape:label="#rect875" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -129,6 +129,19 @@ org.freedesktop.ratbag1.Device
This is the case if the device has SVGs available but not
for the given theme.
.. js:function:: GetSvgFd(s) → (h)
:param s: the theme name
:returns: An open file descriptor to the SVG for the given theme
Returns an open file descriptor to the SVG for the given theme or an
errno on error. The theme must be one of :js:attr:`Themes`.
The theme **'default'** is guaranteed to be available.
ratbagd may return ENOENT if a file doesn't exist.
This is the case if the device has SVGs available but not
for the given theme.
.. js:function:: Resync()
:type: Signal

View File

@@ -1,8 +1,8 @@
project('libratbag', 'c', 'cpp',
version : '0.9.902',
version : '0.9.904',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.38.0')
meson_version : '>= 0.40.0')
libratbag_version = meson.project_version().split('.')
@@ -38,6 +38,7 @@ add_project_arguments(cppflags, language: 'cpp')
# is generated at the end of this file
config_h = configuration_data()
config_h.set('_GNU_SOURCE', '1')
config_h.set_quoted('RATBAG_VERSION', meson.project_version())
config_h.set_quoted('FALLBACK_SVG_NAME', 'fallback.svg')
libratbag_data_dir = join_paths(get_option('prefix'),
get_option('datadir'),
@@ -45,15 +46,43 @@ libratbag_data_dir = join_paths(get_option('prefix'),
libratbag_data_dir_devel = join_paths(meson.source_root(), 'data')
config_h.set_quoted('LIBRATBAG_DATA_DIR', libratbag_data_dir)
# Coverity breaks because it doesn't define _Float128 correctly, you'll end
# up with a bunch of messages in the form:
# "/usr/include/stdlib.h", line 133: error #20: identifier "_Float128" is
# undefined
# extern _Float128 strtof128 (const char *__restrict __nptr,
# ^
# We don't use float128 ourselves, it gets pulled in from math.h or
# something, so let's just define it as uint128 and move on.
# Unfortunately we can't detect the coverity build at meson configure
# time, we only know it fails at runtime. So make this an option instead, to
# be removed when coverity fixes this again.
if get_option('coverity')
config_h.set('_Float128', '__uint128_t')
config_h.set('_Float32', 'int')
config_h.set('_Float32x', 'int')
config_h.set('_Float64', 'long')
config_h.set('_Float64x', 'long')
endif
# dependencies
pkgconfig = import('pkgconfig')
dep_udev = dependency('libudev')
dep_libevdev = dependency('libevdev')
dep_libsystemd = dependency('libsystemd', version : '>=227')
dep_systemd = dependency('systemd')
dep_glib = dependency('glib-2.0')
dep_lm = cc.find_library('m')
if get_option('logind-provider') == 'elogind'
logind_dep = dependency('libelogind', version : '>=227')
else
logind_dep = dependency('libsystemd', version : '>=227')
endif
enable_systemd = get_option('systemd')
if enable_systemd
dep_systemd = dependency('systemd')
endif
#### libutil.a ####
src_libutil = [
'src/libratbag-util.c',
@@ -131,6 +160,7 @@ src_libratbag = [
'src/driver-hidpp20.c',
'src/driver-hidpp10.c',
'src/driver-logitech-g300.c',
'src/driver-logitech-g600.c',
'src/driver-roccat.c',
'src/driver-gskill.c',
'src/driver-steelseries.c',
@@ -155,15 +185,6 @@ deps_libratbag = [
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('.'),
@@ -241,40 +262,56 @@ man_lur_command = configure_file (
)
#### data files ####
# list ordered by git ls-files data/devices/*.device
data_files = files(
'data/devices/etekcity-scroll-alpha.device',
'data/devices/gskill-MX-780.device',
'data/devices/logitech-g300.device',
'data/devices/logitech-g303.device',
'data/devices/logitech-g403.device',
'data/devices/logitech-g500.device',
'data/devices/logitech-g500s.device',
'data/devices/logitech-g502-Proteus-Core-over-USB.device',
'data/devices/logitech-g502-Proteus-Spectrum-over-USB.device',
'data/devices/logitech-g5-2007.device',
'data/devices/logitech-g5.device',
'data/devices/logitech-g602.device',
'data/devices/logitech-g700.device',
'data/devices/logitech-g700s.device',
'data/devices/logitech-g700s-wireless.device',
'data/devices/logitech-g700-wireless.device',
'data/devices/logitech-g703.device',
'data/devices/logitech-g7.device',
'data/devices/logitech-g900.device',
'data/devices/logitech-g900-wireless.device',
'data/devices/logitech-g9.device',
'data/devices/logitech-g9x-[Call-of-Duty-MW3-Edition].device',
'data/devices/logitech-g9x-[Original].device',
'data/devices/logitech-M325.device',
'data/devices/logitech-M570.device',
'data/devices/logitech-M705.device',
'data/devices/logitech-MX-Master.device',
'data/devices/logitech-MX-Master-2S.device',
'data/devices/logitech-T650.device',
'data/devices/logitech-Wireless-Touchpad.device',
'data/devices/logitech-g-pro-wireless.device',
'data/devices/logitech-g-pro.device',
'data/devices/logitech-g102-g203.device',
'data/devices/logitech-g300.device',
'data/devices/logitech-g302.device',
'data/devices/logitech-g303.device',
'data/devices/logitech-g402.device',
'data/devices/logitech-g403.device',
'data/devices/logitech-g5-2007.device',
'data/devices/logitech-g5.device',
'data/devices/logitech-g500.device',
'data/devices/logitech-g500s.device',
'data/devices/logitech-g502-HERO-over-USB.device',
'data/devices/logitech-g502-Proteus-Core-over-USB.device',
'data/devices/logitech-g502-Proteus-Spectrum-over-USB.device',
'data/devices/logitech-g600.device',
'data/devices/logitech-g602.device',
'data/devices/logitech-g7.device',
'data/devices/logitech-g700-wireless.device',
'data/devices/logitech-g700.device',
'data/devices/logitech-g700s-wireless.device',
'data/devices/logitech-g700s.device',
'data/devices/logitech-g703.device',
'data/devices/logitech-g9.device',
'data/devices/logitech-g900-wireless.device',
'data/devices/logitech-g900.device',
'data/devices/logitech-g903.device',
'data/devices/logitech-g9x-[Call-of-Duty-MW3-Edition].device',
'data/devices/logitech-g9x-[Original].device',
'data/devices/logitech-gpowerplay-wireless.device',
'data/devices/logitech-MX-Anywhere2.device',
'data/devices/logitech-MX-Anywhere2S.device',
'data/devices/roccat-kone-xtd.device',
'data/devices/steelseries-kinzu-v2.device',
'data/devices/steelseries-rival-310.device',
'data/devices/steelseries-rival-600.device',
'data/devices/steelseries-rival.device',
'data/devices/steelseries-sensei-310.device',
'data/devices/steelseries-sensei-raw.device',
)
install_data(data_files,
@@ -286,23 +323,36 @@ test('data-parse-test', data_parse_test,
'--svg-dir', join_paths(meson.source_root(), 'data/gnome') ] + data_files)
#### svg files ####
# list ordered by git ls-files data/gnome/*.svg
gnome_svg_files = files(
'data/gnome/fallback.svg',
'data/gnome/logitech-g102-g203.svg',
'data/gnome/logitech-g300.svg',
'data/gnome/logitech-g303.svg',
'data/gnome/logitech-g402.svg',
'data/gnome/logitech-g403.svg',
'data/gnome/logitech-g500.svg',
'data/gnome/logitech-g500s.svg',
'data/gnome/logitech-g502.svg',
'data/gnome/logitech-g600.svg',
'data/gnome/logitech-g603.svg',
'data/gnome/logitech-g700.svg',
'data/gnome/logitech-g703.svg',
'data/gnome/logitech-g9.svg',
'data/gnome/logitech-g900.svg',
'data/gnome/logitech-g-pro.svg',
'data/gnome/logitech-g-pro-wireless.svg',
'data/gnome/logitech-mx-anywhere2.svg',
'data/gnome/logitech-mx-anywhere2s.svg',
'data/gnome/logitech-mx-master-2s.svg',
'data/gnome/logitech-mx-master.svg',
'data/gnome/roccat-kone-xtd.svg',
'data/gnome/steelseries-kinzu-v2.svg',
'data/gnome/steelseries-rival.svg',
'data/gnome/steelseries-rival310.svg',
'data/gnome/steelseries-rival600.svg',
'data/gnome/steelseries-sensei310.svg',
'data/gnome/steelseries-senseiraw.svg',
)
install_data(gnome_svg_files,
install_dir : join_paths(get_option('datadir'), 'libratbag', 'gnome'))
@@ -311,7 +361,7 @@ test_svg_files = find_program('data/gnome/check-svg.py')
test('check-svg', test_svg_files, args : gnome_svg_files)
#### tests ####
enable_tests = get_option('enable-tests')
enable_tests = get_option('tests')
if enable_tests
dep_check = dependency('check', version: '>= 0.9.10')
@@ -399,7 +449,7 @@ src_ratbagd = [
deps_ratbagd = [
dep_udev,
dep_libsystemd,
logind_dep,
dep_libratbag,
]
@@ -430,35 +480,39 @@ configure_file(input : 'dbus/org.freedesktop.ratbag_devel1.conf.in',
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>
if enable_systemd
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
See https://github.com/libratbag/libratbag/issues/188
'''.format(default_unitdir))
unitdir = default_unitdir
else
unitdir = intended_unitdir
endif
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)
if enable_systemd
configure_file(input : 'ratbagd/ratbagd.service.in',
output : 'ratbagd.service',
configuration : config_bindir,
install_dir : unitdir)
endif
dbusdir = get_option('dbus-root-dir')
if dbusdir == ''
@@ -489,9 +543,15 @@ configure_file(input : 'dbus/org.freedesktop.ratbag1.conf.in',
#### tools ####
python = import('python3')
py3 = python.find_python()
if meson.version().version_compare('<0.48.0')
python = import('python3')
py3 = python.find_python()
else
pymod = import('python')
py3 = pymod.find_installation()
endif
# ratbagctl calls ratbagd over DBus
custom_target('ratbagctl',
output : 'ratbagctl',
input : ['tools/ratbagctl.in', 'tools/ratbagd.py'],
@@ -515,27 +575,25 @@ config_ratbagctl_devel = configuration_data()
config_ratbagctl_devel.set('ratbagd_sha', ratbagd_sha)
config_ratbagctl_devel.set('MESON_BUILD_ROOT', meson.build_root())
config_ratbagctl_devel.set('LIBRATBAG_DATA_DIR', libratbag_data_dir_devel)
configure_file(input : 'tools/toolbox.py',
output : 'toolbox.py',
configuration : config_ratbagctl_devel)
# ratbagctl.devel starts a custom ratbagd and interacts with that over DBus
configure_file(input : 'tools/ratbagctl.devel.in',
output : 'ratbagctl.devel',
configuration : config_ratbagctl_devel)
# ratbagctl.test starts a custom ratbagd and interacts with that over DBus
configure_file(input : 'tools/ratbagctl.test.in',
output : 'ratbagctl.test',
configuration : config_ratbagctl_devel)
env_test = environment()
env_test.set('LIBRATBAG_DATA_DIR', libratbag_data_dir_devel)
ratbagctl_test = find_program(join_paths(meson.build_root(), 'ratbagctl.test'))
test('ratbagctl-test', ratbagctl_test, args: ['-v'], env : env_test)
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)
subdir('doc')
#### swig generated ####
# ratbag-command uses Swig bindings to call libratbag directly
swig = find_program('swig')
swig_gen = generator(
swig,
@@ -566,13 +624,23 @@ shared_library(
install: false,
)
# ratbagc is the layer that maps ratbagctl to the swig bindings
ratbagc_py_conf = configuration_data()
ratbagc_py_conf.set('LIBRATBAG_DATA_DIR', libratbag_data_dir_devel)
ratbagc_py = configure_file(input: 'tools/ratbagc.py.in',
output: 'ratbagc.py',
configuration: ratbagc_py_conf)
custom_target('ratbag-command',
output : 'ratbag-command',
input : ['tools/ratbagctl.in', 'tools/ratbagc.py'],
input : ['tools/ratbagctl.in', ratbagc_py],
build_by_default : true,
command : [py3, join_paths(meson.source_root(), 'tools', 'merge_ratbagd.py'),
'@INPUT@',
'--output', '@OUTPUT@',
'--version', meson.project_version()],
install: false)
#### output files ####
configure_file(output: 'config.h', install: false, configuration: config_h)
subdir('doc')

View File

@@ -1,9 +1,9 @@
option('udev-dir',
type: 'string',
value: '',
description: 'udev base directory [default=$prefix/lib/udev]')
type: 'string',
value: '',
description: 'udev base directory [default=$prefix/lib/udev]')
option('enable-tests',
option('tests',
type: 'boolean',
value: true,
description: 'Build the tests (default=yes)')
@@ -27,3 +27,19 @@ option('dbus-group',
type: 'string',
value : '',
description : 'The UNIX group that is granted access to the ratbagd D-Bus service. By default all users may access it.')
option('systemd',
type : 'boolean',
value : true,
description : 'Build systemd unit files')
option('logind-provider',
type : 'combo',
choices: [ 'elogind', 'systemd'],
value : 'systemd',
description : 'Which logind provider to use')
option('coverity',
type: 'boolean',
value: false,
description: 'Enable coverity build fixes, see meson.build for details [default=false]')

View File

@@ -58,6 +58,8 @@ static int ratbagd_button_get_type(sd_bus *bus,
t = ratbag_button_get_type(button->lib_button);
verify_unsigned_int(t);
return sd_bus_message_append(reply, "u", t);
}
@@ -74,6 +76,8 @@ static int ratbagd_button_get_button(sd_bus *bus,
b = ratbag_button_get_button(button->lib_button);
verify_unsigned_int(b);
return sd_bus_message_append(reply, "u", b);
}
@@ -128,8 +132,14 @@ static int ratbagd_button_get_special(sd_bus *bus,
enum ratbag_button_action_special special;
special = ratbag_button_get_special(button->lib_button);
if (special == RATBAG_BUTTON_ACTION_SPECIAL_INVALID)
special = RATBAG_BUTTON_ACTION_SPECIAL_UNKNOWN;
return sd_bus_message_append(reply, "u", special);
verify_unsigned_int(special);
CHECK_CALL(sd_bus_message_append(reply, "u", special));
return 0;
}
static int ratbagd_button_set_special(sd_bus *bus,
@@ -144,9 +154,7 @@ static int ratbagd_button_set_special(sd_bus *bus,
enum ratbag_button_action_special special;
int r;
r = sd_bus_message_read(m, "u", &special);
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_read(m, "u", &special));
r = ratbag_button_set_special(button->lib_button, special);
@@ -180,12 +188,9 @@ static int ratbagd_button_get_macro(sd_bus *bus,
{
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;
CHECK_CALL(sd_bus_message_open_container(reply, 'a', "(uu)"));
macro = ratbag_button_get_macro(button->lib_button);
if (!macro)
@@ -213,13 +218,16 @@ static int ratbagd_button_get_macro(sd_bus *bus,
abort();
}
r = sd_bus_message_append(reply, "(uu)", type, value);
if (r < 0)
return r;
verify_unsigned_int(type);
verify_unsigned_int(value);
CHECK_CALL(sd_bus_message_append(reply, "(uu)", type, value));
}
out:
return sd_bus_message_close_container(reply);
CHECK_CALL(sd_bus_message_close_container(reply));
return 0;
}
static int ratbagd_button_set_macro(sd_bus *bus,
@@ -235,9 +243,7 @@ static int ratbagd_button_set_macro(sd_bus *bus,
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;
CHECK_CALL(sd_bus_message_enter_container(m, 'a', "(uu)"));
macro = ratbag_button_macro_new("macro");
while ((r = sd_bus_message_read(m, "(uu)", &type, &value)) > 0) {
@@ -251,9 +257,7 @@ static int ratbagd_button_set_macro(sd_bus *bus,
if (r < 0)
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_exit_container(m));
r = ratbag_button_set_macro(button->lib_button, macro);
if (r < 0) {
@@ -295,7 +299,11 @@ static int ratbagd_button_get_action_type(sd_bus *bus,
if (type == RATBAG_BUTTON_ACTION_TYPE_KEY)
type = RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
return sd_bus_message_append(reply, "u", type);
verify_unsigned_int(type);
CHECK_CALL(sd_bus_message_append(reply, "u", type));
return 0;
}
static int ratbagd_button_get_action_types(sd_bus *bus,
@@ -307,7 +315,6 @@ static int ratbagd_button_get_action_types(sd_bus *bus,
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,
@@ -316,20 +323,19 @@ static int ratbagd_button_get_action_types(sd_bus *bus,
enum ratbag_button_action_type *t;
r = sd_bus_message_open_container(reply, 'a', "u");
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_open_container(reply, 'a', "u"));
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;
verify_unsigned_int(*t);
CHECK_CALL(sd_bus_message_append(reply, "u", *t));
}
return sd_bus_message_close_container(reply);
CHECK_CALL(sd_bus_message_close_container(reply));
return 0;
}
static int ratbagd_button_disable(sd_bus_message *m,
@@ -339,9 +345,7 @@ static int ratbagd_button_disable(sd_bus_message *m,
struct ratbagd_button *button = userdata;
int r;
r = sd_bus_message_read(m, "");
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_read(m, ""));
r = ratbag_button_disable(button->lib_button);
if (r < 0) {
@@ -351,7 +355,9 @@ static int ratbagd_button_disable(sd_bus_message *m,
return r;
}
return sd_bus_reply_method_return(m, "u", 0);
CHECK_CALL(sd_bus_reply_method_return(m, "u", 0));
return 0;
}
const sd_bus_vtable ratbagd_button_vtable[] = {
@@ -427,3 +433,15 @@ struct ratbagd_button *ratbagd_button_free(struct ratbagd_button *button)
return mfree(button);
}
int ratbagd_button_resync(sd_bus *bus,
struct ratbagd_button *button)
{
return sd_bus_emit_properties_changed(bus,
button->path,
RATBAGD_NAME_ROOT ".Button",
"ButtonMapping",
"SpecialMapping",
"Macro",
"ActionType",
NULL);
}

View File

@@ -130,7 +130,9 @@ static int ratbagd_device_get_device_name(sd_bus *bus,
name = "";
}
return sd_bus_message_append(reply, "s", name);
CHECK_CALL(sd_bus_message_append(reply, "s", name));
return 0;
}
static int ratbagd_device_get_svg(sd_bus *bus,
@@ -151,7 +153,9 @@ static int ratbagd_device_get_svg(sd_bus *bus,
svg = "";
}
return sd_bus_message_append(reply, "s", svg);
CHECK_CALL(sd_bus_message_append(reply, "s", svg));
return 0;
}
static int ratbagd_device_get_theme_svg(sd_bus_message *m,
@@ -162,17 +166,13 @@ static int ratbagd_device_get_theme_svg(sd_bus_message *m,
char svg_path[PATH_MAX] = {0};
const char *theme;
const char *svg;
int r;
const char *datadir;
datadir = getenv("LIBRATBAG_DATA_DIR");
if (!datadir)
datadir = LIBRATBAG_DATA_DIR;
r = sd_bus_message_read(m, "s", &theme);
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_read(m, "s", &theme));
svg = ratbag_device_get_svg_name(device->lib_device);
if (!svg) {
@@ -183,7 +183,48 @@ static int ratbagd_device_get_theme_svg(sd_bus_message *m,
snprintf(svg_path, sizeof(svg_path), "%s/%s/%s", datadir, theme, svg);
return sd_bus_reply_method_return(m, "s", svg_path);
CHECK_CALL(sd_bus_reply_method_return(m, "s", svg_path));
return 0;
}
static int ratbagd_device_get_theme_svg_fd(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;
const char *datadir;
_cleanup_close_ int fd = -1;
datadir = getenv("LIBRATBAG_DATA_DIR");
if (!datadir)
datadir = LIBRATBAG_DATA_DIR;
CHECK_CALL(sd_bus_message_read(m, "s", &theme));
svg = ratbag_device_get_svg_name(device->lib_device);
if (!svg) {
log_error("%s: failed to fetch SVG, using fallback\n",
ratbagd_device_get_sysname(device));
svg = FALLBACK_SVG_NAME;
}
snprintf(svg_path, sizeof(svg_path), "%s/%s/%s", datadir, theme, svg);
fd = open(svg_path, O_RDONLY);
if (fd == -1) {
log_error("%s: failed to open SVG '%s' (%m)\n",
ratbagd_device_get_sysname(device),
svg_path);
return -ENOENT;
}
CHECK_CALL(sd_bus_reply_method_return(m, "h", fd));
return 0;
}
static int ratbagd_device_get_profiles(sd_bus *bus,
@@ -197,25 +238,32 @@ static int ratbagd_device_get_profiles(sd_bus *bus,
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;
CHECK_CALL(sd_bus_message_open_container(reply, 'a', "o"));
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;
CHECK_CALL(sd_bus_message_append(reply,
"o",
ratbagd_profile_get_path(profile)));
}
return sd_bus_message_close_container(reply);
CHECK_CALL(sd_bus_message_close_container(reply));
return 0;
}
static void ratbagd_device_commit_pending(void *data)
{
struct ratbagd_device *device = data;
int r;
r = ratbag_device_commit(device->lib_device);
if (r < 0)
ratbagd_device_resync(device, device->ctx->bus);
}
static int ratbagd_device_commit(sd_bus_message *m,
@@ -223,17 +271,14 @@ static int ratbagd_device_commit(sd_bus_message *m,
sd_bus_error *error)
{
struct ratbagd_device *device = userdata;
int r;
r = ratbag_device_commit(device->lib_device);
if (r < 0) {
sd_bus *bus = sd_bus_message_get_bus(m);
r = ratbagd_device_resync(device, bus);
if (r < 0)
return r;
}
ratbagd_schedule_task(device->ctx,
ratbagd_device_commit_pending,
device);
return sd_bus_reply_method_return(m, "u", 0);
CHECK_CALL(sd_bus_reply_method_return(m, "u", 0));
return 0;
}
static int
@@ -261,23 +306,20 @@ ratbagd_device_get_capabilities(sd_bus *bus,
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;
CHECK_CALL(sd_bus_message_open_container(reply, 'a', "u"));
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;
CHECK_CALL(sd_bus_message_append(reply, "u", cap));
}
}
return sd_bus_message_close_container(reply);
CHECK_CALL(sd_bus_message_close_container(reply));
return 0;
}
const sd_bus_vtable ratbagd_device_vtable[] = {
@@ -288,6 +330,7 @@ const sd_bus_vtable ratbagd_device_vtable[] = {
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("GetSvgFd", "s", "h", ratbagd_device_get_theme_svg_fd, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Commit", "", "u", ratbagd_device_commit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("Resync", "", 0),
SD_BUS_VTABLE_END,
@@ -324,10 +367,10 @@ int ratbagd_device_new(struct ratbagd_device **out,
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",
sysname,
ratbag_device_get_name(lib_device),
device->n_profiles);
log_info("%s: \"%s\", %d profiles\n",
sysname,
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);
@@ -400,6 +443,10 @@ int ratbagd_device_resync(struct ratbagd_device *device, sd_bus *bus)
{
assert(device);
assert(bus);
ratbagd_for_each_profile_signal(bus, device,
ratbagd_profile_resync);
return sd_bus_emit_signal(bus,
device->path,
RATBAGD_NAME_ROOT ".Device",

View File

@@ -50,20 +50,19 @@ static int ratbagd_led_get_modes(sd_bus *bus,
{
struct ratbagd_led *led = userdata;
enum ratbag_led_mode mode = 0;
int r;
r = sd_bus_message_open_container(reply, 'a', "u");
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_open_container(reply, 'a', "u"));
while (mode <= RATBAG_LED_BREATHING) {
if (ratbag_led_has_mode(led->lib_led, mode))
sd_bus_message_append(reply, "u", mode);
CHECK_CALL(sd_bus_message_append(reply, "u", mode));
mode++;
}
return sd_bus_message_close_container(reply);
CHECK_CALL(sd_bus_message_close_container(reply));
return 0;
}
static int ratbagd_led_get_mode(sd_bus *bus,
@@ -78,7 +77,12 @@ static int ratbagd_led_get_mode(sd_bus *bus,
enum ratbag_led_mode mode;
mode = ratbag_led_get_mode(led->lib_led);
return sd_bus_message_append(reply, "u", mode);
verify_unsigned_int(mode);
CHECK_CALL(sd_bus_message_append(reply, "u", mode));
return 0;
}
static int ratbagd_led_set_mode(sd_bus *bus,
@@ -93,9 +97,7 @@ static int ratbagd_led_set_mode(sd_bus *bus,
enum ratbag_led_mode mode;
int r;
r = sd_bus_message_read(m, "u", &mode);
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_read(m, "u", &mode));
r = ratbag_led_set_mode(led->lib_led, mode);
@@ -124,7 +126,11 @@ static int ratbagd_led_get_type(sd_bus *bus,
type = ratbag_led_get_type(led->lib_led);
return sd_bus_message_append(reply, "u", type);
verify_unsigned_int(type);
CHECK_CALL(sd_bus_message_append(reply, "u", type));
return 0;
}
static int ratbagd_led_get_color(sd_bus *bus,
@@ -139,7 +145,9 @@ static int ratbagd_led_get_color(sd_bus *bus,
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);
CHECK_CALL(sd_bus_message_append(reply, "(uuu)", c.red, c.green, c.blue));
return 0;
}
static int ratbagd_led_set_color(sd_bus *bus,
@@ -154,9 +162,7 @@ static int ratbagd_led_set_color(sd_bus *bus,
struct ratbag_color c;
int r;
r = sd_bus_message_read(m, "(uuu)", &c.red, &c.green, &c.blue);
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_read(m, "(uuu)", &c.red, &c.green, &c.blue));
if (c.red > 255)
c.red = 255;
@@ -191,7 +197,12 @@ static int ratbagd_led_get_effect_duration(sd_bus *bus,
int rate;
rate = ratbag_led_get_effect_duration(led->lib_led);
return sd_bus_message_append(reply, "u", rate);
verify_unsigned_int(rate);
CHECK_CALL(sd_bus_message_append(reply, "u", rate));
return 0;
}
static int ratbagd_led_set_effect_duration(sd_bus *bus,
@@ -206,9 +217,7 @@ static int ratbagd_led_set_effect_duration(sd_bus *bus,
unsigned int rate;
int r;
r = sd_bus_message_read(m, "u", &rate);
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_read(m, "u", &rate));
if (rate > 10000)
rate = 10000;
@@ -239,7 +248,12 @@ static int ratbagd_led_get_brightness(sd_bus *bus,
unsigned int brightness;
brightness = ratbag_led_get_brightness(led->lib_led);
return sd_bus_message_append(reply, "u", brightness);
verify_unsigned_int(brightness);
CHECK_CALL(sd_bus_message_append(reply, "u", brightness));
return 0;
}
static int ratbagd_led_set_brightness(sd_bus *bus,
@@ -254,9 +268,7 @@ static int ratbagd_led_set_brightness(sd_bus *bus,
unsigned int brightness;
int r;
r = sd_bus_message_read(m, "u", &brightness);
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_read(m, "u", &brightness));
if (brightness > 255)
brightness = 255;
@@ -348,3 +360,15 @@ struct ratbagd_led *ratbagd_led_free(struct ratbagd_led *led)
return mfree(led);
}
int ratbagd_led_resync(sd_bus *bus,
struct ratbagd_led *led)
{
return sd_bus_emit_properties_changed(bus,
led->path,
RATBAGD_NAME_ROOT ".Led",
"Mode",
"Color",
"EffectDuration",
"Brightness",
NULL);
}

View File

@@ -102,25 +102,22 @@ static int ratbagd_profile_get_resolutions(sd_bus *bus,
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;
CHECK_CALL(sd_bus_message_open_container(reply, 'a', "o"));
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;
CHECK_CALL(sd_bus_message_append(reply,
"o",
ratbagd_resolution_get_path(resolution)));
}
return sd_bus_message_close_container(reply);
CHECK_CALL(sd_bus_message_close_container(reply));
return 0;
}
static int ratbagd_profile_get_buttons(sd_bus *bus,
@@ -134,25 +131,22 @@ static int ratbagd_profile_get_buttons(sd_bus *bus,
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;
CHECK_CALL(sd_bus_message_open_container(reply, 'a', "o"));
for (i = 0; i < profile->n_buttons; ++i) {
button = profile->buttons[i];
if (!button)
continue;
r = sd_bus_message_append(reply,
CHECK_CALL(sd_bus_message_append(reply,
"o",
ratbagd_button_get_path(button));
if (r < 0)
return r;
ratbagd_button_get_path(button)));
}
return sd_bus_message_close_container(reply);
CHECK_CALL(sd_bus_message_close_container(reply));
return 0;
}
static int ratbagd_profile_get_leds(sd_bus *bus,
@@ -166,25 +160,22 @@ static int ratbagd_profile_get_leds(sd_bus *bus,
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;
CHECK_CALL(sd_bus_message_open_container(reply, 'a', "o"));
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;
CHECK_CALL(sd_bus_message_append(reply,
"o",
ratbagd_led_get_path(led)));
}
return sd_bus_message_close_container(reply);
CHECK_CALL(sd_bus_message_close_container(reply));
return 0;
}
static int ratbagd_profile_is_active(sd_bus *bus,
@@ -198,9 +189,11 @@ static int ratbagd_profile_is_active(sd_bus *bus,
struct ratbagd_profile *profile = userdata;
int is_active;
is_active = !!ratbag_profile_is_active(profile->lib_profile);
is_active = ratbag_profile_is_active(profile->lib_profile);
return sd_bus_message_append(reply, "b", is_active);
CHECK_CALL(sd_bus_message_append(reply, "b", is_active));
return 0;
}
static int ratbagd_profile_find_button(sd_bus *bus,
@@ -286,9 +279,7 @@ static int ratbagd_profile_set_active(sd_bus_message *m,
struct ratbagd_profile *profile = userdata;
int r;
r = sd_bus_message_read(m, "");
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_read(m, ""));
r = ratbag_profile_set_active(profile->lib_profile);
if (r < 0) {
@@ -302,7 +293,9 @@ static int ratbagd_profile_set_active(sd_bus_message *m,
profile->device,
ratbagd_profile_active_signal_cb);
return sd_bus_reply_method_return(m, "u", 0);
CHECK_CALL(sd_bus_reply_method_return(m, "u", 0));
return 0;
}
static int
@@ -318,9 +311,7 @@ ratbagd_profile_set_enabled(sd_bus *bus,
int enabled;
int r;
r = sd_bus_message_read(m, "b", &enabled);
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_read(m, "b", &enabled));
r = ratbag_profile_set_enabled(profile->lib_profile, enabled);
if (r == 0) {
@@ -347,7 +338,9 @@ ratbagd_profile_is_enabled(sd_bus *bus,
struct ratbagd_profile *profile = userdata;
int enabled = ratbag_profile_is_enabled(profile->lib_profile) != 0;
return sd_bus_message_append(reply, "b", enabled);
CHECK_CALL(sd_bus_message_append(reply, "b", enabled));
return 0;
}
static int
@@ -363,9 +356,7 @@ ratbagd_profile_set_name(sd_bus *bus,
char *name;
int r;
r = sd_bus_message_read(m, "s", &name);
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_read(m, "s", &name));
r = ratbag_profile_set_name(profile->lib_profile, name);
@@ -392,7 +383,9 @@ ratbagd_profile_get_name(sd_bus *bus,
struct ratbagd_profile *profile = userdata;
const char *name = ratbag_profile_get_name(profile->lib_profile);
return sd_bus_message_append(reply, "s", name);
CHECK_CALL(sd_bus_message_append(reply, "s", name));
return 0;
}
static int
@@ -410,23 +403,20 @@ ratbagd_profile_get_capabilities(sd_bus *bus,
enum ratbag_profile_capability caps[] = {
RATBAG_PROFILE_CAP_WRITABLE_NAME,
};
int r;
size_t i;
r = sd_bus_message_open_container(reply, 'a', "u");
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_open_container(reply, 'a', "u"));
for (i = 0; i < ELEMENTSOF(caps); i++) {
cap = caps[i];
if (ratbag_profile_has_capability(lib_profile, cap)) {
r = sd_bus_message_append(reply, "u", cap);
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_append(reply, "u", cap));
}
}
return sd_bus_message_close_container(reply);
CHECK_CALL(sd_bus_message_close_container(reply));
return 0;
}
const sd_bus_vtable ratbagd_profile_vtable[] = {
@@ -798,3 +788,47 @@ int ratbagd_for_each_resolution_signal(sd_bus *bus,
return rc;
}
int ratbagd_for_each_button_signal(sd_bus *bus,
struct ratbagd_profile *profile,
int (*func)(sd_bus *bus,
struct ratbagd_button *button))
{
int rc = 0;
for (size_t i = 0; rc == 0 && i < profile->n_buttons; i++)
rc = func(bus, profile->buttons[i]);
return rc;
}
int ratbagd_for_each_led_signal(sd_bus *bus,
struct ratbagd_profile *profile,
int (*func)(sd_bus *bus,
struct ratbagd_led *button))
{
int rc = 0;
for (size_t i = 0; rc == 0 && i < profile->n_leds; i++)
rc = func(bus, profile->leds[i]);
return rc;
}
int ratbagd_profile_resync(sd_bus *bus,
struct ratbagd_profile *profile)
{
ratbagd_for_each_resolution_signal(bus, profile, ratbagd_resolution_resync);
ratbagd_for_each_button_signal(bus, profile, ratbagd_button_resync);
ratbagd_for_each_led_signal(bus, profile, ratbagd_led_resync);
return sd_bus_emit_properties_changed(bus,
profile->path,
RATBAGD_NAME_ROOT ".Profile",
"Resolutions",
"Buttons",
"Leds",
"IsActive",
NULL);
}

View File

@@ -45,6 +45,19 @@ struct ratbagd_resolution {
char *path;
};
int ratbagd_resolution_resync(sd_bus *bus,
struct ratbagd_resolution *resolution)
{
return sd_bus_emit_properties_changed(bus,
resolution->path,
RATBAGD_NAME_ROOT ".Resolution",
"IsDefault",
"Resolution",
"ReportRate",
"IsActive",
NULL);
}
static int ratbagd_resolution_active_signal_cb(sd_bus *bus,
struct ratbagd_resolution *resolution)
{
@@ -167,7 +180,7 @@ ratbagd_resolution_is_active(sd_bus *bus,
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
int is_active;
is_active = !!ratbag_resolution_is_active(lib_resolution);
is_active = ratbag_resolution_is_active(lib_resolution);
return sd_bus_message_append(reply, "b", is_active);
}
@@ -185,7 +198,7 @@ ratbagd_resolution_is_default(sd_bus *bus,
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
int is_default;
is_default = !!ratbag_resolution_is_default(lib_resolution);
is_default = ratbag_resolution_is_default(lib_resolution);
return sd_bus_message_append(reply, "b", is_default);
}
@@ -214,6 +227,7 @@ ratbagd_resolution_get_resolutions(sd_bus *bus,
assert(ndpis <= ARRAY_LENGTH(dpis));
for (unsigned int i = 0; i < ndpis; i++) {
verify_unsigned_int(dpis[i]);
r = sd_bus_message_append(reply, "u", dpis[i]);
if (r < 0)
return r;
@@ -238,6 +252,9 @@ ratbagd_resolution_get_resolution(sd_bus *bus,
xres = ratbag_resolution_get_dpi_x(lib_resolution);
yres = ratbag_resolution_get_dpi_y(lib_resolution);
verify_unsigned_int(xres);
verify_unsigned_int(yres);
return sd_bus_message_append(reply, "(uu)", xres, yres);
}
@@ -293,6 +310,7 @@ ratbagd_resolution_get_report_rate(sd_bus *bus,
int rate;
rate = ratbag_resolution_get_report_rate(lib_resolution);
verify_unsigned_int(rate);
return sd_bus_message_append(reply, "u", rate);
}
@@ -307,7 +325,7 @@ ratbagd_resolution_get_report_rates(sd_bus *bus,
{
struct ratbagd_resolution *resolution = userdata;
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
unsigned int rates[5];
unsigned int rates[8];
unsigned int nrates = ARRAY_LENGTH(rates);
int r;
@@ -320,6 +338,7 @@ ratbagd_resolution_get_report_rates(sd_bus *bus,
assert(nrates <= ARRAY_LENGTH(rates));
for (unsigned int i = 0; i < nrates; i++) {
verify_unsigned_int(rates[i]);
r = sd_bus_message_append(reply, "u", rates[i]);
if (r < 0)
return r;

View File

@@ -42,8 +42,12 @@
#include "ratbagd-test.h"
static bool verbose = false;
static bool verbose_raw = false;
enum log_level {
LL_QUIET = 1,
LL_INFO,
LL_VERBOSE,
LL_RAW,
} log_level = LL_INFO;
static const char *SVG_THEMES[] = {
"default",
@@ -51,13 +55,23 @@ static const char *SVG_THEMES[] = {
NULL
};
void log_info(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (log_level >= LL_INFO)
vprintf(fmt, args);
va_end(args);
}
void log_verbose(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (verbose)
if (log_level >= LL_VERBOSE)
vprintf(fmt, args);
va_end(args);
}
@@ -142,21 +156,18 @@ static int ratbagd_get_devices(sd_bus *bus,
{
struct ratbagd *ctx = userdata;
struct ratbagd_device *device;
int r;
r = sd_bus_message_open_container(reply, 'a', "o");
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_open_container(reply, 'a', "o"));
RATBAGD_DEVICE_FOREACH(device, ctx) {
r = sd_bus_message_append(reply,
"o",
ratbagd_device_get_path(device));
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_append(reply,
"o",
ratbagd_device_get_path(device)));
}
return sd_bus_message_close_container(reply);
CHECK_CALL(sd_bus_message_close_container(reply));
return 0;
}
static int ratbagd_get_themes(sd_bus *bus,
@@ -169,23 +180,18 @@ static int ratbagd_get_themes(sd_bus *bus,
{
struct ratbagd *ctx = userdata;
const char **theme;
int r;
r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_open_container(reply, 'a', "s"));
theme = ctx->themes;
while(*theme) {
r = sd_bus_message_append(reply,
"s",
*theme);
if (r < 0)
return r;
CHECK_CALL(sd_bus_message_append(reply, "s", *theme));
theme++;
}
return sd_bus_message_close_container(reply);
CHECK_CALL(sd_bus_message_close_container(reply));
return 0;
}
static const sd_bus_vtable ratbagd_vtable[] = {
@@ -384,10 +390,10 @@ static int ratbagd_new(struct ratbagd **out)
if (!ctx->lib_ctx)
return -ENOMEM;
if (verbose_raw)
if (log_level >= LL_RAW)
ratbag_log_set_priority(ctx->lib_ctx,
RATBAG_LOG_PRIORITY_RAW);
else if (verbose)
else if (log_level >= LL_VERBOSE)
ratbag_log_set_priority(ctx->lib_ctx,
RATBAG_LOG_PRIORITY_DEBUG);
@@ -483,6 +489,26 @@ exit:
return r;
}
static int on_timeout_cb(sd_event_source *s, uint64_t usec, void *userdata)
{
log_info("Exiting after idle\n");
sd_event_exit(sd_event_source_get_event(s), 0);
return 0;
}
static int before_idle_cb(sd_event_source *s, void *userdata)
{
struct ratbagd *ctx = userdata;
uint64_t usec;
sd_event_now(sd_event_source_get_event(s), CLOCK_MONOTONIC, &usec);
#define min2us(us_) (us_ * 1000000 * 60)
usec += min2us(20);
sd_event_source_set_time(ctx->timeout_source, usec);
return 0;
}
static int sighandler(sd_event_source *source,
const struct signalfd_siginfo *si,
void *userdata)
@@ -496,18 +522,6 @@ 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;
@@ -518,6 +532,24 @@ static int ratbagd_run(struct ratbagd *ctx)
sigprocmask(SIG_BLOCK, &sigset, NULL);
sd_event_add_signal(ctx->event, NULL, SIGINT, sighandler, NULL);
/* exit-on-idle: we set up a timer to simply exit. Since we don't
* store anything, it doesn't matter and we can just restart next
* time someone wants us.
*
* since we don't want to monitor every single dbus call, we just
* set up a post source that gets called before we go idle. That
* resets the timer, so as long as something is happening, ratbagd
* won't exit.
*/
sd_event_add_time(ctx->event,
&ctx->timeout_source,
CLOCK_MONOTONIC,
-1, /* infinite, see before_idle_cb */
min2us(1), /* accuracy doesn't matter */
on_timeout_cb,
ctx);
sd_event_add_post(ctx->event, NULL, before_idle_cb, ctx);
return sd_event_loop(ctx->event);
}
@@ -533,12 +565,17 @@ int main(int argc, char *argv[])
#endif
if (argc > 1) {
if (streq(argv[1], "--verbose=raw")) {
verbose_raw = true;
if (streq(argv[1], "--version")) {
printf("%s\n", RATBAG_VERSION);
return 0;
} else if (streq(argv[1], "--quiet")) {
log_level = LL_QUIET;
} else if (streq(argv[1], "--verbose=raw")) {
log_level = LL_RAW;
} else if (streq(argv[1], "--verbose")) {
verbose = true;
log_level = LL_VERBOSE;
} else {
fprintf(stderr, "Usage: %s [--verbose[=raw]]\n",
fprintf(stderr, "Usage: %s [--version | --quiet | --verbose[=raw]]\n",
program_invocation_short_name);
r = -EINVAL;
goto exit;
@@ -557,10 +594,46 @@ exit:
ratbagd_free(ctx);
if (r < 0) {
errno = -r;
log_error("Failed to start ratbagd: %m\n");
if (r == -EEXIST) {
log_error("Bus name is taken, another instance of ratbagd is already running.\n");
} else {
errno = -r;
log_error("Failed to start ratbagd: %m\n");
}
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
struct ratbagd_callback
{
ratbagd_callback_t callback;
void *userdata;
};
static int ratbagd_callback_handler(sd_event_source *s, void *userdata)
{
struct ratbagd_callback *cb = userdata;
cb->callback(cb->userdata);
sd_event_source_set_enabled(s, SD_EVENT_OFF);
sd_event_source_unref(s);
free(cb);
return 0;
}
void ratbagd_schedule_task(struct ratbagd *ctx,
ratbagd_callback_t callback,
void *userdata)
{
struct ratbagd_callback *cb = zalloc(sizeof *cb);
sd_event_source *source;
cb->callback = callback;
cb->userdata = userdata;
sd_event_add_post(ctx->event, &source, ratbagd_callback_handler, cb);
}

View File

@@ -54,9 +54,20 @@ struct ratbagd_resolution;
struct ratbagd_button;
struct ratbagd_led;
void log_info(const char *fmt, ...) _printf_(1, 2);
void log_verbose(const char *fmt, ...) _printf_(1, 2);
void log_error(const char *fmt, ...) _printf_(1, 2);
#define CHECK_CALL(_call) \
do { \
int _r = _call; \
if (_r < 0) { \
log_error("%s: '%s' failed with: %s\n", __func__, #_call, strerror(-_r)); \
return _r; \
} \
} while (0)
/*
* Profiles
*/
@@ -89,6 +100,15 @@ int ratbagd_for_each_resolution_signal(sd_bus *bus,
struct ratbagd_profile *profile,
int (*func)(sd_bus *bus,
struct ratbagd_resolution *resolution));
int ratbagd_for_each_button_signal(sd_bus *bus,
struct ratbagd_profile *profile,
int (*func)(sd_bus *bus,
struct ratbagd_button *button));
int ratbagd_for_each_led_signal(sd_bus *bus,
struct ratbagd_profile *profile,
int (*func)(sd_bus *bus,
struct ratbagd_led *led));
int ratbagd_profile_resync(sd_bus *bus, struct ratbagd_profile *profile);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_profile *, ratbagd_profile_free);
@@ -104,6 +124,7 @@ int ratbagd_resolution_new(struct ratbagd_resolution **out,
unsigned int index);
struct ratbagd_resolution *ratbagd_resolution_free(struct ratbagd_resolution *resolution);
const char *ratbagd_resolution_get_path(struct ratbagd_resolution *resolution);
int ratbagd_resolution_resync(sd_bus *bus, struct ratbagd_resolution *resolution);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_resolution *, ratbagd_resolution_free);
@@ -119,6 +140,7 @@ int ratbagd_button_new(struct ratbagd_button **out,
unsigned int index);
struct ratbagd_button *ratbagd_button_free(struct ratbagd_button *button);
const char *ratbagd_button_get_path(struct ratbagd_button *button);
int ratbagd_button_resync(sd_bus *bus, struct ratbagd_button *button);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_button *, ratbagd_button_free);
@@ -134,6 +156,7 @@ int ratbagd_led_new(struct ratbagd_led **out,
unsigned int index);
struct ratbagd_led *ratbagd_led_free(struct ratbagd_led *led);
const char *ratbagd_led_get_path(struct ratbagd_led *led);
int ratbagd_led_resync(sd_bus *bus, struct ratbagd_led *led);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_led *, ratbagd_led_free);
@@ -176,6 +199,19 @@ struct ratbagd_device *ratbagd_device_next(struct ratbagd_device *device);
(_device); \
_device = (_safe), \
_safe = (_safe) ? ratbagd_device_next(_safe) : NULL)
/* Verify that _val is not -1. This traps DBus API errors where we end up
* sending a valid-looking index across and then fail on the other side.
*
* do {} while(0) so we can terminate with a ; without the compiler
* complaining about an empty statement;
* */
#define verify_unsigned_int(_val) \
do { if ((int)_val == -1) { \
log_error("%s:%d - %s: expected unsigned int, got -1\n", __FILE__, __LINE__, __func__); \
return -EINVAL; \
} } while(0)
/*
* Context
*/
@@ -184,6 +220,7 @@ struct ratbagd {
sd_event *event;
struct ratbag *lib_ctx;
struct udev_monitor *monitor;
sd_event_source *timeout_source;
sd_event_source *monitor_source;
sd_bus *bus;
@@ -192,3 +229,9 @@ struct ratbagd {
const char **themes; /* NULL-terminated */
};
typedef void (*ratbagd_callback_t)(void *userdata);
void ratbagd_schedule_task(struct ratbagd *ctx,
ratbagd_callback_t callback,
void *userdata);

View File

@@ -987,7 +987,7 @@ gskill_read_resolutions(struct ratbag_profile *profile,
hz = GSKILL_MAX_POLLING_RATE / (report->polling_rate + 1);
for (i = 0; i < report->dpi_num; i++) {
_cleanup_resolution_ struct ratbag_resolution *resolution;
_cleanup_resolution_ struct ratbag_resolution *resolution = NULL;
unsigned int rates[] = { 500, 1000 }; /* let's assume that is true */
dpi_x = report->dpi_levels[i].x * GSKILL_DPI_UNIT;
@@ -1108,7 +1108,7 @@ gskill_update_resolutions(struct ratbag_profile *profile)
* lost on exit
*/
for (i = 0; i < GSKILL_NUM_DPI; i++) {
_cleanup_resolution_ struct ratbag_resolution *res;
_cleanup_resolution_ struct ratbag_resolution *res = NULL;
struct gskill_raw_dpi_level *level =
&report->dpi_levels[report->dpi_num];

View File

@@ -407,8 +407,14 @@ hidpp10drv_read_profile(struct ratbag_profile *profile)
if (rc)
return;
ratbag_profile_set_cap(profile, RATBAG_PROFILE_CAP_WRITABLE_NAME);
profile->is_enabled = p.enabled;
if (profile->name)
free(profile->name);
profile->name = strdup_safe((char*)p.name);
rc = hidpp10_get_current_profile(hidpp10, &idx);
if (rc == 0 && (unsigned int)idx == profile->index)
profile->is_active = true;
@@ -543,6 +549,7 @@ hidpp10drv_commit(struct ratbag_device *device)
return rc;
p.enabled = profile->is_enabled;
strncpy_safe((char*)p.name, profile->name, 24);
ratbag_profile_for_each_resolution(profile, resolution) {
p.dpi_modes[resolution->index].xres = resolution->dpi_x;
@@ -605,7 +612,7 @@ hidpp10drv_probe(struct ratbag_device *device)
goto err;
drv_data = zalloc(sizeof(*drv_data));
hidpp_device_init(&base, device->hidraw.fd);
hidpp_device_init(&base, device->hidraw[0].fd);
hidpp_device_set_log_handler(&base, hidpp10_log, HIDPP_LOG_PRIORITY_RAW, device);
typestr = ratbag_device_data_hidpp10_get_profile_type(device->data);
@@ -689,6 +696,18 @@ hidpp10drv_probe(struct ratbag_device *device)
ratbag_device_for_each_profile(device, profile)
hidpp10drv_read_profile(profile);
if (device->num_profiles == 1) {
_cleanup_profile_ struct ratbag_profile *profile;
profile = ratbag_device_get_profile(device, 0);
if (!profile->is_active) {
log_debug(device->ratbag,
"%s: forcing profile 0 to active.\n",
device->name);
profile->is_active = true;
}
}
return 0;
err:
free(drv_data);

View File

@@ -90,7 +90,7 @@ hidpp20drv_read_button_1b04(struct ratbag_button *button)
control = &drv_data->controls[button->index];
mapping = control->control_id;
if (control->reporting.divert || control->reporting.persist)
if (control->reporting.remapped)
mapping = control->reporting.remapped;
log_raw(device->ratbag,
" - button%d: %s (%02x) %s%s:%d\n",
@@ -197,6 +197,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;
unsigned int modifiers = 0;
int rc;
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100))
@@ -215,6 +216,7 @@ hidpp20drv_read_button_8100(struct ratbag_button *button)
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
button->action.action.key.key = ratbag_hidraw_get_keycode_from_keyboard_usage(device,
profile->buttons[button->index].keyboard_keys.key);
modifiers = profile->buttons[button->index].keyboard_keys.modifier_flags;
break;
case HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
@@ -237,6 +239,20 @@ hidpp20drv_read_button_8100(struct ratbag_button *button)
break;
}
if (button->action.type == RATBAG_BUTTON_ACTION_TYPE_KEY) {
int rc;
rc = ratbag_button_macro_new_from_keycode(button,
button->action.action.key.key,
modifiers);
if (rc < 0) {
log_error(device->ratbag,
"Error while reading button %d\n",
button->index);
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
}
}
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);
@@ -421,9 +437,6 @@ hidpp20drv_update_button_1b04(struct ratbag_button *button)
control->reporting.updated = 1;
rc = hidpp20_special_key_mouse_set_control(drv_data->dev, control);
if (rc == HIDPP20_ERR_INVALID_ARGUMENT)
return -EINVAL;
if (rc)
log_error(device->ratbag,
"Error while writing profile: '%s' (%d)\n",
@@ -440,6 +453,8 @@ hidpp20drv_update_button_8100(struct ratbag_button *button)
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_profile *profile;
struct ratbag_button_action *action = &button->action;
unsigned int modifiers, key;
int rc;
uint8_t code, type, subtype;
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100))
@@ -453,6 +468,34 @@ hidpp20drv_update_button_8100(struct ratbag_button *button)
profile->buttons[button->index].button.subtype = HIDPP20_BUTTON_HID_TYPE_MOUSE;
profile->buttons[button->index].button.buttons = action->action.button;
break;
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
type = HIDPP20_BUTTON_HID_TYPE;
subtype = HIDPP20_BUTTON_HID_TYPE_KEYBOARD;
rc = ratbag_action_keycode_from_macro(action,
&key,
&modifiers);
if (rc < 0) {
log_error(device->ratbag,
"Error while writing macro for button %d\n",
button->index);
}
code = ratbag_hidraw_get_keyboard_usage_from_keycode(device, key);
if (code == 0) {
subtype = HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL;
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, action->action.key.key);
if (code == 0)
return -EINVAL;
}
profile->buttons[button->index].subany.type = type;
profile->buttons[button->index].subany.subtype = subtype;
if (subtype == HIDPP20_BUTTON_HID_TYPE_KEYBOARD) {
profile->buttons[button->index].keyboard_keys.key = code;
profile->buttons[button->index].keyboard_keys.modifier_flags = modifiers;
} else {
profile->buttons[button->index].consumer_control.consumer_control = code;
}
break;
case RATBAG_BUTTON_ACTION_TYPE_KEY:
type = HIDPP20_BUTTON_HID_TYPE;
subtype = HIDPP20_BUTTON_HID_TYPE_KEYBOARD;
@@ -477,7 +520,6 @@ hidpp20drv_update_button_8100(struct ratbag_button *button)
profile->buttons[button->index].special.type = HIDPP20_BUTTON_SPECIAL;
profile->buttons[button->index].special.special = code;
break;
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
default:
return -ENOTSUP;
}
@@ -546,6 +588,20 @@ hidpp20drv_update_led_1300(struct ratbag_led *led, struct hidpp20drv_data *data)
if (rc)
return rc;
/*
* We revert to HW control of the LEDs.
* This effectively disable the capability of changing the DPI/profile
* LEDs, but this allows to make them work with the internal DPI/profile
* switches.
*
* The net result is:
* - DPI/profiles can't be controlled (but who wants this?)
* - The logo can properly controlled
*/
rc = hidpp20_led_sw_control_set_sw_ctrl(data->dev, false);
if (rc)
return rc;
return RATBAG_SUCCESS;
}
@@ -716,6 +772,9 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
struct ratbag_device *device = profile->device;
struct ratbag *ratbag = device->ratbag;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag_resolution *res;
unsigned int default_dpi = 1000;
unsigned int default_rate = 500;
int rc;
if (drv_data->capabilities & HIDPP_CAP_RESOLUTION_2200) {
@@ -734,8 +793,6 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
}
if (drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201) {
struct ratbag_resolution *res;
rc = hidpp20drv_read_resolution_dpi_2201(device);
if (rc < 0)
return rc;
@@ -756,13 +813,12 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
* they are from different sensors */
res->is_active = true;
}
return 0;
} else {
ratbag_profile_for_each_resolution(profile, res)
ratbag_resolution_set_dpi_list(res, &default_dpi, 1);
}
if (drv_data->capabilities & HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060) {
struct ratbag_resolution *res;
rc = hidpp20drv_read_report_rate_8060(device);
if (rc < 0)
return rc;
@@ -771,6 +827,9 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
ratbag_resolution_set_report_rate_list(res,
drv_data->report_rates,
drv_data->num_report_rates);
} else {
ratbag_profile_for_each_resolution(profile, res)
ratbag_resolution_set_report_rate_list(res, &default_rate, 1);
}
return 0;
@@ -1049,7 +1108,7 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
/* we read the profile once to get the correct number of
* supported buttons. */
if (!hidpp20drv_read_special_key_mouse(device))
device->num_buttons = drv_data->num_controls;
drv_data->num_buttons = drv_data->num_controls;
break;
}
case HIDPP_PAGE_BATTERY_LEVEL_STATUS: {
@@ -1074,7 +1133,7 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
/* we read the profile once to get the correct number of
* supported buttons. */
if (!hidpp20drv_read_kbd_reprogrammable_key(device))
device->num_buttons = drv_data->num_controls;
drv_data->num_buttons = drv_data->num_controls;
break;
}
case HIDPP_PAGE_ADJUSTABLE_REPORT_RATE: {
@@ -1120,6 +1179,9 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
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);
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);
rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles);
if (rc < 0)
@@ -1163,8 +1225,6 @@ hidpp20drv_commit(struct ratbag_device *device)
if (!profile->dirty)
continue;
drv_data->profiles->profiles[profile->index].enabled = profile->is_enabled;
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->is_active)
dpi_index = resolution->index;
@@ -1197,6 +1257,9 @@ hidpp20drv_commit(struct ratbag_device *device)
}
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
list_for_each(profile, &device->profiles, link)
drv_data->profiles->profiles[profile->index].enabled = profile->is_enabled;
rc = hidpp20_onboard_profiles_commit(drv_data->dev,
drv_data->profiles);
if (rc)
@@ -1306,7 +1369,7 @@ hidpp20drv_probe(struct ratbag_device *device)
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
hidpp_device_init(&base, device->hidraw.fd);
hidpp_device_init(&base, device->hidraw[0].fd);
hidpp_device_set_log_handler(&base, hidpp20_log, HIDPP_LOG_PRIORITY_RAW, device);
device_idx = ratbag_device_data_hidpp20_get_index(device->data);
@@ -1335,7 +1398,7 @@ hidpp20drv_probe(struct ratbag_device *device)
drv_data->num_profiles = 1;
drv_data->num_resolutions = 1;
drv_data->num_buttons = 8;
drv_data->num_leds = 2;
drv_data->num_leds = 0;
rc = hidpp20drv_20_probe(device);
if (rc)

671
src/driver-logitech-g600.c Normal file
View File

@@ -0,0 +1,671 @@
/*
* Copyright © 2018 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_G600_NUM_PROFILES 3
#define LOGITECH_G600_NUM_BUTTONS 20
#define LOGITECH_G600_NUM_DPI 4
#define LOGITECH_G600_NUM_LED 1
#define LOGITECH_G600_DPI_MIN 200
#define LOGITECH_G600_DPI_MAX 8200
#define LOGITECH_G600_REPORT_ID_GET_ACTIVE 0xF0
#define LOGITECH_G600_REPORT_ID_SET_ACTIVE 0xF0
#define LOGITECH_G600_REPORT_ID_PROFILE_0 0xF3
#define LOGITECH_G600_REPORT_ID_PROFILE_1 0xF4
#define LOGITECH_G600_REPORT_ID_PROFILE_2 0xF5
#define LOGITECH_G600_REPORT_SIZE_PROFILE 154
#define LOGITECH_G600_LED_SOLID 0x00
#define LOGITECH_G600_LED_BREATHE 0x01
#define LOGITECH_G600_LED_CYCLE 0x02
struct logitech_g600_button {
uint8_t code;
uint8_t modifier;
uint8_t key;
} __attribute__((packed));
struct logitech_g600_profile_report {
uint8_t id;
uint8_t led_red;
uint8_t led_green;
uint8_t led_blue;
uint8_t led_effect;
uint8_t led_duration;
uint8_t unknown1[5];
uint8_t frequency; /* frequency = 1000 / (value + 1) */
uint8_t dpi_shift; /* value is a linear range between 200->0x04, 8200->0xa4, so value * 50, 0x00 is disabled */
uint8_t dpi_default; /* between 1 and 4*/
uint8_t dpi[4]; /* value is a linear range between 200->0x04, 8200->0xa4, so value * 50, 0x00 is disabled */
uint8_t unknown2[13];
struct logitech_g600_button buttons[20];
uint8_t g_shift_color[3]; /* can't assign it in LGS, but the 3rd profile has one that shows the feature :) */
struct logitech_g600_button g_shift_buttons[20];
} __attribute__((packed));
struct logitech_g600_active_profile_report {
uint8_t id;
uint8_t unknown1 :1;
uint8_t resolution :2;
uint8_t unknown2 :1;
uint8_t profile :4;
uint8_t unknown3;
uint8_t unknown4;
} __attribute__((packed));
struct logitech_g600_profile_data {
struct logitech_g600_profile_report report;
};
struct logitech_g600_data {
struct logitech_g600_profile_data profile_data[LOGITECH_G600_NUM_PROFILES];
};
_Static_assert(sizeof(struct logitech_g600_profile_report) == LOGITECH_G600_REPORT_SIZE_PROFILE,
"Size of logitech_g600_profile_report is wrong");
struct logitech_g600_button_type_mapping {
uint8_t raw;
enum ratbag_button_type type;
};
static const struct logitech_g600_button_type_mapping logitech_g600_button_type_mapping[] = {
{ 0, RATBAG_BUTTON_TYPE_LEFT },
{ 1, RATBAG_BUTTON_TYPE_RIGHT },
{ 2, RATBAG_BUTTON_TYPE_MIDDLE },
{ 3, RATBAG_BUTTON_TYPE_WHEEL_LEFT },
{ 4, RATBAG_BUTTON_TYPE_WHEEL_RIGHT },
{ 5, RATBAG_BUTTON_TYPE_PINKIE },
{ 6, RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP },
{ 7, RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP },
{ 8, RATBAG_BUTTON_TYPE_SIDE }, // 8 = G9 ... 19 = G20
{ 9, RATBAG_BUTTON_TYPE_SIDE },
{ 10, RATBAG_BUTTON_TYPE_SIDE },
{ 11, RATBAG_BUTTON_TYPE_SIDE },
{ 12, RATBAG_BUTTON_TYPE_SIDE },
{ 13, RATBAG_BUTTON_TYPE_SIDE },
{ 14, RATBAG_BUTTON_TYPE_SIDE },
{ 15, RATBAG_BUTTON_TYPE_SIDE },
{ 16, RATBAG_BUTTON_TYPE_SIDE },
{ 17, RATBAG_BUTTON_TYPE_SIDE },
{ 18, RATBAG_BUTTON_TYPE_SIDE },
{ 19, RATBAG_BUTTON_TYPE_SIDE },
};
static enum ratbag_button_type
logitech_g600_raw_to_button_type(uint8_t data)
{
const struct logitech_g600_button_type_mapping *mapping;
ARRAY_FOR_EACH(logitech_g600_button_type_mapping, mapping) {
if (mapping->raw == data)
return mapping->type;
}
return RATBAG_BUTTON_TYPE_UNKNOWN;
}
struct logitech_g600_button_mapping {
uint8_t raw;
struct ratbag_button_action action;
};
static struct logitech_g600_button_mapping logitech_g600_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) },
{ 0x11, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP) },
{ 0x12, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN) },
{ 0x13, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP) },
{ 0x14, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP) },
{ 0x15, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE) },
{ 0x17, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE) },
};
static const struct ratbag_button_action*
logitech_g600_raw_to_button_action(uint8_t data)
{
struct logitech_g600_button_mapping *mapping;
ARRAY_FOR_EACH(logitech_g600_button_mapping, mapping) {
if (mapping->raw == data)
return &mapping->action;
}
return NULL;
}
static uint8_t logitech_g600_modifier_to_raw(int modifier_flags)
{
uint8_t modifiers = 0x00;
if (modifier_flags & MODIFIER_LEFTCTRL)
modifiers |= 0x01;
if (modifier_flags & MODIFIER_LEFTSHIFT)
modifiers |= 0x02;
if (modifier_flags & MODIFIER_LEFTALT)
modifiers |= 0x04;
if (modifier_flags & MODIFIER_LEFTMETA)
modifiers |= 0x08;
if (modifier_flags & MODIFIER_RIGHTCTRL)
modifiers |= 0x10;
if (modifier_flags & MODIFIER_RIGHTSHIFT)
modifiers |= 0x20;
if (modifier_flags & MODIFIER_RIGHTALT)
modifiers |= 0x40;
if (modifier_flags & MODIFIER_RIGHTMETA)
modifiers |= 0x80;
return modifiers;
}
static int logitech_g600_raw_to_modifiers(uint8_t data)
{
int modifiers = 0;
if (data & 0x01)
modifiers |= MODIFIER_LEFTCTRL;
if (data & 0x02)
modifiers |= MODIFIER_LEFTSHIFT;
if (data & 0x04)
modifiers |= MODIFIER_LEFTALT;
if (data & 0x08)
modifiers |= MODIFIER_LEFTMETA;
if (data & 0x10)
modifiers |= MODIFIER_RIGHTCTRL;
if (data & 0x20)
modifiers |= MODIFIER_RIGHTSHIFT;
if (data & 0x40)
modifiers |= MODIFIER_RIGHTALT;
if (data & 0x80)
modifiers |= MODIFIER_RIGHTMETA;
return modifiers;
}
static uint8_t
logitech_g600_button_action_to_raw(const struct ratbag_button_action *action)
{
struct logitech_g600_button_mapping *mapping;
ARRAY_FOR_EACH(logitech_g600_button_mapping, mapping) {
if (ratbag_button_action_match(&mapping->action, action))
return mapping->raw;
}
return 0;
}
static int
logitech_g600_get_active_profile_and_resolution(struct ratbag_device *device)
{
struct ratbag_profile *profile;
struct logitech_g600_active_profile_report buf;
int ret;
ret = ratbag_hidraw_raw_request(device, LOGITECH_G600_REPORT_ID_GET_ACTIVE,
(uint8_t*)&buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret < 0)
return ret;
if (ret != sizeof(buf))
return -EIO;
list_for_each(profile, &device->profiles, link) {
struct ratbag_resolution *resolution;
if (profile->index != buf.profile)
continue;
profile->is_active = true;
ratbag_profile_for_each_resolution(profile, resolution) {
resolution->is_active = resolution->index == buf.resolution;
}
}
return 0;
}
static int
logitech_g600_set_current_resolution(struct ratbag_device *device, unsigned int index)
{
uint8_t buf[] = {LOGITECH_G600_REPORT_ID_SET_ACTIVE, 0x40 | (index << 1), 0x00, 0x00};
int ret;
log_debug(device->ratbag, "Setting active resolution to %d\n", index);
if (index >= LOGITECH_G600_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;
}
static int
logitech_g600_set_active_profile(struct ratbag_device *device, unsigned int index)
{
struct ratbag_profile *profile;
uint8_t buf[] = {LOGITECH_G600_REPORT_ID_SET_ACTIVE, 0x80 | (index << 4), 0x00, 0x00};
int ret, active_resolution = 0;
if (index >= LOGITECH_G600_NUM_PROFILES)
return -EINVAL;
ret = ratbag_hidraw_raw_request(device, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret != sizeof(buf))
return ret;
/* Update the active resolution. After profile change the default is used. */
list_for_each(profile, &device->profiles, link) {
struct ratbag_resolution *resolution;
if (profile->index != index)
continue;
ratbag_profile_for_each_resolution(profile, resolution) {
resolution->is_active = resolution->is_default;
if (resolution->is_active)
active_resolution = resolution->index;
}
}
ret = logitech_g600_set_current_resolution(device, active_resolution);
if (ret < 0)
return ret;
return 0;
}
static void
logitech_g600_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_g600_data *drv_data = device->drv_data;
struct logitech_g600_profile_data *pdata;
struct logitech_g600_profile_report *profile_report;
struct logitech_g600_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);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
button->type = logitech_g600_raw_to_button_type(button->index);
action = logitech_g600_raw_to_button_action(button_report->code);
if (action) {
ratbag_button_set_action(button, action);
}
else if (button_report->code == 0x00 && (button_report->modifier > 0x00 || button_report->key > 0x00))
{
unsigned int key, modifiers;
int rc;
key = ratbag_hidraw_get_keycode_from_keyboard_usage(device,
button_report->key);
modifiers = logitech_g600_raw_to_modifiers(button_report->modifier);
rc = ratbag_button_macro_new_from_keycode(button, key, modifiers);
if (rc < 0) {
log_error(device->ratbag,
"Error while reading button %d\n",
button->index);
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
}
}
}
static void
logitech_g600_read_led(struct ratbag_led *led)
{
struct ratbag_profile *profile = led->profile;
struct ratbag_device *device = profile->device;
struct logitech_g600_data *drv_data = device->drv_data;
struct logitech_g600_profile_data *pdata;
struct logitech_g600_profile_report *report;
pdata = &drv_data->profile_data[profile->index];
report = &pdata->report;
led->type = RATBAG_LED_TYPE_SIDE;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
switch (report->led_effect) {
case LOGITECH_G600_LED_SOLID:
led->mode = RATBAG_LED_ON;
break;
case LOGITECH_G600_LED_BREATHE:
led->mode = RATBAG_LED_BREATHING;
led->ms = report->led_duration * 1000;
break;
case LOGITECH_G600_LED_CYCLE:
led->mode = RATBAG_LED_CYCLE;
led->ms = report->led_duration * 1000;
break;
}
led->color.red = report->led_red;
led->color.green = report->led_green;
led->color.blue = report->led_blue;
}
static void
logitech_g600_read_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct logitech_g600_data *drv_data = device->drv_data;
struct logitech_g600_profile_data *pdata;
struct logitech_g600_profile_report *report;
struct ratbag_resolution *resolution;
unsigned int report_rates[] = { 125, 142, 166, 200, 250, 333, 500, 1000 };
uint8_t report_id;
int rc;
struct ratbag_button *button;
struct ratbag_led *led;
assert(profile->index < LOGITECH_G600_NUM_PROFILES);
pdata = &drv_data->profile_data[profile->index];
report = &pdata->report;
switch (profile->index) {
case 0: report_id = LOGITECH_G600_REPORT_ID_PROFILE_0; break;
case 1: report_id = LOGITECH_G600_REPORT_ID_PROFILE_1; break;
case 2: report_id = LOGITECH_G600_REPORT_ID_PROFILE_2; break;
}
rc = ratbag_hidraw_raw_request(device, report_id,
(uint8_t*)report,
sizeof(*report),
HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (rc < (int)sizeof(*report)) {
log_error(device->ratbag,
"Error while requesting profile: %d\n", rc);
return;
}
ratbag_profile_for_each_resolution(profile, resolution) {
resolution->dpi_x = report->dpi[resolution->index] * 50;
resolution->dpi_y = report->dpi[resolution->index] * 50;
resolution->hz = 1000 / (report->frequency + 1);
resolution->is_default = report->dpi_default - 1U == resolution->index;
resolution->is_active = resolution->is_default;
ratbag_resolution_set_dpi_list_from_range(resolution,
LOGITECH_G600_DPI_MIN,
LOGITECH_G600_DPI_MAX);
ratbag_resolution_set_report_rate_list(resolution, report_rates,
ARRAY_LENGTH(report_rates));
}
ratbag_profile_for_each_button(profile, button)
logitech_g600_read_button(button);
ratbag_profile_for_each_led(profile, led)
logitech_g600_read_led(led);
log_debug(device->ratbag, "Unknown data in profile %d\n", profile->index);
log_buf_debug(device->ratbag, " profile->unknown1: ",
report->unknown1, 5);
log_buf_debug(device->ratbag, " profile->unknown2: ",
report->unknown2, 13);
}
static int
logitech_g600_test_hidraw(struct ratbag_device *device)
{
return ratbag_hidraw_has_report(device, LOGITECH_G600_REPORT_ID_GET_ACTIVE);
}
static int
logitech_g600_probe(struct ratbag_device *device)
{
int rc;
struct logitech_g600_data *drv_data = NULL;
struct ratbag_profile *profile;
rc = ratbag_find_hidraw(device, logitech_g600_test_hidraw);
if (rc)
goto err;
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
ratbag_device_init_profiles(device,
LOGITECH_G600_NUM_PROFILES,
LOGITECH_G600_NUM_DPI,
LOGITECH_G600_NUM_BUTTONS,
LOGITECH_G600_NUM_LED);
ratbag_device_for_each_profile(device, profile)
logitech_g600_read_profile(profile);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_RESOLUTION);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_PROFILE);
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);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
rc = logitech_g600_get_active_profile_and_resolution(device);
if (rc < 0) {
log_error(device->ratbag,
"Can't talk to the mouse: '%s' (%d)\n",
strerror(-rc),
rc);
rc = -ENODEV;
goto err;
}
return 0;
err:
free(drv_data);
ratbag_set_drv_data(device, NULL);
return rc;
}
static int
logitech_g600_write_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct logitech_g600_data *drv_data = device->drv_data;
struct logitech_g600_profile_data *pdata;
struct logitech_g600_profile_report *report;
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct ratbag_led *led;
uint8_t *buf;
int rc, active_resolution = 0;
pdata = &drv_data->profile_data[profile->index];
report = &pdata->report;
ratbag_profile_for_each_resolution(profile, resolution) {
report->dpi[resolution->index] = resolution->dpi_x / 50;
if (resolution->is_default)
report->dpi_default = resolution->index + 1;
if (profile->is_active && resolution->is_active)
active_resolution = resolution->index;
/* The same hz is used for all resolutions */
if (resolution->index == 0)
report->frequency = (1000 / resolution->hz) - 1;
}
list_for_each(button, &profile->buttons, link) {
struct ratbag_button_action *action = &button->action;
struct logitech_g600_button *raw_button;
raw_button = &report->buttons[button->index];
raw_button->code = logitech_g600_button_action_to_raw(action);
raw_button->modifier = 0x00;
raw_button->key = 0x00;
if (action->type == RATBAG_BUTTON_ACTION_TYPE_MACRO) {
unsigned int key, modifiers;
rc = ratbag_action_keycode_from_macro(action,
&key,
&modifiers);
if (rc < 0) {
log_error(device->ratbag,
"Error while writing macro for button %d\n",
button->index);
}
raw_button->key = ratbag_hidraw_get_keyboard_usage_from_keycode(
device, key);
raw_button->modifier = logitech_g600_modifier_to_raw(modifiers);
}
}
list_for_each(led, &profile->leds, link) {
report->led_red = led->color.red;
report->led_green = led->color.green;
report->led_blue = led->color.blue;
switch (led->mode) {
case RATBAG_LED_ON:
report->led_effect = LOGITECH_G600_LED_SOLID;
break;
case RATBAG_LED_OFF:
report->led_effect = LOGITECH_G600_LED_SOLID;
report->led_red = 0x00;
report->led_green = 0x00;
report->led_blue = 0x00;
break;
case RATBAG_LED_BREATHING:
report->led_effect = LOGITECH_G600_LED_BREATHE;
report->led_duration = led->ms / 1000;
break;
case RATBAG_LED_CYCLE:
report->led_effect = LOGITECH_G600_LED_CYCLE;
report->led_duration = led->ms / 1000;
break;
}
if (report->led_duration > 0x0f)
report->led_duration = 0x0f;
}
buf = (uint8_t*)report;
rc = ratbag_hidraw_raw_request(device, report->id,
buf,
sizeof(*report),
HID_FEATURE_REPORT,
HID_REQ_SET_REPORT);
if (rc < (int)sizeof(*report)) {
log_error(device->ratbag,
"Error while writing profile: %d\n", rc);
return rc;
}
if (profile->is_active) {
rc = logitech_g600_set_current_resolution(device, active_resolution);
if (rc < 0)
return rc;
}
return 0;
}
static int
logitech_g600_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_g600_write_profile(profile);
if (rc)
return rc;
}
return 0;
}
static void
logitech_g600_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
free(ratbag_get_drv_data(device));
}
struct ratbag_driver logitech_g600_driver = {
.name = "Logitech G600",
.id = "logitech_g600",
.probe = logitech_g600_probe,
.remove = logitech_g600_remove,
.commit = logitech_g600_commit,
.set_active_profile = logitech_g600_set_active_profile,
};

View File

@@ -748,6 +748,7 @@ roccat_read_profile(struct ratbag_profile *profile)
ratbag_resolution_set_report_rate_list(resolution, report_rates,
ARRAY_LENGTH(report_rates));
ratbag_resolution_set_dpi_list_from_range(resolution, 200, 8200);
}
ratbag_profile_for_each_button(profile, button)

View File

@@ -37,6 +37,8 @@
#define STEELSERIES_NUM_PROFILES 1
#define STEELSERIES_NUM_DPI 2
#define STEELSERIES_INPUT_ENDPOINT 0
#define STEELSERIES_INPUT_HIDRAW 1
/* not sure these two are used for */
#define STEELSERIES_REPORT_ID_1 0x01
@@ -48,6 +50,7 @@
#define STEELSERIES_ID_DPI_SHORT 0x03
#define STEELSERIES_ID_REPORT_RATE_SHORT 0x04
#define STEELSERIES_ID_LED_INTENSITY_SHORT 0x05
#define STEELSERIES_ID_LED_EFFECT_SHORT 0x07
#define STEELSERIES_ID_LED_COLOR_SHORT 0x08
#define STEELSERIES_ID_SAVE_SHORT 0x09
@@ -57,14 +60,58 @@
#define STEELSERIES_ID_REPORT_RATE 0x54
#define STEELSERIES_ID_LED 0x5b
#define STEELSERIES_ID_SAVE 0x59
#define STEELSERIES_ID_FIRMWARE 0x90
#define STEELSERIES_ID_SETTTINGS 0x92
#define STEELSERIES_ID_DPI_PROTOCOL3 0x03
#define STEELSERIES_ID_REPORT_RATE_PROTOCOL3 0x04
#define STEELSERIES_ID_LED_PROTOCOL3 0x05
#define STEELSERIES_ID_SAVE_PROTOCOL3 0x09
#define STEELSERIES_ID_FIRMWARE_PROTOCOL3 0x10
#define STEELSERIES_ID_SETTTINGS_PROTOCOL3 0x16
#define STEELSERIES_BUTTON_OFF 0x00
#define STEELSERIES_BUTTON_RES_CYCLE 0x30
#define STEELSERIES_BUTTON_WHEEL_UP 0x31
#define STEELSERIES_BUTTON_WHEEL_DOWN 0x32
#define STEELSERIES_BUTTON_KEY 0x10
#define STEELSERIES_BUTTON_KBD 0x51
#define STEELSERIES_BUTTON_CONSUMER 0x61
struct steelseries_data {
int firmware_major;
int firmware_minor;
};
struct steelseries_point {
struct list link;
struct ratbag_color color; /* point color */
uint8_t pos; /* relative position in the cycle */
};
struct steelseries_led_cycle {
uint8_t led_id; /* led id */
uint16_t duration; /* cycle duration */
bool repeat; /* if the cycle restarts automatically */
uint8_t trigger_buttons; /* trigger button combination */
struct list points; /* colors in the cycle */
};
struct steelseries_led_cycle_spec {
int hid_report_type; /* either HID_OUTPUT_REPORT or HID_FEATURE_REPORT */
int header_len; /* number of bytes in the header */
uint8_t cmd_val; /* command value for the color command */
bool has_2_led_ids; /* some mice have 2 fields for led id */
int led_id_idx; /* index of the led id field */
int led_id2_idx; /* 2nd led id index (if required by protocol) */
int duration_idx; /* index of the cycle duration field */
int repeat_idx; /* index of the repeat field */
int trigger_idx; /* index of the trigger mask field */
int point_count_idx; /* index of the point counter field */
};
static int
steelseries_test_hidraw(struct ratbag_device *device)
{
@@ -98,6 +145,10 @@ button_defaults_for_layout(struct ratbag_button *button, int button_count)
button_actions[5].action.special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP;
button_actions[6].type = RATBAG_BUTTON_ACTION_TYPE_NONE;
button_actions[7].type = RATBAG_BUTTON_ACTION_TYPE_NONE;
} else if (button_count == 7) {
button_actions[6].type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL;
button_actions[6].action.special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP;
button_actions[7].type = RATBAG_BUTTON_ACTION_TYPE_NONE;
} else {
button_actions[7].type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL;
button_actions[7].action.special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP;
@@ -110,6 +161,9 @@ button_defaults_for_layout(struct ratbag_button *button, int button_count)
button_types[4] = RATBAG_BUTTON_TYPE_THUMB2;
if (button_count <= 6) {
button_types[5] = RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP;
} else if (button_count == 7) {
button_types[5] = RATBAG_BUTTON_TYPE_THUMB3;
button_types[6] = RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP;
} else {
button_types[5] = RATBAG_BUTTON_TYPE_PINKIE;
button_types[6] = RATBAG_BUTTON_TYPE_PINKIE2;
@@ -120,14 +174,103 @@ button_defaults_for_layout(struct ratbag_button *button, int button_count)
ratbag_button_set_action(button, &button_actions[button->index]);
}
static int
steelseries_get_firmware_version(struct ratbag_device *device)
{
struct steelseries_data *drv_data = device->drv_data;
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
size_t buf_len = STEELSERIES_REPORT_SIZE;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
int ret;
if (device_version == 2)
buf[0] = STEELSERIES_ID_FIRMWARE;
else if (device_version == 3)
buf[0] = STEELSERIES_ID_FIRMWARE_PROTOCOL3;
else
return -ENOTSUP;
msleep(10);
ret = ratbag_hidraw_output_report(device, buf, buf_len);
if (ret < 0)
return ret;
ret = ratbag_hidraw_read_input_report_index(device, buf, buf_len, STEELSERIES_INPUT_HIDRAW);
if (ret < 0)
return ret;
drv_data->firmware_major = buf[1];
drv_data->firmware_minor = buf[0];
return 0;
}
static int
steelseries_read_settings(struct ratbag_device *device)
{
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
struct ratbag_profile *profile = NULL;
struct ratbag_resolution *resolution;
struct ratbag_led *led;
size_t buf_len = STEELSERIES_REPORT_SIZE;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
int ret;
unsigned int active_resolution;
if (device_version == 2)
buf[0] = STEELSERIES_ID_SETTTINGS;
else if (device_version == 3)
buf[0] = STEELSERIES_ID_SETTTINGS_PROTOCOL3;
else
return -ENOTSUP;
msleep(10);
ret = ratbag_hidraw_output_report(device, buf, buf_len);
if (ret < 0)
return ret;
ret = ratbag_hidraw_read_input_report_index(device, buf, buf_len, STEELSERIES_INPUT_HIDRAW);
if (ret < 0)
return ret;
if (device_version == 2) {
active_resolution = buf[1] - 1;
ratbag_device_for_each_profile(device, profile) {
ratbag_profile_for_each_resolution(profile, resolution) {
resolution->is_active = resolution->index == active_resolution;
resolution->dpi_x = 100 * (1 + buf[2 + resolution->index*2]);
resolution->dpi_y = resolution->dpi_x;
}
ratbag_profile_for_each_led(profile, led) {
led->color.red = buf[6 + led->index * 3];
led->color.green = buf[7 + led->index * 3];
led->color.blue = buf[8 + led->index * 3];
}
}
} else if (device_version == 3) {
active_resolution = buf[0] - 1;
ratbag_device_for_each_profile(device, profile) {
ratbag_profile_for_each_resolution(profile, resolution) {
resolution->is_active = resolution->index == active_resolution;
}
}
}
return 0;
}
static int
steelseries_probe(struct ratbag_device *device)
{
struct steelseries_data *drv_data = NULL;
struct ratbag_profile *profile = NULL;
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct ratbag_led *led;
int rc, button_count, led_count, device_version;
int rc, button_count, led_count, device_version, mono_led, short_button;
_cleanup_(dpi_list_freep) struct dpi_list *dpilist = NULL;
_cleanup_(freep) struct dpi_range *dpirange = NULL;
@@ -137,11 +280,19 @@ steelseries_probe(struct ratbag_device *device)
if (rc)
return rc;
rc = ratbag_open_hidraw_index(device,
STEELSERIES_INPUT_ENDPOINT,
STEELSERIES_INPUT_HIDRAW);
if (rc)
return rc;
device_version = ratbag_device_data_steelseries_get_device_version(device->data);
button_count = ratbag_device_data_steelseries_get_button_count(device->data);
led_count = ratbag_device_data_steelseries_get_led_count(device->data);
dpirange = ratbag_device_data_steelseries_get_dpi_range(device->data);
dpilist = ratbag_device_data_steelseries_get_dpi_list(device->data);
mono_led = ratbag_device_data_steelseries_get_mono_led(device->data);
short_button = ratbag_device_data_steelseries_get_short_button(device->data);
ratbag_device_init_profiles(device,
STEELSERIES_NUM_PROFILES,
@@ -154,7 +305,9 @@ steelseries_probe(struct ratbag_device *device)
/* set these caps manually as they are not assumed with only 1 profile */
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_KEY);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
if (short_button == 0) {
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_MACROS);
}
}
ratbag_device_unset_capability(device, RATBAG_DEVICE_CAP_QUERY_CONFIGURATION);
@@ -193,7 +346,9 @@ steelseries_probe(struct ratbag_device *device)
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);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
if (short_button == 0) {
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
}
button_defaults_for_layout(button, button_count);
}
@@ -201,10 +356,15 @@ steelseries_probe(struct ratbag_device *device)
ratbag_profile_for_each_led(profile, led) {
led->type = led->index == 0 ? RATBAG_LED_TYPE_LOGO : RATBAG_LED_TYPE_WHEEL;
led->mode = RATBAG_LED_ON;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
led->color.red = 0;
led->color.green = 0;
led->color.blue = 255;
if (mono_led) {
led->colordepth = RATBAG_LED_COLORDEPTH_MONOCHROME;
led->brightness = 255;
} else {
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
led->color.red = 0;
led->color.green = 0;
led->color.blue = 255;
}
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
@@ -213,6 +373,16 @@ steelseries_probe(struct ratbag_device *device)
}
}
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
steelseries_get_firmware_version(device);
log_debug(device->ratbag, "SteelSeries firmware version %d.%d\n",
drv_data->firmware_major,
drv_data->firmware_minor);
steelseries_read_settings(device);
return 0;
}
@@ -248,12 +418,20 @@ steelseries_write_dpi(struct ratbag_resolution *resolution)
buf[0] = STEELSERIES_ID_DPI_SHORT;
buf[1] = resolution->index + 1;
buf[2] = i;
} else {
} else if (device_version == 2) {
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_DPI;
buf[2] = resolution->index + 1;
buf[3] = resolution->dpi_x / dpirange->step - 1;
buf[6] = 0x42; /* not sure if needed */
} else if (device_version == 3) {
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_DPI_PROTOCOL3;
buf[2] = resolution->index + 1;
buf[3] = resolution->dpi_x / dpirange->step - 1;
buf[5] = 0x42; /* not sure if needed */
} else {
return -ENOTSUP;
}
msleep(10);
@@ -277,10 +455,16 @@ steelseries_write_report_rate(struct ratbag_resolution *resolution)
buf_len = STEELSERIES_REPORT_SIZE_SHORT;
buf[0] = STEELSERIES_ID_REPORT_RATE_SHORT;
buf[2] = 1000 / resolution->hz;
} else {
} else if (device_version == 2) {
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_REPORT_RATE;
buf[2] = 1000 / resolution->hz;
} else if (device_version == 3) {
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_REPORT_RATE_PROTOCOL3;
buf[2] = 1000 / resolution->hz;
} else {
return -ENOTSUP;
}
msleep(10);
@@ -295,13 +479,21 @@ static int
steelseries_write_buttons(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
uint8_t buf[STEELSERIES_REPORT_LONG_SIZE] = {0};
struct ratbag_button *button;
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
int ret;
if (ratbag_device_data_steelseries_get_macro_length(device->data) == 0)
return 0;
int short_button = ratbag_device_data_steelseries_get_short_button(device->data);
int button_size = (short_button == 0) ? 5 : 3;
int report_size = (short_button == 0) ? STEELSERIES_REPORT_LONG_SIZE : STEELSERIES_REPORT_SIZE_SHORT;
int max_modifiers = (short_button == 0) ? 3 : 0;
uint8_t buf[report_size];
memset(buf, 0, report_size);
buf[0] = STEELSERIES_ID_BUTTONS;
ratbag_profile_for_each_button(profile, button) {
@@ -310,8 +502,8 @@ steelseries_write_buttons(struct ratbag_profile *profile)
unsigned int key, modifiers;
int idx;
/* Each button takes up 5 bytes starting from index 2 */
idx = 2 + button->index * 5;
/* Each button takes up 3 or 5 bytes starting from index 2 */
idx = 2 + button->index * button_size;
switch (action->type) {
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
@@ -322,34 +514,38 @@ steelseries_write_buttons(struct ratbag_profile *profile)
ratbag_action_keycode_from_macro(action, &key, &modifiers);
/* There is only space for 3 modifiers */
if (__builtin_popcount(modifiers > 3)) {
log_debug(device->ratbag,
"Too many modifiers in macro for button %d\n",
button->index);
if (__builtin_popcount(modifiers) > max_modifiers) {
log_error(device->ratbag,
"Too many modifiers in macro for button %d (maximum %d)\n",
button->index, max_modifiers);
break;
}
code = ratbag_hidraw_get_keyboard_usage_from_keycode(
device, key);
if (code) {
buf[idx] = STEELSERIES_BUTTON_KBD;
if (short_button) {
buf[idx] = STEELSERIES_BUTTON_KEY;
} else {
buf[idx] = STEELSERIES_BUTTON_KBD;
if (modifiers & MODIFIER_LEFTCTRL)
buf[++idx] = 0xE0;
if (modifiers & MODIFIER_LEFTSHIFT)
buf[++idx] = 0xE1;
if (modifiers & MODIFIER_LEFTALT)
buf[++idx] = 0xE2;
if (modifiers & MODIFIER_LEFTMETA)
buf[++idx] = 0xE3;
if (modifiers & MODIFIER_RIGHTCTRL)
buf[++idx] = 0xE4;
if (modifiers & MODIFIER_RIGHTSHIFT)
buf[++idx] = 0xE5;
if (modifiers & MODIFIER_RIGHTALT)
buf[++idx] = 0xE6;
if (modifiers & MODIFIER_RIGHTMETA)
buf[++idx] = 0xE7;
if (modifiers & MODIFIER_LEFTCTRL)
buf[++idx] = 0xE0;
if (modifiers & MODIFIER_LEFTSHIFT)
buf[++idx] = 0xE1;
if (modifiers & MODIFIER_LEFTALT)
buf[++idx] = 0xE2;
if (modifiers & MODIFIER_LEFTMETA)
buf[++idx] = 0xE3;
if (modifiers & MODIFIER_RIGHTCTRL)
buf[++idx] = 0xE4;
if (modifiers & MODIFIER_RIGHTSHIFT)
buf[++idx] = 0xE5;
if (modifiers & MODIFIER_RIGHTALT)
buf[++idx] = 0xE6;
if (modifiers & MODIFIER_RIGHTMETA)
buf[++idx] = 0xE7;
}
buf[idx + 1] = code;
} else {
@@ -385,7 +581,12 @@ steelseries_write_buttons(struct ratbag_profile *profile)
}
msleep(10);
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
if (device_version == 3)
ret = ratbag_hidraw_raw_request(device, STEELSERIES_ID_BUTTONS,
buf,sizeof(buf),HID_FEATURE_REPORT,HID_REQ_SET_REPORT);
else
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -421,11 +622,24 @@ steelseries_write_led_v1(struct ratbag_led *led)
if (ret < 0)
return ret;
buf[0] = STEELSERIES_ID_LED_COLOR_SHORT;
buf[1] = led->index + 1;
buf[2] = led->color.red;
buf[3] = led->color.green;
buf[4] = led->color.blue;
int mono_led = ratbag_device_data_steelseries_get_mono_led(device->data);
if (mono_led) {
buf[0] = STEELSERIES_ID_LED_INTENSITY_SHORT;
buf[1] = led->index + 1;
if (led->mode == RATBAG_LED_OFF || led->brightness == 0) {
buf[2] = 1;
} else {
//split the brightness into roughly 3 equal intensities
buf[2] = (led->brightness / 86) + 2;
}
} else {
buf[0] = STEELSERIES_ID_LED_COLOR_SHORT;
buf[1] = led->index + 1;
buf[2] = led->color.red;
buf[3] = led->color.green;
buf[4] = led->color.blue;
}
msleep(10);
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
@@ -435,112 +649,194 @@ steelseries_write_led_v1(struct ratbag_led *led)
return 0;
}
static void
create_cycle(struct steelseries_led_cycle *cycle)
{
cycle->led_id = 0x00;
cycle->duration = 5000;
cycle->repeat = true;
cycle->trigger_buttons = 0x00;
list_init(&cycle->points);
}
static void
construct_cycle_buffer(struct steelseries_led_cycle *cycle,
struct steelseries_led_cycle_spec *spec,
uint8_t *buf,
uint8_t buf_size)
{
struct steelseries_point *point;
uint16_t duration;
uint8_t npoints = 0;
uint16_t cycle_size = 0;
buf[0] = spec->cmd_val;
buf[spec->led_id_idx] = cycle->led_id;
if (spec->has_2_led_ids)
buf[spec->led_id2_idx] = cycle->led_id;
if (!cycle->repeat)
buf[spec->repeat_idx] = 0x01;
buf[spec->trigger_idx] = cycle->trigger_buttons;
int color_idx = spec->header_len;
list_for_each(point, &cycle->points, link) {
if(npoints == 0){
/* this is not a mistake,
we need to write the first
point as the first data after the header */
buf[color_idx++] = point->color.red;
buf[color_idx++] = point->color.green;
buf[color_idx++] = point->color.blue;
}
cycle_size += point->pos;
assert(cycle_size < 256);
assert(34 + npoints*4 <= buf_size);
buf[(color_idx ) + npoints*4] = point->color.red;
buf[(color_idx+1) + npoints*4] = point->color.green;
buf[(color_idx+2) + npoints*4] = point->color.blue;
buf[(color_idx+3) + npoints*4] = point->pos;
npoints++;
}
buf[spec->point_count_idx] = npoints;
/* this seems to be the minimum allowed */
duration = max(buf[spec->point_count_idx] * 330, cycle->duration);
hidpp_set_unaligned_le_u16(&buf[spec->duration_idx], duration);
}
static int
steelseries_write_led_v2(struct ratbag_led *led)
steelseries_write_led_cycle(struct ratbag_led *led,
struct steelseries_led_cycle_spec *cycle_spec)
{
struct ratbag_device *device = led->profile->device;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
uint16_t duration, min_duration;
int ret;
buf[0] = STEELSERIES_ID_LED;
buf[2] = led->index;
struct steelseries_led_cycle cycle;
struct steelseries_point point[4];
/* not sure if these two are needed */
buf[15] = 0x01;
buf[17] = 0x01;
const struct ratbag_color black = { 0x00 };
const struct ratbag_color red = { 0xFF, 0x00, 0x00 };
const struct ratbag_color green = { 0x00, 0xFF, 0x00 };
const struct ratbag_color blue = { 0x00, 0x00, 0xFF };
create_cycle(&cycle);
cycle.led_id = led->index;
switch(led->mode) {
case RATBAG_LED_OFF:
buf[19] = 0x01;
buf[27] = 0x01;
buf[28] = buf[31] = 0x00;
buf[29] = buf[32] = 0x00;
buf[30] = buf[33] = 0x00;
/* not sure why the duration is set for the steady color or why
it is different for the two LEDs */
duration = led->index == 0 ? 10000 : 5000; /* 0x1027 or 0x8813 */
cycle.repeat = false;
point[0].color = black;
point[0].pos = 0x00;
list_append(&cycle.points, &point[0].link);
break;
case RATBAG_LED_ON:
buf[19] = 0x01;
buf[27] = 0x01;
buf[28] = buf[31] = led->color.red;
buf[29] = buf[32] = led->color.green;
buf[30] = buf[33] = led->color.blue;
cycle.repeat = false;
/* not sure why the duration is set for the steady color or why
it is different for the two LEDs */
duration = led->index == 0 ? 10000 : 5000; /* 0x1027 or 0x8813 */
point[0].color = led->color;
point[0].pos = 0x00;
list_append(&cycle.points, &point[0].link);
break;
case RATBAG_LED_CYCLE:
buf[27] = 0x04; /* number of steps in cycle */
case RATBAG_LED_CYCLE:
point[0].color = red;
point[0].pos = 0x00;
/* start color */
buf[28] = buf[31] = 0xFF;
buf[29] = buf[32] = 0x00;
buf[30] = buf[33] = 0x00;
point[1].color = green;
point[1].pos = 0x55;
/* Cycle to green */
buf[35] = 0x00;
buf[36] = 0xFF;
buf[37] = 0x00;
buf[38] = 0x54; /* normalized time share of animation */
point[2].color = blue;
point[2].pos = 0x55;
/* Cycle to blue */
buf[39] = 0x00;
buf[40] = 0x00;
buf[41] = 0xFF;
buf[42] = 0x54; /* normalized time share of animation */
point[3].color = red;
point[3].pos = 0x55;
/* Cycle to red */
buf[43] = 0xFF;
buf[44] = 0x00;
buf[45] = 0x00;
buf[46] = 0x56; /* normalized time share of animation */
list_append(&cycle.points, &point[0].link);
list_append(&cycle.points, &point[1].link);
list_append(&cycle.points, &point[2].link);
list_append(&cycle.points, &point[3].link);
duration = led->ms;
cycle.duration = led->ms;
break;
case RATBAG_LED_BREATHING:
buf[27] = 0x03; /* number of steps in cycle */
point[0].color = black;
point[0].pos = 0x00;
/* start color */
buf[28] = buf[31] = led->color.red;
buf[29] = buf[32] = led->color.green;
buf[30] = buf[33] = led->color.blue;
point[1].color = led->color;
point[1].pos = 0x7F;
/* Cycle to black */
buf[35] = 0x00;
buf[36] = 0x00;
buf[37] = 0x00;
buf[38] = 0x7F; /* normalized time share of animation */
point[2].color = black;
point[2].pos = 0x7F;
list_append(&cycle.points, &point[0].link);
list_append(&cycle.points, &point[1].link);
list_append(&cycle.points, &point[2].link);
/* Cycle to selected color */
buf[39] = led->color.red;
buf[40] = led->color.green;
buf[41] = led->color.blue;
buf[42] = 0x7F; /* normalized time share of animation */
duration = led->ms;
cycle.duration = led->ms;
break;
default:
return -EINVAL;
}
/* this seems to be the minimum allowed */
min_duration = buf[27] * 330;
if (duration < min_duration)
duration = min_duration;
hidpp_set_unaligned_le_u16(&buf[3], duration);
construct_cycle_buffer(&cycle, cycle_spec, buf, sizeof(buf));
msleep(10);
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
ret = ratbag_hidraw_raw_request(device, cycle_spec->cmd_val, buf,
sizeof(buf), cycle_spec->hid_report_type, HID_REQ_SET_REPORT);
if (ret < 0)
return ret;
return 0;
}
static int
steelseries_write_led_v2(struct ratbag_led *led)
{
struct steelseries_led_cycle_spec spec;
spec.hid_report_type = HID_OUTPUT_REPORT;
spec.header_len = 28;
spec.cmd_val = STEELSERIES_ID_LED;
spec.has_2_led_ids = false;
spec.led_id_idx = 2;
spec.duration_idx = 3;
spec.repeat_idx = 19;
spec.trigger_idx = 23;
spec.point_count_idx = 27;
return steelseries_write_led_cycle(led, &spec);
}
static int
steelseries_write_led_v3(struct ratbag_led *led)
{
struct steelseries_led_cycle_spec spec;
spec.hid_report_type = HID_FEATURE_REPORT;
spec.header_len = 30;
spec.cmd_val = STEELSERIES_ID_LED_PROTOCOL3;
spec.has_2_led_ids = true;
spec.led_id_idx = 2;
spec.led_id2_idx = 7;
spec.duration_idx = 8;
spec.repeat_idx = 24;
spec.trigger_idx = 25;
spec.point_count_idx = 29;
return steelseries_write_led_cycle(led, &spec);
}
static int
steelseries_write_led(struct ratbag_led *led)
{
@@ -551,6 +847,8 @@ steelseries_write_led(struct ratbag_led *led)
return steelseries_write_led_v1(led);
else if (device_version == 2)
return steelseries_write_led_v2(led);
else if (device_version == 3)
return steelseries_write_led_v3(led);
return -ENOTSUP;
}
@@ -566,9 +864,14 @@ steelseries_write_save(struct ratbag_device *device)
if (device_version == 1) {
buf_len = STEELSERIES_REPORT_SIZE_SHORT;
buf[0] = STEELSERIES_ID_SAVE_SHORT;
} else {
} else if (device_version == 2) {
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_SAVE;
} else if (device_version == 3) {
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_SAVE_PROTOCOL3;
} else {
return -ENOTSUP;
}
msleep(20);
@@ -658,7 +961,9 @@ steelseries_commit(struct ratbag_device *device)
static void
steelseries_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
ratbag_close_hidraw_index(device, 0);
ratbag_close_hidraw_index(device, 1);
free(ratbag_get_drv_data(device));
}
struct ratbag_driver steelseries_driver = {

View File

@@ -190,7 +190,7 @@ test_read_profile(struct ratbag_profile *profile)
r0 = &p0->resolutions[0];
for (i = 0; i < d->num_resolutions; i++) {
_cleanup_resolution_ struct ratbag_resolution *res;
_cleanup_resolution_ struct ratbag_resolution *res = NULL;
size_t nrates = 0;
r = &p->resolutions[i];
@@ -225,7 +225,7 @@ test_read_profile(struct ratbag_profile *profile)
/* special case triggered by the test suite when num_resolutions is 0 */
if (d->num_resolutions) {
_cleanup_resolution_ struct ratbag_resolution *res;
_cleanup_resolution_ struct ratbag_resolution *res = NULL;
res = ratbag_profile_get_resolution(profile, 0);
assert(res);

View File

@@ -149,6 +149,7 @@ static const struct hidpp20_1b04_physical_mapping hidpp20_1b04_physical_mapping[
BUTTON_PHYS_MAPPING(98, "Do Nothing" , RATBAG_BUTTON_TYPE_UNKNOWN),
BUTTON_PHYS_MAPPING(156, "Gesture Button" , RATBAG_BUTTON_TYPE_UNKNOWN),
BUTTON_PHYS_MAPPING(157, "SmartShift" , RATBAG_BUTTON_TYPE_WHEEL_RATCHET_MODE_SHIFT),
BUTTON_PHYS_MAPPING(169, "Gesture Button" , RATBAG_BUTTON_TYPE_MIDDLE),
BUTTON_PHYS_MAPPING(221, "LedToggle" , RATBAG_BUTTON_TYPE_UNKNOWN),
};

View File

@@ -55,9 +55,12 @@ hidpp20_feature_get_name(uint16_t feature)
CASE_RETURN_STRING(HIDPP_PAGE_ROOT);
CASE_RETURN_STRING(HIDPP_PAGE_FEATURE_SET);
CASE_RETURN_STRING(HIDPP_PAGE_DEVICE_INFO);
CASE_RETURN_STRING(HIDPP_PAGE_DEVICE_NAME);
CASE_RETURN_STRING(HIDPP_PAGE_RESET);
CASE_RETURN_STRING(HIDPP_PAGE_BATTERY_LEVEL_STATUS);
CASE_RETURN_STRING(HIDPP_PAGE_KBD_REPROGRAMMABLE_KEYS);
CASE_RETURN_STRING(HIDPP_PAGE_SPECIAL_KEYS_BUTTONS);
CASE_RETURN_STRING(HIDPP_PAGE_WIRELESS_DEVICE_STATUS);
CASE_RETURN_STRING(HIDPP_PAGE_MOUSE_POINTER_BASIC);
CASE_RETURN_STRING(HIDPP_PAGE_ADJUSTABLE_DPI);
CASE_RETURN_STRING(HIDPP_PAGE_ADJUSTABLE_REPORT_RATE);
@@ -189,7 +192,9 @@ out_err:
int
hidpp20_request_command(struct hidpp20_device *device, union hidpp20_message *msg)
{
return hidpp20_request_command_allow_error(device, msg, false);
int ret = hidpp20_request_command_allow_error(device, msg, false);
return ret > 0 ? -EPROTO : ret;
}
/* -------------------------------------------------------------------------- */
@@ -1040,7 +1045,7 @@ int hidpp20_special_key_mouse_get_controls(struct hidpp20_device *device,
{
uint8_t feature_index;
struct hidpp20_control_id *c_list, *control;
uint8_t num_controls;
uint8_t num_controls, real_num_controls = 0;
unsigned i;
int rc;
@@ -1060,22 +1065,30 @@ int hidpp20_special_key_mouse_get_controls(struct hidpp20_device *device,
return 0;
}
hidpp_log_debug(&device->base, "device has %d buttons\n", num_controls);
c_list = zalloc(num_controls * sizeof(struct hidpp20_control_id));
for (i = 0; i < num_controls; i++) {
control = &c_list[i];
control = &c_list[real_num_controls];
control->index = i;
rc = hidpp20_special_keys_buttons_get_info(device,
feature_index,
control);
if (rc)
goto err;
if (rc) {
hidpp_log_error(&device->base,
"error getting button info for control %d, ignoring\n", i);
continue;
}
rc = hidpp20_special_keys_buttons_get_reporting(device,
feature_index,
control);
if (rc)
goto err;
if (rc) {
hidpp_log_error(&device->base,
"error getting button reporting for control %d, ignoring\n", i);
continue;
}
hidpp_log_raw(&device->base,
"control %d: cid: '%s' (%d) tid: '%s' (%d) flags: 0x%02x pos: %d group: %d gmask: 0x%02x raw_XY: %s\n"
@@ -1095,13 +1108,11 @@ int hidpp20_special_key_mouse_get_controls(struct hidpp20_device *device,
control->reporting.divert ? "yes" : "no",
hidpp20_1b04_get_logical_mapping_name(control->reporting.remapped),
control->reporting.remapped);
}
*controls_list = c_list;
return num_controls;
err:
free(c_list);
return rc;
real_num_controls++;
}
*controls_list = realloc(c_list, real_num_controls * sizeof(struct hidpp20_control_id));
return real_num_controls;
}
int
@@ -2362,7 +2373,7 @@ hidpp20_onboard_profiles_parse(struct hidpp20_device *device,
profiles->sector_size,
data);
if (rc < 0)
return rc;
return rc; // ignore_clang_sa_mem_leak
crc_valid = hidpp20_onboard_profiles_is_sector_valid(device,
profiles->sector_size,

View File

@@ -100,6 +100,18 @@ int hidpp20_root_get_protocol_version(struct hidpp20_device *dev,
#define HIDPP_PAGE_DEVICE_INFO 0x0003
/* -------------------------------------------------------------------------- */
/* 0x0005: Device Name */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_DEVICE_NAME 0x0005
/* -------------------------------------------------------------------------- */
/* 0x0020: Reset */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_RESET 0x0020
/* -------------------------------------------------------------------------- */
/* 0x1000: Battery level status */
/* -------------------------------------------------------------------------- */
@@ -316,6 +328,12 @@ const char *hidpp20_1b04_get_logical_mapping_name(uint16_t value);
enum ratbag_button_type hidpp20_1b04_get_physical_mapping(uint16_t value);
const char *hidpp20_1b04_get_physical_mapping_name(uint16_t value);
/* -------------------------------------------------------------------------- */
/* 0x1d4b: Wireless Device Status */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_WIRELESS_DEVICE_STATUS 0x1d4b
/* -------------------------------------------------------------------------- */
/* 0x2200: Mouse Pointer Basic Optical Sensors */
/* -------------------------------------------------------------------------- */

View File

@@ -195,7 +195,7 @@ lur_receiver_enumerate(struct lur_receiver *lur,
dev->present = false;
for (i = 0; i < MAX_DEVICES; i++) {
_cleanup_hidpp10_device_destroy_ struct hidpp10_device *d;
_cleanup_hidpp10_device_destroy_ struct hidpp10_device *d = NULL;
size_t name_size = 64;
char name[name_size];
uint8_t interval, type;

View File

@@ -28,6 +28,7 @@
#include <assert.h>
#include <stdlib.h>
#include <glib.h>
#include <limits.h>
#include "libratbag.h"
#include "libratbag-private.h"
@@ -47,6 +48,7 @@ enum driver {
ETEKCITY,
GSKILL,
LOGITECH_G300,
LOGITECH_G600,
STEELSERIES,
};
@@ -71,6 +73,8 @@ struct data_steelseries {
struct dpi_list *dpi_list;
struct dpi_range *dpi_range;
int macro_length;
int mono_led;
int short_button;
};
struct ratbag_device_data {
@@ -174,6 +178,8 @@ init_data_steelseries(struct ratbag *ratbag,
data->steelseries.led_count = -1;
data->steelseries.dpi_list = NULL;
data->steelseries.dpi_range = NULL;
data->steelseries.mono_led = 0;
data->steelseries.short_button = 0;
num = g_key_file_get_integer(keyfile, group, "Buttons", &error);
if (num != 0 || !error)
@@ -210,6 +216,20 @@ init_data_steelseries(struct ratbag *ratbag,
data->steelseries.macro_length = num;
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "MonoLed", &error);
if (num > 0 || !error)
data->steelseries.mono_led = num;
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "ShortButton", &error);
if (num > 0 || !error)
data->steelseries.short_button = num;
if (error)
g_error_free(error);
}
static const struct driver_map {
@@ -225,6 +245,7 @@ static const struct driver_map {
{ ETEKCITY, "etekcity", NULL},
{ GSKILL, "gskill", NULL },
{ LOGITECH_G300, "logitech_g300", NULL},
{ LOGITECH_G600, "logitech_g600", NULL},
{ STEELSERIES, "steelseries", init_data_steelseries },
};
@@ -388,7 +409,7 @@ file_data_matches(struct ratbag *ratbag,
data->refcount = 1;
data->name = g_key_file_get_string(keyfile, GROUP_DEVICE, "Name", NULL);
if (!data->name) {
return false;
return false; // ignore_clang_sa_mem_leak
}
data->driver = g_key_file_get_string(keyfile, GROUP_DEVICE, "Driver", NULL);
@@ -599,3 +620,19 @@ ratbag_device_data_steelseries_get_macro_length(const struct ratbag_device_data
return data->steelseries.macro_length;
}
int
ratbag_device_data_steelseries_get_mono_led(const struct ratbag_device_data *data)
{
assert(data->drivertype == STEELSERIES);
return data->steelseries.mono_led;
}
int
ratbag_device_data_steelseries_get_short_button(const struct ratbag_device_data *data)
{
assert(data->drivertype == STEELSERIES);
return data->steelseries.short_button;
}

View File

@@ -110,3 +110,9 @@ ratbag_device_data_steelseries_get_dpi_range(const struct ratbag_device_data *da
int
ratbag_device_data_steelseries_get_macro_length(const struct ratbag_device_data *data);
int
ratbag_device_data_steelseries_get_mono_led(const struct ratbag_device_data *data);
int
ratbag_device_data_steelseries_get_short_button(const struct ratbag_device_data *data);

View File

@@ -1223,7 +1223,7 @@ static int
ratbag_hidraw_parse_report_descriptor(struct ratbag_device *device)
{
int rc, desc_size = 0;
struct ratbag_hidraw *hidraw = &device->hidraw;
struct ratbag_hidraw *hidraw = &device->hidraw[0];
struct hidraw_report_descriptor report_desc = {0};
unsigned int i, j;
unsigned int usage_page, usage;
@@ -1291,7 +1291,7 @@ ratbag_hidraw_parse_report_descriptor(struct ratbag_device *device)
}
static int
ratbag_open_hidraw_node(struct ratbag_device *device, struct udev_device *hidraw_udev)
ratbag_open_hidraw_node(struct ratbag_device *device, struct udev_device *hidraw_udev, int idx)
{
struct hidraw_devinfo info;
struct ratbag_device *tmp_device;
@@ -1300,15 +1300,17 @@ ratbag_open_hidraw_node(struct ratbag_device *device, struct udev_device *hidraw
const char *sysname;
size_t reports_size;
device->hidraw.fd = -1;
assert(idx >= 0 && idx < MAX_HIDRAW);
device->hidraw[idx].fd = -1;
sysname = udev_device_get_sysname(hidraw_udev);
if (!strneq("hidraw", sysname, 6))
return -ENODEV;
list_for_each(tmp_device, &device->ratbag->devices, link) {
if (tmp_device->hidraw.sysname &&
streq(tmp_device->hidraw.sysname, sysname)) {
if (tmp_device->hidraw[idx].sysname &&
streq(tmp_device->hidraw[idx].sysname, sysname)) {
return -ENODEV;
}
}
@@ -1339,7 +1341,7 @@ ratbag_open_hidraw_node(struct ratbag_device *device, struct udev_device *hidraw
device->name,
udev_device_get_devnode(hidraw_udev));
device->hidraw.fd = fd;
device->hidraw[idx].fd = fd;
/* parse first to count the number of reports */
res = ratbag_hidraw_parse_report_descriptor(device);
@@ -1348,19 +1350,19 @@ ratbag_open_hidraw_node(struct ratbag_device *device, struct udev_device *hidraw
"Error while parsing the report descriptor: '%s' (%d)\n",
strerror(-res),
res);
device->hidraw.fd = -1;
device->hidraw[idx].fd = -1;
goto err;
}
if (device->hidraw.num_reports)
reports_size = device->hidraw.num_reports * sizeof(struct ratbag_hid_report);
if (device->hidraw[idx].num_reports)
reports_size = device->hidraw[idx].num_reports * sizeof(struct ratbag_hid_report);
else
reports_size = sizeof(struct ratbag_hid_report);
device->hidraw.reports = zalloc(reports_size);
device->hidraw[idx].reports = zalloc(reports_size);
ratbag_hidraw_parse_report_descriptor(device);
device->hidraw.sysname = strdup_safe(sysname);
device->hidraw[idx].sysname = strdup_safe(sysname);
return 0;
err:
@@ -1372,17 +1374,18 @@ err:
static int
ratbag_find_hidraw_node(struct ratbag_device *device,
int (*match)(struct ratbag_device *device),
int use_usb_parent)
int use_usb_parent,
int match_index, int hidraw_index)
{
struct ratbag *ratbag = device->ratbag;
struct udev_enumerate *e;
_cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
struct udev_list_entry *entry;
const char *path;
struct udev_device *hid_udev;
struct udev_device *parent_udev;
struct udev *udev = ratbag->udev;
int rc = -ENODEV;
int matched;
int matched, endpoint_index = 0;
assert(match);
@@ -1414,29 +1417,30 @@ ratbag_find_hidraw_node(struct ratbag_device *device,
if (!udev_device)
continue;
rc = ratbag_open_hidraw_node(device, udev_device);
if (match_index > 0 && match_index != endpoint_index++)
continue;
rc = ratbag_open_hidraw_node(device, udev_device, hidraw_index);
if (rc)
goto skip;
matched = match(device);
rc = matched ? 0 : -ENODEV;
if (matched == 1)
goto out;
return rc;
skip:
ratbag_close_hidraw(device);
ratbag_close_hidraw_index(device, hidraw_index);
}
out:
udev_enumerate_unref(e);
return rc;
}
int
ratbag_find_hidraw(struct ratbag_device *device, int (*match)(struct ratbag_device *device))
{
return ratbag_find_hidraw_node(device, match, true);
return ratbag_find_hidraw_node(device, match, true, -1, 0);
}
static int
@@ -1448,7 +1452,13 @@ hidraw_match_all(struct ratbag_device *device)
int
ratbag_open_hidraw(struct ratbag_device *device)
{
return ratbag_find_hidraw_node(device, hidraw_match_all, false);
return ratbag_find_hidraw_node(device, hidraw_match_all, false, -1, 0);
}
int
ratbag_open_hidraw_index(struct ratbag_device *device, int endpoint_index, int hidraw_index)
{
return ratbag_find_hidraw_node(device, hidraw_match_all, true, endpoint_index, hidraw_index);
}
static struct ratbag_hid_report *
@@ -1457,15 +1467,15 @@ ratbag_hidraw_get_report(struct ratbag_device *device, unsigned int report_id)
unsigned i;
if (report_id == 0) {
if (device->hidraw.reports[0].report_id == report_id)
return &device->hidraw.reports[0];
if (device->hidraw[0].reports[0].report_id == report_id)
return &device->hidraw[0].reports[0];
else
return NULL;
}
for (i = 0; i < device->hidraw.num_reports; i++) {
if (device->hidraw.reports[i].report_id == report_id)
return &device->hidraw.reports[i];
for (i = 0; i < device->hidraw[0].num_reports; i++) {
if (device->hidraw[0].reports[i].report_id == report_id)
return &device->hidraw[0].reports[i];
}
return NULL;
@@ -1507,20 +1517,28 @@ ratbag_hidraw_get_usage(struct ratbag_device *device, unsigned int report_id)
void
ratbag_close_hidraw(struct ratbag_device *device)
{
if (device->hidraw.fd < 0)
ratbag_close_hidraw_index(device, 0);
}
void
ratbag_close_hidraw_index(struct ratbag_device *device, int idx)
{
assert(idx >= 0 && idx < MAX_HIDRAW);
if (device->hidraw[idx].fd < 0)
return;
if (device->hidraw.sysname) {
free(device->hidraw.sysname);
device->hidraw.sysname = NULL;
if (device->hidraw[idx].sysname) {
free(device->hidraw[idx].sysname);
device->hidraw[idx].sysname = NULL;
}
ratbag_close_fd(device, device->hidraw.fd);
device->hidraw.fd = -1;
ratbag_close_fd(device, device->hidraw[idx].fd);
device->hidraw[idx].fd = -1;
if (device->hidraw.reports) {
free(device->hidraw.reports);
device->hidraw.reports = NULL;
if (device->hidraw[idx].reports) {
free(device->hidraw[idx].reports);
device->hidraw[idx].reports = NULL;
}
}
@@ -1531,7 +1549,7 @@ ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char reportnum,
uint8_t tmp_buf[HID_MAX_BUFFER_SIZE];
int rc;
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf || device->hidraw.fd < 0)
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf || device->hidraw[0].fd < 0)
return -EINVAL;
if (rtype != HID_FEATURE_REPORT)
@@ -1542,7 +1560,7 @@ ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char reportnum,
memset(tmp_buf, 0, len);
tmp_buf[0] = reportnum;
rc = ioctl(device->hidraw.fd, HIDIOCGFEATURE(len), tmp_buf);
rc = ioctl(device->hidraw[0].fd, HIDIOCGFEATURE(len), tmp_buf);
if (rc < 0)
return -errno;
@@ -1554,7 +1572,7 @@ ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char reportnum,
buf[0] = reportnum;
log_buf_raw(device->ratbag, "feature set: ", buf, len);
rc = ioctl(device->hidraw.fd, HIDIOCSFEATURE(len), buf);
rc = ioctl(device->hidraw[0].fd, HIDIOCSFEATURE(len), buf);
if (rc < 0)
return -errno;
@@ -1569,12 +1587,12 @@ ratbag_hidraw_output_report(struct ratbag_device *device, uint8_t *buf, size_t l
{
int rc;
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf || device->hidraw.fd < 0)
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf || device->hidraw[0].fd < 0)
return -EINVAL;
log_buf_raw(device->ratbag, "output report: ", buf, len);
rc = write(device->hidraw.fd, buf, len);
rc = write(device->hidraw[0].fd, buf, len);
if (rc < 0)
return -errno;
@@ -1587,14 +1605,22 @@ ratbag_hidraw_output_report(struct ratbag_device *device, uint8_t *buf, size_t l
int
ratbag_hidraw_read_input_report(struct ratbag_device *device, uint8_t *buf, size_t len)
{
return ratbag_hidraw_read_input_report_index(device, buf, len, 0);
}
int
ratbag_hidraw_read_input_report_index(struct ratbag_device *device, uint8_t *buf, size_t len, int hidrawno)
{
int rc;
struct pollfd fds;
if (len < 1 || !buf || device->hidraw.fd < 0)
assert(hidrawno >= 0 && hidrawno < MAX_HIDRAW);
if (len < 1 || !buf || device->hidraw[hidrawno].fd < 0)
return -EINVAL;
fds.fd = device->hidraw.fd;
fds.fd = device->hidraw[hidrawno].fd;
fds.events = POLLIN;
rc = poll(&fds, 1, 1000);
@@ -1604,7 +1630,7 @@ ratbag_hidraw_read_input_report(struct ratbag_device *device, uint8_t *buf, size
if (rc == 0)
return -ETIMEDOUT;
rc = read(device->hidraw.fd, buf, len);
rc = read(device->hidraw[hidrawno].fd, buf, len);
if (rc > 0)
log_buf_raw(device->ratbag, "input report: ", buf, rc);

View File

@@ -32,6 +32,7 @@
#define HID_INPUT_REPORT 0
#define HID_OUTPUT_REPORT 1
#define HID_FEATURE_REPORT 2
#define MAX_HIDRAW 2
struct ratbag_hid_report {
unsigned int report_id;
@@ -55,6 +56,18 @@ struct ratbag_hidraw {
*/
int ratbag_open_hidraw(struct ratbag_device *device);
/**
* Open a hidraw device associated with the device by its enumeration order to
* a specific internal hidraw index.
*
* @param device the ratbag device
* @param enumeration index of the endpoint
* @param interal index of hidraw array
*
* @return 0 on success or a negative errno on error
*/
int ratbag_open_hidraw_index(struct ratbag_device *device, int endpoint_index, int hidraw_index);
/**
* Find and open the hidraw device associated with the device by using the
* given matching function.
@@ -75,6 +88,14 @@ ratbag_find_hidraw(struct ratbag_device *device,
*/
void ratbag_close_hidraw(struct ratbag_device *device);
/**
* Close a hidraw device associated with the device by the internal index.
*
* @param device the ratbag device
* @param interal index of hidraw array
*/
void ratbag_close_hidraw_index(struct ratbag_device *device, int idx);
/**
* Send report request to device
*
@@ -115,6 +136,19 @@ int ratbag_hidraw_output_report(struct ratbag_device *device, uint8_t *buf, size
*/
int ratbag_hidraw_read_input_report(struct ratbag_device *device, uint8_t *buf, size_t len);
/**
* Read an input report from the device from a specific hidraw index
*
* @param device the ratbag device
* @param[out] buf resulting raw data
* @param len length of buf
* @param interal index of hidraw array
*
* @return count of data transfered, or a negative errno on error
*/
int ratbag_hidraw_read_input_report_index(struct ratbag_device *device, uint8_t *buf, size_t len, int hidrawno);
/**
* Tells if a given device has the specified report ID.
*

View File

@@ -129,7 +129,7 @@ struct ratbag_device {
void *userdata;
struct udev_device *udev_device;
struct ratbag_hidraw hidraw;
struct ratbag_hidraw hidraw[MAX_HIDRAW];
int refcount;
struct input_id ids;
struct ratbag_driver *driver;
@@ -262,7 +262,7 @@ struct ratbag_resolution {
unsigned int dpi_y; /**< y resolution in dpi */
unsigned int hz; /**< report rate in Hz */
unsigned int rates[5];
unsigned int rates[8];
size_t nrates;
bool is_active;
@@ -603,6 +603,7 @@ struct ratbag_driver etekcity_driver;
struct ratbag_driver hidpp20_driver;
struct ratbag_driver hidpp10_driver;
struct ratbag_driver logitech_g300_driver;
struct ratbag_driver logitech_g600_driver;
struct ratbag_driver roccat_driver;
struct ratbag_driver gskill_driver;
struct ratbag_driver steelseries_driver;

View File

@@ -583,6 +583,7 @@ ratbag_create_context(const struct ratbag_interface *interface,
ratbag_register_driver(ratbag, &hidpp20_driver);
ratbag_register_driver(ratbag, &hidpp10_driver);
ratbag_register_driver(ratbag, &logitech_g300_driver);
ratbag_register_driver(ratbag, &logitech_g600_driver);
ratbag_register_driver(ratbag, &roccat_driver);
ratbag_register_driver(ratbag, &gskill_driver);
ratbag_register_driver(ratbag, &steelseries_driver);
@@ -649,7 +650,7 @@ ratbag_create_led(struct ratbag_profile *profile, unsigned int index)
return led;
}
LIBRATBAG_EXPORT int
LIBRATBAG_EXPORT bool
ratbag_profile_has_capability(const struct ratbag_profile *profile,
enum ratbag_profile_capability cap)
{
@@ -831,16 +832,16 @@ ratbag_profile_set_enabled(struct ratbag_profile *profile, bool enabled)
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT int
LIBRATBAG_EXPORT bool
ratbag_profile_is_active(struct ratbag_profile *profile)
{
return profile->is_active;
return !!profile->is_active;
}
LIBRATBAG_EXPORT bool
ratbag_profile_is_enabled(const struct ratbag_profile *profile)
{
return profile->is_enabled;
return !!profile->is_enabled;
}
LIBRATBAG_EXPORT unsigned int
@@ -892,7 +893,7 @@ ratbag_device_unset_capability(struct ratbag_device *device,
long_clear_bit(device->capabilities, cap);
}
LIBRATBAG_EXPORT int
LIBRATBAG_EXPORT bool
ratbag_device_has_capability(const struct ratbag_device *device,
enum ratbag_device_capability cap)
{
@@ -1001,7 +1002,7 @@ ratbag_device_commit(struct ratbag_device *device)
list_for_each(resolution, &profile->resolutions, link)
resolution->dirty = false;
}
return RATBAG_SUCCESS;
@@ -1083,7 +1084,7 @@ ratbag_resolution_unref(struct ratbag_resolution *resolution)
return NULL;
}
LIBRATBAG_EXPORT int
LIBRATBAG_EXPORT bool
ratbag_resolution_has_capability(struct ratbag_resolution *resolution,
enum ratbag_resolution_capability cap)
{
@@ -1226,10 +1227,10 @@ ratbag_resolution_get_report_rate_list(struct ratbag_resolution *resolution,
return resolution->nrates;
}
LIBRATBAG_EXPORT int
LIBRATBAG_EXPORT bool
ratbag_resolution_is_active(const struct ratbag_resolution *resolution)
{
return resolution->is_active;
return !!resolution->is_active;
}
LIBRATBAG_EXPORT enum ratbag_error_code
@@ -1248,10 +1249,10 @@ ratbag_resolution_set_active(struct ratbag_resolution *resolution)
}
LIBRATBAG_EXPORT int
LIBRATBAG_EXPORT bool
ratbag_resolution_is_default(const struct ratbag_resolution *resolution)
{
return resolution->is_default;
return !!resolution->is_default;
}
LIBRATBAG_EXPORT enum ratbag_error_code
@@ -1314,7 +1315,7 @@ ratbag_button_get_action_type(struct ratbag_button *button)
return button->action.type;
}
LIBRATBAG_EXPORT int
LIBRATBAG_EXPORT bool
ratbag_button_has_action_type(struct ratbag_button *button,
enum ratbag_button_action_type action_type)
{
@@ -1544,7 +1545,7 @@ ratbag_led_get_type(struct ratbag_led *led)
return led->type;
}
LIBRATBAG_EXPORT int
LIBRATBAG_EXPORT bool
ratbag_led_has_mode(struct ratbag_led *led,
enum ratbag_led_mode mode)
{
@@ -1553,7 +1554,7 @@ ratbag_led_has_mode(struct ratbag_led *led,
if (mode == RATBAG_LED_OFF)
return 1;
return (led->modes & (1 << mode));
return !!(led->modes & (1 << mode));
}
LIBRATBAG_EXPORT struct ratbag_color
@@ -1681,6 +1682,7 @@ ratbag_profile_set_name(struct ratbag_profile *profile,
free(profile->name);
profile->name = name_copy;
profile->dirty = true;
return 0;
}

View File

@@ -452,7 +452,7 @@ ratbag_device_get_svg_name(const struct ratbag_device* device);
* @retval 1 The device has the capability
* @retval 0 The device does not have the capability
*/
int
bool
ratbag_device_has_capability(const struct ratbag_device *device,
enum ratbag_device_capability cap);
@@ -540,7 +540,7 @@ ratbag_profile_unref(struct ratbag_profile *profile);
*
* @return non-zero if the capability is available, zero otherwise.
*/
int
bool
ratbag_profile_has_capability(const struct ratbag_profile *profile,
enum ratbag_profile_capability cap);
@@ -655,7 +655,7 @@ ratbag_device_get_profile(struct ratbag_device *device, unsigned int index);
*
* @return non-zero if the profile is currently active, zero otherwise
*/
int
bool
ratbag_profile_is_active(struct ratbag_profile *profile);
/**
@@ -771,7 +771,7 @@ ratbag_resolution_get_user_data(const struct ratbag_resolution *resolution);
*
* @return non-zero if the capability is available, zero otherwise.
*/
int
bool
ratbag_resolution_has_capability(struct ratbag_resolution *resolution,
enum ratbag_resolution_capability cap);
@@ -988,7 +988,7 @@ ratbag_resolution_set_active(struct ratbag_resolution *resolution);
* @return Non-zero if the resolution mode is the active one, zero
* otherwise.
*/
int
bool
ratbag_resolution_is_active(const struct ratbag_resolution *resolution);
/**
@@ -1023,7 +1023,7 @@ ratbag_resolution_set_default(struct ratbag_resolution *resolution);
* @return Non-zero if the resolution mode is the default one, zero
* otherwise.
*/
int
bool
ratbag_resolution_is_default(const struct ratbag_resolution *resolution);
/**
@@ -1117,7 +1117,7 @@ ratbag_button_get_action_type(struct ratbag_button *button);
*
* @return non-zero if the action type is supported, zero otherwise.
*/
int
bool
ratbag_button_has_action_type(struct ratbag_button *button,
enum ratbag_button_action_type action_type);
@@ -1218,7 +1218,7 @@ ratbag_led_get_type(struct ratbag_led *led);
*
* @see ratbag_led_set_mode
*/
int
bool
ratbag_led_has_mode(struct ratbag_led *led,
enum ratbag_led_mode mode);

View File

@@ -135,6 +135,7 @@ static inline void reset_errno(int *saved_errno)
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev*, udev_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_device*, udev_device_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_enumerate*, udev_enumerate_unref);
/*
* ELEMENTSOF() - number of array elements

99
tools/check_scan_build.py Executable file
View File

@@ -0,0 +1,99 @@
#!/bin/env python3
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
import bs4
import os
import re
import sys
class Bug(object):
def __init__(self):
self.line_number = None
self.cfile = None
self.sa_type = None
self.sa_descr = None
def main(argv):
if len(argv) < 2:
print(f'usage {argv[0]} PATH')
sys.exit(1)
scanbuild_path = argv[1]
ignored = []
bugs = []
line_re = re.compile(r'line (\d+), column (\d+)')
for root, dirs, files in os.walk(scanbuild_path):
for filename in files:
if not filename.startswith('report'):
continue
with open(os.path.join(root, filename)) as f:
soup = bs4.BeautifulSoup(f, 'html.parser')
# the first table is the summary
summary = soup.table
bug = Bug()
# iterate over the table
for tr in summary('tr'):
if 'File:' in tr.contents[0].string:
bug.cfile = os.path.abspath(tr.contents[1].string)
else:
bug.sa_type = tr.contents[0].string.rstrip(':').lower()
for s in tr.contents[1].strings:
# retrieve the line number and the description
m = line_re.match(s)
if m is None:
bug.sa_descr = s
else:
bug.line_number = int(m.group(1))
# retrieve the html line corresponding to the code
code = soup.find('td', id=f'LN{bug.line_number}').parent
# fetching any comments in this line
try:
comments = code.find('span', class_='comment').string
except AttributeError:
# no comments on the line
bugs.append(bug)
else:
if 'ignore_clang_sa_' in comments:
ignored.append(bug)
else:
bugs.append(bug)
if len(ignored) > 0:
print(f'{len(ignored)} bugs are ignored:')
for b in ignored:
print(f' {b.sa_type}: {b.sa_descr} at {b.cfile}:{b.line_number}')
if len(bugs) > 0:
print()
if len(bugs) > 0:
print(f'ERROR: {len(bugs)} bugs found:')
for b in bugs:
print(f' * {b.sa_type.upper()}: {b.sa_descr} at {b.cfile}:{b.line_number}')
sys.exit(1)
sys.exit(0)
if __name__ == '__main__':
main(sys.argv)

View File

@@ -31,6 +31,7 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <liblur.h>
#include <libratbag-util.h>

View File

@@ -30,7 +30,7 @@ import sys
def print_ratbagctl(ratbagctl_path, ratbagd_path, version_string):
with open(ratbagctl_path, 'r') as ratbagctl, open(ratbagd_path, 'r') as ratbagd:
with open(ratbagctl_path, 'r', encoding='utf-8') as ratbagctl, open(ratbagd_path, 'r', encoding='utf-8') as ratbagd:
for l in ratbagctl.readlines():
if l.startswith("from ratbagd import "):
headers = True
@@ -53,7 +53,7 @@ def main(argv):
parser.add_argument("--version", action="store", default="git_master")
ns = parser.parse_args(sys.argv[1:])
if ns.output:
ns.output_file = open(ns.output, 'w')
ns.output_file = open(ns.output, 'w', encoding='utf-8')
st = os.stat(ns.output)
os.chmod(ns.output, st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
sys.stdout = ns.output_file
@@ -63,5 +63,6 @@ def main(argv):
except AttributeError:
pass
if __name__ == "__main__":
main(sys.argv[1:])

View File

@@ -27,6 +27,8 @@ import sys
from evdev import ecodes
LIBRATBAG_DATA_DIR = "@LIBRATBAG_DATA_DIR@"
# Deferred translations, see https://docs.python.org/3/library/gettext.html#deferred-translations
def N_(x):
@@ -53,29 +55,34 @@ class MetaRatbag(type):
class RatbagErrorCode(metaclass=MetaRatbag):
RATBAG_SUCCESS = libratbag.RATBAG_SUCCESS
SUCCESS = libratbag.RATBAG_SUCCESS
"""An error occured on the device. Either the device is not a libratbag
device or communication with the device failed."""
RATBAG_ERROR_DEVICE = libratbag.RATBAG_ERROR_DEVICE
DEVICE = libratbag.RATBAG_ERROR_DEVICE
"""Insufficient capabilities. This error occurs when a requested change is
beyond the device's capabilities."""
RATBAG_ERROR_CAPABILITY = libratbag.RATBAG_ERROR_CAPABILITY
CAPABILITY = libratbag.RATBAG_ERROR_CAPABILITY
"""Invalid value or value range. The provided value or value range is
outside of the legal or supported range."""
RATBAG_ERROR_VALUE = libratbag.RATBAG_ERROR_VALUE
VALUE = libratbag.RATBAG_ERROR_VALUE
"""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 = libratbag.RATBAG_ERROR_SYSTEM
SYSTEM = libratbag.RATBAG_ERROR_SYSTEM
"""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 = libratbag.RATBAG_ERROR_IMPLEMENTATION
IMPLEMENTATION = libratbag.RATBAG_ERROR_IMPLEMENTATION
class RatbagdUnavailable(Exception):
"""Signals DBus is unavailable or the ratbagd daemon is not available."""
pass
class RatbagError(Exception):
@@ -84,37 +91,37 @@ class RatbagError(Exception):
class RatbagErrorDevice(RatbagError):
"""An exception corresponding to RatbagErrorCode.RATBAG_ERROR_DEVICE."""
"""An exception corresponding to RatbagErrorCode.DEVICE."""
pass
class RatbagErrorCapability(RatbagError):
"""An exception corresponding to RatbagErrorCode.RATBAG_ERROR_CAPABILITY."""
"""An exception corresponding to RatbagErrorCode.CAPABILITY."""
pass
class RatbagErrorValue(RatbagError):
"""An exception corresponding to RatbagErrorCode.RATBAG_ERROR_Value."""
"""An exception corresponding to RatbagErrorCode.VALUE."""
pass
class RatbagErrorSystem(RatbagError):
"""An exception corresponding to RatbagErrorCode.RATBAG_ERROR_System."""
"""An exception corresponding to RatbagErrorCode.SYSTEM."""
pass
class RatbagErrorImplementation(RatbagError):
"""An exception corresponding to RatbagErrorCode.RATBAG_ERROR_IMPLEMENTATION."""
"""An exception corresponding to RatbagErrorCode.IMPLEMENTATION."""
pass
"""A table mapping RatbagErrorCode values to RatbagError* exceptions."""
EXCEPTION_TABLE = {
RatbagErrorCode.RATBAG_ERROR_DEVICE: RatbagErrorDevice,
RatbagErrorCode.RATBAG_ERROR_CAPABILITY: RatbagErrorCapability,
RatbagErrorCode.RATBAG_ERROR_VALUE: RatbagErrorValue,
RatbagErrorCode.RATBAG_ERROR_SYSTEM: RatbagErrorSystem,
RatbagErrorCode.RATBAG_ERROR_IMPLEMENTATION: RatbagErrorImplementation
RatbagErrorCode.DEVICE: RatbagErrorDevice,
RatbagErrorCode.CAPABILITY: RatbagErrorCapability,
RatbagErrorCode.VALUE: RatbagErrorValue,
RatbagErrorCode.SYSTEM: RatbagErrorSystem,
RatbagErrorCode.IMPLEMENTATION: RatbagErrorImplementation
}
@@ -126,6 +133,7 @@ class Ratbagd(object):
"""
def __init__(self):
os.environ['LIBRATBAG_DATA_DIR'] = LIBRATBAG_DATA_DIR
self._ratbag = libratbag.ratbag_create_context(libratbag.interface, None)
self._devices = {}
self._devices_initialized = False
@@ -180,6 +188,9 @@ class Ratbagd(object):
def __getitem__(self, id):
"""Returns the requested device, or None."""
if id.startswith("event"):
id = "/dev/input/{}".format(id)
if id not in self._devices.keys():
try:
dev = RatbagdDevice(self._ratbag, id)
@@ -281,25 +292,33 @@ class RatbagdDevice(metaclass=MetaRatbag):
@param theme The theme from which to retrieve the SVG, as str
"""
return os.path.join(theme, libratbag.ratbag_device_get_svg_name(self._device))
svg = libratbag.ratbag_device_get_svg_name(self._device)
if svg is None:
return ''
def commit(self, callback=None):
return os.path.join(LIBRATBAG_DATA_DIR, theme, svg)
def get_svg_fd(self, theme):
"""Returns a File-like object to the SVG for the given theme, or
raises FileNotFoundError if the file is not available.
The theme must be one of org.freedesktop.ratbag1.Manager.Themes. The
theme 'default' is guaranteed to be available (but the file itself
may not be).
@param theme The theme from which to retrieve the SVG, as str
"""
return open(self.get_svg(theme))
def commit(self):
"""Commits all changes made to the device.
This is an async call to DBus and this method does not return
anything. Any success or failure code is reported to the callback
provided when ratbagd finishes writing to the device. Note that upon
failure, the device is automatically resynchronized by ratbagd and no
further interaction is required by the client; clients can thus treat a
commit as being always successful.
@param callback The function to call with the result of the commit, as
a function that takes the return value of the Commit
method.
This is implemented asynchronously inside ratbagd. Hence, we just call
this method and always succeed. Any failure is handled inside ratbagd
by emitting the Resync signal, which automatically resynchronizes the
device. No further interaction is required by the client.
"""
r = libratbag.ratbag_device_commit(self._device)
if callback is not None:
callback(r)
return libratbag.ratbag_device_commit(self._device)
class RatbagdProfile(metaclass=MetaRatbag):

View File

@@ -6,7 +6,7 @@ ratbagctl \- inspect and modify configurable mice
.SH DESCRIPTION
.PP
The
.I ratbagctl
.B ratbagctl
tool queries or changes a device's settings.
.PP
This tool usually needs ratbagd to be running.
@@ -18,12 +18,13 @@ Show program's version number and exit.
.B \-\-verbose, \-v
Print debugging output. Multiple -v options increase the verbosity. For example,
.B \-vvv
will show the protocol output.
will show the protocol output.
.TP 8
.B \-\-nocommit
Do not immediately write the settings to the mouse. This allows to set
multiple parameters in a script, and the last call to ratbagctl will write
them all.
multiple parameters in a script, and the last call to
.B ratbagctl
will write them all.
.TP 8
.B \-\-help, \-h
Print the help.

View File

@@ -30,6 +30,7 @@ import time
import toolbox
import signal
import sys
from ratbagctl import RatbagErrorCapability # NOQA
def main(argv):
@@ -64,10 +65,13 @@ def main(argv):
parser.print_help()
return
else:
f(ratbagd, cmd)
# give time for ratbagd to receive async requests before
# we try to terminate it
time.sleep(0.5)
try:
f(ratbagd, cmd)
# give time for ratbagd to receive async requests before
# we try to terminate it
time.sleep(0.5)
except RatbagErrorCapability as e:
print("Error: {}".format(e), file=sys.stderr)
finally:
try:
if cmd.keepalive:

View File

@@ -26,8 +26,10 @@
import evdev
import sys
import argparse
import re
from ratbagd import Ratbagd, RatbagdDevice, RatbagdProfile, RatbagdMacro, RatbagdResolution, RatbagdButton, RatbagdLed, RatbagdUnavailable, RatbagError, RatbagErrorCode, RatbagErrorCapability # NOQA
from ratbagd import Ratbagd, RatbagdDevice, RatbagdProfile, RatbagdMacro, RatbagdResolution, RatbagdButton, RatbagdLed, RatbagdDBusUnavailable, RatbagError, RatbagErrorCode, RatbagErrorCapability # NOQA
button_specials_strmap = {
RatbagdButton.ACTION_SPECIAL_UNKNOWN: "unknown",
@@ -266,8 +268,18 @@ def print_device(d, level):
RatbagdDevice.CAP_LED: "led"}
capabilities = [caps[c] for c in d.capabilities]
svg_name = 'unknown'
try:
with d.get_svg_fd('gnome') as fd:
svg = fd.read()
match = re.search('sodipodi:docname=".*"\n', svg)
if match:
svg_name = match.group(0).split('"')[1]
except FileNotFoundError:
pass
print(" " * level + "{} - {}".format(d.id, d.name))
print(" " * level + " SVG: {}".format(d.get_svg('gnome')))
print(" " * level + " SVG: {}".format(svg_name))
print(" " * level + " Capabilities: {}".format(", ".join(capabilities)))
print(" " * level + " Number of Buttons: {}".format(len(p.buttons)))
print(" " * level + " Number of Leds: {}".format(len(p.leds)))
@@ -367,12 +379,17 @@ def func_button_get(r, args):
def func_button_action_set_button(r, args):
b, p, d = find_button(r, args)
if not d.CAP_BUTTON in d.capabilities:
raise RatbagErrorCapability("assigning a button is not supported on this device")
b.mapping = args.target_button
commit(d, args)
def func_button_action_set_special(r, args):
b, p, d = find_button(r, args)
if not d.CAP_BUTTON in d.capabilities:
raise RatbagErrorCapability("assigning a button is not supported on this device")
try:
special = args.target_special
except AttributeError:
@@ -384,6 +401,9 @@ def func_button_action_set_special(r, args):
def func_button_action_set_macro(r, args):
b, p, d = find_button(r, args)
if not d.CAP_BUTTON_MACROS in d.capabilities:
raise RatbagErrorCapability("assigning a macro is not supported on this device")
macro_keys = args.target_macro
macro = RatbagdMacro()
for s in macro_keys:
@@ -602,15 +622,10 @@ link = 'link'
################################################################################
def commit_cb(status):
if not status == RatbagErrorCode.RATBAG_SUCCESS:
print("Could not commit changes to the device")
def commit(device, args):
if args.nocommit:
return
device.commit(commit_cb)
device.commit()
def color(string):
@@ -651,6 +666,7 @@ def dpi(string):
else:
return (int_x, int_y)
# note: 'eventX' is assumed at the end of each command
parser_def = [
{
@@ -1408,6 +1424,7 @@ class RatbagParserLink(RatbagParser):
dest = self.get_dest()
print(" {:<36}Use {}for '{} Commands'".format(prefix + self.dest + " ...", prefix, dest.group))
classes = {
switch: RatbagParserSwitch,
set: RatbagParserSet,
@@ -1506,8 +1523,8 @@ def open_ratbagd(ratbagd_process=None, verbose=0):
try:
r = Ratbagd()
r.verbose = verbose
except RatbagdDBusUnavailable:
print("Unable to connect to ratbagd over dbus")
except RatbagdUnavailable:
print("Unable to connect to ratbagd")
return None
if ratbagd_process is not None:
@@ -1540,7 +1557,11 @@ def main(argv):
parser.print_help()
return
else:
f(r, cmd)
try:
f(r, cmd)
except RatbagErrorCapability as e:
print("Error: {}".format(e), file=sys.stderr)
if __name__ == "__main__":
main(sys.argv[1:])

View File

@@ -36,32 +36,32 @@ def N_(x):
class RatbagErrorCode(IntEnum):
RATBAG_SUCCESS = 0
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
DEVICE = -1000
"""Insufficient capabilities. This error occurs when a requested change is
beyond the device's capabilities."""
RATBAG_ERROR_CAPABILITY = -1001
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
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
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
IMPLEMENTATION = -1004
class RatbagdDBusUnavailable(Exception):
class RatbagdUnavailable(Exception):
"""Signals DBus is unavailable or the ratbagd daemon is not available."""
pass
@@ -77,37 +77,37 @@ class RatbagError(Exception):
class RatbagErrorDevice(RatbagError):
"""An exception corresponding to RatbagErrorCode.RATBAG_ERROR_DEVICE."""
"""An exception corresponding to RatbagErrorCode.DEVICE."""
pass
class RatbagErrorCapability(RatbagError):
"""An exception corresponding to RatbagErrorCode.RATBAG_ERROR_CAPABILITY."""
"""An exception corresponding to RatbagErrorCode.CAPABILITY."""
pass
class RatbagErrorValue(RatbagError):
"""An exception corresponding to RatbagErrorCode.RATBAG_ERROR_Value."""
"""An exception corresponding to RatbagErrorCode.VALUE."""
pass
class RatbagErrorSystem(RatbagError):
"""An exception corresponding to RatbagErrorCode.RATBAG_ERROR_System."""
"""An exception corresponding to RatbagErrorCode.SYSTEM."""
pass
class RatbagErrorImplementation(RatbagError):
"""An exception corresponding to RatbagErrorCode.RATBAG_ERROR_IMPLEMENTATION."""
"""An exception corresponding to RatbagErrorCode.IMPLEMENTATION."""
pass
"""A table mapping RatbagErrorCode values to RatbagError* exceptions."""
EXCEPTION_TABLE = {
RatbagErrorCode.RATBAG_ERROR_DEVICE: RatbagErrorDevice,
RatbagErrorCode.RATBAG_ERROR_CAPABILITY: RatbagErrorCapability,
RatbagErrorCode.RATBAG_ERROR_VALUE: RatbagErrorValue,
RatbagErrorCode.RATBAG_ERROR_SYSTEM: RatbagErrorSystem,
RatbagErrorCode.RATBAG_ERROR_IMPLEMENTATION: RatbagErrorImplementation
RatbagErrorCode.DEVICE: RatbagErrorDevice,
RatbagErrorCode.CAPABILITY: RatbagErrorCapability,
RatbagErrorCode.VALUE: RatbagErrorValue,
RatbagErrorCode.SYSTEM: RatbagErrorSystem,
RatbagErrorCode.IMPLEMENTATION: RatbagErrorImplementation
}
@@ -121,7 +121,7 @@ class _RatbagdDBus(GObject.GObject):
try:
_RatbagdDBus._dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
except GLib.Error as e:
raise RatbagdDBusUnavailable(e.message)
raise RatbagdUnavailable(e.message)
ratbag1 = "org.freedesktop.ratbag1"
if os.environ.get('RATBAGCTL_DEVEL'):
@@ -142,10 +142,10 @@ class _RatbagdDBus(GObject.GObject):
self._interface,
None)
except GLib.Error as e:
raise RatbagdDBusUnavailable(e.message)
raise RatbagdUnavailable(e.message)
if self._proxy.get_name_owner() is None:
raise RatbagdDBusUnavailable("No one currently owns {}".format(ratbag1))
raise RatbagdUnavailable("No one currently owns {}".format(ratbag1))
self._proxy.connect("g-properties-changed", self._on_properties_changed)
@@ -186,27 +186,6 @@ class _RatbagdDBus(GObject.GObject):
# update
self._proxy.set_cached_property(property, val)
def _dbus_call_async(self, method, type, *value, callback):
# The callback handler to unwrap the data
def callback_handler(obj, result, user_callback):
try:
res = obj.call_finish(result)
res = res.unpack()[0] # Result is always a tuple
user_callback(res)
except GLib.Error as e:
# Unrecognized error code; print the message to stderr and raise
# the GLib.Error.
print(e.message, file=sys.stderr)
raise
# Calls a method asynchronously on the bus, using the given method
# name, type signature and values. The callback is invoked when the
# method finishes
val = GLib.Variant("({})".format(type), value)
self._proxy.call(method, val,
Gio.DBusCallFlags.NO_AUTO_START,
500, None, callback_handler, callback)
def _dbus_call(self, method, type, *value):
# Calls a method synchronously on the bus, using the given method name,
# type signature and values.
@@ -232,6 +211,35 @@ class _RatbagdDBus(GObject.GObject):
print(e.message, file=sys.stderr)
raise
def _dbus_call_return_one_fd(self, method, type, *value):
# Calls a method synchronously on the bus, using the given method name,
# type signature and values.
#
# It the result is valid, it is returned. Invalid results raise the
# appropriate RatbagError* or RatbagdDBus* exception, or GLib.Error if
# it is an unexpected exception that probably shouldn't be passed up to
# the UI.
val = GLib.Variant("({})".format(type), value)
try:
res = self._proxy.call_with_unix_fd_list_sync(method, val,
Gio.DBusCallFlags.NO_AUTO_START,
2000, None, None)
if res in EXCEPTION_TABLE:
raise EXCEPTION_TABLE[res]
# Result is a tuple of (GLib.Variant('(h)', (0,)), fd_list),
# we return the first file descriptor in that list
return res[1].get(0)
except GLib.Error as e:
if e.code == Gio.DBusError.FILE_NOT_FOUND:
raise FileNotFoundError('Failed to open SVG file')
elif e.code == Gio.IOErrorEnum.TIMED_OUT:
raise RatbagdDBusTimeout(e.message)
else:
# Unrecognized error code; print the message to stderr and raise
# the GLib.Error.
print(e.message, file=sys.stderr)
raise
def __eq__(self, other):
return other and self._object_path == other._object_path
@@ -241,7 +249,7 @@ class Ratbagd(_RatbagdDBus):
through ratbagd; actual interaction with the devices is via the
RatbagdDevice, RatbagdProfile, RatbagdResolution and RatbagdButton objects.
Throws RatbagdDBusUnavailable when the DBus service is not available.
Throws RatbagdUnavailable when the DBus service is not available.
"""
__gsignals__ = {
@@ -249,12 +257,18 @@ class Ratbagd(_RatbagdDBus):
(GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
"device-removed":
(GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
"daemon-disappeared":
(GObject.SignalFlags.RUN_FIRST, None, ()),
}
def __init__(self):
_RatbagdDBus.__init__(self, "Manager", None)
result = self._get_dbus_property("Devices")
result = self._get_dbus_property("Devices") or []
self._devices = [RatbagdDevice(objpath) for objpath in result]
self._proxy.connect("notify::g-name-owner", self._on_name_owner_changed)
def _on_name_owner_changed(self, *kwargs):
self.emit("daemon-disappeared")
def _on_properties_changed(self, proxy, changed_props, invalidated_props):
if "Devices" in changed_props.keys():
@@ -286,7 +300,7 @@ class Ratbagd(_RatbagdDBus):
def themes(self):
"""A list of theme names. The theme 'default' is guaranteed to be
available."""
return self._get_dbus_property("Themes")
return self._get_dbus_property("Themes") or []
def __enter__(self):
return self
@@ -321,7 +335,7 @@ class RatbagdDevice(_RatbagdDBus):
# FIXME: if we start adding and removing objects from this list,
# things will break!
result = self._get_dbus_property("Profiles")
result = self._get_dbus_property("Profiles") or []
self._profiles = [RatbagdProfile(objpath) for objpath in result]
for profile in self._profiles:
profile.connect("notify::is-active", self._on_active_profile_changed)
@@ -343,7 +357,7 @@ class RatbagdDevice(_RatbagdDBus):
if RatbagdDevice.CAP_SWITCHABLE_RESOLUTION is in device.capabilities:
do something
"""
return self._get_dbus_property("Capabilities")
return self._get_dbus_property("Capabilities") or []
@GObject.Property
def name(self):
@@ -367,6 +381,19 @@ class RatbagdDevice(_RatbagdDBus):
print("No active profile. Please report this bug to the libratbag developers", file=sys.stderr)
return self._profiles[0]
def get_svg_fd(self, theme):
"""Returns a File-like object to the SVG for the given theme, or
raises FileNotFoundError if the file is not available.
The theme must be one of org.freedesktop.ratbag1.Manager.Themes. The
theme 'default' is guaranteed to be available (but the file itself
may not be).
@param theme The theme from which to retrieve the SVG, as str
"""
fd = self._dbus_call_return_one_fd("GetSvgFd", "s", theme)
return os.fdopen(fd)
def get_svg(self, theme):
"""Gets the full path to the SVG for the given theme, or the empty
string if none is available.
@@ -378,21 +405,15 @@ class RatbagdDevice(_RatbagdDBus):
"""
return self._dbus_call("GetSvg", "s", theme)
def commit(self, callback):
def commit(self):
"""Commits all changes made to the device.
This is an async call to DBus and this method does not return
anything. Any success or failure code is reported to the callback
provided when ratbagd finishes writing to the device. Note that upon
failure, the device is automatically resynchronized by ratbagd and no
further interaction is required by the client; clients can thus treat a
commit as being always successful.
@param callback The function to call with the result of the commit, as
a function that takes the return value of the Commit
method.
This is implemented asynchronously inside ratbagd. Hence, we just call
this method and always succeed. Any failure is handled inside ratbagd
by emitting the Resync signal, which automatically resynchronizes the
device. No further interaction is required by the client.
"""
self._dbus_call_async("Commit", "", callback=callback)
self._dbus_call("Commit", "")
for profile in self._profiles:
if profile.dirty:
profile._dirty = False
@@ -411,15 +432,15 @@ class RatbagdProfile(_RatbagdDBus):
# FIXME: if we start adding and removing objects from any of these
# lists, things will break!
result = self._get_dbus_property("Resolutions")
result = self._get_dbus_property("Resolutions") or []
self._resolutions = [RatbagdResolution(objpath) for objpath in result]
self._subscribe_dirty(self._resolutions)
result = self._get_dbus_property("Buttons")
result = self._get_dbus_property("Buttons") or []
self._buttons = [RatbagdButton(objpath) for objpath in result]
self._subscribe_dirty(self._buttons)
result = self._get_dbus_property("Leds")
result = self._get_dbus_property("Leds") or []
self._leds = [RatbagdLed(objpath) for objpath in result]
self._subscribe_dirty(self._leds)
@@ -448,7 +469,7 @@ class RatbagdProfile(_RatbagdDBus):
if RatbagdProfile.CAP_WRITABLE_NAME is in profile.capabilities:
do something
"""
return self._get_dbus_property("Capabilities")
return self._get_dbus_property("Capabilities") or []
@GObject.Property
def name(self):
@@ -565,7 +586,7 @@ class RatbagdResolution(_RatbagdDBus):
if RatbagdResolution.CAP_SEPARATE_XY_RESOLUTION is in resolution.capabilities:
do something
"""
return self._get_dbus_property("Capabilities")
return self._get_dbus_property("Capabilities") or []
@GObject.Property
def resolution(self):
@@ -596,12 +617,12 @@ class RatbagdResolution(_RatbagdDBus):
@GObject.Property
def resolutions(self):
"""The list of supported DPI values"""
return self._get_dbus_property("Resolutions")
return self._get_dbus_property("Resolutions") or []
@GObject.Property
def report_rates(self):
"""The list of supported report rates"""
return self._get_dbus_property("ReportRates")
return self._get_dbus_property("ReportRates") or []
@GObject.Property
def is_active(self):

View File

@@ -28,17 +28,22 @@ udev_device_from_path(struct udev *udev, const char *path)
{
struct udev_device *udev_device;
const char *event_node_prefix = "/dev/input/event";
_cleanup_(freep) char *path_canonical = NULL;
if (strneq(path, event_node_prefix, strlen(event_node_prefix))) {
if ((path_canonical = canonicalize_file_name(path)) == NULL) {
error("Failed to canonicalize path '%s': %s\n", path, strerror(errno));
return NULL;
}
if (strneq(path_canonical, event_node_prefix, strlen(event_node_prefix))) {
struct stat st;
if (stat(path, &st) == -1) {
if (stat(path_canonical, &st) == -1) {
error("Failed to stat '%s': %s\n", path, strerror(errno));
return NULL;
}
udev_device = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
} else {
udev_device = udev_device_new_from_syspath(udev, path);
udev_device = udev_device_new_from_syspath(udev, path_canonical);
}
if (!udev_device) {
error("Can't open '%s': %s\n", path, strerror(errno));

View File

@@ -61,6 +61,8 @@ def start_ratbagd(verbosity=0):
from gi.repository import Gio
import time
os.makedirs(DBUS_CONF_DIR, exist_ok=True)
# first copy the policy for the ratbagd daemon to be allowed to run
shutil.copy(os.path.join('@MESON_BUILD_ROOT@', DBUS_CONF_NAME),
DBUS_CONF_PATH)
@@ -73,6 +75,8 @@ def start_ratbagd(verbosity=0):
args.append('--verbose=raw')
elif verbosity >= 2:
args.append('--verbose')
elif verbosity == 0:
args.append('--quiet')
ratbagd_process = subprocess.Popen(args, shell=False, stdout=sys.stdout, stderr=sys.stderr)