mirror of
https://github.com/libratbag/libratbag.git
synced 2026-11-07 15:14:38 +01:00
Compare commits
433 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
925b2a2a0a | ||
|
|
df42eeb1ca | ||
|
|
aab91b4b11 | ||
|
|
c39640d943 | ||
|
|
0c3cfe71a0 | ||
|
|
dcba13152f | ||
|
|
61da98ffd4 | ||
|
|
98eec0941a | ||
|
|
d8301414c7 | ||
|
|
a4da425298 | ||
|
|
ea7b4b6821 | ||
|
|
833468b292 | ||
|
|
cc954af7a7 | ||
|
|
213ccce0a8 | ||
|
|
bb6ab46a1a | ||
|
|
05c5815fb9 | ||
|
|
0fc6764b47 | ||
|
|
07b40316fb | ||
|
|
260e4adffc | ||
|
|
8c3c6598c3 | ||
|
|
1140875cd8 | ||
|
|
f87bccb370 | ||
|
|
c1955598fe | ||
|
|
d261458cae | ||
|
|
ef3822af82 | ||
|
|
2d8115d2bf | ||
|
|
4c102358a2 | ||
|
|
d133d30f62 | ||
|
|
16a1028e1d | ||
|
|
704d5d2e65 | ||
|
|
bef26c1890 | ||
|
|
85241fdd1b | ||
|
|
2dea887d2f | ||
|
|
2004a3c13e | ||
|
|
48bccf4c98 | ||
|
|
3ea0d44176 | ||
|
|
3e1bc79aac | ||
|
|
9715c5c150 | ||
|
|
07f0eafa57 | ||
|
|
72f8708e0e | ||
|
|
6ac5ba6dbb | ||
|
|
45a8fd9ec3 | ||
|
|
e9dff40258 | ||
|
|
94f28df134 | ||
|
|
4d646db24f | ||
|
|
e21366e5e5 | ||
|
|
21ac98e5f9 | ||
|
|
567d666b96 | ||
|
|
b62565da56 | ||
|
|
3ff3705dbf | ||
|
|
2aa9f012fe | ||
|
|
e7cc62a9c8 | ||
|
|
0608a0156d | ||
|
|
3723c5177f | ||
|
|
9d59c1992a | ||
|
|
ee54001032 | ||
|
|
7f0d058b66 | ||
|
|
cd2ad37db7 | ||
|
|
d08f1f21b7 | ||
|
|
2d21c0dac1 | ||
|
|
7016b50f1b | ||
|
|
217847adde | ||
|
|
c12edcd4c7 | ||
|
|
ae53f6123b | ||
|
|
6dbaeaf506 | ||
|
|
75c8cf83c3 | ||
|
|
706fe34382 | ||
|
|
5e57aa07ea | ||
|
|
1f4bf8fe5f | ||
|
|
dadfb06b98 | ||
|
|
d3c37a6836 | ||
|
|
1b04bcffba | ||
|
|
e5093e1009 | ||
|
|
132fc21212 | ||
|
|
3793e32ebd | ||
|
|
4e931cb712 | ||
|
|
e1682171ce | ||
|
|
e1334214b8 | ||
|
|
fd0954dbdc | ||
|
|
aa1ab47f59 | ||
|
|
7585d73ff5 | ||
|
|
81ca281f2c | ||
|
|
48d654827e | ||
|
|
6c9306b199 | ||
|
|
a9c0060029 | ||
|
|
a289612d85 | ||
|
|
f7b7007897 | ||
|
|
16333068b7 | ||
|
|
eace92870a | ||
|
|
c176825051 | ||
|
|
ba3547457c | ||
|
|
3f936eff68 | ||
|
|
c25864c18a | ||
|
|
cf64aac5f1 | ||
|
|
0a39552a65 | ||
|
|
03d2d71f82 | ||
|
|
1edb6df25b | ||
|
|
9c7494c5c0 | ||
|
|
7f29c0423f | ||
|
|
07307b85d3 | ||
|
|
219218118d | ||
|
|
1e2489c9e7 | ||
|
|
479c4352af | ||
|
|
763655516c | ||
|
|
944c4f0af5 | ||
|
|
268ccab111 | ||
|
|
603b0ca1fc | ||
|
|
4e50c506de | ||
|
|
4351eb3d72 | ||
|
|
9f4a1ba405 | ||
|
|
451894b1b4 | ||
|
|
c6051cb9ea | ||
|
|
34fafbc0f8 | ||
|
|
75d075cc67 | ||
|
|
2f330b6466 | ||
|
|
c7adc9b9f6 | ||
|
|
16a7b6ed18 | ||
|
|
7ff73c8515 | ||
|
|
f11e31d671 | ||
|
|
f69457c280 | ||
|
|
756fee1202 | ||
|
|
d4126463e4 | ||
|
|
00832cb9f0 | ||
|
|
866d4e6433 | ||
|
|
cbba00f997 | ||
|
|
d2e4fcc23e | ||
|
|
f0c66d0e1f | ||
|
|
b2fb91e261 | ||
|
|
1a64d2b85d | ||
|
|
8f5fdf8590 | ||
|
|
baad36060d | ||
|
|
7ecabea70d | ||
|
|
c0c8d6c544 | ||
|
|
37587bb99c | ||
|
|
cc2fff9c06 | ||
|
|
b80d3ac0a3 | ||
|
|
a757180d60 | ||
|
|
8c0124d654 | ||
|
|
b5d41e3657 | ||
|
|
bfd2fc9969 | ||
|
|
566d160de3 | ||
|
|
e0dc0a3a5b | ||
|
|
5b7af0ec14 | ||
|
|
44a9959344 | ||
|
|
755dbddfb3 | ||
|
|
1e3a85433a | ||
|
|
b3b3e3f838 | ||
|
|
f05b671dc1 | ||
|
|
bfadfc3c91 | ||
|
|
96d93ae387 | ||
|
|
03891189da | ||
|
|
02f3487f52 | ||
|
|
d2f1ff0ca2 | ||
|
|
d5a7ad3730 | ||
|
|
e98f86fa2a | ||
|
|
fb14cb576b | ||
|
|
2c83f9c3ef | ||
|
|
b71f439ed6 | ||
|
|
dff102be43 | ||
|
|
6bb92dc313 | ||
|
|
92c5923255 | ||
|
|
6c8a2d36fe | ||
|
|
875aae6985 | ||
|
|
a55297e0fa | ||
|
|
fa95abbc33 | ||
|
|
48d1b21036 | ||
|
|
904aedc555 | ||
|
|
4fa8663cfb | ||
|
|
58086882a3 | ||
|
|
aef92645b7 | ||
|
|
514ce5419f | ||
|
|
2a0a3cf203 | ||
|
|
f96ba13f07 | ||
|
|
724d31c8fe | ||
|
|
66e44d76b9 | ||
|
|
e908871e82 | ||
|
|
9732aa009e | ||
|
|
92299d4673 | ||
|
|
5c3df3b218 | ||
|
|
62f26e797e | ||
|
|
d460743bd7 | ||
|
|
d47f2ed946 | ||
|
|
2ce254e51b | ||
|
|
c7c9c908e1 | ||
|
|
b339370b6a | ||
|
|
37ae638a7c | ||
|
|
6a071dc1ee | ||
|
|
7c9a75da9e | ||
|
|
dd1c72a1d0 | ||
|
|
8d7cbcba4f | ||
|
|
4075227eb7 | ||
|
|
abcec1d349 | ||
|
|
206bc5996d | ||
|
|
366f19abdd | ||
|
|
952bd9dc07 | ||
|
|
6b97d94a2f | ||
|
|
ccaa8d1b6c | ||
|
|
abe451df33 | ||
|
|
a3fa876c3d | ||
|
|
230647c7f3 | ||
|
|
3f99446a1b | ||
|
|
a766f8e909 | ||
|
|
9ae975f9a9 | ||
|
|
780934b1a1 | ||
|
|
337fb935c4 | ||
|
|
c9cf5fe90c | ||
|
|
18af0f97d2 | ||
|
|
286f13c654 | ||
|
|
c6b9cc0c2a | ||
|
|
7d82bb51a6 | ||
|
|
8976033da7 | ||
|
|
c9c6a1675f | ||
|
|
f1e2a4ccff | ||
|
|
104543fdf1 | ||
|
|
b3275e10d7 | ||
|
|
120908bda8 | ||
|
|
32d6443553 | ||
|
|
ae943eaee7 | ||
|
|
4a9503f2da | ||
|
|
e268100d31 | ||
|
|
8ec0e8bbc5 | ||
|
|
e6a9fba498 | ||
|
|
e753db72f9 | ||
|
|
e1f4fac9a2 | ||
|
|
8f981c8b87 | ||
|
|
01d2cf8477 | ||
|
|
f7c22ed88c | ||
|
|
aeda65cbfa | ||
|
|
ed2439e5e2 | ||
|
|
f407521e8f | ||
|
|
3c4fe23f70 | ||
|
|
9e2ac55dbc | ||
|
|
1e72ebaa7b | ||
|
|
920bc2b993 | ||
|
|
20b573668e | ||
|
|
49acc11db3 | ||
|
|
364e3fd965 | ||
|
|
bb230d390d | ||
|
|
93a076bed9 | ||
|
|
e7b50c9061 | ||
|
|
9d533bdc3e | ||
|
|
d7906b54b4 | ||
|
|
f1eba370ed | ||
|
|
7b5ab2ed99 | ||
|
|
d2d5471836 | ||
|
|
9cfc1e98e3 | ||
|
|
691661276f | ||
|
|
25ed99db84 | ||
|
|
02226d0428 | ||
|
|
f822fb0fa7 | ||
|
|
b88be0add0 | ||
|
|
9fc6d12486 | ||
|
|
0c64dad9e1 | ||
|
|
f61485c603 | ||
|
|
8c295479d8 | ||
|
|
e55b04ec7d | ||
|
|
1231415470 | ||
|
|
660da27021 | ||
|
|
481b6f029a | ||
|
|
768d93a45a | ||
|
|
1c6e039d01 | ||
|
|
d16df8d100 | ||
|
|
4b1e49d522 | ||
|
|
786cebcb0b | ||
|
|
d53454bcb3 | ||
|
|
a0def65b64 | ||
|
|
2a40279903 | ||
|
|
02c95422f0 | ||
|
|
4ce1cf2807 | ||
|
|
89168f2187 | ||
|
|
bb92337268 | ||
|
|
a8316c07f3 | ||
|
|
39769c57dd | ||
|
|
200a8af7a7 | ||
|
|
fcf9f7745d | ||
|
|
633c9fc55d | ||
|
|
ee73434ead | ||
|
|
d60b1c9d71 | ||
|
|
88a60f55b4 | ||
|
|
dba202ae8b | ||
|
|
6cdcce9de4 | ||
|
|
e2c3373145 | ||
|
|
02575a9008 | ||
|
|
1097b1ba68 | ||
|
|
77b67775da | ||
|
|
5b23c9ebae | ||
|
|
6d0974be25 | ||
|
|
ea9111a2a6 | ||
|
|
42787ee422 | ||
|
|
611dd9be0d | ||
|
|
fd20f9e642 | ||
|
|
9be5576967 | ||
|
|
9fe511fcc5 | ||
|
|
f0b69aab19 | ||
|
|
7610b80ced | ||
|
|
f0a05bb522 | ||
|
|
2669cc28b2 | ||
|
|
302702d94a | ||
|
|
e1136c4d30 | ||
|
|
0012853a1d | ||
|
|
afb85f868c | ||
|
|
b76fcdadde | ||
|
|
ba7c092ee2 | ||
|
|
3483bd8c14 | ||
|
|
0fb0eb3d42 | ||
|
|
5fb1f4429d | ||
|
|
5f5e294598 | ||
|
|
b4218737cd | ||
|
|
3875bb70fd | ||
|
|
e9d67b7235 | ||
|
|
8a476a8724 | ||
|
|
d67cbec5f8 | ||
|
|
f82c6f72b6 | ||
|
|
af30fc28cd | ||
|
|
3fb8a51456 | ||
|
|
ceaa3c3ad5 | ||
|
|
551b918ba8 | ||
|
|
3105428ba1 | ||
|
|
b3f2406180 | ||
|
|
902160f430 | ||
|
|
fd6d0b6198 | ||
|
|
5ed98fdb22 | ||
|
|
e0e73c2971 | ||
|
|
0d3b4f1b64 | ||
|
|
20b6e3deeb | ||
|
|
ffcdee912d | ||
|
|
5357d4cbc2 | ||
|
|
26c534cc74 | ||
|
|
904c7988bb | ||
|
|
7d255a2f93 | ||
|
|
d0a5cb1f0e | ||
|
|
71b1d66287 | ||
|
|
32ebbca42f | ||
|
|
a0afbfeb3f | ||
|
|
9dfd4fd03a | ||
|
|
b0ea75979a | ||
|
|
a5c1ca202e | ||
|
|
8d40ad6c0c | ||
|
|
72d11de597 | ||
|
|
47cc5fce92 | ||
|
|
5da791b268 | ||
|
|
e63802b01a | ||
|
|
cd89b123ce | ||
|
|
f6546b3222 | ||
|
|
cd8ddc8444 | ||
|
|
cdb3226ef1 | ||
|
|
ce377fc36c | ||
|
|
fe1308fe38 | ||
|
|
983d3e2a88 | ||
|
|
3a3d9e1148 | ||
|
|
afbba26feb | ||
|
|
8cc37047fb | ||
|
|
c8f56cc535 | ||
|
|
b6936ddd3b | ||
|
|
d4d9712316 | ||
|
|
2353af214b | ||
|
|
a4d19efb93 | ||
|
|
cb0b458824 | ||
|
|
48c476b28c | ||
|
|
d6c3bf881d | ||
|
|
1b000e627a | ||
|
|
75bcf39114 | ||
|
|
33ced3dfb7 | ||
|
|
906f17add2 | ||
|
|
016de548fc | ||
|
|
665c022382 | ||
|
|
81c7b42bec | ||
|
|
edb054c6f8 | ||
|
|
8ae995b72d | ||
|
|
a941b75ef2 | ||
|
|
8cb85da6be | ||
|
|
7ff59a636f | ||
|
|
6cd050f541 | ||
|
|
ab3f1df06e | ||
|
|
caa31e17cf | ||
|
|
b7e6103496 | ||
|
|
b1b6a2761e | ||
|
|
11f4f32947 | ||
|
|
4f5d0dffec | ||
|
|
d213bdb86e | ||
|
|
e69e1de17b | ||
|
|
cad05ffced | ||
|
|
196d53e65f | ||
|
|
c606b1abe3 | ||
|
|
b4009669b9 | ||
|
|
d458305f93 | ||
|
|
9c11960665 | ||
|
|
e8fc8b45d1 | ||
|
|
e64a121572 | ||
|
|
607e47953c | ||
|
|
a5816055dc | ||
|
|
62ac0772f5 | ||
|
|
fc9e63a5aa | ||
|
|
936a8f7a92 | ||
|
|
818e665853 | ||
|
|
3a3b11ade4 | ||
|
|
3bc31edd72 | ||
|
|
485df20a83 | ||
|
|
49291c4b45 | ||
|
|
02ec2442d3 | ||
|
|
a8b4d9c6f3 | ||
|
|
22a6ac1c7e | ||
|
|
42c302c860 | ||
|
|
e2b1581aef | ||
|
|
10a3e87980 | ||
|
|
2d465a63e1 | ||
|
|
c2315323f4 | ||
|
|
05d7e9b324 | ||
|
|
71a399f20f | ||
|
|
8259c2e706 | ||
|
|
89980a4a72 | ||
|
|
79e94b4a7e | ||
|
|
122f05c272 | ||
|
|
b06ac1035e | ||
|
|
647fe432b2 | ||
|
|
4e2e9f037b | ||
|
|
f92fee0b15 | ||
|
|
d7c394f379 | ||
|
|
7fec19f8f2 | ||
|
|
11c1e2d71a | ||
|
|
e2cbaf9dea | ||
|
|
bf9d86e228 | ||
|
|
f24a3bd441 | ||
|
|
df5742ebfe | ||
|
|
0c569e0bf7 | ||
|
|
5ea1731f38 | ||
|
|
572064aba8 | ||
|
|
da2764e78d | ||
|
|
4e56afae22 | ||
|
|
d46251900c | ||
|
|
43def1b3a5 | ||
|
|
247ff7695d | ||
|
|
7a14026f2d |
20
.editorconfig
Normal file
20
.editorconfig
Normal file
@@ -0,0 +1,20 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{c,h}]
|
||||
indent_style = tab
|
||||
tab_width = 8
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{*.{py,py.in},tools/ratbagctl.*.in}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{yml,xsl}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Information**
|
||||
<!-- If your issue is related to a Logitech device please make sure you are running at least Linux 5.2 -->
|
||||
- `ratbagd` version (`ratbagd --version`):
|
||||
- Distribution:
|
||||
- Kernel version (ex. `uname -srmo`): `KERNEL VERSION HERE`
|
||||
|
||||
Device info (if applicable):
|
||||
```
|
||||
$ ratbagctl <device> info
|
||||
OUTPUT HERE
|
||||
```
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Logs**
|
||||
Please start the daemon with verbosity and reproduce the issue.
|
||||
|
||||
First make sure it isn't running already.
|
||||
```
|
||||
$ ratbagd --verbose=raw
|
||||
OUTPUT HERE
|
||||
```
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
39
.github/actions/meson/action.yml
vendored
Normal file
39
.github/actions/meson/action.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# A Github Action to build a repository with meson
|
||||
name: "Setup and run meson"
|
||||
description: "Setup and run meson"
|
||||
inputs:
|
||||
builddir:
|
||||
description: "The build directory"
|
||||
default: "builddir"
|
||||
srcdir:
|
||||
description: "The source directory"
|
||||
default: "."
|
||||
meson_args:
|
||||
description: "Arguments passed to meson setup"
|
||||
default: ""
|
||||
meson_skip_test:
|
||||
description: "Set to a nonempty string to skip meson test"
|
||||
default: ""
|
||||
meson_test_args:
|
||||
description: "Arguments passed to meson test"
|
||||
default: ""
|
||||
meson_precmd:
|
||||
description: "Set to 'sudo' to run with sudo"
|
||||
default: ""
|
||||
ninja_args:
|
||||
description: "Arguments passed to ninja"
|
||||
default: ""
|
||||
ninja_precmd:
|
||||
description: "Set to 'sudo' to run with sudo"
|
||||
default: ""
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- run: |
|
||||
${{inputs.meson_precmd}} meson setup ${{inputs.builddir}} ${{inputs.srcdir}} ${{inputs.meson_args}}
|
||||
${{inputs.meson_precmd}} meson configure ${{inputs.builddir}}
|
||||
${{inputs.ninja_precmd}} ninja -C ${{inputs.builddir}} ${{inputs.ninja_args}}
|
||||
if [[ -z "${{inputs.meson_skip_test}}" ]]; then
|
||||
${{inputs.meson_precmd}} meson test -C ${{inputs.builddir}} --print-errorlogs ${{inputs.meson_test_args}};
|
||||
fi
|
||||
shell: bash
|
||||
17
.github/actions/pkginstall/action.yml
vendored
Normal file
17
.github/actions/pkginstall/action.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: "Install packages"
|
||||
description: "Install a set of packages from sources"
|
||||
inputs:
|
||||
apt:
|
||||
description: "The package list to install with apt"
|
||||
pip:
|
||||
description: "The package list to install with pip"
|
||||
pip_precmd:
|
||||
description: "Set to the string 'sudo' to run through sudo"
|
||||
default: ""
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- run: sudo ${{ github.action_path }}/install-apt.sh ${{ inputs.apt }}
|
||||
shell: bash
|
||||
- run: ${{inputs.pip_precmd}} ${{ github.action_path }}/install-pip.sh ${{ inputs.pip }}
|
||||
shell: bash
|
||||
14
.github/actions/pkginstall/install-apt.sh
vendored
Executable file
14
.github/actions/pkginstall/install-apt.sh
vendored
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
# If called without arguments, just skip the rest
|
||||
if [[ -z "$@" ]]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
# Don't care about these bits
|
||||
echo 'path-exclude=/usr/share/doc/*' > /etc/dpkg/dpkg.cfg.d/99-exclude-cruft
|
||||
echo 'path-exclude=/usr/share/locale/*' >> /etc/dpkg/dpkg.cfg.d/99-exclude-cruft
|
||||
echo 'path-exclude=/usr/share/man/*' >> /etc/dpkg/dpkg.cfg.d/99-exclude-cruft
|
||||
|
||||
apt-get update
|
||||
apt-get install -yq --no-install-suggests --no-install-recommends $@
|
||||
9
.github/actions/pkginstall/install-pip.sh
vendored
Executable file
9
.github/actions/pkginstall/install-pip.sh
vendored
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
# If called without arguments, just skip the rest
|
||||
if [[ -z "$@" ]]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install --upgrade "$@"
|
||||
50
.github/workflows/main.yml
vendored
Normal file
50
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: linux
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
env:
|
||||
UBUNTU_DEP_BUILD: |
|
||||
gcc g++ pkg-config systemd libevdev-dev libglib2.0-dev
|
||||
libjson-glib-dev libsystemd-dev libudev-dev libunistring-dev
|
||||
python3-dev python3-evdev swig python3-sphinx
|
||||
UBUNTU_DEP_TEST: check python3-gi python3-lxml valgrind
|
||||
PIP_PACKAGES: meson ninja libevdev pyudev pytest sphinx_rtd_theme
|
||||
|
||||
jobs:
|
||||
build-and-dist:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
meson_args:
|
||||
- ''
|
||||
- '-Dbuildtype=plain'
|
||||
- '-Dbuildtype=release'
|
||||
- '-Ddocumentation=true'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/pkginstall
|
||||
with:
|
||||
apt: $UBUNTU_DEP_BUILD $UBUNTU_DEP_TEST
|
||||
pip: $PIP_PACKAGES
|
||||
- name: meson test ${{matrix.meson_args}}
|
||||
uses: ./.github/actions/meson
|
||||
with:
|
||||
meson_args: --prefix=$PWD/_instdir ${{matrix.meson_args}}
|
||||
ninja_args: install
|
||||
- name: check installation of data files
|
||||
run: diff -u <(cd data/devices; ls *.device) <(cd _instdir/share/libratbag; ls *.device)
|
||||
- name: ninja uninstall
|
||||
uses: ./.github/actions/meson
|
||||
with:
|
||||
meson_args: --prefix=$PWD/_instdir
|
||||
ninja_args: uninstall
|
||||
- name: check if any files are left after uninstall
|
||||
run: (test -d _instdir && tree _instdir && exit 1) || exit 0
|
||||
# Capture all the meson logs, even if we failed
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ always() }} # even if we fail
|
||||
with:
|
||||
name: meson test logs
|
||||
path: |
|
||||
builddir/meson-logs/testlog*.txt
|
||||
builddir/meson-logs/meson-log.txt
|
||||
31
.github/workflows/pages.yml
vendored
Normal file
31
.github/workflows/pages.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
UBUNTU_DEP_BUILD: gcc g++ pkg-config systemd libevdev-dev libglib2.0-dev libjson-glib-dev libsystemd-dev libudev-dev libunistring-dev python3-dev python3-evdev python3-pip swig python3-sphinx python3-sphinx-rtd-theme
|
||||
PIP_PACKAGES: meson ninja
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/pkginstall
|
||||
with:
|
||||
apt: $UBUNTU_DEP_BUILD
|
||||
pip: $PIP_PACKAGES
|
||||
- name: Build docs
|
||||
uses: ./.github/actions/meson
|
||||
with:
|
||||
meson_args: -Ddocumentation=true -Dtests=false
|
||||
meson_skip_test: yes
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./builddir/doc/html/
|
||||
allow_empty_commit: true
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1,11 +0,0 @@
|
||||
*.o
|
||||
*.pc
|
||||
*.swp
|
||||
*~
|
||||
*.sig
|
||||
*.tar.*
|
||||
*.announce
|
||||
*.patch
|
||||
*.rej
|
||||
*.trs
|
||||
tags
|
||||
13
README.md
13
README.md
@@ -3,9 +3,10 @@ libratbag
|
||||
|
||||
<img src="https://libratbag.github.io/_images/logo.svg" alt="" width="30%" align="right">
|
||||
|
||||
libratbag provides **ratbagd**, a DBus daemon to configure gaming mice.
|
||||
The daemon provides a generic way to access the various features exposed by
|
||||
these mice and abstracts away hardware-specific and kernel-specific quirks.
|
||||
libratbag provides **ratbagd**, a DBus daemon to configure input devices,
|
||||
mainly gaming mice. The daemon provides a generic way to access the various
|
||||
features exposed by these mice and abstracts away hardware-specific and
|
||||
kernel-specific quirks.
|
||||
|
||||
libratbag currently supports devices from Logitech, Etekcity, GSkill,
|
||||
Roccat, Steelseries. See [the device
|
||||
@@ -44,6 +45,10 @@ And to build or re-build after code-changes, run:
|
||||
|
||||
ninja -C builddir
|
||||
sudo ninja -C builddir install
|
||||
|
||||
To remove/uninstal simply run:
|
||||
|
||||
sudo ninja -C builddir uninstall
|
||||
|
||||
Note: `builddir` is the build output directory and can be changed to any
|
||||
other directory name. To set configure-time options, use e.g.
|
||||
@@ -168,5 +173,3 @@ libratbag is licensed under the MIT license.
|
||||
> Software is furnished to do so, subject to the following conditions: [...]
|
||||
|
||||
See the COPYING file for the full license information.
|
||||
|
||||
[](https://circleci.com/gh/libratbag/libratbag)
|
||||
|
||||
225
circle.yml
225
circle.yml
@@ -1,225 +0,0 @@
|
||||
# vim: set expandtab shiftwidth=2 tabstop=8:
|
||||
libratbag_references:
|
||||
default_settings: &default_settings
|
||||
working_directory: ~/libratbag
|
||||
environment:
|
||||
LANG: C.UTF-8
|
||||
build_default: &build_default
|
||||
name: Build
|
||||
command: |
|
||||
rm -rf build
|
||||
meson build ${MESON_PARAMS}
|
||||
meson configure build
|
||||
ninja -v -C build ${NINJA_ARGS}
|
||||
environment:
|
||||
MESON_PARAMS: --prefix=/usr
|
||||
build_and_test: &build_and_test
|
||||
run:
|
||||
<<: *build_default
|
||||
name: Build and test
|
||||
environment:
|
||||
NINJA_ARGS: test
|
||||
build_with_docs: &build_with_docs
|
||||
run:
|
||||
<<: *build_default
|
||||
name: Build with documentation
|
||||
environment:
|
||||
MESON_PARAMS: -Ddocumentation=true
|
||||
install: &install
|
||||
run:
|
||||
name: Installing
|
||||
command: ninja -C build install
|
||||
check_install: &check_install
|
||||
run:
|
||||
name: Checking installation
|
||||
command: |
|
||||
PREFIX=/usr/local/
|
||||
diff -u <(cd data/devices; ls *.device) <(cd $PREFIX/share/libratbag; ls *.device)
|
||||
check_uninstall: &check_uninstall
|
||||
run:
|
||||
name: Checking if any files are left after uninstall
|
||||
command: |
|
||||
PREFIX=/root/test_install
|
||||
# workaround until https://github.com/mesonbuild/meson/pull/2033 is merged
|
||||
# and a new release appears
|
||||
git clone https://github.com/whot/meson -b wip/remove-directories-on-uninstall
|
||||
./meson/meson.py build_install --prefix=$PREFIX
|
||||
ninja -C build_install install
|
||||
ninja -C build_install uninstall
|
||||
if [ -d $PREFIX ]
|
||||
then
|
||||
tree $PREFIX
|
||||
exit 1
|
||||
fi
|
||||
export_logs: &export_logs
|
||||
store_artifacts:
|
||||
path: ~/libratbag/build/meson-logs
|
||||
start_dbus: &start_dbus
|
||||
run:
|
||||
name: Start dbus daemon
|
||||
command: |
|
||||
mkdir /run/dbus
|
||||
/usr/bin/dbus-daemon --system --fork
|
||||
|
||||
fedora_prep_cache: &fedora_prep_cache
|
||||
<<: *default_settings
|
||||
steps:
|
||||
- run:
|
||||
name: Initializing Fedora dnf cache
|
||||
command: dnf install -y --downloadonly libsolv tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml libunistring-devel dbus-daemon
|
||||
- persist_to_workspace:
|
||||
root: /var/cache/
|
||||
paths:
|
||||
- dnf/*
|
||||
|
||||
fedora_fetch_cache: &fedora_fetch_cache
|
||||
attach_workspace:
|
||||
at: /var/cache/
|
||||
|
||||
fedora_install: &fedora_install
|
||||
run:
|
||||
name: Install prerequisites
|
||||
command: |
|
||||
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
|
||||
sed -i 's/systemd//' /etc/nsswitch.conf
|
||||
|
||||
fedora_settings: &fedora_settings
|
||||
<<: *default_settings
|
||||
steps:
|
||||
- *fedora_fetch_cache
|
||||
- *fedora_install
|
||||
- checkout
|
||||
- *start_dbus
|
||||
- *build_and_test
|
||||
- *install
|
||||
- *check_install
|
||||
- *check_uninstall
|
||||
- *export_logs
|
||||
|
||||
|
||||
ubuntu_settings: &ubuntu_settings
|
||||
<<: *default_settings
|
||||
steps:
|
||||
- run:
|
||||
name: Install prerequisites
|
||||
command: |
|
||||
apt-get update
|
||||
apt-get install -y software-properties-common
|
||||
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
|
||||
- checkout
|
||||
- *start_dbus
|
||||
- *build_and_test
|
||||
- *install
|
||||
- *check_install
|
||||
- *check_uninstall
|
||||
- *export_logs
|
||||
|
||||
doc_build: &doc_build
|
||||
<<: *default_settings
|
||||
steps:
|
||||
- *fedora_fetch_cache
|
||||
- *fedora_install
|
||||
- run:
|
||||
name: Install documentation build-deps
|
||||
command: dnf install -y python3-sphinx python3-sphinx_rtd_theme
|
||||
- checkout
|
||||
- *build_with_docs
|
||||
- *export_logs
|
||||
- store_artifacts:
|
||||
path: ~/libratbag/build/doc/html
|
||||
- persist_to_workspace:
|
||||
root: build
|
||||
paths:
|
||||
- doc/html/*
|
||||
|
||||
docs_deploy: &docs_deploy
|
||||
<<: *default_settings
|
||||
steps:
|
||||
- *fedora_fetch_cache
|
||||
- run:
|
||||
name: Install prerequisites
|
||||
command: dnf install -y git tree
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: build
|
||||
- run:
|
||||
name: Setup the deploy SSH key
|
||||
command: |
|
||||
ssh-add -L | tail -n1 > ~/.ssh/deploy.pub
|
||||
cat <<EOF > ~/.ssh/config
|
||||
Host github.com
|
||||
IdentityFile /root/.ssh/deploy.pub
|
||||
IdentitiesOnly yes
|
||||
EOF
|
||||
- run:
|
||||
name: Clone doc repository
|
||||
command: |
|
||||
cd ~
|
||||
git clone ssh://git@github.com/${CIRCLE_PROJECT_REPONAME}/${CIRCLE_PROJECT_REPONAME}.github.io
|
||||
- run:
|
||||
name: Copy the doc, commit and push
|
||||
command: |
|
||||
set +e
|
||||
cd ~/${CIRCLE_PROJECT_REPONAME}.github.io
|
||||
\cp -r ~/${CIRCLE_PROJECT_REPONAME}/build/doc/html/* .
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
git config --global user.email "libratbag@librabtag.github.io"
|
||||
git config --global user.name "The libratbag crew"
|
||||
git add .
|
||||
git commit -m "update docs from ${CIRCLE_SHA1}"
|
||||
git push
|
||||
fi
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
fedora_rawhide:
|
||||
<<: *fedora_settings
|
||||
docker:
|
||||
- image: fedora:rawhide
|
||||
fedora_cache:
|
||||
<<: *fedora_prep_cache
|
||||
docker:
|
||||
- image: fedora:30
|
||||
fedora_latest:
|
||||
<<: *fedora_settings
|
||||
docker:
|
||||
- image: fedora:30
|
||||
ubuntu_19_04:
|
||||
<<: *ubuntu_settings
|
||||
docker:
|
||||
- image: ubuntu:19.04
|
||||
doc_build:
|
||||
<<: *doc_build
|
||||
docker:
|
||||
- image: fedora:30
|
||||
doc_deploy:
|
||||
<<: *docs_deploy
|
||||
docker:
|
||||
- image: fedora:30
|
||||
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
compile_and_test:
|
||||
jobs:
|
||||
# - fedora_rawhide
|
||||
- fedora_cache
|
||||
- ubuntu_19_04
|
||||
- fedora_latest:
|
||||
requires:
|
||||
- fedora_cache
|
||||
- doc_build:
|
||||
requires:
|
||||
- fedora_cache
|
||||
- doc_deploy:
|
||||
requires:
|
||||
- fedora_cache
|
||||
- doc_build
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
11
data/devices/asus-rog-buzzard.device
Normal file
11
data/devices/asus-rog-buzzard.device
Normal file
@@ -0,0 +1,11 @@
|
||||
[Device]
|
||||
Name=ASUS ROG GX860 Buzzard Mouse
|
||||
DeviceMatch=usb:0b05:1816
|
||||
Driver=asus
|
||||
|
||||
[Driver/asus]
|
||||
Profiles=3
|
||||
Buttons=10
|
||||
Leds=0
|
||||
Dpis=2
|
||||
DpiRange=100:8200@100
|
||||
13
data/devices/asus-rog-chakram.device
Normal file
13
data/devices/asus-rog-chakram.device
Normal file
@@ -0,0 +1,13 @@
|
||||
[Device]
|
||||
Name=ASUS ROG Chakram
|
||||
DeviceMatch=usb:0b05:18e3;usb:0b05:18e5
|
||||
Driver=asus
|
||||
|
||||
[Driver/asus]
|
||||
Profiles=3
|
||||
Buttons=5
|
||||
Leds=3
|
||||
Dpis=4
|
||||
Wireless=1
|
||||
DpiRange=100:16000@100
|
||||
Quirks=DOUBLE_DPI;STRIX_PROFILE
|
||||
13
data/devices/asus-rog-gladius2-origin-pink.device
Normal file
13
data/devices/asus-rog-gladius2-origin-pink.device
Normal file
@@ -0,0 +1,13 @@
|
||||
[Device]
|
||||
Name=ASUS ROG Gladius II Origin PNK LTD
|
||||
DeviceMatch=usb:0b05:18cd
|
||||
Driver=asus
|
||||
|
||||
[Driver/asus]
|
||||
Profiles=3
|
||||
Buttons=8
|
||||
Leds=3
|
||||
Dpis=2
|
||||
DpiRange=100:12000@100
|
||||
ButtonMapping=0;1;2;7;8;5;3;4
|
||||
Quirks=DOUBLE_DPI
|
||||
14
data/devices/asus-rog-gladius2-origin.device
Normal file
14
data/devices/asus-rog-gladius2-origin.device
Normal file
@@ -0,0 +1,14 @@
|
||||
# "ASUS ROG Gladius II" without "DPI target button"
|
||||
[Device]
|
||||
Name=ASUS ROG Gladius II Origin
|
||||
DeviceMatch=usb:0b05:1877
|
||||
Driver=asus
|
||||
|
||||
[Driver/asus]
|
||||
Profiles=3
|
||||
Buttons=8
|
||||
Leds=3
|
||||
Dpis=2
|
||||
DpiRange=100:12000@100
|
||||
ButtonMapping=0;1;2;7;8;5;3;4
|
||||
Quirks=DOUBLE_DPI
|
||||
12
data/devices/asus-rog-gladius2.device
Normal file
12
data/devices/asus-rog-gladius2.device
Normal file
@@ -0,0 +1,12 @@
|
||||
[Device]
|
||||
Name=ASUS ROG Gladius II
|
||||
DeviceMatch=usb:0b05:1845
|
||||
Driver=asus
|
||||
|
||||
[Driver/asus]
|
||||
Profiles=3
|
||||
Buttons=8
|
||||
Leds=3
|
||||
Dpis=2
|
||||
DpiRange=100:12000@100
|
||||
ButtonMapping=0;1;2;7;8;5;3;4
|
||||
13
data/devices/asus-rog-keris-wireless.device
Normal file
13
data/devices/asus-rog-keris-wireless.device
Normal file
@@ -0,0 +1,13 @@
|
||||
[Device]
|
||||
Name=ASUS ROG Keris Wireless
|
||||
DeviceMatch=usb:0b05:195e;usb:0b05:1960
|
||||
Driver=asus
|
||||
|
||||
[Driver/asus]
|
||||
Profiles=3
|
||||
Buttons=8
|
||||
Leds=2
|
||||
Dpis=4
|
||||
Wireless=1
|
||||
DpiRange=100:16000@100
|
||||
Quirks=DOUBLE_DPI
|
||||
14
data/devices/asus-rog-pugio.device
Normal file
14
data/devices/asus-rog-pugio.device
Normal file
@@ -0,0 +1,14 @@
|
||||
[Device]
|
||||
Name=ASUS ROG Pugio
|
||||
DeviceMatch=usb:0b05:1846
|
||||
Driver=asus
|
||||
|
||||
[Driver/asus]
|
||||
Profiles=3
|
||||
Buttons=10
|
||||
Leds=3
|
||||
Dpis=2
|
||||
DpiRange=100:7200@100
|
||||
|
||||
# Mappings the same as the Gladius II but with an extra 2 buttons
|
||||
ButtonMapping=0;1;2;7;8;5;3;4;9;10
|
||||
13
data/devices/asus-rog-strix-carry.device
Normal file
13
data/devices/asus-rog-strix-carry.device
Normal file
@@ -0,0 +1,13 @@
|
||||
[Device]
|
||||
Name=ASUS ROG Strix Carry
|
||||
DeviceMatch=usb:0b05:18b4
|
||||
Driver=asus
|
||||
|
||||
[Driver/asus]
|
||||
Profiles=3
|
||||
Buttons=8
|
||||
Leds=0
|
||||
Dpis=2
|
||||
Wireless=1
|
||||
DpiRange=100:7200@100
|
||||
Quirks=STRIX_PROFILE
|
||||
11
data/devices/asus-rog-strix-impact.device
Normal file
11
data/devices/asus-rog-strix-impact.device
Normal file
@@ -0,0 +1,11 @@
|
||||
[Device]
|
||||
Name=ASUS ROG Strix Impact
|
||||
DeviceMatch=usb:0b05:1847
|
||||
Driver=asus
|
||||
|
||||
[Driver/asus]
|
||||
Profiles=0
|
||||
Buttons=6
|
||||
Leds=1
|
||||
Dpis=2
|
||||
DpiRange=100:5000@100
|
||||
12
data/devices/asus-rog-strix-impact2-wireless.device
Normal file
12
data/devices/asus-rog-strix-impact2-wireless.device
Normal file
@@ -0,0 +1,12 @@
|
||||
[Device]
|
||||
Name=ASUS ROG Strix Impact II Wireless
|
||||
DeviceMatch=usb:0b05:1947;usb:0b05:1949
|
||||
Driver=asus
|
||||
|
||||
[Driver/asus]
|
||||
Profiles=3
|
||||
Buttons=8
|
||||
Leds=3
|
||||
Dpis=4
|
||||
Wireless=1
|
||||
DpiRange=100:16000@100
|
||||
@@ -52,3 +52,29 @@ Leds=1
|
||||
# The HID++ 2.0 device index
|
||||
# Should be unset unless required
|
||||
# DeviceIndex=1
|
||||
|
||||
|
||||
[Driver/asus]
|
||||
# Number of profiles
|
||||
Profiles=3
|
||||
|
||||
# Number of buttons
|
||||
Buttons=8
|
||||
|
||||
# Number of LEDs
|
||||
Leds=3
|
||||
|
||||
# Number of DPI presets
|
||||
Dpis=2
|
||||
|
||||
# Set if device is wireless
|
||||
# Wireless=1
|
||||
|
||||
# DPI range in format min:max@step
|
||||
DpiRange=100:16000@100
|
||||
|
||||
# Button index mapping
|
||||
# ButtonMapping=0;1;2;6;7;5;3;4
|
||||
|
||||
# Device quirks
|
||||
# Quirk=DOUBLE_DPI;STRIX_PROFILE
|
||||
|
||||
11
data/devices/glorious-model-d.device
Normal file
11
data/devices/glorious-model-d.device
Normal file
@@ -0,0 +1,11 @@
|
||||
[Device]
|
||||
Name=Glorious Model D
|
||||
DeviceMatch=usb:258a:0033
|
||||
Driver=sinowealth
|
||||
|
||||
[Driver/sinowealth/devices/GloriousModelD]
|
||||
ButtonCount=6
|
||||
DeviceName=Glorious Model D
|
||||
FwVersion=V102
|
||||
LedType=RBG
|
||||
SensorType=PMW3360
|
||||
11
data/devices/glorious-model-o.device
Normal file
11
data/devices/glorious-model-o.device
Normal file
@@ -0,0 +1,11 @@
|
||||
[Device]
|
||||
Name=Glorious Model O/O-
|
||||
DeviceMatch=usb:258a:0036
|
||||
Driver=sinowealth
|
||||
|
||||
[Driver/sinowealth/devices/GloriousModelO]
|
||||
ButtonCount=6
|
||||
DeviceName=Glorious Model O/O- (updated firmware)
|
||||
FwVersion=V103
|
||||
LedType=RBG
|
||||
SensorType=PMW3360
|
||||
5
data/devices/logitech-M500s.device
Normal file
5
data/devices/logitech-M500s.device
Normal file
@@ -0,0 +1,5 @@
|
||||
# Logitech M500s
|
||||
[Device]
|
||||
Name=Logitech M500s
|
||||
DeviceMatch=usb:046d:c093
|
||||
Driver=hidpp20
|
||||
5
data/devices/logitech-M545.device
Normal file
5
data/devices/logitech-M545.device
Normal file
@@ -0,0 +1,5 @@
|
||||
# Logitech M545
|
||||
[Device]
|
||||
Name=Logitech M545
|
||||
DeviceMatch=usb:046d:4052
|
||||
Driver=hidpp20
|
||||
5
data/devices/logitech-M585-M590.device
Normal file
5
data/devices/logitech-M585-M590.device
Normal file
@@ -0,0 +1,5 @@
|
||||
# Logitech M585/M590
|
||||
[Device]
|
||||
Name=Logitech M585/M590
|
||||
DeviceMatch=usb:046d:406b;bluetooth:046d:b01b
|
||||
Driver=hidpp20
|
||||
4
data/devices/logitech-M720.device
Normal file
4
data/devices/logitech-M720.device
Normal file
@@ -0,0 +1,4 @@
|
||||
[Device]
|
||||
Name=Logitech M720
|
||||
DeviceMatch=usb:046d:405e;bluetooth:046d:b015
|
||||
Driver=hidpp20
|
||||
5
data/devices/logitech-MX-Anywhere-3.device
Normal file
5
data/devices/logitech-MX-Anywhere-3.device
Normal file
@@ -0,0 +1,5 @@
|
||||
# Logitech MX Anywhere 3
|
||||
[Device]
|
||||
Name=Logitech MX Anywhere 3
|
||||
DeviceMatch=bluetooth:046d:b025;usb:046d:4090
|
||||
Driver=hidpp20
|
||||
@@ -1,4 +1,4 @@
|
||||
[Device]
|
||||
Name=Logitech MX Anywhere 2
|
||||
DeviceMatch=usb:046d:404a;bluetooth:046d:b013;bluetooth:046d:b018
|
||||
DeviceMatch=usb:046d:404a;usb:046d:4072;usb:046d:4063;bluetooth:046d:b013;bluetooth:046d:b018;bluetooth:046d:b01f
|
||||
Driver=hidpp20
|
||||
|
||||
4
data/devices/logitech-MX-Anywhere3B.device
Normal file
4
data/devices/logitech-MX-Anywhere3B.device
Normal file
@@ -0,0 +1,4 @@
|
||||
# Logitech MX Anywhere 3B
|
||||
[Device]
|
||||
Name=Logitech MX Anywhere 3B
|
||||
DeviceMatch=bluetooth:046d:b02d
|
||||
5
data/devices/logitech-MX-Master-3.device
Normal file
5
data/devices/logitech-MX-Master-3.device
Normal file
@@ -0,0 +1,5 @@
|
||||
# Logitech MX Master 3
|
||||
[Device]
|
||||
Name=Logitech MX Master 3
|
||||
DeviceMatch=bluetooth:046d:b023;usb:046d:4082
|
||||
Driver=hidpp20
|
||||
@@ -1,5 +1,5 @@
|
||||
# Logitech MX Master
|
||||
[Device]
|
||||
Name=Logitech MX Master
|
||||
DeviceMatch=usb:046d:4041;bluetooth:046d:b012;usb:046d:4060;bluetooth:046d:b017
|
||||
DeviceMatch=usb:046d:4041;bluetooth:046d:b012;usb:046d:4060;bluetooth:046d:b017;bluetooth:046d:b01e;usb:046d:4071
|
||||
Driver=hidpp20
|
||||
|
||||
4
data/devices/logitech-MX-Vertical.device
Normal file
4
data/devices/logitech-MX-Vertical.device
Normal file
@@ -0,0 +1,4 @@
|
||||
[Device]
|
||||
Name=Logitech MX Vertical
|
||||
DeviceMatch=bluetooth:046d:b020;usb:046d:407b;usb:046d:c08a
|
||||
Driver=hidpp20
|
||||
8
data/devices/logitech-g-powerplay.device
Normal file
8
data/devices/logitech-g-powerplay.device
Normal file
@@ -0,0 +1,8 @@
|
||||
[Device]
|
||||
Name=Logitech G Powerplay
|
||||
DeviceMatch=usb:046d:405f
|
||||
LedTypes=logo
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
DeviceIndex=7
|
||||
5
data/devices/logitech-g-pro-x-wireless-superlight.device
Normal file
5
data/devices/logitech-g-pro-x-wireless-superlight.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Logitech G Pro X Wireless Superlight
|
||||
DeviceMatch=usb:046d:4093;usb:046d:c094
|
||||
LedTypes=logo;battery;dpi;
|
||||
Driver=hidpp20
|
||||
@@ -1,6 +1,6 @@
|
||||
# G102 and G203 (USB)
|
||||
# G102, G103 and G203 (USB)
|
||||
[Device]
|
||||
Name=Logitech Gaming Mouse G102/G103
|
||||
DeviceMatch=usb:046d:c084
|
||||
Name=Logitech Gaming Mouse G102/G103/G203
|
||||
DeviceMatch=usb:046d:c084;usb:046d:c092
|
||||
Driver=hidpp20
|
||||
LedTypes=logo;side;
|
||||
|
||||
8
data/devices/logitech-g305.device
Normal file
8
data/devices/logitech-g305.device
Normal file
@@ -0,0 +1,8 @@
|
||||
[Device]
|
||||
Name=Logitech Gaming Mouse G305
|
||||
DeviceMatch=usb:046d:4074
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
Leds=0
|
||||
Quirk=G305
|
||||
@@ -2,4 +2,5 @@
|
||||
[Device]
|
||||
Name=Logitech G402 Gaming Mouse
|
||||
DeviceMatch=usb:046d:c07e
|
||||
LedTypes=logo
|
||||
Driver=hidpp20
|
||||
|
||||
@@ -7,3 +7,4 @@ Driver=hidpp10
|
||||
[Driver/hidpp10]
|
||||
DpiList=400;800;1600;2000
|
||||
Profiles=1
|
||||
ProfileType=G500
|
||||
|
||||
4
data/devices/logitech-g502-hero-wireless.device
Normal file
4
data/devices/logitech-g502-hero-wireless.device
Normal file
@@ -0,0 +1,4 @@
|
||||
[Device]
|
||||
Name=Logitech G502 Hero Wireless
|
||||
DeviceMatch=usb:046d:407f;usb:046d:c08d
|
||||
Driver=hidpp20
|
||||
4
data/devices/logitech-g502-x-wireless.device
Normal file
4
data/devices/logitech-g502-x-wireless.device
Normal file
@@ -0,0 +1,4 @@
|
||||
[Device]
|
||||
Name=Logitech G502 X Wireless
|
||||
DeviceMatch=usb:046d:c098
|
||||
Driver=hidpp20
|
||||
4
data/devices/logitech-g502-x.device
Normal file
4
data/devices/logitech-g502-x.device
Normal file
@@ -0,0 +1,4 @@
|
||||
[Device]
|
||||
Name=Logitech G502 X
|
||||
DeviceMatch=usb:046d:c099
|
||||
Driver=hidpp20
|
||||
5
data/devices/logitech-g513.device
Normal file
5
data/devices/logitech-g513.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Logitech G513
|
||||
DeviceMatch=usb:046d:c33c
|
||||
LedTypes=switches
|
||||
Driver=hidpp20
|
||||
@@ -6,3 +6,4 @@ Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
DeviceIndex=1
|
||||
Quirk=G602
|
||||
|
||||
4
data/devices/logitech-g604.device
Normal file
4
data/devices/logitech-g604.device
Normal file
@@ -0,0 +1,4 @@
|
||||
[Device]
|
||||
Name=Logitech G604
|
||||
DeviceMatch=usb:046d:4085;bluetooth:046d:b024
|
||||
Driver=hidpp20
|
||||
8
data/devices/logitech-g635.device
Normal file
8
data/devices/logitech-g635.device
Normal file
@@ -0,0 +1,8 @@
|
||||
[Device]
|
||||
Name=Logitech G635
|
||||
DeviceMatch=usb:046d:0a89
|
||||
LedTypes=logo;side
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
Buttons=0
|
||||
@@ -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
|
||||
7
data/devices/logitech-g733.device
Normal file
7
data/devices/logitech-g733.device
Normal file
@@ -0,0 +1,7 @@
|
||||
[Device]
|
||||
Name=Logitech G733 Gaming Headset
|
||||
DeviceMatch=usb:046d:0ab5
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
Buttons=0
|
||||
5
data/devices/logitech-g815.device
Normal file
5
data/devices/logitech-g815.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Logitech G815
|
||||
DeviceMatch=usb:046d:c33f
|
||||
LedTypes=logo;switches
|
||||
Driver=hidpp20
|
||||
8
data/devices/logitech-g910.device
Normal file
8
data/devices/logitech-g910.device
Normal file
@@ -0,0 +1,8 @@
|
||||
[Device]
|
||||
Name=Logitech G910
|
||||
DeviceMatch=usb:046d:c335
|
||||
LedTypes=logo;switches
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
DeviceIndex=ff
|
||||
8
data/devices/logitech-g915-tkl.device
Normal file
8
data/devices/logitech-g915-tkl.device
Normal file
@@ -0,0 +1,8 @@
|
||||
[Device]
|
||||
Name=Logitech G915 TKL
|
||||
DeviceMatch=usb:046d:c343;bluetooth:046d:b35f
|
||||
LedTypes=logo;switches
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
DeviceIndex=1
|
||||
8
data/devices/logitech-g915.device
Normal file
8
data/devices/logitech-g915.device
Normal file
@@ -0,0 +1,8 @@
|
||||
[Device]
|
||||
Name=Logitech G915
|
||||
DeviceMatch=usb:046d:c33e
|
||||
LedTypes=logo;switches
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
DeviceIndex=1
|
||||
5
data/devices/logitech-g935.device
Normal file
5
data/devices/logitech-g935.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Logitech G935
|
||||
DeviceMatch=usb:046d:0a87
|
||||
LedTypes=logo;side
|
||||
Driver=hidpp20
|
||||
@@ -4,8 +4,10 @@
|
||||
Name=G9x [Original]
|
||||
DeviceMatch=usb:046d:c066
|
||||
Driver=hidpp10
|
||||
LedTypes=dpi
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiRange=0:5700@23.53
|
||||
DpiRange=200:5700@50
|
||||
ProfileType=G500
|
||||
Profiles=5
|
||||
Leds=1
|
||||
5
data/devices/nubwo-x7-spectrum.device
Normal file
5
data/devices/nubwo-x7-spectrum.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Nubwo x7 spectrum
|
||||
DeviceMatch=usb:258a:0012
|
||||
Driver=sinowealth_nubwo
|
||||
|
||||
4
data/devices/openinput.device
Normal file
4
data/devices/openinput.device
Normal file
@@ -0,0 +1,4 @@
|
||||
[Device]
|
||||
Name=openinput
|
||||
DeviceMatch=usb:1d50:616a
|
||||
Driver=openinput
|
||||
4
data/devices/roccat-kone-emp.device
Normal file
4
data/devices/roccat-kone-emp.device
Normal file
@@ -0,0 +1,4 @@
|
||||
[Device]
|
||||
Name=Roccat Kone EMP
|
||||
DeviceMatch=usb:1e7d:2e24
|
||||
Driver=roccat-kone-emp
|
||||
4
data/devices/roccat-kone-pure.device
Normal file
4
data/devices/roccat-kone-pure.device
Normal file
@@ -0,0 +1,4 @@
|
||||
[Device]
|
||||
Name=Roccat Kone Pure
|
||||
DeviceMatch=usb:1e7d:2dc2;usb:1e7d:2dbe;usb:1e7d:2dcb
|
||||
Driver=roccat-kone-pure
|
||||
38
data/devices/sinowealth-0027.device
Normal file
38
data/devices/sinowealth-0027.device
Normal file
@@ -0,0 +1,38 @@
|
||||
[Device]
|
||||
DeviceMatch=usb:258a:0027
|
||||
Driver=sinowealth
|
||||
Name=SinoWealth Generic Mouse (0027)
|
||||
|
||||
[Driver/sinowealth/devices/DreamMachinesDM5Blink]
|
||||
ButtonCount=8
|
||||
DeviceName=DreamMachines DM5 Blink
|
||||
FwVersion=3106
|
||||
LedType=RGB
|
||||
SensorType=PMW3389
|
||||
|
||||
[Driver/sinowealth/devices/GWolvesHatiHTM]
|
||||
ButtonCount=6
|
||||
# Can be both `Ace` and `Classic` version.
|
||||
DeviceName=G-Wolves Hati HT-M Wired
|
||||
FwVersion=V161
|
||||
LedType=None
|
||||
# Can also be PMW3389
|
||||
SensorType=PMW3360
|
||||
|
||||
[Driver/sinowealth/devices/GenesysXenon770]
|
||||
# There are 5 main buttons.
|
||||
# Side buttons are detachable.
|
||||
# There can be either 3 or 9 buttons on the side.
|
||||
# Going for the maximum count: 5 + 9 = 14.
|
||||
ButtonCount=14
|
||||
DeviceName=Genesys Xenon 770
|
||||
FwVersion=3110
|
||||
LedType=RGB
|
||||
SensorType=PMW3327
|
||||
|
||||
[Driver/sinowealth/devices/GloriousModelOOld]
|
||||
ButtonCount=6
|
||||
DeviceName=Glorious Model O (old firmware)
|
||||
FwVersion=V102
|
||||
LedType=RBG
|
||||
SensorType=PMW3360
|
||||
12
data/devices/sinowealth-0029.device
Normal file
12
data/devices/sinowealth-0029.device
Normal file
@@ -0,0 +1,12 @@
|
||||
[Device]
|
||||
DeviceMatch=usb:258a:0029
|
||||
Driver=sinowealth
|
||||
Name=SinoWealth Generic Mouse (0029)
|
||||
|
||||
# Configuration data size is 131 bytes.
|
||||
[Driver/sinowealth/devices/MachenikeM620]
|
||||
ButtonCount=6
|
||||
DeviceName=Machenike M620
|
||||
FwVersion=V127
|
||||
LedType=RGB
|
||||
SensorType=PMW3389
|
||||
20
data/devices/sinowealth-1007.device
Normal file
20
data/devices/sinowealth-1007.device
Normal file
@@ -0,0 +1,20 @@
|
||||
[Device]
|
||||
DeviceMatch=usb:258a:1007
|
||||
Driver=sinowealth
|
||||
Name=SinoWealth Generic Mouse (1007)
|
||||
|
||||
# This does NOT work. See libratbag/libratbag#1296.
|
||||
# For future: ID of A3050 sensor is 0x2, and maximum DPI is 4000.
|
||||
#[Driver/sinowealth/devices/AntEsportsGM500]
|
||||
#ButtonCount=7
|
||||
#DeviceName=Ant Esports GM500
|
||||
#FwVersion=0910
|
||||
#LedType=RBG
|
||||
#SensorType=A3050
|
||||
|
||||
[Driver/sinowealth/devices/MarvoScorpionG961]
|
||||
ButtonCount=6
|
||||
DeviceName=Marvo Scorpion G961
|
||||
FwVersion=9677
|
||||
LedType=RGB
|
||||
SensorType=PMW3327
|
||||
11
data/devices/steelseries-kinzu-v2-pro.device
Normal file
11
data/devices/steelseries-kinzu-v2-pro.device
Normal file
@@ -0,0 +1,11 @@
|
||||
[Device]
|
||||
Name=SteelSeries Kinzu V2 Pro Edition
|
||||
DeviceMatch=usb:1038:1366
|
||||
Driver=steelseries
|
||||
|
||||
[Driver/steelseries]
|
||||
DeviceVersion=1
|
||||
Buttons=3
|
||||
Leds=0
|
||||
DpiList=400;800;1600;3200
|
||||
MacroLength=0
|
||||
11
data/devices/steelseries-kinzu-v3.device
Normal file
11
data/devices/steelseries-kinzu-v3.device
Normal file
@@ -0,0 +1,11 @@
|
||||
[Device]
|
||||
Name=SteelSeries Kinzu V3
|
||||
DeviceMatch=usb:1038:1388
|
||||
Driver=steelseries
|
||||
|
||||
[Driver/steelseries]
|
||||
DeviceVersion=1
|
||||
Buttons=3
|
||||
Leds=0
|
||||
DpiList=400;800;1600;2000
|
||||
MacroLength=0
|
||||
13
data/devices/steelseries-rival-100.device
Normal file
13
data/devices/steelseries-rival-100.device
Normal file
@@ -0,0 +1,13 @@
|
||||
[Device]
|
||||
# 100; 100 (Dell China); 100 Dota 2 Edition (retail); 100 Dota 2 Edition (Lenovo); 105.
|
||||
DeviceMatch=usb:1038:1702;usb:1038:170a;usb:1038:170b;usb:1038:170c;usb:1038:1814
|
||||
Driver=steelseries
|
||||
Name=SteelSeries Rival 100/105
|
||||
|
||||
[Driver/steelseries]
|
||||
# Actually 6.
|
||||
Buttons=0
|
||||
DeviceVersion=1
|
||||
DpiList=250;500;1000;1250;1500;1750;2000;4000
|
||||
Leds=1
|
||||
Quirk=Rival100
|
||||
@@ -1,6 +1,6 @@
|
||||
[Device]
|
||||
Name=SteelSeries Rival
|
||||
DeviceMatch=usb:1038:1384
|
||||
DeviceMatch=usb:1038:1384;usb:1038:1392;usb:1038:1710;usb:1038:1712;usb:1038:171c;usb:1038:1394;usb:1038:171a;usb:1038:1716;usb:1038:1714;usb:1038:1718
|
||||
Driver=steelseries
|
||||
|
||||
[Driver/steelseries]
|
||||
|
||||
@@ -10,5 +10,4 @@ Buttons=8
|
||||
Leds=1
|
||||
DpiRange=90:5670@90
|
||||
MacroLength=1
|
||||
MonoLed=1
|
||||
ShortButton=1
|
||||
Quirk=SenseiRAW
|
||||
|
||||
@@ -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>
|
||||
|
||||
13
doc/dbus.rst
13
doc/dbus.rst
@@ -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
|
||||
|
||||
253
meson.build
253
meson.build
@@ -1,14 +1,14 @@
|
||||
project('libratbag', 'c',
|
||||
version : '0.9.906',
|
||||
version : '0.17',
|
||||
license : 'MIT/Expat',
|
||||
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
|
||||
meson_version : '>= 0.40.0')
|
||||
meson_version : '>= 0.50.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,22 @@ lib_libhidpp = static_library('hidpp',
|
||||
dependencies : deps_libhidpp)
|
||||
dep_libhidpp = declare_dependency(link_with: lib_libhidpp)
|
||||
|
||||
### liblur.a ####
|
||||
### libasus.a ####
|
||||
src_libasus = [
|
||||
'src/asus.c',
|
||||
]
|
||||
|
||||
deps_libasus = [ ]
|
||||
|
||||
lib_libasus = static_library('asus',
|
||||
src_libasus,
|
||||
dependencies : deps_libasus)
|
||||
dep_libasus = declare_dependency(link_with: lib_libasus)
|
||||
|
||||
### liblur ####
|
||||
#
|
||||
# liblur is the library to handle logitech unifying receivers.
|
||||
#
|
||||
install_headers('src/liblur.h')
|
||||
|
||||
src_liblur = [
|
||||
@@ -129,6 +147,7 @@ src_liblur = [
|
||||
deps_liblur = [
|
||||
dep_libutil,
|
||||
dep_libhidpp,
|
||||
dep_libasus,
|
||||
]
|
||||
|
||||
lur_mapfile = 'src/liblur.sym'
|
||||
@@ -154,17 +173,28 @@ 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',
|
||||
'src/driver-asus.c',
|
||||
'src/driver-etekcity.c',
|
||||
'src/driver-hidpp20.c',
|
||||
'src/driver-hidpp10.c',
|
||||
'src/driver-logitech-g300.c',
|
||||
'src/driver-logitech-g600.c',
|
||||
'src/driver-roccat.c',
|
||||
'src/driver-roccat-kone-emp.c',
|
||||
'src/driver-roccat-kone-pure.c',
|
||||
'src/driver-gskill.c',
|
||||
'src/driver-openinput.c',
|
||||
'src/driver-steelseries.c',
|
||||
'src/driver-steelseries.h',
|
||||
'src/driver-sinowealth.c',
|
||||
'src/driver-sinowealth.h',
|
||||
'src/driver-sinowealth-nubwo.c',
|
||||
'src/driver-test.c',
|
||||
'src/libratbag.c',
|
||||
'src/libratbag.h',
|
||||
@@ -182,8 +212,10 @@ deps_libratbag = [
|
||||
dep_udev,
|
||||
dep_libevdev,
|
||||
dep_glib,
|
||||
dep_json_glib,
|
||||
dep_libutil,
|
||||
dep_libhidpp,
|
||||
dep_libasus,
|
||||
]
|
||||
|
||||
lib_libratbag = static_library('ratbag',
|
||||
@@ -242,6 +274,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,
|
||||
@@ -263,69 +297,19 @@ man_lur_command = configure_file (
|
||||
)
|
||||
|
||||
#### data files ####
|
||||
# list ordered by git ls-files data/devices/*.device
|
||||
data_files = files(
|
||||
'data/devices/etekcity-scroll-alpha.device',
|
||||
'data/devices/gskill-MX-780.device',
|
||||
'data/devices/logitech-M325.device',
|
||||
'data/devices/logitech-M570.device',
|
||||
'data/devices/logitech-M705.device',
|
||||
'data/devices/logitech-MX-Ergo.device',
|
||||
'data/devices/logitech-MX-Master.device',
|
||||
'data/devices/logitech-MX-Master-2S.device',
|
||||
'data/devices/logitech-MX518.device',
|
||||
'data/devices/logitech-T650.device',
|
||||
'data/devices/logitech-Wireless-Touchpad.device',
|
||||
'data/devices/logitech-g-pro-wireless.device',
|
||||
'data/devices/logitech-g-pro.device',
|
||||
'data/devices/logitech-g102-g203.device',
|
||||
'data/devices/logitech-g300.device',
|
||||
'data/devices/logitech-g302.device',
|
||||
'data/devices/logitech-g303.device',
|
||||
'data/devices/logitech-g402.device',
|
||||
'data/devices/logitech-g403-hero.device',
|
||||
'data/devices/logitech-g403-wireless.device',
|
||||
'data/devices/logitech-g403.device',
|
||||
'data/devices/logitech-g5-2007.device',
|
||||
'data/devices/logitech-g5.device',
|
||||
'data/devices/logitech-g500.device',
|
||||
'data/devices/logitech-g500s.device',
|
||||
'data/devices/logitech-g502-hero.device',
|
||||
'data/devices/logitech-g502-proteus-core.device',
|
||||
'data/devices/logitech-g502-proteus-spectrum.device',
|
||||
'data/devices/logitech-g600.device',
|
||||
'data/devices/logitech-g602.device',
|
||||
'data/devices/logitech-g603.device',
|
||||
'data/devices/logitech-g7.device',
|
||||
'data/devices/logitech-g700-wireless.device',
|
||||
'data/devices/logitech-g700.device',
|
||||
'data/devices/logitech-g700s-wireless.device',
|
||||
'data/devices/logitech-g700s.device',
|
||||
'data/devices/logitech-g703-hero.device',
|
||||
'data/devices/logitech-g703.device',
|
||||
'data/devices/logitech-g9.device',
|
||||
'data/devices/logitech-g900.device',
|
||||
'data/devices/logitech-g903-hero.device',
|
||||
'data/devices/logitech-g903.device',
|
||||
'data/devices/logitech-g9x-[Call-of-Duty-MW3-Edition].device',
|
||||
'data/devices/logitech-g9x-[Original].device',
|
||||
'data/devices/logitech-Marathon-M705.device',
|
||||
'data/devices/logitech-MX-Anywhere2.device',
|
||||
'data/devices/logitech-MX-Anywhere2S.device',
|
||||
'data/devices/roccat-kone-xtd.device',
|
||||
'data/devices/steelseries-kinzu-v2.device',
|
||||
'data/devices/steelseries-rival-310.device',
|
||||
'data/devices/steelseries-rival-600.device',
|
||||
'data/devices/steelseries-rival.device',
|
||||
'data/devices/steelseries-sensei-310.device',
|
||||
'data/devices/steelseries-sensei-raw.device',
|
||||
)
|
||||
install_subdir('data/devices',
|
||||
strip_directory : true,
|
||||
exclude_files : ['device.example', 'README.md'],
|
||||
install_dir : join_paths(get_option('datadir'), 'libratbag'))
|
||||
|
||||
install_data(data_files,
|
||||
install_dir : join_paths(get_option('datadir'), 'libratbag'))
|
||||
data_parse_test = find_program(join_paths(meson.source_root(), 'test/data-parse-test.py'))
|
||||
test('data-parse-test', data_parse_test, args: 'data/devices')
|
||||
|
||||
data_parse_test = find_program(join_paths(meson.source_root(), 'data/devices/data-parse-test.py'))
|
||||
test('data-parse-test', data_parse_test, args: data_files)
|
||||
duplicate_test = find_program(join_paths(meson.source_root(), 'test/duplicate-check.py'))
|
||||
test('duplicate-test', duplicate_test, args : 'data/devices')
|
||||
|
||||
receiver_id_test = find_program(join_paths(meson.source_root(), 'test/receiver-check.py'))
|
||||
test('receiver-id-test', receiver_id_test, args : 'data/devices')
|
||||
|
||||
#### tests ####
|
||||
enable_tests = get_option('tests')
|
||||
@@ -361,19 +345,25 @@ if enable_tests
|
||||
test('test-util', test_util)
|
||||
test('test-iconv-helper', test_iconv_helper)
|
||||
|
||||
valgrind = find_program('valgrind')
|
||||
valgrind_suppressions_file = join_paths(meson.source_root(), 'test', 'valgrind.suppressions')
|
||||
add_test_setup('valgrind',
|
||||
exe_wrapper : [
|
||||
valgrind,
|
||||
'--leak-check=full',
|
||||
'--quiet',
|
||||
'--error-exitcode=3',
|
||||
'--suppressions=' + valgrind_suppressions_file ],
|
||||
timeout_multiplier: 5)
|
||||
valgrind = find_program('valgrind', required : false)
|
||||
if valgrind.found()
|
||||
valgrind_suppressions_file = join_paths(meson.source_root(), 'test', 'valgrind.suppressions')
|
||||
add_test_setup('valgrind',
|
||||
exe_wrapper : [
|
||||
valgrind,
|
||||
'--leak-check=full',
|
||||
'--quiet',
|
||||
'--error-exitcode=3',
|
||||
'--suppressions=' + valgrind_suppressions_file ],
|
||||
timeout_multiplier: 5)
|
||||
endif
|
||||
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 +376,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 +399,33 @@ 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.
|
||||
#
|
||||
|
||||
dbus_devel_policy = configure_file(input : 'dbus/org.freedesktop.ratbag_devel1.conf.in',
|
||||
output : 'org.freedesktop.ratbag_devel1.conf',
|
||||
copy: true)
|
||||
|
||||
# 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 +464,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',
|
||||
@@ -487,23 +491,41 @@ configure_file(input : 'dbus/org.freedesktop.ratbag1.conf.in',
|
||||
|
||||
#### tools ####
|
||||
|
||||
if meson.version().version_compare('<0.48.0')
|
||||
python = import('python3')
|
||||
py3 = python.find_python()
|
||||
else
|
||||
pymod = import('python')
|
||||
py3 = pymod.find_installation()
|
||||
# This is a special idiomatic construct in meson to get a fast dependency that
|
||||
# doesn't exist and isn't logged as "not found".
|
||||
dep_python3 = dependency('', required : false)
|
||||
pymod = import('python')
|
||||
py3 = pymod.find_installation()
|
||||
if meson.version().version_compare('>= 0.53.0')
|
||||
dep_python3 = py3.dependency(embed : true)
|
||||
endif
|
||||
py_version = py3.language_version()
|
||||
|
||||
# From python 3.8 we neeed python3-embed
|
||||
embed = py_version.version_compare('>= 3.8') ? '-embed' : ''
|
||||
if not dep_python3.found()
|
||||
dep_python3 = dependency('python-@0@@1@'.format(py_version, embed), required : false)
|
||||
if not dep_python3.found()
|
||||
dep_python3 = dependency('python3@0@'.format(embed))
|
||||
endif
|
||||
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 +538,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',
|
||||
@@ -549,12 +571,6 @@ swig_gen = generator(
|
||||
'@INPUT@'],
|
||||
)
|
||||
|
||||
# From python 3.8 we neeed python3-embed
|
||||
dep_python3 = dependency('python3-embed', required: false)
|
||||
if not dep_python3.found()
|
||||
dep_python3 = dependency('python3')
|
||||
endif
|
||||
|
||||
wrapper_deps = [
|
||||
dep_python3,
|
||||
dep_libratbag,
|
||||
@@ -567,7 +583,25 @@ shared_library(
|
||||
'libratbag',
|
||||
c_source,
|
||||
name_prefix: '_',
|
||||
c_args : ['-Wno-missing-prototypes', '-Wno-format-nonliteral'],
|
||||
c_args : ['-Wno-missing-prototypes',
|
||||
'-Wno-format-nonliteral',
|
||||
'-Wno-missing-field-initializers'],
|
||||
extra_files: [ i_source ] + src_libratbag ,
|
||||
dependencies: deps_libratbag + wrapper_deps,
|
||||
include_directories : include_directories('src', 'tools'),
|
||||
install: false,
|
||||
)
|
||||
|
||||
i_source = join_paths(meson.source_root(), 'src', 'hidpp.i')
|
||||
c_source = swig_gen.process(i_source)
|
||||
shared_library(
|
||||
'hidpp',
|
||||
c_source,
|
||||
name_prefix: '_',
|
||||
c_args : ['-Wno-missing-prototypes',
|
||||
'-Wno-format-nonliteral',
|
||||
'-Wno-missing-field-initializers',
|
||||
'-Wno-strict-prototypes'],
|
||||
extra_files: [ i_source ] + src_libratbag ,
|
||||
dependencies: deps_libratbag + wrapper_deps,
|
||||
include_directories : include_directories('src', 'tools'),
|
||||
@@ -580,14 +614,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 ####
|
||||
|
||||
12
ratbagd/.gitignore
vendored
12
ratbagd/.gitignore
vendored
@@ -1,12 +0,0 @@
|
||||
*.a
|
||||
*.log
|
||||
*.o
|
||||
*.stamp
|
||||
*.swp
|
||||
*.trs
|
||||
*~
|
||||
.dirstamp
|
||||
.libs/
|
||||
/*.tar.xz
|
||||
tags
|
||||
__pycache__
|
||||
@@ -41,6 +41,8 @@
|
||||
#include "libratbag-util.h"
|
||||
|
||||
struct ratbagd_device {
|
||||
unsigned int refcount;
|
||||
|
||||
struct ratbagd *ctx;
|
||||
RBNode node;
|
||||
char *sysname;
|
||||
@@ -170,8 +172,11 @@ static void ratbagd_device_commit_pending(void *data)
|
||||
int r;
|
||||
|
||||
r = ratbag_device_commit(device->lib_device);
|
||||
if (r)
|
||||
log_error("error committing device (%d)\n", r);
|
||||
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 +187,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 +235,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 +245,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 +288,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 +307,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)
|
||||
|
||||
563
ratbagd/ratbagd-json.c
Normal file
563
ratbagd/ratbagd-json.c
Normal file
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
* 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_SWITCHES)
|
||||
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-cycle-down", 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;
|
||||
g_autoptr(GList) list = 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;
|
||||
|
||||
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
28
ratbagd/ratbagd-json.h
Normal 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);
|
||||
@@ -507,10 +507,8 @@ ratbagd_profile_set_report_rate(sd_bus *bus,
|
||||
return r;
|
||||
|
||||
/* basic sanity check */
|
||||
if (rate > 5000)
|
||||
rate = 5000;
|
||||
else if (rate % 100)
|
||||
rate = rate - (rate % 100);
|
||||
if (rate > 8000)
|
||||
rate = 8000;
|
||||
|
||||
r = ratbag_profile_set_report_rate(profile->lib_profile, rate);
|
||||
if (r == 0) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -3,12 +3,27 @@
|
||||
ratbagd \- system daemon to introspect and modify configurable mice
|
||||
.SH SYNOPSIS
|
||||
.B ratbagd
|
||||
.RB [ \-\-verbose ]
|
||||
.RB [ \-\-verbose[=debug]|\-\-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=debug
|
||||
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
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <libratbag.h>
|
||||
#include <libudev.h>
|
||||
#include <stdio.h>
|
||||
@@ -166,9 +167,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 +202,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 +285,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 +347,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)
|
||||
@@ -354,6 +357,7 @@ static int ratbagd_new(struct ratbagd **out)
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_verbose("Initializing libratbag\n");
|
||||
ctx->lib_ctx = ratbag_create_context(&ratbagd_lib_interface, ctx);
|
||||
if (!ctx->lib_ctx)
|
||||
return -ENOMEM;
|
||||
@@ -518,13 +522,99 @@ static int ratbagd_run(struct ratbagd *ctx)
|
||||
ctx);
|
||||
sd_event_add_post(ctx->event, NULL, before_idle_cb, ctx);
|
||||
|
||||
log_verbose("DBus server ready\n");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (mkdir_p(dirname(strdupa(DBUS_POLICY_DST)), 0755)) {
|
||||
log_error("Failed to create destination path: %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;
|
||||
int r;
|
||||
int r = 0;
|
||||
|
||||
#if DISABLE_COREDUMP
|
||||
const struct rlimit corelimit = { 0, 0 };
|
||||
@@ -538,18 +628,21 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
} else if (streq(argv[1], "--quiet")) {
|
||||
log_level = LL_QUIET;
|
||||
} else if (streq(argv[1], "--verbose=raw")) {
|
||||
} else if (streq(argv[1], "--verbose") || streq(argv[1], "--verbose=raw")) {
|
||||
log_level = LL_RAW;
|
||||
} else if (streq(argv[1], "--verbose")) {
|
||||
} else if (streq(argv[1], "--verbose=debug")) {
|
||||
log_level = LL_VERBOSE;
|
||||
} else {
|
||||
fprintf(stderr, "Usage: %s [--version | --quiet | --verbose[=raw]]\n",
|
||||
fprintf(stderr, "Usage: %s [--version | --quiet | --verbose[=debug]]\n",
|
||||
program_invocation_short_name);
|
||||
r = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!install_ratbagd_devel_dbus_policy())
|
||||
goto exit;
|
||||
|
||||
r = ratbagd_new(&ctx);
|
||||
if (r < 0)
|
||||
goto exit;
|
||||
@@ -558,6 +651,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
r = ratbagd_run(ctx);
|
||||
|
||||
remove_ratbagd_devel_dbus_policy();
|
||||
exit:
|
||||
ratbagd_free(ctx);
|
||||
|
||||
@@ -603,5 +697,5 @@ void ratbagd_schedule_task(struct ratbagd *ctx,
|
||||
cb->callback = callback;
|
||||
cb->userdata = userdata;
|
||||
|
||||
sd_event_add_post(ctx->event, &source, ratbagd_callback_handler, cb);
|
||||
sd_event_add_defer(ctx->event, &source, ratbagd_callback_handler, cb);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
120
release.sh
120
release.sh
@@ -90,13 +90,11 @@ check_option_args() {
|
||||
#
|
||||
check_modules_specification() {
|
||||
|
||||
if [ x"$MODFILE" = x ]; then
|
||||
if [ x"${INPUT_MODULES}" = x ]; then
|
||||
echo ""
|
||||
echo "Error: no modules specified (blank command line)."
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
if [ x"${INPUT_MODULES}" = x ]; then
|
||||
echo ""
|
||||
echo "Error: no modules specified (blank command line)."
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
}
|
||||
@@ -112,7 +110,7 @@ Subject: [ANNOUNCE] $pkg_name $pkg_version
|
||||
To: $list_to
|
||||
Cc: $list_cc
|
||||
|
||||
libratbag $tag_name is out.
|
||||
$pkg_name $tag_name is out.
|
||||
|
||||
The list of interesting changes are:
|
||||
|
||||
@@ -129,43 +127,12 @@ RELEASE
|
||||
The libratbag project does not generate tarballs for releases, you can
|
||||
grab one directly from github:
|
||||
|
||||
https://github.com/libratbag/libratbag/archive/$tag_name/$tarball
|
||||
https://github.com/libratbag/$pkg_name/archive/$tag_name/$tarball
|
||||
|
||||
RELEASE
|
||||
done
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Function: read_modfile
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Read the module names from the file and set a variable to hold them
|
||||
# This will be the same interface as cmd line supplied modules
|
||||
#
|
||||
read_modfile() {
|
||||
|
||||
if [ x"$MODFILE" != x ]; then
|
||||
# Make sure the file is sane
|
||||
if [ ! -r "$MODFILE" ]; then
|
||||
echo "Error: module file '$MODFILE' is not readable or does not exist."
|
||||
exit 1
|
||||
fi
|
||||
# read from input file, skipping blank and comment lines
|
||||
while read line; do
|
||||
# skip blank lines
|
||||
if [ x"$line" = x ]; then
|
||||
continue
|
||||
fi
|
||||
# skip comment lines
|
||||
if echo "$line" | $GREP -q "^#" ; then
|
||||
continue;
|
||||
fi
|
||||
INPUT_MODULES="$INPUT_MODULES $line"
|
||||
done <"$MODFILE"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Function: print_epilog
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -215,46 +182,6 @@ process_modules() {
|
||||
done
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Function: get_section
|
||||
#------------------------------------------------------------------------------
|
||||
# Code 'return 0' on success
|
||||
# Code 'return 1' on error
|
||||
# Sets global variable $section
|
||||
get_section() {
|
||||
local module_url
|
||||
local full_module_url
|
||||
|
||||
# Obtain the git url in order to find the section to which this module belongs
|
||||
full_module_url=`git config --get remote.$remote_name.url | sed 's:\.git$::'`
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: unable to obtain git url for remote \"$remote_name\"."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# The last part of the git url will tell us the section. Look for xorg first
|
||||
echo "$full_module_url"
|
||||
module_url=`echo "$full_module_url" | $GREP -o "libratbag/.*"`
|
||||
if [ $? -eq 0 ]; then
|
||||
module_url=`echo $module_url | cut -d'/' -f2,3`
|
||||
else
|
||||
echo "Error: unable to locate a valid project url from \"$full_module_url\"."
|
||||
echo "Cannot establish url as one of libratbag"
|
||||
cd $top_src
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Find the section (subdirs) where the tarballs are to be uploaded
|
||||
# The module relative path can be app/xfs, xserver, or mesa/drm for example
|
||||
section=`echo $module_url | cut -d'/' -f1`
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: unable to extract section from $module_url first field."
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Function: process_module
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -299,26 +226,27 @@ process_module() {
|
||||
current_branch=`git branch | $GREP "\*" | sed -e "s/\* //"`
|
||||
remote_name=`git config --get branch.$current_branch.remote`
|
||||
remote_branch=`git config --get branch.$current_branch.merge | cut -d'/' -f3,4`
|
||||
remote_url_root="https://github.com/libratbag/libratbag/archive"
|
||||
echo "Info: working off the \"$current_branch\" branch tracking the remote \"$remote_name/$remote_branch\"."
|
||||
|
||||
# Obtain the section
|
||||
get_section
|
||||
if [ $? -ne 0 ]; then
|
||||
cd $top_src
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Find out the tarname from meson.build
|
||||
pkg_name=$($GREP '^project(' meson.build | sed 's|project(\([^\s,]*\).*|\1|' | sed "s/'//g")
|
||||
pkg_version=$($GREP -m 1 '^ version : ' meson.build | sed 's| version : \([^\s,]*\).*|\1|' | sed "s/'//g")
|
||||
pkg_version=$($GREP -m 1 '^[ ]*version \?: ' meson.build | sed 's|[ ]*version \?: \([^\s,]*\).*|\1|' | sed "s/'//g")
|
||||
tar_name="$pkg_name-$pkg_version"
|
||||
targz=$tar_name.tar.gz
|
||||
tag_name=$(echo "v$pkg_version" | sed 's|.0$||')
|
||||
# libratbag tags with v0.3 but piper user just 0.3
|
||||
case `git describe` in
|
||||
v*)
|
||||
tag_name=$(echo "v$pkg_version" | sed 's|\.0$||')
|
||||
;;
|
||||
*)
|
||||
tag_name=$(echo "$pkg_version" | sed 's|\.0$||')
|
||||
;;
|
||||
esac
|
||||
remote_url_root="https://github.com/libratbag/$pkg_name/archive"
|
||||
|
||||
# Now get the tarballs from github directly to compute their checksum
|
||||
remote_targz_url=$remote_url_root/$tag_name/$targz
|
||||
echo "downloading the tarbal from $remote_targz_url"
|
||||
echo "downloading the tarball from $remote_targz_url"
|
||||
curl $remote_targz_url > $targz
|
||||
|
||||
# Obtain the top commit SHA which should be the version bump
|
||||
@@ -513,13 +441,6 @@ do
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [ x"${MODFILE}" != x ]; then
|
||||
echo ""
|
||||
echo "Error: specifying both modules and --modfile is not permitted"
|
||||
echo ""
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
INPUT_MODULES="${INPUT_MODULES} $1"
|
||||
;;
|
||||
esac
|
||||
@@ -530,9 +451,6 @@ done
|
||||
# If no modules specified (blank cmd line) display help
|
||||
check_modules_specification
|
||||
|
||||
# Read the module file and normalize input in INPUT_MODULES
|
||||
read_modfile
|
||||
|
||||
# Loop through each module to release
|
||||
# Exit on error if --no-quit no specified
|
||||
process_modules
|
||||
|
||||
496
src/asus.c
Normal file
496
src/asus.c
Normal file
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Kyoken, kyoken@kyoken.ninja
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "asus.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "libratbag-data.h"
|
||||
#include "libratbag-private.h"
|
||||
|
||||
/* ASUS commands */
|
||||
#define ASUS_CMD_GET_LED_DATA 0x0312 /* get all LEDs */
|
||||
#define ASUS_CMD_GET_SETTINGS 0x0412 /* dpi, rate, button response, angle snapping */
|
||||
#define ASUS_CMD_GET_BUTTON_DATA 0x0512 /* get all buttons */
|
||||
#define ASUS_CMD_GET_PROFILE_DATA 0x0012 /* get current profile info */
|
||||
#define ASUS_CMD_SET_LED 0x2851 /* set single led */
|
||||
#define ASUS_CMD_SET_SETTING 0x3151 /* dpi / rate / button response / angle snapping */
|
||||
#define ASUS_CMD_SET_BUTTON 0x2151 /* set single button */
|
||||
#define ASUS_CMD_SET_PROFILE 0x0250 /* switch profile */
|
||||
#define ASUS_CMD_SAVE 0x0350 /* save settings */
|
||||
|
||||
/* fields order in _asus_dpiX_data, used for setting with ASUS_CMD_SET_SETTING */
|
||||
#define ASUS_FIELD_RATE 0
|
||||
#define ASUS_FIELD_RESPONSE 1
|
||||
#define ASUS_FIELD_SNAPPING 2
|
||||
|
||||
/* order is mandatory because index is mapped from ButtonMapping configuration property */
|
||||
static struct asus_button ASUS_BUTTON_MAPPING[] = {
|
||||
{ 0xf0, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 1, 0 }, /* left */
|
||||
{ 0xf1, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 2, 0 }, /* right (button 3 in xev) */
|
||||
{ 0xf2, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 3, 0 }, /* middle (button 2 in xev) */
|
||||
{ 0xe4, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 4, 0 }, /* backward (on left side) */
|
||||
{ 0xe5, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 5, 0 }, /* forward (on left side) */
|
||||
{ 0xe6, RATBAG_BUTTON_ACTION_TYPE_SPECIAL, 0, RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP },
|
||||
{ 0xe8, RATBAG_BUTTON_ACTION_TYPE_SPECIAL, 0, RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP },
|
||||
{ 0xe9, RATBAG_BUTTON_ACTION_TYPE_SPECIAL, 0, RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN },
|
||||
{ 0xe1, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 4, 0 }, /* backward on right side */
|
||||
{ 0xe2, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 5, 0 }, /* forward on right side */
|
||||
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
|
||||
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
|
||||
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
|
||||
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
|
||||
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
|
||||
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
|
||||
};
|
||||
|
||||
/* key mapping, the index is actual ASUS code */
|
||||
static unsigned char ASUS_KEY_MAPPING[] = {
|
||||
/* 00 */ 0, 0, 0, 0,
|
||||
/* 04 */ KEY_A, KEY_B, KEY_C, KEY_D,
|
||||
/* 08 */ KEY_E, KEY_F, KEY_G, KEY_H,
|
||||
/* 0C */ KEY_I, KEY_J, KEY_K, KEY_L,
|
||||
/* 0E */ KEY_M, KEY_N, KEY_O, KEY_P,
|
||||
/* 14 */ KEY_Q, KEY_R, KEY_S, KEY_T,
|
||||
/* 18 */ KEY_U, KEY_V, KEY_W, KEY_X,
|
||||
/* 1C */ KEY_Y, KEY_Z, KEY_1, KEY_2,
|
||||
/* 1E */ KEY_3, KEY_4, KEY_5, KEY_6,
|
||||
/* 24 */ KEY_7, KEY_8, KEY_9, KEY_0,
|
||||
/* 28 */ KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB,
|
||||
/* 2C */ KEY_SPACE, KEY_MINUS, KEY_KPPLUS, 0,
|
||||
/* 2E */ 0, 0, 0, 0,
|
||||
/* 34 */ 0, KEY_GRAVE, KEY_EQUAL, 0,
|
||||
/* 38 */ KEY_SLASH, 0, KEY_F1, KEY_F2,
|
||||
/* 3C */ KEY_F3, KEY_F4, KEY_F5, KEY_F6,
|
||||
/* 3E */ KEY_F7, KEY_F8, KEY_F9, KEY_F10,
|
||||
/* 44 */ KEY_F11, KEY_F12, 0, 0,
|
||||
/* 48 */ 0, 0, KEY_HOME, KEY_PAGEUP,
|
||||
/* 4C */ KEY_DELETE, 0, KEY_PAGEDOWN, KEY_RIGHT,
|
||||
/* 4E */ KEY_LEFT, KEY_DOWN, KEY_UP, 0,
|
||||
/* 54 */ 0, 0, 0, 0,
|
||||
/* 58 */ 0, KEY_KP1, KEY_KP2, KEY_KP3,
|
||||
/* 5C */ KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7,
|
||||
/* 5E */ KEY_KP8, KEY_KP9, 0,
|
||||
};
|
||||
|
||||
static unsigned int ASUS_LED_TYPE[] = {
|
||||
RATBAG_LED_TYPE_LOGO, /* Mouse logo */
|
||||
RATBAG_LED_TYPE_WHEEL, /* Mouse wheel */
|
||||
RATBAG_LED_TYPE_SIDE, /* Bottom of the mouse */
|
||||
};
|
||||
|
||||
static unsigned int ASUS_POLLING_RATES[] = { 125, 250, 500, 1000 };
|
||||
|
||||
/* search for ASUS button by ratbag types */
|
||||
struct asus_button *
|
||||
asus_find_button_by_action(struct ratbag_button_action action)
|
||||
{
|
||||
struct asus_button *asus_button;
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH(ASUS_BUTTON_MAPPING); i++) {
|
||||
asus_button = &ASUS_BUTTON_MAPPING[i];
|
||||
if ((action.type == RATBAG_BUTTON_ACTION_TYPE_BUTTON && asus_button->button == action.action.button) ||
|
||||
(action.type == RATBAG_BUTTON_ACTION_TYPE_SPECIAL && asus_button->special == action.action.special))
|
||||
return asus_button;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* search for ASUS button by ASUS button code */
|
||||
struct asus_button *
|
||||
asus_find_button_by_code(uint8_t asus_code)
|
||||
{
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH(ASUS_BUTTON_MAPPING); i++) {
|
||||
if (ASUS_BUTTON_MAPPING[i].asus_code == asus_code)
|
||||
return &ASUS_BUTTON_MAPPING[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* search for ASUS key code by Linux key code */
|
||||
int
|
||||
asus_find_key_code(unsigned int linux_code)
|
||||
{
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH(ASUS_KEY_MAPPING); i++) {
|
||||
if (ASUS_KEY_MAPPING[i] == linux_code)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* convert ASUS key code to Linux key code */
|
||||
int
|
||||
asus_get_linux_key_code(uint8_t asus_code) {
|
||||
assert(asus_code <= ARRAY_LENGTH(ASUS_KEY_MAPPING));
|
||||
return ASUS_KEY_MAPPING[asus_code];
|
||||
}
|
||||
|
||||
int
|
||||
asus_query(struct ratbag_device *device,
|
||||
union asus_request *request, union asus_response *response)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ratbag_hidraw_output_report(device, request->raw, ASUS_PACKET_SIZE);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
memset(response, 0, sizeof(union asus_response));
|
||||
rc = ratbag_hidraw_read_input_report(device, response->raw, ASUS_PACKET_SIZE, NULL);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* invalid state, disconnected or sleeping */
|
||||
if (response->data.code == 0xaaff) {
|
||||
return ASUS_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
asus_setup_profile(struct ratbag_device *device, struct ratbag_profile *profile)
|
||||
{
|
||||
ratbag_profile_set_report_rate_list(
|
||||
profile, ASUS_POLLING_RATES, ARRAY_LENGTH(ASUS_POLLING_RATES));
|
||||
}
|
||||
|
||||
void
|
||||
asus_setup_button(struct ratbag_device *device, struct ratbag_button *button)
|
||||
{
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
|
||||
}
|
||||
|
||||
void
|
||||
asus_setup_resolution(struct ratbag_device *device, struct ratbag_resolution *resolution)
|
||||
{
|
||||
const struct dpi_range *dpirange = ratbag_device_data_asus_get_dpi_range(device->data);
|
||||
if (!dpirange)
|
||||
return;
|
||||
|
||||
ratbag_resolution_set_dpi_list_from_range(
|
||||
resolution, dpirange->min, dpirange->max);
|
||||
}
|
||||
|
||||
void
|
||||
asus_setup_led(struct ratbag_device *device, struct ratbag_led *led)
|
||||
{
|
||||
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
|
||||
led->type = ASUS_LED_TYPE[led->index];
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
|
||||
}
|
||||
|
||||
int
|
||||
asus_save_profile(struct ratbag_device *device)
|
||||
{
|
||||
int rc;
|
||||
union asus_response response;
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_SAVE,
|
||||
};
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
asus_get_profile_data(struct ratbag_device *device, struct asus_profile_data *data)
|
||||
{
|
||||
int rc;
|
||||
uint32_t quirks = ratbag_device_data_asus_get_quirks(device->data);
|
||||
union asus_response response;
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_GET_PROFILE_DATA,
|
||||
};
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (quirks & ASUS_QUIRK_STRIX_PROFILE)
|
||||
data->profile_id = response.data.results[7];
|
||||
else
|
||||
data->profile_id = response.data.results[8];
|
||||
|
||||
data->version_primary_major = response.data.results[13];
|
||||
data->version_primary_minor = response.data.results[12];
|
||||
data->version_primary_build = response.data.results[11];
|
||||
|
||||
data->version_secondary_major = response.data.results[4];
|
||||
data->version_secondary_minor = response.data.results[3];
|
||||
data->version_secondary_build = response.data.results[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
asus_set_profile(struct ratbag_device *device, unsigned int index)
|
||||
{
|
||||
int rc;
|
||||
union asus_response response;
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_SET_PROFILE,
|
||||
.data.params[0] = index,
|
||||
};
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read button bindings using ButtonMapping configuration property */
|
||||
int
|
||||
asus_get_binding_data(struct ratbag_device *device, union asus_binding_data *data)
|
||||
{
|
||||
int rc;
|
||||
union asus_response response;
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_GET_BUTTON_DATA,
|
||||
};
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
memcpy(data->raw, response.raw, sizeof(data->raw));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set button binding using ASUS code of the button */
|
||||
int
|
||||
asus_set_button_action(struct ratbag_device *device, unsigned int asus_index, uint8_t asus_code, uint8_t asus_type)
|
||||
{
|
||||
int rc;
|
||||
union asus_response response;
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_SET_BUTTON,
|
||||
};
|
||||
|
||||
/* source (physical mouse button) */
|
||||
request.data.params[2] = ASUS_BUTTON_MAPPING[asus_index].asus_code;
|
||||
request.data.params[3] = ASUS_BUTTON_ACTION_TYPE_BUTTON;
|
||||
|
||||
/* destination (mouse button or keyboard key action) */
|
||||
request.data.params[4] = asus_code;
|
||||
request.data.params[5] = asus_type;
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_data *data)
|
||||
{
|
||||
int rc;
|
||||
uint32_t quirks = ratbag_device_data_asus_get_quirks(device->data);
|
||||
union asus_response response;
|
||||
unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions;
|
||||
unsigned int i;
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_GET_SETTINGS,
|
||||
};
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
memcpy(data->raw, response.raw, sizeof(data->raw));
|
||||
|
||||
/* convert DPI rates */
|
||||
switch (dpi_count) {
|
||||
case 2: /* 2 DPI presets */
|
||||
for (i = 0; i < dpi_count; i++) {
|
||||
data->data2.dpi[i] = data->data2.dpi[i] * 50 + 50;
|
||||
if (quirks & ASUS_QUIRK_DOUBLE_DPI)
|
||||
data->data2.dpi[i] *= 2;
|
||||
}
|
||||
data->data2.rate = ASUS_POLLING_RATES[data->data2.rate];
|
||||
break;
|
||||
|
||||
case 4: /* 4 DPI presets */
|
||||
for (i = 0; i < dpi_count; i++) {
|
||||
data->data4.dpi[i] = data->data4.dpi[i] * 50 + 50;
|
||||
if (quirks & ASUS_QUIRK_DOUBLE_DPI)
|
||||
data->data4.dpi[i] *= 2;
|
||||
}
|
||||
data->data4.rate = ASUS_POLLING_RATES[data->data4.rate];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set DPI for the specified preset */
|
||||
int
|
||||
asus_set_dpi(struct ratbag_device *device, unsigned int index, unsigned int dpi)
|
||||
{
|
||||
int rc;
|
||||
uint32_t quirks = ratbag_device_data_asus_get_quirks(device->data);
|
||||
union asus_response response;
|
||||
unsigned int idpi;
|
||||
|
||||
idpi = dpi;
|
||||
if (quirks & ASUS_QUIRK_DOUBLE_DPI)
|
||||
idpi /= 2;
|
||||
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_SET_SETTING,
|
||||
.data.params[0] = index,
|
||||
.data.params[2] = (idpi - 50) / 50,
|
||||
};
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set polling rate in Hz */
|
||||
int
|
||||
asus_set_polling_rate(struct ratbag_device *device, unsigned int hz)
|
||||
{
|
||||
int rc;
|
||||
union asus_response response;
|
||||
unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions;
|
||||
unsigned int i;
|
||||
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_SET_SETTING,
|
||||
.data.params[0] = dpi_count + ASUS_FIELD_RATE, /* field index to set */
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(ASUS_POLLING_RATES); i++) {
|
||||
if (ASUS_POLLING_RATES[i] == hz) {
|
||||
request.data.params[2] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set button response/debounce in ms (from 4 to 32 with step of 4) */
|
||||
int
|
||||
asus_set_button_response(struct ratbag_device *device, unsigned int ms)
|
||||
{
|
||||
assert(ms >= 4);
|
||||
|
||||
int rc;
|
||||
unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions;
|
||||
union asus_response response;
|
||||
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_SET_SETTING,
|
||||
.data.params[0] = dpi_count + ASUS_FIELD_RESPONSE, /* field index to set */
|
||||
.data.params[2] = ms / 4 - 1,
|
||||
};
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
asus_set_angle_snapping(struct ratbag_device *device, bool is_enabled)
|
||||
{
|
||||
int rc;
|
||||
union asus_response response;
|
||||
unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions;
|
||||
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_SET_SETTING,
|
||||
.data.params[0] = dpi_count + ASUS_FIELD_SNAPPING, /* field index to set */
|
||||
.data.params[2] = is_enabled ? 1 : 0,
|
||||
};
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
asus_get_led_data(struct ratbag_device *device, union asus_led_data *data)
|
||||
{
|
||||
int rc;
|
||||
union asus_response response;
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_GET_LED_DATA,
|
||||
};
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
memcpy(data->raw, response.raw, sizeof(data->raw));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set LED mode, brightness (0-4) and color */
|
||||
int
|
||||
asus_set_led(struct ratbag_device *device,
|
||||
uint8_t index, uint8_t mode, uint8_t brightness,
|
||||
struct ratbag_color color)
|
||||
{
|
||||
int rc;
|
||||
union asus_response response;
|
||||
union asus_request request = {
|
||||
.data.cmd = ASUS_CMD_SET_LED,
|
||||
.data.params[0] = index,
|
||||
.data.params[2] = mode,
|
||||
.data.params[3] = brightness,
|
||||
.data.params[4] = color.red,
|
||||
.data.params[5] = color.green,
|
||||
.data.params[6] = color.blue,
|
||||
};
|
||||
|
||||
rc = asus_query(device, &request, &response);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
214
src/asus.h
Normal file
214
src/asus.h
Normal file
@@ -0,0 +1,214 @@
|
||||
#pragma once
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "libratbag-private.h"
|
||||
#include "libratbag-hidraw.h"
|
||||
|
||||
#define ASUS_QUIRK_DOUBLE_DPI 1 << 0
|
||||
#define ASUS_QUIRK_STRIX_PROFILE 1 << 1
|
||||
|
||||
#define ASUS_PACKET_SIZE 64
|
||||
#define ASUS_BUTTON_ACTION_TYPE_KEY 0 /* keyboard key */
|
||||
#define ASUS_BUTTON_ACTION_TYPE_BUTTON 1 /* mouse button */
|
||||
#define ASUS_BUTTON_CODE_DISABLED 0xff /* disabled mouse button */
|
||||
#define ASUS_STATUS_ERROR 0xffaa /* invalid state/request, disconnected or sleeping */
|
||||
|
||||
/* maximum number of buttons across all ASUS devices */
|
||||
#define ASUS_MAX_NUM_BUTTON 16
|
||||
|
||||
/* maximum number of DPI presets across all ASUS devices */
|
||||
/* for 4 DPI devices: 0 - red, 1 - purple, 2 - blue (default), 3 - green */
|
||||
/* for 2 DPI devices: 0 - main (default), 1 - alternative */
|
||||
#define ASUS_MAX_NUM_DPI 4
|
||||
|
||||
/* maximum number of LEDs across all ASUS devices */
|
||||
#define ASUS_MAX_NUM_LED 3
|
||||
|
||||
/* base request */
|
||||
|
||||
struct _asus_request {
|
||||
uint16_t cmd;
|
||||
uint8_t params[ASUS_PACKET_SIZE - 2];
|
||||
} __attribute__((packed));
|
||||
_Static_assert(sizeof(struct _asus_request) == ASUS_PACKET_SIZE, "The size of `_asus_request` is wrong.");
|
||||
|
||||
union asus_request {
|
||||
struct _asus_request data;
|
||||
uint8_t raw[ASUS_PACKET_SIZE];
|
||||
};
|
||||
|
||||
/* base response */
|
||||
|
||||
struct _asus_response {
|
||||
uint16_t code;
|
||||
uint8_t results[ASUS_PACKET_SIZE - 2];
|
||||
} __attribute__((packed));
|
||||
_Static_assert(sizeof(struct _asus_response) == ASUS_PACKET_SIZE, "The size of `_asus_response` is wrong.");
|
||||
|
||||
union asus_response {
|
||||
struct _asus_response data;
|
||||
uint8_t raw[ASUS_PACKET_SIZE];
|
||||
};
|
||||
|
||||
/* current profile ID and firmware info */
|
||||
|
||||
struct asus_profile_data {
|
||||
unsigned int profile_id;
|
||||
uint8_t version_primary_major;
|
||||
uint8_t version_primary_minor;
|
||||
uint8_t version_primary_build;
|
||||
uint8_t version_secondary_major;
|
||||
uint8_t version_secondary_minor;
|
||||
uint8_t version_secondary_build;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* button bindings */
|
||||
|
||||
struct _asus_binding {
|
||||
uint8_t action; /* ASUS code (for both keyboard keys and mouse buttons) */
|
||||
uint8_t type; /* ASUS action type */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct _asus_binding_data {
|
||||
uint32_t pad;
|
||||
struct _asus_binding binding[ASUS_MAX_NUM_BUTTON];
|
||||
} __attribute__((packed));
|
||||
|
||||
union asus_binding_data {
|
||||
struct _asus_binding_data data;
|
||||
uint8_t raw[ASUS_PACKET_SIZE];
|
||||
};
|
||||
|
||||
/* DPI data */
|
||||
|
||||
struct _asus_dpi2_data {
|
||||
uint32_t pad;
|
||||
uint16_t dpi[2]; /* DPI presets */
|
||||
uint16_t rate; /* polling rate */
|
||||
uint16_t response; /* button response */
|
||||
uint16_t snapping; /* angle snapping (on/off) */
|
||||
} __attribute__((packed)); /* struct for storing 2 DPI presets and extra settings */
|
||||
|
||||
struct _asus_dpi4_data {
|
||||
uint32_t pad;
|
||||
uint16_t dpi[4];
|
||||
uint16_t rate;
|
||||
uint16_t response;
|
||||
uint16_t snapping;
|
||||
} __attribute__((packed)); /* struct for storing 4 DPI presets and extra settings */
|
||||
|
||||
union asus_resolution_data {
|
||||
struct _asus_dpi2_data data2; /* data for 2 DPI presets */
|
||||
struct _asus_dpi4_data data4; /* data for 4 DPI presets */
|
||||
uint8_t raw[sizeof(struct _asus_dpi4_data)];
|
||||
};
|
||||
|
||||
/* LED data */
|
||||
|
||||
struct _asus_led {
|
||||
uint8_t mode;
|
||||
uint8_t brightness; /* 0-4 */
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct _asus_led_data {
|
||||
uint32_t pad;
|
||||
struct _asus_led led[ASUS_MAX_NUM_LED]; /* LEDs */
|
||||
} __attribute__((packed));
|
||||
|
||||
union asus_led_data {
|
||||
struct _asus_led_data data;
|
||||
uint8_t raw[sizeof(struct _asus_led_data)];
|
||||
};
|
||||
|
||||
/* button define */
|
||||
struct asus_button {
|
||||
uint8_t asus_code; /* used for button action */
|
||||
enum ratbag_button_action_type type;
|
||||
uint8_t button; /* mouse button number, optional */
|
||||
enum ratbag_button_action_special special; /* special action, optional */
|
||||
};
|
||||
|
||||
struct asus_button *
|
||||
asus_find_button_by_action(struct ratbag_button_action action);
|
||||
|
||||
struct asus_button *
|
||||
asus_find_button_by_code(uint8_t asus_code);
|
||||
|
||||
int
|
||||
asus_find_key_code(unsigned int linux_code);
|
||||
|
||||
int
|
||||
asus_get_linux_key_code(uint8_t asus_code);
|
||||
|
||||
/* initializers of ratbag data */
|
||||
|
||||
void
|
||||
asus_setup_profile(struct ratbag_device *device, struct ratbag_profile *profile);
|
||||
|
||||
void
|
||||
asus_setup_button(struct ratbag_device *device, struct ratbag_button *button);
|
||||
|
||||
void
|
||||
asus_setup_resolution(struct ratbag_device *device, struct ratbag_resolution *resolution);
|
||||
|
||||
void
|
||||
asus_setup_led(struct ratbag_device *device, struct ratbag_led *led);
|
||||
|
||||
/* generic i/o */
|
||||
|
||||
int
|
||||
asus_query(struct ratbag_device *device, union asus_request *request, union asus_response *response);
|
||||
|
||||
/* commit */
|
||||
|
||||
int
|
||||
asus_save_profile(struct ratbag_device *device);
|
||||
|
||||
/* profiles */
|
||||
|
||||
int
|
||||
asus_get_profile_data(struct ratbag_device *device, struct asus_profile_data *data);
|
||||
|
||||
int
|
||||
asus_set_profile(struct ratbag_device *device, unsigned int index);
|
||||
|
||||
/* button bindings */
|
||||
|
||||
int
|
||||
asus_get_binding_data(struct ratbag_device *device, union asus_binding_data *data);
|
||||
|
||||
int
|
||||
asus_set_button_action(struct ratbag_device *device, unsigned int index,
|
||||
uint8_t asus_code, uint8_t asus_type);
|
||||
|
||||
/* resolution settings */
|
||||
|
||||
int
|
||||
asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_data *data);
|
||||
|
||||
int
|
||||
asus_set_dpi(struct ratbag_device *device, unsigned int index, unsigned int dpi);
|
||||
|
||||
int
|
||||
asus_set_polling_rate(struct ratbag_device *device, unsigned int hz);
|
||||
|
||||
int
|
||||
asus_set_button_response(struct ratbag_device *device, unsigned int ms);
|
||||
|
||||
int
|
||||
asus_set_angle_snapping(struct ratbag_device *device, bool is_enabled);
|
||||
|
||||
/* LED settings */
|
||||
|
||||
int
|
||||
asus_get_led_data(struct ratbag_device *device, union asus_led_data *data);
|
||||
|
||||
int
|
||||
asus_set_led(struct ratbag_device *device,
|
||||
uint8_t index, uint8_t mode, uint8_t brightness,
|
||||
struct ratbag_color color);
|
||||
517
src/driver-asus.c
Normal file
517
src/driver-asus.c
Normal file
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Kyoken, kyoken@kyoken.ninja
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <linux/input.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libratbag-private.h"
|
||||
#include "libratbag-hidraw.h"
|
||||
#include "libratbag-data.h"
|
||||
|
||||
#include "asus.h"
|
||||
|
||||
/* ButtonMapping configuration property defaults, stores indices for ASUS_BUTTON_MAPPING */
|
||||
static int8_t ASUS_BUTTON_MAPPING_INDEX[] = {
|
||||
0, /* left */
|
||||
1, /* right (button 3 in xev) */
|
||||
2, /* middle (button 2 in xev) */
|
||||
6, /* wheel up */
|
||||
7, /* wheel down */
|
||||
5, /* DPI */
|
||||
3, /* backward */
|
||||
4, /* forward */
|
||||
-1, /* placeholder */
|
||||
-1, /* placeholder */
|
||||
-1, /* placeholder */
|
||||
-1, /* placeholder */
|
||||
-1, /* placeholder */
|
||||
-1, /* placeholder */
|
||||
-1, /* placeholder */
|
||||
-1, /* placeholder */
|
||||
};
|
||||
|
||||
static unsigned int ASUS_LED_MODE[] = {
|
||||
RATBAG_LED_ON,
|
||||
RATBAG_LED_BREATHING,
|
||||
RATBAG_LED_CYCLE,
|
||||
RATBAG_LED_ON, /* rainbow wave */
|
||||
RATBAG_LED_ON, /* reactive - react to clicks */
|
||||
RATBAG_LED_ON, /* custom - depends on mouse type */
|
||||
RATBAG_LED_ON, /* battery - battery indicator */
|
||||
};
|
||||
|
||||
struct asus_data {
|
||||
uint8_t is_ready;
|
||||
};
|
||||
|
||||
static int
|
||||
asus_driver_load_profile(struct ratbag_device *device, struct ratbag_profile *profile)
|
||||
{
|
||||
int rc;
|
||||
const int8_t *bmi = ratbag_device_data_asus_get_button_mapping(device->data);
|
||||
struct _asus_binding *asus_binding;
|
||||
struct _asus_led *asus_led;
|
||||
struct asus_button *asus_button;
|
||||
struct ratbag_button *button;
|
||||
struct ratbag_led *led;
|
||||
struct ratbag_resolution *resolution;
|
||||
union asus_binding_data binding_data;
|
||||
union asus_led_data led_data;
|
||||
union asus_resolution_data resolution_data;
|
||||
unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions;
|
||||
|
||||
/* get buttons */
|
||||
|
||||
log_debug(device->ratbag, "Loading buttons data\n");
|
||||
rc = asus_get_binding_data(device, &binding_data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
ratbag_profile_for_each_button(profile, button) {
|
||||
int asus_index = bmi[button->index] != -1 ? bmi[button->index] : ASUS_BUTTON_MAPPING_INDEX[button->index];
|
||||
if (asus_index == -1) {
|
||||
log_debug(device->ratbag, "No mapping for button %d\n", button->index);
|
||||
continue;
|
||||
}
|
||||
|
||||
asus_binding = &binding_data.data.binding[asus_index];
|
||||
|
||||
/* disabled */
|
||||
if (asus_binding->action == ASUS_BUTTON_CODE_DISABLED) {
|
||||
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* got action */
|
||||
switch (asus_binding->type) {
|
||||
case ASUS_BUTTON_ACTION_TYPE_KEY:
|
||||
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
|
||||
/* ASUS code to Linux code */
|
||||
button->action.action.key.key = asus_get_linux_key_code(asus_binding->action);
|
||||
break;
|
||||
|
||||
case ASUS_BUTTON_ACTION_TYPE_BUTTON:
|
||||
asus_button = asus_find_button_by_code(asus_binding->action);
|
||||
if (asus_button != NULL) { /* found button to bind to */
|
||||
button->action.type = asus_button->type;
|
||||
if (asus_button->type == RATBAG_BUTTON_ACTION_TYPE_BUTTON)
|
||||
button->action.action.button = asus_button->button;
|
||||
else if (asus_button->type == RATBAG_BUTTON_ACTION_TYPE_SPECIAL)
|
||||
button->action.action.special = asus_button->special;
|
||||
} else {
|
||||
log_debug(device->ratbag, "Unknown action code %02x\n", asus_binding->action);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* get DPIs */
|
||||
|
||||
log_debug(device->ratbag, "Loading resolutions data\n");
|
||||
rc = asus_get_resolution_data(device, &resolution_data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
switch (dpi_count) {
|
||||
case 2: /* 2 DPI presets */
|
||||
ratbag_profile_set_report_rate(profile, resolution_data.data2.rate);
|
||||
ratbag_profile_for_each_resolution(profile, resolution)
|
||||
ratbag_resolution_set_resolution(
|
||||
resolution,
|
||||
resolution_data.data2.dpi[resolution->index],
|
||||
resolution_data.data2.dpi[resolution->index]);
|
||||
break;
|
||||
|
||||
case 4: /* 4 DPI presets */
|
||||
ratbag_profile_set_report_rate(profile, resolution_data.data4.rate);
|
||||
ratbag_profile_for_each_resolution(profile, resolution)
|
||||
ratbag_resolution_set_resolution(
|
||||
resolution,
|
||||
resolution_data.data4.dpi[resolution->index],
|
||||
resolution_data.data4.dpi[resolution->index]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* get LEDs */
|
||||
|
||||
log_debug(device->ratbag, "Loading LEDs data\n");
|
||||
rc = asus_get_led_data(device, &led_data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
ratbag_profile_for_each_led(profile, led) {
|
||||
asus_led = &led_data.data.led[led->index];
|
||||
led->mode = ASUS_LED_MODE[asus_led->mode];
|
||||
/* convert brightness from 0-4 to 0-256 */
|
||||
led->brightness = asus_led->brightness * 64;
|
||||
led->color.red = asus_led->r;
|
||||
led->color.green = asus_led->g;
|
||||
led->color.blue = asus_led->b;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
asus_driver_save_profile(struct ratbag_device *device, struct ratbag_profile *profile)
|
||||
{
|
||||
int rc = 0;
|
||||
const int8_t *bmi = ratbag_device_data_asus_get_button_mapping(device->data);
|
||||
struct asus_button *asus_button;
|
||||
struct ratbag_button *button;
|
||||
struct ratbag_led *led;
|
||||
struct ratbag_resolution *resolution;
|
||||
|
||||
/* set buttons */
|
||||
ratbag_profile_for_each_button(profile, button) {
|
||||
if (!button->dirty)
|
||||
continue;
|
||||
|
||||
int asus_index = bmi[button->index] != -1 ? bmi[button->index] : ASUS_BUTTON_MAPPING_INDEX[button->index];
|
||||
int asus_code;
|
||||
if (asus_index == -1) {
|
||||
log_debug(device->ratbag, "No mapping for button %d\n", button->index);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug(device->ratbag, "Button %d (%d) changed\n", button->index, asus_index);
|
||||
|
||||
switch (button->action.type) {
|
||||
case RATBAG_BUTTON_ACTION_TYPE_NONE:
|
||||
rc = asus_set_button_action(
|
||||
device, asus_index,
|
||||
ASUS_BUTTON_CODE_DISABLED,
|
||||
ASUS_BUTTON_ACTION_TYPE_BUTTON);
|
||||
break;
|
||||
|
||||
case RATBAG_BUTTON_ACTION_TYPE_KEY:
|
||||
/* Linux code to ASUS code */
|
||||
asus_code = asus_find_key_code(button->action.action.key.key);
|
||||
if (asus_code >= 0)
|
||||
rc = asus_set_button_action(
|
||||
device, asus_index,
|
||||
asus_code,
|
||||
ASUS_BUTTON_ACTION_TYPE_KEY);
|
||||
break;
|
||||
|
||||
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
|
||||
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
|
||||
asus_button = asus_find_button_by_action(button->action);
|
||||
if (asus_button != NULL) /* found button to bind to */
|
||||
rc = asus_set_button_action(
|
||||
device, asus_index,
|
||||
asus_button->asus_code,
|
||||
ASUS_BUTTON_ACTION_TYPE_BUTTON);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* set extra settings */
|
||||
if (profile->rate_dirty) {
|
||||
log_debug(device->ratbag, "Polling rate changed to %d Hz\n", profile->hz);
|
||||
rc = asus_set_polling_rate(device, profile->hz);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* set DPIs */
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (!resolution->dirty)
|
||||
continue;
|
||||
|
||||
log_debug(
|
||||
device->ratbag, "Resolution %d changed to %d\n",
|
||||
resolution->index, resolution->dpi_x);
|
||||
|
||||
rc = asus_set_dpi(device, resolution->index, resolution->dpi_x);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* set LEDs */
|
||||
ratbag_profile_for_each_led(profile, led) {
|
||||
if (!led->dirty)
|
||||
continue;
|
||||
|
||||
log_debug(device->ratbag, "LED %d changed\n", led->index);
|
||||
uint8_t led_mode = 0;
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH(ASUS_LED_MODE); i++) {
|
||||
if (ASUS_LED_MODE[i] == led->mode) {
|
||||
led_mode = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* convert brightness from 0-256 to 0-4 */
|
||||
uint8_t led_brightness = (uint8_t)round((double)led->brightness / 64.0);
|
||||
rc = asus_set_led(device, led->index, led_mode, led_brightness, led->color);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
asus_driver_load_profiles(struct ratbag_device *device)
|
||||
{
|
||||
int rc;
|
||||
struct asus_profile_data profile_data;
|
||||
struct ratbag_profile *profile;
|
||||
unsigned int current_profile_id;
|
||||
|
||||
/* get current profile id */
|
||||
rc = asus_get_profile_data(device, &profile_data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
current_profile_id = profile_data.profile_id;
|
||||
log_debug(device->ratbag, "Initial profile is %d\n", current_profile_id);
|
||||
log_debug(
|
||||
device->ratbag, "Primary version %02X.%02X.%02X\n",
|
||||
profile_data.version_primary_major,
|
||||
profile_data.version_primary_minor,
|
||||
profile_data.version_primary_build);
|
||||
log_debug(
|
||||
device->ratbag, "Secondary version %02X.%02X.%02X\n",
|
||||
profile_data.version_secondary_major,
|
||||
profile_data.version_secondary_minor,
|
||||
profile_data.version_secondary_build);
|
||||
|
||||
/* read ratbag profiles */
|
||||
ratbag_device_for_each_profile(device, profile) {
|
||||
if (profile->index == current_profile_id) { /* profile is already selected */
|
||||
profile->is_active = true;
|
||||
} else { /* switch profile */
|
||||
profile->is_active = false;
|
||||
|
||||
log_debug(device->ratbag, "Switching to profile %d\n", profile->index);
|
||||
rc = asus_set_profile(device, profile->index);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = asus_driver_load_profile(device, profile);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* back to initial profile */
|
||||
if (device->num_profiles > 1) {
|
||||
log_debug(device->ratbag, "Switching back to initial profile %d\n", current_profile_id);
|
||||
rc = asus_set_profile(device, current_profile_id);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
asus_driver_save_profiles(struct ratbag_device *device)
|
||||
{
|
||||
int rc;
|
||||
struct asus_profile_data profile_data;
|
||||
struct ratbag_profile *profile;
|
||||
unsigned int current_profile_id;
|
||||
|
||||
/* get current profile id */
|
||||
rc = asus_get_profile_data(device, &profile_data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
current_profile_id = profile_data.profile_id;
|
||||
log_debug(device->ratbag, "Initial profile is %d\n", current_profile_id);
|
||||
|
||||
ratbag_device_for_each_profile(device, profile) {
|
||||
if (!profile->dirty)
|
||||
continue;
|
||||
|
||||
log_debug(device->ratbag, "Profile %d changed\n", profile->index);
|
||||
|
||||
/* switch profile */
|
||||
if (profile->index != current_profile_id) {
|
||||
log_debug(device->ratbag, "Switching to profile %d\n", profile->index);
|
||||
rc = asus_set_profile(device, profile->index);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = asus_driver_save_profile(device, profile);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* save profile */
|
||||
log_debug(device->ratbag, "Saving profile\n");
|
||||
rc = asus_save_profile(device);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* back to initial profile */
|
||||
if (device->num_profiles > 1) {
|
||||
log_debug(device->ratbag, "Switching back to initial profile %d\n", current_profile_id);
|
||||
rc = asus_set_profile(device, current_profile_id);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
asus_driver_probe(struct ratbag_device *device)
|
||||
{
|
||||
int rc;
|
||||
unsigned int profile_count, dpi_count, button_count, led_count;
|
||||
struct asus_data *drv_data;
|
||||
struct ratbag_profile *profile;
|
||||
struct ratbag_button *button;
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_led *led;
|
||||
|
||||
rc = ratbag_open_hidraw(device);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* create device state data */
|
||||
drv_data = zalloc(sizeof(*drv_data));
|
||||
ratbag_set_drv_data(device, drv_data);
|
||||
drv_data->is_ready = 1;
|
||||
|
||||
/* get device properties from configuration file */
|
||||
profile_count = ratbag_device_data_asus_get_profile_count(device->data);
|
||||
dpi_count = ratbag_device_data_asus_get_dpi_count(device->data);
|
||||
button_count = ratbag_device_data_asus_get_button_count(device->data);
|
||||
led_count = ratbag_device_data_asus_get_led_count(device->data);
|
||||
|
||||
/* init profiles */
|
||||
ratbag_device_init_profiles(
|
||||
device,
|
||||
max(profile_count, 1),
|
||||
max(dpi_count, 2),
|
||||
max(button_count, 8),
|
||||
max(led_count, 0));
|
||||
|
||||
/* setup profiles */
|
||||
ratbag_device_for_each_profile(device, profile) {
|
||||
if (profile->index == 0) {
|
||||
profile->is_active = true;
|
||||
}
|
||||
|
||||
asus_setup_profile(device, profile);
|
||||
|
||||
ratbag_profile_for_each_button(profile, button)
|
||||
asus_setup_button(device, button);
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution)
|
||||
asus_setup_resolution(device, resolution);
|
||||
|
||||
ratbag_profile_for_each_led(profile, led)
|
||||
asus_setup_led(device, led);
|
||||
}
|
||||
|
||||
/* load profiles */
|
||||
rc = asus_driver_load_profiles(device);
|
||||
if (rc == ASUS_STATUS_ERROR) { /* mouse in invalid state */
|
||||
drv_data->is_ready = 0;
|
||||
} else if (rc) { /* other errors */
|
||||
log_error(
|
||||
device->ratbag, "Can't talk to the mouse: '%s' (%d)\n",
|
||||
strerror(-rc), rc);
|
||||
free(drv_data);
|
||||
ratbag_set_drv_data(device, NULL);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
asus_driver_remove(struct ratbag_device *device)
|
||||
{
|
||||
ratbag_close_hidraw(device);
|
||||
free(ratbag_get_drv_data(device));
|
||||
}
|
||||
|
||||
static int
|
||||
asus_driver_commit(struct ratbag_device *device)
|
||||
{
|
||||
int rc;
|
||||
struct asus_data *drv_data;
|
||||
|
||||
/* check last device state */
|
||||
drv_data = ratbag_get_drv_data(device);
|
||||
if (!drv_data->is_ready) { /* device was not ready */
|
||||
log_error(device->ratbag, "Device was not ready, trying to reload\n");
|
||||
rc = asus_driver_load_profiles(device);
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "Device reloading failed (%d)\n", rc);
|
||||
if (rc != ASUS_STATUS_ERROR)
|
||||
return rc;
|
||||
} else {
|
||||
drv_data->is_ready = 1;
|
||||
log_error(device->ratbag, "Device was successfully reloaded\n");
|
||||
}
|
||||
return RATBAG_ERROR_DEVICE; /* fail in any case because we tried to rollback instead of commit */
|
||||
}
|
||||
|
||||
/* save profiles */
|
||||
rc = asus_driver_save_profiles(device);
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "Commit failed (%d)\n", rc);
|
||||
if (rc != ASUS_STATUS_ERROR)
|
||||
return rc;
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ratbag_driver asus_driver = {
|
||||
.name = "ASUS",
|
||||
.id = "asus",
|
||||
.probe = asus_driver_probe,
|
||||
.remove = asus_driver_remove,
|
||||
.commit = asus_driver_commit,
|
||||
.set_active_profile = asus_set_profile,
|
||||
};
|
||||
@@ -496,9 +496,9 @@ etekcity_read_profile(struct ratbag_profile *profile)
|
||||
}
|
||||
|
||||
static int
|
||||
etekcity_write_macro(struct ratbag_button *button,
|
||||
const struct ratbag_button_action *action)
|
||||
etekcity_write_macro(struct ratbag_button *button)
|
||||
{
|
||||
const struct ratbag_button_action *action = &button->action;
|
||||
struct ratbag_device *device;
|
||||
struct etekcity_macro *macro;
|
||||
struct etekcity_data *drv_data;
|
||||
@@ -555,9 +555,9 @@ etekcity_write_macro(struct ratbag_button *button,
|
||||
}
|
||||
|
||||
static int
|
||||
etekcity_write_button(struct ratbag_button *button,
|
||||
const struct ratbag_button_action *action)
|
||||
etekcity_write_button(struct ratbag_button *button)
|
||||
{
|
||||
const struct ratbag_button_action *action = &button->action;
|
||||
struct ratbag_profile *profile = button->profile;
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct etekcity_data *drv_data = ratbag_get_drv_data(device);
|
||||
@@ -572,15 +572,7 @@ etekcity_write_button(struct ratbag_button *button,
|
||||
|
||||
*data = rc;
|
||||
|
||||
rc = etekcity_write_profile(button->profile);
|
||||
if (rc) {
|
||||
log_error(device->ratbag,
|
||||
"unable to write the profile to the device: '%s' (%d)\n",
|
||||
strerror(-rc), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = etekcity_write_macro(button, action);
|
||||
rc = etekcity_write_macro(button);
|
||||
if (rc) {
|
||||
log_error(device->ratbag,
|
||||
"unable to write the macro to the device: '%s' (%d)\n",
|
||||
@@ -592,8 +584,7 @@ etekcity_write_button(struct ratbag_button *button,
|
||||
}
|
||||
|
||||
static int
|
||||
etekcity_write_resolution_dpi(struct ratbag_resolution *resolution,
|
||||
int dpi_x, int dpi_y)
|
||||
etekcity_write_resolution(struct ratbag_resolution *resolution)
|
||||
{
|
||||
struct ratbag_profile *profile = resolution->profile;
|
||||
struct ratbag_device *device = profile->device;
|
||||
@@ -602,6 +593,9 @@ etekcity_write_resolution_dpi(struct ratbag_resolution *resolution,
|
||||
uint8_t *buf;
|
||||
int rc;
|
||||
|
||||
const unsigned int dpi_x = resolution->dpi_x;
|
||||
const unsigned int dpi_y = resolution->dpi_y;
|
||||
|
||||
if (dpi_x < 50 || dpi_x > 8200 || dpi_x % 50)
|
||||
return -EINVAL;
|
||||
if (dpi_y < 50 || dpi_y > 8200 || dpi_y % 50)
|
||||
@@ -705,13 +699,49 @@ etekcity_remove(struct ratbag_device *device)
|
||||
free(ratbag_get_drv_data(device));
|
||||
}
|
||||
|
||||
static int
|
||||
etekcity_commit(struct ratbag_device *device)
|
||||
{
|
||||
int rc = 0;
|
||||
struct ratbag_button *button = NULL;
|
||||
struct ratbag_profile *profile = NULL;
|
||||
struct ratbag_resolution *resolution = NULL;
|
||||
|
||||
ratbag_device_for_each_profile(device, profile) {
|
||||
if (!profile->dirty)
|
||||
continue;
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (!resolution->dirty)
|
||||
continue;
|
||||
|
||||
rc = etekcity_write_resolution(resolution);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
ratbag_profile_for_each_button(profile, button) {
|
||||
if (!button->dirty)
|
||||
continue;
|
||||
|
||||
rc = etekcity_write_button(button);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = etekcity_write_profile(profile);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ratbag_driver etekcity_driver = {
|
||||
.name = "EtekCity",
|
||||
.id = "etekcity",
|
||||
.probe = etekcity_probe,
|
||||
.remove = etekcity_remove,
|
||||
.write_profile = etekcity_write_profile,
|
||||
.commit = etekcity_commit,
|
||||
.set_active_profile = etekcity_set_current_profile,
|
||||
.write_button = etekcity_write_button,
|
||||
.write_resolution_dpi = etekcity_write_resolution_dpi,
|
||||
};
|
||||
|
||||
@@ -164,6 +164,7 @@ hidpp10drv_map_button(struct ratbag_device *device,
|
||||
{
|
||||
struct hidpp10_profile profile;
|
||||
int ret;
|
||||
unsigned int modifiers = 0;
|
||||
|
||||
ret = hidpp10_get_profile(hidpp10, button->profile->index, &profile);
|
||||
if (ret)
|
||||
@@ -178,6 +179,7 @@ hidpp10drv_map_button(struct ratbag_device *device,
|
||||
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
|
||||
button->action.action.key.key = ratbag_hidraw_get_keycode_from_keyboard_usage(device,
|
||||
profile.buttons[button->index].keys.key);
|
||||
modifiers = profile.buttons[button->index].keys.modifier_flags;
|
||||
break;
|
||||
case PROFILE_BUTTON_TYPE_CONSUMER_CONTROL:
|
||||
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
|
||||
@@ -199,6 +201,14 @@ hidpp10drv_map_button(struct ratbag_device *device,
|
||||
}
|
||||
}
|
||||
|
||||
if (button->action.type == RATBAG_BUTTON_ACTION_TYPE_KEY) {
|
||||
ret = ratbag_button_macro_new_from_keycode(button, button->action.action.key.key, modifiers);
|
||||
if (ret < 0) {
|
||||
log_error(device->ratbag, "hidpp10: error while reading button %d\n", button->index);
|
||||
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
|
||||
}
|
||||
|
||||
@@ -299,6 +309,8 @@ hidpp10drv_write_button(struct hidpp10_profile *profile,
|
||||
struct hidpp10drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct hidpp10_device *hidpp10 = drv_data->dev;
|
||||
uint8_t code;
|
||||
unsigned int modifiers, key;
|
||||
int rc;
|
||||
|
||||
if (hidpp10->profile_type == HIDPP10_PROFILE_UNKNOWN)
|
||||
return -ENOTSUP;
|
||||
@@ -330,6 +342,26 @@ hidpp10drv_write_button(struct hidpp10_profile *profile,
|
||||
profile->buttons[button->index].special.special = code;
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
|
||||
rc = ratbag_action_keycode_from_macro(action, &key, &modifiers);
|
||||
if (rc < 0) {
|
||||
log_error(device->ratbag, "hidpp10: can't convert macro action to keycode in button %d\n", button->index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
code = ratbag_hidraw_get_keyboard_usage_from_keycode(device, key);
|
||||
if (code == 0) {
|
||||
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, key);
|
||||
if (code == 0)
|
||||
return -EINVAL;
|
||||
|
||||
profile->buttons[button->index].consumer_control.type = PROFILE_BUTTON_TYPE_CONSUMER_CONTROL;
|
||||
profile->buttons[button->index].consumer_control.consumer_control = code;
|
||||
} else {
|
||||
profile->buttons[button->index].keys.type = PROFILE_BUTTON_TYPE_KEYS;
|
||||
profile->buttons[button->index].keys.key = code;
|
||||
profile->buttons[button->index].keys.modifier_flags = modifiers;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
@@ -412,9 +444,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[0] != '\0')
|
||||
profile->name = strdup_safe((char*)p.name);
|
||||
|
||||
rc = hidpp10_get_current_profile(hidpp10, &idx);
|
||||
if (rc == 0 && (unsigned int)idx == profile->index)
|
||||
@@ -513,7 +548,7 @@ hidpp10_log(void *userdata, enum hidpp_log_priority priority, const char *format
|
||||
{
|
||||
struct ratbag_device *device = userdata;
|
||||
|
||||
log_msg_va(device->ratbag, priority, format, args);
|
||||
log_msg_va(device->ratbag, (enum ratbag_log_priority)priority, format, args);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -538,7 +573,8 @@ hidpp10drv_commit(struct ratbag_device *device)
|
||||
return rc;
|
||||
|
||||
p.enabled = profile->is_enabled;
|
||||
strncpy_safe((char*)p.name, profile->name, 24);
|
||||
if (profile->name != NULL)
|
||||
strncpy_safe((char*)p.name, profile->name, 24);
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
p.dpi_modes[resolution->index].xres = resolution->dpi_x;
|
||||
@@ -555,8 +591,10 @@ hidpp10drv_commit(struct ratbag_device *device)
|
||||
continue;
|
||||
|
||||
rc = hidpp10drv_write_button(&p, button, &action);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp10: failed to update buttons (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
ratbag_profile_for_each_led(profile, led)
|
||||
@@ -564,8 +602,10 @@ hidpp10drv_commit(struct ratbag_device *device)
|
||||
|
||||
if (dev->profile_type != HIDPP10_PROFILE_UNKNOWN) {
|
||||
rc = hidpp10_set_profile(dev, profile->index, &p);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp10: failed to set profile (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the current resolution in case it changed */
|
||||
@@ -573,8 +613,10 @@ hidpp10drv_commit(struct ratbag_device *device)
|
||||
rc = hidpp10_set_current_resolution(dev,
|
||||
active_resolution->dpi_x,
|
||||
active_resolution->dpi_y);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp10: failed to set active resolution (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
|
||||
active_resolution = NULL;
|
||||
}
|
||||
@@ -628,9 +670,9 @@ hidpp10drv_probe(struct ratbag_device *device)
|
||||
* If there is a special need like for G700(s), then add a DeviceIndex
|
||||
* entry to the .device file.
|
||||
*/
|
||||
dev = hidpp10_device_new(&base, device_idx, type, profile_count);
|
||||
rc = hidpp10_device_new(&base, device_idx, type, profile_count, &dev);
|
||||
|
||||
if (!dev) {
|
||||
if (rc) {
|
||||
log_error(device->ratbag,
|
||||
"Failed to get HID++1.0 device for %s\n",
|
||||
device->name);
|
||||
@@ -711,14 +753,14 @@ static void
|
||||
hidpp10drv_remove(struct ratbag_device *device)
|
||||
{
|
||||
struct hidpp10drv_data *drv_data;
|
||||
struct hidpp10_device *dev;
|
||||
|
||||
ratbag_close_hidraw(device);
|
||||
|
||||
drv_data = ratbag_get_drv_data(device);
|
||||
dev = drv_data->dev;
|
||||
if (!drv_data)
|
||||
return;
|
||||
|
||||
hidpp10_device_destroy(dev);
|
||||
hidpp10_device_destroy(drv_data->dev);
|
||||
|
||||
free(drv_data);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -68,6 +69,7 @@ struct hidpp20drv_data {
|
||||
unsigned num_controls;
|
||||
struct hidpp20_control_id *controls;
|
||||
struct hidpp20_profiles *profiles;
|
||||
struct hidpp20_led *leds;
|
||||
union hidpp20_generic_led_zone_info led_infos;
|
||||
|
||||
unsigned int report_rates[4];
|
||||
@@ -333,14 +335,24 @@ static void
|
||||
hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_data)
|
||||
{
|
||||
struct hidpp20_profile *profile;
|
||||
struct hidpp20_led *h_led;
|
||||
struct hidpp20_led h_led_val;
|
||||
struct hidpp20_led *h_led = &h_led_val;
|
||||
struct hidpp20_color_led_zone_info* led_info;
|
||||
struct hidpp20_color_led_info info;
|
||||
int rc;
|
||||
|
||||
led_info = &drv_data->led_infos.color_leds[led->index];
|
||||
profile = &drv_data->profiles->profiles[led->profile->index];
|
||||
h_led = &profile->leds[led->index];
|
||||
led_info = &drv_data->led_infos.color_leds_8070[led->index];
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
|
||||
profile = &drv_data->profiles->profiles[led->profile->index];
|
||||
h_led = &profile->leds[led->index];
|
||||
} else {
|
||||
rc = hidpp20_color_led_effects_get_zone_effect(drv_data->dev, led->index, h_led);
|
||||
if (rc) {
|
||||
log_debug(led->profile->device->ratbag,
|
||||
"Failed to read led settings\n");
|
||||
}
|
||||
}
|
||||
|
||||
switch (h_led->mode) {
|
||||
case HIDPP20_LED_ON:
|
||||
@@ -359,7 +371,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;
|
||||
@@ -396,10 +408,85 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
|
||||
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);
|
||||
log_debug(led->profile->device->ratbag,
|
||||
"%s: Unsupported effect (%d)\n",
|
||||
led->profile->device->name,
|
||||
ei.effect_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_debug(led->profile->device->ratbag,
|
||||
"%s: Unsupported effect (%d)\n",
|
||||
led->profile->device->name,
|
||||
ei.effect_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -413,6 +500,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);
|
||||
}
|
||||
@@ -486,7 +575,7 @@ hidpp20drv_update_button_8100(struct ratbag_button *button)
|
||||
code = ratbag_hidraw_get_keyboard_usage_from_keycode(device, key);
|
||||
if (code == 0) {
|
||||
subtype = HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL;
|
||||
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, action->action.key.key);
|
||||
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, key);
|
||||
if (code == 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -609,15 +698,17 @@ 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;
|
||||
struct hidpp20_led h_led_val;
|
||||
struct hidpp20_led *h_led = &h_led_val;
|
||||
|
||||
h_profile = &drv_data->profiles->profiles[profile->index];
|
||||
|
||||
h_led = &(h_profile->leds[led->index]);
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
|
||||
h_profile = &drv_data->profiles->profiles[profile->index];
|
||||
h_led = &(h_profile->leds[led->index]);
|
||||
}
|
||||
|
||||
if (!h_led)
|
||||
return -EINVAL;
|
||||
@@ -642,6 +733,13 @@ hidpp20drv_update_led_8070(struct ratbag_led *led, struct ratbag_profile* profil
|
||||
h_led->period = led->ms;
|
||||
h_led->brightness = led->brightness * 100 / 255;
|
||||
|
||||
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)) {
|
||||
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
|
||||
hidpp20_color_led_effects_set_zone_effect(drv_data->dev,
|
||||
led->index,
|
||||
h_led_val);
|
||||
}
|
||||
|
||||
return RATBAG_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -652,8 +750,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);
|
||||
@@ -743,9 +842,12 @@ hidpp20drv_read_report_rate_8060(struct ratbag_device *device)
|
||||
{
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct ratbag *ratbag = device->ratbag;
|
||||
struct ratbag_profile *profile;
|
||||
uint8_t bitflags_ms;
|
||||
int nrates = 0;
|
||||
int rc;
|
||||
uint8_t rate_ms;
|
||||
unsigned rate_hz;
|
||||
|
||||
rc = hidpp20_adjustable_report_rate_get_report_rate_list(drv_data->dev,
|
||||
&bitflags_ms);
|
||||
@@ -764,6 +866,37 @@ hidpp20drv_read_report_rate_8060(struct ratbag_device *device)
|
||||
|
||||
drv_data->num_report_rates = nrates;
|
||||
|
||||
if (!hidpp20_adjustable_report_rate_get_report_rate(drv_data->dev, &rate_ms)) {
|
||||
switch (rate_ms)
|
||||
{
|
||||
case 1:
|
||||
rate_hz = 1000;
|
||||
break;
|
||||
case 2:
|
||||
case 3: /* 3ms = 333.(3)Hz, we round to 500Hz */
|
||||
rate_hz = 500;
|
||||
break;
|
||||
case 4:
|
||||
case 5: /* 5ms = 200Hz, we round to 250Hz */
|
||||
rate_hz = 250;
|
||||
break;
|
||||
case 6: /* 6ms = 166.(6)Hz, we round to 125Hz */
|
||||
case 7: /* 7ms = 142Hz, we round to 125Hz */
|
||||
case 8:
|
||||
rate_hz = 125;
|
||||
break;
|
||||
default:
|
||||
rate_hz = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rate_hz) {
|
||||
log_debug(ratbag, "report rate is %u\n", rate_hz);
|
||||
ratbag_device_for_each_profile(device, profile)
|
||||
profile->hz = rate_hz;
|
||||
}
|
||||
}
|
||||
|
||||
log_debug(ratbag, "device has %d report rates\n", nrates);
|
||||
|
||||
return 0;
|
||||
@@ -780,7 +913,8 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
|
||||
unsigned int default_rate = 500;
|
||||
int rc;
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_RESOLUTION_2200) {
|
||||
if ((drv_data->capabilities & HIDPP_CAP_RESOLUTION_2200) &&
|
||||
(drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201) == 0) {
|
||||
uint16_t resolution;
|
||||
uint8_t flags;
|
||||
|
||||
@@ -823,7 +957,7 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060) {
|
||||
rc = hidpp20drv_read_report_rate_8060(device);
|
||||
if (rc < 0)
|
||||
if (rc < 0 && drv_data->report_rates[0] == 0)
|
||||
return rc;
|
||||
|
||||
ratbag_profile_set_report_rate_list(profile,
|
||||
@@ -849,6 +983,9 @@ hidpp20drv_update_resolution_dpi_8100(struct ratbag_resolution *resolution,
|
||||
h_profile = &drv_data->profiles->profiles[profile->index];
|
||||
h_profile->dpi[resolution->index] = dpi;
|
||||
|
||||
if (resolution->is_default)
|
||||
h_profile->default_dpi = resolution->index;
|
||||
|
||||
return RATBAG_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -860,7 +997,7 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct hidpp20_sensor *sensor;
|
||||
int rc, i;
|
||||
int i;
|
||||
int dpi = dpi_x; /* dpi_x == dpi_y if we don't have the individual resolution cap */
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
|
||||
@@ -876,14 +1013,13 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
|
||||
sensor = &drv_data->sensors[0];
|
||||
|
||||
/* validate that the sensor accepts the given DPI */
|
||||
rc = -EINVAL;
|
||||
if (dpi < sensor->dpi_min || dpi > sensor->dpi_max)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
if (sensor->dpi_steps) {
|
||||
for (i = sensor->dpi_min; i < dpi; i += sensor->dpi_steps) {
|
||||
}
|
||||
if (i != dpi)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
} else {
|
||||
i = 0;
|
||||
while (sensor->dpi_list[i]) {
|
||||
@@ -891,13 +1027,24 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
|
||||
break;
|
||||
}
|
||||
if (sensor->dpi_list[i] != dpi)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = hidpp20_adjustable_dpi_set_sensor_dpi(drv_data->dev, sensor, dpi);
|
||||
return hidpp20_adjustable_dpi_set_sensor_dpi(drv_data->dev, sensor, dpi);
|
||||
}
|
||||
|
||||
out:
|
||||
return rc;
|
||||
static int
|
||||
hidpp20drv_update_report_rate_8060(struct ratbag_profile *profile, int hz)
|
||||
{
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
int rc;
|
||||
|
||||
rc = hidpp20_adjustable_report_rate_set_report_rate(drv_data->dev, 1000/hz);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return RATBAG_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -918,12 +1065,20 @@ hidpp20drv_update_report_rate(struct ratbag_profile *profile, int hz)
|
||||
{
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
int rc;
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
|
||||
return hidpp20drv_update_report_rate_8100(profile, hz);
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060)
|
||||
return -ENOTSUP;
|
||||
if (drv_data->capabilities & HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060) {
|
||||
rc = hidpp20drv_update_report_rate_8060(profile, hz);
|
||||
|
||||
/* re-populate the profile with the correct value if we fail */
|
||||
if (rc)
|
||||
hidpp20drv_read_report_rate_8060(profile->device);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
@@ -941,6 +1096,7 @@ hidpp20drv_read_special_key_mouse(struct ratbag_device *device)
|
||||
drv_data->controls = NULL;
|
||||
drv_data->num_controls = 0;
|
||||
rc = hidpp20_special_key_mouse_get_controls(drv_data->dev, &drv_data->controls);
|
||||
log_raw(device->ratbag, "num_control %d\n", rc);
|
||||
if (rc > 0) {
|
||||
drv_data->num_controls = rc;
|
||||
rc = 0;
|
||||
@@ -971,19 +1127,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 +1145,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)
|
||||
{
|
||||
@@ -1025,9 +1209,7 @@ hidpp20drv_read_profile_8100(struct ratbag_profile *profile)
|
||||
|
||||
profile->is_enabled = drv_data->profiles->profiles[profile->index].enabled;
|
||||
|
||||
profile->is_active = false;
|
||||
if ((int)profile->index == hidpp20drv_current_profile(device))
|
||||
profile->is_active = true;
|
||||
profile->is_active = (profile->index == drv_data->profiles->active_profile_index);
|
||||
|
||||
if (profile->is_active)
|
||||
dpi_index = hidpp20_onboard_profiles_get_current_dpi_index(drv_data->dev);
|
||||
@@ -1068,6 +1250,60 @@ hidpp20drv_read_profile_8100(struct ratbag_profile *profile)
|
||||
ratbag_profile_set_report_rate(profile, p->report_rate);
|
||||
}
|
||||
|
||||
static int
|
||||
hidpp20drv_init_leds_8070_8071(struct ratbag_device *device)
|
||||
{
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct ratbag *ratbag = device->ratbag;
|
||||
|
||||
/* we only support 0x8071 via 0x8100 */
|
||||
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) &&
|
||||
drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071) {
|
||||
log_debug(ratbag, "disabling 0x8071 (RGB Effects) feature because the device doesn't have 0x8100 (Onboard Memory Profiles)\n");
|
||||
drv_data->capabilities &= ~HIDPP_CAP_RGB_EFFECTS_8071;
|
||||
}
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070 ||
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
hidpp20drv_init_profile_8100(struct ratbag_device *device)
|
||||
{
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct ratbag *ratbag = device->ratbag;
|
||||
int rc;
|
||||
|
||||
log_debug(ratbag, "initializing onboard profiles\n");
|
||||
rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = hidpp20_onboard_profiles_initialize(drv_data->dev, drv_data->profiles);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
drv_data->num_profiles = drv_data->profiles->num_profiles;
|
||||
drv_data->num_buttons = drv_data->profiles->num_buttons;
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201)
|
||||
drv_data->num_resolutions = drv_data->profiles->num_modes;
|
||||
/* We ignore the profile's num_leds and require
|
||||
* HIDPP_PAGE_COLOR_LED_EFFECTS to succeed instead
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hidpp20drv_read_profile(struct ratbag_profile *profile)
|
||||
{
|
||||
@@ -1195,15 +1431,18 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
|
||||
break;
|
||||
}
|
||||
case HIDPP_PAGE_COLOR_LED_EFFECTS: {
|
||||
/* The 8070 feauture implemented in the G602 doesn't follow the spec,
|
||||
* so we ignore it */
|
||||
if (ratbag_device_data_hidpp20_get_quirk(device->data) == HIDPP20_QUIRK_G602)
|
||||
break;
|
||||
|
||||
log_debug(ratbag, "device has color effects\n");
|
||||
drv_data->capabilities |= HIDPP_CAP_COLOR_LED_EFFECTS_8070;
|
||||
|
||||
/* we read the profile once to get the correct number of
|
||||
* supported leds. */
|
||||
if (hidpp20drv_read_color_leds(device))
|
||||
return 0;
|
||||
|
||||
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;
|
||||
break;
|
||||
}
|
||||
case HIDPP_PAGE_LED_SW_CONTROL: {
|
||||
@@ -1221,22 +1460,6 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
|
||||
case HIDPP_PAGE_ONBOARD_PROFILES: {
|
||||
log_debug(ratbag, "device has onboard profiles\n");
|
||||
drv_data->capabilities |= HIDPP_CAP_ONBOARD_PROFILES_8100;
|
||||
|
||||
rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = hidpp20_onboard_profiles_initialize(drv_data->dev, drv_data->profiles);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
drv_data->num_profiles = drv_data->profiles->num_profiles;
|
||||
drv_data->num_resolutions = drv_data->profiles->num_modes;
|
||||
drv_data->num_buttons = drv_data->profiles->num_buttons;
|
||||
/* We ignore the profile's num_leds and require
|
||||
* HIDPP_PAGE_COLOR_LED_EFFECTS to succeed instead
|
||||
*/
|
||||
|
||||
break;
|
||||
}
|
||||
case HIDPP_PAGE_MOUSE_BUTTON_SPY: {
|
||||
@@ -1258,27 +1481,27 @@ hidpp20drv_commit(struct ratbag_device *device)
|
||||
struct ratbag_led *led;
|
||||
struct ratbag_resolution *resolution;
|
||||
int rc;
|
||||
unsigned int dpi_index = 0;
|
||||
|
||||
list_for_each(profile, &device->profiles, link) {
|
||||
if (!profile->dirty)
|
||||
continue;
|
||||
|
||||
rc = hidpp20drv_update_report_rate(profile, profile->hz);
|
||||
if (rc)
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
if (profile->rate_dirty) {
|
||||
rc = hidpp20drv_update_report_rate(profile, profile->hz);
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp20: failed to update report rate (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (resolution->is_active) {
|
||||
log_raw(device->ratbag, "dpi index: %d, profile %d\n", resolution->index, profile->index);
|
||||
dpi_index = resolution->index;
|
||||
}
|
||||
|
||||
rc = hidpp20drv_update_resolution_dpi(resolution,
|
||||
resolution->dpi_x,
|
||||
resolution->dpi_y);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp20: failed to update resolution (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each(button, &profile->buttons, link) {
|
||||
@@ -1286,8 +1509,10 @@ hidpp20drv_commit(struct ratbag_device *device)
|
||||
continue;
|
||||
|
||||
rc = hidpp20drv_update_button(button);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp20: failed to update button (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each(led, &profile->leds, link) {
|
||||
@@ -1295,10 +1520,11 @@ hidpp20drv_commit(struct ratbag_device *device)
|
||||
continue;
|
||||
|
||||
rc = hidpp20drv_update_led(led);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp20: failed to update led (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
|
||||
@@ -1307,17 +1533,18 @@ hidpp20drv_commit(struct ratbag_device *device)
|
||||
|
||||
rc = hidpp20_onboard_profiles_commit(drv_data->dev,
|
||||
drv_data->profiles);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp20: failed to commit profile (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
|
||||
list_for_each(profile, &device->profiles, link) {
|
||||
if (profile->is_active) {
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (resolution->is_active)
|
||||
dpi_index = resolution->index;
|
||||
hidpp20_onboard_profiles_set_current_dpi_index(drv_data->dev,
|
||||
resolution->index);
|
||||
}
|
||||
hidpp20_onboard_profiles_set_current_dpi_index(drv_data->dev,
|
||||
dpi_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1348,8 +1575,21 @@ hidpp20drv_20_probe(struct ratbag_device *device)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* initializations that depend on other features */
|
||||
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070 ||
|
||||
drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071) {
|
||||
rc = hidpp20drv_init_leds_8070_8071(device);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
|
||||
rc = hidpp20drv_init_profile_8100(device);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1363,7 +1603,7 @@ hidpp20_log(void *userdata, enum hidpp_log_priority priority, const char *format
|
||||
{
|
||||
struct ratbag_device *device = userdata;
|
||||
|
||||
log_msg_va(device->ratbag, priority, format, args);
|
||||
log_msg_va(device->ratbag, (enum ratbag_log_priority)priority, format, args);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1380,9 +1620,10 @@ 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->controls);
|
||||
free(drv_data->sensors);
|
||||
free(drv_data->leds);
|
||||
if (drv_data->dev)
|
||||
hidpp20_device_destroy(drv_data->dev);
|
||||
free(drv_data);
|
||||
@@ -1393,6 +1634,20 @@ hidpp20drv_init_device(struct ratbag_device *device,
|
||||
struct hidpp20drv_data *drv_data)
|
||||
{
|
||||
struct ratbag_profile *profile;
|
||||
bool active_profile = false;
|
||||
int num;
|
||||
|
||||
num = ratbag_device_data_hidpp20_get_led_count(device->data);
|
||||
if (num >= 0)
|
||||
drv_data->num_leds = num;
|
||||
|
||||
num = ratbag_device_data_hidpp20_get_button_count(device->data);
|
||||
if (num >= 0)
|
||||
drv_data->num_buttons = num;
|
||||
|
||||
num = ratbag_device_data_hidpp20_get_report_rate(device->data);
|
||||
if (num >= 0)
|
||||
drv_data->report_rates[0] = num;
|
||||
|
||||
ratbag_device_init_profiles(device,
|
||||
drv_data->num_profiles,
|
||||
@@ -1402,6 +1657,18 @@ hidpp20drv_init_device(struct ratbag_device *device,
|
||||
|
||||
ratbag_device_for_each_profile(device, profile)
|
||||
hidpp20drv_read_profile(profile);
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
|
||||
/* Fallback to the first profile if no profile is active */
|
||||
list_for_each(profile, &device->profiles, link)
|
||||
if (profile->is_active)
|
||||
active_profile = true;
|
||||
|
||||
if (!active_profile && device->num_profiles >= 1) {
|
||||
profile = ratbag_device_get_profile(device, 0);
|
||||
profile->is_active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1434,19 +1701,24 @@ hidpp20drv_probe(struct ratbag_device *device)
|
||||
* If there is a special need like for G900, we can add this in the
|
||||
* device data file.
|
||||
*/
|
||||
dev = hidpp20_device_new(&base, device_idx);
|
||||
dev = hidpp20_device_new(&base, device_idx, (struct hidpp_hid_report*) device->hidraw[0].reports, device->hidraw[0].num_reports);
|
||||
if (!dev) {
|
||||
rc = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev->quirk = ratbag_device_data_hidpp20_get_quirk(device->data);
|
||||
|
||||
drv_data->dev = dev;
|
||||
|
||||
log_debug(device->ratbag, "'%s' is using protocol v%d.%d\n", ratbag_device_get_name(device), dev->proto_major, dev->proto_minor);
|
||||
|
||||
if(dev->quirk != HIDPP20_QUIRK_NONE)
|
||||
log_debug(device->ratbag, "'%s' is quirked (%s)\n", ratbag_device_get_name(device), hidpp20_get_quirk_string(dev->quirk));
|
||||
|
||||
/* add some defaults that will be overwritten by the device */
|
||||
drv_data->num_profiles = 1;
|
||||
drv_data->num_resolutions = 1;
|
||||
drv_data->num_resolutions = 0;
|
||||
drv_data->num_buttons = 8;
|
||||
drv_data->num_leds = 0;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#define LOGITECH_G300_BUTTON_MAX 8
|
||||
#define LOGITECH_G300_NUM_DPI 4
|
||||
#define LOGITECH_G300_NUM_LED 1
|
||||
#define LOGITECH_G300_DPI_MIN 250
|
||||
#define LOGITECH_G300_DPI_MIN 100
|
||||
#define LOGITECH_G300_DPI_MAX 2500
|
||||
|
||||
#define LOGITECH_G300_REPORT_ID_GET_ACTIVE 0xF0
|
||||
@@ -51,7 +51,7 @@
|
||||
#define LOGITECH_G300_REPORT_SIZE_PROFILE 35
|
||||
|
||||
struct logitech_g300_resolution {
|
||||
uint8_t dpi :7; /* Range 1-10. dpi = 250*value */
|
||||
uint8_t dpi :7; /* Range 1-10. dpi = 100*value */
|
||||
uint8_t is_default :1;
|
||||
} __attribute__((packed));
|
||||
|
||||
@@ -449,11 +449,10 @@ logitech_g300_read_profile(struct ratbag_profile *profile)
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
struct logitech_g300_resolution *res =
|
||||
&report->dpi_levels[resolution->index];
|
||||
unsigned int dpis[10] = { 250, 500, 750, 1000, 1250, 1500, 1750,
|
||||
2000, 2250, 2500 };
|
||||
unsigned int dpis[49] = { 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950, 2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350, 2400, 2450, 2500, };
|
||||
|
||||
resolution->dpi_x = res->dpi * 250;
|
||||
resolution->dpi_y = res->dpi * 250;
|
||||
resolution->dpi_x = res->dpi * 50;
|
||||
resolution->dpi_y = res->dpi * 50;
|
||||
resolution->is_default = res->is_default;
|
||||
resolution->is_active = res->is_default;
|
||||
|
||||
@@ -546,7 +545,7 @@ logitech_g300_write_profile(struct ratbag_profile *profile)
|
||||
struct logitech_g300_resolution *res =
|
||||
&report->dpi_levels[resolution->index];
|
||||
|
||||
res->dpi = resolution->dpi_x / 250;
|
||||
res->dpi = resolution->dpi_x / 50;
|
||||
res->is_default = resolution->is_default;
|
||||
|
||||
if (profile->is_active && resolution->is_active)
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include "libratbag-hidraw.h"
|
||||
|
||||
#define LOGITECH_G600_NUM_PROFILES 3
|
||||
#define LOGITECH_G600_NUM_BUTTONS 20
|
||||
#define LOGITECH_G600_NUM_BUTTONS 41 // 20 buttons + 1 color buffer + 20 G-shift
|
||||
#define LOGITECH_G600_NUM_DPI 4
|
||||
#define LOGITECH_G600_NUM_LED 1
|
||||
#define LOGITECH_G600_DPI_MIN 200
|
||||
@@ -109,20 +109,41 @@ static const struct logitech_g600_button_type_mapping logitech_g600_button_type_
|
||||
{ 3, RATBAG_BUTTON_TYPE_WHEEL_LEFT },
|
||||
{ 4, RATBAG_BUTTON_TYPE_WHEEL_RIGHT },
|
||||
{ 5, RATBAG_BUTTON_TYPE_PINKIE },
|
||||
{ 6, RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP },
|
||||
{ 7, RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP },
|
||||
{ 8, RATBAG_BUTTON_TYPE_SIDE }, // 8 = G9 ... 19 = G20
|
||||
{ 9, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 10, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 11, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 12, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 13, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 14, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 15, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 16, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 17, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 18, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 19, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 6, RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP }, // G07
|
||||
{ 7, RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP }, // G08
|
||||
{ 8, RATBAG_BUTTON_TYPE_SIDE }, // G09
|
||||
{ 9, RATBAG_BUTTON_TYPE_SIDE }, // G10
|
||||
{ 10, RATBAG_BUTTON_TYPE_SIDE }, // G11
|
||||
{ 11, RATBAG_BUTTON_TYPE_SIDE }, // G12
|
||||
{ 12, RATBAG_BUTTON_TYPE_SIDE }, // G13
|
||||
{ 13, RATBAG_BUTTON_TYPE_SIDE }, // G14
|
||||
{ 14, RATBAG_BUTTON_TYPE_SIDE }, // G15
|
||||
{ 15, RATBAG_BUTTON_TYPE_SIDE }, // G16
|
||||
{ 16, RATBAG_BUTTON_TYPE_SIDE }, // G17
|
||||
{ 17, RATBAG_BUTTON_TYPE_SIDE }, // G18
|
||||
{ 18, RATBAG_BUTTON_TYPE_SIDE }, // G19
|
||||
{ 19, RATBAG_BUTTON_TYPE_SIDE }, // G20
|
||||
{ 20, RATBAG_BUTTON_TYPE_UNKNOWN }, //HACK is where the G-shift color information is located
|
||||
{ 21, RATBAG_BUTTON_TYPE_LEFT }, // G-shift + left
|
||||
{ 22, RATBAG_BUTTON_TYPE_RIGHT }, // G-shift + right click
|
||||
{ 23, RATBAG_BUTTON_TYPE_MIDDLE }, // G-shift + middle
|
||||
{ 24, RATBAG_BUTTON_TYPE_WHEEL_LEFT}, // G-shift + scroll left
|
||||
{ 25, RATBAG_BUTTON_TYPE_WHEEL_RIGHT}, // G-shift + scroll right
|
||||
{ 26, RATBAG_BUTTON_TYPE_PINKIE }, // G-shift + Pinky(shift modifer)
|
||||
{ 27, RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP }, // G-shift + 07
|
||||
{ 28, RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP }, // G-shift + G08
|
||||
{ 29, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G09
|
||||
{ 30, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G10
|
||||
{ 31, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G11
|
||||
{ 32, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G12
|
||||
{ 33, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G13
|
||||
{ 34, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G14
|
||||
{ 35, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G15
|
||||
{ 36, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G16
|
||||
{ 37, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G17
|
||||
{ 38, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G18
|
||||
{ 39, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G19
|
||||
{ 40, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G20
|
||||
};
|
||||
|
||||
static enum ratbag_button_type
|
||||
@@ -144,7 +165,7 @@ struct logitech_g600_button_mapping {
|
||||
};
|
||||
|
||||
static struct logitech_g600_button_mapping logitech_g600_button_mapping[] = {
|
||||
/* 0x00 is either key or unassigned. Must be handled separatly */
|
||||
/* 0x00 is either key or unassigned. Must be handled separately */
|
||||
{ 0x01, BUTTON_ACTION_BUTTON(1) },
|
||||
{ 0x02, BUTTON_ACTION_BUTTON(2) },
|
||||
{ 0x03, BUTTON_ACTION_BUTTON(3) },
|
||||
@@ -600,6 +621,12 @@ logitech_g600_write_profile(struct ratbag_profile *profile)
|
||||
report->led_duration = 0x0f;
|
||||
}
|
||||
|
||||
// For now the default will copy over the main color into the g-shift color
|
||||
// future update may add support to set this via cli command
|
||||
report->g_shift_color[0] = report->led_red;
|
||||
report->g_shift_color[1] = report->led_green;
|
||||
report->g_shift_color[2] = report->led_blue;
|
||||
|
||||
buf = (uint8_t*)report;
|
||||
|
||||
rc = ratbag_hidraw_raw_request(device, report->id,
|
||||
|
||||
498
src/driver-openinput.c
Normal file
498
src/driver-openinput.c
Normal file
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* Copyright © 2021 Filipe Laíns
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "libratbag-private.h"
|
||||
|
||||
|
||||
/* reports */
|
||||
#define OI_REPORT_SHORT 0x20
|
||||
#define OI_REPORT_LONG 0x21
|
||||
#define OI_REPORT_SHORT_SIZE 8
|
||||
#define OI_REPORT_LONG_SIZE 32
|
||||
|
||||
#define OI_REPORT_MAX_SIZE OI_REPORT_LONG_SIZE
|
||||
#define OI_REPORT_DATA_INDEX 3
|
||||
#define OI_REPORT_DATA_MAX_SIZE OI_REPORT_LONG_SIZE - OI_REPORT_DATA_INDEX
|
||||
|
||||
/* protocol function pages */
|
||||
#define OI_PAGE_INFO 0x00
|
||||
#define OI_PAGE_GIMMICKS 0xFD
|
||||
#define OI_PAGE_DEBUG 0xFE
|
||||
#define OI_PAGE_ERROR 0xFF
|
||||
|
||||
/* info page (0x00) functions */
|
||||
#define OI_FUNCTION_VERSION 0x00
|
||||
#define OI_FUNCTION_FW_INFO 0x01
|
||||
#define OI_FUNCTION_SUPPORTED_FUNCTION_PAGES 0x02
|
||||
#define OI_FUNCTION_SUPPORTED_FUNCTIONS 0x03
|
||||
|
||||
/* error page (0xFF) */
|
||||
#define OI_ERROR_INVALID_VALUE 0x01
|
||||
#define OI_ERROR_UNSUPPORTED_FUNCTION 0x02
|
||||
#define OI_ERROR_CUSTOM 0xFE
|
||||
|
||||
|
||||
static unsigned int report_rates[] = { 125, 250, 500, 750, 1000 };
|
||||
|
||||
struct openinput_drv_data {
|
||||
unsigned int num_profiles;
|
||||
unsigned int num_resolutions;
|
||||
unsigned int num_buttons;
|
||||
unsigned int num_leds;
|
||||
unsigned int fw_major;
|
||||
unsigned int fw_minor;
|
||||
unsigned int fw_patch;
|
||||
uint64_t supported;
|
||||
};
|
||||
|
||||
struct oi_report_t {
|
||||
uint8_t id;
|
||||
uint8_t function_page;
|
||||
uint8_t function;
|
||||
uint8_t data[29];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
|
||||
#define CASE_RETURN_STRING(a) case a: return #a; break
|
||||
static const char*
|
||||
openinput_function_page_get_name(uint8_t page)
|
||||
{
|
||||
static char numeric[16];
|
||||
char *str;
|
||||
|
||||
switch(page) {
|
||||
CASE_RETURN_STRING(OI_PAGE_INFO);
|
||||
CASE_RETURN_STRING(OI_PAGE_GIMMICKS);
|
||||
CASE_RETURN_STRING(OI_PAGE_DEBUG);
|
||||
CASE_RETURN_STRING(OI_PAGE_ERROR);
|
||||
default:
|
||||
sprintf_safe(numeric, "0x%02x", page);
|
||||
str = numeric;
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static const char*
|
||||
openinput_function_get_name(uint8_t page, uint8_t function)
|
||||
{
|
||||
static char numeric[32];
|
||||
char *str = NULL;
|
||||
|
||||
switch(page) {
|
||||
case OI_PAGE_INFO:
|
||||
switch (function) {
|
||||
CASE_RETURN_STRING(OI_FUNCTION_VERSION);
|
||||
CASE_RETURN_STRING(OI_FUNCTION_FW_INFO);
|
||||
CASE_RETURN_STRING(OI_FUNCTION_SUPPORTED_FUNCTION_PAGES);
|
||||
CASE_RETURN_STRING(OI_FUNCTION_SUPPORTED_FUNCTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
if (!str) {
|
||||
sprintf_safe(numeric, "0x%02x 0x%02x", page, function);
|
||||
str = numeric;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
#undef CASE_RETURN_STRING
|
||||
|
||||
static const char*
|
||||
openinput_get_error_string(struct oi_report_t *report)
|
||||
{
|
||||
char help_str[OI_REPORT_LONG_SIZE - OI_REPORT_DATA_INDEX + 1] = {0};
|
||||
char *str;
|
||||
|
||||
switch (report->function) {
|
||||
case OI_ERROR_INVALID_VALUE:
|
||||
xasprintf(&str, "Invalid value (in position %u)", report->data[2]);
|
||||
break;
|
||||
case OI_ERROR_CUSTOM:
|
||||
memcpy(help_str, report->data, sizeof(help_str)-1); /* copy error string */
|
||||
xasprintf(&str, "Custom error (%s)", help_str);
|
||||
break;
|
||||
case OI_ERROR_UNSUPPORTED_FUNCTION:
|
||||
xasprintf(&str, "Unsupported function (0x%02x, 0x%02x)", report->data[0], report->data[1]);
|
||||
break;
|
||||
default:
|
||||
xasprintf(&str, "Unknown error (%u)", report->function);
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static size_t
|
||||
openinput_get_report_size(unsigned int report)
|
||||
{
|
||||
switch (report) {
|
||||
case OI_REPORT_SHORT:
|
||||
return OI_REPORT_SHORT_SIZE;
|
||||
case OI_REPORT_LONG:
|
||||
return OI_REPORT_LONG_SIZE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
openinput_report_filter(uint8_t *buf, size_t len)
|
||||
{
|
||||
if (len < 1)
|
||||
return false;
|
||||
|
||||
switch(buf[0]) {
|
||||
case OI_REPORT_SHORT:
|
||||
return len == OI_REPORT_SHORT_SIZE;
|
||||
case OI_REPORT_LONG:
|
||||
return len == OI_REPORT_LONG_SIZE;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
openinput_send_report(struct ratbag_device *device, struct oi_report_t *report)
|
||||
{
|
||||
int ret;
|
||||
uint8_t buffer[OI_REPORT_MAX_SIZE];
|
||||
size_t size = openinput_get_report_size(report->id);
|
||||
|
||||
memcpy(buffer, report, size);
|
||||
|
||||
ret = ratbag_hidraw_output_report(device, buffer, size);
|
||||
if (ret < 0) {
|
||||
log_error(device->ratbag, "openinput: failed to send data to device (%s)\n",
|
||||
strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ratbag_hidraw_read_input_report(device, buffer, OI_REPORT_MAX_SIZE, openinput_report_filter);
|
||||
if (ret < 0) {
|
||||
log_error(device->ratbag, "openinput: failed to read data from device (%s)\n",
|
||||
strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
memcpy(report, buffer, openinput_get_report_size(buffer[0]));
|
||||
|
||||
/* check for error */
|
||||
if (report->function_page == OI_PAGE_ERROR) {
|
||||
log_error(device->ratbag, "openinput: %s\n", openinput_get_error_string(report));
|
||||
return report->function;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
openinput_info_version(struct ratbag_device *device)
|
||||
{
|
||||
int ret;
|
||||
struct openinput_drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct oi_report_t report = {
|
||||
.id = OI_REPORT_SHORT,
|
||||
.function_page = OI_PAGE_INFO,
|
||||
.function = OI_FUNCTION_VERSION
|
||||
};
|
||||
|
||||
ret = openinput_send_report(device, &report);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drv_data->fw_major = report.data[0];
|
||||
drv_data->fw_minor = report.data[1];
|
||||
drv_data->fw_patch = report.data[2];
|
||||
|
||||
log_info(device->ratbag, "openinput: protocol version %u.%u.%u\n",
|
||||
drv_data->fw_major, drv_data->fw_minor, drv_data->fw_patch);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define OI_FUNCTION_FW_INFO_VENDOR 0x00
|
||||
#define OI_FUNCTION_FW_INFO_VERSION 0x01
|
||||
#define OI_FUNCTION_FW_INFO_DEVICE_NAME 0x02
|
||||
|
||||
static int
|
||||
openinput_info_fw_info(struct ratbag_device *device,
|
||||
uint8_t field_id,
|
||||
unsigned char *description,
|
||||
size_t description_size)
|
||||
{
|
||||
int ret;
|
||||
struct oi_report_t report = {
|
||||
.id = OI_REPORT_SHORT,
|
||||
.function_page = OI_PAGE_INFO,
|
||||
.function = OI_FUNCTION_FW_INFO,
|
||||
.data = {field_id}
|
||||
};
|
||||
|
||||
ret = openinput_send_report(device, &report);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(description, report.data, min(sizeof(report.data), description_size));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
openinput_info_supported_function_pages(struct ratbag_device *device,
|
||||
uint8_t start_index,
|
||||
uint8_t *pages_count,
|
||||
uint8_t *pages_left,
|
||||
uint8_t *pages,
|
||||
size_t pages_size)
|
||||
{
|
||||
int ret;
|
||||
struct oi_report_t report = {
|
||||
.id = OI_REPORT_SHORT,
|
||||
.function_page = OI_PAGE_INFO,
|
||||
.function = OI_FUNCTION_SUPPORTED_FUNCTION_PAGES,
|
||||
.data = {start_index}
|
||||
};
|
||||
|
||||
ret = openinput_send_report(device, &report);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*pages_count = report.data[0];
|
||||
*pages_left = report.data[1];
|
||||
memcpy(pages, report.data + 2, min(sizeof(report.data), pages_size));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
openinput_info_supported_functions(struct ratbag_device *device,
|
||||
uint8_t function_page,
|
||||
uint8_t start_index,
|
||||
uint8_t *functions_count,
|
||||
uint8_t *functions_left,
|
||||
uint8_t *functions,
|
||||
size_t functions_size)
|
||||
{
|
||||
int ret;
|
||||
struct oi_report_t report = {
|
||||
.id = OI_REPORT_SHORT,
|
||||
.function_page = OI_PAGE_INFO,
|
||||
.function = OI_FUNCTION_SUPPORTED_FUNCTIONS,
|
||||
.data = {function_page, start_index}
|
||||
};
|
||||
|
||||
ret = openinput_send_report(device, &report);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*functions_count = report.data[0];
|
||||
*functions_left = report.data[1];
|
||||
memcpy(functions, report.data + 2, min(sizeof(report.data), functions_size));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
openinput_read_supported_functions(struct ratbag_device *device, uint8_t page)
|
||||
{
|
||||
struct ratbag *ratbag = device->ratbag;
|
||||
int ret;
|
||||
uint8_t i, total, read = 0, count = 0, left = 0;
|
||||
uint8_t buffer[OI_REPORT_DATA_MAX_SIZE];
|
||||
|
||||
ret = openinput_info_supported_functions(device,
|
||||
page,
|
||||
read,
|
||||
&count, &left,
|
||||
buffer, sizeof(buffer));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
total = count + left;
|
||||
uint8_t functions[total];
|
||||
|
||||
memcpy(functions, buffer, count);
|
||||
|
||||
/* there are still functions left to read! */
|
||||
while (left) {
|
||||
ret = openinput_info_supported_functions(device,
|
||||
page,
|
||||
read,
|
||||
&count, &left,
|
||||
buffer, sizeof(buffer));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* make sure the new size values make sense, to avoid deadlocks */
|
||||
if (total != (read + count + left)) {
|
||||
log_error(ratbag, "openinput: invalid number of functions left to read (%u)\n", left);
|
||||
return -EINVAL;
|
||||
}
|
||||
log_debug(ratbag, "openinput: read %u functions, %u left\n", count, left);
|
||||
|
||||
memcpy(functions + read, buffer, count);
|
||||
read += count;
|
||||
}
|
||||
|
||||
/* iterate over read functions */
|
||||
for (i = 0; i < total; i++) {
|
||||
log_debug(ratbag, "openinput: found function %s\n", openinput_function_get_name(page, functions[i]));
|
||||
/* TODO: set bits in drv_data->supported when we implement support for certain capabilities */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
openinput_read_supported_function_pages(struct ratbag_device *device)
|
||||
{
|
||||
struct ratbag *ratbag = device->ratbag;
|
||||
int ret;
|
||||
uint8_t i, total, read = 0, count = 0, left = 0;
|
||||
uint8_t buffer[OI_REPORT_DATA_MAX_SIZE];
|
||||
|
||||
log_debug(ratbag, "openinput: starting reading device functions...\n");
|
||||
|
||||
ret = openinput_info_supported_function_pages(device,
|
||||
read,
|
||||
&count, &left,
|
||||
buffer, sizeof(buffer));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
total = count + left;
|
||||
uint8_t pages[total];
|
||||
|
||||
memcpy(pages, buffer, count);
|
||||
|
||||
/* there are still function pages left to read! */
|
||||
while (left) {
|
||||
ret = openinput_info_supported_function_pages(device,
|
||||
read,
|
||||
&count, &left,
|
||||
buffer, sizeof(buffer));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* make sure the new size values make sense, to avoid deadlocks */
|
||||
if (total != (read + count + left)) {
|
||||
log_error(ratbag, "openinput: invalid number of function pages left to read (%u)\n", left);
|
||||
return -EINVAL;
|
||||
}
|
||||
log_debug(ratbag, "openinput: read %u pages, %u left\n", count, left);
|
||||
|
||||
memcpy(pages + read, buffer, count);
|
||||
read += count;
|
||||
}
|
||||
|
||||
/* iterate over read function pages, and read their functions */
|
||||
for (i = 0; i < total; i++) {
|
||||
log_debug(ratbag, "openinput: found function page %s\n", openinput_function_page_get_name(pages[i]));
|
||||
openinput_read_supported_functions(device, pages[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
openinput_read_profile(struct ratbag_profile *profile)
|
||||
{
|
||||
ratbag_profile_set_report_rate_list(profile, report_rates, sizeof(*report_rates));
|
||||
profile->is_active = true;
|
||||
}
|
||||
|
||||
static int
|
||||
openinput_test_hidraw(struct ratbag_device *device)
|
||||
{
|
||||
return ratbag_hidraw_has_report(device, OI_REPORT_SHORT);
|
||||
}
|
||||
|
||||
static int
|
||||
openinput_probe(struct ratbag_device *device)
|
||||
{
|
||||
int ret;
|
||||
struct openinput_drv_data *drv_data;
|
||||
struct ratbag_profile *profile;
|
||||
unsigned char str[OI_REPORT_DATA_MAX_SIZE];
|
||||
|
||||
ret = ratbag_find_hidraw(device, openinput_test_hidraw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drv_data = zalloc(sizeof(*drv_data));
|
||||
|
||||
drv_data->num_profiles = 1;
|
||||
|
||||
ratbag_set_drv_data(device, drv_data);
|
||||
|
||||
openinput_info_version(device);
|
||||
|
||||
ret = openinput_info_fw_info(device, OI_FUNCTION_FW_INFO_VENDOR, str, sizeof(str));
|
||||
if (ret)
|
||||
return ret;
|
||||
log_info(device->ratbag, "openinput: firmware vendor: %s\n", str);
|
||||
|
||||
ret = openinput_info_fw_info(device, OI_FUNCTION_FW_INFO_VERSION, str, sizeof(str));
|
||||
if (ret)
|
||||
return ret;
|
||||
log_info(device->ratbag, "openinput: firmware version: %s\n", str);
|
||||
|
||||
ret = openinput_info_fw_info(device, OI_FUNCTION_FW_INFO_DEVICE_NAME, str, sizeof(str));
|
||||
if (ret)
|
||||
return ret;
|
||||
log_info(device->ratbag, "openinput: device: %s\n", str);
|
||||
|
||||
ret = openinput_read_supported_function_pages(device);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ratbag_device_init_profiles(device,
|
||||
drv_data->num_profiles,
|
||||
drv_data->num_resolutions,
|
||||
drv_data->num_buttons,
|
||||
drv_data->num_leds);
|
||||
|
||||
ratbag_device_for_each_profile(device, profile)
|
||||
openinput_read_profile(profile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
openinput_remove(struct ratbag_device *device)
|
||||
{
|
||||
ratbag_close_hidraw(device);
|
||||
free(ratbag_get_drv_data(device));
|
||||
}
|
||||
|
||||
struct ratbag_driver openinput_driver = {
|
||||
.name = "openinput",
|
||||
.id = "openinput",
|
||||
.probe = openinput_probe,
|
||||
.remove = openinput_remove,
|
||||
};
|
||||
1131
src/driver-roccat-kone-emp.c
Normal file
1131
src/driver-roccat-kone-emp.c
Normal file
File diff suppressed because it is too large
Load Diff
884
src/driver-roccat-kone-pure.c
Normal file
884
src/driver-roccat-kone-pure.c
Normal file
@@ -0,0 +1,884 @@
|
||||
/*
|
||||
* Copyright © 2015 Red Hat, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <linux/input.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libratbag-private.h"
|
||||
#include "libratbag-hidraw.h"
|
||||
|
||||
#define ROCCAT_PROFILE_MAX 4
|
||||
#define ROCCAT_BUTTON_MAX 17
|
||||
#define ROCCAT_NUM_DPI 5
|
||||
#define ROCCAT_LED_MAX 0
|
||||
|
||||
#define ROCCAT_MAX_RETRY_READY 10
|
||||
|
||||
#define ROCCAT_REPORT_ID_CONFIGURE_PROFILE 4
|
||||
#define ROCCAT_REPORT_ID_PROFILE 5
|
||||
#define ROCCAT_REPORT_ID_SETTINGS 6
|
||||
#define ROCCAT_REPORT_ID_KEY_MAPPING 7
|
||||
#define ROCCAT_REPORT_ID_MACRO 8
|
||||
|
||||
#define ROCCAT_REPORT_SIZE_PROFILE 59
|
||||
#define ROCCAT_REPORT_SIZE_SETTINGS 31
|
||||
#define ROCCAT_REPORT_SIZE_MACRO 2082
|
||||
|
||||
#define ROCCAT_CONFIG_SETTINGS 0x80
|
||||
#define ROCCAT_CONFIG_KEY_MAPPING 0x90
|
||||
|
||||
#define ROCCAT_MAX_MACRO_LENGTH 500
|
||||
|
||||
struct roccat_settings_report {
|
||||
uint8_t reportID;
|
||||
uint8_t twoB;
|
||||
uint8_t profileID;
|
||||
uint8_t x_y_linked;
|
||||
uint8_t x_sensitivity; /* 0x06 means 0 */
|
||||
uint8_t y_sensitivity; /* 0x06 means 0 */
|
||||
uint8_t dpi_mask;
|
||||
uint8_t xres[5];
|
||||
uint8_t current_dpi;
|
||||
uint8_t yres[5];
|
||||
uint8_t padding1;
|
||||
uint8_t report_rate;
|
||||
uint8_t padding2[11];
|
||||
// uint16_t checksum;
|
||||
// uint8_t padding2[4];
|
||||
// uint8_t light;
|
||||
// uint8_t light_heartbit;
|
||||
// uint8_t padding3[5];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct roccat_macro {
|
||||
uint8_t reportID;
|
||||
uint8_t twentytwo;
|
||||
uint8_t height;
|
||||
uint8_t profile;
|
||||
uint8_t button_index;
|
||||
uint8_t active;
|
||||
uint8_t padding[24];
|
||||
char group[24];
|
||||
char name[24];
|
||||
uint16_t length;
|
||||
struct {
|
||||
uint8_t keycode;
|
||||
uint8_t flag;
|
||||
uint16_t time;
|
||||
} keys[ROCCAT_MAX_MACRO_LENGTH];
|
||||
uint16_t checksum;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct roccat_data {
|
||||
uint8_t profiles[(ROCCAT_PROFILE_MAX + 1)][ROCCAT_REPORT_SIZE_PROFILE];
|
||||
struct roccat_settings_report settings[(ROCCAT_PROFILE_MAX + 1)];
|
||||
struct roccat_macro macros[(ROCCAT_PROFILE_MAX + 1)][(ROCCAT_BUTTON_MAX + 1)];
|
||||
};
|
||||
|
||||
struct roccat_button_type_mapping {
|
||||
uint8_t raw;
|
||||
enum ratbag_button_type type;
|
||||
};
|
||||
|
||||
static const struct roccat_button_type_mapping roccat_button_type_mapping[] = {
|
||||
{ 0, RATBAG_BUTTON_TYPE_LEFT },
|
||||
{ 1, RATBAG_BUTTON_TYPE_RIGHT },
|
||||
{ 2, RATBAG_BUTTON_TYPE_MIDDLE },
|
||||
{ 3, RATBAG_BUTTON_TYPE_EXTRA },
|
||||
{ 4, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 5, RATBAG_BUTTON_TYPE_WHEEL_UP },
|
||||
{ 6, RATBAG_BUTTON_TYPE_WHEEL_DOWN },
|
||||
{ 7, RATBAG_BUTTON_TYPE_RESOLUTION_UP },
|
||||
{ 8, RATBAG_BUTTON_TYPE_RESOLUTION_DOWN },
|
||||
{ 9, RATBAG_BUTTON_TYPE_LEFT },
|
||||
{ 10, RATBAG_BUTTON_TYPE_RIGHT },
|
||||
{ 11, RATBAG_BUTTON_TYPE_MIDDLE },
|
||||
{ 12, RATBAG_BUTTON_TYPE_EXTRA },
|
||||
{ 13, RATBAG_BUTTON_TYPE_SIDE },
|
||||
{ 14, RATBAG_BUTTON_TYPE_WHEEL_UP },
|
||||
{ 15, RATBAG_BUTTON_TYPE_WHEEL_DOWN },
|
||||
{ 16, RATBAG_BUTTON_TYPE_RESOLUTION_UP },
|
||||
{ 17, RATBAG_BUTTON_TYPE_RESOLUTION_DOWN },
|
||||
};
|
||||
|
||||
static enum ratbag_button_type
|
||||
roccat_raw_to_button_type(uint8_t data)
|
||||
{
|
||||
const struct roccat_button_type_mapping *mapping;
|
||||
|
||||
ARRAY_FOR_EACH(roccat_button_type_mapping, mapping) {
|
||||
if (mapping->raw == data)
|
||||
return mapping->type;
|
||||
}
|
||||
|
||||
return RATBAG_BUTTON_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
struct roccat_button_mapping {
|
||||
uint8_t raw;
|
||||
struct ratbag_button_action action;
|
||||
};
|
||||
|
||||
static struct roccat_button_mapping roccat_button_mapping[] = {
|
||||
/* FIXME: { 0, Disabled }, */
|
||||
{ 1, BUTTON_ACTION_BUTTON(1) },
|
||||
{ 2, BUTTON_ACTION_BUTTON(2) },
|
||||
{ 3, BUTTON_ACTION_BUTTON(3) },
|
||||
{ 4, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK) },
|
||||
/* FIXME: { 5, Shortcut (modifier + key) }, */
|
||||
{ 6, BUTTON_ACTION_NONE },
|
||||
{ 7, BUTTON_ACTION_BUTTON(4) },
|
||||
{ 8, BUTTON_ACTION_BUTTON(5) },
|
||||
//{ 9, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT) },
|
||||
//{ 10, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT) },
|
||||
{ 13, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP) },
|
||||
{ 14, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN) },
|
||||
/* FIXME: { 15, quicklaunch }, -> hidraw report 03 00 60 07 01 00 00 00 */
|
||||
{ 16, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP) },
|
||||
{ 17, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP) },
|
||||
{ 18, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN) },
|
||||
{ 20, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP) },
|
||||
{ 21, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP) },
|
||||
{ 22, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN) },
|
||||
{ 26, BUTTON_ACTION_KEY(KEY_LEFTMETA) },
|
||||
/* FIXME: { 27, open driver }, -> hidraw report 02 83 01 00 00 00 00 00 */
|
||||
//{ 32, BUTTON_ACTION_KEY(KEY_CONFIG) },
|
||||
{ 33, BUTTON_ACTION_KEY(KEY_PREVIOUSSONG) },
|
||||
{ 34, BUTTON_ACTION_KEY(KEY_NEXTSONG) },
|
||||
{ 35, BUTTON_ACTION_KEY(KEY_PLAYPAUSE) },
|
||||
{ 36, BUTTON_ACTION_KEY(KEY_STOPCD) },
|
||||
{ 37, BUTTON_ACTION_KEY(KEY_MUTE) },
|
||||
{ 38, BUTTON_ACTION_KEY(KEY_VOLUMEUP) },
|
||||
{ 39, BUTTON_ACTION_KEY(KEY_VOLUMEDOWN) },
|
||||
{ 48, BUTTON_ACTION_MACRO },
|
||||
{ 65, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE) },
|
||||
/* FIXME: { 66, Easywheel sensitivity }, */
|
||||
/* FIXME: { 67, Easywheel profile }, */
|
||||
/* FIXME: { 68, Easywheel CPI }, */
|
||||
/* FIXME: { 81, Other Easyshift }, -> hidraw report 03 00 ff 05 01 00 00 00 */
|
||||
/* FIXME: { 82, Other Easyshift Lock }, -> hidraw report 03 00 ff 05 01 00 00 00 */
|
||||
/* FIXME: { 83, Both Easyshift }, -> hidraw report 03 00 ff 04 01 00 00 00 */
|
||||
};
|
||||
|
||||
static const struct ratbag_button_action*
|
||||
roccat_raw_to_button_action(uint8_t data)
|
||||
{
|
||||
struct roccat_button_mapping *mapping;
|
||||
|
||||
ARRAY_FOR_EACH(roccat_button_mapping, mapping) {
|
||||
if (mapping->raw == data)
|
||||
return &mapping->action;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
roccat_button_action_to_raw(const struct ratbag_button_action *action)
|
||||
{
|
||||
struct roccat_button_mapping *mapping;
|
||||
|
||||
ARRAY_FOR_EACH(roccat_button_mapping, mapping) {
|
||||
if (ratbag_button_action_match(&mapping->action, action))
|
||||
return mapping->raw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
roccat_get_unaligned_u16(uint8_t *buf)
|
||||
{
|
||||
return (buf[1] << 8) | buf[0];
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
roccat_compute_crc(uint8_t *buf, unsigned int len)
|
||||
{
|
||||
unsigned i;
|
||||
uint16_t crc = 0;
|
||||
|
||||
if (len < 3)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < len - 2; i++) {
|
||||
crc += buf[i];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
static inline int
|
||||
roccat_crc_is_valid(struct ratbag_device *device, uint8_t *buf, unsigned int len)
|
||||
{
|
||||
uint16_t crc;
|
||||
uint16_t given_crc;
|
||||
|
||||
if (len < 3)
|
||||
return 0;
|
||||
|
||||
crc = roccat_compute_crc(buf, len);
|
||||
|
||||
given_crc = roccat_get_unaligned_u16(&buf[len - 2]);
|
||||
|
||||
log_raw(device->ratbag,
|
||||
"checksum computed: 0x%04x, checksum given: 0x%04x\n",
|
||||
crc,
|
||||
given_crc);
|
||||
|
||||
return crc == given_crc;
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_is_ready(struct ratbag_device *device)
|
||||
{
|
||||
uint8_t buf[3] = { 0 };
|
||||
int rc;
|
||||
|
||||
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_CONFIGURE_PROFILE,
|
||||
buf, sizeof(buf));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (rc != sizeof(buf))
|
||||
return -EIO;
|
||||
|
||||
if (buf[1] == 0x03)
|
||||
msleep(100);
|
||||
|
||||
if (buf[1] == 0x02)
|
||||
return 2;
|
||||
|
||||
return buf[1] == 0x01;
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_wait_ready(struct ratbag_device *device)
|
||||
{
|
||||
unsigned count = 0;
|
||||
int rc;
|
||||
|
||||
msleep(10);
|
||||
while (count < ROCCAT_MAX_RETRY_READY) {
|
||||
rc = roccat_is_ready(device);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (rc == 1)
|
||||
return 0;
|
||||
|
||||
if (rc == 2)
|
||||
return 2;
|
||||
|
||||
msleep(10);
|
||||
count++;
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_current_profile(struct ratbag_device *device)
|
||||
{
|
||||
uint8_t buf[3];
|
||||
int ret;
|
||||
|
||||
ret = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_PROFILE, buf,
|
||||
sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != 3)
|
||||
return -EIO;
|
||||
|
||||
return buf[2];
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_set_current_profile(struct ratbag_device *device, unsigned int index)
|
||||
{
|
||||
uint8_t buf[] = {ROCCAT_REPORT_ID_PROFILE, 0x03, index};
|
||||
int ret;
|
||||
|
||||
if (index > ROCCAT_PROFILE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
|
||||
sizeof(buf));
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != sizeof(buf))
|
||||
return -EIO;
|
||||
|
||||
ret = roccat_wait_ready(device);
|
||||
if (ret)
|
||||
log_error(device->ratbag,
|
||||
"Error while waiting for the device to be ready: %s (%d)\n",
|
||||
strerror(-ret), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_set_config_profile(struct ratbag_device *device, uint8_t profile, uint8_t type)
|
||||
{
|
||||
uint8_t buf[] = {ROCCAT_REPORT_ID_CONFIGURE_PROFILE, profile, type};
|
||||
int ret;
|
||||
|
||||
if (profile > ROCCAT_PROFILE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
|
||||
sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != sizeof(buf))
|
||||
return -EIO;
|
||||
|
||||
ret = roccat_wait_ready(device);
|
||||
if (ret < 0)
|
||||
log_error(device->ratbag,
|
||||
"Error while waiting for the device to be ready: %s (%d)\n",
|
||||
strerror(-ret), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct ratbag_button_action *
|
||||
roccat_button_to_action(struct ratbag_profile *profile,
|
||||
unsigned int button_index)
|
||||
{
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct roccat_data *drv_data = ratbag_get_drv_data(device);
|
||||
uint8_t data;
|
||||
|
||||
data = drv_data->profiles[profile->index][3 + button_index * 3];
|
||||
return roccat_raw_to_button_action(data);
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_write_profile(struct ratbag_profile *profile)
|
||||
{
|
||||
struct ratbag_device *device = profile->device;
|
||||
unsigned int index = profile->index;
|
||||
struct roccat_data *drv_data;
|
||||
int rc;
|
||||
uint8_t *buf;
|
||||
uint16_t *crc;
|
||||
|
||||
assert(index <= ROCCAT_PROFILE_MAX);
|
||||
|
||||
drv_data = ratbag_get_drv_data(device);
|
||||
buf = drv_data->profiles[index];
|
||||
crc = (uint16_t *)&buf[ROCCAT_REPORT_SIZE_PROFILE - 2];
|
||||
*crc = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_PROFILE);
|
||||
|
||||
roccat_set_config_profile(device, index, ROCCAT_CONFIG_KEY_MAPPING);
|
||||
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;
|
||||
|
||||
log_raw(device->ratbag, "profile: %d written %s:%d\n",
|
||||
buf[2],
|
||||
__FILE__, __LINE__);
|
||||
|
||||
rc = roccat_wait_ready(device);
|
||||
if (rc)
|
||||
log_error(device->ratbag,
|
||||
"Error while waiting for the device to be ready: %s (%d)\n",
|
||||
strerror(-rc), rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
roccat_read_button(struct ratbag_button *button)
|
||||
{
|
||||
const struct ratbag_button_action *action;
|
||||
struct ratbag_device *device;
|
||||
struct roccat_macro *macro;
|
||||
struct roccat_data *drv_data;
|
||||
uint8_t *buf;
|
||||
unsigned j, time;
|
||||
int rc;
|
||||
|
||||
device = button->profile->device;
|
||||
drv_data = ratbag_get_drv_data(device);
|
||||
action = roccat_button_to_action(button->profile, button->index);
|
||||
if (action)
|
||||
ratbag_button_set_action(button, action);
|
||||
button->type = roccat_raw_to_button_type(button->index);
|
||||
// if (action == NULL)
|
||||
// log_error(device->ratbag, "button: %d -> %d %s:%d\n",
|
||||
// button->index, drv_data->profiles[button->profile->index][3 + button->index * 3],
|
||||
// __FILE__, __LINE__);
|
||||
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
|
||||
|
||||
if (action && action->type == RATBAG_BUTTON_ACTION_TYPE_MACRO) {
|
||||
struct ratbag_button_macro *m = NULL;
|
||||
|
||||
roccat_set_config_profile(device,
|
||||
button->profile->index,
|
||||
0);
|
||||
roccat_set_config_profile(device,
|
||||
button->profile->index,
|
||||
button->index);
|
||||
macro = &drv_data->macros[button->profile->index][button->index];
|
||||
buf = (uint8_t*)macro;
|
||||
buf[0] = ROCCAT_REPORT_ID_MACRO;
|
||||
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",
|
||||
button->index, button->profile->index,
|
||||
rc < 0 ? strerror(-rc) : "not read enough", rc);
|
||||
} else {
|
||||
if (buf[0] != ROCCAT_REPORT_ID_MACRO) {
|
||||
log_error(device->ratbag,
|
||||
"Error while reading the macro of button %d of profile %d.\n",
|
||||
button->index,
|
||||
button->profile->index);
|
||||
goto out_macro;
|
||||
}
|
||||
if (!roccat_crc_is_valid(device, buf, ROCCAT_REPORT_SIZE_MACRO)) {
|
||||
log_error(device->ratbag,
|
||||
"wrong checksum while reading the macro of button %d of profile %d.\n",
|
||||
button->index,
|
||||
button->profile->index);
|
||||
goto out_macro;
|
||||
}
|
||||
|
||||
m = ratbag_button_macro_new(macro->name);
|
||||
log_raw(device->ratbag,
|
||||
"macro on button %d of profile %d is named '%s', and contains %d events:\n",
|
||||
button->index, button->profile->index,
|
||||
macro->name, macro->length);
|
||||
for (j = 0; j < macro->length; j++) {
|
||||
unsigned int keycode = ratbag_hidraw_get_keycode_from_keyboard_usage(device,
|
||||
macro->keys[j].keycode);
|
||||
ratbag_button_macro_set_event(m,
|
||||
j * 2,
|
||||
macro->keys[j].flag & 0x01 ? RATBAG_MACRO_EVENT_KEY_PRESSED : RATBAG_MACRO_EVENT_KEY_RELEASED,
|
||||
keycode);
|
||||
if (macro->keys[j].time)
|
||||
time = macro->keys[j].time;
|
||||
else
|
||||
time = macro->keys[j].flag & 0x01 ? 10 : 50;
|
||||
ratbag_button_macro_set_event(m,
|
||||
j * 2 + 1,
|
||||
RATBAG_MACRO_EVENT_WAIT,
|
||||
time);
|
||||
|
||||
log_raw(device->ratbag,
|
||||
" - %s %s\n",
|
||||
libevdev_event_code_get_name(EV_KEY, keycode),
|
||||
macro->keys[j].flag & 0x80 ? "released" : "pressed");
|
||||
}
|
||||
ratbag_button_copy_macro(button, m);
|
||||
}
|
||||
out_macro:
|
||||
msleep(10);
|
||||
ratbag_button_macro_unref(m);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_write_macro(struct ratbag_button *button,
|
||||
const struct ratbag_button_action *action)
|
||||
{
|
||||
struct ratbag_device *device;
|
||||
struct roccat_macro *macro;
|
||||
struct roccat_data *drv_data;
|
||||
uint8_t *buf;
|
||||
unsigned i, count = 0;
|
||||
int rc;
|
||||
|
||||
if (action->type != RATBAG_BUTTON_ACTION_TYPE_MACRO)
|
||||
return 0;
|
||||
|
||||
device = button->profile->device;
|
||||
drv_data = ratbag_get_drv_data(device);
|
||||
macro = &drv_data->macros[button->profile->index][button->index];
|
||||
buf = (uint8_t*)macro;
|
||||
|
||||
memset(buf, 0, ROCCAT_REPORT_SIZE_MACRO);
|
||||
|
||||
for (i = 0; i < MAX_MACRO_EVENTS && count < ROCCAT_MAX_MACRO_LENGTH; i++) {
|
||||
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_INVALID)
|
||||
return -EINVAL; /* should not happen, ever */
|
||||
|
||||
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_NONE)
|
||||
break;
|
||||
|
||||
/* ignore the first wait */
|
||||
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_WAIT &&
|
||||
!count)
|
||||
continue;
|
||||
|
||||
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_KEY_PRESSED ||
|
||||
action->macro->events[i].type == RATBAG_MACRO_EVENT_KEY_RELEASED) {
|
||||
macro->keys[count].keycode = ratbag_hidraw_get_keyboard_usage_from_keycode(device, action->macro->events[i].event.key);
|
||||
}
|
||||
|
||||
switch (action->macro->events[i].type) {
|
||||
case RATBAG_MACRO_EVENT_KEY_PRESSED:
|
||||
macro->keys[count].flag = 0x01;
|
||||
break;
|
||||
case RATBAG_MACRO_EVENT_KEY_RELEASED:
|
||||
macro->keys[count].flag = 0x02;
|
||||
break;
|
||||
case RATBAG_MACRO_EVENT_WAIT:
|
||||
macro->keys[--count].time = action->macro->events[i].event.timeout;
|
||||
break;
|
||||
case RATBAG_MACRO_EVENT_INVALID:
|
||||
case RATBAG_MACRO_EVENT_NONE:
|
||||
/* should not happen */
|
||||
log_error(device->ratbag,
|
||||
"something went wrong while writing a macro.\n");
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
macro->reportID = ROCCAT_REPORT_ID_MACRO;
|
||||
macro->twentytwo = 0x22;
|
||||
macro->height = 0x08;
|
||||
macro->profile = button->profile->index;
|
||||
macro->button_index = button->index;
|
||||
macro->active = 0x01;
|
||||
strcpy(macro->group, "g0");
|
||||
strncpy(macro->name, action->macro->name, 23);
|
||||
macro->length = count;
|
||||
macro->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_MACRO);
|
||||
|
||||
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_MACRO,
|
||||
buf, ROCCAT_REPORT_SIZE_MACRO);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (rc != ROCCAT_REPORT_SIZE_MACRO)
|
||||
return -EIO;
|
||||
|
||||
rc = roccat_wait_ready(device);
|
||||
if (rc)
|
||||
log_error(device->ratbag,
|
||||
"Error while waiting for the device to be ready: %s (%d)\n",
|
||||
strerror(-rc), rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_write_button(struct ratbag_button *button)
|
||||
{
|
||||
const struct ratbag_button_action *action = &button->action;
|
||||
struct ratbag_profile *profile = button->profile;
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct roccat_data *drv_data = ratbag_get_drv_data(device);
|
||||
uint8_t rc, *data;
|
||||
|
||||
data = &drv_data->profiles[profile->index][3 + button->index * 3];
|
||||
|
||||
rc = roccat_button_action_to_raw(action);
|
||||
if (!rc)
|
||||
return -EINVAL;
|
||||
|
||||
*data = rc;
|
||||
|
||||
rc = roccat_write_profile(button->profile);
|
||||
if (rc) {
|
||||
log_error(device->ratbag,
|
||||
"unable to write the profile to the device: '%s' (%d)\n",
|
||||
strerror(-rc), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = roccat_write_macro(button, action);
|
||||
if (rc) {
|
||||
log_error(device->ratbag,
|
||||
"unable to write the macro to the device: '%s' (%d)\n",
|
||||
strerror(-rc), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_write_resolution(struct ratbag_resolution *resolution)
|
||||
{
|
||||
struct ratbag_profile *profile = resolution->profile;
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct roccat_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct roccat_settings_report *settings_report;
|
||||
uint8_t *buf;
|
||||
int rc;
|
||||
|
||||
const unsigned int dpi_x = resolution->dpi_x;
|
||||
const unsigned int dpi_y = resolution->dpi_y;
|
||||
|
||||
if (dpi_x < 200 || dpi_x > 8200 || dpi_x % 50)
|
||||
return -EINVAL;
|
||||
if (dpi_y < 100 || dpi_y > 5000 || dpi_y % 50)
|
||||
return -EINVAL;
|
||||
|
||||
settings_report = &drv_data->settings[profile->index];
|
||||
|
||||
settings_report->xres[resolution->index] = dpi_x / 50;
|
||||
settings_report->yres[resolution->index] = dpi_y / 50;
|
||||
if (resolution->is_active)
|
||||
settings_report->current_dpi = resolution->index;
|
||||
|
||||
buf = (uint8_t*)settings_report;
|
||||
|
||||
// No checksum for settings report on Kone Pure.
|
||||
//settings_report->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_SETTINGS);
|
||||
|
||||
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
|
||||
buf, ROCCAT_REPORT_SIZE_SETTINGS);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (rc != ROCCAT_REPORT_SIZE_SETTINGS)
|
||||
return -EIO;
|
||||
|
||||
rc = roccat_wait_ready(device);
|
||||
if (rc)
|
||||
log_error(device->ratbag,
|
||||
"Error while waiting for the device to be ready: %s (%d)\n",
|
||||
strerror(-rc), rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
roccat_read_profile(struct ratbag_profile *profile)
|
||||
{
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct roccat_data *drv_data;
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_button *button;
|
||||
struct roccat_settings_report *setting_report;
|
||||
uint8_t *buf;
|
||||
unsigned int report_rate;
|
||||
int dpi_x, dpi_y;
|
||||
int rc;
|
||||
unsigned int report_rates[] = { 125, 250, 500, 1000 };
|
||||
|
||||
assert(profile->index <= ROCCAT_PROFILE_MAX);
|
||||
|
||||
drv_data = ratbag_get_drv_data(device);
|
||||
|
||||
setting_report = &drv_data->settings[profile->index];
|
||||
buf = (uint8_t*)setting_report;
|
||||
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_SETTINGS);
|
||||
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
|
||||
buf, ROCCAT_REPORT_SIZE_SETTINGS);
|
||||
|
||||
if (rc < ROCCAT_REPORT_SIZE_SETTINGS)
|
||||
return;
|
||||
|
||||
/* first retrieve the report rate, it is set per profile */
|
||||
if (setting_report->report_rate < ARRAY_LENGTH(report_rates)) {
|
||||
report_rate = report_rates[setting_report->report_rate];
|
||||
} else {
|
||||
log_error(device->ratbag,
|
||||
"error while reading the report rate of the mouse (0x%02x)\n",
|
||||
buf[26]);
|
||||
report_rate = 0;
|
||||
}
|
||||
|
||||
ratbag_profile_set_report_rate_list(profile, report_rates,
|
||||
ARRAY_LENGTH(report_rates));
|
||||
ratbag_profile_set_report_rate(profile, report_rate);
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
dpi_x = setting_report->xres[resolution->index] * 50;
|
||||
dpi_y = setting_report->yres[resolution->index] * 50;
|
||||
if (!(setting_report->dpi_mask & (1 << resolution->index))) {
|
||||
/* the profile is disabled, overwrite it */
|
||||
dpi_x = 0;
|
||||
dpi_y = 0;
|
||||
}
|
||||
|
||||
ratbag_resolution_set_resolution(resolution, dpi_x, dpi_y);
|
||||
ratbag_resolution_set_cap(resolution,
|
||||
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
|
||||
resolution->is_active = (resolution->index == setting_report->current_dpi);
|
||||
|
||||
ratbag_resolution_set_dpi_list_from_range(resolution, 100, 5000);
|
||||
}
|
||||
|
||||
buf = drv_data->profiles[profile->index];
|
||||
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_KEY_MAPPING);
|
||||
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_KEY_MAPPING,
|
||||
buf, ROCCAT_REPORT_SIZE_PROFILE);
|
||||
|
||||
msleep(10);
|
||||
|
||||
if (rc < ROCCAT_REPORT_SIZE_PROFILE)
|
||||
return;
|
||||
|
||||
// Buttons were read from the buffer that was yet un-initialized with the device data.
|
||||
ratbag_profile_for_each_button(profile, button)
|
||||
roccat_read_button(button);
|
||||
|
||||
if (!roccat_crc_is_valid(device, buf, ROCCAT_REPORT_SIZE_PROFILE))
|
||||
log_error(device->ratbag,
|
||||
"Error while reading profile %d, continuing...\n",
|
||||
profile->index);
|
||||
|
||||
log_raw(device->ratbag, "profile: %d %s:%d\n",
|
||||
buf[2],
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_probe(struct ratbag_device *device)
|
||||
{
|
||||
int rc;
|
||||
struct ratbag_profile *profile;
|
||||
struct roccat_data *drv_data;
|
||||
int active_idx;
|
||||
|
||||
rc = ratbag_open_hidraw(device);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!ratbag_hidraw_has_report(device, ROCCAT_REPORT_ID_KEY_MAPPING)) {
|
||||
ratbag_close_hidraw(device);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
drv_data = zalloc(sizeof(*drv_data));
|
||||
ratbag_set_drv_data(device, drv_data);
|
||||
|
||||
/* profiles are 0-indexed */
|
||||
ratbag_device_init_profiles(device,
|
||||
ROCCAT_PROFILE_MAX + 1,
|
||||
ROCCAT_NUM_DPI,
|
||||
ROCCAT_BUTTON_MAX + 1,
|
||||
ROCCAT_LED_MAX);
|
||||
|
||||
ratbag_device_for_each_profile(device, profile)
|
||||
roccat_read_profile(profile);
|
||||
|
||||
active_idx = roccat_current_profile(device);
|
||||
if (active_idx < 0) {
|
||||
log_error(device->ratbag,
|
||||
"Can't talk to the mouse: '%s' (%d)\n",
|
||||
strerror(-active_idx),
|
||||
active_idx);
|
||||
rc = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
list_for_each(profile, &device->profiles, link) {
|
||||
if (profile->index == (unsigned int)active_idx) {
|
||||
profile->is_active = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log_raw(device->ratbag,
|
||||
"'%s' is in profile %d\n",
|
||||
ratbag_device_get_name(device),
|
||||
profile->index);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
free(drv_data);
|
||||
ratbag_set_drv_data(device, NULL);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
roccat_remove(struct ratbag_device *device)
|
||||
{
|
||||
ratbag_close_hidraw(device);
|
||||
free(ratbag_get_drv_data(device));
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_commit(struct ratbag_device *device)
|
||||
{
|
||||
|
||||
int rc = 0;
|
||||
struct ratbag_button *button = NULL;
|
||||
struct ratbag_profile *profile = NULL;
|
||||
struct ratbag_resolution *resolution = NULL;
|
||||
|
||||
ratbag_device_for_each_profile(device, profile) {
|
||||
if (!profile->dirty)
|
||||
continue;
|
||||
|
||||
rc = roccat_write_profile(profile);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (!resolution->dirty)
|
||||
continue;
|
||||
|
||||
rc = roccat_write_resolution(resolution);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
ratbag_profile_for_each_button(profile, button) {
|
||||
if (!button->dirty)
|
||||
continue;
|
||||
|
||||
rc = roccat_write_button(button);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ratbag_driver roccat_kone_pure_driver = {
|
||||
.name = "Roccat Kone Pure",
|
||||
.id = "roccat-kone-pure",
|
||||
.probe = roccat_probe,
|
||||
.remove = roccat_remove,
|
||||
.commit = roccat_commit,
|
||||
.set_active_profile = roccat_set_current_profile,
|
||||
};
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
struct roccat_settings_report {
|
||||
uint8_t reportID;
|
||||
uint8_t twoB;
|
||||
uint8_t report_length;
|
||||
uint8_t profileID;
|
||||
uint8_t x_y_linked;
|
||||
uint8_t x_sensitivity; /* 0x06 means 0 */
|
||||
@@ -78,8 +78,7 @@ struct roccat_settings_report {
|
||||
|
||||
struct roccat_macro {
|
||||
uint8_t reportID;
|
||||
uint8_t twentytwo;
|
||||
uint8_t height;
|
||||
uint16_t report_length;
|
||||
uint8_t profile;
|
||||
uint8_t button_index;
|
||||
uint8_t active;
|
||||
@@ -267,9 +266,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 +313,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 +333,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 +360,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 +407,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 +465,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",
|
||||
@@ -584,8 +580,7 @@ roccat_write_macro(struct ratbag_button *button,
|
||||
}
|
||||
|
||||
macro->reportID = ROCCAT_REPORT_ID_MACRO;
|
||||
macro->twentytwo = 0x22;
|
||||
macro->height = 0x08;
|
||||
macro->report_length = 0x0822;
|
||||
macro->profile = button->profile->index;
|
||||
macro->button_index = button->index;
|
||||
macro->active = 0x01;
|
||||
@@ -594,9 +589,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;
|
||||
|
||||
@@ -613,9 +607,9 @@ roccat_write_macro(struct ratbag_button *button,
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_write_button(struct ratbag_button *button,
|
||||
const struct ratbag_button_action *action)
|
||||
roccat_write_button(struct ratbag_button *button)
|
||||
{
|
||||
const struct ratbag_button_action *action = &button->action;
|
||||
struct ratbag_profile *profile = button->profile;
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct roccat_data *drv_data = ratbag_get_drv_data(device);
|
||||
@@ -649,8 +643,7 @@ roccat_write_button(struct ratbag_button *button,
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
|
||||
int dpi_x, int dpi_y)
|
||||
roccat_write_resolution(struct ratbag_resolution *resolution)
|
||||
{
|
||||
struct ratbag_profile *profile = resolution->profile;
|
||||
struct ratbag_device *device = profile->device;
|
||||
@@ -659,6 +652,9 @@ roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
|
||||
uint8_t *buf;
|
||||
int rc;
|
||||
|
||||
const unsigned int dpi_x = resolution->dpi_x;
|
||||
const unsigned int dpi_y = resolution->dpi_y;
|
||||
|
||||
if (dpi_x < 200 || dpi_x > 8200 || dpi_x % 50)
|
||||
return -EINVAL;
|
||||
if (dpi_y < 200 || dpi_y > 8200 || dpi_y % 50)
|
||||
@@ -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);
|
||||
|
||||
@@ -842,13 +835,50 @@ roccat_remove(struct ratbag_device *device)
|
||||
free(ratbag_get_drv_data(device));
|
||||
}
|
||||
|
||||
static int
|
||||
roccat_commit(struct ratbag_device *device)
|
||||
{
|
||||
|
||||
int rc = 0;
|
||||
struct ratbag_button *button = NULL;
|
||||
struct ratbag_profile *profile = NULL;
|
||||
struct ratbag_resolution *resolution = NULL;
|
||||
|
||||
ratbag_device_for_each_profile(device, profile) {
|
||||
if (!profile->dirty)
|
||||
continue;
|
||||
|
||||
rc = roccat_write_profile(profile);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (!resolution->dirty)
|
||||
continue;
|
||||
|
||||
rc = roccat_write_resolution(resolution);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
ratbag_profile_for_each_button(profile, button) {
|
||||
if (!button->dirty)
|
||||
continue;
|
||||
|
||||
rc = roccat_write_button(button);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ratbag_driver roccat_driver = {
|
||||
.name = "Roccat Kone XTD",
|
||||
.id = "roccat",
|
||||
.probe = roccat_probe,
|
||||
.remove = roccat_remove,
|
||||
.write_profile = roccat_write_profile,
|
||||
.commit = roccat_commit,
|
||||
.set_active_profile = roccat_set_current_profile,
|
||||
.write_button = roccat_write_button,
|
||||
.write_resolution_dpi = roccat_write_resolution_dpi,
|
||||
};
|
||||
|
||||
320
src/driver-sinowealth-nubwo.c
Normal file
320
src/driver-sinowealth-nubwo.c
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright © 2020 Pipat Saengow
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "libratbag-enums.h"
|
||||
#include "libratbag-hidraw.h"
|
||||
#include "libratbag-private.h"
|
||||
#include "libratbag-util.h"
|
||||
#include "shared-macro.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SINOWEALTHNUBWO_PERF_CMD_REPORTID 0x02
|
||||
#define SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID 0x03
|
||||
#define SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID 0x04
|
||||
|
||||
#define SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE 256
|
||||
#define SINOWEALTHNUBWO_GET_FIRMWARE_MSGOFFSET 48
|
||||
|
||||
#define SINOWEALTHNUBWO_PERF_CMD_MSGSIZE 16
|
||||
|
||||
// Actually more but I only implemented one
|
||||
#define SINOWEALTHNUBWO_NUM_PROFILES 1
|
||||
#define SINOWEALTHNUBWO_NUM_RESOLUTIONS 1
|
||||
// Actually 8 but I am not going to implement macros.
|
||||
#define SINOWEALTHNUBWO_NUM_BUTTONS 0
|
||||
#define SINOWEALTHNUBWO_NUM_LEDS 1
|
||||
|
||||
//Magic set_feature that must be called before requesting firmware string
|
||||
static uint8_t PREFIRMWARE_QUERY_MSG[] = {0x02, 0x01, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
static unsigned int REPORT_RATES[] = { 125, 250, 333, 500, 1000 };
|
||||
static uint8_t REPORT_RATES_ENCODED[] = {0x08, 0x04, 0x03, 0x02, 0x01};
|
||||
static uint8_t REPORT_RATES_CMD[] = {0x02, 0x06, 0xbb, 0xaa, 0x28, 0x00, 0x01, 0x00};
|
||||
|
||||
static unsigned int DPILIST[] = { 1000, 2000, 3000, 5000, 15000};
|
||||
static uint8_t DPI_ENCODED[] = {0x04, 0x03, 0x02, 0x01, 0x00};
|
||||
static uint8_t DPI_CMD[] = { 0x02, 0x06, 0xbb, 0xaa, 0x32, 0x00, 0x01, 0x00 };
|
||||
|
||||
enum
|
||||
sinowealthnubwo_color_mode {
|
||||
SINOWEALTHNUBWO_COLOR_OFF = 0x00,
|
||||
SINOWEALTHNUBWO_COLOR_ON = 0x01,
|
||||
SINOWEALTHNUBWO_COLOR_BREATHING = 0x02,
|
||||
SINOWEALTHNUBWO_COLOR_COLOR_SHIFT = 0x03,
|
||||
SINOWEALTHNUBWO_COLOR_SPECTRUM = 0x04,
|
||||
SINOWEALTHNUBWO_COLOR_MARQUEE = 0x05,
|
||||
};
|
||||
|
||||
struct
|
||||
sinowealthnubwo_aesthetic_report {
|
||||
uint8_t report_id; //0x03
|
||||
uint8_t cmd[7];
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t color_mode;
|
||||
uint8_t padzero; // 0x00
|
||||
uint8_t brightness; // 0x01 to 0x03
|
||||
uint8_t tempo; // 0x05 0x03 0x01
|
||||
uint8_t padzero2[16*3+1];
|
||||
} __packed__;
|
||||
|
||||
static uint8_t AESTHETIC_CMD[] = {0x06, 0xbb, 0xaa, 0x2a, 0x00, 0x0a, 0x00};
|
||||
|
||||
static int
|
||||
sinowealthnubwo_test_hidraw(struct ratbag_device *device)
|
||||
{
|
||||
return
|
||||
ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID)
|
||||
&& ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID)
|
||||
&& ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID);
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealth_get_firmware_string(struct ratbag_device *device, char **output)
|
||||
{
|
||||
uint8_t buff[SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE + 1] = {0}; //Purposefully overalloacate to prevent buffer overrun
|
||||
int size;
|
||||
|
||||
size = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, PREFIRMWARE_QUERY_MSG, ARRAY_LENGTH(PREFIRMWARE_QUERY_MSG));
|
||||
if (size < 0) {
|
||||
log_error(device->ratbag, "Error while sending pre-firmware request message: %d\n", size);
|
||||
return size;
|
||||
}
|
||||
|
||||
size = ratbag_hidraw_get_feature_report(device, SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID, buff, SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE);
|
||||
if (size != SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE) {
|
||||
log_error(device->ratbag ,"Firmware report reply size mismatch expected %d got %d\n", SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
*output = strdup_ascii_only((char *) buff + SINOWEALTHNUBWO_GET_FIRMWARE_MSGOFFSET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_probe(struct ratbag_device *device)
|
||||
{
|
||||
int error;
|
||||
struct ratbag_profile *profile;
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_led *led;
|
||||
|
||||
error = ratbag_find_hidraw(device, sinowealthnubwo_test_hidraw);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
char *fwstr;
|
||||
error = sinowealth_get_firmware_string(device, &fwstr);
|
||||
if (error)
|
||||
return error;
|
||||
log_info(device->ratbag, "Firmware: %s\n", fwstr);
|
||||
free(fwstr);
|
||||
|
||||
ratbag_device_init_profiles(device,
|
||||
SINOWEALTHNUBWO_NUM_PROFILES,
|
||||
SINOWEALTHNUBWO_NUM_RESOLUTIONS,
|
||||
SINOWEALTHNUBWO_NUM_BUTTONS,
|
||||
SINOWEALTHNUBWO_NUM_LEDS);
|
||||
|
||||
ratbag_device_for_each_profile(device, profile) {
|
||||
profile->is_active = true;
|
||||
|
||||
ratbag_profile_set_cap(profile, RATBAG_PROFILE_CAP_WRITE_ONLY);
|
||||
ratbag_profile_set_report_rate_list(profile, REPORT_RATES, ARRAY_LENGTH(REPORT_RATES));
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
ratbag_resolution_set_dpi_list(resolution, DPILIST, ARRAY_LENGTH(DPILIST));
|
||||
resolution->dpi_x = resolution->dpi_y = DPILIST[ARRAY_LENGTH(DPILIST)-1];
|
||||
resolution->is_active = true;
|
||||
resolution->is_default = true;
|
||||
}
|
||||
ratbag_profile_for_each_led(profile, led) {
|
||||
led->mode = RATBAG_LED_OFF;
|
||||
led->color.red = led->color.green = led->color.blue = 0xFF;
|
||||
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
|
||||
led->type = RATBAG_LED_TYPE_SIDE;
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
|
||||
//Actually more
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
encode_dpi(unsigned int dpi)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(DPILIST); i++)
|
||||
if (DPILIST[i] == dpi)
|
||||
return DPI_ENCODED[i];
|
||||
return DPI_ENCODED[0];
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
encode_report_rate(unsigned int reportrate)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(REPORT_RATES); i++)
|
||||
if (REPORT_RATES[i] == reportrate)
|
||||
return REPORT_RATES_ENCODED[i];
|
||||
return REPORT_RATES_ENCODED[0];
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_set_dpi(struct ratbag_device *device, int dpi)
|
||||
{
|
||||
uint8_t buf[SINOWEALTHNUBWO_PERF_CMD_MSGSIZE] = {0};
|
||||
memcpy(buf, DPI_CMD, ARRAY_LENGTH(DPI_CMD));
|
||||
buf[ARRAY_LENGTH(DPI_CMD)] = encode_dpi(dpi);
|
||||
int error = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, buf, SINOWEALTHNUBWO_PERF_CMD_MSGSIZE);
|
||||
if (error < 0)
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_set_report_rate(struct ratbag_device *device, int reportrate)
|
||||
{
|
||||
uint8_t buf[SINOWEALTHNUBWO_PERF_CMD_MSGSIZE] = {0};
|
||||
memcpy(buf, REPORT_RATES_CMD, ARRAY_LENGTH(REPORT_RATES_CMD));
|
||||
buf[ARRAY_LENGTH(REPORT_RATES_CMD)] = encode_report_rate(reportrate);
|
||||
int error = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, buf, SINOWEALTHNUBWO_PERF_CMD_MSGSIZE);
|
||||
if (error < 0)
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum sinowealthnubwo_color_mode encode_color(enum ratbag_led_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case RATBAG_LED_OFF:
|
||||
return SINOWEALTHNUBWO_COLOR_OFF;
|
||||
case RATBAG_LED_ON:
|
||||
return SINOWEALTHNUBWO_COLOR_ON;
|
||||
case RATBAG_LED_CYCLE:
|
||||
return SINOWEALTHNUBWO_COLOR_MARQUEE;
|
||||
case RATBAG_LED_BREATHING:
|
||||
return SINOWEALTHNUBWO_COLOR_BREATHING;
|
||||
default:
|
||||
return SINOWEALTHNUBWO_COLOR_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t normalize_duration(int duration)
|
||||
{
|
||||
const int MAX_DURATION = 10000;
|
||||
const uint8_t avail_dura[] = {0x01, 0x03, 0x05};
|
||||
const int selected = (duration * ((int) ARRAY_LENGTH(avail_dura)) - 1) / MAX_DURATION;
|
||||
return avail_dura[selected];
|
||||
}
|
||||
|
||||
static uint8_t normalize_brightness(int brightness)
|
||||
{
|
||||
const int MAX_BRIGHTNESS = 255;
|
||||
return 1 + (brightness * 3 - 1) / MAX_BRIGHTNESS;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_set_aesthetic(struct ratbag_device *device, struct ratbag_led *led) {
|
||||
struct sinowealthnubwo_aesthetic_report report;
|
||||
memset(&report, 0, sizeof(struct sinowealthnubwo_aesthetic_report));
|
||||
report.report_id = SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID;
|
||||
memcpy(report.cmd, AESTHETIC_CMD, ARRAY_LENGTH(AESTHETIC_CMD));
|
||||
report.r = led->color.red;
|
||||
report.g = led->color.green;
|
||||
report.b = led->color.blue;
|
||||
report.color_mode = encode_color(led->mode);
|
||||
report.tempo = normalize_duration(led->ms);
|
||||
report.brightness = normalize_brightness(led->brightness);
|
||||
|
||||
int size = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID, (uint8_t *) &report, sizeof(report));
|
||||
if (size < 0)
|
||||
return size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_write_profile(struct ratbag_device *device, struct ratbag_profile *profile)
|
||||
{
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_led *led;
|
||||
int error;
|
||||
|
||||
log_debug(device->ratbag, "Writing updates\n");
|
||||
|
||||
log_debug(device->ratbag, "Setting report rate\n");
|
||||
error = sinowealthnubwo_set_report_rate(device, profile->hz);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (!resolution->dirty)
|
||||
continue;
|
||||
log_debug(device->ratbag, "Setting DPI\n");
|
||||
error = sinowealthnubwo_set_dpi(device, resolution->dpi_x);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
ratbag_profile_for_each_led(profile, led) {
|
||||
if (!led->dirty)
|
||||
continue;
|
||||
log_debug(device->ratbag, "Setting aesthetic\n");
|
||||
error = sinowealthnubwo_set_aesthetic(device, led);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_commit(struct ratbag_device *device)
|
||||
{
|
||||
struct ratbag_profile *profile;
|
||||
list_for_each(profile, &device->profiles, link) {
|
||||
if (!profile->dirty) continue;
|
||||
|
||||
int error = sinowealthnubwo_write_profile(device, profile);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sinowealthnubwo_remove(struct ratbag_device *device)
|
||||
{
|
||||
ratbag_close_hidraw(device);
|
||||
}
|
||||
|
||||
struct ratbag_driver sinowealth_nubwo_driver = {
|
||||
.name = "Sinowealth Nubwo",
|
||||
.id = "sinowealth_nubwo",
|
||||
.probe = sinowealthnubwo_probe,
|
||||
.remove = sinowealthnubwo_remove,
|
||||
.commit = sinowealthnubwo_commit,
|
||||
};
|
||||
1796
src/driver-sinowealth.c
Normal file
1796
src/driver-sinowealth.c
Normal file
File diff suppressed because it is too large
Load Diff
41
src/driver-sinowealth.h
Normal file
41
src/driver-sinowealth.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2022 Yaroslav Chvanov
|
||||
*
|
||||
* 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-util.h"
|
||||
|
||||
enum sinowealth_led_format {
|
||||
SINOWEALTH_LED_TYPE_NONE,
|
||||
SINOWEALTH_LED_TYPE_RBG,
|
||||
SINOWEALTH_LED_TYPE_RGB,
|
||||
};
|
||||
|
||||
struct sinowealth_device_data {
|
||||
char *fw_version;
|
||||
char *device_name;
|
||||
enum sinowealth_led_format led_type;
|
||||
unsigned int button_count;
|
||||
|
||||
struct list link;
|
||||
};
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "driver-steelseries.h"
|
||||
#include "libratbag-private.h"
|
||||
#include "libratbag-hidraw.h"
|
||||
#include "libratbag-data.h"
|
||||
@@ -41,6 +42,7 @@
|
||||
#define STEELSERIES_INPUT_HIDRAW 1
|
||||
|
||||
/* not sure these two are used for */
|
||||
#define STEELSERIES_REPORT_ID 0x00 // steelseries doesn't use numbered reports
|
||||
#define STEELSERIES_REPORT_ID_1 0x01
|
||||
#define STEELSERIES_REPORT_ID_2 0x02
|
||||
|
||||
@@ -53,14 +55,16 @@
|
||||
#define STEELSERIES_ID_LED_INTENSITY_SHORT 0x05
|
||||
#define STEELSERIES_ID_LED_EFFECT_SHORT 0x07
|
||||
#define STEELSERIES_ID_LED_COLOR_SHORT 0x08
|
||||
#define STEELSERIES_ID_LED_COLOR_SHORT_RIVAL100 0x05
|
||||
#define STEELSERIES_ID_SAVE_SHORT 0x09
|
||||
#define STEELSERIES_ID_FIRMWARE_PROTOCOL1 0x10
|
||||
|
||||
#define STEELSERIES_ID_BUTTONS 0x31
|
||||
#define STEELSERIES_ID_DPI 0x53
|
||||
#define STEELSERIES_ID_REPORT_RATE 0x54
|
||||
#define STEELSERIES_ID_LED 0x5b
|
||||
#define STEELSERIES_ID_SAVE 0x59
|
||||
#define STEELSERIES_ID_FIRMWARE 0x90
|
||||
#define STEELSERIES_ID_FIRMWARE_PROTOCOL2 0x90
|
||||
#define STEELSERIES_ID_SETTTINGS 0x92
|
||||
|
||||
#define STEELSERIES_ID_DPI_PROTOCOL3 0x03
|
||||
@@ -112,11 +116,31 @@ struct steelseries_led_cycle_spec {
|
||||
int point_count_idx; /* index of the point counter field */
|
||||
};
|
||||
|
||||
struct _steelseries_message {
|
||||
uint8_t report_id;
|
||||
uint8_t parameters[STEELSERIES_REPORT_SIZE - 1];
|
||||
} __attribute__((packed));
|
||||
|
||||
union steelseries_message {
|
||||
struct _steelseries_message msg;
|
||||
uint8_t data[STEELSERIES_REPORT_SIZE];
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(union steelseries_message) == STEELSERIES_REPORT_SIZE,
|
||||
"Size of union steelseries_message is wrong");
|
||||
|
||||
static int
|
||||
steelseries_test_hidraw(struct ratbag_device *device)
|
||||
{
|
||||
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
|
||||
|
||||
/* Rival mice are composite devices with multiple HID devices
|
||||
* and only the HID vendor device can be used to configure the
|
||||
* device.
|
||||
*/
|
||||
if (!ratbag_hidraw_has_vendor_page(device))
|
||||
return false;
|
||||
|
||||
if (device_version > 1)
|
||||
return ratbag_hidraw_has_report(device, STEELSERIES_REPORT_ID_1);
|
||||
else
|
||||
@@ -179,23 +203,38 @@ steelseries_get_firmware_version(struct ratbag_device *device)
|
||||
{
|
||||
struct steelseries_data *drv_data = device->drv_data;
|
||||
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
|
||||
size_t buf_len = STEELSERIES_REPORT_SIZE;
|
||||
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
|
||||
size_t msg_len;
|
||||
uint8_t buf[2] = {0};
|
||||
int ret;
|
||||
|
||||
if (device_version == 2)
|
||||
buf[0] = STEELSERIES_ID_FIRMWARE;
|
||||
else if (device_version == 3)
|
||||
buf[0] = STEELSERIES_ID_FIRMWARE_PROTOCOL3;
|
||||
else
|
||||
union steelseries_message msg = {
|
||||
.msg.report_id = STEELSERIES_REPORT_ID,
|
||||
.msg.parameters = {0},
|
||||
};
|
||||
|
||||
switch (device_version) {
|
||||
case 1:
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_FIRMWARE_PROTOCOL1;
|
||||
msg_len = STEELSERIES_REPORT_SIZE_SHORT;
|
||||
break;
|
||||
case 2:
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_FIRMWARE_PROTOCOL2;
|
||||
msg_len = STEELSERIES_REPORT_SIZE;
|
||||
break;
|
||||
case 3:
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_FIRMWARE_PROTOCOL3;
|
||||
msg_len = STEELSERIES_REPORT_SIZE;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
msleep(10);
|
||||
ret = ratbag_hidraw_output_report(device, buf, buf_len);
|
||||
ret = ratbag_hidraw_output_report(device, msg.data, msg_len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ratbag_hidraw_read_input_report_index(device, buf, buf_len, STEELSERIES_INPUT_HIDRAW);
|
||||
ret = ratbag_hidraw_read_input_report_index(device, buf, sizeof(buf), STEELSERIES_INPUT_HIDRAW, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -213,24 +252,33 @@ steelseries_read_settings(struct ratbag_device *device)
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_led *led;
|
||||
|
||||
size_t buf_len = STEELSERIES_REPORT_SIZE;
|
||||
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
|
||||
int ret;
|
||||
unsigned int active_resolution;
|
||||
|
||||
if (device_version == 2)
|
||||
buf[0] = STEELSERIES_ID_SETTTINGS;
|
||||
else if (device_version == 3)
|
||||
buf[0] = STEELSERIES_ID_SETTTINGS_PROTOCOL3;
|
||||
else
|
||||
union steelseries_message msg = {
|
||||
.msg.report_id = STEELSERIES_REPORT_ID,
|
||||
.msg.parameters = {0},
|
||||
};
|
||||
|
||||
switch (device_version) {
|
||||
case 2:
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_SETTTINGS;
|
||||
break;
|
||||
case 3:
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_SETTTINGS_PROTOCOL3;
|
||||
break;
|
||||
case 1:
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
msleep(10);
|
||||
ret = ratbag_hidraw_output_report(device, buf, buf_len);
|
||||
ret = ratbag_hidraw_output_report(device, msg.data, STEELSERIES_REPORT_SIZE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ratbag_hidraw_read_input_report_index(device, buf, buf_len, STEELSERIES_INPUT_HIDRAW);
|
||||
ret = ratbag_hidraw_read_input_report_index(device, buf, STEELSERIES_REPORT_SIZE, STEELSERIES_INPUT_HIDRAW, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -270,7 +318,7 @@ steelseries_probe(struct ratbag_device *device)
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_button *button;
|
||||
struct ratbag_led *led;
|
||||
int rc, button_count, led_count, device_version, mono_led, short_button;
|
||||
int rc, button_count, led_count, device_version, quirk;
|
||||
struct dpi_list *dpilist = NULL;
|
||||
struct dpi_range *dpirange = NULL;
|
||||
|
||||
@@ -287,12 +335,22 @@ steelseries_probe(struct ratbag_device *device)
|
||||
return rc;
|
||||
|
||||
device_version = ratbag_device_data_steelseries_get_device_version(device->data);
|
||||
if (device_version == -1) {
|
||||
log_error(device->ratbag, "Device version not set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
button_count = ratbag_device_data_steelseries_get_button_count(device->data);
|
||||
if (button_count == -1)
|
||||
button_count = 0;
|
||||
|
||||
led_count = ratbag_device_data_steelseries_get_led_count(device->data);
|
||||
if (led_count == -1)
|
||||
led_count = 0;
|
||||
|
||||
dpirange = ratbag_device_data_steelseries_get_dpi_range(device->data);
|
||||
dpilist = ratbag_device_data_steelseries_get_dpi_list(device->data);
|
||||
mono_led = ratbag_device_data_steelseries_get_mono_led(device->data);
|
||||
short_button = ratbag_device_data_steelseries_get_short_button(device->data);
|
||||
quirk = ratbag_device_data_steelseries_get_quirk(device->data);
|
||||
|
||||
ratbag_device_init_profiles(device,
|
||||
STEELSERIES_NUM_PROFILES,
|
||||
@@ -336,7 +394,7 @@ steelseries_probe(struct ratbag_device *device)
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
|
||||
if (short_button == 0) {
|
||||
if (quirk != STEELSERIES_QUIRK_SENSEIRAW) {
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
|
||||
}
|
||||
|
||||
@@ -346,7 +404,7 @@ steelseries_probe(struct ratbag_device *device)
|
||||
ratbag_profile_for_each_led(profile, led) {
|
||||
led->type = led->index == 0 ? RATBAG_LED_TYPE_LOGO : RATBAG_LED_TYPE_WHEEL;
|
||||
led->mode = RATBAG_LED_ON;
|
||||
if (mono_led) {
|
||||
if (quirk == STEELSERIES_QUIRK_SENSEIRAW) {
|
||||
led->colordepth = RATBAG_LED_COLORDEPTH_MONOCHROME;
|
||||
led->brightness = 255;
|
||||
} else {
|
||||
@@ -385,15 +443,19 @@ steelseries_write_dpi(struct ratbag_resolution *resolution)
|
||||
struct dpi_list *dpilist = NULL;
|
||||
struct dpi_range *dpirange = NULL;
|
||||
int ret;
|
||||
int i = 0;
|
||||
size_t buf_len;
|
||||
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
|
||||
|
||||
union steelseries_message msg = {
|
||||
.msg.report_id = STEELSERIES_REPORT_ID,
|
||||
.msg.parameters = {0},
|
||||
};
|
||||
|
||||
dpirange = ratbag_device_data_steelseries_get_dpi_range(device->data);
|
||||
dpilist = ratbag_device_data_steelseries_get_dpi_list(device->data);
|
||||
|
||||
if (device_version == 1) {
|
||||
int i = 0;
|
||||
|
||||
switch (device_version) {
|
||||
case 1:
|
||||
/* when using lists the entries are enumerated in reverse */
|
||||
if (dpilist) {
|
||||
for (i = 0; i < (int)resolution->ndpis; i++) {
|
||||
@@ -406,27 +468,30 @@ steelseries_write_dpi(struct ratbag_resolution *resolution)
|
||||
}
|
||||
|
||||
buf_len = STEELSERIES_REPORT_SIZE_SHORT;
|
||||
buf[0] = STEELSERIES_ID_DPI_SHORT;
|
||||
buf[1] = resolution->index + 1;
|
||||
buf[2] = i;
|
||||
} else if (device_version == 2) {
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_DPI_SHORT;
|
||||
msg.msg.parameters[1] = resolution->index + 1;
|
||||
msg.msg.parameters[2] = i;
|
||||
break;
|
||||
case 2:
|
||||
buf_len = STEELSERIES_REPORT_SIZE;
|
||||
buf[0] = STEELSERIES_ID_DPI;
|
||||
buf[2] = resolution->index + 1;
|
||||
buf[3] = resolution->dpi_x / dpirange->step - 1;
|
||||
buf[6] = 0x42; /* not sure if needed */
|
||||
} else if (device_version == 3) {
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_DPI;
|
||||
msg.msg.parameters[2] = resolution->index + 1;
|
||||
msg.msg.parameters[3] = resolution->dpi_x / dpirange->step - 1;
|
||||
msg.msg.parameters[6] = 0x42; /* not sure if needed */
|
||||
break;
|
||||
case 3:
|
||||
buf_len = STEELSERIES_REPORT_SIZE;
|
||||
buf[0] = STEELSERIES_ID_DPI_PROTOCOL3;
|
||||
buf[2] = resolution->index + 1;
|
||||
buf[3] = resolution->dpi_x / dpirange->step - 1;
|
||||
buf[5] = 0x42; /* not sure if needed */
|
||||
} else {
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_DPI_PROTOCOL3;
|
||||
msg.msg.parameters[2] = resolution->index + 1;
|
||||
msg.msg.parameters[3] = resolution->dpi_x / dpirange->step - 1;
|
||||
msg.msg.parameters[5] = 0x42; /* not sure if needed */
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
msleep(10);
|
||||
ret = ratbag_hidraw_output_report(device, buf, buf_len);
|
||||
ret = ratbag_hidraw_output_report(device, msg.data, buf_len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -440,26 +505,49 @@ steelseries_write_report_rate(struct ratbag_profile *profile)
|
||||
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
|
||||
int ret;
|
||||
size_t buf_len;
|
||||
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
|
||||
uint8_t reported_rate = 0;
|
||||
|
||||
union steelseries_message msg = {
|
||||
.msg.report_id = STEELSERIES_REPORT_ID,
|
||||
.msg.parameters = {0},
|
||||
};
|
||||
|
||||
switch (device_version) {
|
||||
case 1:
|
||||
if (profile->hz >= 1000) {
|
||||
profile->hz = 1000;
|
||||
reported_rate = 0x01;
|
||||
} else if (profile->hz >= 375) {
|
||||
profile->hz = 500;
|
||||
reported_rate = 0x02;
|
||||
} else if (profile->hz <= 125) {
|
||||
profile->hz = 125;
|
||||
reported_rate = 0x04;
|
||||
} else {
|
||||
profile->hz = 250;
|
||||
reported_rate = 0x03;
|
||||
}
|
||||
|
||||
if (device_version == 1) {
|
||||
buf_len = STEELSERIES_REPORT_SIZE_SHORT;
|
||||
buf[0] = STEELSERIES_ID_REPORT_RATE_SHORT;
|
||||
buf[2] = 1000 / profile->hz;
|
||||
} else if (device_version == 2) {
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_REPORT_RATE_SHORT;
|
||||
msg.msg.parameters[2] = reported_rate;
|
||||
break;
|
||||
case 2:
|
||||
buf_len = STEELSERIES_REPORT_SIZE;
|
||||
buf[0] = STEELSERIES_ID_REPORT_RATE;
|
||||
buf[2] = 1000 / profile->hz;
|
||||
} else if (device_version == 3) {
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_REPORT_RATE;
|
||||
msg.msg.parameters[2] = 1000 / profile->hz;
|
||||
break;
|
||||
case 3:
|
||||
buf_len = STEELSERIES_REPORT_SIZE;
|
||||
buf[0] = STEELSERIES_ID_REPORT_RATE_PROTOCOL3;
|
||||
buf[2] = 1000 / profile->hz;
|
||||
} else {
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_REPORT_RATE_PROTOCOL3;
|
||||
msg.msg.parameters[2] = 1000 / profile->hz;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
msleep(10);
|
||||
ret = ratbag_hidraw_output_report(device, buf, buf_len);
|
||||
ret = ratbag_hidraw_output_report(device, msg.data, buf_len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -477,15 +565,17 @@ steelseries_write_buttons(struct ratbag_profile *profile)
|
||||
if (ratbag_device_data_steelseries_get_macro_length(device->data) == 0)
|
||||
return 0;
|
||||
|
||||
int short_button = ratbag_device_data_steelseries_get_short_button(device->data);
|
||||
int button_size = (short_button == 0) ? 5 : 3;
|
||||
int report_size = (short_button == 0) ? STEELSERIES_REPORT_LONG_SIZE : STEELSERIES_REPORT_SIZE_SHORT;
|
||||
int max_modifiers = (short_button == 0) ? 3 : 0;
|
||||
const bool is_senseiraw = ratbag_device_data_steelseries_get_quirk(device->data) == STEELSERIES_QUIRK_SENSEIRAW;
|
||||
const int button_size = is_senseiraw ? 3 : 5;
|
||||
const int report_size = is_senseiraw ? STEELSERIES_REPORT_SIZE_SHORT : STEELSERIES_REPORT_LONG_SIZE;
|
||||
const int max_modifiers = is_senseiraw ? 0 : 3;
|
||||
|
||||
uint8_t buf[report_size];
|
||||
memset(buf, 0, report_size);
|
||||
union steelseries_message msg = {
|
||||
.msg.report_id = STEELSERIES_REPORT_ID,
|
||||
.msg.parameters = {0},
|
||||
};
|
||||
|
||||
buf[0] = STEELSERIES_ID_BUTTONS;
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_BUTTONS;
|
||||
|
||||
ratbag_profile_for_each_button(profile, button) {
|
||||
struct ratbag_button_action *action = &button->action;
|
||||
@@ -498,7 +588,7 @@ steelseries_write_buttons(struct ratbag_profile *profile)
|
||||
|
||||
switch (action->type) {
|
||||
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
|
||||
buf[idx] = action->action.button;
|
||||
msg.msg.parameters[idx] = action->action.button;
|
||||
break;
|
||||
|
||||
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
|
||||
@@ -515,48 +605,48 @@ steelseries_write_buttons(struct ratbag_profile *profile)
|
||||
code = ratbag_hidraw_get_keyboard_usage_from_keycode(
|
||||
device, key);
|
||||
if (code) {
|
||||
if (short_button) {
|
||||
buf[idx] = STEELSERIES_BUTTON_KEY;
|
||||
if (is_senseiraw) {
|
||||
msg.msg.parameters[idx] = STEELSERIES_BUTTON_KEY;
|
||||
} else {
|
||||
buf[idx] = STEELSERIES_BUTTON_KBD;
|
||||
msg.msg.parameters[idx] = STEELSERIES_BUTTON_KBD;
|
||||
|
||||
if (modifiers & MODIFIER_LEFTCTRL)
|
||||
buf[++idx] = 0xE0;
|
||||
msg.msg.parameters[++idx] = 0xE0;
|
||||
if (modifiers & MODIFIER_LEFTSHIFT)
|
||||
buf[++idx] = 0xE1;
|
||||
msg.msg.parameters[++idx] = 0xE1;
|
||||
if (modifiers & MODIFIER_LEFTALT)
|
||||
buf[++idx] = 0xE2;
|
||||
msg.msg.parameters[++idx] = 0xE2;
|
||||
if (modifiers & MODIFIER_LEFTMETA)
|
||||
buf[++idx] = 0xE3;
|
||||
msg.msg.parameters[++idx] = 0xE3;
|
||||
if (modifiers & MODIFIER_RIGHTCTRL)
|
||||
buf[++idx] = 0xE4;
|
||||
msg.msg.parameters[++idx] = 0xE4;
|
||||
if (modifiers & MODIFIER_RIGHTSHIFT)
|
||||
buf[++idx] = 0xE5;
|
||||
msg.msg.parameters[++idx] = 0xE5;
|
||||
if (modifiers & MODIFIER_RIGHTALT)
|
||||
buf[++idx] = 0xE6;
|
||||
msg.msg.parameters[++idx] = 0xE6;
|
||||
if (modifiers & MODIFIER_RIGHTMETA)
|
||||
buf[++idx] = 0xE7;
|
||||
msg.msg.parameters[++idx] = 0xE7;
|
||||
}
|
||||
|
||||
buf[idx + 1] = code;
|
||||
msg.msg.parameters[idx + 1] = code;
|
||||
} else {
|
||||
code = ratbag_hidraw_get_consumer_usage_from_keycode(
|
||||
device, action->action.key.key);
|
||||
buf[idx] = STEELSERIES_BUTTON_CONSUMER;
|
||||
buf[idx + 1] = code;
|
||||
msg.msg.parameters[idx] = STEELSERIES_BUTTON_CONSUMER;
|
||||
msg.msg.parameters[idx + 1] = code;
|
||||
}
|
||||
break;
|
||||
|
||||
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
|
||||
switch (action->action.special) {
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP:
|
||||
buf[idx] = STEELSERIES_BUTTON_RES_CYCLE;
|
||||
msg.msg.parameters[idx] = STEELSERIES_BUTTON_RES_CYCLE;
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP:
|
||||
buf[idx] = STEELSERIES_BUTTON_WHEEL_UP;
|
||||
msg.msg.parameters[idx] = STEELSERIES_BUTTON_WHEEL_UP;
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN:
|
||||
buf[idx] = STEELSERIES_BUTTON_WHEEL_DOWN;
|
||||
msg.msg.parameters[idx] = STEELSERIES_BUTTON_WHEEL_DOWN;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -566,7 +656,7 @@ steelseries_write_buttons(struct ratbag_profile *profile)
|
||||
case RATBAG_BUTTON_ACTION_TYPE_KEY:
|
||||
case RATBAG_BUTTON_ACTION_TYPE_NONE:
|
||||
default:
|
||||
buf[idx] = STEELSERIES_BUTTON_OFF;
|
||||
msg.msg.parameters[idx] = STEELSERIES_BUTTON_OFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -574,9 +664,10 @@ steelseries_write_buttons(struct ratbag_profile *profile)
|
||||
msleep(10);
|
||||
if (device_version == 3)
|
||||
ret = ratbag_hidraw_raw_request(device, STEELSERIES_ID_BUTTONS,
|
||||
buf,sizeof(buf),HID_FEATURE_REPORT,HID_REQ_SET_REPORT);
|
||||
msg.msg.parameters, report_size - 1,
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
else
|
||||
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
|
||||
ret = ratbag_hidraw_output_report(device, msg.data, report_size);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -588,19 +679,34 @@ static int
|
||||
steelseries_write_led_v1(struct ratbag_led *led)
|
||||
{
|
||||
struct ratbag_device *device = led->profile->device;
|
||||
uint8_t buf[STEELSERIES_REPORT_SIZE_SHORT] = {0};
|
||||
const enum steelseries_quirk quirk = ratbag_device_data_steelseries_get_quirk(device->data);
|
||||
int ret;
|
||||
|
||||
buf[0] = STEELSERIES_ID_LED_EFFECT_SHORT;
|
||||
buf[1] = led->index + 1;
|
||||
union steelseries_message msg = {
|
||||
.msg.report_id = STEELSERIES_REPORT_ID,
|
||||
.msg.parameters = {0},
|
||||
};
|
||||
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_LED_EFFECT_SHORT;
|
||||
msg.msg.parameters[1] = quirk == STEELSERIES_QUIRK_RIVAL100 ? 0x00 : led->index + 1;
|
||||
|
||||
switch(led->mode) {
|
||||
case RATBAG_LED_OFF:
|
||||
case RATBAG_LED_ON:
|
||||
buf[2] = 0x01;
|
||||
msg.msg.parameters[2] = 0x01;
|
||||
break;
|
||||
case RATBAG_LED_BREATHING:
|
||||
buf[2] = 0x03; /* 0x02: slow, 0x03: medium, 0x04: fast */
|
||||
/* 0x2/3/4 - speed (by eye it's 3, 5 and 7 seconds) */
|
||||
if (led->ms <= 3000) {
|
||||
led->ms = 3000;
|
||||
msg.msg.parameters[2] = 0x04;
|
||||
} else if (led->ms <= 5000) {
|
||||
led->ms = 5000;
|
||||
msg.msg.parameters[2] = 0x03;
|
||||
} else {
|
||||
led->ms = 7000;
|
||||
msg.msg.parameters[2] = 0x02;
|
||||
}
|
||||
break;
|
||||
case RATBAG_LED_CYCLE:
|
||||
/* Cycle mode is not supported on this version */
|
||||
@@ -609,31 +715,36 @@ steelseries_write_led_v1(struct ratbag_led *led)
|
||||
}
|
||||
|
||||
msleep(10);
|
||||
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
|
||||
ret = ratbag_hidraw_output_report(device, msg.data, STEELSERIES_REPORT_SIZE_SHORT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
int mono_led = ratbag_device_data_steelseries_get_mono_led(device->data);
|
||||
memset(msg.data, 0, STEELSERIES_REPORT_SIZE); // reset the msg buffer before reusing
|
||||
|
||||
if (mono_led) {
|
||||
buf[0] = STEELSERIES_ID_LED_INTENSITY_SHORT;
|
||||
buf[1] = led->index + 1;
|
||||
if (quirk == STEELSERIES_QUIRK_SENSEIRAW) {
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_LED_INTENSITY_SHORT;
|
||||
msg.msg.parameters[1] = led->index + 1;
|
||||
if (led->mode == RATBAG_LED_OFF || led->brightness == 0) {
|
||||
buf[2] = 1;
|
||||
msg.msg.parameters[2] = 1;
|
||||
} else {
|
||||
//split the brightness into roughly 3 equal intensities
|
||||
buf[2] = (led->brightness / 86) + 2;
|
||||
msg.msg.parameters[2] = (led->brightness / 86) + 2;
|
||||
}
|
||||
} else {
|
||||
buf[0] = STEELSERIES_ID_LED_COLOR_SHORT;
|
||||
buf[1] = led->index + 1;
|
||||
buf[2] = led->color.red;
|
||||
buf[3] = led->color.green;
|
||||
buf[4] = led->color.blue;
|
||||
if (quirk != STEELSERIES_QUIRK_RIVAL100) {
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_LED_COLOR_SHORT;
|
||||
msg.msg.parameters[1] = led->index + 1;
|
||||
} else {
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_LED_COLOR_SHORT_RIVAL100;
|
||||
msg.msg.parameters[1] = 0x00;
|
||||
}
|
||||
msg.msg.parameters[2] = led->color.red;
|
||||
msg.msg.parameters[3] = led->color.green;
|
||||
msg.msg.parameters[4] = led->color.blue;
|
||||
}
|
||||
|
||||
msleep(10);
|
||||
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
|
||||
ret = ratbag_hidraw_output_report(device, msg.data, STEELSERIES_REPORT_SIZE_SHORT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -701,7 +812,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
|
||||
@@ -709,10 +820,14 @@ steelseries_write_led_cycle(struct ratbag_led *led,
|
||||
struct steelseries_led_cycle_spec *cycle_spec)
|
||||
{
|
||||
struct ratbag_device *device = led->profile->device;
|
||||
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
|
||||
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
|
||||
int ret;
|
||||
|
||||
union steelseries_message msg = {
|
||||
.msg.report_id = STEELSERIES_REPORT_ID,
|
||||
.msg.parameters = {0},
|
||||
};
|
||||
|
||||
struct steelseries_led_cycle cycle;
|
||||
struct steelseries_point point[4];
|
||||
|
||||
@@ -781,14 +896,15 @@ steelseries_write_led_cycle(struct ratbag_led *led,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
construct_cycle_buffer(&cycle, cycle_spec, buf, sizeof(buf));
|
||||
construct_cycle_buffer(&cycle, cycle_spec, msg.msg.parameters, sizeof(msg.msg.parameters));
|
||||
|
||||
msleep(10);
|
||||
if (device_version == 3)
|
||||
ret = ratbag_hidraw_raw_request(device, cycle_spec->cmd_val, buf,
|
||||
sizeof(buf), cycle_spec->hid_report_type, HID_REQ_SET_REPORT);
|
||||
ret = ratbag_hidraw_raw_request(device, cycle_spec->cmd_val, msg.msg.parameters,
|
||||
sizeof(msg.msg.parameters), cycle_spec->hid_report_type,
|
||||
HID_REQ_SET_REPORT);
|
||||
else
|
||||
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
|
||||
ret = ratbag_hidraw_output_report(device, msg.data, sizeof(msg.data));
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -855,23 +971,27 @@ steelseries_write_save(struct ratbag_device *device)
|
||||
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
|
||||
int ret;
|
||||
size_t buf_len;
|
||||
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
|
||||
|
||||
union steelseries_message msg = {
|
||||
.msg.report_id = STEELSERIES_REPORT_ID,
|
||||
.msg.parameters = {0},
|
||||
};
|
||||
|
||||
if (device_version == 1) {
|
||||
buf_len = STEELSERIES_REPORT_SIZE_SHORT;
|
||||
buf[0] = STEELSERIES_ID_SAVE_SHORT;
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_SAVE_SHORT;
|
||||
} else if (device_version == 2) {
|
||||
buf_len = STEELSERIES_REPORT_SIZE;
|
||||
buf[0] = STEELSERIES_ID_SAVE;
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_SAVE;
|
||||
} else if (device_version == 3) {
|
||||
buf_len = STEELSERIES_REPORT_SIZE;
|
||||
buf[0] = STEELSERIES_ID_SAVE_PROTOCOL3;
|
||||
msg.msg.parameters[0] = STEELSERIES_ID_SAVE_PROTOCOL3;
|
||||
} else {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
ret = ratbag_hidraw_output_report(device, buf, buf_len);
|
||||
ret = ratbag_hidraw_output_report(device, msg.data, buf_len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -887,9 +1007,11 @@ steelseries_write_profile(struct ratbag_profile *profile)
|
||||
int rc;
|
||||
bool buttons_dirty = false;
|
||||
|
||||
rc = steelseries_write_report_rate(profile);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
if (profile->rate_dirty) {
|
||||
rc = steelseries_write_report_rate(profile);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (!resolution->dirty)
|
||||
|
||||
7
src/driver-steelseries.h
Normal file
7
src/driver-steelseries.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
enum steelseries_quirk {
|
||||
STEELSERIES_QURIK_NONE,
|
||||
STEELSERIES_QUIRK_RIVAL100,
|
||||
STEELSERIES_QUIRK_SENSEIRAW,
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user