1
0
forked from drath/libratbag

Compare commits

...

60 Commits

Author SHA1 Message Date
Peter Hutterer
ab3f1df06e meson.build: libratbag 0.10
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-02 11:19:16 +10:00
Peter Hutterer
caa31e17cf circle CI: add meson plain and release build types
Second time lucky.

Fixes #770

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 20:02:28 +10:00
Peter Hutterer
b7e6103496 circleci: move existing config to the new location
CircleCI 2.0 requires a new config file location, otherwise it won't run. And
since 1.0 is sunset now, this means our CI wasn't getting executed.

The actual format of the config is the same, we were already using 2.0
features.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 10:24:09 +10:00
Peter Hutterer
b1b6a2761e Revert "circle CI: add meson plain and release build types"
This broke CircleCI but since it didn't run for that build, no-one noticed.

This reverts commit 3bc31edd72.
2019-08-01 10:24:09 +10:00
Peter Hutterer
11f4f32947 Revert "hidpp20: add quirk system"
This reverts commit 196d53e65f.
2019-08-01 09:47:00 +10:00
Peter Hutterer
4f5d0dffec Revert "hidpp20/data: add the G305"
This reverts commit cad05ffced.
2019-08-01 09:47:00 +10:00
Peter Hutterer
d213bdb86e data: add a test for duplicate DeviceMatch lines
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 09:22:03 +10:00
Peter Hutterer
e69e1de17b devices: drop the G700s wireless entry
The G700 and G700s both use the receiver's product id, so they both end up
having the same DeviceMatch. One of those two never gets loaded but which one
is implementation-defined and may change accidentally.

Let's drop the G700s entry because the G700 is closer to a lowest comon
denominator. Once we have kernel support for the actual product id, we can
re-instante them.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 09:22:03 +10:00
Filipe Laíns
cad05ffced hidpp20/data: add the G305
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-01 09:20:47 +10:00
Filipe Laíns
196d53e65f hidpp20: add quirk system
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-01 09:20:47 +10:00
Peter Hutterer
c606b1abe3 tools: flake8 fixes
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 09:08:06 +10:00
Peter Hutterer
b4009669b9 ratbagd: Add an APIVersion property to make version checks easier
While the DBus API is still in flux, things can break at any time.

Keeping piper and libratbag in version-step works terribly, doubly so where
piper is flatpaked but ratbagd comes from some repo somewhere. We can't check
at build-time, we can't run ratbagd --version at runtime.

so let's add a version field for now and drop it if we ever get to stable.
Meanwhile, ratbagd.py will throw an exception if the expected version doesn't
match exactly.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 09:08:06 +10:00
Peter Hutterer
d458305f93 tools: ratbagdctl.test: if an exception occurs during startup, terminate
Don't leave a ratbagd.devel process hanging if something goes wrong.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 09:08:06 +10:00
Peter Hutterer
9c11960665 ratbagd: document the various commandline options in the man page
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 08:46:19 +10:00
Peter Hutterer
e8fc8b45d1 ratbagd: drop the hardcoded test device
Force the tests to load a test device with JSON that matches their
expectations instead of having a magic device pre-setup where the tests have
to know what the values are.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 08:36:16 +10:00
Peter Hutterer
e64a121572 tools: switch the button tests over to json 2019-07-31 08:36:16 +10:00
Peter Hutterer
607e47953c tools: switch led tests to use the json parsing 2019-07-31 08:36:16 +10:00
Peter Hutterer
a5816055dc tools: switch the report rate to json 2019-07-31 08:36:16 +10:00
Peter Hutterer
62ac0772f5 tools: switch the dpi test to json 2019-07-31 08:36:16 +10:00
Peter Hutterer
fc9e63a5aa tools: switch the resolution tests to use the json loading 2019-07-31 08:36:16 +10:00
Peter Hutterer
936a8f7a92 tools: switch the ratbagctl.test to use the new json loading 2019-07-31 08:36:16 +10:00
Peter Hutterer
818e665853 ratbagd: add ability to load json for test devices
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 08:36:16 +10:00
Peter Hutterer
3a3b11ade4 ratbagctl: autogenerate the choices list for the specials and led modes
Let's not maintain that list manually if we can fetch it from the enums.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 12:39:11 +02:00
Peter Hutterer
3bc31edd72 circle CI: add meson plain and release build types
Fixes #770

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 12:31:01 +02:00
Peter Hutterer
485df20a83 tools: fix typo in ratbagd.py
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 19:30:11 +10:00
Peter Hutterer
49291c4b45 ratbagd: encode the test device path without underscores
sd-bus encodes underscores as _5f which looks like memory corruption. Let's
skip them, they don't add value anyway.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 19:28:07 +10:00
Filipe Laíns
02ec2442d3 hidpp20: add support for RGB Effects feature (0x8071)
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-30 15:12:10 +10:00
Filipe Laíns
a8b4d9c6f3 hidpp20: add new profile layout (0x04)
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-30 15:11:54 +10:00
Peter Hutterer
22a6ac1c7e hidpp10: make raw debugging messages a bit easier to read
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 12:01:42 +10:00
Peter Hutterer
42c302c860 tools: fix a few comments referring to old names
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 09:53:36 +10:00
Peter Hutterer
e2b1581aef tools: ratbagctl.test: set the data dir correctly
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 09:35:56 +10:00
Peter Hutterer
10a3e87980 meson.build: remove unused fallback SVG name
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 06:55:30 +10:00
Peter Hutterer
2d465a63e1 meson: drop the unused libratbag version define
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 06:52:14 +10:00
Eric Culp
c2315323f4 meson.build: fix builds with --buildtype=plain
When building with --buildtype=plain, the build fails with the following
error:

    cc1: error: ‘-Wformat-security’ ignored without ‘-Wformat’ [-Werror=format-security]

-Werror=format-security was recently added in 43def1b3a5 by #745
2019-07-27 09:43:49 +10:00
Peter Hutterer
05d7e9b324 ratbagd: poke DBus to reload its config after installing the devel policy
Fixes #753
2019-07-26 16:42:01 +10:00
Peter Hutterer
71a399f20f ratbagd: encode the test device iteration in the test device name
The sequence triggered by the ratbagctl.test suite is:
- DBus call Commit()
- ratbagd schedules the callback as idle func
- DBus call ResetTestDevice()
- ratbagd cleans up the test device
- callback is called on next idle
  - boom! the device is already freed

To avoid this, we need to take a ref of the device while it's being scheduled
for the commit handling. But that blocks the object paths for new test devices
because the test device name is encoded in those paths (like "hidraw" for real
devices) and it doesn't change. So let's add a count to the test device name
to make sure the new device doesn't re-use the old device's paths.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 16:41:04 +10:00
Peter Hutterer
8259c2e706 ratbagd: introduce ref/unref for ratbagd_device pointers
We'll need to keep references around to devices even when caller is done with
them.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 16:41:04 +10:00
Peter Hutterer
89980a4a72 meson: add the prefix to the dbus dir
Otherwise where the directory is used within meson.build, the prefix would be
missing. No real effect since we don't use it anywhere but let's be safe.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
79e94b4a7e ratbagd: install the devel dbus policy file on startup
Don't rely on some other tool to install the policy file, let's just do this
in ratbagd.devel directly so it's immediately executable.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
122f05c272 tools: drop the RATBAGCTL_DEVEL environ, just use RATBAG_TEST instead
Which is the same env that ratbagd uses as well. And we don't have variable
values in RATBAGCTL_DEVEL anymore, so no point in having two different
variables here.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
b06ac1035e tools: add --use-existing-ratbagd to the ratbagctl.test script as well
This makes it possible to run the script against a manually started
ratbagd.devel - with a lot less effort.

And since we're don't need to be root to do this, let's move that check too.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
647fe432b2 tools: add argument to ratbagctl.devel to connect to running ratbagd
This makes it a lot easier to start ratbagd.devel in gdb, then run
ratbagctl.devel against it.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
4e2e9f037b Drop the ratbagctl.devel sha bus name
We already have a separate name for the devel bus anyway, the git sha doesn't
give us a huge amount of benefits but makes manual debugging a major PITA.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
f92fee0b15 meson: document the various built bits a bit better
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
d7c394f379 tools: ratbactl.test: if RATBAGCTL_DEVEL is set, use the existing server
When there's a bug that's triggered by the test suite it's hard to figure out
what exactly crashes. So allow starting ratbagd.devel manually and run the
test suite against that process so we can gdb it.

The steps required for this are a bit insane, so the instructions are listed.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 21:38:12 +10:00
Peter Hutterer
7fec19f8f2 test: switch the resolution capabilities to an array
Just like the profile ones

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 09:08:55 +02:00
Peter Hutterer
11c1e2d71a Fix setting the capability flags on the resolutions
2 hours of debugging... ffs

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 09:08:55 +02:00
Peter Hutterer
e2cbaf9dea tools: print the Model in ratbagctl info
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 07:26:27 +02:00
Peter Hutterer
bf9d86e228 hidpp10: if the profile name is the empty string, make it NULL
Because our profile struct can never have an NULL name, let's assume the
empty string equals NULL.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 13:02:18 +10:00
Filipe Laíns
f24a3bd441 hidpp20: fix 0x1001 comments, add missing ASE and use get_unaligned_be_u16
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-24 19:17:31 +10:00
Filipe Laíns
df5742ebfe libratbag-util: move data manipulation functions out of hidpp-generic
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-24 19:17:21 +10:00
Peter Hutterer
0c569e0bf7 tools: use super() in the ratbagctl test
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-24 09:16:24 +02:00
Peter Hutterer
5ea1731f38 tools: use super() without arguments in ratbagctl
I *think* this is a Python2 leftover or something. Either way, let's remove
it.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-24 09:16:24 +02:00
Peter Hutterer
572064aba8 tools: use super() instead of the named parent class in ratbagd.py
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-24 09:16:24 +02:00
Peter Hutterer
da2764e78d Append the created profiles/leds/buttons/resolutions, don't insert
We're initializing them with incrementing indices, let's make sure the list is
initialized that way too. Makes things more obvious in the log when the first
profile to be queried from the device isn't profile 4.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-23 13:47:11 +02:00
Peter Hutterer
4e56afae22 roccat: use the new get/set feature helpers
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-23 13:46:23 +02:00
Peter Hutterer
d46251900c hidraw: add a feature report get/set helper functions
No real functional changes since they just forward to the existing function.
But it reduces the cognitive load a bit when reading the code.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-23 13:46:23 +02:00
Peter Hutterer
43def1b3a5 meson.build: enable -Werror=format-security
We don't want to accidentally ignore those.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-19 07:17:45 +02:00
Filipe Laíns
247ff7695d data: add MX Vertical
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-19 07:57:53 +10:00
Filipe Laíns
7a14026f2d hidpp20: fix warnings
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 15:13:51 +01:00
38 changed files with 1974 additions and 535 deletions

View File

@@ -13,6 +13,18 @@ libratbag_references:
ninja -v -C build ${NINJA_ARGS}
environment:
MESON_PARAMS: --prefix=/usr
build_buildtype_plain: &build_buildtype_plain
run:
<<: *build_default
name: Build with buildtype plain
environment:
MESON_PARAMS: --prefix=/usr -Dbuildtype=plain
build_buildtype_release: &build_buildtype_release
run:
<<: *build_default
name: Build with buildtype release
environment:
MESON_PARAMS: --prefix=/usr -Dbuildtype=release
build_and_test: &build_and_test
run:
<<: *build_default
@@ -66,7 +78,7 @@ fedora_prep_cache: &fedora_prep_cache
steps:
- run:
name: Initializing Fedora dnf cache
command: dnf install -y --downloadonly libsolv tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml libunistring-devel dbus-daemon
command: dnf install -y --downloadonly libsolv tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml libunistring-devel dbus-daemon json-glib-devel
- persist_to_workspace:
root: /var/cache/
paths:
@@ -82,7 +94,7 @@ fedora_install: &fedora_install
command: |
echo keepcache=1 >> /etc/dnf/dnf.conf
dnf upgrade -y libsolv
dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml python3-devel swig libunistring-devel dbus-daemon
dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml python3-devel swig libunistring-devel dbus-daemon json-glib-devel
sed -i 's/systemd//' /etc/nsswitch.conf
fedora_settings: &fedora_settings
@@ -96,6 +108,8 @@ fedora_settings: &fedora_settings
- *install
- *check_install
- *check_uninstall
- *build_buildtype_plain
- *build_buildtype_release
- *export_logs
@@ -110,13 +124,15 @@ ubuntu_settings: &ubuntu_settings
apt-get remove -y libnss-systemd
add-apt-repository universe
apt-get update
apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev valgrind python3-gi python3-evdev libglib2.0-dev python3-lxml python3-dev swig systemd libunistring-dev
apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev valgrind python3-gi python3-evdev libglib2.0-dev python3-lxml python3-dev swig systemd libunistring-dev libjson-glib-dev
- checkout
- *start_dbus
- *build_and_test
- *install
- *check_install
- *check_uninstall
- *build_buildtype_plain
- *build_buildtype_release
- *export_logs
doc_build: &doc_build

58
data/devices/duplicate-check.py Executable file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright © 2018 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
import argparse
import os
import sys
import configparser
def parse_data_file(path):
data = configparser.ConfigParser(strict=True)
# Don't convert to lowercase
data.optionxform = lambda option: option
data.read(path)
matches = data['Device']['DeviceMatch']
return matches.split(';')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Device duplicate match checker')
parser.add_argument('file', nargs='+')
args = parser.parse_args()
device_matches = {}
duplicates = False
for path in args.file:
matches = parse_data_file(path)
fname = os.path.basename(path)
for m in matches:
if m in device_matches:
print('Duplicate DeviceMatch={} in {} and {}'.format(m, fname, device_matches[m]))
duplicates = True
device_matches[m] = fname
if duplicates:
sys.exit(1)

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech MX Vertical
DeviceMatch=usb:046d:407b
Driver=hidpp20

View File

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

View File

@@ -12,15 +12,15 @@
<busconfig>
<policy user="root">
<allow own="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow send_destination="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow receive_sender="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow own="org.freedesktop.ratbag_devel1"/>
<allow send_destination="org.freedesktop.ratbag_devel1"/>
<allow receive_sender="org.freedesktop.ratbag_devel1"/>
</policy>
<!-- allow everyone to talk to the daemon -->
<policy context="default">
<allow send_destination="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow receive_sender="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow send_destination="org.freedesktop.ratbag_devel1"/>
<allow receive_sender="org.freedesktop.ratbag_devel1"/>
</policy>
</busconfig>

View File

@@ -80,6 +80,19 @@ org.freedesktop.ratbag1.Manager
The **org.freedesktop.ratbag1.Manager** interface is the entry point to
interact with ratbagd.
.. attribute:: APIVersion
:type: i
:flags: read-only,constant
The DBus API version. This is a stopgap feature while the DBus API
is still in flux. The version has no backwards or
forward-compatibilty guarantees - your client must understand
**exactly** the same version, it is not enough to support an older
or a newer version.
This API will be removed once the DBus API is declared stable.
.. attribute:: Devices
:type: ao

View File

@@ -1,14 +1,14 @@
project('libratbag', 'c',
version : '0.9.906',
version : '0.10',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.40.0')
libratbag_version = meson.project_version().split('.')
git = find_program('git')
r = run_command('tools/ratbag-git-sha.sh')
ratbagd_sha = r.stdout().strip()
# The DBus API version. Increase this every time the DBus API changes.
# No backwards/forwards guarantee, clients are expected to understand
# whatever ratbagd speaks or bail out. This should be removed if we ever
# finish the API and declare it stable.
ratbagd_api_version = 1
# We use libtool-version numbers because it's easier to understand.
# Before making a release, the libratbag_so_* and liblur_so_*
@@ -31,6 +31,8 @@ cc = meson.get_compiler('c')
cflags = ['-Wno-unused-parameter',
'-fvisibility=hidden',
'-Wmissing-prototypes',
'-Wformat', # required by format-security
'-Werror=format-security',
'-Wstrict-prototypes']
add_project_arguments(cflags, language: 'c')
@@ -39,7 +41,7 @@ add_project_arguments(cflags, language: 'c')
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')
config_h.set('RATBAGD_API_VERSION', ratbagd_api_version)
libratbag_data_dir = join_paths(get_option('prefix'),
get_option('datadir'),
'libratbag')
@@ -70,6 +72,7 @@ pkgconfig = import('pkgconfig')
dep_udev = dependency('libudev')
dep_libevdev = dependency('libevdev')
dep_glib = dependency('glib-2.0')
dep_json_glib = dependency('json-glib-1.0')
dep_lm = cc.find_library('m')
dep_unistring = cc.find_library('unistring')
@@ -118,7 +121,10 @@ lib_libhidpp = static_library('hidpp',
dependencies : deps_libhidpp)
dep_libhidpp = declare_dependency(link_with: lib_libhidpp)
### liblur.a ####
### liblur ####
#
# liblur is the library to handle logitech unifying receivers.
#
install_headers('src/liblur.h')
src_liblur = [
@@ -154,6 +160,9 @@ pkgconfig.generate (
)
#### libratbag.so ####
#
# libratbag is an internal-only library and the bit that does the actual
# work talking to the mouse. it's used by ratbagd.
src_libratbag = [
'src/libratbag-enums.h',
'src/libratbag.h',
@@ -182,6 +191,7 @@ deps_libratbag = [
dep_udev,
dep_libevdev,
dep_glib,
dep_json_glib,
dep_libutil,
dep_libhidpp,
]
@@ -242,6 +252,8 @@ executable('hidpp20-reset',
)
#### lur-command ####
#
# A tool to access and manipulate logitech unifying receivers.
src_lur_command = [ 'tools/lur-command.c' ]
executable('lur-command',
src_lur_command,
@@ -299,7 +311,6 @@ data_files = files(
'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-hero.device',
'data/devices/logitech-g703.device',
@@ -312,6 +323,7 @@ data_files = files(
'data/devices/logitech-Marathon-M705.device',
'data/devices/logitech-MX-Anywhere2.device',
'data/devices/logitech-MX-Anywhere2S.device',
'data/devices/logitech-MX-Vertical.device',
'data/devices/roccat-kone-xtd.device',
'data/devices/steelseries-kinzu-v2.device',
'data/devices/steelseries-rival-310.device',
@@ -327,6 +339,9 @@ install_data(data_files,
data_parse_test = find_program(join_paths(meson.source_root(), 'data/devices/data-parse-test.py'))
test('data-parse-test', data_parse_test, args: data_files)
duplicate_test = find_program(join_paths(meson.source_root(), 'data/devices/duplicate-check.py'))
test('duplicate-test', duplicate_test, args : data_files)
#### tests ####
enable_tests = get_option('tests')
if enable_tests
@@ -374,6 +389,10 @@ if enable_tests
endif
#### ratbagd ####
#
# The main item of this repo, a DBus server that uses libratbag to talk to
# the mice. The DBus API is the public-facing API.
#
src_ratbagd = [
'src/shared-macro.h',
'src/shared-rbtree.h',
@@ -386,6 +405,8 @@ src_ratbagd = [
'ratbagd/ratbagd-profile.c',
'ratbagd/ratbagd-resolution.c',
'ratbagd/ratbagd-test.c',
'ratbagd/ratbagd-json.c',
'ratbagd/ratbagd-json.h',
'src/libratbag-util.h',
'src/libratbag-util.c',
]
@@ -407,21 +428,34 @@ executable('ratbagd',
install_man('ratbagd/ratbagd.8')
#### ratbagd_devel ####
#
# A development ratbagd server that owns a different name on the bus
# (org.freedesktop.ratbag_devel1). This server is used by ratbagdctl.devel.
#
config_ratbagd_devel = configuration_data()
dbus_devel_policy = configure_file(input : 'dbus/org.freedesktop.ratbag_devel1.conf.in',
output : 'org.freedesktop.ratbag_devel1.conf',
configuration : config_ratbagd_devel)
# This is a hack. We always install the devel policy file into
# /etc/dbus-1/system.d, independent of any prefixes we use otherwise.
# This should never be used outside of developer machines anyway, but
# installations on those may use different prefixes for building.
# This is not set in stone, suggest something better if you can.
dbussystemdir = join_paths('/', get_option('sysconfdir'), 'dbus-1', 'system.d')
executable('ratbagd.devel',
src_ratbagd,
dependencies : deps_ratbagd,
include_directories : include_directories('src'),
install : false,
c_args : ['-DRATBAG_DBUS_INTERFACE="ratbag_devel1_@0@"'.format(ratbagd_sha),
c_args : ['-DRATBAG_DBUS_INTERFACE="ratbag_devel1"',
'-DDBUS_POLICY_SRC="@0@/@1@"'.format(meson.build_root(), dbus_devel_policy),
'-DDBUS_POLICY_DST="@0@/@1@"'.format(dbussystemdir, dbus_devel_policy),
'-DDISABLE_COREDUMP=1'],
)
config_ratbagd_devel = configuration_data()
config_ratbagd_devel.set('ratbagd_sha', ratbagd_sha)
configure_file(input : 'dbus/org.freedesktop.ratbag_devel1.conf.in',
output : 'org.freedesktop.ratbag_devel1.conf',
configuration : config_ratbagd_devel)
#### unit file ####
if enable_systemd
@@ -460,7 +494,7 @@ endif
dbusdir = get_option('dbus-root-dir')
if dbusdir == ''
dbusdir = join_paths(get_option('datadir'), 'dbus-1')
dbusdir = join_paths(get_option('prefix'), get_option('datadir'), 'dbus-1')
endif
configure_file(input : 'dbus/org.freedesktop.ratbag1.service.in',
@@ -495,15 +529,22 @@ else
py3 = pymod.find_installation()
endif
# ratbagctl calls ratbagd over DBus
config_ratbagctl = configuration_data()
config_ratbagctl.set('RATBAGD_API_VERSION', ratbagd_api_version)
config_ratbagctl.set('version', meson.project_version())
ratbagctl_in = configure_file(input : 'tools/ratbagctl.in.in',
output : 'ratbagctl.in',
configuration: config_ratbagctl)
# ratbagctl is the commandline tool to interact with ratbagd over DBus.
custom_target('ratbagctl',
output : 'ratbagctl',
input : ['tools/ratbagctl.in', 'tools/ratbagd.py'],
input : [ratbagctl_in, 'tools/ratbagd.py'],
build_by_default : true,
command : [py3, join_paths(meson.source_root(), 'tools', 'merge_ratbagd.py'),
'@INPUT@',
'--output', '@OUTPUT@',
'--version', meson.project_version()],
'--output', '@OUTPUT@'],
install: true,
install_dir: get_option('bindir'))
@@ -516,9 +557,9 @@ man_ratbagctl = configure_file (
)
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)
config_ratbagctl_devel.set('RATBAGD_API_VERSION', ratbagd_api_version)
configure_file(input : 'tools/toolbox.py',
output : 'toolbox.py',
@@ -580,14 +621,17 @@ 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)
# ratbag-command is a commandline tool with the UI as ratbagctl but instead
# of connecting to ratbagd over DBus, it uses libratbag directly. This is a
# development tool for protocol debugging etc.
custom_target('ratbag-command',
output : 'ratbag-command',
input : ['tools/ratbagctl.in', ratbagc_py],
input : [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()],
'--output', '@OUTPUT@'],
install: false)
#### output files ####

View File

@@ -41,6 +41,8 @@
#include "libratbag-util.h"
struct ratbagd_device {
unsigned int refcount;
struct ratbagd *ctx;
RBNode node;
char *sysname;
@@ -172,6 +174,7 @@ static void ratbagd_device_commit_pending(void *data)
r = ratbag_device_commit(device->lib_device);
if (r < 0)
ratbagd_device_resync(device, device->ctx->bus);
ratbagd_device_unref(device);
}
static int ratbagd_device_commit(sd_bus_message *m,
@@ -182,7 +185,7 @@ static int ratbagd_device_commit(sd_bus_message *m,
ratbagd_schedule_task(device->ctx,
ratbagd_device_commit_pending,
device);
ratbagd_device_ref(device));
CHECK_CALL(sd_bus_reply_method_return(m, "u", 0));
@@ -230,7 +233,7 @@ int ratbagd_device_new(struct ratbagd_device **out,
const char *sysname,
struct ratbag_device *lib_device)
{
_cleanup_(ratbagd_device_freep) struct ratbagd_device *device = NULL;
_cleanup_(ratbagd_device_unrefp) struct ratbagd_device *device = NULL;
struct ratbag_profile *profile;
unsigned int i;
int r;
@@ -240,6 +243,7 @@ int ratbagd_device_new(struct ratbagd_device **out,
assert(sysname);
device = zalloc(sizeof(*device));
device->refcount = 1;
device->ctx = ctx;
rbnode_init(&device->node);
device->lib_device = ratbag_device_ref(lib_device);
@@ -282,12 +286,12 @@ int ratbagd_device_new(struct ratbagd_device **out,
return 0;
}
struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device)
static void ratbagd_device_free(struct ratbagd_device *device)
{
unsigned int i;
if (!device)
return NULL;
return;
assert(!ratbagd_device_linked(device));
@@ -301,7 +305,26 @@ struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device)
assert(!device->lib_device); /* ratbag yields !NULL if still pinned */
return mfree(device);
mfree(device);
}
struct ratbagd_device *ratbagd_device_ref(struct ratbagd_device *device)
{
assert(device->refcount > 0);
++device->refcount;
return device;
}
struct ratbagd_device *ratbagd_device_unref(struct ratbagd_device *device)
{
assert(device->refcount > 0);
--device->refcount;
if (device->refcount == 0)
ratbagd_device_free(device);
return NULL;
}
const char *ratbagd_device_get_sysname(struct ratbagd_device *device)

562
ratbagd/ratbagd-json.c Normal file
View File

@@ -0,0 +1,562 @@
/*
* Copyright © 2019 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/* JSON parser for ratbag test devices. The JSON format is not fixed and may
change at any time, but basically looks like this:
{
"profiles": [
{
"is_active": bool,
"is_default": bool,
"is_disabled": bool,
"rate": int,
"report_rates": [ int, ...],
"capabilities": [int, ...] # profile capabilities
"resolutions": [
{
"xres": int,
"yres": int,
"dpi_min", int,
"dpi_max", int,
"is_active": bool,
"is_default": bool,
"capabilities": [int, ...] # resolution capabilities
}
]
"buttons": [
"action_type": "<enum>" # none, button, key, special, macro, unknown
"button": int, # if button
"key": int, # if key
"special": "<enum>", # if special, for values see see special_lookup()
"macro": ["+B", "-B", "t400"] # if macro. uppercase only
]
"leds": [ ]
},
{
... next profile ...
}
]
}
ratbagd uses a minimum sane device (1 profile, 1 resolution, etc.) and any
JSON instructions get merged into that device. Which means you usually
only need to set those bits you care about being checked.
*/
#include "libratbag-util.h"
#include "ratbagd-json.h"
#include "ratbagd-test.h"
#include <json-glib/json-glib.h>
#include <libevdev/libevdev.h>
/* Hack because we don't do proper data passing */
static int num_resolutions;
static int num_buttons;
static int num_leds;
static int parse_error = 0;
#define parser_error(element_) { \
log_error("json: parser error: %s:%d: element '%s'\n", __func__, __LINE__, (element_)); \
parse_error = -EINVAL; \
goto out; \
}
static void parse_resolution_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_resolution *resolution = data;
if (streq(name, "xres")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("xres");
resolution->xres = v;
log_verbose("json: xres: %d\n", v);
} else if (streq(name, "yres")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("yres");
resolution->yres = v;
log_verbose("json: yres: %d\n", v);
} else if (streq(name, "dpi_min")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("dpi_min");
resolution->dpi_min = v;
log_verbose("json: dpi_min: %d\n", v);
} else if (streq(name, "dpi_max")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("dpi_min");
resolution->dpi_max = v;
log_verbose("json: dpi_max: %d\n", v);
} else if (streq(name, "is_active")) {
gboolean v = json_object_get_boolean_member(obj, name);
resolution->active = v;
log_verbose("json: is_active: %d\n", v);
} else if (streq(name, "is_default")) {
gboolean v = json_object_get_boolean_member(obj, name);
resolution->dflt = v;
log_verbose("json: is_default: %d\n", v);
} else if (streq(name, "capabilities")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(resolution->caps));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
if (v > RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION)
parser_error("capabilities");
resolution->caps[s] = v;
}
log_verbose("json: caps: %d %d %d %d %d...\n",
resolution->caps[0],
resolution->caps[1],
resolution->caps[2],
resolution->caps[3],
resolution->caps[4]);
} else {
log_error("json: unknown resolution key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_resolution(JsonNode *node,
struct ratbag_test_resolution *resolution)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_resolution_member, resolution);
}
static void parse_led_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_led *led = data;
if (streq(name, "type")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > RATBAG_LED_TYPE_WHEEL)
parser_error("type");
led->type = v;
log_verbose("json: type: %d\n", v);
} else if (streq(name, "mode")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > RATBAG_LED_BREATHING)
parser_error("mode");
led->mode = v;
log_verbose("json: mode: %d\n", v);
} else if (streq(name, "duration")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 10000)
parser_error("duration");
led->ms = v;
log_verbose("json: duration: %d\n", v);
} else if (streq(name, "brightness")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 100)
parser_error("brightness");
led->brightness = v;
log_verbose("json: brightness: %d\n", v);
} else if (streq(name, "color")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) == 3);
led->color.red = json_array_get_int_element(a, 0);
led->color.green = json_array_get_int_element(a, 1);
led->color.blue = json_array_get_int_element(a, 2);
log_verbose("json: color: %02x%02x%02x\n",
led->color.red,
led->color.green,
led->color.blue);
} else {
log_error("json: unknown led key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_led(JsonNode *node, struct ratbag_test_led *led)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_led_member, led);
}
static enum ratbag_button_action_special
special_lookup(const char *string)
{
const struct {
const char *key;
enum ratbag_button_action_special special;
} lut[] = {
{ "invalid", RATBAG_BUTTON_ACTION_SPECIAL_INVALID},
{ "unknown", RATBAG_BUTTON_ACTION_SPECIAL_UNKNOWN},
{ "doubleclick", RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK},
{ "wheel-left", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT},
{ "wheel-right", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT},
{ "wheel-up", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP},
{ "wheel-down", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN},
{ "ratchet-mode-switch", RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH},
{ "resolution-cycle-up", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP},
{ "resolution-cycledown", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN},
{ "resolution-up", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP},
{ "resolution-down", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN},
{ "resolution-alternate", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE},
{ "resolution-default", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT},
{ "profile-cycle-up", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP},
{ "profile-cycle-down", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_DOWN},
{ "profile-up", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP},
{ "profile-down", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN},
{ "second-mode", RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE},
{ "battery-level", RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL},
};
for (size_t i = 0; i < ARRAY_LENGTH(lut); i++) {
if (streq(string, lut[i].key))
return lut[i].special;
}
parser_error("special");
out:
return RATBAG_BUTTON_ACTION_SPECIAL_INVALID;
}
static enum ratbag_button_action_type
action_type_lookup(const char *string)
{
const struct {
const char *key;
enum ratbag_button_action_type type;
} lut[] = {
{ "none", RATBAG_BUTTON_ACTION_TYPE_NONE},
{ "button", RATBAG_BUTTON_ACTION_TYPE_BUTTON},
{ "special", RATBAG_BUTTON_ACTION_TYPE_SPECIAL},
{ "key", RATBAG_BUTTON_ACTION_TYPE_KEY},
{ "macro", RATBAG_BUTTON_ACTION_TYPE_MACRO},
{ "unknown", RATBAG_BUTTON_ACTION_TYPE_UNKNOWN},
};
for (size_t i = 0; i < ARRAY_LENGTH(lut); i++) {
if (streq(string, lut[i].key))
return lut[i].type;
}
parser_error("action_type");
out:
return RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
}
static inline struct ratbag_test_macro_event
parse_macro(const char *m)
{
struct ratbag_test_macro_event event = {
.type = RATBAG_MACRO_EVENT_INVALID,
.value = 0,
};
char keyname[32];
if (strlen(m) < 2)
goto out;
switch(m[0]) {
case '+':
event.type = RATBAG_MACRO_EVENT_KEY_PRESSED;
snprintf(keyname, sizeof(keyname), "KEY_%s", m + 1);
event.value = libevdev_event_code_from_name(EV_KEY,
keyname);
log_verbose("json: macro: +%s\n", keyname);
break;
case '-':
event.type = RATBAG_MACRO_EVENT_KEY_RELEASED;
snprintf(keyname, sizeof(keyname), "KEY_%s", m + 1);
event.value = libevdev_event_code_from_name(EV_KEY,
keyname);
log_verbose("json: macro: -%s\n", keyname);
break;
case 't':
event.type = RATBAG_MACRO_EVENT_WAIT;
event.value = atoi(m+1);
log_verbose("json: macro: t%d\n", event.value);
break;
default:
parser_error("macro");
break;
}
out:
return event;
}
static void parse_button_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_button *button = data;
if (streq(name, "action_type")) {
const gchar* v = json_object_get_string_member(obj, name);
button->action_type = action_type_lookup(v);
log_verbose("json: action_type: %s\n", v);
} else if (streq(name, "button")) {
gint v = json_object_get_int_member(obj, name);
if (v < 0 || v > 32)
parser_error("button");
button->button = v;
log_verbose("json: button: %d\n", v);
} else if (streq(name, "key")) {
gint v = json_object_get_int_member(obj, name);
if (v < 0 || v > KEY_MAX)
parser_error("key");
button->key = v;
log_verbose("json: key: %d\n", v);
} else if (streq(name, "special")) {
const gchar *v = json_object_get_string_member(obj, name);
button->special = special_lookup(v);
log_verbose("json: special: %s\n", v);
} else if (streq(name, "macro")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(button->macro));
for (size_t s = 0; s < json_array_get_length(a); s++) {
const gchar *v = json_array_get_string_element(a, s);
button->macro[s] = parse_macro(v);
}
} else {
log_error("json: unknown button key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_button(JsonNode *node, struct ratbag_test_button *button)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_button_member, button);
}
static void parse_profile_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_profile *profile = data;
if (streq(name, "name")) {
const char *v = json_object_get_string_member(obj, name);
if (v == NULL)
parser_error("name");
free(profile->name);
profile->name = strdup_safe(v);
log_verbose("name: %s\n", v);
} else if (streq(name, "is_default")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->dflt = v;
log_verbose("json: is_default: %d\n", v);
} else if (streq(name, "is_active")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->active = v;
log_verbose("json: is_active: %d\n", v);
} else if (streq(name, "is_disabled")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->disabled = v;
log_verbose("json: is_disabled: %d\n", v);
} else if (streq(name, "rate")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("rate");
profile->hz = v;
log_verbose("json: rate: %d\n", v);
} else if (streq(name, "report_rates")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(profile->report_rates));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
if (v < 0 || v > 20000)
parser_error("report_rate");
profile->report_rates[s] = v;
}
log_verbose("json: report rates: %d %d %d %d %d\n",
profile->report_rates[0],
profile->report_rates[1],
profile->report_rates[2],
profile->report_rates[3],
profile->report_rates[4]);
} else if (streq(name, "capabilities")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(profile->caps));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
profile->caps[s] = v;
}
log_verbose("json: caps: %d %d %d %d %d...\n",
profile->caps[0],
profile->caps[1],
profile->caps[2],
profile->caps[3],
profile->caps[4]);
} else if (streq(name, "resolutions")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing resolution %d\n", idx);
parse_resolution(l->data, &profile->resolutions[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_resolutions = max(idx, num_resolutions);
} else if (streq(name, "leds")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing LED %d\n", idx);
parse_led(l->data, &profile->leds[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_leds = max(idx, num_leds);
} else if (streq(name, "buttons")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing button %d\n", idx);
parse_button(l->data, &profile->buttons[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_buttons = max(idx, num_buttons);
} else {
log_error("json: unknown profile key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_profile(JsonNode *node,
struct ratbag_test_profile *profile)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_profile_member, profile);
}
/* declared here because this isn't really public API, we just need to
* access it from the tests */
int ratbagd_parse_json(const char *data, struct ratbag_test_device *device)
{
g_autoptr(JsonParser) parser = NULL;
JsonNode *root;
JsonObject *obj;
JsonArray *arr;
int r = -EINVAL;
g_autoptr(GError) error = NULL;
error = 0;
parser = json_parser_new();
if (!json_parser_load_from_data(parser, data, strlen(data), &error)) {
log_error("Failed to load JSON: %s\n", error->message);
r = -EINVAL;
goto out;
}
log_verbose("json: data: %s\n", data);
root = json_parser_get_root(parser);
if (JSON_NODE_TYPE(root) != JSON_NODE_OBJECT)
parser_error("root");
obj = json_node_get_object(root);
arr = json_object_get_array_member(obj, "profiles");
if (!arr)
parser_error("profiles");
/* Our test device is preloaded with sane defaults, let's keep those */
num_resolutions = device->num_resolutions;
num_buttons = device->num_buttons;
num_leds = device->num_leds;
g_autoptr(GList) list = json_array_get_elements(arr);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing profile %d\n", idx);
parse_profile(l->data, &device->profiles[idx]);
if (parse_error != 0)
goto out;;
l = g_list_next(l);
idx++;
}
device->num_profiles = idx;
device->num_resolutions = num_resolutions;
device->num_buttons = num_buttons;
device->num_leds = num_leds;
r = 0;
out:
return r;
}

28
ratbagd/ratbagd-json.h Normal file
View File

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

View File

@@ -30,159 +30,21 @@
#include <systemd/sd-event.h>
#include "libratbag-test.h"
#include "ratbagd-json.h"
/* A pre-setup sane device. Use for sanity testing by toggling the various
* error conditions.
*/
static const struct ratbag_test_device ratbagd_test_device_descr = {
.num_profiles = 4,
.num_resolutions = 3,
.num_buttons = 4,
.num_leds = 3,
.profiles = {
{
.name = NULL,
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 0 },
{ .button_type = RATBAG_BUTTON_TYPE_MIDDLE,
.action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = KEY_3 },
{ .button_type = RATBAG_BUTTON_TYPE_RIGHT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
.special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO,
.macro = {
{ .type = RATBAG_MACRO_EVENT_KEY_PRESSED,
.value = KEY_B },
{ .type = RATBAG_MACRO_EVENT_KEY_RELEASED,
.value = KEY_B },
{ .type = RATBAG_MACRO_EVENT_WAIT,
.value = 300 },
},
}
},
.resolutions = {
{ .xres = 100, .yres = 200,
.dpi_min = 50, .dpi_max = 5000},
{ .xres = 200, .yres = 300, .active = true, .dflt = true },
{ .xres = 300, .yres = 400 },
},
.active = true,
.dflt = false,
.hz = 1000,
.report_rates = {500, 1000},
.leds = {
{
.mode = RATBAG_LED_OFF,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20,
.type = RATBAG_LED_TYPE_LOGO,
},
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20,
.type = RATBAG_LED_TYPE_SIDE,
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.ms = 333,
.brightness = 40,
.type = RATBAG_LED_TYPE_SIDE,
}
},
},
{
.buttons = {
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 4 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 5 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 6 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 7 },
},
.resolutions = {
{ .xres = 1100, .yres = 1200, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 1200, .yres = 1300, .dflt = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 1300, .yres = 1400, .active = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
},
.hz = 2000,
.active = false,
.dflt = true,
.name = "test profile 2",
},
{
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
.special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO,
.macro = {
{ .type = RATBAG_MACRO_EVENT_KEY_PRESSED,
.value = KEY_A },
{ .type = RATBAG_MACRO_EVENT_KEY_RELEASED,
.value = KEY_A },
{ .type = RATBAG_MACRO_EVENT_WAIT,
.value = 150 },
}
},
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 2 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 3 },
},
.resolutions = {
{ .xres = 2100, .yres = 2200, .active = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 2200, .yres = 2300, .dflt = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 2300, .yres = 2400, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
},
.hz = 3000,
.leds = {
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.ms = 333,
.brightness = 40
}
},
.active = false,
.dflt = false,
},
{
.disabled = true,
},
},
.destroyed = NULL,
.destroyed_data = NULL,
};
int ratbagd_reset_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
static int load_test_device(sd_bus_message *m,
struct ratbagd *ctx,
const struct ratbag_test_device *source)
{
static int count;
static struct ratbagd_device *ratbagd_test_device = NULL;
struct ratbagd *ctx = userdata;
struct ratbag_device *device;
int r;
char devicename[64];
if (ratbagd_test_device) {
ratbagd_device_unlink(ratbagd_test_device);
ratbagd_device_free(ratbagd_test_device);
ratbagd_device_unref(ratbagd_test_device);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
@@ -191,9 +53,10 @@ int ratbagd_reset_test_device(sd_bus_message *m,
NULL);
}
device = ratbag_device_new_test_device(ctx->lib_ctx, &ratbagd_test_device_descr);
device = ratbag_device_new_test_device(ctx->lib_ctx, source);
r = ratbagd_device_new(&ratbagd_test_device, ctx, "test_device", device);
snprintf(devicename, sizeof(devicename), "testdevice%d", count++);
r = ratbagd_device_new(&ratbagd_test_device, ctx, devicename, device);
/* the ratbagd_device takes its own reference, drop ours */
ratbag_device_unref(device);
@@ -216,6 +79,55 @@ int ratbagd_reset_test_device(sd_bus_message *m,
return 0;
}
static const struct ratbag_test_device default_device_descr = {
.num_profiles = 1,
.num_resolutions = 1,
.num_buttons = 1,
.num_leds = 0,
.profiles = {
{
.name = NULL,
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 0 },
},
.resolutions = {
{ .xres = 1000, .yres = 1000,
.dpi_min = 1000, .dpi_max = 1000},
},
.disabled = false,
.active = true,
.dflt = true,
.hz = 1000,
.report_rates = {1000},
},
},
.destroyed = NULL,
.destroyed_data = NULL,
};
int ratbagd_load_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd *ctx = userdata;
struct ratbag_test_device td = default_device_descr;
char *data;
int r = 0;
CHECK_CALL(sd_bus_message_read(m, "s", &data));
r = ratbagd_parse_json(data, &td);
if (r != 0) {
log_error("Failed to parse JSON data\n");
} else {
r = load_test_device(m, ctx, &td);
}
return sd_bus_reply_method_return(m, "i", r);
}
#endif
void ratbagd_init_test_device(struct ratbagd *ctx)
@@ -223,7 +135,7 @@ void ratbagd_init_test_device(struct ratbagd *ctx)
#ifdef RATBAG_DEVELOPER_EDITION
setenv("RATBAG_TEST", "1", 0);
ratbagd_reset_test_device(NULL, ctx, NULL);
load_test_device(NULL, ctx, &default_device_descr);
#endif
}

View File

@@ -32,4 +32,7 @@ void ratbagd_init_test_device(struct ratbagd *ctx);
int ratbagd_reset_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error);
int ratbagd_load_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error);
#endif /* RATBAG_DEVELOPER_EDITION */

View File

@@ -3,12 +3,27 @@
ratbagd \- system daemon to introspect and modify configurable mice
.SH SYNOPSIS
.B ratbagd
.RB [ \-\-verbose ]
.RB [ \-\-verbose[=raw]|\-\-quiet|\-\-version|\-\-help]
.SH DESCRIPTION
.B ratbagd
starts the daemon. It shouldn't be invoked directly;
.B ratbagd
is normally started through DBus activation.
.SH OPTIONS
.TP 8
.B \-\-help
Print help and exit
.TP 8
.B \-\-verbose, \-\-verbose=raw
Enable verbose output, optionally including raw messages as sent and
received from the device. This should be use to debug any issues with the
device.
.TP 8
.B \-\-quiet
Disable any output but error messages.
.TP 8
.B \-\-version
Show the version number.
.SH SEE ALSO
.BR ratbagctl (1)
.SH AUTHORS

View File

@@ -166,9 +166,10 @@ static int ratbagd_get_devices(sd_bus *bus,
static const sd_bus_vtable ratbagd_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("APIVersion", "i", 0, offsetof(struct ratbagd, api_version), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Devices", "ao", ratbagd_get_devices, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
#ifdef RATBAG_DEVELOPER_EDITION
SD_BUS_METHOD("ResetTestDevice", "", "u", ratbagd_reset_test_device, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("LoadTestDevice", "s", "i", ratbagd_load_test_device, SD_BUS_VTABLE_UNPRIVILEGED),
#endif /* RATBAG_DEVELOPER_EDITION */
SD_BUS_VTABLE_END,
};
@@ -200,7 +201,7 @@ static void ratbagd_process_device(struct ratbagd *ctx,
/* device was removed, unlink it and destroy our context */
if (device) {
ratbagd_device_unlink(device);
ratbagd_device_free(device);
ratbagd_device_unref(device);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
@@ -283,7 +284,7 @@ static struct ratbagd *ratbagd_free(struct ratbagd *ctx)
RATBAGD_DEVICE_FOREACH_SAFE(device, tmp, ctx) {
ratbagd_device_unlink(device);
ratbagd_device_free(device);
ratbagd_device_unref(device);
}
ctx->bus = sd_bus_flush_close_unref(ctx->bus);
@@ -345,6 +346,7 @@ static int ratbagd_new(struct ratbagd **out)
int r;
ctx = zalloc(sizeof(*ctx));
ctx->api_version = RATBAGD_API_VERSION;
r = sd_event_default(&ctx->event);
if (r < 0)
@@ -521,6 +523,83 @@ static int ratbagd_run(struct ratbagd *ctx)
return sd_event_loop(ctx->event);
}
static bool install_ratbagd_devel_dbus_policy(void)
{
bool rc = true;
#ifdef RATBAG_DEVELOPER_EDITION
_cleanup_close_ int in = -1, out = -1;
int nread, nwrite;
char buf[40960];
int r;
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL;
sd_bus *bus = NULL;
rc = false;
log_verbose("Installing DBus policy file to %s\n", DBUS_POLICY_DST);
in = open(DBUS_POLICY_SRC, O_RDONLY);
if (in == -1) {
log_error("Failed to source policy file: %m\n");
goto out;
}
out = open(DBUS_POLICY_DST, O_CREAT|O_WRONLY, 0644);
if (out == -1) {
log_error("Failed to open destination: %m\n");
goto out;
}
nread = read(in, buf, sizeof(buf));
if (nread < 0) {
log_error("Failed to read policy file: %m\n");
goto out;
}
nwrite = write(out, buf, nread);
if (nread != nwrite) {
log_error("Failed to write policy file: %m\n");
goto out;
}
/* Now poke DBus to reload itself */
r = sd_bus_open_system(&bus);
if (r < 0) {
log_error("Unable to open system bus: %s\n", strerror(-r));
goto out;
}
r = sd_bus_call_method(bus, "org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"ReloadConfig",
&error,
&m,
"");
if (r < 0) {
log_error("Failed to call DBus ReloadConfig: %s\n",
error.message);
goto out;
}
rc = true;
out:
sd_bus_error_free(&error);
sd_bus_message_unref(m);
sd_bus_unref(bus);
#endif
return rc;
}
static void remove_ratbagd_devel_dbus_policy(void)
{
#ifdef RATBAG_DEVELOPER_EDITION
unlink(DBUS_POLICY_DST);
#endif
}
int main(int argc, char *argv[])
{
struct ratbagd *ctx = NULL;
@@ -550,6 +629,9 @@ int main(int argc, char *argv[])
}
}
if (!install_ratbagd_devel_dbus_policy())
goto exit;
r = ratbagd_new(&ctx);
if (r < 0)
goto exit;
@@ -558,6 +640,7 @@ int main(int argc, char *argv[])
r = ratbagd_run(ctx);
remove_ratbagd_devel_dbus_policy();
exit:
ratbagd_free(ctx);

View File

@@ -170,7 +170,8 @@ int ratbagd_device_new(struct ratbagd_device **out,
struct ratbagd *ctx,
const char *sysname,
struct ratbag_device *lib_device);
struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device);
struct ratbagd_device *ratbagd_device_ref(struct ratbagd_device *device);
struct ratbagd_device *ratbagd_device_unref(struct ratbagd_device *device);
const char *ratbagd_device_get_sysname(struct ratbagd_device *device);
const char *ratbagd_device_get_path(struct ratbagd_device *device);
unsigned int ratbagd_device_get_num_buttons(struct ratbagd_device *device);
@@ -181,7 +182,7 @@ bool ratbagd_device_linked(struct ratbagd_device *device);
void ratbagd_device_link(struct ratbagd_device *device);
void ratbagd_device_unlink(struct ratbagd_device *device);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_device *, ratbagd_device_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_device *, ratbagd_device_unref);
struct ratbagd_device *ratbagd_device_lookup(struct ratbagd *ctx,
const char *name);
@@ -217,6 +218,8 @@ struct ratbagd_device *ratbagd_device_next(struct ratbagd_device *device);
*/
struct ratbagd {
int api_version;
sd_event *event;
struct ratbag *lib_ctx;
struct udev_monitor *monitor;

View File

@@ -412,9 +412,12 @@ hidpp10drv_read_profile(struct ratbag_profile *profile)
profile->is_enabled = p.enabled;
if (profile->name)
if (profile->name) {
free(profile->name);
profile->name = strdup_safe((char*)p.name);
profile->name = NULL;
}
if (p.name && p.name[0] != '\0')
profile->name = strdup_safe((char*)p.name);
rc = hidpp10_get_current_profile(hidpp10, &idx);
if (rc == 0 && (unsigned int)idx == profile->index)

View File

@@ -57,6 +57,7 @@
#define HIDPP_CAP_LED_SW_CONTROL_1300 (1 << 7)
#define HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060 (1 << 8)
#define HIDPP_CAP_BATTERY_VOLTAGE_1001 (1 << 9)
#define HIDPP_CAP_RGB_EFFECTS_8071 (1 << 10)
#define HIDPP_HIDDEN_FEATURE (1 << 6)
@@ -338,7 +339,7 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
struct hidpp20_color_led_info info;
int rc;
led_info = &drv_data->led_infos.color_leds[led->index];
led_info = &drv_data->led_infos.color_leds_8070[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
@@ -359,7 +360,7 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
/* pre-filled, only override if unknown */
if (led->type == RATBAG_LED_TYPE_UNKNOWN)
led->type = hidpp20_8070_get_location_mapping(led_info->location);
led->type = hidpp20_led_get_location_mapping(led_info->location);
led->color.red = h_led->color.red;
led->color.green = h_led->color.green;
led->color.blue = h_led->color.blue;
@@ -405,6 +406,81 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
}
}
static void
hidpp20drv_read_led_8071(struct ratbag_led *led, struct hidpp20drv_data* drv_data)
{
struct hidpp20_profile *profile;
struct hidpp20_led *h_led;
struct hidpp20_rgb_device_info device_info;
struct hidpp20_rgb_cluster_info *cluster_info;
int rc;
hidpp20_rgb_effects_get_device_info(drv_data->dev, &device_info);
cluster_info = &drv_data->led_infos.color_leds_8071[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
switch (h_led->mode) {
case HIDPP20_LED_ON:
led->mode = RATBAG_LED_ON;
break;
case HIDPP20_LED_CYCLE:
led->mode = RATBAG_LED_CYCLE;
break;
case HIDPP20_LED_BREATHING:
led->mode = RATBAG_LED_BREATHING;
break;
default:
led->mode = RATBAG_LED_OFF;
break;
}
/* pre-filled, only override if unknown */
if (led->type == RATBAG_LED_TYPE_UNKNOWN)
led->type = hidpp20_led_get_location_mapping(cluster_info->location);
led->color.red = h_led->color.red;
led->color.green = h_led->color.green;
led->color.blue = h_led->color.blue;
led->ms = h_led->period;
led->brightness = h_led->brightness * 255 / 100;
if (device_info.ext_caps & HIDPP20_COLOR_LED_INFO_EXT_CAP_MONOCHROME_ONLY)
led->colordepth = RATBAG_LED_COLORDEPTH_MONOCHROME;
else
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
for (int i = 0; i < cluster_info->num_effects; i++) {
struct hidpp20_rgb_effect_info ei;
rc = hidpp20_rgb_effects_get_effect_info(drv_data->dev,
cluster_info->index,
i, &ei);
if (rc < 0)
break;
switch (ei.effect_id) {
case HIDPP20_COLOR_LED_ZONE_EFFECT_DISABLED:
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_FIXED:
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_CYCLING:
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_WAVE:
case HIDPP20_COLOR_LED_ZONE_EFFECT_BREATHING:
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
break;
default:
log_bug_libratbag(led->profile->device->ratbag,
"%s: Unknown effect id %d\n",
led->profile->device->name,
ei.effect_id);
break;
}
}
}
static void
hidpp20drv_read_led(struct ratbag_led *led)
{
@@ -413,6 +489,8 @@ hidpp20drv_read_led(struct ratbag_led *led)
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
hidpp20drv_read_led_8070(led, drv_data);
else if (drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071)
hidpp20drv_read_led_8071(led, drv_data);
else if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
hidpp20drv_read_led_1300(led, drv_data);
}
@@ -609,8 +687,8 @@ hidpp20drv_update_led_1300(struct ratbag_led *led, struct hidpp20drv_data *data)
}
static int
hidpp20drv_update_led_8070(struct ratbag_led *led, struct ratbag_profile* profile,
struct hidpp20drv_data *drv_data)
hidpp20drv_update_led_8070_8071(struct ratbag_led *led, struct ratbag_profile* profile,
struct hidpp20drv_data *drv_data)
{
struct hidpp20_profile *h_profile;
struct hidpp20_led *h_led;
@@ -652,8 +730,9 @@ hidpp20drv_update_led(struct ratbag_led *led)
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
return hidpp20drv_update_led_8070(led, profile, drv_data);
if ((drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070) |
(drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071))
return hidpp20drv_update_led_8070_8071(led, profile, drv_data);
if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
return hidpp20drv_update_led_1300(led, drv_data);
@@ -971,19 +1050,15 @@ hidpp20drv_read_kbd_reprogrammable_key(struct ratbag_device *device)
}
static int
hidpp20drv_read_color_leds(struct ratbag_device *device)
hidpp20drv_read_color_leds_8070(struct hidpp20drv_data *drv_data)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
int rc;
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return 0;
free(drv_data->led_infos.color_leds);
drv_data->led_infos.color_leds = NULL;
free(drv_data->led_infos.color_leds_8070);
drv_data->led_infos.color_leds_8070 = NULL;
drv_data->num_leds = 0;
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos.color_leds);
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos.color_leds_8070);
if (rc > 0) {
drv_data->num_leds = rc;
@@ -993,6 +1068,38 @@ hidpp20drv_read_color_leds(struct ratbag_device *device)
return rc;
}
static int
hidpp20drv_read_color_leds_8071(struct hidpp20drv_data *drv_data)
{
int rc;
free(drv_data->led_infos.color_leds_8071);
drv_data->led_infos.color_leds_8071 = NULL;
drv_data->num_leds = 0;
rc = hidpp20_rgb_effects_get_cluster_infos(drv_data->dev, &drv_data->led_infos.color_leds_8071);
if (rc > 0) {
drv_data->num_leds = rc;
rc = 0;
}
return rc;
}
static int
hidpp20drv_read_color_leds(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
return hidpp20drv_read_color_leds_8070(drv_data);
else if (drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071)
return hidpp20drv_read_color_leds_8071(drv_data);
return 0;
}
static int
hidpp20drv_read_leds(struct ratbag_device *device)
{
@@ -1206,6 +1313,18 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
device->num_leds = drv_data->num_leds;
break;
}
case HIDPP_PAGE_RGB_EFFECTS: {
log_debug(ratbag, "device has color effects\n");
drv_data->capabilities |= HIDPP_CAP_RGB_EFFECTS_8071;
/* we read the profile once to get the correct number of
* supported leds. */
if (hidpp20drv_read_color_leds(device))
return 0;
device->num_leds = drv_data->num_leds;
break;
}
case HIDPP_PAGE_LED_SW_CONTROL: {
log_debug(ratbag, "device has non-rgb leds\n");
drv_data->capabilities |= HIDPP_CAP_LED_SW_CONTROL_1300;
@@ -1380,7 +1499,8 @@ hidpp20drv_remove(struct ratbag_device *device)
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(drv_data->profiles);
free(drv_data->led_infos.color_leds);
free(drv_data->led_infos.color_leds_8070);
free(drv_data->led_infos.color_leds_8071);
free(drv_data->controls);
free(drv_data->sensors);
if (drv_data->dev)

View File

@@ -267,9 +267,8 @@ roccat_is_ready(struct ratbag_device *device)
uint8_t buf[3] = { 0 };
int rc;
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_CONFIGURE_PROFILE,
buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_CONFIGURE_PROFILE,
buf, sizeof(buf));
if (rc < 0)
return rc;
if (rc != sizeof(buf))
@@ -315,8 +314,8 @@ roccat_current_profile(struct ratbag_device *device)
uint8_t buf[3];
int ret;
ret = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_PROFILE, buf,
sizeof(buf), HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
ret = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_PROFILE, buf,
sizeof(buf));
if (ret < 0)
return ret;
@@ -335,8 +334,8 @@ roccat_set_current_profile(struct ratbag_device *device, unsigned int index)
if (index > ROCCAT_PROFILE_MAX)
return -EINVAL;
ret = ratbag_hidraw_raw_request(device, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
sizeof(buf));
if (ret < 0)
return ret;
@@ -362,8 +361,8 @@ roccat_set_config_profile(struct ratbag_device *device, uint8_t profile, uint8_t
if (profile > ROCCAT_PROFILE_MAX)
return -EINVAL;
ret = ratbag_hidraw_raw_request(device, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
sizeof(buf));
if (ret < 0)
return ret;
@@ -409,9 +408,8 @@ roccat_write_profile(struct ratbag_profile *profile)
*crc = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_PROFILE);
roccat_set_config_profile(device, index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE);
if (rc < ROCCAT_REPORT_SIZE_PROFILE)
return -EIO;
@@ -468,9 +466,8 @@ roccat_read_button(struct ratbag_button *button)
macro = &drv_data->macros[button->profile->index][button->index];
buf = (uint8_t*)macro;
buf[0] = ROCCAT_REPORT_ID_MACRO;
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO);
if (rc != ROCCAT_REPORT_SIZE_MACRO) {
log_error(device->ratbag,
"Unable to retrieve the macro for button %d of profile %d: %s (%d)\n",
@@ -594,9 +591,8 @@ roccat_write_macro(struct ratbag_button *button,
macro->length = count;
macro->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_MACRO);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO);
if (rc < 0)
return rc;
@@ -673,9 +669,8 @@ roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
settings_report->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_SETTINGS);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS);
if (rc < 0)
return rc;
@@ -713,9 +708,8 @@ roccat_read_profile(struct ratbag_profile *profile)
setting_report = &drv_data->settings[profile->index];
buf = (uint8_t*)setting_report;
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_SETTINGS);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS);
if (rc < ROCCAT_REPORT_SIZE_SETTINGS)
return;
@@ -756,9 +750,8 @@ roccat_read_profile(struct ratbag_profile *profile)
buf = drv_data->profiles[profile->index];
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE);
msleep(10);

View File

@@ -701,7 +701,7 @@ construct_cycle_buffer(struct steelseries_led_cycle *cycle,
/* 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);
set_unaligned_le_u16(&buf[spec->duration_idx], duration);
}
static int

View File

@@ -214,7 +214,10 @@ test_read_profile(struct ratbag_profile *profile)
res->is_default = r->dflt;
if (r->dflt)
default_set = true;
ratbag_resolution_set_cap(res, r->caps);
for (size_t j = 0; j < ARRAY_LENGTH(r->caps) && r->caps[j]; j++) {
ratbag_resolution_set_cap(res, r->caps[j]);
}
}
/* special case triggered by the test suite when num_resolutions is 0 */

View File

@@ -234,7 +234,7 @@ hidpp20_1b04_get_physical_mapping_name(uint16_t value)
}
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value)
hidpp20_led_get_location_mapping(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;
@@ -247,7 +247,7 @@ hidpp20_8070_get_location_mapping(uint16_t value)
}
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value)
hidpp20_led_get_location_mapping_name(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;
@@ -322,6 +322,21 @@ hidpp_log(struct hidpp_device *dev,
va_end(args);
}
char *
hidpp_buffer_to_string(const uint8_t *buf, size_t len)
{
size_t dstlen = len * 6;
char *dst = zalloc(dstlen);
unsigned int n = 0;
char *sep = "";
for (unsigned i = 0; i < len; ++i) {
n += snprintf_safe(&dst[n], dstlen - n, "%s%02x", sep, buf[i] & 0xFF);
sep = " ";
}
return dst;
}
void
hidpp_log_buffer(struct hidpp_device *dev,
enum hidpp_log_priority priority,
@@ -329,23 +344,10 @@ hidpp_log_buffer(struct hidpp_device *dev,
uint8_t *buf, size_t len)
{
_cleanup_free_ char *output_buf = NULL;
char *sep = "";
unsigned int i, n;
unsigned int buf_len;
_cleanup_free_ char *bytes = NULL;
buf_len = header ? strlen(header) : 0;
buf_len += len * 3;
buf_len += 1; /* terminating '\0' */
output_buf = zalloc(buf_len);
n = 0;
if (header)
n += snprintf_safe(output_buf, buf_len - n, "%s", header);
for (i = 0; i < len; ++i) {
n += snprintf_safe(&output_buf[n], buf_len - n, "%s%02x", sep, buf[i] & 0xFF);
sep = " ";
}
bytes = hidpp_buffer_to_string(buf, len);
asprintf(&output_buf, "%s %s", header ? header : "", bytes);
hidpp_log(dev, priority, "%s\n", output_buf);
}

View File

@@ -32,6 +32,8 @@
#include <stdarg.h>
#include <stddef.h>
#include "libratbag-util.h"
#define HIDPP_RECEIVER_IDX 0xFF
#define HIDPP_WIRED_DEVICE_IDX 0x00
@@ -118,10 +120,10 @@ enum ratbag_button_type
hidpp20_1b04_get_physical_mapping(uint16_t value);
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value);
hidpp20_led_get_location_mapping(uint16_t value);
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value);
hidpp20_led_get_location_mapping_name(uint16_t value);
const char *
hidpp20_1b04_get_logical_mapping_name(uint16_t value);
@@ -145,6 +147,9 @@ hidpp_log(struct hidpp_device *dev,
...)
__attribute__ ((format (printf, 3, 4)));
char *
hidpp_buffer_to_string(const uint8_t *buf, size_t len);
void
hidpp_log_buffer(struct hidpp_device *dev,
enum hidpp_log_priority priority,
@@ -163,62 +168,30 @@ hidpp_log_buffer(struct hidpp_device *dev,
uint16_t hidpp_crc_ccitt(uint8_t *data, unsigned int length);
static inline uint16_t
hidpp_get_unaligned_be_u16(uint8_t *buf)
{
return (buf[0] << 8) | buf[1];
}
static inline uint16_t
hidpp_be_u16_to_cpu(uint16_t data)
{
return hidpp_get_unaligned_be_u16((uint8_t *)&data);
}
static inline void
hidpp_set_unaligned_be_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value >> 8;
buf[1] = value & 0xFF;
return get_unaligned_be_u16((uint8_t *)&data);
}
static inline uint16_t
hidpp_cpu_to_be_u16(uint16_t data)
{
uint16_t result;
hidpp_set_unaligned_be_u16((uint8_t *)&result, data);
set_unaligned_be_u16((uint8_t *)&result, data);
return result;
}
static inline uint16_t
hidpp_get_unaligned_le_u16(uint8_t *buf)
{
return (buf[1] << 8) | buf[0];
}
static inline uint16_t
hidpp_le_u16_to_cpu(uint16_t data)
{
return hidpp_get_unaligned_le_u16((uint8_t *)&data);
}
static inline void
hidpp_set_unaligned_le_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value & 0xFF;
buf[1] = value >> 8;
return get_unaligned_le_u16((uint8_t *)&data);
}
static inline uint16_t
hidpp_cpu_to_le_u16(uint16_t data)
{
uint16_t result;
hidpp_set_unaligned_le_u16((uint8_t *)&result, data);
set_unaligned_le_u16((uint8_t *)&result, data);
return result;
}
static inline uint32_t
hidpp_get_unaligned_be_u32(uint8_t *buf)
{
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}

View File

@@ -161,7 +161,6 @@ hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev,
return 0;
}
static int
hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
{
@@ -171,6 +170,7 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
int ret;
uint8_t hidpp_err = 0;
int command_size;
_cleanup_free_ char *rxdata = NULL, *txdata = NULL;
switch (msg->msg.report_id) {
case REPORT_ID_SHORT:
@@ -186,9 +186,19 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
/* create the expected header */
expected_header = *msg;
txdata = hidpp_buffer_to_string(&msg->data[4], command_size - 4);
hidpp_log_raw(&dev->base, "hidpp10 tx: %02x | %02x | %02x | %02x | %s\n",
msg->msg.report_id,
msg->msg.device_idx,
msg->msg.sub_id,
msg->msg.address,
txdata);
/* response message length doesn't depend on request length */
#if 0
hidpp_log_buf_raw(&dev->base, " expected_header: ?? ", &expected_header.data[1], 3);
hidpp_log_buf_raw(&dev->base, " expected_error_dev: ", expected_error_dev.data, SHORT_MESSAGE_LENGTH);
#endif
/* Send the message to the Device */
ret = hidpp_write_command(&dev->base, msg->data, command_size);
@@ -238,8 +248,15 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
goto out_err;
}
rxdata = hidpp_buffer_to_string(&read_buffer.data[4], ret - 4);
hidpp_log_raw(&dev->base, "hidpp10 rx: %02x | %02x | %02x | %02x | %s\n",
read_buffer.msg.report_id,
read_buffer.msg.device_idx,
read_buffer.msg.sub_id,
read_buffer.msg.address,
rxdata);
if (!hidpp_err) {
hidpp_log_buf_raw(&dev->base, " received: ", read_buffer.data, ret);
/* copy the answer for the caller */
*msg = read_buffer;
}
@@ -277,7 +294,8 @@ hidpp10_get_hidpp_notifications(struct hidpp10_device *dev,
union hidpp10_message notifications = CMD_HIDPP_NOTIFICATIONS(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching HID++ notifications\n");
hidpp_log_raw(&dev->base, "Fetching HID++ notifications (%#02x)\n",
__CMD_HIDPP_NOTIFICATIONS);
res = hidpp10_request_command(dev, &notifications);
if (res)
@@ -298,7 +316,8 @@ hidpp10_set_hidpp_notifications(struct hidpp10_device *dev,
union hidpp10_message notifications = CMD_HIDPP_NOTIFICATIONS(idx, SET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting HID++ notifications\n");
hidpp_log_raw(&dev->base, "Setting HID++ notifications (%#02x\n",
__CMD_HIDPP_NOTIFICATIONS);
notifications.msg.parameters[0] = reporting_flags & 0xFF;
notifications.msg.parameters[1] = (reporting_flags >> 8) & 0x1F;
@@ -333,7 +352,8 @@ hidpp10_get_individual_features(struct hidpp10_device *dev,
union hidpp10_message features = CMD_ENABLE_INDIVIDUAL_FEATURES(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching individual features\n");
hidpp_log_raw(&dev->base, "Fetching individual features (%#02x)\n",
__CMD_ENABLE_INDIVIDUAL_FEATURES);
res = hidpp10_request_command(dev, &features);
if (res)
@@ -356,7 +376,8 @@ hidpp10_set_individual_features(struct hidpp10_device *dev,
union hidpp10_message mode = CMD_ENABLE_INDIVIDUAL_FEATURES(idx, SET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting individual features\n");
hidpp_log_raw(&dev->base, "Setting individual features (%#02x)\n",
__CMD_ENABLE_INDIVIDUAL_FEATURES);
mode.msg.device_idx = dev->index;
@@ -700,7 +721,7 @@ hidpp10_write_profile_directory(struct hidpp10_device *dev)
}
crc = hidpp_crc_ccitt(bytes, HIDPP10_PAGE_SIZE - 2);
hidpp_set_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2], crc);
set_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2], crc);
res = hidpp10_send_hot_payload(dev,
0x00, 0x0000, /* destination: RAM */
@@ -782,7 +803,8 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, uint8_t *current_profile
unsigned i;
uint8_t type, page, offset;
hidpp_log_raw(&dev->base, "Fetching current profile\n");
hidpp_log_raw(&dev->base, "Fetching current profile (%#02x)\n",
__CMD_PROFILE);
res = hidpp10_request_command(dev, &profile);
if (res)
@@ -829,7 +851,8 @@ hidpp10_set_internal_current_profile(struct hidpp10_device *dev,
union hidpp10_message profile = CMD_PROFILE(idx, SET_REGISTER_REQ);
int8_t page, offset;
hidpp_log_raw(&dev->base, "Setting current profile\n");
hidpp_log_raw(&dev->base, "Setting current profile (%#02x)\n",
__CMD_PROFILE);
profile.msg.parameters[0] = profile_type;
@@ -964,9 +987,9 @@ hidpp10_fill_dpi_modes_16(struct hidpp10_device *dev,
struct _hidpp10_dpi_mode_16 *dpi = &dpi_list[i];
be = (uint8_t*)&dpi->xres;
profile->dpi_modes[i].xres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_be_u16(be));
profile->dpi_modes[i].xres = hidpp10_get_dpi_value(dev, get_unaligned_be_u16(be));
be = (uint8_t*)&dpi->yres;
profile->dpi_modes[i].yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_be_u16(be));
profile->dpi_modes[i].yres = hidpp10_get_dpi_value(dev, get_unaligned_be_u16(be));
profile->dpi_modes[i].led[0] = dpi->led1 == 0x2;
profile->dpi_modes[i].led[1] = dpi->led2 == 0x2;
@@ -988,9 +1011,9 @@ hidpp10_write_dpi_modes_16(struct hidpp10_device *dev,
struct _hidpp10_dpi_mode_16 *dpi = &dpi_list[i];
be = (uint8_t*)&dpi->xres;
hidpp_set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].xres));
set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].xres));
be = (uint8_t*)&dpi->yres;
hidpp_set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].yres));
set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].yres));
dpi->led1 = profile->dpi_modes[i].led[0] ? 0x02 : 0x01;
dpi->led2 = profile->dpi_modes[i].led[1] ? 0x02 : 0x01;
@@ -1532,57 +1555,62 @@ hidpp10_read_profile(struct hidpp10_device *dev, uint8_t number)
}
profile->initialized = 1;
hidpp_log_buf_raw(&dev->base,
"+++++++++++++++++++ Profile data: +++++++++++++++++ \n",
data->data, 78);
hidpp_log_raw(&dev->base,
"+++++++++++++++++++ Profile data: +++++++++++++++++ \n");
for (size_t x = 0; x < 78; x += 8) {
hidpp_log_buf_raw(&dev->base, NULL, &data->data[x], min(8, 78 - x));
}
hidpp_log_raw(&dev->base,
"+++++++++++++++++++ Profile data end +++++++++++++++++ \n");
}
hidpp_log_raw(&dev->base, "Profile %d:\n", number);
for (i = 0; i < 5; i++) {
hidpp_log_raw(&dev->base,
"DPI mode: %dx%d dpi\n",
" DPI mode: %dx%d dpi\n",
profile->dpi_modes[i].xres,
profile->dpi_modes[i].yres);
hidpp_log_raw(&dev->base,
"LED status: 1:%s 2:%s 3:%s 4:%s\n",
" LED status: 1:%s 2:%s 3:%s 4:%s\n",
profile->dpi_modes[i].led[0] ? "on" : "off",
profile->dpi_modes[i].led[1] ? "on" : "off",
profile->dpi_modes[i].led[2] ? "on" : "off",
profile->dpi_modes[i].led[3] ? "on" : "off");
}
hidpp_log_raw(&dev->base, "Angle correction: %d\n", profile->angle_correction);
hidpp_log_raw(&dev->base, "Default DPI mode: %d\n", profile->default_dpi_mode);
hidpp_log_raw(&dev->base, "Refresh rate: %d\n", profile->refresh_rate);
hidpp_log_raw(&dev->base, " Angle correction: %d\n", profile->angle_correction);
hidpp_log_raw(&dev->base, " Default DPI mode: %d\n", profile->default_dpi_mode);
hidpp_log_raw(&dev->base, " Refresh rate: %d\n", profile->refresh_rate);
for (i = 0; i < 13; i++) {
union hidpp10_button *button = &profile->buttons[i];
switch (button->any.type) {
case PROFILE_BUTTON_TYPE_BUTTON:
hidpp_log_raw(&dev->base,
"Button %zd: button %d\n",
" Button %zd: button %d\n",
i,
button->button.button);
break;
case PROFILE_BUTTON_TYPE_KEYS:
hidpp_log_raw(&dev->base,
"Button %zd: key %d modifier %x\n",
" Button %zd: key %d modifier %x\n",
i,
button->keys.key,
button->keys.modifier_flags);
break;
case PROFILE_BUTTON_TYPE_SPECIAL:
hidpp_log_raw(&dev->base,
"Button %zd: special %x\n",
" Button %zd: special %x\n",
i,
button->special.special);
break;
case PROFILE_BUTTON_TYPE_CONSUMER_CONTROL:
hidpp_log_raw(&dev->base,
"Button %zd: consumer: %x\n",
" Button %zd: consumer: %x\n",
i,
button->consumer_control.consumer_control);
break;
case PROFILE_BUTTON_TYPE_DISABLED:
hidpp_log_raw(&dev->base, "Button %zd: disabled\n", i);
hidpp_log_raw(&dev->base, " Button %zd: disabled\n", i);
break;
default:
/* FIXME: this is the page number for the macro,
@@ -1757,7 +1785,7 @@ hidpp10_set_profile(struct hidpp10_device *dev, uint8_t number, struct hidpp10_p
}
crc = hidpp_crc_ccitt(page_data, HIDPP10_PAGE_SIZE - 2);
hidpp_set_unaligned_be_u16(&page_data[HIDPP10_PAGE_SIZE - 2], crc);
set_unaligned_be_u16(&page_data[HIDPP10_PAGE_SIZE - 2], crc);
/*
* writing the data in several steps to prevent shroedinger state
@@ -1851,7 +1879,8 @@ hidpp10_get_led_status(struct hidpp10_device *dev,
uint8_t *status = led_status.msg.parameters;
int res;
hidpp_log_raw(&dev->base, "Fetching LED status\n");
hidpp_log_raw(&dev->base, "Fetching LED status (%#02x)\n",
__CMD_LED_STATUS);
res = hidpp10_request_command(dev, &led_status);
if (res)
@@ -1877,7 +1906,8 @@ hidpp10_set_led_status(struct hidpp10_device *dev,
int res;
int i;
hidpp_log_raw(&dev->base, "Setting LED status\n");
hidpp_log_raw(&dev->base, "Setting LED status (%#02x)\n",
__CMD_LED_STATUS);
for (i = 0; i < 6; i++) {
switch (led[i]) {
@@ -1928,7 +1958,8 @@ hidpp10_get_led_intensity(struct hidpp10_device *dev,
uint8_t *intensity = led_intensity.msg.parameters;
int res;
hidpp_log_raw(&dev->base, "Fetching LED intensity\n");
hidpp_log_raw(&dev->base, "Fetching LED intensity (%#02x)\n",
__CMD_LED_INTENSITY);
res = hidpp10_request_command(dev, &led_intensity);
if (res)
@@ -1953,7 +1984,8 @@ hidpp10_set_led_intensity(struct hidpp10_device *dev,
uint8_t *intensity = led_intensity.msg.parameters;
int res;
hidpp_log_raw(&dev->base, "Setting LED intensity\n");
hidpp_log_raw(&dev->base, "Setting LED intensity (%#02x)\n",
__CMD_LED_INTENSITY);
intensity[0] = led_intensity_in_percent[0]/10 & 0xF;
intensity[0] |= (led_intensity_in_percent[1]/10 & 0xF) << 4;
@@ -1992,7 +2024,8 @@ hidpp10_get_led_color(struct hidpp10_device *dev,
union hidpp10_message led_color = CMD_LED_COLOR(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching LED color\n");
hidpp_log_raw(&dev->base, "Fetching LED color (%#02x)\n",
__CMD_LED_COLOR);
res = hidpp10_request_command(dev, &led_color);
if (res)
@@ -2015,7 +2048,8 @@ hidpp10_set_led_color(struct hidpp10_device *dev,
union hidpp10_message led_color = CMD_LED_COLOR(idx, SET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting LED color\n");
hidpp_log_raw(&dev->base, "Setting LED color (%#02x)\n",
__CMD_LED_COLOR);
led_color.msg.parameters[0] = red;
led_color.msg.parameters[1] = green;
@@ -2048,7 +2082,8 @@ hidpp10_get_optical_sensor_settings(struct hidpp10_device *dev,
union hidpp10_message sensor = CMD_OPTICAL_SENSOR_SETTINGS(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching optical sensor settings\n");
hidpp_log_raw(&dev->base, "Fetching optical sensor settings (%#02x)\n",
__CMD_OPTICAL_SENSOR_SETTINGS);
res = hidpp10_request_command(dev, &sensor);
if (res)
@@ -2086,7 +2121,8 @@ hidpp10_get_current_resolution(struct hidpp10_device *dev,
union hidpp10_message resolution_long = CMD_CURRENT_RESOLUTION(REPORT_ID_SHORT, idx, GET_LONG_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching current resolution\n");
hidpp_log_raw(&dev->base, "Fetching current resolution (%#02x)\n",
__CMD_CURRENT_RESOLUTION);
switch (dev->profile_type) {
case HIDPP10_PROFILE_G9:
@@ -2094,15 +2130,15 @@ hidpp10_get_current_resolution(struct hidpp10_device *dev,
if (res)
return res;
/* resolution is in 50dpi multiples */
*xres = *yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution.data[4]));
*xres = *yres = hidpp10_get_dpi_value(dev, get_unaligned_le_u16(&resolution.data[4]));
break;
default:
res = hidpp10_request_command(dev, &resolution_long);
if (res)
return res;
/* resolution is in 50dpi multiples */
*xres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution_long.data[4]));
*yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution_long.data[6]));
*xres = hidpp10_get_dpi_value(dev, get_unaligned_le_u16(&resolution_long.data[4]));
*yres = hidpp10_get_dpi_value(dev, get_unaligned_le_u16(&resolution_long.data[6]));
}
return 0;
@@ -2118,14 +2154,17 @@ hidpp10_set_current_resolution(struct hidpp10_device *dev,
union hidpp10_message resolution_long = CMD_CURRENT_RESOLUTION(REPORT_ID_LONG, idx, SET_LONG_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting current resolution (%#02x)\n",
__CMD_CURRENT_RESOLUTION);
switch (dev->profile_type) {
case HIDPP10_PROFILE_G9:
resolution.data[4] = hidpp10_get_dpi_mapping(dev, xres);
res = hidpp10_request_command(dev, &resolution);
break;
default:
hidpp_set_unaligned_le_u16(&resolution_long.data[4], hidpp10_get_dpi_mapping(dev, xres));
hidpp_set_unaligned_le_u16(&resolution_long.data[6], hidpp10_get_dpi_mapping(dev, yres));
set_unaligned_le_u16(&resolution_long.data[4], hidpp10_get_dpi_mapping(dev, xres));
set_unaligned_le_u16(&resolution_long.data[6], hidpp10_get_dpi_mapping(dev, yres));
res = hidpp10_request_command(dev, &resolution_long);
break;
@@ -2157,7 +2196,8 @@ hidpp10_get_usb_refresh_rate(struct hidpp10_device *dev,
union hidpp10_message refresh = CMD_USB_REFRESH_RATE(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching USB refresh rate\n");
hidpp_log_raw(&dev->base, "Fetching USB refresh rate (%#02x)\n",
__CMD_USB_REFRESH_RATE);
res = hidpp10_request_command(dev, &refresh);
if (res)
@@ -2178,7 +2218,8 @@ hidpp10_set_usb_refresh_rate(struct hidpp10_device *dev,
unsigned idx = dev->index;
union hidpp10_message refresh = CMD_USB_REFRESH_RATE(idx, GET_REGISTER_REQ);
hidpp_log_raw(&dev->base, "Setting USB refresh rate\n");
hidpp_log_raw(&dev->base, "Setting USB refresh rate (%#02x)\n",
__CMD_USB_REFRESH_RATE);
refresh.msg.parameters[0] = 1000/rate;
@@ -2483,7 +2524,7 @@ hidpp10_read_page(struct hidpp10_device *dev, uint8_t page,
}
crc = hidpp_crc_ccitt(bytes, HIDPP10_PAGE_SIZE - 2);
read_crc = hidpp_get_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2]);
read_crc = get_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2]);
if (crc != read_crc)
return -EILSEQ; /* return illegal sequence */
@@ -2573,7 +2614,7 @@ hidpp10_get_pairing_information(struct hidpp10_device *dev,
return -1;
*report_interval = pairing_information.msg.string[2];
*wpid = hidpp_get_unaligned_be_u16(&pairing_information.msg.string[3]);
*wpid = get_unaligned_be_u16(&pairing_information.msg.string[3]);
*device_type = pairing_information.msg.string[7];
return 0;
@@ -2613,7 +2654,7 @@ hidpp10_get_extended_pairing_information(struct hidpp10_device *dev,
if (res)
return -1;
*serial = hidpp_get_unaligned_be_u32(&info.msg.string[1]);
*serial = get_unaligned_be_u32(&info.msg.string[1]);
return 0;
}
@@ -2665,7 +2706,7 @@ hidpp10_get_firmare_information(struct hidpp10_device *dev,
res = hidpp10_request_command(dev, &build_information);
if (res)
return res;
build = hidpp_get_unaligned_be_u16(&build_information.msg.string[1]);
build = get_unaligned_be_u16(&build_information.msg.string[1]);
*major_out = maj;
*minor_out = min;

View File

@@ -66,6 +66,7 @@ hidpp20_feature_get_name(uint16_t feature)
CASE_RETURN_STRING(HIDPP_PAGE_ADJUSTABLE_DPI);
CASE_RETURN_STRING(HIDPP_PAGE_ADJUSTABLE_REPORT_RATE);
CASE_RETURN_STRING(HIDPP_PAGE_COLOR_LED_EFFECTS);
CASE_RETURN_STRING(HIDPP_PAGE_RGB_EFFECTS);
CASE_RETURN_STRING(HIDPP_PAGE_ONBOARD_PROFILES);
CASE_RETURN_STRING(HIDPP_PAGE_MOUSE_BUTTON_SPY);
default:
@@ -245,7 +246,7 @@ hidpp_root_get_feature(struct hidpp20_device *device,
.msg.address = CMD_ROOT_GET_FEATURE,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], feature);
set_unaligned_be_u16(&msg.msg.parameters[0], feature);
rc = hidpp20_request_command(device, &msg);
if (rc)
@@ -333,7 +334,7 @@ hidpp20_feature_set_get_feature_id(struct hidpp20_device *device,
if (rc)
return rc;
*feature = hidpp_get_unaligned_be_u16(msg.msg.parameters);
*feature = get_unaligned_be_u16(msg.msg.parameters);
*type = msg.msg.parameters[2];
return 0;
@@ -432,10 +433,11 @@ hidpp20_batterylevel_get_battery_level(struct hidpp20_device *device,
}
/* -------------------------------------------------------------------------- */
/* 0x1000: Battery level status */
/* 0x1001: Battery voltage */
/* -------------------------------------------------------------------------- */
#define CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE 0x00
#define CMD_BATTERY_VOLTAGE_GET_SHOW_BATTERY_STATUS 0x10
int
hidpp20_batteryvoltage_get_battery_voltage(struct hidpp20_device *device,
@@ -460,7 +462,7 @@ hidpp20_batteryvoltage_get_battery_voltage(struct hidpp20_device *device,
if (rc)
return rc;
*voltage = (msg.msg.parameters[0] << 8) + msg.msg.parameters[1];
*voltage = get_unaligned_be_u16(msg.msg.parameters);
return msg.msg.parameters[2];
}
@@ -720,10 +722,10 @@ int hidpp20_led_sw_control_set_led_state(struct hidpp20_device* device,
return -EINVAL;
msg.msg.parameters[0] = state->index;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[1], state->mode);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[3], state->blink.index);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[5], state->blink.on_time);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[7], state->blink.off_time);
set_unaligned_be_u16(&msg.msg.parameters[1], state->mode);
set_unaligned_be_u16(&msg.msg.parameters[3], state->blink.index);
set_unaligned_be_u16(&msg.msg.parameters[5], state->blink.on_time);
set_unaligned_be_u16(&msg.msg.parameters[7], state->blink.off_time);
rc = hidpp20_request_command(device, &msg);
@@ -777,8 +779,8 @@ hidpp20_kbd_reprogrammable_keys_get_info(struct hidpp20_device *device,
if (rc)
return rc;
control->control_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[2]);
control->control_id = get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
control->flags = msg.msg.parameters[4];
return 0;
@@ -941,7 +943,7 @@ hidpp20_color_led_effects_get_zone_infos(struct hidpp20_device *device,
"led_info %d: location: %d type %s num_effects: %d persistency_caps: 0x%02x\n",
info->index,
info->location,
hidpp20_8070_get_location_mapping_name(info->location),
hidpp20_led_get_location_mapping_name(info->location),
info->num_effects,
info->persistency_caps);
}
@@ -983,9 +985,179 @@ hidpp20_color_led_effect_get_zone_effect_info(struct hidpp20_device *device,
info->zone_index = msg.msg.parameters[0];
info->zone_effect_index = msg.msg.parameters[1];
info->effect_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[2]);
info->effect_caps = hidpp_get_unaligned_be_u16(&msg.msg.parameters[4]);
info->effect_period = hidpp_get_unaligned_be_u16(&msg.msg.parameters[6]);
info->effect_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->effect_caps = get_unaligned_be_u16(&msg.msg.parameters[4]);
info->effect_period = get_unaligned_be_u16(&msg.msg.parameters[6]);
return 0;
}
/* -------------------------------------------------------------------------- */
/* 0x8071: RGB Effects */
/* -------------------------------------------------------------------------- */
#define CMD_RGB_EFFECTS_GET_INFO 0x00
#define CMD_RGB_EFFECTS_SET_RGB_CLUSTER_EFFECT 0x10
#define CMD_RGB_EFFECTS_SET_MULTI_LED_RGB_CLUSTER_PATTERN 0x20
#define CMD_RGB_EFFECTS_MANAGE_NV_CONFIG 0x30
#define CMD_RGB_EFFECTS_MANAGE_RGB_LED_BIN_INFO 0x40
#define CMD_RGB_EFFECTS_MANAGE_SW_CONTROL 0x50
#define CMD_RGB_EFFECTS_SET_EFFECT_SYNC_CORRECTION 0x60
#define CMD_RGB_EFFECTS_MANAGE_RGB_POWER_MODE_CONFIG 0x70
#define CMD_RGB_EFFECTS_MANAGE_RGB_POWER_MODE 0x80
int
hidpp20_rgb_effects_get_device_info(struct hidpp20_device *device,
struct hidpp20_rgb_device_info *info)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[1] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->cluster_index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->cluster_count = msg.msg.parameters[2];
info->nv_caps = get_unaligned_be_u16(&msg.msg.parameters[3]);
info->ext_caps = get_unaligned_be_u16(&msg.msg.parameters[4]);
return 0;
}
int
hidpp20_rgb_effects_get_cluster_info(struct hidpp20_device *device,
uint8_t cluster_index,
struct hidpp20_rgb_cluster_info *info)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = cluster_index,
.msg.parameters[1] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->location = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->num_effects = get_unaligned_be_u16(&msg.msg.parameters[4]);
info->persistency_caps = get_unaligned_be_u16(&msg.msg.parameters[6]);
return 0;
}
int
hidpp20_rgb_effects_get_cluster_infos(struct hidpp20_device *device,
struct hidpp20_rgb_cluster_info **infos_list)
{
struct hidpp20_rgb_cluster_info *i_list, *info;
struct hidpp20_rgb_device_info device_info = {0};
uint8_t num_infos;
unsigned i;
int rc;
rc = hidpp20_rgb_effects_get_device_info(device, &device_info);
if (rc < 0)
return rc;
num_infos = device_info.cluster_count;
if (num_infos == 0) {
*infos_list = NULL;
return 0;
}
i_list = zalloc(num_infos * sizeof(struct hidpp20_rgb_cluster_info));
for (i = 0; i < num_infos; i++) {
info = &i_list[i];
info->index = i;
rc = hidpp20_rgb_effects_get_cluster_info(device, i, info);
if (rc)
goto err;
hidpp_log_raw(&device->base,
"cluster_info %d: location: %d type %s num_effects: %d persistency_caps: 0x%02x\n",
info->index,
info->location,
hidpp20_led_get_location_mapping_name(info->location),
info->num_effects,
info->persistency_caps);
}
*infos_list = i_list;
return num_infos;
err:
free(i_list);
return rc;
}
int
hidpp20_rgb_effects_get_effect_info(struct hidpp20_device *device,
uint8_t cluster_index,
uint8_t effect_index,
struct hidpp20_rgb_effect_info *info)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = cluster_index,
.msg.parameters[1] = effect_index,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->cluster_index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->effect_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->capabilities = get_unaligned_be_u16(&msg.msg.parameters[4]);
info->effect_period = get_unaligned_be_u16(&msg.msg.parameters[6]);
return 0;
}
@@ -1035,8 +1207,8 @@ hidpp20_special_keys_buttons_get_info(struct hidpp20_device *device,
if (rc)
return rc;
control->control_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[2]);
control->control_id = get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
control->flags = msg.msg.parameters[4];
control->position = msg.msg.parameters[5];
control->group = msg.msg.parameters[6];
@@ -1060,13 +1232,13 @@ hidpp20_special_keys_buttons_get_reporting(struct hidpp20_device *device,
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_REPORTING,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
control->reporting.remapped = hidpp_get_unaligned_be_u16(&msg.msg.parameters[3]);
control->reporting.remapped = get_unaligned_be_u16(&msg.msg.parameters[3]);
control->reporting.raw_XY = !!(msg.msg.parameters[2] & 0x10);
control->reporting.persist = !!(msg.msg.parameters[2] & 0x04);
control->reporting.divert = !!(msg.msg.parameters[2] & 0x01);
@@ -1160,8 +1332,8 @@ hidpp20_special_key_mouse_set_control(struct hidpp20_device *device,
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_SET_REPORTING,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[3], control->reporting.remapped);
set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
set_unaligned_be_u16(&msg.msg.parameters[3], control->reporting.remapped);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_SPECIAL_KEYS_BUTTONS);
@@ -1212,7 +1384,7 @@ hidpp20_mousepointer_get_mousepointer_info(struct hidpp20_device *device,
if (rc)
return rc;
*resolution = hidpp_get_unaligned_be_u16(msg.msg.parameters);
*resolution = get_unaligned_be_u16(msg.msg.parameters);
*flags = msg.msg.parameters[2];
return 0;
@@ -1268,8 +1440,8 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
sensor->index = msg.msg.parameters[0];
while (i < LONG_MESSAGE_LENGTH - 4U &&
hidpp_get_unaligned_be_u16(&msg.msg.parameters[i]) != 0) {
uint16_t value = hidpp_get_unaligned_be_u16(&msg.msg.parameters[i]);
get_unaligned_be_u16(&msg.msg.parameters[i]) != 0) {
uint16_t value = get_unaligned_be_u16(&msg.msg.parameters[i]);
if (value > 0xe000) {
sensor->dpi_steps = value - 0xe000;
@@ -1304,8 +1476,8 @@ hidpp20_adjustable_dpi_get_dpi(struct hidpp20_device *device,
if (rc)
return rc;
sensor->dpi = hidpp_get_unaligned_be_u16(&msg.msg.parameters[1]);
sensor->default_dpi = hidpp_get_unaligned_be_u16(&msg.msg.parameters[3]);
sensor->dpi = get_unaligned_be_u16(&msg.msg.parameters[1]);
sensor->default_dpi = get_unaligned_be_u16(&msg.msg.parameters[3]);
return 0;
}
@@ -1379,7 +1551,7 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[1], dpi);
set_unaligned_be_u16(&msg.msg.parameters[1], dpi);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ADJUSTABLE_DPI);
@@ -1392,7 +1564,7 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
if (rc)
return rc;
returned_parameters = hidpp_get_unaligned_be_u16(&msg.msg.parameters[1]);
returned_parameters = get_unaligned_be_u16(&msg.msg.parameters[1]);
/* version 0 of the protocol does not echo the parameters */
if (returned_parameters != dpi && returned_parameters)
@@ -1462,6 +1634,7 @@ int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *d
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G402 0x01
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303 0x02
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900 0x03
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G903_HERO 0x04
#define HIDPP20_ONBOARD_PROFILES_MACRO_TYPE_G402 0x01
#define HIDPP20_USER_PROFILES_G402 0x0000
@@ -1480,7 +1653,9 @@ union hidpp20_internal_profile {
struct hidpp20_color profile_color;
uint8_t power_mode;
uint8_t angle_snapping;
uint8_t reserved[14];
uint8_t reserved[10];
uint16_t powersave_timeout;
uint16_t poweroff_timeout;
union hidpp20_button_binding buttons[16];
union hidpp20_button_binding alternate_buttons[16];
union {
@@ -1488,7 +1663,8 @@ union hidpp20_internal_profile {
uint8_t raw[16 * 3];
} name;
struct hidpp20_internal_led leds[2]; /* G303, g502, g900 only */
uint8_t free[24];
struct hidpp20_internal_led alt_leds[2];
uint8_t free[2];
uint16_t crc;
} __attribute__((packed)) profile;
};
@@ -1540,7 +1716,7 @@ hidpp20_onboard_profiles_read_sector(struct hidpp20_device *device,
.msg.address = CMD_ONBOARD_PROFILES_MEMORY_READ,
};
hidpp_log_debug(device, "Reading sector 0x%04x\n", sector);
hidpp_log_debug(&device->base, "Reading sector 0x%04x\n", sector);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
@@ -1548,7 +1724,7 @@ hidpp20_onboard_profiles_read_sector(struct hidpp20_device *device,
return -ENOTSUP;
msg.msg.sub_id = feature_index;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], sector);
set_unaligned_be_u16(&msg.msg.parameters[0], sector);
count = sector_size;
@@ -1559,7 +1735,7 @@ hidpp20_onboard_profiles_read_sector(struct hidpp20_device *device,
* less than 16 bytes to read we need to read from sector_size - 16
*/
offset = (sector_size - offset < 16) ? sector_size - 16 : offset;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[2], offset);
set_unaligned_be_u16(&msg.msg.parameters[2], offset);
buf = msg;
rc = hidpp20_request_command(device, &buf);
if (rc)
@@ -1587,10 +1763,10 @@ hidpp20_onboard_profiles_is_sector_valid(struct hidpp20_device *device,
uint16_t crc, read_crc;
crc = hidpp_crc_ccitt(data, sector_size - 2);
read_crc = hidpp_get_unaligned_be_u16(&data[sector_size - 2]);
read_crc = get_unaligned_be_u16(&data[sector_size - 2]);
if (crc != read_crc)
hidpp_log_debug(device, "Invalid CRC (%04x != %04x)\n", read_crc, crc);
hidpp_log_debug(&device->base, "Invalid CRC (%04x != %04x)\n", read_crc, crc);
return crc == read_crc;
}
@@ -1610,9 +1786,9 @@ hidpp20_onboard_profiles_write_start(struct hidpp20_device *device,
.msg.address = CMD_ONBOARD_PROFILES_MEMORY_ADDR_WRITE,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], sector);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[2], sub_address);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[4], count);
set_unaligned_be_u16(&msg.msg.parameters[0], sector);
set_unaligned_be_u16(&msg.msg.parameters[2], sub_address);
set_unaligned_be_u16(&msg.msg.parameters[4], count);
rc = hidpp20_request_command(device, &msg);
if (rc)
@@ -1680,7 +1856,7 @@ hidpp20_onboard_profiles_write_sector(struct hidpp20_device *device,
if (write_crc) {
crc = hidpp_crc_ccitt(data, sector_size - 2);
hidpp_set_unaligned_be_u16(&data[sector_size - 2], crc);
set_unaligned_be_u16(&data[sector_size - 2], crc);
}
rc = hidpp20_onboard_profiles_write_start(device,
@@ -1877,7 +2053,8 @@ hidpp20_onboard_profiles_validate(struct hidpp20_device *device,
if ((info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G402) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900)) {
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G903_HERO)) {
hidpp_log_error(&device->base,
"Profile layout not supported: 0x%02x.\n",
info->profile_format_id);
@@ -2371,12 +2548,18 @@ hidpp20_onboard_profiles_parse_profile(struct hidpp20_device *device,
profile->default_dpi = pdata->profile.default_dpi;
profile->switched_dpi = pdata->profile.switched_dpi;
profile->powersave_timeout = pdata->profile.powersave_timeout;
profile->poweroff_timeout = pdata->profile.poweroff_timeout;
for (i = 0; i < 5; i++) {
profile->dpi[i] = hidpp_get_unaligned_le_u16(&data[2 * i + 3]);
profile->dpi[i] = get_unaligned_le_u16(&data[2 * i + 3]);
}
for (i = 0; i < profiles_list->num_leds; i++)
{
hidpp20_onboard_profiles_read_led(&profile->leds[i], pdata->profile.leds[i]);
hidpp20_onboard_profiles_read_led(&profile->alt_leds[i], pdata->profile.alt_leds[i]);
}
hidpp20_buttons_to_cpu(device, profiles_list, profile, pdata->profile.buttons, profiles_list->num_buttons);
@@ -2429,7 +2612,7 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
for (i = 0; i < profiles->num_profiles; i++) {
uint8_t *d = data + 4 * i;
addr = hidpp_get_unaligned_be_u16(d);
addr = get_unaligned_be_u16(d);
if(addr == HIDPP20_PROFILE_DIR_END)
break;
@@ -2447,14 +2630,14 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
profiles->profiles[i].enabled = !!d[HIDPP20_PROFILE_DIR_ENABLED];
}
} else {
hidpp_log_debug(device, "Profile directory has an invalid CRC... Reading ROM profiles.\n");
hidpp_log_debug(&device->base, "Profile directory has an invalid CRC... Reading ROM profiles.\n");
read_userdata = false;
}
for (i = 0; i < profiles->num_profiles; i++) {
if (read_userdata) {
hidpp_log_debug(device, "Parsing profile %u\n", i);
hidpp_log_debug(&device->base, "Parsing profile %u\n", i);
rc = hidpp20_onboard_profiles_parse_profile(device,
profiles,
i,
@@ -2462,7 +2645,7 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
/* on fail to read the user profile fallback to the default profile */
if (rc)
hidpp_log_debug("Profile %u is bad. Falling back to the ROM settings.\n", i);
hidpp_log_debug(&device->base, "Profile %u is bad. Falling back to the ROM settings.\n", i);
else
continue;
}
@@ -2551,12 +2734,19 @@ hidpp20_onboard_profiles_write_profile(struct hidpp20_device *device,
pdata->profile.default_dpi = profile->default_dpi;
pdata->profile.switched_dpi = profile->switched_dpi;
pdata->profile.powersave_timeout = profile->powersave_timeout;
pdata->profile.poweroff_timeout = profile->poweroff_timeout;
for (i = 0; i < 5; i++) {
pdata->profile.dpi[i] = hidpp_cpu_to_le_u16(profile->dpi[i]);
}
for (i = 0; i < profiles_list->num_leds; i++)
{
hidpp20_onboard_profiles_write_led(&pdata->profile.leds[i], &profile->leds[i]);
/* we write the current led instead of the stored value */
hidpp20_onboard_profiles_write_led(&pdata->profile.alt_leds[i], &profile->leds[i]);
}
hidpp20_buttons_from_cpu(profile, pdata->profile.buttons, profiles_list->num_buttons);

View File

@@ -458,6 +458,31 @@ hidpp20_color_led_effect_get_zone_effect_info(struct hidpp20_device *device,
uint8_t zone_effect_index,
struct hidpp20_color_led_zone_effect_info *info);
struct hidpp20_rgb_device_info;
int
hidpp20_rgb_effects_get_device_info(struct hidpp20_device *device,
struct hidpp20_rgb_device_info *info);
struct hidpp20_rgb_cluster_info;
int
hidpp20_rgb_effects_get_cluster_info(struct hidpp20_device *device,
uint8_t cluster_index,
struct hidpp20_rgb_cluster_info *info);
int
hidpp20_rgb_effects_get_cluster_infos(struct hidpp20_device *device,
struct hidpp20_rgb_cluster_info **infos_list);
struct hidpp20_rgb_effect_info;
int
hidpp20_rgb_effects_get_effect_info(struct hidpp20_device *device,
uint8_t cluster_index,
uint8_t effect_index,
struct hidpp20_rgb_effect_info *info);
enum hidpp20_color_led_location {
HIDPP20_COLOR_LED_LOCATION_UNDEFINED = 0,
HIDPP20_COLOR_LED_LOCATION_PRIMARY,
@@ -491,8 +516,30 @@ enum hidpp20_color_led_zone_effect {
HIDPP20_COLOR_LED_ZONE_EFFECT_BOOT_UP = 8,
HIDPP20_COLOR_LED_ZONE_EFFECT_DEMO_MODE = 8,
HIDPP20_COLOR_LED_ZONE_EFFECT_BREATHING = 10,
HIDPP20_COLOR_LED_ZONE_EFFECT_RIPPLE = 11,
HIDPP20_COLOR_LED_ZONE_EFFECT_CUSTOM = 12,
};
/* -------------------------------------------------------------------------- */
/* 0x8071 - RGB Effects */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_RGB_EFFECTS 0x8071
#define HIDPP20_RGB_EFFECTS_INDEX_ALL 0xFF
/* type of info */
#define HIDPP20_RGB_EFFECTS_TOI_GENERAL 0x00
#define HIDPP20_RGB_EFFECTS_TOI_EFFECT 0x01
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_STATE 0x00
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_DEFAULTS 0x01
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_UUID_0_10 0x02
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_UUID_11_16 0x03
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_0_10 0x04
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_11_21 0x05
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_21_31 0x06
/* -------------------------------------------------------------------------- */
/* 0x8100 - Onboard Profiles */
/* -------------------------------------------------------------------------- */
@@ -578,11 +625,6 @@ struct hidpp20_color_led_zone_info {
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_info) == 5, "Invalid size");
union hidpp20_generic_led_zone_info {
struct hidpp20_led_sw_ctrl_led_info* leds;
struct hidpp20_color_led_zone_info* color_leds;
};
struct hidpp20_color_led_zone_effect_info {
uint8_t zone_index;
uint8_t zone_effect_index;
@@ -592,6 +634,41 @@ struct hidpp20_color_led_zone_effect_info {
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_effect_info) == 8, "Invalid size");
struct hidpp20_rgb_device_info {
uint8_t cluster_index;
uint8_t effect_index;
uint8_t cluster_count;
/* we don't care about NV capabilities for libratbag, they just
* indicate sale demo effects */
uint16_t nv_caps;
uint16_t ext_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_device_info) == 7, "Invalid size");
struct hidpp20_rgb_cluster_info {
uint8_t index;
uint8_t effect_index;
uint16_t location;
uint16_t num_effects;
uint16_t persistency_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_cluster_info) == 8, "Invalid size");
struct hidpp20_rgb_effect_info {
uint8_t cluster_index;
uint8_t effect_index;
uint16_t effect_id;
uint16_t capabilities;
uint16_t effect_period;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_effect_info) == 8, "Invalid size");
union hidpp20_generic_led_zone_info {
struct hidpp20_led_sw_ctrl_led_info* leds;
struct hidpp20_color_led_zone_info* color_leds_8070;
struct hidpp20_rgb_cluster_info* color_leds_8071;
};
struct hidpp20_color {
uint8_t red;
uint8_t green;
@@ -692,6 +769,8 @@ struct hidpp20_profile {
uint16_t address;
uint8_t enabled;
char name[16 * 3];
uint16_t powersave_timeout;
uint16_t poweroff_timeout;
unsigned report_rate;
unsigned default_dpi;
unsigned switched_dpi;
@@ -700,6 +779,7 @@ struct hidpp20_profile {
union hidpp20_button_binding buttons[32];
union hidpp20_macro_data *macros[32];
struct hidpp20_led leds[HIDPP20_LED_COUNT];
struct hidpp20_led alt_leds[HIDPP20_LED_COUNT];
};
struct hidpp20_onboard_profiles_info {

View File

@@ -1239,6 +1239,8 @@ ratbag_hidraw_parse_report_descriptor(struct ratbag_device *device)
if (rc < 0)
return rc;
log_debug(device->ratbag, "Parsing HID report descriptor\n");
i = 0;
usage_page = 0;
usage = 0;
@@ -1260,7 +1262,7 @@ ratbag_hidraw_parse_report_descriptor(struct ratbag_device *device)
switch (hid) {
case HID_REPORT_ID:
if (hidraw->reports) {
log_debug(device->ratbag, "HID report ID %02x\n", content);
log_debug(device->ratbag, "- HID report ID %02x\n", content);
hidraw->reports[hidraw->num_reports].report_id = content;
hidraw->reports[hidraw->num_reports].usage_page = usage_page;
hidraw->reports[hidraw->num_reports].usage = usage;
@@ -1586,6 +1588,23 @@ ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char reportnum,
return -EINVAL;
}
int
ratbag_hidraw_get_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len)
{
return ratbag_hidraw_raw_request(device, reportnum, buf, len,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
}
int
ratbag_hidraw_set_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len)
{
return ratbag_hidraw_raw_request(device, reportnum, buf, len,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
}
int
ratbag_hidraw_output_report(struct ratbag_device *device, uint8_t *buf, size_t len)
{

View File

@@ -101,7 +101,7 @@ void ratbag_close_hidraw_index(struct ratbag_device *device, int idx);
*
* @param device the ratbag device
* @param reportnum report ID
* @param buf in/out data to transfer
* @param buf[in/out] data to transfer
* @param len length of buf
* @param rtype HID report type
* @param reqtype HID_REQ_GET_REPORT or HID_REQ_SET_REPORT
@@ -114,6 +114,41 @@ int ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char report
uint8_t *buf, size_t len, unsigned char rtype,
int reqtype);
/**
* Get feature report from device.
*
* This call sends a HID_REQ_GET_REPORT to the device for the given feature
* and returns the data in the caller-allocated buffer buf of size len.
*
* @param device the ratbag device
* @param reportnum report ID
* @param buf[out] data returned from device
* @param len length of buf
*
* @return count of data transfered, or a negative errno on error
*
* Convenience wrapper around ratbag_hidraw_raw_request()
*/
int ratbag_hidraw_get_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len);
/**
* Set feature report on device.
*
* This call sends a HID_REQ_SET_REPORT to the device for the given feature
* and the buffer buf of size len.
*
* @param device the ratbag device
* @param reportnum report ID
* @param buf[in] data returned from device
* @param len length of buf
*
* @return count of data transfered, or a negative errno on error
*
* Convenience wrapper around ratbag_hidraw_raw_request()
*/
int ratbag_hidraw_set_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len);
/**
* Send output report to device
*

View File

@@ -571,7 +571,7 @@ ratbag_resolution_set_cap(struct ratbag_resolution *res,
{
assert(cap <= RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
res->capabilities = (1 << cap);
res->capabilities |= (1 << cap);
}
static inline void

View File

@@ -53,7 +53,7 @@ struct ratbag_test_resolution {
int xres, yres;
bool active;
bool dflt;
uint32_t caps;
uint32_t caps[10];
int dpi_min, dpi_max;
};
@@ -73,7 +73,7 @@ struct ratbag_test_led {
};
struct ratbag_test_profile {
const char *name;
char *name;
struct ratbag_test_button buttons[RATBAG_TEST_MAX_BUTTONS];
struct ratbag_test_resolution resolutions[RATBAG_TEST_MAX_RESOLUTIONS];
struct ratbag_test_led leds[RATBAG_TEST_MAX_LEDS];

View File

@@ -328,3 +328,35 @@ err:
dpi_list_free(list);
return NULL;
}
static inline uint16_t
get_unaligned_be_u16(uint8_t *buf)
{
return (buf[0] << 8) | buf[1];
}
static inline void
set_unaligned_be_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value >> 8;
buf[1] = value & 0xFF;
}
static inline uint16_t
get_unaligned_le_u16(uint8_t *buf)
{
return (buf[1] << 8) | buf[0];
}
static inline void
set_unaligned_le_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value & 0xFF;
buf[1] = value >> 8;
}
static inline uint32_t
get_unaligned_be_u32(uint8_t *buf)
{
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}

View File

@@ -628,7 +628,7 @@ ratbag_create_button(struct ratbag_profile *profile, unsigned int index)
button->index = index;
button->type = RATBAG_BUTTON_TYPE_UNKNOWN;
list_insert(&profile->buttons, &button->link);
list_append(&profile->buttons, &button->link);
return button;
}
@@ -648,7 +648,7 @@ ratbag_create_led(struct ratbag_profile *profile, unsigned int index)
if (device->data)
led->type = ratbag_device_data_get_led_type(device->data, led->index);
list_insert(&profile->leds, &led->link);
list_append(&profile->leds, &led->link);
return led;
}
@@ -673,7 +673,7 @@ ratbag_create_resolution(struct ratbag_profile *profile, int index)
res->profile = profile;
res->index = index;
list_insert(&profile->resolutions, &res->link);
list_append(&profile->resolutions, &res->link);
profile->num_resolutions++;
}
@@ -697,7 +697,7 @@ ratbag_create_profile(struct ratbag_device *device,
profile->is_enabled = true;
profile->name = NULL;
list_insert(&device->profiles, &profile->link);
list_append(&device->profiles, &profile->link);
list_init(&profile->buttons);
list_init(&profile->leds);
list_init(&profile->resolutions);

View File

@@ -1,10 +0,0 @@
#!/bin/sh
GIT_DIR="$MESON_SOURCE_ROOT/.git"
export GIT_DIR
git log --format="format:%h" -1 2>/dev/null
if test $? -ne 0; then
echo "unknown"
fi

View File

@@ -216,11 +216,11 @@ class Ratbagd(object):
def get_capabilities(type, object):
capabilities = []
for k in libratbag.__dict__.keys():
if k.startswith("RATBAG_{}_CAP_".format(type.upper())) and "CAP_NONE" not in k:
cap = getattr(libratbag, k)
func = getattr(libratbag, "ratbag_{}_has_capability".format(type.lower()))
if func(object, cap):
capabilities.append(cap)
if k.startswith("RATBAG_{}_CAP_".format(type.upper())) and "CAP_NONE" not in k:
cap = getattr(libratbag, k)
func = getattr(libratbag, "ratbag_{}_has_capability".format(type.lower()))
if func(object, cap):
capabilities.append(cap)
return capabilities

View File

@@ -25,6 +25,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
import argparse
import os
import time
import toolbox
@@ -33,11 +34,13 @@ import sys
from ratbagctl import RatbagErrorCapability # NOQA
def main(argv):
def main(argv, start_ratbagd=True):
if not os.geteuid() == 0:
sys.exit('Script must be run as root')
# replaced by meson
os.environ['LIBRATBAG_DATA_DIR'] = '@LIBRATBAG_DATA_DIR@'
os.environ['RATBAG_TEST'] = "1"
if not argv:
argv = ["list"]
@@ -52,9 +55,10 @@ def main(argv):
parser.print_help()
return
ratbagd_process = toolbox.start_ratbagd(verbosity=cmd.verbose)
if ratbagd_process is None:
sys.exit("Failed to start or connect to ratbagd")
if start_ratbagd:
ratbagd_process = toolbox.start_ratbagd(verbosity=cmd.verbose)
if ratbagd_process is None:
sys.exit("Failed to start or connect to ratbagd")
_ratbagd = toolbox.open_ratbagd()
if _ratbagd is not None:
@@ -75,14 +79,20 @@ def main(argv):
finally:
try:
if cmd.keepalive:
print('\nexport RATBAGCTL_DEVEL="{}"\n'.format(os.environ['RATBAGCTL_DEVEL']))
signal.pause()
except KeyboardInterrupt:
pass
except AttributeError: # in case the parsing failed and cmd is None
pass
toolbox.terminate_ratbagd(ratbagd_process)
if ratbagd_process:
toolbox.terminate_ratbagd(ratbagd_process)
if __name__ == "__main__":
main(sys.argv[1:])
parser = argparse.ArgumentParser(description='commandline tool to access the ratbagd.devel debug server')
parser.add_argument('--use-existing-ratbagd', dest='use_existing', action='store_true', default=False,
help='Don\'t start up ratbagd.devel, connect to the already running one')
args, remainder = parser.parse_known_args()
main(remainder, not args.use_existing)

View File

@@ -31,6 +31,13 @@ import re
from ratbagd import Ratbagd, RatbagdDevice, RatbagdProfile, RatbagdMacro, RatbagdResolution, RatbagdButton, RatbagdLed, RatbagdUnavailable, RatbagError, RatbagErrorCode, RatbagErrorCapability # NOQA
def humanize(string):
return string.lower().replace('_', '-')
button_special_names = [humanize(e.name) for e in RatbagdButton.ActionSpecial if e.name != "INVALID"]
led_mode_names = [humanize(e.name) for e in RatbagdLed.Mode]
button_specials_strmap = {
**{e: e.name.lower().replace("_", "-") for e in RatbagdButton.ActionSpecial},
**{e.name.lower().replace("_", "-"): e for e in RatbagdButton.ActionSpecial}
@@ -202,6 +209,7 @@ def print_device(d, level):
p = d.profiles[0] # there should be always one
print(" " * level + "{} - {}".format(d.id, d.name))
print(" " * level + " Model: {}".format(d.model))
print(" " * level + " Number of Buttons: {}".format(len(p.buttons)))
print(" " * level + " Number of Leds: {}".format(len(p.leds)))
print(" " * level + "Number of Profiles: {}".format(len(d.profiles)))
@@ -949,27 +957,7 @@ active profile if none is given.""",
name: 'target_special',
metavar: 'S',
help_str: 'The new special value to assign',
choices: [
"unknown",
"doubleclick",
"wheel-left",
"wheel-right",
"wheel-up",
"wheel-down",
"ratchet-mode-switch",
"resolution-cycle-up",
"resolution-cycle-down",
"resolution-up",
"resolution-down",
"resolution-alternate",
"resolution-default",
"profile-cycle-up",
"profile-cycle-down",
"profile-up",
"profile-down",
"second-mode",
"battery-level",
]
choices: button_special_names
},
],
func: func_button_action_set_special,
@@ -1054,7 +1042,7 @@ active profile if none is given.""",
name: 'mode',
metavar: 'mode',
help_str: 'The mode to set as current',
choices: ['on', 'off', 'cycle', 'breathing'],
choices: led_mode_names,
},
],
},
@@ -1164,7 +1152,7 @@ class RatbagParser(object):
class RatbagParserSwitch(RatbagParser):
def __init__(self, type, name, group=None, switch=[], N_access=None, tag=None, func=None, help=None):
super(RatbagParserSwitch, self).__init__(type, name, group, tag, func, help)
super().__init__(type, name, group, tag, func, help)
self.switch = [classes[obj[of_type]](**obj) for obj in switch]
if N_access is not None:
self.N_access = RatbagParserNAccess(**N_access)
@@ -1218,7 +1206,7 @@ class RatbagParserSwitch(RatbagParser):
class RatbagParserNAccess(RatbagParserSwitch):
def __init__(self, type, name, group=None, switch=[], metavar=None, tag=None, func=None, help=None):
super(RatbagParserNAccess, self).__init__(type, name, group, switch, None, tag, func, help)
super().__init__(type, name, group, switch, None, tag, func, help)
self.metavar = metavar
def _sub_parse(self, parent, input_string, ns):
@@ -1244,7 +1232,7 @@ class RatbagParserNAccess(RatbagParserSwitch):
class RatbagParserSet(RatbagParserSwitch):
def __init__(self, type, name, group=None, switch=[], N_access=None, tag=None, func=None, help=None):
super(RatbagParserSet, self).__init__(type, name, group, switch, N_access, tag, func, help)
super().__init__(type, name, group, switch, N_access, tag, func, help)
def _add_to_subparsers(self, parent):
parser = parent.add_parser(self.name, help=self.help)
@@ -1276,7 +1264,7 @@ class RatbagParserSet(RatbagParserSwitch):
class RatbagParserCommand(RatbagParser):
def __init__(self, type, name, group=None, pos_args=[], tag=None, func=None, help=None):
super(RatbagParserCommand, self).__init__(type, name, group, tag, func, help)
super().__init__(type, name, group, tag, func, help)
self.pos_args = [classes[obj[of_type]](**obj) for obj in pos_args]
def _add_to_subparsers(self, parent):
@@ -1301,7 +1289,7 @@ class RatbagParserCommand(RatbagParser):
class RatbagParserArgument(RatbagParser):
def __init__(self, type, name, group=None, arg_type=None, metavar=None, nargs=None, choices=None, tag=None, func=None, help=None):
super(RatbagParserArgument, self).__init__(type, name, group, tag, func, help)
super().__init__(type, name, group, tag, func, help)
self.arg_type = arg_type
self.metavar = metavar
self.nargs = nargs
@@ -1313,7 +1301,7 @@ class RatbagParserArgument(RatbagParser):
class RatbagParserLink(RatbagParser):
def __init__(self, type, dest=None, group=None, tag=None, func=None, help=None):
super(RatbagParserLink, self).__init__(type, dest, group, tag, func, help)
super().__init__(type, dest, group, tag, func, help)
self.dest = dest
def get_dest(self):
@@ -1477,7 +1465,7 @@ def on_device_added(ratbagd, device):
def open_ratbagd(ratbagd_process=None, verbose=0):
try:
r = Ratbagd()
r = Ratbagd(@RATBAGD_API_VERSION@)
r.verbose = verbose
try:
r.connect('device-added', on_device_added)

View File

@@ -38,10 +38,20 @@ run_ratbagctl_in_subprocess = False
class TestRatbagCtl(unittest.TestCase):
json = None
@classmethod
def setUpClass(cls):
super(TestRatbagCtl, cls).setUpClass()
cls.reset_test_device()
super().setUpClass()
# it's a lot easier to debug a validataion failure here than
# with ratbagd failing
if cls.json is not None:
import json
json.loads(cls.json)
if cls.json is not None:
cls.load_test_device(cls.json)
cls.find_test_device()
@classmethod
@@ -50,7 +60,7 @@ class TestRatbagCtl(unittest.TestCase):
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True,
env={"RATBAGCTL_DEVEL": "org.freedesktop.ratbag_devel1_@ratbagd_sha@"})
env={"RATBAG_TEST": "1"})
(stdout, stderr) = ratbagctl.communicate()
return (ratbagctl.returncode,
@@ -71,9 +81,9 @@ class TestRatbagCtl(unittest.TestCase):
cmd.func(ratbagd, cmd)
except SystemExit as e:
returncode = e.code
except AttributeError as e:
except AttributeError:
returncode = 2
except argparse.ArgumentTypeError as e:
except argparse.ArgumentTypeError:
returncode = 2
output = sys.stdout.getvalue()
error = sys.stderr.getvalue()
@@ -95,6 +105,13 @@ class TestRatbagCtl(unittest.TestCase):
ratbagd._dbus_call("ResetTestDevice", "")
toolbox.sync_dbus()
@classmethod
def load_test_device(cls, json):
global ratbagd
rc = ratbagd._dbus_call("LoadTestDevice", "s", json)
assert(rc == 0)
toolbox.sync_dbus()
@classmethod
def find_test_device(cls):
rc, stdout, stderr = cls.run_ratbagctl_subprocess('list')
@@ -153,24 +170,42 @@ class TestRatbagCtlName(TestRatbagCtl):
class TestRatbagCtlProfile(TestRatbagCtl):
json = '''
{
"profiles": [
{
"is_active": false
},
{
"name": "test profile x2",
"is_active": false
},
{
"is_active": true
},
{
"is_active": false,
"is_disabled": true
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlProfile, cls).setUpClass()
cls.setProfile(0)
cls.setProfile(1)
cls.setProfile(0)
super().setUpClass()
def test_profile_name_get(self):
command = "profile 1 name get"
r = self.launch_good_test("test_device " + command)
self.assertEqual(r, 'test profile 2')
self.assertEqual(r, 'test profile x2')
self.launch_fail_test(command)
self.launch_fail_test("test_device " + command + " X")
self.launch_fail_test("test_device profile name get")
def test_profile_name_set(self):
r = self.launch_good_test("test_device profile 1 name get")
self.assertEqual(r, 'test profile 2')
self.assertEqual(r, 'test profile x2')
self.launch_good_test("test_device profile 1 name set banana")
r = self.launch_good_test("test_device profile 1 name get")
self.assertEqual(r, 'banana')
@@ -186,7 +221,7 @@ class TestRatbagCtlProfile(TestRatbagCtl):
def test_profile_active_get(self):
command = "profile active get"
r = self.launch_good_test("test_device " + command)
self.assertEqual(int(r), 0)
self.assertEqual(int(r), 2)
self.launch_fail_test(command)
self.launch_fail_test("test_device " + command + " X")
@@ -235,10 +270,41 @@ class TestRatbagCtlProfile(TestRatbagCtl):
class TestRatbagCtlResolution(TestRatbagCtl):
json = '''
{
"profiles": [
{
"is_active": false
},
{
"is_active": false
},
{
"is_active": true,
"resolutions": [
{ "is_active": false,
"is_default": true },
{ "xres": 1200, "yres": 1300,
"capabilities": [1],
"is_default": false,
"is_active": false },
{ "xres": 2300, "yres": 2400,
"capabilities": [1],
"is_active": true,
"is_default": false }
]
},
{
"is_active": false,
"is_disabled": true
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlResolution, cls).setUpClass()
cls.setProfile(1)
super().setUpClass()
def test_resolution_active_get(self):
command = "resolution active get"
@@ -267,13 +333,13 @@ class TestRatbagCtlResolution(TestRatbagCtl):
self.launch_fail_test("resolution 1 get X")
def test_profile_resolution(self):
r = self.launch_good_test("test_device profile 2 resolution 2 get")
self.assertEqual(r, "2: 2300x2400dpi")
r = self.launch_good_test("test_device profile 2 resolution 1 get")
self.assertEqual(r, "1: 1200x1300dpi")
def test_resolution_default_get(self):
command = "resolution default get"
r = self.launch_good_test("test_device " + command)
self.assertEqual(int(r), 1)
self.assertEqual(int(r), 0)
self.launch_fail_test("test_device " + command + " X")
self.launch_fail_test("test_device " + command + " 10")
@@ -290,10 +356,46 @@ class TestRatbagCtlResolution(TestRatbagCtl):
class TestRatbagCtlDPI(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"resolutions": [
{ "xres": 200,
"is_active": true,
"dpi_min": 50,
"dpi_max": 5000 },
{ "xres": 250,
"is_active": false },
{ "xres": 300,
"is_active": false }
]
},
{ "is_active": false,
"resolutions": [
{ "xres": 1000, "yres": 1100,
"is_active": false },
{ "xres": 1300, "yres": 1400,
"capabilities": [1],
"is_active": true }
]
},
{ "is_active": false,
"resolutions": [
{ "xres": 2100, "yres": 2200,
"capabilities": [1],
"is_active": true},
{ "xres": 2200, "yres": 2300,
"capabilities": [1],
"is_active": false }
]
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlDPI, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_dpi_get(self):
command = "dpi get"
@@ -351,8 +453,8 @@ class TestRatbagCtlDPI(TestRatbagCtl):
self.setProfile(0)
r = self.launch_good_test("test_device profile 1 dpi get")
self.assertEqual(r, "1300x1400dpi")
r = self.launch_good_test("test_device profile 1 resolution 1 dpi get")
self.assertEqual(r, "1200x1300dpi")
r = self.launch_good_test("test_device profile 2 resolution 1 dpi get")
self.assertEqual(r, "2200x2300dpi")
r = self.launch_good_test("test_device resolution 2 dpi get")
self.assertEqual(int(r[:-3]), 300) # drop 'dpi' suffix
self.launch_fail_test("test_device profile 1 profile 1 dpi get")
@@ -360,10 +462,25 @@ class TestRatbagCtlDPI(TestRatbagCtl):
class TestRatbagCtlReportRate(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"rate": 1000,
"report_rates": [500, 1000]
},
{ "is_active": false,
"rate": 2000
},
{ "is_active": false,
"rate": 3000
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlReportRate, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_rate_get(self):
command = "rate get"
@@ -407,10 +524,38 @@ class TestRatbagCtlReportRate(TestRatbagCtl):
class TestRatbagCtlButton(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"buttons": [
{ "button": 0 },
{ "action_type": "none" },
{ "action_type": "special",
"special": "profile-cycle-up" },
{ "action_type": "macro",
"macro": [ "+B", "-B", "t300" ] }
]
},
{ "is_active": false
},
{ "is_active": false,
"buttons": [
{ "action_type": "special",
"special": "profile-cycle-up" },
{ "action_type": "special" },
{ "button": 2,
"action_type": "button" },
{ "button": 3,
"action_type": "button" }
]
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlButton, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_button_count(self):
command = "button count"
@@ -493,10 +638,57 @@ class TestRatbagCtlButton(TestRatbagCtl):
class TestRatbagCtlLED(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"leds": [
{ "type": 1,
"mode": 0,
"duration": 1000,
"brightness": 20 },
{ "type": 1,
"mode": 1,
"duration": 333,
"brightness": 40,
"color": [255, 0, 0] },
{ "type": 1,
"mode": 2,
"duration": 333,
"brightness": 40 }
]
},
{
"leds": [
{ "type": 1,
"mode": 1 },
{ "type": 1,
"mode": 0 },
{ "type": 1,
"mode": 1 }
]
},
{
"leds": [
{ "type": 1,
"mode": 1,
"color": [255, 0, 0],
"duration": 1000 },
{ "type": 1,
"mode": 2,
"brightness": 40,
"duration": 333 },
{ "type": 1,
"mode": 1 }
]
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlLED, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_led_n_get(self):
self.setProfile(0)
@@ -603,36 +795,51 @@ class TestRatbagCtlLED(TestRatbagCtl):
def setUpModule():
global ratbagd_process, ratbagd, parser
ratbagd_process = toolbox.start_ratbagd()
ratbagd_process = None
if start_ratbagd:
ratbagd_process = toolbox.start_ratbagd()
assert ratbagd_process is not None
assert ratbagd_process is not None
ratbagd = toolbox.open_ratbagd(ratbagd_process)
assert ratbagd is not None
try:
ratbagd = toolbox.open_ratbagd(ratbagd_process)
assert ratbagd is not None
except Exception as e:
if ratbagd_process is not None:
toolbox.terminate_ratbagd(ratbagd_process)
raise e
parser = toolbox.get_parser()
def tearDownModule():
global ratbagd_process
toolbox.terminate_ratbagd(ratbagd_process)
if start_ratbagd:
toolbox.terminate_ratbagd(ratbagd_process)
def parse(input_string):
global run_ratbagctl_in_subprocess
global run_ratbagctl_in_subprocess, start_ratbagd
parser_test = argparse.ArgumentParser(description="Testsuite for ratbagd/ratbagctl", add_help=False)
parser_test.add_argument("--subprocess", action='store_true', default=False)
parser_test.add_argument("--use-existing-ratbagd", dest='use_existing',
action='store_true', default=False,
help='Don\'t start up ratbagd.devel, connect to the already running one')
ns, rest = parser_test.parse_known_args(input_string)
run_ratbagctl_in_subprocess = ns.subprocess
start_ratbagd = not ns.use_existing
if start_ratbagd and not os.geteuid() == 0:
print('Script must be run as root', file=sys.stderr)
sys.exit(77)
return rest
def main(argv):
if not os.geteuid() == 0:
print('Script must be run as root', file=sys.stderr)
sys.exit(77)
os.environ['RATBAG_TEST'] = '1'
os.environ['LIBRATBAG_DATA_DIR'] = '@LIBRATBAG_DATA_DIR@'
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
args = parse(argv)

View File

@@ -62,6 +62,18 @@ class RatbagErrorCode(IntEnum):
IMPLEMENTATION = -1004
class RatbagdIncompatible(Exception):
"""ratbagd is incompatible with this client"""
def __init__(self, ratbagd_version, required_version):
super().__init__()
self.ratbagd_version = ratbagd_version
self.required_version = required_version
self.message = "ratbagd API version is {} but we require {}".format(ratbagd_version, required_version)
def __str__(self):
return self.message
class RatbagdUnavailable(Exception):
"""Signals DBus is unavailable or the ratbagd daemon is not available."""
pass
@@ -116,7 +128,7 @@ class _RatbagdDBus(GObject.GObject):
_dbus = None
def __init__(self, interface, object_path):
GObject.GObject.__init__(self)
super().__init__()
if _RatbagdDBus._dbus is None:
try:
@@ -125,8 +137,8 @@ class _RatbagdDBus(GObject.GObject):
raise RatbagdUnavailable(e.message)
ratbag1 = "org.freedesktop.ratbag1"
if os.environ.get('RATBAGCTL_DEVEL'):
ratbag1 = os.environ['RATBAGCTL_DEVEL']
if os.environ.get('RATBAG_TEST'):
ratbag1 = "org.freedesktop.ratbag_devel1"
if object_path is None:
object_path = "/" + ratbag1.replace('.', '/')
@@ -196,7 +208,7 @@ class _RatbagdDBus(GObject.GObject):
# 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
# If 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.
@@ -238,11 +250,13 @@ class Ratbagd(_RatbagdDBus):
(GObject.SignalFlags.RUN_FIRST, None, ()),
}
def __init__(self):
_RatbagdDBus.__init__(self, "Manager", None)
def __init__(self, api_version):
super().__init__("Manager", None)
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)
if self.api_version != api_version:
raise RatbagdIncompatible(self.api_version or -1, api_version)
def _on_name_owner_changed(self, *kwargs):
self.emit("daemon-disappeared")
@@ -261,6 +275,10 @@ class Ratbagd(_RatbagdDBus):
self.emit("device-removed", device)
self.notify("devices")
@GObject.Property
def api_version(self):
return self._get_dbus_property("APIVersion")
@GObject.Property
def devices(self):
"""A list of RatbagdDevice objects supported by ratbagd."""
@@ -291,7 +309,7 @@ class RatbagdDevice(_RatbagdDBus):
}
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Device", object_path)
super().__init__("Device", object_path)
# FIXME: if we start adding and removing objects from this list,
# things will break!
@@ -370,7 +388,7 @@ class RatbagdProfile(_RatbagdDBus):
CAP_WRITE_ONLY = 103
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Profile", object_path)
super().__init__("Profile", object_path)
self._dirty = False
self._active = self._get_dbus_property("IsActive")
@@ -516,7 +534,7 @@ class RatbagdResolution(_RatbagdDBus):
"""Represents a ratbagd resolution."""
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Resolution", object_path)
super().__init__("Resolution", object_path)
self._active = self._get_dbus_property("IsActive")
self._default = self._get_dbus_property("IsDefault")
@@ -664,7 +682,7 @@ class RatbagdButton(_RatbagdDBus):
}
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Button", object_path)
super().__init__("Button", object_path)
def _on_properties_changed(self, proxy, changed_props, invalidated_props):
if "Mapping" in changed_props.keys():
@@ -784,7 +802,7 @@ class RatbagdMacro(GObject.Object):
}
def __init__(self, **kwargs):
GObject.Object.__init__(self, **kwargs)
super().__init__(**kwargs)
self._macro = []
def __str__(self):
@@ -811,7 +829,7 @@ class RatbagdMacro(GObject.Object):
@GObject.Property
def keys(self):
"""A list of (RatbagdButton.MACRO_*, value) tuples representing the
"""A list of (RatbagdButton.Macro.*, value) tuples representing the
current macro."""
return self._macro
@@ -821,7 +839,7 @@ class RatbagdMacro(GObject.Object):
libratbag format.
@param macro The macro in libratbag format, as
[(RatbagdButton.MACRO_*, value)].
[(RatbagdButton.Macro.*, value)].
"""
ratbagd_macro = RatbagdMacro()
@@ -838,7 +856,7 @@ class RatbagdMacro(GObject.Object):
def append(self, type, value):
"""Appends the given event to the current macro.
@param type The type of event, as one of RatbagdButton.MACRO_*.
@param type The type of event, as one of RatbagdButton.Macro.*.
@param value If the type denotes a key event, the X.Org or Gdk keycode
of the event, as int. Otherwise, the value of the timeout
in milliseconds, as int.
@@ -883,7 +901,7 @@ class RatbagdLed(_RatbagdDBus):
}
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Led", object_path)
super().__init__("Led", object_path)
@GObject.Property
def index(self):

View File

@@ -25,7 +25,6 @@
import imp
import os
import shutil
import subprocess
import sys
@@ -36,9 +35,6 @@ RATBAGCTL_NAME = 'ratbagctl'
RATBAGCTL_PATH = os.path.join('@MESON_BUILD_ROOT@', RATBAGCTL_NAME)
RATBAGCTL_DEVEL_NAME = 'ratbagctl.devel'
RATBAGCTL_DEVEL_PATH = os.path.join('@MESON_BUILD_ROOT@', RATBAGCTL_DEVEL_NAME)
DBUS_CONF_DIR = '/etc/dbus-1/system.d'
DBUS_CONF_NAME = 'org.freedesktop.ratbag_devel1.conf'
DBUS_CONF_PATH = os.path.join(DBUS_CONF_DIR, DBUS_CONF_NAME)
def import_non_standard_path(name, path):
@@ -61,12 +57,6 @@ 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)
# FIXME: kill any running ratbagd.devel
args = [os.path.join('@MESON_BUILD_ROOT@', "ratbagd.devel")]
@@ -88,15 +78,15 @@ def start_ratbagd(verbosity=0):
proxy = Gio.DBusProxy.new_sync(dbus,
Gio.DBusProxyFlags.NONE,
None,
"org.freedesktop.ratbag_devel1_@ratbagd_sha@",
"/org/freedesktop/ratbag_devel1_@ratbagd_sha@",
"org.freedesktop.ratbag_devel1_@ratbagd_sha@.Manager",
"org.freedesktop.ratbag_devel1",
"/org/freedesktop/ratbag_devel1",
"org.freedesktop.ratbag_devel1.Manager",
None)
name_owner = proxy.get_name_owner()
if name_owner is None:
time.sleep(0.2)
os.environ['RATBAGCTL_DEVEL'] = "org.freedesktop.ratbag_devel1_@ratbagd_sha@"
os.environ['RATBAG_TEST'] = "1"
if name_owner is None or ratbagd_process.poll() is not None:
return None
@@ -111,10 +101,6 @@ def terminate_ratbagd(ratbagd):
ratbagd.wait(5)
except subprocess.TimeoutExpired:
ratbagd.kill()
try:
os.unlink(DBUS_CONF_PATH)
except FileNotFoundError:
pass
def sync_dbus():
@@ -125,14 +111,11 @@ def sync_dbus():
ratbagctl = import_non_standard_path(RATBAGCTL_NAME, RATBAGCTL_PATH)
from ratbagctl import open_ratbagd, get_parser, RatbagError, RatbagErrorCapability
from ratbagctl import open_ratbagd, get_parser, RatbagError, RatbagErrorCapability # NOQA
__all__ = [
RATBAGCTL_NAME,
RATBAGCTL_PATH,
DBUS_CONF_DIR,
DBUS_CONF_NAME,
DBUS_CONF_PATH,
start_ratbagd,
terminate_ratbagd,
open_ratbagd,