Compare commits
874 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fbeebf121 | ||
|
|
79565cb31e | ||
|
|
9fca203372 | ||
|
|
64d48a19a7 | ||
|
|
1d73217ad2 | ||
|
|
585a3b901a | ||
|
|
0d9fe48b0c | ||
|
|
a9adc06b7a | ||
|
|
64062a456f | ||
|
|
27f6d6c76b | ||
|
|
e82457f284 | ||
|
|
6d56dd538e | ||
|
|
5488593caa | ||
|
|
338a2687d3 | ||
|
|
83f79c55f1 | ||
|
|
2daef846e3 | ||
|
|
c5ef207961 | ||
|
|
c02a1b820b | ||
|
|
a7ddb3efdd | ||
|
|
2f870fe0f7 | ||
|
|
3ef5e63cd4 | ||
|
|
b94ae51ac8 | ||
|
|
3f33d0633a | ||
|
|
dcafaf18e2 | ||
|
|
67e40b35e8 | ||
|
|
30f56a1c0f | ||
|
|
4b97ebd649 | ||
|
|
c2de1b59ef | ||
|
|
4e76c99e0f | ||
|
|
42b83ecce8 | ||
|
|
14c2ac6959 | ||
|
|
a1874c572b | ||
|
|
ee4275f45c | ||
|
|
70a0fcd906 | ||
|
|
18d79f8dd4 | ||
|
|
cdc7a27c9e | ||
|
|
39a5350f30 | ||
|
|
538ab9c947 | ||
|
|
d898edc4a3 | ||
|
|
ed0b730e74 | ||
|
|
50392da360 | ||
|
|
85a86ec3c5 | ||
|
|
6290c84efd | ||
|
|
dbdbbf7fb3 | ||
|
|
3d65a77515 | ||
|
|
e15ce68498 | ||
|
|
011f3f556b | ||
|
|
8854ca6f23 | ||
|
|
4690ca77c4 | ||
|
|
d172d27408 | ||
|
|
67dbe5f4c8 | ||
|
|
5aedbad692 | ||
|
|
9495cc03c2 | ||
|
|
9deb7d66aa | ||
|
|
c67bd32522 | ||
|
|
ce98e89427 | ||
|
|
72b37deb52 | ||
|
|
0cfb9b8896 | ||
|
|
51c6b6f2de | ||
|
|
1c1be84e59 | ||
|
|
4edbf51f17 | ||
|
|
98b5804a21 | ||
|
|
d3799dcf41 | ||
|
|
44b1b5b6d0 | ||
|
|
f5fac6f713 | ||
|
|
be143992ff | ||
|
|
086a8252d2 | ||
|
|
39beaf8b3a | ||
|
|
3969472dd3 | ||
|
|
90bae7db84 | ||
|
|
a734a35b7e | ||
|
|
64915b09f7 | ||
|
|
7add22e861 | ||
|
|
75fec689a0 | ||
|
|
5d885f76e7 | ||
|
|
42624c7102 | ||
|
|
087b99a2de | ||
|
|
b3a6dc5b2e | ||
|
|
95dafbe3aa | ||
|
|
a1dc69d203 | ||
|
|
e32cf61fa8 | ||
|
|
f7488f67c1 | ||
|
|
6c62f975d0 | ||
|
|
62034758f0 | ||
|
|
e66a83aa36 | ||
|
|
a288caa7ee | ||
|
|
5be7541875 | ||
|
|
33a4bf8bf5 | ||
|
|
b2970186df | ||
|
|
52fcbe3b48 | ||
|
|
b34061c766 | ||
|
|
2ce8a3b249 | ||
|
|
8c5800e536 | ||
|
|
804ba267f6 | ||
|
|
6099df1b42 | ||
|
|
fc1e38d196 | ||
|
|
55ed173c39 | ||
|
|
593d27770d | ||
|
|
0dbb93e6a5 | ||
|
|
68049efc41 | ||
|
|
1d18e4d8f7 | ||
|
|
d3a18bb85f | ||
|
|
1cb8f0c874 | ||
|
|
7e360e4416 | ||
|
|
9842972d8b | ||
|
|
a4b54df433 | ||
|
|
6ed09d0643 | ||
|
|
1bd68177ab | ||
|
|
985ebd46cd | ||
|
|
3fb197b78c | ||
|
|
cb67897d15 | ||
|
|
533cd54b92 | ||
|
|
a658f2e2b0 | ||
|
|
5aa45a78c3 | ||
|
|
689d57d097 | ||
|
|
dc3f70ff86 | ||
|
|
58f1a142ec | ||
|
|
10233dac30 | ||
|
|
ec9ac9c920 | ||
|
|
5642a658ab | ||
|
|
08d571aea9 | ||
|
|
3cd76aa191 | ||
|
|
3d3d0d4ccc | ||
|
|
83f45218de | ||
|
|
69df28c155 | ||
|
|
babdb44b4f | ||
|
|
daee35de4a | ||
|
|
8d01e28138 | ||
|
|
04d79810d4 | ||
|
|
6fa1bfd044 | ||
|
|
c1a79a49f8 | ||
|
|
04775a3719 | ||
|
|
20f16e8e10 | ||
|
|
eb0d939d1a | ||
|
|
4b52b77348 | ||
|
|
8fccd84345 | ||
|
|
2ea92db3ca | ||
|
|
205047e278 | ||
|
|
c268ff7a45 | ||
|
|
7dbf92955e | ||
|
|
82a41bf46b | ||
|
|
1de3b2e472 | ||
|
|
f6b76900d9 | ||
|
|
1c6f289d91 | ||
|
|
ca0e2a0a3f | ||
|
|
111f2be5c8 | ||
|
|
f68a831c81 | ||
|
|
b722eccf04 | ||
|
|
53b7c63680 | ||
|
|
aa4456e9dc | ||
|
|
8d657f891a | ||
|
|
a688bfd9ec | ||
|
|
a47f3fa5d1 | ||
|
|
69f889e33f | ||
|
|
52049af8e1 | ||
|
|
7d0402a97f | ||
|
|
0427e5607e | ||
|
|
419a7722ad | ||
|
|
60afd53257 | ||
|
|
a99a5a544e | ||
|
|
244d48d245 | ||
|
|
808a719823 | ||
|
|
56db8fbdf9 | ||
|
|
0af12fbf2e | ||
|
|
646151ae61 | ||
|
|
66138ad7c0 | ||
|
|
d81a971455 | ||
|
|
66f3d5d02b | ||
|
|
6b8e7a2d9c | ||
|
|
504d23fab7 | ||
|
|
52109ade91 | ||
|
|
daafe5ebca | ||
|
|
8be0e21ac7 | ||
|
|
9c04bfad92 | ||
|
|
4025b83dfd | ||
|
|
bc51117f59 | ||
|
|
a8819b06c8 | ||
|
|
6f973cdc27 | ||
|
|
b548d05cad | ||
|
|
ef3d3e5290 | ||
|
|
487e1a3ded | ||
|
|
4206d370f1 | ||
|
|
29ccd55210 | ||
|
|
b399380445 | ||
|
|
1d68ef858e | ||
|
|
717213c506 | ||
|
|
0b5c263799 | ||
|
|
1379da70a8 | ||
|
|
f42ff35f24 | ||
|
|
3d8c9272fb | ||
|
|
381371c899 | ||
|
|
be590c154a | ||
|
|
30e4324906 | ||
|
|
545db1f641 | ||
|
|
5e2b2b6647 | ||
|
|
f3a276d7a3 | ||
|
|
02a7d8cadd | ||
|
|
4da7feec3b | ||
|
|
f75c9845d2 | ||
|
|
b947dd4896 | ||
|
|
ade9c816c7 | ||
|
|
8141fafb4b | ||
|
|
79b18d07c9 | ||
|
|
d92939135f | ||
|
|
733bf913e6 | ||
|
|
1162ccb897 | ||
|
|
73ddb12d8e | ||
|
|
93b5a0008b | ||
|
|
847205eb06 | ||
|
|
98b4aff33d | ||
|
|
a4e4c160c1 | ||
|
|
b97c83be97 | ||
|
|
2e4e0a0676 | ||
|
|
93fe73214c | ||
|
|
4ca2496a6f | ||
|
|
b19006104f | ||
|
|
dba9d472a8 | ||
|
|
7a7aad8977 | ||
|
|
254e2cb77e | ||
|
|
fdb1b43373 | ||
|
|
a9d6b4c319 | ||
|
|
7908a4f5a4 | ||
|
|
757c731610 | ||
|
|
612e8fb4f7 | ||
|
|
3effccf390 | ||
|
|
84e0e74709 | ||
|
|
ec8a51d353 | ||
|
|
3b6a4ed0ed | ||
|
|
815c9755b5 | ||
|
|
1e7050595e | ||
|
|
fc59c0fbf6 | ||
|
|
4874d72c16 | ||
|
|
e6cfd0a0c1 | ||
|
|
ebf7984ad0 | ||
|
|
2654b6bbe0 | ||
|
|
9f7c7209fe | ||
|
|
41fb08c059 | ||
|
|
362d43a7a1 | ||
|
|
0259e44c31 | ||
|
|
df8b6b488c | ||
|
|
825b7b6f34 | ||
|
|
b1d4b2f3cd | ||
|
|
58823763ea | ||
|
|
aeb8588e06 | ||
|
|
ebfd26b3d0 | ||
|
|
95a30a8089 | ||
|
|
dca6b4ba38 | ||
|
|
b1706fa9e2 | ||
|
|
ff5d744183 | ||
|
|
377e44c624 | ||
|
|
6e986ddf8c | ||
|
|
714f8782b3 | ||
|
|
ad57db4a39 | ||
|
|
8b5d56feb4 | ||
|
|
2153035c30 | ||
|
|
dbf6226f77 | ||
|
|
7a82b93aaf | ||
|
|
9de4d392d4 | ||
|
|
b0297694d1 | ||
|
|
7fdba7fe75 | ||
|
|
9f330479c1 | ||
|
|
36e8dcb396 | ||
|
|
708edbc444 | ||
|
|
d87bc594f4 | ||
|
|
7a65c40312 | ||
|
|
6994e1e41c | ||
|
|
1ce67954cb | ||
|
|
1e8956ff96 | ||
|
|
c8fe87ee2d | ||
|
|
aa067b2774 | ||
|
|
b3751913b2 | ||
|
|
09394cfbf5 | ||
|
|
7498febc3a | ||
|
|
4a5c0ea523 | ||
|
|
633760e261 | ||
|
|
68aebc8c1b | ||
|
|
831287ead6 | ||
|
|
1bba8b26de | ||
|
|
718c2d4039 | ||
|
|
1516daa6d0 | ||
|
|
4df84107e1 | ||
|
|
3e4c09380a | ||
|
|
e6947d95d8 | ||
|
|
f5c00a5f2a | ||
|
|
246c6cc1b2 | ||
|
|
2d29db4609 | ||
|
|
374388daf0 | ||
|
|
fe55e39560 | ||
|
|
535b1f02ce | ||
|
|
eec5df5d56 | ||
|
|
c4d2ec4008 | ||
|
|
00ea128775 | ||
|
|
b98033d558 | ||
|
|
16823092bc | ||
|
|
9a13eb0229 | ||
|
|
35114490d3 | ||
|
|
bf3aeff7d8 | ||
|
|
d027c66198 | ||
|
|
04e8281738 | ||
|
|
f5c63ccc39 | ||
|
|
9b8f96e824 | ||
|
|
5117028c52 | ||
|
|
eda0a95585 | ||
|
|
5041530952 | ||
|
|
9d0fcea02e | ||
|
|
1400473877 | ||
|
|
330fadfbce | ||
|
|
b3cc4361e8 | ||
|
|
2e1e8dc9b5 | ||
|
|
44f85d5dee | ||
|
|
78a6ae9208 | ||
|
|
4c31adb907 | ||
|
|
15b1965582 | ||
|
|
6918353a29 | ||
|
|
1973693cc8 | ||
|
|
1361af5501 | ||
|
|
6e5d36e59f | ||
|
|
95dc973748 | ||
|
|
e7047f4606 | ||
|
|
d1908694c4 | ||
|
|
b5bb04b8a2 | ||
|
|
14725e42ee | ||
|
|
5b796bae7d | ||
|
|
e4f5d75095 | ||
|
|
409065ed29 | ||
|
|
56c6a52941 | ||
|
|
fd02b33971 | ||
|
|
e763aeadb4 | ||
|
|
aaee650caf | ||
|
|
6645b5b04c | ||
|
|
e8f169f6f5 | ||
|
|
19a5fa3911 | ||
|
|
64e20533a0 | ||
|
|
86a49128b4 | ||
|
|
a579eb4ded | ||
|
|
59296f41ca | ||
|
|
0b0cf63240 | ||
|
|
5dbf691403 | ||
|
|
c1b6bf92c7 | ||
|
|
3909a79149 | ||
|
|
97caca023f | ||
|
|
11bc5bde37 | ||
|
|
fb328b3993 | ||
|
|
4fbf5aa531 | ||
|
|
07b42e2b34 | ||
|
|
95893f3aac | ||
|
|
46e6c98523 | ||
|
|
e4d3f5e61e | ||
|
|
a2ac8b46c1 | ||
|
|
d2eabe4258 | ||
|
|
204bc6e810 | ||
|
|
e436b1bd1d | ||
|
|
4dfa55c96c | ||
|
|
7f70039284 | ||
|
|
2d2e71daa3 | ||
|
|
4ce2abca0a | ||
|
|
bc6b1de472 | ||
|
|
90cc629f26 | ||
|
|
de0894bc34 | ||
|
|
e86a50cfa9 | ||
|
|
b6615d5abe | ||
|
|
323fb13161 | ||
|
|
fef22de021 | ||
|
|
5a4205d002 | ||
|
|
1c2b347512 | ||
|
|
63872b9146 | ||
|
|
c6506b3508 | ||
|
|
c9c472e391 | ||
|
|
1dc59fd374 | ||
|
|
24798ee310 | ||
|
|
e05c1aa90c | ||
|
|
fe0ab16cc8 | ||
|
|
c38d10a654 | ||
|
|
d386f4369b | ||
|
|
ae4a0eba70 | ||
|
|
72fd2f255e | ||
|
|
a8d067035d | ||
|
|
27c90fa736 | ||
|
|
bbaa144397 | ||
|
|
e751f741f7 | ||
|
|
8e89aa0038 | ||
|
|
627185079f | ||
|
|
de5514aa23 | ||
|
|
0bc58d5653 | ||
|
|
33521558ed | ||
|
|
63fa581d13 | ||
|
|
e6369e0c3c | ||
|
|
be4e88dc1a | ||
|
|
72a8d311bc | ||
|
|
cab523e122 | ||
|
|
070a2bf837 | ||
|
|
0836766bfb | ||
|
|
4379bd0613 | ||
|
|
ad2660a1bd | ||
|
|
6d16462988 | ||
|
|
e69c4c6b4f | ||
|
|
5e4c792a64 | ||
|
|
c5ca2c2818 | ||
|
|
f631f0f551 | ||
|
|
ef54a750dc | ||
|
|
c99f470dd5 | ||
|
|
1dfc4f22df | ||
|
|
68b4ca7abf | ||
|
|
09125bce2d | ||
|
|
aeb03df3f7 | ||
|
|
f191d95990 | ||
|
|
5926596298 | ||
|
|
97fdcbf3ff | ||
|
|
66c8621cab | ||
|
|
a4bf1da6f9 | ||
|
|
922414ae04 | ||
|
|
9beb13a305 | ||
|
|
89995656cd | ||
|
|
f198b2706b | ||
|
|
bd0f9ca7d7 | ||
|
|
784661bbc0 | ||
|
|
579b09619b | ||
|
|
013f383bc8 | ||
|
|
7936d2dd48 | ||
|
|
cb84d3b797 | ||
|
|
79947dca6c | ||
|
|
d41c35b1c6 | ||
|
|
093169fb23 | ||
|
|
6c77aa3b61 | ||
|
|
4a108b3ab7 | ||
|
|
fac6cd6840 | ||
|
|
edf610d235 | ||
|
|
dac9c1bd8e | ||
|
|
352375aa9a | ||
|
|
44722560d2 | ||
|
|
101e24c7bf | ||
|
|
b7e8e646b5 | ||
|
|
97fa7de28a | ||
|
|
302720b0db | ||
|
|
813c238704 | ||
|
|
354c914bd2 | ||
|
|
574aee0cc7 | ||
|
|
a3cdcf4cd1 | ||
|
|
592ced0a53 | ||
|
|
3a2593798e | ||
|
|
67b7d46844 | ||
|
|
5411e43b77 | ||
|
|
66dc0bd285 | ||
|
|
f641554ed5 | ||
|
|
beec4c2130 | ||
|
|
f5bf488ffc | ||
|
|
1deaa09142 | ||
|
|
88a0dfff6f | ||
|
|
f28da6e7c4 | ||
|
|
5290980ac8 | ||
|
|
4d23d3abac | ||
|
|
0c4e757cf9 | ||
|
|
d487a0d889 | ||
|
|
65824a4c08 | ||
|
|
58a1c7ffb0 | ||
|
|
1f896d2b9e | ||
|
|
377c954933 | ||
|
|
4744ce47b2 | ||
|
|
b01b74f2a8 | ||
|
|
6282ae05e8 | ||
|
|
b5e1f47c50 | ||
|
|
8ffaeb112f | ||
|
|
2189d4a35b | ||
|
|
706ee2990f | ||
|
|
06875fb9ab | ||
|
|
f6b0cc59ed | ||
|
|
e3aacdbf05 | ||
|
|
45dc81b4c4 | ||
|
|
0324fd9a1e | ||
|
|
ca9f9b3447 | ||
|
|
624247d378 | ||
|
|
5f8dbdeb6d | ||
|
|
2bdb844557 | ||
|
|
422f336d70 | ||
|
|
563ef0d8ef | ||
|
|
3a85c4f64b | ||
|
|
4056f5b9d3 | ||
|
|
442a7d18a5 | ||
|
|
79be4b20d7 | ||
|
|
b2f3b56c31 | ||
|
|
072f932206 | ||
|
|
d5a3a4fe7e | ||
|
|
a49b7938e0 | ||
|
|
1eef88eb44 | ||
|
|
eda1399330 | ||
|
|
8dbc4b5707 | ||
|
|
687a28d2c2 | ||
|
|
f733390bd1 | ||
|
|
ac6fa9643f | ||
|
|
f28053a09a | ||
|
|
49145ae272 | ||
|
|
af26870e83 | ||
|
|
007163a563 | ||
|
|
a9acdbe47a | ||
|
|
955df30c7f | ||
|
|
23c0397764 | ||
|
|
789f5f05c3 | ||
|
|
ea2c22c015 | ||
|
|
1c09b9c45d | ||
|
|
dd9626f7a4 | ||
|
|
35909afb0d | ||
|
|
8e67bbbc11 | ||
|
|
a5813e4e23 | ||
|
|
d439d43be6 | ||
|
|
11465f2b2a | ||
|
|
079f2dacd4 | ||
|
|
7c6bd4202d | ||
|
|
1740a9a8c4 | ||
|
|
0cadc3247e | ||
|
|
e23de2ee9f | ||
|
|
8fb52a3f37 | ||
|
|
3edac85b90 | ||
|
|
64c76e51ef | ||
|
|
02cac598a0 | ||
|
|
02a47b4003 | ||
|
|
4afe8c893b | ||
|
|
5afa094175 | ||
|
|
196705ba1f | ||
|
|
4c46a999f7 | ||
|
|
7bd31c0d95 | ||
|
|
dc2a1adf26 | ||
|
|
e89b50cdf2 | ||
|
|
d08e0a9574 | ||
|
|
22d37c4c1c | ||
|
|
546ccb7ac2 | ||
|
|
1ab03d4c9f | ||
|
|
a6fcb75aa8 | ||
|
|
907c5ab075 | ||
|
|
515f994ab8 | ||
|
|
25905c5d77 | ||
|
|
babf0f4ded | ||
|
|
41664b8e21 | ||
|
|
5c080a7831 | ||
|
|
5aa4fa0239 | ||
|
|
4f4e610635 | ||
|
|
39791be440 | ||
|
|
2814350f9f | ||
|
|
4a28b48275 | ||
|
|
97474a21de | ||
|
|
719106bf0f | ||
|
|
0a735a32ed | ||
|
|
bd26b9340d | ||
|
|
f45ae5ffad | ||
|
|
0520dde990 | ||
|
|
e53781ec6e | ||
|
|
1bc189e2fe | ||
|
|
91bcfa28ea | ||
|
|
64c1de32da | ||
|
|
56762b5494 | ||
|
|
77e21a0b63 | ||
|
|
d6fa5269c5 | ||
|
|
0198d58dc7 | ||
|
|
c30f0a79bd | ||
|
|
89ee83b906 | ||
|
|
0bef7b84f8 | ||
|
|
c3e90bc73e | ||
|
|
814b7f30ba | ||
|
|
8039c035a7 | ||
|
|
430b70711b | ||
|
|
82915eea4b | ||
|
|
995544396c | ||
|
|
4678f52293 | ||
|
|
26c5a5f9ba | ||
|
|
c6ccc0638d | ||
|
|
6ba8c6c3fc | ||
|
|
9087f4187c | ||
|
|
673c81c096 | ||
|
|
43bfa80753 | ||
|
|
3884ce14d7 | ||
|
|
4fafd4f6be | ||
|
|
0bec0e02b1 | ||
|
|
59c0367283 | ||
|
|
63f523dac6 | ||
|
|
4822975acb | ||
|
|
c389c9c2b7 | ||
|
|
76e70799aa | ||
|
|
2a94cc9233 | ||
|
|
ae9e862f19 | ||
|
|
12f4e187d6 | ||
|
|
51dd2a5d28 | ||
|
|
6981555804 | ||
|
|
3583759d3a | ||
|
|
c92a889b68 | ||
|
|
b9fb005c9c | ||
|
|
a06ea6de11 | ||
|
|
a868b477e1 | ||
|
|
c799e038de | ||
|
|
8468be2126 | ||
|
|
1ff9c3a40b | ||
|
|
9b201f6b0d | ||
|
|
afda6f9f66 | ||
|
|
5cc0b8854d | ||
|
|
f88951c56d | ||
|
|
2aab7f71fd | ||
|
|
6376980213 | ||
|
|
1bb08d011d | ||
|
|
966c55e69d | ||
|
|
6b14004a6f | ||
|
|
7a4f7bbb84 | ||
|
|
155e2f8c40 | ||
|
|
6a1968beff | ||
|
|
f4557233f1 | ||
|
|
a597b81f5a | ||
|
|
a587ae65d1 | ||
|
|
6522b5fef0 | ||
|
|
7f78f0c580 | ||
|
|
127a8053d8 | ||
|
|
6c0acee645 | ||
|
|
60c52eb13f | ||
|
|
8bac8d68f4 | ||
|
|
087a5d2c42 | ||
|
|
481511b6c2 | ||
|
|
21d98294bd | ||
|
|
ebbb54758b | ||
|
|
c07c115ee3 | ||
|
|
8c02320795 | ||
|
|
932164458a | ||
|
|
de79bf1b5c | ||
|
|
5944103aef | ||
|
|
ab4226e066 | ||
|
|
a66121db0d | ||
|
|
eacfdea37f | ||
|
|
68b1f4e413 | ||
|
|
947035a7b7 | ||
|
|
e54134cda9 | ||
|
|
1609c3e98a | ||
|
|
ed9aa76547 | ||
|
|
b8529030e6 | ||
|
|
1412a07899 | ||
|
|
f79ad67ea7 | ||
|
|
dd8018a4d5 | ||
|
|
350784eb70 | ||
|
|
f0fc63e5b7 | ||
|
|
51375d4510 | ||
|
|
2c0ea34694 | ||
|
|
867edf8516 | ||
|
|
dcd1a88250 | ||
|
|
8f95dd323d | ||
|
|
336b778398 | ||
|
|
572c7f5e59 | ||
|
|
6b12b7935f | ||
|
|
ade2fbacec | ||
|
|
a59368f3e7 | ||
|
|
d021d87656 | ||
|
|
5d0d353c74 | ||
|
|
e00d680fc9 | ||
|
|
59b7285fdf | ||
|
|
988bdc2c8d | ||
|
|
1a67b33860 | ||
|
|
d43bffc9d9 | ||
|
|
f8079f4f9d | ||
|
|
47e7f997d5 | ||
|
|
8c0cf9fe9b | ||
|
|
fa8bd83ba3 | ||
|
|
697fb999b4 | ||
|
|
aeb943fc9e | ||
|
|
434af009cb | ||
|
|
cc010cb653 | ||
|
|
e450e959a6 | ||
|
|
10f3421021 | ||
|
|
8eba60d617 | ||
|
|
e3d658ea10 | ||
|
|
ef1c051e10 | ||
|
|
bc566718b4 | ||
|
|
22719f21fc | ||
|
|
c51daa922c | ||
|
|
632d8804be | ||
|
|
a4b7194490 | ||
|
|
ea80c6d639 | ||
|
|
a01e4b2efe | ||
|
|
801bdfa224 | ||
|
|
410d100dc6 | ||
|
|
7078750a3f | ||
|
|
360c92f6b0 | ||
|
|
2377fef3a8 | ||
|
|
684afa871e | ||
|
|
dd2f884504 | ||
|
|
9dd05bb727 | ||
|
|
e96a0fbfbb | ||
|
|
f9618dd0ab | ||
|
|
db0656967b | ||
|
|
53ec751cf1 | ||
|
|
45cc161cc7 | ||
|
|
1ccd2d1856 | ||
|
|
d9d37edc2f | ||
|
|
ae405871fb | ||
|
|
f4700f07ce | ||
|
|
d2d19a615d | ||
|
|
c36cc506b6 | ||
|
|
7180693f89 | ||
|
|
aa08356823 | ||
|
|
197ff7365d | ||
|
|
a0c6a6ce59 | ||
|
|
934ecb821d | ||
|
|
f0cf949336 | ||
|
|
ecd245e8dc | ||
|
|
f65f000d38 | ||
|
|
3b60f205f7 | ||
|
|
d6c93cfcfe | ||
|
|
cbd71f9d24 | ||
|
|
45d5119a5a | ||
|
|
773cb7f45f | ||
|
|
d74e3a9b92 | ||
|
|
fe1fba7ac6 | ||
|
|
4e20ef61c8 | ||
|
|
a750b32d14 | ||
|
|
69006c48c6 | ||
|
|
b12d03dfe3 | ||
|
|
ddbf93fb24 | ||
|
|
65c55b789c | ||
|
|
d83530bee3 | ||
|
|
e6c02f5c52 | ||
|
|
122d76d061 | ||
|
|
fb1442dbcb | ||
|
|
0b8141f092 | ||
|
|
137c32262b | ||
|
|
7c726738b1 | ||
|
|
2570f584b9 | ||
|
|
4c1f568fab | ||
|
|
27bd71253e | ||
|
|
9a9a82016f | ||
|
|
aa03cef61f | ||
|
|
c14e30b6f5 | ||
|
|
b5d43cdc3c | ||
|
|
1edd8a577b | ||
|
|
358e0958bb | ||
|
|
fa5fba796b | ||
|
|
ce215228f3 | ||
|
|
28e43395ab | ||
|
|
e09231c05a | ||
|
|
f29de0f3dd | ||
|
|
ba540338e2 | ||
|
|
2442fee341 | ||
|
|
bbadd3e755 | ||
|
|
2fdce2f938 | ||
|
|
f1fad2d16b | ||
|
|
8a5700b44e | ||
|
|
e046455a05 | ||
|
|
ddbc27486c | ||
|
|
2041007b38 | ||
|
|
7b7e17819e | ||
|
|
f855cd60e1 | ||
|
|
883ed9549d | ||
|
|
d1858f747b | ||
|
|
aa7d1b6410 | ||
|
|
bc3a98ddb2 | ||
|
|
5b01f375e0 | ||
|
|
dd2755909d | ||
|
|
b052ab9ef0 | ||
|
|
9c768d60a1 | ||
|
|
d1d0ab85ff | ||
|
|
e1eee2e078 | ||
|
|
5c38f90cd6 | ||
|
|
45d49b9de8 | ||
|
|
4ccb8ab26d | ||
|
|
ab162583e4 | ||
|
|
e25b47aa1c | ||
|
|
63a98819a6 | ||
|
|
27d3d80d30 | ||
|
|
478381a994 | ||
|
|
51a15a960f | ||
|
|
92967eed23 | ||
|
|
57338303e8 | ||
|
|
e735ce10f0 | ||
|
|
d75b6d2f2b | ||
|
|
bd1470dad7 | ||
|
|
15f97681ef | ||
|
|
7075ce910a | ||
|
|
5ba816dd38 | ||
|
|
a515cc3860 | ||
|
|
73344cbf26 | ||
|
|
cf27328d13 | ||
|
|
869f1e4791 | ||
|
|
e2d65a690e | ||
|
|
3141f06472 | ||
|
|
b88cf5a07b | ||
|
|
a344ad9867 | ||
|
|
76e327d2fa | ||
|
|
0ae0ef195a | ||
|
|
b1f74a1280 | ||
|
|
30431fbfb8 | ||
|
|
fb4c055fbf | ||
|
|
3ea51fe1a2 | ||
|
|
afd36764c2 | ||
|
|
b694809394 | ||
|
|
66296265ba | ||
|
|
952495ce04 | ||
|
|
29eb377267 | ||
|
|
47345e1dcb | ||
|
|
36fc6de760 | ||
|
|
6c0aca8c63 | ||
|
|
bd93689109 | ||
|
|
4e5d4853d6 | ||
|
|
bb2361a986 | ||
|
|
7c8d4815f0 | ||
|
|
0f05589cd6 | ||
|
|
21db60a14e | ||
|
|
091c28ca8b | ||
|
|
9af353dd32 | ||
|
|
981ff00ca3 | ||
|
|
ec0197df3d | ||
|
|
2111107ff4 | ||
|
|
2b08c5e0c9 | ||
|
|
cc8a6c4ab5 | ||
|
|
437e17cf37 | ||
|
|
22191d7d0c | ||
|
|
ba890c2d2e | ||
|
|
9a6cdd9cee | ||
|
|
32324f3e66 | ||
|
|
ffe297b239 | ||
|
|
7fe5983f7c | ||
|
|
d5f14bb5f8 | ||
|
|
f3049ed451 | ||
|
|
8485494ea7 | ||
|
|
7a75813a84 | ||
|
|
35830e3d50 | ||
|
|
46fd4dac56 | ||
|
|
ebd8635f84 | ||
|
|
efbf43b472 | ||
|
|
8a6d8e2ed1 | ||
|
|
bff99799ab | ||
|
|
1434b2a50d | ||
|
|
c3fdbfb643 | ||
|
|
a9ebac264f | ||
|
|
60a591ab6b | ||
|
|
ab3a6b50d4 | ||
|
|
b193b39701 | ||
|
|
806d67a72f | ||
|
|
9c465cd998 | ||
|
|
024a71b618 | ||
|
|
f144816256 | ||
|
|
f0eeea8625 | ||
|
|
72819b6130 | ||
|
|
23e4f32e11 | ||
|
|
243d1548dc | ||
|
|
c3bc56f306 | ||
|
|
288c607069 | ||
|
|
55c307d0e5 | ||
|
|
69febfcea5 | ||
|
|
9934755566 | ||
|
|
a7561eaa9b | ||
|
|
689661169a | ||
|
|
eac40abc3e | ||
|
|
372e4e0949 | ||
|
|
e42f7f1393 | ||
|
|
3860d4a56b | ||
|
|
3b75b69970 | ||
|
|
a4f0eab855 | ||
|
|
f54ea27d97 | ||
|
|
db48fe1b40 | ||
|
|
d0a0e902a7 | ||
|
|
95a6412d5c | ||
|
|
5faecbf4f3 | ||
|
|
5af1719384 | ||
|
|
6a66370ffe | ||
|
|
c0874220d2 | ||
|
|
9c67b1b494 | ||
|
|
22656d5b82 | ||
|
|
d55963caba | ||
|
|
413cebe40f | ||
|
|
140ffae01a | ||
|
|
68251777fb | ||
|
|
188b20a4f1 | ||
|
|
5fba1db11c | ||
|
|
21b1a722fc | ||
|
|
715c50685b | ||
|
|
dd0ba17351 | ||
|
|
576dd4cdd4 | ||
|
|
a2a0380401 | ||
|
|
f1a85f45bd | ||
|
|
200cf48df4 | ||
|
|
14b5993dc3 | ||
|
|
585ba93588 | ||
|
|
e30a692a6e |
10
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,10 @@
|
||||
# yapf bulk change
|
||||
72a8d311bce64b1536c08e754d22bb91efb66460
|
||||
# isort bulk change
|
||||
e6369e0c3c240715a0a2daede6c3b225ed63cf60
|
||||
# pre-commit bulk change
|
||||
33521558ed7007c24b24127f1448c2c23ecbfb35
|
||||
# flake8 bulk change
|
||||
627185079f65c9cc471194b7927c833682e4a7a3
|
||||
# yapf style update
|
||||
27c90fa736d8a7a1ef6acda4345d3599862e185c
|
||||
39
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Information**
|
||||
<!-- Make sure that your issue is not one of the known issues in the Solaar documentation at https://pwr-solaar.github.io/Solaar/ -->
|
||||
<!-- Do not bother opening an issue for a version older than 1.0.6. Please update to the latest version and see if your issue persists. -->
|
||||
- Solaar version (`solaar --version` or `git describe --tags` if cloned from this repository):
|
||||
- Distribution:
|
||||
- Kernel version (ex. `uname -srmo`): `KERNEL VERSION HERE`
|
||||
- Output of `solaar show`:
|
||||
|
||||
<details>
|
||||
|
||||
```
|
||||
OUTPUT HERE
|
||||
```
|
||||
</details>
|
||||
|
||||
**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
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
35
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Information**
|
||||
<!-- Please update to Solaar from this repository before asking for a new feature. -->
|
||||
- Solaar version (`solaar --version` and `git describe --tags`):
|
||||
- Distribution:
|
||||
- Kernel version (ex. `uname -srmo`):
|
||||
- Output of `solaar show` for the target device (if applicable):
|
||||
|
||||
<details>
|
||||
|
||||
```
|
||||
OUTPUT HERE
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
**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.
|
||||
16
.github/workflows/checks.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: checks
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
|
||||
- name: Run pre-commit
|
||||
uses: pre-commit/action@v2.0.0
|
||||
3
.gitignore
vendored
@@ -5,6 +5,7 @@ __pycache__/
|
||||
*.mo
|
||||
|
||||
/lib/Solaar.egg-info/
|
||||
/lib/solaar.egg-info/
|
||||
/build/
|
||||
/sdist/
|
||||
/dist/
|
||||
@@ -14,3 +15,5 @@ __pycache__/
|
||||
/docs/captures/
|
||||
/share/logitech_icons/
|
||||
/share/locale/
|
||||
|
||||
/po/*.po~
|
||||
|
||||
26
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.1.0
|
||||
hooks:
|
||||
- id: check-ast
|
||||
- id: check-builtin-literals
|
||||
- id: check-merge-conflict
|
||||
- id: check-yaml
|
||||
- id: check-toml
|
||||
- id: debug-statements
|
||||
- id: double-quote-string-fixer
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/pre-commit/mirrors-yapf
|
||||
rev: v0.30.0
|
||||
hooks:
|
||||
- id: yapf
|
||||
- repo: https://github.com/pre-commit/mirrors-isort
|
||||
rev: v4.3.21
|
||||
hooks:
|
||||
- id: isort
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.8.3
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies: ['flake8-bugbear']
|
||||
12
COPYRIGHT
@@ -1,2 +1,14 @@
|
||||
Copyright 2012, 2013
|
||||
Daniel Pavel <daniel.pavel@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 2.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
259
ChangeLog
@@ -1,7 +1,262 @@
|
||||
1.0.7:
|
||||
* Don't use time_ns so as not to require Python 3.7
|
||||
* Correctly determine setting box in change_click method
|
||||
* Handle fake Nano connection notifications
|
||||
* Lock on actual handle, not just on handle number
|
||||
* Mark Nano receiver C52F as not unpairing
|
||||
* Upgrade pairing/unpairing documentation
|
||||
|
||||
1.0.7rc2:
|
||||
* Don't signal status change when battery changes from None to None.
|
||||
* Add Japanese translation
|
||||
* Use first word of name for code name if no other code name available.
|
||||
* Better determination of when to add SW ID.
|
||||
* Check for more HID++ feature request failing.
|
||||
* Fix bug with new_fn_inversion setting.
|
||||
* Use correct device number for directly connected devices
|
||||
* Add debug message when candidate device found
|
||||
* Update Polish, Taiwanese, and Brazilian Portugese translations
|
||||
* Add MouseProcess a rule condition like Process but for the window under the mouse
|
||||
* Add parameters for binary settings to support prefixes
|
||||
* Add locks to serialize requests to devices
|
||||
* Fix bug when reprog key requests returns None
|
||||
* Fix bug for empty process name and class
|
||||
* Rules can now trigger on both pressing and releasing a diverted key
|
||||
* Upgrade mouse gestures to allow sequences of movements
|
||||
* Fix gkeys diversion faked read
|
||||
* Add suppor for Logitech g pro x superlight receiver
|
||||
* Convert HID++ 2.0 device kinds to enhanced HID++ 1.0 device kinds
|
||||
* Update about window, bug report templates, and supported kernels.
|
||||
|
||||
1.0.6:
|
||||
* Update sliding DPI to look for suitable keys
|
||||
* Add mouse gestures that can trigger rules
|
||||
* Complain if receivers do not support connection notification
|
||||
* In polling rate setting, only modify onboard profiles when actually writing polling rate
|
||||
* Add ability to ignore settings.
|
||||
* Use symbols for receiver sub-registers
|
||||
* Add support for wired G700
|
||||
* Do not set attention icon
|
||||
* Replace deprecated GTK stock menu icons
|
||||
* Better handling of icons in tray and tray menus
|
||||
* Receiver c52e does not unpair
|
||||
* Match active WM_CLASS as well as active process name in rules
|
||||
* Correctly set icon theme value when regular battery icons are not available
|
||||
* Handle exception when device is not available when device is being added
|
||||
* Perform initial activation of devices in listener threads
|
||||
* Keep track of serial numbers in the configuration file
|
||||
* Don't update settings for non-active devices
|
||||
* Set the current host name if not stored on the device
|
||||
* Add setting for SMART SHIFT ENHANCED feature
|
||||
* Don't unnecessarily use long messages for HID++ 1.0 commands
|
||||
* Correctly select choices in solaar config and use 1-origin addressing
|
||||
* Add quirk for G915 TKL keyboard because its host mode inteferes with its Fn keys
|
||||
* Show command outputs both saved and on-device settings
|
||||
* Update documentation
|
||||
* Fix bug in hidconsole
|
||||
* Update French translation
|
||||
|
||||
1.0.5:
|
||||
* Update documentation on devices forgetting settings.
|
||||
* Improve help messages
|
||||
* Fix bug in finding receiver to pair
|
||||
* Solaar config command can set keyed settings.
|
||||
|
||||
1.0.5rc2:
|
||||
* Add setting for polling rate
|
||||
* Use long HID++ messages for all 2.0 requests
|
||||
* Update German, Italian, and Polish translations
|
||||
* Solaar config command no longer selects paired but unconnected devices
|
||||
* Show HID++ 1.0 remaining pairings value in solaar show for devices that support it
|
||||
* Add option to not use battery icons in system tray.
|
||||
* Update Polish and Dutch translation.
|
||||
* Add Czech translation.
|
||||
* Remove information on SUSE package as it is very old.
|
||||
* Turn GKEY notifications into Gn key keypresses that can trigger rules.
|
||||
* Push device settings to devices after suspend when device is immediately active.
|
||||
* Reduce unneccessary saving of configuration file.
|
||||
* Better handling of disconnected devices.
|
||||
* Implement GUI to edit rules.
|
||||
* Implement rule-base processing of HID++ feature notifications (depends on X11).
|
||||
* Add settings for diversion of crown and remappable keys.
|
||||
* Access widgets by name instead of by index.
|
||||
* Implement UNIFIED_BATTERY feature and use in battery reports.
|
||||
* Add a clickable lock icon that determines where each setting can be changed.
|
||||
|
||||
1.0.4:
|
||||
* Update pt_BR translation
|
||||
|
||||
1.0.4rc1:
|
||||
* Support USB and BT connected devices that are not in descriptors.py
|
||||
* Use FRIENDLY NAME for codename if needed and available.
|
||||
* Extract manufacturer and product ID from Udev HID information.
|
||||
* Add Bluetooth and USB product IDs to device descriptors records.
|
||||
* Support Bluetooth-connected devices.
|
||||
* Add model ID and unit ID to device identification.
|
||||
* Support changing DPI by pressing DPI Switch button and sliding horizontally
|
||||
* Add device-specific notification handlers.
|
||||
* Add MX Vertical USB information.
|
||||
* Udev rule adds seat permissions for all Logitech devices.
|
||||
* Support USB-connected devices in GUI.
|
||||
* Make probe and config work for USB-connected devices.
|
||||
* Improve strings and display for settings.
|
||||
* Correctly handle non-unifying connection notifications.
|
||||
* Update GUI strings for several settings.
|
||||
* Better support for EX100 and devices that connect to it.
|
||||
* Partial support for feature GESTURE_2.
|
||||
* Simplify interface for settings.
|
||||
* Use DJ connection notifications to set device active status
|
||||
* Udev rule sets seat write permissions for hidraw nodes for device as well as receivers.
|
||||
* Handle USB devices that use HID++ protocol in CLI.
|
||||
* Use device hidraw nodes where possible.
|
||||
* Handle receivers with serial numbers that don't provide number of pairings.
|
||||
* Ignore exceptions when setting locale.
|
||||
* Correctly discover settings that share a name.
|
||||
* Don't show pop-up notifications at startup.
|
||||
* Keep battery voltage updated in GUI.
|
||||
* Add Portugese translation.
|
||||
* Update several translations.
|
||||
* Add Lightspeed receivers c545 and c541.
|
||||
* Reimplement REPROG_CONTROLS data structure.
|
||||
|
||||
1.0.3:
|
||||
* Clean up documentation files.
|
||||
* Update documentation on installation.
|
||||
* Update Swedish and French translations.
|
||||
* Add Norwegian Nynorsk and Danish translations.
|
||||
|
||||
1.0.3rc2:
|
||||
* Fix bug handling DJ pairing notifications.
|
||||
* Add Norwegian Bokmål translation.
|
||||
|
||||
1.0.3rc1:
|
||||
* Remove deprecated solaar-cli application.
|
||||
* Don't install udev or autostart files from python (or pip).
|
||||
* Solaar needs Python 3.6+ and probably needs kernel 5.2+
|
||||
* Handle exceptions on dynamic settings when device is not connected.
|
||||
* Fix inifinite loop on some low-level write errors
|
||||
* Add support for EX100 keyboard/mouse and receiver (046d:c517)
|
||||
* Add two settings for THUMB_WHEEL feature - inversion and reporting via HID++
|
||||
* Update German translation
|
||||
* Use REPORT RATE feature when available to determine polling rate.
|
||||
* Improve config command speed when not printing all settings
|
||||
* Improve config command handling and checking of arguments
|
||||
* Add setting for CHANGE_HOST feature
|
||||
* Add argument to settings for values that are not to persist
|
||||
* Add argument to settings to not wait for reply when writing a value to device
|
||||
* Add argument to not wait for reply from request to device
|
||||
* Add settings for MULTIPLATFORM and DUALPLATFORM features
|
||||
* Remove Logitech documents from documentation directory
|
||||
* Change config command to not read all settings when only printing or showing one
|
||||
* Display hosts info in 'solaar show' if device supports it
|
||||
* Remove non-working smooth-scrool from M510 v1
|
||||
* Add yapf and flake8 code style checks
|
||||
* Fix feature k375s Fn inversion
|
||||
* Update controls (keys and buttons) and tasks (actions)
|
||||
* Improved way to specify feature settings.
|
||||
* Don't abort on device notifications with unexpected device numbers, just warn.
|
||||
* Keep track of non-features so as not to ask device multiple times.
|
||||
* Implement KEYBOARD DISABLE KEYS feature.
|
||||
* Don't create notifications for DJ device activity reports.
|
||||
* Update a few special keys and actions.
|
||||
* Add keyed choice settings in configuration panel.
|
||||
* Support remappable keys from reprogrammable keys v4 feature.
|
||||
* Add setting class for keyed choice.
|
||||
* Only check for features once per device.
|
||||
* Use settings interface to show feature values in `solaar show` if no special code for feature.
|
||||
* Remove maximum window size.
|
||||
* Process battery voltage notifications.
|
||||
* Display battery voltage information in main window if regular battery information not available.
|
||||
* Show next battery level where available.
|
||||
* Update list of implemented features and provide information on how to implement features.
|
||||
* Add c53d as a Lightspeed receiver.
|
||||
|
||||
1.0.2:
|
||||
* Add usage document
|
||||
|
||||
1.0.2rc3:
|
||||
* Don't produce error dialog for inaccessible receivers with access control lists.
|
||||
* Add option --battery-icons=symbolic to use symbolic icons if available.
|
||||
* Update French translation
|
||||
* Update installation documentation
|
||||
|
||||
1.0.2rc2:
|
||||
* Remove packaging directory tree as it is not maintained
|
||||
* Pip installs udev rule and solaar autostart when doing install without --user flag
|
||||
* Use Solaar icon instead of a missing battery icon
|
||||
* Use only standard icons for battery levels. Symbolic icons do not change to white in dark themes because of problems external to Solaar.
|
||||
* Better reporting of battery levels when charging for some devices.
|
||||
* Add information on K600 TV, M350 WIPD 4080, and MX Keys
|
||||
* Remove assertion requiring receivers to still be in window when they are updated.
|
||||
* Augment long description of Solaar showing up in repositories.
|
||||
* Update installation directions.
|
||||
* Install udev rule as well as autostart file when doing system install.
|
||||
* Add support for Ayatana AppIndicator.
|
||||
* Use setuptools icon directory on system installs when not using pip.
|
||||
* Add receiver C517 and several older devices.
|
||||
* Improved translations for polish.
|
||||
* Bypass bug in appindicator when solaar is file in current directory.
|
||||
* Don't check that device kind matches feature kind.
|
||||
* Better determination of icons for battery levels.
|
||||
* Use Ayatana AppIndicator if available.
|
||||
* Improve error reporting when required system packages are not install.
|
||||
* Better tooltip description
|
||||
* Add release script to help when creating releases
|
||||
|
||||
1.0.2-rc1:
|
||||
* Look up tray icon filenames to get around a bug in libappindicator.
|
||||
* Make the default behavior be to show the main window at startup.
|
||||
* Support c537 nano receiver
|
||||
* Add logind signals for suspend/resume.
|
||||
* Remove solaar-gnome3 package
|
||||
* Ignore features for devices that don't follow feature specification
|
||||
* Add probe command to command-line interface to dump receiver registers
|
||||
* Don't terminate on malformed or unknown messages
|
||||
* Create fewer internal notifications for messages from devices
|
||||
* Add a button to the main window to terminate (quit) Solaar
|
||||
* Set up nano receivers as receivers with no unpairing and with re-pairing
|
||||
* Set up c534 as receiver with max 2 pairings, no unpairing, re-pairing
|
||||
* Better support receivers that do not unpair or when pairing replace existing pairings
|
||||
* Add information about receiver pairing to receiver data structure
|
||||
* Better support devices that only allow a limited number of total re-pairings
|
||||
* Add --window option to control main window visibility and tray usage
|
||||
* Ignore receiver if USB id is not retrieved
|
||||
* Fix bug with double deleting when devices are disconnected
|
||||
* Determine some receiver information from data structure for USB ids
|
||||
* Treat battery level of 0 as unknown
|
||||
* Fix bug on devices with no serial number
|
||||
* Drop support for python2, and use python3 throughout
|
||||
* Fix bug in remembering features discovered from device reports
|
||||
* Show icons in main window device list
|
||||
* Count offline devices when determining whether pairing is possible
|
||||
* Update French, Dutch, German, and Croation translations
|
||||
* Better icons for battery levels
|
||||
* Support DPI, Backlight 2, Battery Voltage features
|
||||
* Support M585, M590, M330, MX Master 2s and 3, new M310, new K800, craft keyboard
|
||||
* Documentation improvements
|
||||
* Clean up directory structure and remove unused files
|
||||
|
||||
1.0.1:
|
||||
* Updated the repo url.
|
||||
* Fixed typo which was crashing the application.
|
||||
* Improved the HID write routine which was causing issues on some devices.
|
||||
* Fix non-unifying receivers in Linux 5.2.
|
||||
* Add new Lightspeed receiver (used in the G305)
|
||||
|
||||
1.0.0:
|
||||
* Too many to track...
|
||||
|
||||
0.9.3:
|
||||
* Merged solaar-cli functionality into main solaar.
|
||||
* Scrolling over the systray icon switches between multiple peripherals.
|
||||
* Swedish translation courtesy of Daniel Zippert and Emelie Snecker
|
||||
* French translation courtesy of Papoteur, David Geiger and Damien Lallement.
|
||||
* Fixed some untranslated strings.
|
||||
|
||||
0.9.2:
|
||||
* Added support for hand detection on the K800.
|
||||
* Added support for V550 and V450 Nano.
|
||||
* Fixed side-scrolling wit the M705 Marathon.
|
||||
* Fixed side-scrolling with the M705 Marathon.
|
||||
* Fixed identification of the T650 Touchpad.
|
||||
* Added internationalization support and romanian translation.
|
||||
* Polish translation courtesy of Adrian Piotrowicz.
|
||||
@@ -11,7 +266,7 @@
|
||||
* Make sure devices in the window tree are sorted by registration index.
|
||||
* Added an autostart .desktop file.
|
||||
* Replaced single-instance code with GtkApplication.
|
||||
* Fixed indentification of the M505 mouse.
|
||||
* Fixed identification of the M505 mouse.
|
||||
* Fixed an occasional windowing layout bug with the C52F Nano Receiver.
|
||||
|
||||
0.9.0:
|
||||
|
||||
88
README.md
@@ -1,88 +0,0 @@
|
||||
**Solaar** is a Linux device manager for Logitech's [Unifying Receiver][unifying]
|
||||
peripherals. It is able to pair/unpair devices to the receiver, and for most
|
||||
devices read battery status.
|
||||
|
||||
It comes in two flavors, command-line and GUI. Both are able to list the
|
||||
devices paired to a Unifying Receiver, show detailed info for each device, and
|
||||
also pair/unpair supported devices with the receiver.
|
||||
|
||||
[unifying]: http://logitech.com/en-us/66/6079
|
||||
|
||||
## Supported Devices
|
||||
|
||||
**Solaar** will detect all devices paired with your Unifying Receiver, and at
|
||||
the very least display some basic information about them.
|
||||
|
||||
For some devices, extra settings (usually not available through the standard
|
||||
Linux system configuration) are supported. For a full list of supported devices
|
||||
and their features, see [docs/devices.md](docs/devices.md).
|
||||
|
||||
|
||||
## Pre-built packages
|
||||
|
||||
Pre-built packages are available for a few Linux distros.
|
||||
|
||||
* Debian 7 (Wheezy) or higher: packages in this [repository](docs/debian.md)
|
||||
* Ubuntu/Kubuntu 12.04+: [ppa:daniel.pavel/solaar][ppa]
|
||||
|
||||
The `solaar` package uses a standard system tray implementation; to ensure
|
||||
integration with *gnome-shell* or *Unity*, install `solaar-gnome3`.
|
||||
|
||||
* a [Gentoo overlay][gentoo], courtesy of Carlos Silva
|
||||
* an [OpenSUSE rpm][opensuse], courtesy of Mathias Homann
|
||||
* an [Arch package][arch], courtesy of Arnaud Taffanel
|
||||
|
||||
[ppa]: http://launchpad.net/~daniel.pavel/+archive/solaar
|
||||
[gentoo]: http://code.r3pek.org/gentoo-overlay/src
|
||||
[opensuse]: http://software.opensuse.org/package/Solaar
|
||||
[arch]: http://aur.archlinux.org/packages/solaar
|
||||
|
||||
|
||||
## Manual installation
|
||||
|
||||
See [docs/installation.md](docs/installation.md) for the step-by-step
|
||||
procedure for manual installation.
|
||||
|
||||
|
||||
## Known Issues
|
||||
|
||||
- KDE/Kubuntu: if some icons appear broken in the application, make sure you've
|
||||
properly configured the Gtk theme and icon theme in KDE's control panel.
|
||||
|
||||
- Some devices using the [Nano Receiver][nano] (which is very similar to the
|
||||
Unifying Receiver) are supported, but not all. For details, see
|
||||
[docs/devices.md](docs/devices.md).
|
||||
|
||||
- Running the command-line application (`bin/solaar-cli`) while the GUI
|
||||
application is also running *may* occasionally cause either of them to become
|
||||
confused about the state of the devices. I haven't encountered this often
|
||||
enough to be able to be able to diagnose it properly yet.
|
||||
|
||||
[nano]: http://logitech.com/mice-pointers/articles/5926
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This software is distributed under the terms of the
|
||||
[GNU Public License, v2](COPYING).
|
||||
|
||||
|
||||
## Thanks
|
||||
|
||||
This project began as a third-hand clone of [Noah K. Tilton](https://github.com/noah)'s
|
||||
logitech-solar-k750 project on GitHub (no longer available). It was developed
|
||||
further thanks to the diggings in Logitech's HID++ protocol done by many other
|
||||
people:
|
||||
|
||||
- [Julien Danjou](http://julien.danjou.info/blog/2012/logitech-k750-linux-support),
|
||||
who also provided some internal
|
||||
[Logitech documentation](http://julien.danjou.info/blog/2012/logitech-unifying-upower)
|
||||
- [Lars-Dominik Braun](http://6xq.net/git/lars/lshidpp.git)
|
||||
- [Alexander Hofbauer](http://derhofbauer.at/blog/blog/2012/08/28/logitech-performance-mx)
|
||||
- [Clach04](http://bitbucket.org/clach04/logitech-unifying-receiver-tools)
|
||||
- [Peter Wu](https://lekensteyn.nl/logitech-unifying.html)
|
||||
- [Nestor Lopez Casado](http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28)
|
||||
provided some more Logitech specifications for the HID++ protocol
|
||||
|
||||
Also thanks to Douglas Wagner, Julien Gascard and Peter Wu for helping with
|
||||
application testing and supporting new devices.
|
||||
16
RELEASE.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Solaar releases
|
||||
|
||||
## Please read before making a release
|
||||
|
||||
We support two type of releases: normal releases (ex. `1.0.0`) and release
|
||||
candidates (ex. `1.0.0rc1`). Release candidates must have a `rcX` suffix.
|
||||
|
||||
Release routine:
|
||||
|
||||
- Update ChangeLog, setup.py, lib/solaar/__init__.py, and docs/_config.yml to the new version
|
||||
- Create a commit that starts with `release VERSION`
|
||||
- Push commit to Solaar repository
|
||||
- Invoke `./release.sh`
|
||||
- Git tags are signed so you must have GPG set up
|
||||
- You are required to have a github token with `public_repo` access
|
||||
in `~/.github-token`
|
||||
57
Release_Notes
Normal file
@@ -0,0 +1,57 @@
|
||||
Notes on Major Changes in Releases
|
||||
|
||||
|
||||
Version 1.0.7:
|
||||
|
||||
Solaar rules can now trigger on both pressing and releasing a diverted key.
|
||||
|
||||
The new rule condition MouseProcess is like the Process condition except for the process of the window under the mouse.
|
||||
|
||||
Mouse gestures have been upgraded. A mouse gesture is now a sequence of movements separated by no movement periods while the mouse gesture button is held down. The MouseGesture rule condition matches mouse gesture sequences. The old mouse-up, etc., tests are converted to MouseGesture conditions.
|
||||
|
||||
|
||||
Version 1.0.6:
|
||||
|
||||
The sliding DPI setting now looks for suitable keys to use to trigger its effects.
|
||||
|
||||
If a mouse has a suitable button it can generate mouse gestures, which trigger rule processing. Mouse gestures need to be turned on and the button diverted to produce mouse gestures.
|
||||
|
||||
Settings can now be ignored by clicking on the icon at the right-hand edge of a setting until the dialog error icon (usually a red icon) appears. Solaar will not try to restore the value for an ignored setting.
|
||||
|
||||
Icon handling in the tray and the tray menu has been updated to work better with some system tray implementations.
|
||||
|
||||
The process rule condition also matches against the current X11 WM_CLASS.
|
||||
|
||||
The SMART SHIFT ENHANCED feature is supported.
|
||||
|
||||
|
||||
Version 1.0.5:
|
||||
|
||||
Solaar has rules that can perform actions such as pressing keys or scrolling when certain HID++ feature notifications happen. Users can change these rules either by editing ~/.config/solaar/rules.yaml or via a GUI. Rules depend on X11 and so are only available under X11. This is an experimental feature for Solaar and may undergo changes in the future.
|
||||
|
||||
Each setting has a clickable lock icon that determines whether the setting can be changed.
|
||||
|
||||
|
||||
Version 1.0.4:
|
||||
|
||||
Devices that connect directly via Bluetooth or USB are now supported. These devices show up in the GUI as separate lines, not under a receiver. A device that is directly connected and also paired to a receiver will show up twice, but the entry under the receiver will not be active. With this change identifying devices becomes more difficult so occasionally check your Solaar configuration file (at ~/.config/solaar/config.json) to see that there is only one entry for each of your devices.
|
||||
|
||||
There are new settings for gestures, thumb wheels, adjusting the wheel ratchet behavior, and changing DPI using a DPI Switch button.
|
||||
|
||||
Solaar's Udev rule now adds seat permissions for all Logitech devices. Users who install Solaar themselves will have to install the new Udev rule and activate the rule. One way to do this is to restart the user's computer.
|
||||
|
||||
|
||||
Version 1.0.3:
|
||||
|
||||
The separate deprecated solaar-cli command has been removed.
|
||||
|
||||
Devices can be switched between hosts using the Change Host setting. The device will try to connect to the other host. Some devices will detect that there is no active host on the other connections and reconnect back.
|
||||
|
||||
|
||||
Version 1.0.2:
|
||||
|
||||
The separate unneeded solaar-gnome3 command has been removed. The packaging directories have been removed.
|
||||
|
||||
Non-unifying receivers are modelled better. Many of them cannot unpair but instead new pairings replace existing pairings.
|
||||
|
||||
Battery icon selection has been simplified.
|
||||
50
bin/solaar
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- python-mode -*-
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
@@ -22,23 +22,39 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
|
||||
def init_paths():
|
||||
"""Make the app work in the source tree."""
|
||||
import sys
|
||||
import os.path as _path
|
||||
"""Make the app work in the source tree."""
|
||||
import sys
|
||||
import os.path as _path
|
||||
|
||||
prefix = _path.normpath(_path.join(_path.realpath(sys.path[0]), '..'))
|
||||
src_lib = _path.join(prefix, 'lib')
|
||||
share_lib = _path.join(prefix, 'share', 'solaar', 'lib')
|
||||
for location in src_lib, share_lib:
|
||||
init_py = _path.join(location, 'solaar', '__init__.py')
|
||||
# print ("sys.path[0]: checking", init_py)
|
||||
if _path.exists(init_py):
|
||||
# print ("sys.path[0]: found", location, "replacing", sys.path[0])
|
||||
sys.path[0] = location
|
||||
break
|
||||
# Python 2 need conversion from utf-8 filenames
|
||||
# Python 3 might have problems converting back to UTF-8 in case of Unicode surrogates
|
||||
try:
|
||||
if sys.version_info < (3, ):
|
||||
decoded_path = sys.path[0].decode(sys.getfilesystemencoding())
|
||||
else:
|
||||
decoded_path = sys.path[0]
|
||||
sys.path[0].encode(sys.getfilesystemencoding())
|
||||
|
||||
except UnicodeError:
|
||||
sys.stderr.write(
|
||||
'ERROR: Solaar cannot recognize encoding of filesystem path, '
|
||||
'this may happen because non UTF-8 characters in the pathname.\n'
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
prefix = _path.normpath(_path.join(_path.realpath(decoded_path), '..'))
|
||||
src_lib = _path.join(prefix, 'lib')
|
||||
share_lib = _path.join(prefix, 'share', 'solaar', 'lib')
|
||||
for location in src_lib, share_lib:
|
||||
init_py = _path.join(location, 'solaar', '__init__.py')
|
||||
# print ("sys.path[0]: checking", init_py)
|
||||
if _path.exists(init_py):
|
||||
# print ("sys.path[0]: found", location, "replacing", sys.path[0])
|
||||
sys.path[0] = location
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
init_paths()
|
||||
import solaar.gtk
|
||||
solaar.gtk.main()
|
||||
init_paths()
|
||||
import solaar.gtk
|
||||
solaar.gtk.main()
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- python-mode -*-
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
## Copyright (C) 2012-2013 Daniel Pavel
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
|
||||
def init_paths():
|
||||
"""Make the app work in the source tree."""
|
||||
import sys
|
||||
import os.path as _path
|
||||
|
||||
prefix = _path.normpath(_path.join(_path.realpath(sys.path[0]), '..'))
|
||||
src_lib = _path.join(prefix, 'lib')
|
||||
share_lib = _path.join(prefix, 'share', 'solaar', 'lib')
|
||||
for location in src_lib, share_lib:
|
||||
init_py = _path.join(location, 'solaar', '__init__.py')
|
||||
if _path.exists(init_py):
|
||||
sys.path[0] = location
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
init_paths()
|
||||
import solaar.cli
|
||||
solaar.cli.main()
|
||||
6
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# ignore documentation-specific files
|
||||
|
||||
.jekyll-metadata
|
||||
Gemfile
|
||||
Gemfile.lock
|
||||
_site/
|
||||
4
docs/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Documentation Readme
|
||||
|
||||
This project's documentation is hosted using GitHub pages, which serves static pages using Jekyll.
|
||||
[Please refer to the official documentation for instructions for how to build the site locally.](https://help.github.com/articles/setting-up-your-github-pages-site-locally-with-jekyll/)
|
||||
BIN
docs/Solaar-main-window-back-divert.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
docs/Solaar-main-window-button-actions.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
docs/Solaar-main-window-keyboard.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
docs/Solaar-main-window-mouse.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
docs/Solaar-main-window-offline.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
docs/Solaar-main-window-receiver.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
docs/Solaar-menu.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
docs/Solaar-rule-editor.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
10
docs/_config.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
title: Solaar
|
||||
description: Linux Device Manager for Logitech Unifying Receivers and Devices.
|
||||
tagline: Linux Device Manager for Logitech Unifying Receivers and Devices.
|
||||
owner: pwr-Solaar
|
||||
owner_url: https://github.com/pwr-Solaar
|
||||
repository: pwr-Solaar/Solaar
|
||||
version: 1.0.7
|
||||
show_downloads: false
|
||||
encoding: utf-8
|
||||
theme: jekyll-theme-slate
|
||||
53
docs/_layouts/default.html
Normal file
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ site.lang | default: "en-US" }}">
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,maximum-scale=2">
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}">
|
||||
<link rel="icon" type="image/png" href="{{ site.baseurl }}/assets/favicon.png" />
|
||||
|
||||
{% seo %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div id="header_wrap" class="outer">
|
||||
<header class="inner">
|
||||
{% if site.github.is_project_page %}
|
||||
<a id="forkme_banner" href="{{ site.github.repository_url }}">View on GitHub</a>
|
||||
{% endif %}
|
||||
<h1 id="project_title">
|
||||
<img src="{{ site.baseurl }}/assets/solaar.svg" style="margin-bottom: -10px; width: 48px; height: 48px; border: 0; box-shadow: none;" />
|
||||
{{ site.title | default: site.github.repository_name }}</h1>
|
||||
<h2 id="project_tagline">{{ site.description | default: site.github.project_tagline }}</h2>
|
||||
|
||||
{% if site.show_downloads %}
|
||||
<section id="downloads">
|
||||
<a class="zip_download_link" href="{{ site.github.zip_url }}">Download this project as a .zip file</a>
|
||||
<a class="tar_download_link" href="{{ site.github.tar_url }}">Download this project as a tar.gz file</a>
|
||||
</section>
|
||||
{% endif %}
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
<div id="main_content_wrap" class="outer">
|
||||
<section id="main_content" class="inner">
|
||||
{{ content }}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div id="footer_wrap" class="outer">
|
||||
<footer class="inner">
|
||||
{% if site.github.is_project_page %}
|
||||
<p class="copyright">{{ site.title | default: site.github.repository_name }} maintained by <a href="{{ site.github.owner_url }}">{{ site.github.owner_name }}</a></p>
|
||||
{% endif %}
|
||||
<p>Published with <a href="https://pages.github.com">GitHub Pages</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
47
docs/_layouts/page.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ site.lang | default: "en-US" }}">
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,maximum-scale=2">
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}">
|
||||
<link rel="icon" type="image/png" href="{{ site.baseurl }}/assets/favicon.png" />
|
||||
{% seo %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div id="header_wrap" class="outer">
|
||||
<header class="inner">
|
||||
{% if site.github.is_project_page %}
|
||||
<a id="forkme_banner" href="{{ site.github.repository_url }}">View on GitHub</a>
|
||||
{% endif %}
|
||||
|
||||
<h1 id="project_title">
|
||||
<a href="{{ site.baseurl }}" style="color: #fff;">
|
||||
<img src="{{ site.baseurl }}/assets/solaar.svg" style="margin-bottom: -10px; width: 48px; height: 48px; border: 0; box-shadow: none;" />
|
||||
{{ site.title | default: site.github.repository_name }}</h1>
|
||||
</a>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
<div id="main_content_wrap" class="outer">
|
||||
<section id="main_content" class="inner">
|
||||
{{ content }}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div id="footer_wrap" class="outer">
|
||||
<footer class="inner">
|
||||
{% if site.github.is_project_page %}
|
||||
<p class="copyright">{{ site.title | default: site.github.repository_name }} maintained by <a href="{{ site.github.owner_url }}">{{ site.github.owner_name }}</a></p>
|
||||
{% endif %}
|
||||
<p>Published with <a href="https://pages.github.com">GitHub Pages</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
docs/assets/favicon.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
100
docs/assets/solaar.svg
Normal file
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="solaar.svg"
|
||||
inkscape:export-filename="/home/chris/Git/Solaar/_includes/favicon.png"
|
||||
inkscape:export-xdpi="66.666672"
|
||||
inkscape:export-ydpi="66.666672"
|
||||
width="43.200001"
|
||||
height="43.200001">
|
||||
<metadata
|
||||
id="metadata21">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1807"
|
||||
inkscape:window-height="797"
|
||||
id="namedview19"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="2.36"
|
||||
inkscape:cx="1.4135593"
|
||||
inkscape:cy="-4.4"
|
||||
inkscape:window-x="3000"
|
||||
inkscape:window-y="840"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="gradient_blue">
|
||||
<stop
|
||||
style="stop-color:#009099;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop7" />
|
||||
<stop
|
||||
style="stop-color:#00a899;stop-opacity:0.9"
|
||||
offset="1"
|
||||
id="stop9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="5"
|
||||
y1="50"
|
||||
x2="95"
|
||||
y2="50"
|
||||
id="gradient_rect"
|
||||
xlink:href="#gradient_blue"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
x1="37"
|
||||
y1="50"
|
||||
x2="63"
|
||||
y2="50"
|
||||
id="gradient_dot"
|
||||
xlink:href="#gradient_blue"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<g
|
||||
transform="matrix(0.48,0,0,0.48,-2.4,-2.4)"
|
||||
id="g13">
|
||||
<path
|
||||
d="m 21.5,5.5 c -8.864,0 -16,7.136 -16,16 l 0,57 c 0,8.864 7.136,16 16,16 l 57,0 c 8.864,0 16,-7.136 16,-16 l 0,-57 c 0,-8.864 -7.136,-16 -16,-16 l -57,0 z m 16.1875,11.1875 9.03125,15.625 C 47.784179,32.115965 48.877705,32 50,32 c 1.122295,0 2.215821,0.115965 3.28125,0.3125 l 9.03125,-15.625 10.375,6 -9.03125,15.625 C 65.078123,39.972287 66.191785,41.898777 66.9375,44 L 85,44 85,56 66.9375,56 c -0.745715,2.101223 -1.859377,4.027713 -3.28125,5.6875 l 9.03125,15.625 -10.375,6 -9.03125,-15.625 C 52.215821,67.884035 51.122295,68 50,68 48.877705,68 47.784179,67.884035 46.71875,67.6875 l -9.03125,15.625 -10.375,-6 9.03125,-15.625 C 34.921877,60.027713 33.808215,58.101223 33.0625,56 L 15,56 15,44 33.0625,44 c 0.745715,-2.101223 1.859377,-4.027713 3.28125,-5.6875 l -9.03125,-15.625 10.375,-6 z"
|
||||
style="fill:url(#gradient_rect);fill-opacity:1;fill-rule:nonzero;stroke:#16161d;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
|
||||
id="path15"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 62,50 a 12,12 0 1 1 -24,0 12,12 0 1 1 24,0 z"
|
||||
style="fill:url(#gradient_dot);fill-opacity:1;fill-rule:nonzero;stroke:#16161d;stroke-opacity:1"
|
||||
id="path17"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
207
docs/capabilities.md
Normal file
@@ -0,0 +1,207 @@
|
||||
---
|
||||
title: Solaar Capabilities
|
||||
layout: page
|
||||
---
|
||||
|
||||
# Solaar Capabilities
|
||||
|
||||
[**Solaar**][solaar] reports on and controls [Logitech][logitech] devices
|
||||
(keyboards, mice, and trackballs) that connect to your computer via a
|
||||
Logitech USB receiver (a very small piece of hardware that plugs into one of
|
||||
your USB ports).
|
||||
Solaar is designed to detect all connected devices,
|
||||
and at the very least display some basic information about them.
|
||||
At this moment, all [Unifying][unifying] receivers are supported (e.g., devices
|
||||
with USB ID `046d:c52b` or `046d:c532`) as are several Lightspeed Receivers
|
||||
and many Nano receivers.
|
||||
|
||||
Solaar also reports on and controls some Logitech devices that directly connect
|
||||
to your computer using a USB cable or via Bluetooth.
|
||||
Not all such devices supported in Solaar as information needs to be added to Solaar
|
||||
for each device type that directly connects.
|
||||
|
||||
|
||||
## HID++
|
||||
|
||||
The devices that Solaar handles use Logitech's HID++ protocol.
|
||||
|
||||
HID++ is a Logitech-proprietary protocol that extends the standard HID
|
||||
protocol for interfacing with receivers, keyboards, mice, and so on. It allows
|
||||
Logitech receivers to communicate with multiple devices and modify some
|
||||
features of the device. As the HID++ protocol is
|
||||
proprietary, many aspects of it are unknown. Some information about HID++
|
||||
has been obtained from Logitech but even that is subject to change and
|
||||
extension.
|
||||
|
||||
There are several versions of HID++ and many Logitech
|
||||
receivers and devices that utilize it. Different receivers and devices
|
||||
implement different portions of HID++ so even if two devices appear to be
|
||||
the same in both physical appearance and behavior they may work
|
||||
differently underneath. (For example, there are versions of the
|
||||
M510 mouse that use different versions of the HID++ protocol.)
|
||||
Contrariwise, two different devices may appear different physically but
|
||||
actually look the same to software. (For example, some M185 mice look the
|
||||
same to software as some M310 mice.)
|
||||
|
||||
The software identity of a receiver can be determined by its USB product ID
|
||||
(reported by Solaar and also viewable in Linux using `lsusb`). The software
|
||||
identity of a device that connects to a receiver can be determined by
|
||||
its wireless PID as reported by Solaar. The software identity of devices that
|
||||
connect via a USB cable or via bluetooth can be determined by their USB or
|
||||
Bluetooth product ID.
|
||||
|
||||
Even something as fundamental as pairing works differently for different
|
||||
receivers. For Unifying receivers, pairing adds a new paired device, but
|
||||
only if there is an open slot on the receiver. So these receivers need to
|
||||
be able to unpair devices that they have been paired with or else they will
|
||||
not have any open slots for pairing. Some other receivers, like the
|
||||
Nano receiver with USB ID `046d:c534`, can only pair with particular kinds of
|
||||
devices and pairing a new device replaces whatever device of that kind was
|
||||
previously paired to the receiver. These receivers cannot unpair. Further,
|
||||
some receivers can pair an unlimited number of times but others can only
|
||||
pair a limited number of times.
|
||||
|
||||
Only some connections between receivers and devices are possible. In should
|
||||
be possible to connect any device with a Unifying logo on it to any receiver
|
||||
with a Unifying logo on it. Receivers without the Unifying logo probably
|
||||
can connect only to the kind of devices they were bought with and devices
|
||||
without the Unifying logo can probably only connect to the kind of receiver
|
||||
that they were bought with.
|
||||
|
||||
|
||||
## Supported Features
|
||||
|
||||
Solaar uses the HID++ protocol to pair devices to receivers and unpair
|
||||
devices from receivers, and also uses the HID++ protocol to display
|
||||
features of receivers and devices. Currently it only displays some
|
||||
features, and can modify even fewer. For a list of HID++ features
|
||||
and their support see [the features page](features).
|
||||
|
||||
Solaar does not do much beyond using the HID++ protocol to change the
|
||||
behavior of receivers and devices via changing their settings.
|
||||
In particular, Solaar cannot change how
|
||||
the operating system turns the keycodes that a keyboard produces into
|
||||
characters that are sent to programs. That is the province of HID device
|
||||
drivers and other software (such as X11).
|
||||
|
||||
Settings can only be changed in the Solaar GUI when they are unlocked.
|
||||
To unlock a setting click on the icon at the right-hand edge of the setting
|
||||
until an unlocked lock appears (with tooltop "Changes allowed").
|
||||
|
||||
Solaar keep tracks of the changeable settings of a device.
|
||||
Most devices forget changed settings when the are turned off
|
||||
or go into a power-saving mode. When Solaar starts, it restores on-line
|
||||
devices to their previously-known state, and while running it restores
|
||||
devices to their previously-known state when the device itself comes on line.
|
||||
This information is stored in the file `~/.config/solaar/config.json`.
|
||||
|
||||
Updating of settings can be turned off in the Solaar GUI by clicking on the icon
|
||||
at the right-hand edge of the setting until a red icon appears (with tooltip
|
||||
"Ignore this setting" ).
|
||||
|
||||
Solaar keeps track of settings independently on each computer.
|
||||
As a result if a device is switched between different computers
|
||||
Solaar may apply different settings for it on the different computers
|
||||
|
||||
Querying a device for its current state can require quite a few HID++
|
||||
interactions. These interactions can temporarily slow down the device, so
|
||||
Solaar tries to internally cache information about devices while it is
|
||||
running. If the device
|
||||
state is changed by some other means, even sometimes by another invocation
|
||||
of Solaar, this cached information may become incorrect. Currently there is
|
||||
no way to force an update of the cached information besides restarting Solaar.
|
||||
|
||||
Logitech receivers and devices have firmware in them. Some firmware
|
||||
can be updated using Logitech software in Windows. For example, there are
|
||||
security issues with some Logitech receivers and devices and Logitech has
|
||||
firmware updates to alleviate these problems. Some Logitech firmware can
|
||||
also be updated in Linux using `fwupdmgr`.
|
||||
WARNING: Updating firmware can cause a piece of hardware to become
|
||||
permanently non-functional if something goes wrong with the update or the
|
||||
update installs the wrong firmware.
|
||||
|
||||
## Other Solaar Capabilities
|
||||
|
||||
Solaar has a few capabilities that go beyond simply changing device settings.
|
||||
|
||||
### Rule-based Processing of HID++ Notifications
|
||||
|
||||
Solaar can process HID++ Notifications from devices to, for example,
|
||||
change the speed of some thumb wheels. These notifications are only sent
|
||||
for actions that are set in Solaar to their HID++ setting (also known as diverted).
|
||||
For more information on this capability of Solaar see
|
||||
[the rules page](https://pwr-solaar.github.io/Solaar/rules). As much of rule processing
|
||||
depends on X11, this capability is only when running under X11.
|
||||
|
||||
Users can edit rules using a GUI by clicking on the `Edit Rule` button in the Solaar main window.
|
||||
|
||||
Solaar rules is an experimental feature. Significant changes might be made in response to problems.
|
||||
|
||||
### Sliding DPI
|
||||
|
||||
A few mice (such as the MX Vertical) have a button that is supposed to be used to change
|
||||
the sensitivity (DPI) of the mouse by pressing the button and moving the mouse left and right.
|
||||
Other mice (such as the MX Master 3) don't have a button specific for this purpose
|
||||
but have buttons that can be used for it.
|
||||
|
||||
The DPI Sliding Adjustment setting assigns a button for this purpose.
|
||||
Pressing the button, if the button is diverted, causes the mouse pointer to stop moving.
|
||||
When the button is released a new Sensitivity (DPI) value is applied to the mouse,
|
||||
depending on how far right or left the mouse is moved. If the mouse is moved only a little bit
|
||||
the previous value that was set is applied to the mouse.
|
||||
Notifications from Solaar are displayed while the mouse button is done
|
||||
showing the setting that will be applied.
|
||||
|
||||
|
||||
### Mouse Gestures
|
||||
|
||||
Some mice (such as the MX Master 3) have a button that is supposed to be used to
|
||||
create up/down/left/right mouse gestures. Other mice (such as the MX Vertical) don't
|
||||
have a button specific for this purpose but have buttons that can be used for it.
|
||||
|
||||
The Mouse Gestures setting assigns a button for this purpose.
|
||||
Pressing the button, if the button is diverted, causes the mouse pointer to stop moving.
|
||||
When the button is released a MOUSE_GESTURE notification with the total mouse movement
|
||||
while the button was pressed is sent to the Solaar rule system.
|
||||
|
||||
Mouse gestures is an experimental feature.
|
||||
Significant changes might be made to it in the future.
|
||||
|
||||
|
||||
## System Tray
|
||||
|
||||
Solaar's GUI normally uses an icon in the system tray.
|
||||
This allows users to close Solaar and reopen from the tray.
|
||||
This aspect of Solaar depends on having an active system tray which may
|
||||
require some special setup when using Gnome, particularly under Wayland.
|
||||
|
||||
If you are running gnome, you most likely need the
|
||||
`gnome-shell-extension-appindicator` package installed.
|
||||
In Fedora, this can be done by running
|
||||
```
|
||||
sudo dnf install gnome-shell-extension-appindicator
|
||||
```
|
||||
The likely command in Ubuntu and related distributions is
|
||||
```
|
||||
sudo apt install gnome-shell-extension-appindicator
|
||||
```
|
||||
You may have to log out and log in again before the system tray shows up.
|
||||
|
||||
|
||||
## Battery Icons
|
||||
|
||||
For many devices, Solaar shows the approximate battery level via icons that
|
||||
show up in both the main window and the system tray. In previous versions
|
||||
several heuristics to determine which icon names to use for this purpose,
|
||||
but as more and more battery icon schemes have been developed this has
|
||||
become impossible to do well. Solaar now uses the eleven standard
|
||||
battery icon names `battery-{full,good,low,critical,empty}[-charging]` and
|
||||
`battery-missing`.
|
||||
|
||||
Solaar will use the symbolic versions of these icons if started with the
|
||||
option `--battery-icons=symbolic`. Because of external bugs,
|
||||
these symbolic icons may be nearly invisible in dark themes.
|
||||
|
||||
[solaar]: https://github.com/pwr-Solaar/Solaar
|
||||
[logitech]: https://www.logitech.com
|
||||
[unifying]: https://en.wikipedia.org/wiki/Logitech_Unifying_receiver
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
title: Debian Repository
|
||||
layout: page
|
||||
---
|
||||
|
||||
# Debian repository
|
||||
|
||||
To use this repository with your Debian machine, create a file `solaar.list` in
|
||||
`/etc/apt/sources.list.d/`, with the following contents:
|
||||
|
||||
deb http://pwr.github.io/Solaar/packages/ ./
|
||||
deb-src http://pwr.github.io/Solaar/packages/ ./
|
||||
Solaar is now part of the [official Debian repository](https://packages.debian.org/solaar). To install it on your Debian machine, use the following command: `sudo apt install solaar`
|
||||
|
||||
306
docs/devices.md
@@ -1,49 +1,19 @@
|
||||
# Supported devices
|
||||
---
|
||||
title: Supported Devices
|
||||
layout: page
|
||||
---
|
||||
|
||||
**Solaar** will detect all devices paired with your receiver, and at the very
|
||||
least display some basic information about them.
|
||||
# Supported devices and receivers
|
||||
|
||||
At this moment, all [Unifying Receiver][unifying] are supported (devices with
|
||||
USB ID `046d:c52b` or `046d:c532`), but only some newer [Nano Receiver][nano]s
|
||||
(devices with USB ID `046d:c52f`). You can check your connected Logitech devices
|
||||
by running `lsusb -d 046d:` in a console.
|
||||
These tables provide a partial list of supported Logitech receivers and
|
||||
devices and to what degree their
|
||||
features are supported by Solaar. The information in these tables is
|
||||
based on what devices users have been able to test Solaar with.
|
||||
|
||||
For some devices, extra settings (usually not available through the standard
|
||||
Linux system configuration) are supported:
|
||||
|
||||
* The [K750 Solar Keyboard][K750] is also queried for its solar charge status.
|
||||
Pressing the `Light-Check` button on the keyboard will pop-up the application
|
||||
window and display the current lighting value (Lux) as reported by the
|
||||
keyboard, similar to Logitech's *Solar.app* for Windows.
|
||||
|
||||
* The state of the `FN` key can be toggled on some keyboards ([K360][K360],
|
||||
[MK700][K700], [K750][K750] and [K800][K800]). It changes the way the function
|
||||
keys (`F1`..`F12`) work, i.e. whether holding `FN` while pressing the function
|
||||
keys will generate the standard `Fx` keycodes or the special function (yellow
|
||||
icons) keycodes.
|
||||
|
||||
* The DPI can be changed on the [Performance MX Mouse][P_MX].
|
||||
|
||||
* Smooth scrolling (higher sensitivity on vertical scrolling with the wheel) can
|
||||
be toggled on the [M705 Marathon Mouse][M705] and [M510 Wireless Mouse][M510].
|
||||
|
||||
|
||||
# Supported features
|
||||
|
||||
These tables list all known Logitech [Unifying][unifying] devices, and to what
|
||||
degree their features are supported by Solaar. If your device is not listed here
|
||||
at all, it is very unlikely Solaar would be able to support it.
|
||||
|
||||
The information in these tables is incomplete, based on what devices myself and
|
||||
other users have been able to test Solaar with. If your device works with
|
||||
Solaar, but its supported features are not specified here, I would love to hear
|
||||
about it.
|
||||
|
||||
|
||||
Devices marked with an asterisk (*) use a Nano receiver that knows the Unifying
|
||||
protocol, and should be fully supported by Solaar.
|
||||
|
||||
The HID++ column specifies the device's HID++ version.
|
||||
The HID++ column specifies the device's HID++ version. Some devices report
|
||||
version 4.5, but that is the same as version 2.0 as listed here.
|
||||
For devices what support HID++ 2.0 or greater, Solaar is able to discover
|
||||
the features the device supports.
|
||||
|
||||
The Battery column specifies if Solaar is able to read the device's battery
|
||||
level.
|
||||
@@ -51,89 +21,217 @@ level.
|
||||
For mice, the DPI column specifies if the mouse's sensitivity is fixed (`-`),
|
||||
can only be read (`R`), or can be read and changed by Solaar (`R/W`).
|
||||
|
||||
The reprog(rammable) keys feature is currently not fully supported by Solaar.
|
||||
You are able to read this feature using solaar-cli, but it is not possible to
|
||||
assign different keys.
|
||||
## Adding new receivers and devices
|
||||
|
||||
Adding a new receiver requires knowing whether the receiver is a regular
|
||||
Unifying receiver, a nano receiver, or a lightspeed receiver. Add a line to
|
||||
`../lib/logitech_receiver/base_usb.py` defining the receiver as one of these.
|
||||
If the receiver has an unusual number of pairing slots, then this also needs
|
||||
to be specified. Then add the receiver to the tuple of receivers (ALL).
|
||||
|
||||
Most new devices do not need to be known to Solaar to work. However, an
|
||||
entry in `lib/logitech-receiver/descriptors.py` can provide a better name for
|
||||
the device and a feature list can speed up Solaar startup a bit. The
|
||||
arguments to the _D function are the device's long name, its short name
|
||||
(codename), its HID++ protocol version, its wireless product ID (wpid), and
|
||||
a tuple of known feature settings (from `lib/logitech/settings_templates.py`).
|
||||
If the device can connect via a USB cable its USB product ID should be included.
|
||||
If the device can connect via Bluetooth its Bluetooth product ID should be included.
|
||||
|
||||
Some Logitech devices are not suited for use in Solaar. If the device only
|
||||
connects via a USB cable Solaar might not be able to do much or anything
|
||||
with the device so it may not be useful to add it to Solaar. One example is
|
||||
the MX518 Gaming Mouse.
|
||||
|
||||
|
||||
Keyboards:
|
||||
### Receivers
|
||||
|
||||
| Device | HID++ | Battery | Other supported features |
|
||||
|------------------|-------|---------|-----------------------------------------|
|
||||
| K230 | 2.0 | yes | |
|
||||
| K270 | | | |
|
||||
| K340 | | | |
|
||||
| K350 | | | |
|
||||
| K360 | 2.0 | yes | FN swap, reprog keys |
|
||||
| K400 Touch | 2.0 | yes | |
|
||||
| K750 Solar | 2.0 | yes | FN swap, Lux reading, light button |
|
||||
| K800 Illuminated | 1.0 | yes | FN swap, reprog keys |
|
||||
| MK700 | 1.0 | yes | FN swap, reprog keys |
|
||||
| USB ID | Kind | Max Paired Devices |
|
||||
------------|------------|--------------------|
|
||||
| 046d:c517 | 27MHz old | 4 |
|
||||
| 046d:c518 | Nano | 1 |
|
||||
| 046d:c51a | Nano | 1 |
|
||||
| 046d:c51b | Nano | 1 |
|
||||
| 046d:c521 | Nano | 1 |
|
||||
| 046d:c525 | Nano | 1 |
|
||||
| 046d:c526 | Nano | 1 |
|
||||
| 046d:c52b | Unifying | 6 |
|
||||
| 046d:c52e | Nano | 1 |
|
||||
| 046d:c52f | Nano | 1 |
|
||||
| 046d:c531 | Nano | 1 |
|
||||
| 046d:c532 | Unifying | 6 |
|
||||
| 046d:c534 | Nano | 2 |
|
||||
| 046d:c539 | Lightspeed | 1 |
|
||||
| 046d:c53a | Lightspeed | 1 |
|
||||
| 046d:c53d | Lightspeed | 1 |
|
||||
| 046d:c53f | Lightspeed | 1 |
|
||||
| 046d:c545 | Lightspeed | 1 |
|
||||
| 046d:c541 | Lightspeed | 1 |
|
||||
| 17ef:6042 | Nano | 1 |
|
||||
|
||||
Some Nano receivers are only partly supported
|
||||
as they do not fully implement the HID++ 1.0 protocol.
|
||||
|
||||
The receiver with USB Id 046d:c517 is an old 27 MHz receiver, supporting only
|
||||
subset of HID++ 1.0 protocol. Only hardware pairing is supported.
|
||||
|
||||
|
||||
Mice:
|
||||
### Keyboards (Unifying)
|
||||
|
||||
| Device | HID++ | Battery | DPI | Other supported features |
|
||||
|------------------|-------|---------|-------|---------------------------------|
|
||||
| V450 Nano | 1.0 | yes | - | smooth scrolling |
|
||||
| V550 Nano | 1.0 | yes | - | smooth scrolling |
|
||||
| VX Nano | 1.0 | yes | - | smooth scrolling |
|
||||
| M175 * | | yes | | |
|
||||
| M185 * | | yes | | |
|
||||
| M187 * | 2.0 | yes | | |
|
||||
| M215 * | 1.0 | yes | | |
|
||||
| M235 * | | yes | | |
|
||||
| M305 * | 1.0 | yes | | |
|
||||
| M310 * | | yes | | |
|
||||
| M315 * | | yes | | |
|
||||
| M317 | | | | |
|
||||
| M325 | | | | |
|
||||
| M345 | 2.0 | yes | - | |
|
||||
| M505 | 1.0 | yes | | |
|
||||
| M510 | 1.0 | yes | | smooth scrolling |
|
||||
| M515 Couch | 2.0 | yes | - | |
|
||||
| M525 | 2.0 | yes | - | |
|
||||
| M600 Touch | 2.0 | yes | | |
|
||||
| M705 Marathon | 1.0 | yes | - | smooth scrolling |
|
||||
| T400 Zone Touch | | | | |
|
||||
| T620 Touch | 2.0 | | | |
|
||||
| Performance MX | 1.0 | yes | R/W | |
|
||||
| Anywhere MX | 1.0 | yes | - | |
|
||||
| Cube | 2.0 | yes | | |
|
||||
| Device | WPID | HID++ | Battery | Other supported features |
|
||||
|------------------|------|-------|---------|-----------------------------------------|
|
||||
| K230 | 400D | 2.0 | yes | |
|
||||
| K270 | 4003 | 2.0 | yes | |
|
||||
| K340 | 2007 | 1.0 | yes | |
|
||||
| K350 | 200A | 1.0 | yes | |
|
||||
| K360 | 4004 | 2.0 | yes | FN swap, reprog keys |
|
||||
| K375s | 4071 | | | FN swap |
|
||||
| K400 Touch | 400E | 2.0 | yes | FN swap |
|
||||
| K400 Touch | 4024 | 2.0 | yes | FN swap |
|
||||
| K400 Plus | 404D | 2.0 | | FN swap, disable keys, touchpad gestures|
|
||||
| K520 | 2011 | 1.0 | yes | FN swap |
|
||||
| K600 TV | 4078 | 2.0 | yes | FN swap |
|
||||
| K750 Solar | 4002 | 2.0 | yes | FN swap, Lux reading, light button |
|
||||
| K780 | 405B | 2.0 | yes | FN swap |
|
||||
| K800 Illuminated | 2010 | 1.0 | yes | FN swap, reprog keys, LEDs |
|
||||
| K800 (new ver) | 406E | 2.0 | yes | FN swap |
|
||||
| K830 Illuminated | 4032 | 2.0 | yes | FN swap |
|
||||
| MX Keys | 408A | 2.0 | yes | |
|
||||
| N545 | 2006 | | yes | |
|
||||
| TK820 | | 2.0 | yes | FN swap |
|
||||
| Craft | 4066 | 2.0 | | |
|
||||
|
||||
* The [K750 Solar Keyboard][K750] can be queried for its solar charge status.
|
||||
Pressing the `Light-Check` button on the keyboard will pop-up the application
|
||||
window and display the current lighting value (Lux) as reported by the
|
||||
keyboard, similar to Logitech's *Solar.app* for Windows.
|
||||
|
||||
Trackballs:
|
||||
* FN swap changes the way the function keys (`F1`–`F12`) work, i.e., whether holding `FN` while pressing the function keys will generate the standard `Fx` keycodes or the special function (yellow icons) keycodes.
|
||||
|
||||
| Device | HID++ | Battery | DPI | Other supported features |
|
||||
|------------------|-------|---------|-------|---------------------------------|
|
||||
| M570 Trackball | | | | |
|
||||
### Mice (Unifying)
|
||||
|
||||
| Device | WPID | HID++ | Battery | DPI | Other supported features |
|
||||
|------------------|------|-------|---------|-------|-----------------------------------------|
|
||||
| M150 | 4022 | 2.0 | | | |
|
||||
| M185 | 4055 | 2.0 | | R/W | smooth scrolling |
|
||||
| M310 | 4031 | 2.0 | yes | | |
|
||||
| M310 | 4055 | 2.0 | | R/W | smooth scrolling |
|
||||
| M317 | | | | | |
|
||||
| M325 | 400A | 2.0 | yes | 1000 | smooth scrolling |
|
||||
| M330 | | 2.0 | yes | 1000 | smooth scrolling |
|
||||
| M345 | 4017 | 2.0 | yes | – | smooth scrolling |
|
||||
| M350 | 101C | 1.0 | yes | | |
|
||||
| M350 | 4080 | 2.0 | | | |
|
||||
| M505 | 101D | 1.0 | yes | | smooth scrolling, side scrolling |
|
||||
| M510 | 1025 | 1.0 | yes | | smooth scrolling, side scrolling |
|
||||
| M510 | 4051 | 2.0 | yes | | smooth scrolling |
|
||||
| M515 Couch | 4007 | 2.0 | yes | – | smooth scrolling |
|
||||
| M525 | 4013 | 2.0 | yes | – | smooth scrolling |
|
||||
| M560 | | 2.0 | yes | – | smooth scrolling |
|
||||
| M585 | 406B | 2.0 | yes | R/W | smooth scrolling |
|
||||
| M590 | 406B | 2.0 | yes | R/W | smooth scrolling |
|
||||
| M600 Touch | 401A | 2.0 | yes | | |
|
||||
| M705 Marathon | 101B | 1.0 | yes | – | smooth scrolling, side scrolling |
|
||||
| M705 Marathon | 406D | 2.0 | yes | R/W | smooth scrolling |
|
||||
| M720 Triathlon | 405E | 2.0 | yes | – | |
|
||||
| T400 Zone Touch | | 2.0 | yes | | smooth scrolling |
|
||||
| T620 Touch | | 2.0 | yes | | |
|
||||
| Performance MX | 101A | 1.0 | yes | R/W | smooth scrolling, side scrolling |
|
||||
| Anywhere MX | 1017 | 1.0 | yes | R/W | smooth scrolling, side scrolling |
|
||||
| Anywhere MX 2 | 404A | 2.0 | yes | R/W | smooth scrolling |
|
||||
| MX Master | 4041 | 2.0 | yes | R/W | smooth scrolling, smart shift |
|
||||
| MX Master 2S | 4069 | 2.0 | yes | R/W | smooth scrolling, smart shift, gestures |
|
||||
| Cube | | 2.0 | yes | | |
|
||||
|
||||
Touchpads:
|
||||
### Mice (Nano)
|
||||
|
||||
| Device | HID++ | Battery | DPI | Other supported features |
|
||||
|------------------|-------|---------|-------|---------------------------------|
|
||||
| Wireless Touch | 2.0 | | | |
|
||||
| T650 Touchpad | 2.0 | | | |
|
||||
| Device | WPID | HID++ | Battery | DPI | Other supported features |
|
||||
|------------------|------|-------|---------|-------|---------------------------------|
|
||||
| G7 | 1002 | 1.0 | yes | – | |
|
||||
| G700 | 1023 | 1.0 | yes | – | smooth scrolling, side scrolling|
|
||||
| G700s | 102A | 1.0 | yes | – | smooth scrolling, side scrolling|
|
||||
| V450 Nano | 1011 | 1.0 | yes | – | smooth scrolling |
|
||||
| V550 Nano | 1013 | 1.0 | yes | – | smooth scrolling, side scrolling|
|
||||
| VX Nano | 100B | 1.0 | yes | – | smooth scrolling, side scrolling|
|
||||
| VX Nano | 100F | 1.0 | yes | – | smooth scrolling, side scrolling|
|
||||
| M175 | 4008 | | yes | | |
|
||||
| M185 (old) | 4038 | 2.0 | yes | R/W | smooth scrolling (note) |
|
||||
| M185 (new) | 4054 | 2.0 | no | R/W | smooth scrolling (note) |
|
||||
| M187 | 4019 | 2.0 | yes | | |
|
||||
| M215 | 1020 | 1.0 | yes | | |
|
||||
| M235 | 4055 | 2.0 | yes | R/W | smooth scrolling |
|
||||
| M305 | 101F | 1.0 | yes | | side scrolling |
|
||||
| M310 | 1024 | 1.0 | yes | | |
|
||||
| M315 | | | yes | | |
|
||||
| M330 | | ?.? | yes | ? | smooth scrolling |
|
||||
| MX 1100 | 1014 | 1.0 | yes | – | smooth scrolling, side scrolling|
|
||||
|
||||
* (old): M185 with P/N: 810-003496
|
||||
* (new): M185 with P/N: 810-005238 or 810-005232
|
||||
* (note): Currently, smooth scrolling events are not processed in xfce and this
|
||||
setting is useful only to disable smooth scrolling.
|
||||
|
||||
Mouse-Keyboard combos:
|
||||
### Mice (Mini)
|
||||
|
||||
| Device | HID++ | Battery | Other supported features |
|
||||
|------------------|-------|---------|-----------------------------------------|
|
||||
| MK330 | | | |
|
||||
| MK520 | | | |
|
||||
| MK550 | | | |
|
||||
| MK710 | 1.0 | yes | FN swap, reprog keys |
|
||||
| Device | WPID | HID++ | Battery | DPI | Other supported features |
|
||||
|-------------------|------|-------|---------|-------|---------------------------------|
|
||||
| MX610 | 1001 | 1.0 | yes | | |
|
||||
| MX610 left handed | 1004 | 1.0 | yes | | |
|
||||
| MX620 | 100A | 1.0 | yes | | |
|
||||
| MX620 | 1016 | 1.0 | yes | | |
|
||||
| V400 | 1003 | 1.0 | yes | | |
|
||||
| V450 | 1005 | 1.0 | yes | | |
|
||||
| VX Revolution | 1006 | 1.0 | yes | | |
|
||||
| VX Revolution | 100D | 1.0 | yes | | |
|
||||
| MX Air | 1007 | 1.0 | yes | | |
|
||||
| MX Air | 100E | 1.0 | yes | | |
|
||||
| MX Revolution | 1008 | 1.0 | yes | | |
|
||||
| MX Revolution | 100C | 1.0 | yes | | |
|
||||
|
||||
### Trackballs (Unifying)
|
||||
|
||||
[unifying]: http://logitech.com/en-us/66/6079
|
||||
[nano]: http://logitech.com/mice-pointers/articles/5926
|
||||
| Device | WPID | HID++ | Battery | DPI | Other supported features |
|
||||
|-------------------|------|-------|---------|-------|---------------------------------|
|
||||
| M570 Trackball | | 1.0 | yes | – | |
|
||||
| MX Ergo Trackball | | 2.0 | yes | – | |
|
||||
|
||||
### Touchpads (Unifying)
|
||||
|
||||
| Device | WPID | HID++ | Battery | DPI | Other supported features |
|
||||
|------------------|------|-------|---------|-------|---------------------------------|
|
||||
| Wireless Touch | 4011 | 2.0 | yes | | |
|
||||
| T650 Touchpad | 4101 | 2.0 | yes | | smooth scrolling |
|
||||
|
||||
### Mice and Keyboards sold as combos
|
||||
|
||||
| Device | WPID | HID++ | Battery | Other supported features |
|
||||
|------------------|------|-------|---------|-----------------------------------------|
|
||||
| MK220 | | 2.0 | yes | |
|
||||
| MK270 | 4023 | 2.0 | yes | reprog keys |
|
||||
| MK320 | 200F | | | |
|
||||
| MK330 | | | | |
|
||||
| MK345 | 4023 | 2.0 | yes | reprog keys |
|
||||
| MK520 | | M2/K1 | yes | FN swap, reprog keys |
|
||||
| MK550 | | | | |
|
||||
| MK700 | 2008 | 1.0 | yes | FN swap, reprog keys |
|
||||
| MK710 | | 1.0 | yes | FN swap, reprog keys |
|
||||
| EX100 keyboard | 0065 | 1.0 | yes | |
|
||||
| EX100 mouse | 003f | 1.0 | yes | |
|
||||
|
||||
* The EX100 is an old, preunifying receiver and device set, supporting only part of HID++ 1.0 features
|
||||
|
||||
[solaar]: https://github.com/pwr-Solaar/Solaar
|
||||
[logitech]: https://www.logitech.com
|
||||
[unifying]: https://en.wikipedia.org/wiki/Logitech_Unifying_receiver
|
||||
[G700s]: https://gaming.logitech.com/en-us/product/g700s-rechargeable-wireless-gaming-mouse
|
||||
[K360]: http://logitech.com/product/keyboard-k360
|
||||
[K700]: http://logitech.com/product/wireless-desktop-mk710
|
||||
[K750]: http://logitech.com/product/k750-keyboard
|
||||
[K800]: http://logitech.com/product/wireless-illuminated-keyboard-k800
|
||||
[K830]: http://logitech.com/product/living-room-keyboard-k830
|
||||
[M510]: http://logitech.com/product/wireless-mouse-m510
|
||||
[M705]: http://logitech.com/product/marathon-mouse-m705
|
||||
[P_MX]: http://logitech.com/product/performance-mouse-mx
|
||||
[A_MX]: http://logitech.com/product/anywhere-mouse-mx
|
||||
[M325]: http://logitech.com/product/wireless-mouse-m325
|
||||
[M330]: https://www.logitech.com/en-us/product/m330-silent-plus
|
||||
|
||||
48
docs/devices/M510_HIDppV4.5.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
1: Wireless Mouse M510
|
||||
Codename : M510v2
|
||||
Kind : mouse
|
||||
Wireless PID : 4051
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 8989B04C
|
||||
Firmware: RQM 62.00.B0013
|
||||
The power switch is located on the base.
|
||||
Supports 22 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: RESET {0020}
|
||||
5: BATTERY STATUS {1000}
|
||||
6: unknown:1802 {1802} internal, hidden
|
||||
7: unknown:1810 {1810} internal, hidden
|
||||
8: unknown:1830 {1830} internal, hidden
|
||||
9: unknown:1862 {1862} internal, hidden
|
||||
10: unknown:1890 {1890} internal, hidden
|
||||
11: unknown:18A0 {18A0} internal, hidden
|
||||
12: unknown:18B1 {18B1} internal, hidden
|
||||
13: REPROG CONTROLS V4 {1B04}
|
||||
14: WIRELESS DEVICE STATUS {1D4B}
|
||||
15: unknown:1DF0 {1DF0} hidden
|
||||
16: unknown:1DF3 {1DF3} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
18: unknown:1EB0 {1EB0} internal, hidden
|
||||
19: unknown:1F03 {1F03} internal, hidden
|
||||
20: LOWRES WHEEL {2130}
|
||||
21: POINTER SPEED {2205}
|
||||
Has 7 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
divertable, mse, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
divertable, mse, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
3: LEFT SCROLL AS AC PAN , default: HorzScrollLeftSet => LEFT SCROLL AS AC PAN
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
4: RIGHT SCROLL AS AC PAN , default: HorzScrollRightSet => RIGHT SCROLL AS AC PAN
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
5: BACK AS BUTTON 4 , default: BackEx => BACK AS BUTTON 4
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
6: FORWARD AS BUTTON 5 , default: BrowserForwardEx => FORWARD AS BUTTON 5
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
Battery: 70%, discharging.
|
||||
51
docs/devices/anywhere-mx-2.txt
Normal file
@@ -0,0 +1,51 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : A7F5923B
|
||||
Firmware : 24.01.B0023
|
||||
Bootloader : 01.08
|
||||
Other : AA.AD
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=11
|
||||
|
||||
1: Wireless Mouse MX Anywhere 2
|
||||
Codename : MX Anywhere 2
|
||||
Kind : mouse
|
||||
Wireless PID : 404A
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: F3B81C5B
|
||||
Bootloader: BOT 23.00.B0007
|
||||
Firmware: MPM 02.00.B0007
|
||||
Firmware: MPM 02.00.B0007
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 26 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: BATTERY STATUS {1000}
|
||||
7: CHANGE HOST {1814}
|
||||
8: REPROG CONTROLS V4 {1B04}
|
||||
9: ADJUSTABLE DPI {2201}
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HIRES WHEEL {2121}
|
||||
12: DFUCONTROL 2 {00C1}
|
||||
13: unknown:1813 {1813} internal, hidden
|
||||
14: unknown:1830 {1830} internal, hidden
|
||||
15: unknown:1890 {1890} internal, hidden
|
||||
16: unknown:1891 {1891} internal, hidden
|
||||
17: unknown:18A1 {18A1} internal, hidden
|
||||
18: unknown:18C0 {18C0} internal, hidden
|
||||
19: unknown:1DF3 {1DF3} internal, hidden
|
||||
20: unknown:1E00 {1E00} hidden
|
||||
21: unknown:1EB0 {1EB0} internal, hidden
|
||||
22: unknown:1803 {1803} internal, hidden
|
||||
23: unknown:1861 {1861} internal, hidden
|
||||
24: unknown:9000 {9000} internal, hidden
|
||||
25: unknown:1805 {1805} internal, hidden
|
||||
Battery: 0%, recharging.
|
||||
88
docs/devices/anywhere-mx.txt
Normal file
@@ -0,0 +1,88 @@
|
||||
Receiver
|
||||
LZ301AR-DJ
|
||||
M/N:C-U0007
|
||||
(ltunify)
|
||||
Serial number: D1759614
|
||||
Firmware version: 012.001.00019
|
||||
Bootloader version: BL.002.014
|
||||
|
||||
Supported notification flags: 00 09 00
|
||||
- 01: Wireless Notifications
|
||||
- 08: Software Present
|
||||
|
||||
Mouse
|
||||
(ltunify)
|
||||
HID++ version: 1.0
|
||||
Device index 1
|
||||
Mouse
|
||||
Name: Anywhere MX
|
||||
Wireless Product ID: 1017
|
||||
Serial number: 13865F99
|
||||
Firmware version: 016.001.00040
|
||||
Bootloader version: BL.002.010
|
||||
(solaar)
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw2
|
||||
USB id : 046d:c52b
|
||||
Serial : D1759614
|
||||
Firmware : 12.01.B0019
|
||||
Bootloader : 02.14
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
Device activity counters: 1=19
|
||||
|
||||
1: Anywhere Mouse MX
|
||||
Codename : Anywhere MX
|
||||
Kind : mouse
|
||||
Wireless PID : 1017
|
||||
Protocol : HID++ 1.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 13865F99
|
||||
Firmware: 16.01.B0040
|
||||
Bootloader: 02.10
|
||||
Other: 00.06
|
||||
The power switch is located on the base.
|
||||
Notifications: (none).
|
||||
Battery: 100%, discharging.
|
||||
|
||||
(scan-registers)
|
||||
# Old notification flags: 000000
|
||||
# 00 - Enabled Notifications, supported flags: Battery Status (10)
|
||||
>> ( 0.792) [10 01 8100 100000] b'\x10\x01\x81\x00\x10\x00\x00'
|
||||
|
||||
# 01 - scrolling settings?
|
||||
# Flags:
|
||||
# 0x40 - Enable Smooth Scrolling
|
||||
# 0x02 - "confuse KDE", see https://github.com/pwr/Solaar/issues/115
|
||||
<< ( 0.011) [10 01 8101 000000] b'\x10\x01\x81\x01\x00\x00\x00'
|
||||
>> ( 1.710) [10 01 8101 020000] b'\x10\x01\x81\x01\x02\x00\x00'
|
||||
|
||||
# 0D - battery level. first byte is remaining charge in percent; second is
|
||||
# (guessed) maximum?; third is charge status (30=discharging)
|
||||
# "10 ix 0D 64 64 30 00" is a battery notification (when enabled)
|
||||
<< ( 9.789) [10 01 810D 000000] b'\x10\x01\x81\r\x00\x00\x00'
|
||||
>> ( 9.816) [10 01 810D 646430] b'\x10\x01\x81\rdd0'
|
||||
|
||||
# 63 - DPI (range 0x80-0x8e (inclusive))
|
||||
<< ( 75.521) [10 01 8163 000000] b'\x10\x01\x81c\x00\x00\x00'
|
||||
>> ( 75.550) [10 01 8163 890000] b'\x10\x01\x81c\x89\x00\x00'
|
||||
|
||||
# D0 - ?
|
||||
<< ( 163.118) [10 01 81D0 000000] b'\x10\x01\x81\xd0\x00\x00\x00'
|
||||
>> ( 163.148) [10 01 81D0 000000] b'\x10\x01\x81\xd0\x00\x00\x00'
|
||||
|
||||
# D4 - ?
|
||||
<< ( 166.034) [10 01 81D4 000000] b'\x10\x01\x81\xd4\x00\x00\x00'
|
||||
>> ( 166.063) [10 01 81D4 000008] b'\x10\x01\x81\xd4\x00\x00\x08'
|
||||
|
||||
# F1 - firmware/bootloader version
|
||||
<< ( 187.172) [10 01 81F1 000000] b'\x10\x01\x81\xf1\x00\x00\x00'
|
||||
>> ( 187.199) [10 01 8F81 F10300] b'\x10\x01\x8f\x81\xf1\x03\x00'
|
||||
|
||||
# F3 - ?
|
||||
<< ( 188.629) [10 01 81F3 000000] b'\x10\x01\x81\xf3\x00\x00\x00'
|
||||
>> ( 188.661) [10 01 81F3 000000] b'\x10\x01\x81\xf3\x00\x00\x00'
|
||||
|
||||
# FD - ?
|
||||
<< ( 195.715) [10 01 83FD 000000] b'\x10\x01\x83\xfd\x00\x00\x00'
|
||||
>> ( 195.746) [11 01 83FD 00000000000000000000000000000000] b'\x11\x01\x83\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
113
docs/devices/craft.txt
Normal file
@@ -0,0 +1,113 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : E21FAD57
|
||||
Firmware : 24.06.B0030
|
||||
Bootloader : 01.08
|
||||
Other : AA.AC
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=243, 2=173
|
||||
|
||||
1: Craft Advanced Keyboard
|
||||
Codename : Craft
|
||||
Kind : keyboard
|
||||
Wireless PID : 4066
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 428C2F81
|
||||
Bootloader: BOT 41.00.B0014
|
||||
Firmware: MPK 07.01.B0015
|
||||
Other:
|
||||
Other:
|
||||
The power switch is located on the edge of top right corner.
|
||||
Supports 39 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: unknown:0007 {0007}
|
||||
7: BATTERY STATUS {1000}
|
||||
8: CHANGE HOST {1814}
|
||||
9: unknown:1815 {1815}
|
||||
10: unknown:1982 {1982}
|
||||
11: REPROG CONTROLS V4 {1B04}
|
||||
12: unknown:1C00 {1C00}
|
||||
13: K375S FN INVERSION {40A3}
|
||||
14: ENCRYPTION {4100}
|
||||
15: LOCK KEY STATE {4220}
|
||||
16: KEYBOARD DISABLE {4521}
|
||||
17: unknown:4531 {4531}
|
||||
18: unknown:4600 {4600}
|
||||
19: unknown:00C2 {00C2}
|
||||
20: unknown:1803 {1803} internal, hidden
|
||||
21: unknown:1806 {1806} internal, hidden
|
||||
22: unknown:1813 {1813} internal, hidden
|
||||
23: unknown:1805 {1805} internal, hidden
|
||||
24: unknown:1830 {1830} internal, hidden
|
||||
25: unknown:1890 {1890} internal, hidden
|
||||
26: unknown:1891 {1891} internal, hidden
|
||||
27: unknown:1801 {1801} internal, hidden
|
||||
28: unknown:18A1 {18A1} internal, hidden
|
||||
29: unknown:9280 {9280} internal, hidden
|
||||
30: unknown:1A20 {1A20} internal, hidden
|
||||
31: unknown:1DF3 {1DF3} internal, hidden
|
||||
32: unknown:1E00 {1E00} hidden
|
||||
33: unknown:1EB0 {1EB0} internal, hidden
|
||||
34: unknown:1861 {1861} internal, hidden
|
||||
35: unknown:18B0 {18B0} internal, hidden
|
||||
36: unknown:92C0 {92C0} internal, hidden
|
||||
37: unknown:9203 {9203} internal, hidden
|
||||
38: unknown:3615 {3615} internal, hidden
|
||||
Has 24 reprogrammable keys:
|
||||
0: unknown:00D1 , default: unknown:00AE => unknown:00D1
|
||||
divertable, nonstandard, persistently divertable, pos:0, group:0, gmask:0
|
||||
1: unknown:00D2 , default: unknown:00AF => unknown:00D2
|
||||
divertable, nonstandard, persistently divertable, pos:0, group:0, gmask:0
|
||||
2: unknown:00D3 , default: unknown:00B0 => unknown:00D3
|
||||
divertable, nonstandard, persistently divertable, pos:0, group:0, gmask:0
|
||||
3: unknown:00C7 , default: unknown:00A3 => unknown:00C7
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:1, group:0, gmask:0
|
||||
4: unknown:00C8 , default: unknown:00A4 => unknown:00C8
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:2, group:0, gmask:0
|
||||
5: unknown:00E0 , default: unknown:00BF => unknown:00E0
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:3, group:0, gmask:0
|
||||
6: unknown:00E1 , default: unknown:00C0 => unknown:00E1
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:4, group:0, gmask:0
|
||||
7: SHOW DESKTOP , default: ShowDesktop => SHOW DESKTOP
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:5, group:0, gmask:0
|
||||
8: unknown:00E2 , default: unknown:00C1 => unknown:00E2
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:6, group:0, gmask:0
|
||||
9: unknown:00E3 , default: unknown:00C2 => unknown:00E3
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:7, group:0, gmask:0
|
||||
10: unknown:00E4 , default: Previous => unknown:00E4
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:8, group:0, gmask:0
|
||||
11: unknown:00E5 , default: Play/Pause => unknown:00E5
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:9, group:0, gmask:0
|
||||
12: unknown:00E6 , default: Next => unknown:00E6
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:10, group:0, gmask:0
|
||||
13: unknown:00E7 , default: Mute => unknown:00E7
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:11, group:0, gmask:0
|
||||
14: unknown:00E8 , default: Volume Down => unknown:00E8
|
||||
divertable, is FN, FN sensitive, persistently divertable, reprogrammable, pos:12, group:0, gmask:0
|
||||
15: unknown:00E9 , default: Volume Up => unknown:00E9
|
||||
divertable, nonstandard, persistently divertable, reprogrammable, pos:0, group:0, gmask:0
|
||||
16: Calculator , default: Calculator => Calculator
|
||||
divertable, nonstandard, persistently divertable, reprogrammable, pos:0, group:0, gmask:0
|
||||
17: unknown:00BF , default: unknown:009B => unknown:00BF
|
||||
divertable, nonstandard, persistently divertable, reprogrammable, pos:0, group:0, gmask:0
|
||||
18: unknown:00EA , default: unknown:00C3 => unknown:00EA
|
||||
divertable, nonstandard, persistently divertable, reprogrammable, pos:0, group:0, gmask:0
|
||||
19: Lock PC , default: WindowsLock => Lock PC
|
||||
divertable, nonstandard, persistently divertable, reprogrammable, pos:0, group:0, gmask:0
|
||||
20: unknown:00EC , default: unknown:00B8 => unknown:00EC
|
||||
divertable, nonstandard, persistently divertable, pos:0, group:0, gmask:0
|
||||
21: unknown:00EB , default: unknown:00B6 => unknown:00EB
|
||||
divertable, nonstandard, persistently divertable, pos:0, group:0, gmask:0
|
||||
22: unknown:00DE , default: Do Nothing One => unknown:00DE
|
||||
is FN, pos:0, group:0, gmask:0
|
||||
23: unknown:0034 , default: Do Nothing One => unknown:0034
|
||||
nonstandard, pos:0, group:0, gmask:0
|
||||
Battery: 0%, full.
|
||||
120
docs/devices/ex100.txt
Normal file
@@ -0,0 +1,120 @@
|
||||
./scan-registers.sh ff /dev/hidraw4
|
||||
# Old notification flags: 000100
|
||||
>> ( 0.035) [10 FF 8100 000100] '\x10\xff\x81\x00\x00\x01\x00'
|
||||
<< ( 0.015) [10 FF 8101 000000] '\x10\xff\x81\x01\x00\x00\x00'
|
||||
>> ( 0.020) [10 FF 8101 000200] '\x10\xff\x81\x01\x00\x02\x00'
|
||||
<< ( 0.030) [10 FF 8102 000000] '\x10\xff\x81\x02\x00\x00\x00'
|
||||
>> ( 0.036) [10 FF 8102 000200] '\x10\xff\x81\x02\x00\x02\x00'
|
||||
--
|
||||
<< ( 0.142) [10 FF 8109 000000] '\x10\xff\x81\t\x00\x00\x00'
|
||||
>> ( 0.148) [10 FF 8109 010000] '\x10\xff\x81\t\x01\x00\x00'
|
||||
--
|
||||
<< ( 1.790) [10 FF 8170 000000] '\x10\xff\x81p\x00\x00\x00'
|
||||
>> ( 1.796) [10 FF 8170 012100] '\x10\xff\x81p\x01!\x00'
|
||||
<< ( 1.806) [10 FF 8171 000000] '\x10\xff\x81q\x00\x00\x00'
|
||||
>> ( 1.812) [10 FF 8171 011200] '\x10\xff\x81q\x01\x12\x00'
|
||||
--
|
||||
<< ( 1.838) [10 FF 8173 000000] '\x10\xff\x81s\x00\x00\x00'
|
||||
>> ( 1.844) [10 FF 8173 643F00] '\x10\xff\x81sd?\x00'
|
||||
--
|
||||
<< ( 2.046) [10 FF 8180 000000] '\x10\xff\x81\x80\x00\x00\x00'
|
||||
>> ( 2.052) [10 FF 8180 030000] '\x10\xff\x81\x80\x03\x00\x00'
|
||||
--
|
||||
<< ( 3.326) [10 FF 81D0 000000] '\x10\xff\x81\xd0\x00\x00\x00'
|
||||
>> ( 3.332) [10 FF 81D0 000000] '\x10\xff\x81\xd0\x00\x00\x00'
|
||||
|
||||
devices
|
||||
01 mouse
|
||||
Red button pressed
|
||||
>> ( 1676.106) [10 01 0810 000000] '\x10\x01\x08\x10\x00\x00\x00'
|
||||
>> ( 1676.114) [10 01 4600 000021] '\x10\x01F\x00\x00\x00!'
|
||||
>> ( 1676.122) [10 FF 4600 211100] '\x10\xffF\x00!\x11\x00'
|
||||
|
||||
Power lewel?
|
||||
?? Input: 10 01 81 07 00 00 00
|
||||
<< ( 1739.032) [10 01 8107 000000] '\x10\x01\x81\x07\x00\x00\x00'
|
||||
>> ( 1739.040) [10 01 8107 030000] '\x10\x01\x81\x07\x03\x00\x00'
|
||||
[10 01 8107 070000] '\x10\x01\x81\x07\x07\x00\x00'
|
||||
|
||||
power change
|
||||
>> ( 2441.563) [10 01 0703 000000] '\x10\x01\x07\x03\x00\x00\x00'
|
||||
>> ( 100.159) [10 01 0707 000000] '\x10\x01\x07\x07\x00\x00\x00'
|
||||
|
||||
enable power event
|
||||
<< ( 59.190) [10 01 8000 100000] '\x10\x01\x80\x00\x10\x00\x00'
|
||||
>> ( 59.193) [10 01 8000 000000] '\x10\x01\x80\x00\x00\x00\x00'
|
||||
|
||||
|
||||
03 keyboard
|
||||
|
||||
Power level?
|
||||
?? Input: 10 03 81 07 00 00 00
|
||||
<< ( 1777.961) [10 03 8107 000000] '\x10\x03\x81\x07\x00\x00\x00'
|
||||
>> ( 1777.967) [10 03 8107 070000] '\x10\x03\x81\x07\x07\x00\x00'
|
||||
|
||||
power on
|
||||
>> ( 1571.805) [10 03 0810 000000] '\x10\x03\x08\x10\x00\x00\x00'
|
||||
>> ( 1574.709) [10 03 0800 000000] '\x10\x03\x08\x00\x00\x00\x00'
|
||||
|
||||
red button pressed
|
||||
>> ( 1619.043) [10 03 0810 000000] '\x10\x03\x08\x10\x00\x00\x00'
|
||||
>> ( 1619.051) [10 03 4600 000011] '\x10\x03F\x00\x00\x00\x11'
|
||||
>> ( 1619.059) [10 FF 4600 221100] '\x10\xffF\x00"\x11\x00'
|
||||
>> ( 1621.747) [10 03 0800 000000] '\x10\x03\x08\x00\x00\x00\x00'
|
||||
|
||||
Fn pressed
|
||||
>> ( 1651.715) [10 03 032C 100000] '\x10\x03\x03,\x10\x00\x00'
|
||||
>> ( 1652.170) [10 03 0300 000000] '\x10\x03\x03\x00\x00\x00\x00'
|
||||
|
||||
|
||||
$ bin/solaar probe
|
||||
Nano Receiver
|
||||
Device path : /dev/hidraw3
|
||||
USB id : 046d:c517
|
||||
Serial : None
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless (0x000100)
|
||||
Register Dump
|
||||
Notification Register 0x00: 0x000100
|
||||
Connection State 0x02: 0x000200
|
||||
Device Activity 0xb3: None
|
||||
Pairing Register 0xb5 0x00: None
|
||||
Pairing Register 0xb5 0x10: None
|
||||
Pairing Register 0xb5 0x20: None
|
||||
Pairing Register 0xb5 0x30: None
|
||||
Pairing Name 0xb5 0x40: None
|
||||
Pairing Register 0xb5 0x01: None
|
||||
Pairing Register 0xb5 0x11: None
|
||||
Pairing Register 0xb5 0x21: None
|
||||
Pairing Register 0xb5 0x31: None
|
||||
Pairing Name 0xb5 0x41: None
|
||||
Pairing Register 0xb5 0x02: None
|
||||
Pairing Register 0xb5 0x12: None
|
||||
Pairing Register 0xb5 0x22: None
|
||||
Pairing Register 0xb5 0x32: None
|
||||
Pairing Name 0xb5 0x42: None
|
||||
Pairing Register 0xb5 0x03: None
|
||||
Pairing Register 0xb5 0x13: None
|
||||
Pairing Register 0xb5 0x23: None
|
||||
Pairing Register 0xb5 0x33: None
|
||||
Pairing Name 0xb5 0x43: None
|
||||
Pairing Register 0xb5 0x04: None
|
||||
Pairing Register 0xb5 0x14: None
|
||||
Pairing Register 0xb5 0x24: None
|
||||
Pairing Register 0xb5 0x34: None
|
||||
Pairing Name 0xb5 0x44: None
|
||||
Pairing Register 0xb5 0x05: None
|
||||
Pairing Register 0xb5 0x15: None
|
||||
Pairing Register 0xb5 0x25: None
|
||||
Pairing Register 0xb5 0x35: None
|
||||
Pairing Name 0xb5 0x45: None
|
||||
Firmware 0xf1 0x00: None
|
||||
Firmware 0xf1 0x01: None
|
||||
Firmware 0xf1 0x02: None
|
||||
Firmware 0xf1 0x03: None
|
||||
Firmware 0xf1 0x04: None
|
||||
|
||||
Battery status:
|
||||
1.9V critical
|
||||
2.3V low
|
||||
2.5V full
|
||||
42
docs/devices/k270-unifying.txt
Normal file
@@ -0,0 +1,42 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw2
|
||||
USB id : 046d:c52b
|
||||
Serial : C03E9E2E
|
||||
Firmware : 12.01.B0019
|
||||
Bootloader : 02.14
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 2=118
|
||||
|
||||
2: Wireless Keyboard K270(unifying)
|
||||
Codename : K270(unifying)
|
||||
Kind : keyboard
|
||||
Wireless PID : 4003
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: FE8C8542
|
||||
Firmware: RQK 35.00.B0017
|
||||
The power switch is located on the top case.
|
||||
Supports 12 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: unknown:1820 {1820} hidden
|
||||
6: REPROG CONTROLS {1B00}
|
||||
7: WIRELESS DEVICE STATUS {1D4B}
|
||||
8: unknown:1DF0 {1DF0} hidden
|
||||
9: unknown:1DF3 {1DF3} hidden
|
||||
10: ENCRYPTION {4100}
|
||||
11: KEYBOARD LAYOUT {4520}
|
||||
Has 8 reprogrammable keys:
|
||||
0: Play/Pause => Play/Pause nonstandard
|
||||
1: Mute => Mute nonstandard
|
||||
2: Volume Down => Volume Down nonstandard
|
||||
3: Volume Up => Volume Up nonstandard
|
||||
4: MY HOME => HomePage nonstandard, reprogrammable
|
||||
5: Mail => Email nonstandard, reprogrammable
|
||||
6: SLEEP => Sleep nonstandard, reprogrammable
|
||||
7: Calculator => Calculator nonstandard, reprogrammable
|
||||
Battery: 90%, discharging.
|
||||
@@ -58,19 +58,19 @@ Total number of HID++ 2.0 features: 12
|
||||
Firmware : RQK 36.00.B0007
|
||||
The power switch is located on the top case
|
||||
Supports 13 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: unknown:1820 {1820} hidden
|
||||
6: REPROG CONTROLS {1B00}
|
||||
7: WIRELESS DEVICE STATUS {1D4B}
|
||||
6: REPROG CONTROLS {1B00}
|
||||
7: WIRELESS DEVICE STATUS {1D4B}
|
||||
8: unknown:1DF0 {1DF0} hidden
|
||||
9: unknown:1DF3 {1DF3} hidden
|
||||
10: FN INVERSION {40A0}
|
||||
11: ENCRYPTION {4100}
|
||||
12: KEYBOARD LAYOUT {4520}
|
||||
10: FN INVERSION {40A0}
|
||||
11: ENCRYPTION {4100}
|
||||
12: KEYBOARD LAYOUT {4520}
|
||||
Has 18 reprogrammable keys:
|
||||
0: MY HOME => HomePage FN sensitive, is FN, reprogrammable
|
||||
1: Mail => Mail FN sensitive, is FN, reprogrammable
|
||||
|
||||
39
docs/devices/k750.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
(from Julien Danjou)
|
||||
|
||||
(solaar)
|
||||
2: Wireless Solar Keyboard K750
|
||||
Codename : K750
|
||||
Kind : keyboard
|
||||
Wireless PID : 4002
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 5692B2EC
|
||||
Firmware: RQK 33.00.B0015
|
||||
Bootloader: DFU 00.02.B0003
|
||||
The power switch is located on the edge of top right corner.
|
||||
Supports 11 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: REPROG CONTROLS {1B00}
|
||||
5: WIRELESS DEVICE STATUS {1D4B}
|
||||
6: unknown:1DF3 {1DF3} hidden
|
||||
7: FN INVERSION {40A0}
|
||||
8: ENCRYPTION {4100}
|
||||
9: SOLAR DASHBOARD {4301}
|
||||
10: KEYBOARD LAYOUT {4520}
|
||||
Has 12 reprogrammable keys:
|
||||
0: MY HOME => HomePage FN sensitive, is FN, reprogrammable
|
||||
1: Mail => Email FN sensitive, is FN, reprogrammable
|
||||
2: SEARCH => Search FN sensitive, is FN, reprogrammable
|
||||
3: Calculator => Calculator FN sensitive, is FN, reprogrammable
|
||||
4: MEDIA PLAYER => Music FN sensitive, is FN, reprogrammable
|
||||
5: Previous => Previous FN sensitive, is FN
|
||||
6: Play/Pause => Play/Pause FN sensitive, is FN
|
||||
7: Next => Next FN sensitive, is FN
|
||||
8: Mute => Mute FN sensitive, is FN
|
||||
9: Volume Down => Volume Down FN sensitive, is FN
|
||||
10: Volume Up => Volume Up FN sensitive, is FN
|
||||
11: SLEEP => Sleep FN sensitive, is FN, reprogrammable
|
||||
Battery status unavailable.
|
||||
44
docs/devices/k780.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
2: K780 Multi-Device Keyboard
|
||||
Codename : K780
|
||||
Kind : keyboard
|
||||
Wireless PID : 405B
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 4D71FEE1
|
||||
Bootloader: BOT 25.00.B0005
|
||||
Firmware: MPK 01.00.B0018
|
||||
Other:
|
||||
The power switch is located on the edge of top right corner.
|
||||
Supports 31 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: unknown:0007 {0007}
|
||||
7: BATTERY STATUS {1000}
|
||||
8: CHANGE HOST {1814}
|
||||
9: unknown:1815 {1815}
|
||||
10: REPROG CONTROLS V4 {1B04}
|
||||
11: unknown:1C00 {1C00}
|
||||
12: NEW FN INVERSION {40A2}
|
||||
13: ENCRYPTION {4100}
|
||||
14: KEYBOARD DISABLE {4521}
|
||||
15: unknown:4531 {4531}
|
||||
16: LOCK KEY STATE {4220}
|
||||
17: unknown:00C2 {00C2}
|
||||
18: unknown:1803 {1803} internal, hidden
|
||||
19: unknown:1806 {1806} internal, hidden
|
||||
20: unknown:1805 {1805} internal, hidden
|
||||
21: unknown:1813 {1813} internal, hidden
|
||||
22: unknown:1830 {1830} internal, hidden
|
||||
23: unknown:1861 {1861} internal, hidden
|
||||
24: unknown:1890 {1890} internal, hidden
|
||||
25: unknown:1891 {1891} internal, hidden
|
||||
26: unknown:18A1 {18A1} internal, hidden
|
||||
27: unknown:1DF3 {1DF3} internal, hidden
|
||||
28: unknown:1E00 {1E00} hidden
|
||||
29: unknown:1EB0 {1EB0} internal, hidden
|
||||
30: unknown:18B0 {18B0} internal, hidden
|
||||
Battery: 90%, discharging.
|
||||
557
docs/devices/k800-new.txt
Normal file
@@ -0,0 +1,557 @@
|
||||
[kamil@kamil-pc Solaar]$ bin/solaar -dd show
|
||||
16:01:46,533 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 030000]
|
||||
16:01:46,539 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 03DC279AB20A06090000000000000000]
|
||||
16:01:46,539 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 80B2 000000]
|
||||
16:01:46,550 DEBUG [MainThread] logitech_receiver.base: (3) => r[20 01 4101 71401E0000000400000000]
|
||||
16:01:46,551 DEBUG [MainThread] logitech_receiver.base: (3) => r[20 02 4101 6E401A4000000400000000]
|
||||
16:01:46,551 DEBUG [MainThread] logitech_receiver.base: (3) => r[20 00 4102 0000000000000000000000]
|
||||
16:01:46,551 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 8F80 B20300]
|
||||
16:01:46,551 DEBUG [MainThread] logitech_receiver.base: (3) device 0xFF error on request {80B2}: 3 = invalid value
|
||||
16:01:46,551 DEBUG [MainThread] solaar.cli: [/dev/hidraw0] => <UnifyingReceiver(/dev/hidraw0,3)>
|
||||
16:01:46,646 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 8102 000000]
|
||||
16:01:46,651 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 8102 000200]
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : DC279AB2
|
||||
16:01:46,652 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 81F1 010000]
|
||||
16:01:46,657 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 81F1 012411]
|
||||
16:01:46,658 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 81F1 020000]
|
||||
16:01:46,664 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 81F1 020036]
|
||||
16:01:46,664 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 81F1 040000]
|
||||
16:01:46,669 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 81F1 040209]
|
||||
16:01:46,670 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 81F1 030000]
|
||||
16:01:46,675 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 81F1 03AAAC]
|
||||
Firmware : 24.11.B0036
|
||||
Bootloader : 02.09
|
||||
Other : AA.AC
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
16:01:46,676 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 8100 000000]
|
||||
16:01:46,681 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 8100 000900]
|
||||
Notifications: wireless, software present (0x000900)
|
||||
16:01:46,682 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B3 000000]
|
||||
16:01:46,687 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B3 51060000000000000000000000000000]
|
||||
Device activity counters: 1=81, 2=6
|
||||
16:01:46,688 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 8102 000000]
|
||||
16:01:46,693 DEBUG [MainThread] logitech_receiver.base: (3) => r[10 FF 8102 000200]
|
||||
16:01:46,694 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 200000]
|
||||
16:01:46,699 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 20090840710402020700000000000000]
|
||||
16:01:46,700 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 400000]
|
||||
16:01:46,705 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 40094D58204D61737465720000000000]
|
||||
16:01:46,706 INFO [MainThread] logitech_receiver.receiver: <UnifyingReceiver(/dev/hidraw0,3)>: found new device 1 (4071)
|
||||
|
||||
16:01:46,706 DEBUG [MainThread] logitech_receiver.base: (3) pinging device 1
|
||||
16:01:46,706 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 001E 000016]
|
||||
16:01:47,033 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 00000000000000000000000000000000]
|
||||
16:01:47,049 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 001E 04051600000000000000000000000000]
|
||||
1: Wireless Mouse MX Master
|
||||
Codename : MX Master
|
||||
Kind : mouse
|
||||
Wireless PID : 4071
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
16:01:47,049 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 300000]
|
||||
16:01:47,055 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 30A6A400641E00000001000000000000]
|
||||
Serial number: A6A40064
|
||||
16:01:47,055 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 000100]
|
||||
16:01:47,061 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 00000000000000000000000000000000]
|
||||
16:01:47,077 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 01000100000000000000000000000000]
|
||||
16:01:47,077 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0108 000000]
|
||||
16:01:47,093 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000D 00000000000000000000000000000000]
|
||||
16:01:47,101 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0108 1F000000000000000000000000000000]
|
||||
16:01:47,101 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 000300]
|
||||
16:01:47,109 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 00000000000000000000000000000000]
|
||||
16:01:47,117 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 02000200000000000000000000000000]
|
||||
16:01:47,117 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 020D 000000]
|
||||
16:01:47,125 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 00000000000000000000000000000000]
|
||||
16:01:47,133 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 020D 040382B4290006B01E40710000030000]
|
||||
16:01:47,134 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 021E 000000]
|
||||
16:01:47,141 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 00000000000000000000000000000000]
|
||||
16:01:47,149 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 021E 01424F54561000050040717622661101]
|
||||
16:01:47,150 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 021C 010000]
|
||||
16:01:47,171 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 021C 004D504D1210000500B01E7622661101]
|
||||
16:01:47,171 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 021E 020000]
|
||||
16:01:47,191 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 021E 004D504D121000050140717622661101]
|
||||
16:01:47,191 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 021F 030000]
|
||||
16:01:47,213 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 021F 050000000000005A0000000000000000]
|
||||
Bootloader: BOT 56.10.B0005
|
||||
Firmware: MPM 12.10.B0005
|
||||
Firmware: MPM 12.10.B0005
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 32 HID++ 2.0 features:
|
||||
16:01:47,214 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000B 000000]
|
||||
16:01:47,311 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 00000100000000000000000000000000]
|
||||
0: ROOT {0000}
|
||||
16:01:47,312 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 000100]
|
||||
16:01:47,333 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 01000100000000000000000000000000]
|
||||
1: FEATURE SET {0001}
|
||||
16:01:47,334 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000F 000300]
|
||||
16:01:47,431 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 02000200000000000000000000000000]
|
||||
2: DEVICE FW VERSION {0003}
|
||||
16:01:47,431 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011C 030000]
|
||||
16:01:47,453 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011C 00050000000000000000000000000000]
|
||||
16:01:47,454 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000F 000500]
|
||||
16:01:47,475 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 03000000000000000000000000000000]
|
||||
3: DEVICE NAME {0005}
|
||||
16:01:47,475 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 040000]
|
||||
16:01:47,575 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 1D4B0000000000000000000000000000]
|
||||
16:01:47,575 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000A 1D4B00]
|
||||
16:01:47,593 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000A 04000000000000000000000000000000]
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
16:01:47,593 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011A 050000]
|
||||
16:01:47,617 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011A 00200000000000000000000000000000]
|
||||
16:01:47,617 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 002000]
|
||||
16:01:47,715 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 05000000000000000000000000000000]
|
||||
5: RESET {0020}
|
||||
16:01:47,715 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011E 060000]
|
||||
16:01:47,735 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011E 00210000000000000000000000000000]
|
||||
16:01:47,735 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000B 002100]
|
||||
16:01:47,755 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 06000000000000000000000000000000]
|
||||
6: unknown:0021 {0021}
|
||||
16:01:47,755 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011C 070000]
|
||||
16:01:47,775 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011C 10000000000000000000000000000000]
|
||||
16:01:47,775 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 100000]
|
||||
16:01:47,795 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 07000000000000000000000000000000]
|
||||
7: BATTERY STATUS {1000}
|
||||
16:01:47,795 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011F 080000]
|
||||
16:01:47,815 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011F 18066000000000000000000000000000]
|
||||
16:01:47,815 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 180600]
|
||||
16:01:47,835 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 08600000000000000000000000000000]
|
||||
8: unknown:1806 {1806} internal, hidden
|
||||
16:01:47,835 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 090000]
|
||||
16:01:47,855 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 18140001000000000000000000000000]
|
||||
16:01:47,855 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000A 181400]
|
||||
16:01:47,875 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000A 09000100000000000000000000000000]
|
||||
9: CHANGE HOST {1814}
|
||||
16:01:47,875 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011A 0A0000]
|
||||
16:01:47,895 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011A 1B040003000000000000000000000000]
|
||||
16:01:47,895 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000A 1B0400]
|
||||
16:01:47,915 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000A 0A000300000000000000000000000000]
|
||||
10: REPROG CONTROLS V4 {1B04}
|
||||
16:01:47,915 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011E 0B0000]
|
||||
16:01:47,935 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011E 22010001000000000000000000000000]
|
||||
16:01:47,935 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000E 220100]
|
||||
16:01:47,957 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000E 0B000100000000000000000000000000]
|
||||
11: ADJUSTABLE DPI {2201}
|
||||
16:01:47,957 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011A 0C0000]
|
||||
16:01:47,977 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011A 21000000000000000000000000000000]
|
||||
16:01:47,977 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 210000]
|
||||
16:01:47,997 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 0C000000000000000000000000000000]
|
||||
12: VERTICAL SCROLLING {2100}
|
||||
16:01:47,998 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0C0C 000000]
|
||||
16:01:48,017 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0C0C 03180000000000000000000000000000]
|
||||
Roller type: 3G
|
||||
Ratchet per turn: 24
|
||||
Scroll lines: 0
|
||||
16:01:48,017 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011A 0D0000]
|
||||
16:01:48,037 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011A 21100000000000000000000000000000]
|
||||
16:01:48,037 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0008 211000]
|
||||
16:01:48,057 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0008 0D000000000000000000000000000000]
|
||||
13: SMART SHIFT {2110}
|
||||
16:01:48,057 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011C 0E0000]
|
||||
16:01:48,077 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011C 21210000000000000000000000000000]
|
||||
16:01:48,077 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 212100]
|
||||
16:01:48,097 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 0E000000000000000000000000000000]
|
||||
14: HIRES WHEEL {2121}
|
||||
16:01:48,097 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0E09 000000]
|
||||
16:01:48,117 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0E09 080C0000000000000000000000000000]
|
||||
16:01:48,117 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0E1D 000000]
|
||||
16:01:48,137 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0E1D 02000000000000000000000000000000]
|
||||
16:01:48,137 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0E38 000000]
|
||||
16:01:48,159 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0E38 01000000000000000000000000000000]
|
||||
Multiplier: 8
|
||||
Has invert
|
||||
Normal wheel motion
|
||||
Has ratchet switch
|
||||
Normal wheel mode
|
||||
High resolution mode
|
||||
HID notification
|
||||
16:01:48,159 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011F 0F0000]
|
||||
16:01:48,179 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011F 65010000000000000000000000000000]
|
||||
16:01:48,179 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000A 650100]
|
||||
16:01:48,199 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000A 0F000000000000000000000000000000]
|
||||
15: GESTURE 2 {6501}
|
||||
16:01:48,199 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 100000]
|
||||
16:01:48,219 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 00C20000000000000000000000000000]
|
||||
16:01:48,219 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 00C200]
|
||||
16:01:48,239 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 10000000000000000000000000000000]
|
||||
16: unknown:00C2 {00C2}
|
||||
16:01:48,240 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011B 110000]
|
||||
16:01:48,259 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011B 18136000000000000000000000000000]
|
||||
16:01:48,259 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 181300]
|
||||
16:01:48,279 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 11600000000000000000000000000000]
|
||||
17: unknown:1813 {1813} internal, hidden
|
||||
16:01:48,279 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011C 120000]
|
||||
16:01:48,299 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011C 18306000000000000000000000000000]
|
||||
16:01:48,299 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 183000]
|
||||
16:01:48,319 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 12600000000000000000000000000000]
|
||||
18: unknown:1830 {1830} internal, hidden
|
||||
16:01:48,319 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011B 130000]
|
||||
16:01:48,339 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011B 18906000000000000000000000000000]
|
||||
16:01:48,339 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0008 189000]
|
||||
16:01:48,359 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0008 13600000000000000000000000000000]
|
||||
19: unknown:1890 {1890} internal, hidden
|
||||
16:01:48,359 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011B 140000]
|
||||
16:01:48,381 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011B 18916000000000000000000000000000]
|
||||
16:01:48,381 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0008 189100]
|
||||
16:01:48,401 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0008 14600000000000000000000000000000]
|
||||
20: unknown:1891 {1891} internal, hidden
|
||||
16:01:48,401 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011C 150000]
|
||||
16:01:48,421 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011C 18A16000000000000000000000000000]
|
||||
16:01:48,421 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000F 18A100]
|
||||
16:01:48,441 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 15600000000000000000000000000000]
|
||||
21: unknown:18A1 {18A1} internal, hidden
|
||||
16:01:48,441 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011A 160000]
|
||||
16:01:48,461 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011A 18C06000000000000000000000000000]
|
||||
16:01:48,461 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0008 18C000]
|
||||
16:01:48,481 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0008 16600000000000000000000000000000]
|
||||
22: unknown:18C0 {18C0} internal, hidden
|
||||
16:01:48,481 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011E 170000]
|
||||
16:01:48,501 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011E 1DF36000000000000000000000000000]
|
||||
16:01:48,501 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 1DF300]
|
||||
16:01:48,521 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 17600000000000000000000000000000]
|
||||
23: unknown:1DF3 {1DF3} internal, hidden
|
||||
16:01:48,521 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011E 180000]
|
||||
16:01:48,541 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011E 1E004000000000000000000000000000]
|
||||
16:01:48,541 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 1E0000]
|
||||
16:01:48,561 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 18400000000000000000000000000000]
|
||||
24: unknown:1E00 {1E00} hidden
|
||||
16:01:48,561 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 190000]
|
||||
16:01:48,581 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 1EB06000000000000000000000000000]
|
||||
16:01:48,581 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 1EB000]
|
||||
16:01:48,603 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 19600000000000000000000000000000]
|
||||
25: unknown:1EB0 {1EB0} internal, hidden
|
||||
16:01:48,603 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011F 1A0000]
|
||||
16:01:48,623 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011F 18036000000000000000000000000000]
|
||||
16:01:48,623 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000F 180300]
|
||||
16:01:48,643 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 1A600000000000000000000000000000]
|
||||
26: unknown:1803 {1803} internal, hidden
|
||||
16:01:48,643 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0118 1B0000]
|
||||
16:01:48,663 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0118 18616000000000000000000000000000]
|
||||
16:01:48,663 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0009 186100]
|
||||
16:01:48,683 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0009 1B600000000000000000000000000000]
|
||||
27: unknown:1861 {1861} internal, hidden
|
||||
16:01:48,683 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0118 1C0000]
|
||||
16:01:48,703 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0118 90016000000000000000000000000000]
|
||||
16:01:48,703 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000F 900100]
|
||||
16:01:48,723 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000F 1C600000000000000000000000000000]
|
||||
28: unknown:9001 {9001} internal, hidden
|
||||
16:01:48,723 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 1D0000]
|
||||
16:01:48,743 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 92006000000000000000000000000000]
|
||||
16:01:48,743 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000D 920000]
|
||||
16:01:48,763 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000D 1D600000000000000000000000000000]
|
||||
29: unknown:9200 {9200} internal, hidden
|
||||
16:01:48,763 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 011F 1E0000]
|
||||
16:01:48,783 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 011F 92026000000000000000000000000000]
|
||||
16:01:48,783 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000C 920200]
|
||||
16:01:48,805 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000C 1E600000000000000000000000000000]
|
||||
30: unknown:9202 {9202} internal, hidden
|
||||
16:01:48,805 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0119 1F0000]
|
||||
16:01:48,825 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0119 18056000000000000000000000000000]
|
||||
16:01:48,825 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 000B 180500]
|
||||
16:01:48,845 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 000B 1F600000000000000000000000000000]
|
||||
31: unknown:1805 {1805} internal, hidden
|
||||
16:01:48,845 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A0A 000000]
|
||||
16:01:48,865 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A0A 08000000000000000000000000000000]
|
||||
Has 8 reprogrammable keys:
|
||||
16:01:48,865 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1C 000000]
|
||||
16:01:48,885 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1C 00500038010001010000000000000000]
|
||||
16:01:48,885 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2B 005000]
|
||||
16:01:48,905 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2B 00500000000000000000000000000000]
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
16:01:48,905 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1D 010000]
|
||||
16:01:48,925 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1D 00510039010001010000000000000000]
|
||||
16:01:48,925 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A28 005100]
|
||||
16:01:48,945 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A28 00510000000000000000000000000000]
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
16:01:48,945 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1B 020000]
|
||||
16:01:48,965 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1B 0052003A310003070100000000000000]
|
||||
16:01:48,965 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2B 005200]
|
||||
16:01:48,985 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2B 00520000000000000000000000000000]
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
16:01:48,985 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1E 030000]
|
||||
16:01:49,005 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1E 0053003C310002030100000000000000]
|
||||
16:01:49,006 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A28 005300]
|
||||
16:01:49,027 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A28 00530000000000000000000000000000]
|
||||
3: BACK AS BUTTON 4 , default: BackEx => BACK AS BUTTON 4
|
||||
mse, reprogrammable, divertable, pos:0, group:2, gmask:3
|
||||
16:01:49,027 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1B 040000]
|
||||
16:01:49,047 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1B 0056003E310002030100000000000000]
|
||||
16:01:49,047 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2F 005600]
|
||||
16:01:49,067 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2F 00560000000000000000000000000000]
|
||||
4: FORWARD AS BUTTON 5 , default: BrowserForwardEx => FORWARD AS BUTTON 5
|
||||
mse, reprogrammable, divertable, pos:0, group:2, gmask:3
|
||||
16:01:49,067 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1C 050000]
|
||||
16:01:49,087 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1C 00C300A9310003070100000000000000]
|
||||
16:01:49,087 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2A 00C300]
|
||||
16:01:49,107 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2A 00C30000000000000000000000000000]
|
||||
5: unknown:00C3 , default: unknown:00A9 => unknown:00C3
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
16:01:49,107 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1A 060000]
|
||||
16:01:49,127 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1A 00C4009D310003070100000000000000]
|
||||
16:01:49,127 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2C 00C400]
|
||||
16:01:49,147 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2C 00C40000000000000000000000000000]
|
||||
6: unknown:00C4 , default: unknown:009D => unknown:00C4
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
16:01:49,147 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A1C 070000]
|
||||
16:01:49,167 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A1C 00D700B4A00004000300000000000000]
|
||||
16:01:49,167 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 0A2C 00D700]
|
||||
16:01:49,187 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 0A2C 00D70000000000000000000000000000]
|
||||
7: unknown:00D7 , default: unknown:00B4 => unknown:00D7
|
||||
divertable, virtual, pos:0, group:4, gmask:0
|
||||
16:01:49,187 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 01 070B 000000]
|
||||
16:01:49,207 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 01 070B 32140000000000000000000000000000]
|
||||
16:01:49,207 DEBUG [MainThread] logitech_receiver.hidpp20: device 1 battery 50% charged, next level 20% charge, status 0 = discharging
|
||||
Battery: 50%, discharging.
|
||||
16:01:49,207 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 210000]
|
||||
16:01:49,213 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 210814406E0402010700000000000000]
|
||||
16:01:49,213 INFO [MainThread] logitech_receiver.receiver: <UnifyingReceiver(/dev/hidraw0,3)>: found new device 2 (406E)
|
||||
|
||||
16:01:49,213 DEBUG [MainThread] logitech_receiver.base: (3) pinging device 2
|
||||
16:01:49,213 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 001F 0000F4]
|
||||
16:01:49,245 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 001F 0405F400000000000000000000000000]
|
||||
2: Wireless Illuminated Keyboard K800 new
|
||||
Codename : new
|
||||
Kind : keyboard
|
||||
Wireless PID : 406E
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 20 ms (50Hz)
|
||||
16:01:49,245 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 FF 83B5 310000]
|
||||
16:01:49,251 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 FF 83B5 31636E14131A40000007000000000000]
|
||||
Serial number: 636E1413
|
||||
16:01:49,251 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000A 000100]
|
||||
16:01:49,285 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000A 01000100000000000000000000000000]
|
||||
16:01:49,285 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 010B 000000]
|
||||
16:01:49,325 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 010B 18000000000000000000000000000000]
|
||||
16:01:49,325 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0009 000300]
|
||||
16:01:49,365 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0009 02000200000000000000000000000000]
|
||||
16:01:49,365 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 020C 000000]
|
||||
16:01:49,405 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 020C 0254FC62F90004406E00000000010000]
|
||||
16:01:49,405 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 021E 000000]
|
||||
16:01:49,446 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 021E 01424F546000000200406ED4E7454501]
|
||||
16:01:49,446 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 021C 010000]
|
||||
16:01:49,487 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 021C 0052514B6801000901406ED4E7454501]
|
||||
Bootloader: BOT 60.00.B0002
|
||||
Firmware: RQK 68.01.B0009
|
||||
The power switch is located on the top right corner.
|
||||
Supports 25 HID++ 2.0 features:
|
||||
16:01:49,487 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 000000]
|
||||
16:01:49,527 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 00000100000000000000000000000000]
|
||||
0: ROOT {0000}
|
||||
16:01:49,527 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 000100]
|
||||
16:01:49,567 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 01000100000000000000000000000000]
|
||||
1: FEATURE SET {0001}
|
||||
16:01:49,568 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000C 000300]
|
||||
16:01:49,607 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000C 02000200000000000000000000000000]
|
||||
2: DEVICE FW VERSION {0003}
|
||||
16:01:49,607 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011A 030000]
|
||||
16:01:49,647 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011A 00050000000000000000000000000000]
|
||||
16:01:49,647 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000D 000500]
|
||||
16:01:49,687 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000D 03000000000000000000000000000000]
|
||||
3: DEVICE NAME {0005}
|
||||
16:01:49,687 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011C 040000]
|
||||
16:01:49,729 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011C 1D4B0000000000000000000000000000]
|
||||
16:01:49,729 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000D 1D4B00]
|
||||
16:01:49,770 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000D 04000000000000000000000000000000]
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
16:01:49,770 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0119 050000]
|
||||
16:01:49,809 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0119 00200000000000000000000000000000]
|
||||
16:01:49,809 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000D 002000]
|
||||
16:01:49,849 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000D 05000000000000000000000000000000]
|
||||
5: RESET {0020}
|
||||
16:01:49,849 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 060000]
|
||||
16:01:49,889 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 10000000000000000000000000000000]
|
||||
16:01:49,889 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0009 100000]
|
||||
16:01:49,929 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0009 06000000000000000000000000000000]
|
||||
6: BATTERY STATUS {1000}
|
||||
16:01:49,929 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011D 070000]
|
||||
16:01:49,969 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011D 19830000000000000000000000000000]
|
||||
16:01:49,969 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000B 198300]
|
||||
16:01:50,009 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000B 07000000000000000000000000000000]
|
||||
7: unknown:1983 {1983}
|
||||
16:01:50,010 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011F 080000]
|
||||
16:01:50,051 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011F 1B040003000000000000000000000000]
|
||||
16:01:50,051 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000E 1B0400]
|
||||
16:01:50,091 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000E 08000300000000000000000000000000]
|
||||
8: REPROG CONTROLS V4 {1B04}
|
||||
16:01:50,091 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0119 090000]
|
||||
16:01:50,131 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0119 40A00000000000000000000000000000]
|
||||
16:01:50,131 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0009 40A000]
|
||||
16:01:50,171 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0009 09000000000000000000000000000000]
|
||||
9: FN INVERSION {40A0}
|
||||
16:01:50,171 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011C 0A0000]
|
||||
16:01:50,211 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011C 41000000000000000000000000000000]
|
||||
16:01:50,211 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000A 410000]
|
||||
16:01:50,251 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000A 0A000000000000000000000000000000]
|
||||
10: ENCRYPTION {4100}
|
||||
16:01:50,251 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 0B0000]
|
||||
16:01:50,293 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 45210000000000000000000000000000]
|
||||
16:01:50,293 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 452100]
|
||||
16:01:50,333 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 0B000000000000000000000000000000]
|
||||
11: KEYBOARD DISABLE {4521}
|
||||
16:01:50,334 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011E 0C0000]
|
||||
16:01:50,373 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011E 00C20000000000000000000000000000]
|
||||
16:01:50,373 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 00C200]
|
||||
16:01:50,413 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 0C000000000000000000000000000000]
|
||||
12: unknown:00C2 {00C2}
|
||||
16:01:50,414 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 0D0000]
|
||||
16:01:50,453 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 18036000000000000000000000000000]
|
||||
16:01:50,454 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0008 180300]
|
||||
16:01:50,493 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0008 0D600000000000000000000000000000]
|
||||
13: unknown:1803 {1803} internal, hidden
|
||||
16:01:50,494 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011D 0E0000]
|
||||
16:01:50,533 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011D 18066001000000000000000000000000]
|
||||
16:01:50,533 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000C 180600]
|
||||
16:01:50,575 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000C 0E600100000000000000000000000000]
|
||||
14: unknown:1806 {1806} internal, hidden
|
||||
16:01:50,576 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011A 0F0000]
|
||||
16:01:50,615 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011A 18116000000000000000000000000000]
|
||||
16:01:50,615 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0008 181100]
|
||||
16:01:50,655 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0008 0F600000000000000000000000000000]
|
||||
15: unknown:1811 {1811} internal, hidden
|
||||
16:01:50,656 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011B 100000]
|
||||
16:01:50,695 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011B 18306000000000000000000000000000]
|
||||
16:01:50,696 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 183000]
|
||||
16:01:50,735 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 10600000000000000000000000000000]
|
||||
16: unknown:1830 {1830} internal, hidden
|
||||
16:01:50,736 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0119 110000]
|
||||
16:01:50,775 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0119 18906000000000000000000000000000]
|
||||
16:01:50,776 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000A 189000]
|
||||
16:01:50,815 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000A 11600000000000000000000000000000]
|
||||
17: unknown:1890 {1890} internal, hidden
|
||||
16:01:50,816 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 120000]
|
||||
16:01:50,858 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 18A16000000000000000000000000000]
|
||||
16:01:50,858 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 18A100]
|
||||
16:01:50,897 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 12600000000000000000000000000000]
|
||||
18: unknown:18A1 {18A1} internal, hidden
|
||||
16:01:50,898 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 130000]
|
||||
16:01:50,937 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 1DF36000000000000000000000000000]
|
||||
16:01:50,938 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000B 1DF300]
|
||||
16:01:50,977 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000B 13600000000000000000000000000000]
|
||||
19: unknown:1DF3 {1DF3} internal, hidden
|
||||
16:01:50,978 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011B 140000]
|
||||
16:01:51,017 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011B 1E004000000000000000000000000000]
|
||||
16:01:51,018 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000E 1E0000]
|
||||
16:01:51,057 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000E 14400000000000000000000000000000]
|
||||
20: unknown:1E00 {1E00} hidden
|
||||
16:01:51,058 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011B 150000]
|
||||
16:01:51,097 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011B 1EB06000000000000000000000000000]
|
||||
16:01:51,098 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000F 1EB000]
|
||||
16:01:51,137 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000F 15600000000000000000000000000000]
|
||||
21: unknown:1EB0 {1EB0} internal, hidden
|
||||
16:01:51,138 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011D 160000]
|
||||
16:01:51,179 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011D 18616000000000000000000000000000]
|
||||
16:01:51,180 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000E 186100]
|
||||
16:01:51,220 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000E 16600000000000000000000000000000]
|
||||
22: unknown:1861 {1861} internal, hidden
|
||||
16:01:51,220 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 011D 170000]
|
||||
16:01:51,260 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 011D 1A206000000000000000000000000000]
|
||||
16:01:51,260 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0008 1A2000]
|
||||
16:01:51,300 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0008 17600000000000000000000000000000]
|
||||
23: unknown:1A20 {1A20} internal, hidden
|
||||
16:01:51,300 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0118 180000]
|
||||
16:01:51,339 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0118 18B06000000000000000000000000000]
|
||||
16:01:51,340 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 000E 18B000]
|
||||
16:01:51,379 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 000E 18600000000000000000000000000000]
|
||||
24: unknown:18B0 {18B0} internal, hidden
|
||||
16:01:51,380 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 080E 000000]
|
||||
16:01:51,419 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 080E 0F000000000000000000000000000000]
|
||||
Has 15 reprogrammable keys:
|
||||
16:01:51,420 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081C 000000]
|
||||
16:01:51,461 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081C 0022001A3A0100000000000000000000]
|
||||
16:01:51,462 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082F 002200]
|
||||
16:01:51,501 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082F 00220000000000000000000000000000]
|
||||
0: MY HOME , default: HomePage => MY HOME
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:1, group:0, gmask:0
|
||||
16:01:51,502 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0819 010000]
|
||||
16:01:51,541 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0819 000E000E3A0200000000000000000000]
|
||||
16:01:51,542 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0829 000E00]
|
||||
16:01:51,581 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0829 000E0000000000000000000000000000]
|
||||
1: Mail , default: Email => Mail
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:2, group:0, gmask:0
|
||||
16:01:51,582 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0818 020000]
|
||||
16:01:51,621 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0818 003E002D3A0300000000000000000000]
|
||||
16:01:51,622 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0828 003E00]
|
||||
16:01:51,661 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0828 003E0000000000000000000000000000]
|
||||
2: SEARCH , default: SearchForFiles => SEARCH
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:3, group:0, gmask:0
|
||||
16:01:51,662 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081B 030000]
|
||||
16:01:51,701 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081B 000800083A0400000000000000000000]
|
||||
16:01:51,702 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082E 000800]
|
||||
16:01:51,743 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082E 00080000000000000000000000000000]
|
||||
3: Application Switcher , default: Application Switcher => Application Switcher
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:4, group:0, gmask:0
|
||||
16:01:51,744 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081D 040000]
|
||||
16:01:51,784 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081D 00E200C12A0500000000000000000000]
|
||||
16:01:51,784 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082E 00E200]
|
||||
16:01:51,824 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082E 00E20000000000000000000000000000]
|
||||
4: unknown:00E2 , default: unknown:00C1 => unknown:00E2
|
||||
is FN, FN sensitive, divertable, pos:5, group:0, gmask:0
|
||||
16:01:51,824 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081A 050000]
|
||||
16:01:51,864 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081A 00E300C22A0600000000000000000000]
|
||||
16:01:51,864 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082B 00E300]
|
||||
16:01:51,904 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082B 00E30000000000000000000000000000]
|
||||
5: unknown:00E3 , default: unknown:00C2 => unknown:00E3
|
||||
is FN, FN sensitive, divertable, pos:6, group:0, gmask:0
|
||||
16:01:51,904 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0818 060000]
|
||||
16:01:51,943 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0818 0040002F3A0800000000000000000000]
|
||||
16:01:51,944 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082E 004000]
|
||||
16:01:51,984 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082E 00400000000000000000000000000000]
|
||||
6: SLEEP , default: Sleep => SLEEP
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:8, group:0, gmask:0
|
||||
16:01:51,984 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081D 070000]
|
||||
16:01:52,025 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081D 0028001D3A0900000000000000000000]
|
||||
16:01:52,026 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0828 002800]
|
||||
16:01:52,065 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0828 00280000000000000000000000000000]
|
||||
7: MEDIA PLAYER , default: Music => MEDIA PLAYER
|
||||
is FN, FN sensitive, reprogrammable, divertable, pos:9, group:0, gmask:0
|
||||
16:01:52,066 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081F 080000]
|
||||
16:01:52,106 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081F 000600062A0A00000000000000000000]
|
||||
16:01:52,106 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0828 000600]
|
||||
16:01:52,145 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0828 00060000000000000000000000000000]
|
||||
8: Previous , default: Previous => Previous
|
||||
is FN, FN sensitive, divertable, pos:10, group:0, gmask:0
|
||||
16:01:52,146 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081F 090000]
|
||||
16:01:52,186 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081F 000400042A0B00000000000000000000]
|
||||
16:01:52,186 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082B 000400]
|
||||
16:01:52,225 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082B 00040000000000000000000000000000]
|
||||
9: Play/Pause , default: Play/Pause => Play/Pause
|
||||
is FN, FN sensitive, divertable, pos:11, group:0, gmask:0
|
||||
16:01:52,226 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081B 0A0000]
|
||||
16:01:52,265 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081B 000500052A0C00000000000000000000]
|
||||
16:01:52,266 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0829 000500]
|
||||
16:01:52,308 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0829 00050000000000000000000000000000]
|
||||
10: Next , default: Next => Next
|
||||
is FN, FN sensitive, divertable, pos:12, group:0, gmask:0
|
||||
16:01:52,308 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0818 0B0000]
|
||||
16:01:52,348 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0818 00010001240000000000000000000000]
|
||||
16:01:52,348 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082C 000100]
|
||||
16:01:52,387 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082C 00010000000000000000000000000000]
|
||||
11: Volume Up , default: Volume Up => Volume Up
|
||||
nonstandard, divertable, pos:0, group:0, gmask:0
|
||||
16:01:52,388 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081C 0C0000]
|
||||
16:01:52,427 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081C 00020002240000000000000000000000]
|
||||
16:01:52,428 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082C 000200]
|
||||
16:01:52,468 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082C 00020000000000000000000000000000]
|
||||
12: Volume Down , default: Volume Down => Volume Down
|
||||
nonstandard, divertable, pos:0, group:0, gmask:0
|
||||
16:01:52,468 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 0819 0D0000]
|
||||
16:01:52,507 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 0819 00030003240000000000000000000000]
|
||||
16:01:52,508 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082D 000300]
|
||||
16:01:52,547 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082D 00030000000000000000000000000000]
|
||||
13: Mute , default: Mute => Mute
|
||||
nonstandard, divertable, pos:0, group:0, gmask:0
|
||||
16:01:52,548 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 081B 0E0000]
|
||||
16:01:52,589 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 081B 000A000A340000000000000000000000]
|
||||
16:01:52,590 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 082F 000A00]
|
||||
16:01:52,630 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 082F 000A0000000000000000000000000000]
|
||||
14: Calculator , default: Calculator => Calculator
|
||||
nonstandard, reprogrammable, divertable, pos:0, group:0, gmask:0
|
||||
16:01:52,630 DEBUG [MainThread] logitech_receiver.base: (3) <= w[10 02 060C 000000]
|
||||
16:01:52,670 DEBUG [MainThread] logitech_receiver.base: (3) => r[11 02 060C 32140000000000000000000000000000]
|
||||
16:01:52,670 DEBUG [MainThread] logitech_receiver.hidpp20: device 2 battery 50% charged, next level 20% charge, status 0 = discharging
|
||||
Battery: 50%, discharging.
|
||||
41
docs/devices/k830.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
# Provided by Mikkel Munch Mortensen
|
||||
|
||||
(solaar)
|
||||
3: Illuminated Living-Room Keyboard K830
|
||||
Codename : K830
|
||||
Kind : keyboard
|
||||
Wireless PID : 4032
|
||||
Protocol : HID++ 4.1
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 9F7C6FD7
|
||||
Firmware: RQK 56.00.B0020
|
||||
The power switch is located on the edge of top right corner.
|
||||
Supports 27 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: unknown:0020 {0020}
|
||||
6: BATTERY STATUS {1000}
|
||||
7: BACKLIGHT {1981}
|
||||
8: unknown:1B04 {1B04}
|
||||
9: unknown:2005 {2005}
|
||||
10: NEW FN INVERSION {40A2}
|
||||
11: ENCRYPTION {4100}
|
||||
12: unknown:4521 {4521}
|
||||
13: TOUCHPAD RAW XY {6100} hidden
|
||||
14: unknown:6501 {6501}
|
||||
15: unknown:00C1 {00C1}
|
||||
16: unknown:1811 {1811} internal, hidden
|
||||
17: unknown:1830 {1830} internal, hidden
|
||||
18: unknown:1890 {1890} internal, hidden
|
||||
19: unknown:18A0 {18A0} internal, hidden
|
||||
20: unknown:1DF3 {1DF3} internal, hidden
|
||||
21: unknown:1E00 {1E00} hidden
|
||||
22: unknown:1EB0 {1EB0} internal, hidden
|
||||
23: unknown:1861 {1861} internal, hidden
|
||||
24: unknown:1A20 {1A20} internal, hidden
|
||||
25: unknown:18B0 {18B0} internal, hidden
|
||||
26: unknown:1F07 {1F07} internal, hidden
|
||||
Battery: 50%, discharging.
|
||||
48
docs/devices/m185_new.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
P/N: 810-005238
|
||||
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw3
|
||||
USB id : 046d:c534
|
||||
Serial : 0
|
||||
Firmware : 29.01.B0016
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
|
||||
2: Wireless Mouse M185
|
||||
Codename : M185
|
||||
Kind : mouse
|
||||
Wireless PID : 4054
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 1377ED51
|
||||
Firmware: RQM 64.00.B0008
|
||||
The power switch is located on the base.
|
||||
Supports 20 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: RESET {0020}
|
||||
5: REPROG CONTROLS V4 {1B04}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: LOWRES WHEEL {2130}
|
||||
8: POINTER SPEED {2205}
|
||||
9: unknown:1802 {1802} internal, hidden
|
||||
10: unknown:1810 {1810} internal, hidden
|
||||
11: unknown:1830 {1830} internal, hidden
|
||||
12: unknown:1850 {1850} internal, hidden
|
||||
13: unknown:1869 {1869} internal, hidden
|
||||
14: unknown:1890 {1890} internal, hidden
|
||||
15: unknown:18B1 {18B1} internal, hidden
|
||||
16: unknown:1DF3 {1DF3} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
18: unknown:1F03 {1F03} internal, hidden
|
||||
19: unknown:1E80 {1E80} internal, hidden
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
Battery status unavailable.
|
||||
50
docs/devices/m185_old.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
P/N: 810-003496
|
||||
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw3
|
||||
USB id : 046d:c52f
|
||||
Serial : 6D0342C5
|
||||
Firmware : 30.00.B0009
|
||||
Has 1 paired device(s) out of a maximum of 1.
|
||||
Notifications: (none)
|
||||
|
||||
1: Wireless Mouse
|
||||
Codename : Wireless Mouse
|
||||
Kind : mouse
|
||||
Wireless PID : 4055
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 6D0342C5
|
||||
Firmware: RQM 65.00.B0003
|
||||
The power switch is located on the base.
|
||||
Supports 22 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: RESET {0020}
|
||||
5: BATTERY STATUS {1000}
|
||||
6: unknown:1810 {1810} internal, hidden
|
||||
7: unknown:1830 {1830} internal, hidden
|
||||
8: unknown:1802 {1802} internal, hidden
|
||||
9: unknown:1862 {1862} internal, hidden
|
||||
10: unknown:1890 {1890} internal, hidden
|
||||
11: unknown:18A0 {18A0} internal, hidden
|
||||
12: unknown:18B1 {18B1} internal, hidden
|
||||
13: REPROG CONTROLS V4 {1B04}
|
||||
14: WIRELESS DEVICE STATUS {1D4B}
|
||||
15: unknown:1DF0 {1DF0} hidden
|
||||
16: unknown:1DF3 {1DF3} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
18: unknown:1EB0 {1EB0} internal, hidden
|
||||
19: unknown:1F03 {1F03} internal, hidden
|
||||
20: LOWRES WHEEL {2130}
|
||||
21: POINTER SPEED {2205}
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
Battery: 5%, discharging.
|
||||
40
docs/devices/m235.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
Wireless Mouse M235
|
||||
Codename : M235
|
||||
Kind : mouse
|
||||
Wireless PID : 4055
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQM 65.00.B0003
|
||||
The power switch is located on the base.
|
||||
Supports 22 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: RESET {0020}
|
||||
5: BATTERY STATUS {1000}
|
||||
6: unknown:1810 {1810} internal, hidden
|
||||
7: unknown:1830 {1830} internal, hidden
|
||||
8: unknown:1802 {1802} internal, hidden
|
||||
9: unknown:1862 {1862} internal, hidden
|
||||
10: unknown:1890 {1890} internal, hidden
|
||||
11: unknown:18A0 {18A0} internal, hidden
|
||||
12: unknown:18B1 {18B1} internal, hidden
|
||||
13: REPROG CONTROLS V4 {1B04}
|
||||
14: WIRELESS DEVICE STATUS {1D4B}
|
||||
15: unknown:1DF0 {1DF0} hidden
|
||||
16: unknown:1DF3 {1DF3} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
18: unknown:1EB0 {1EB0} internal, hidden
|
||||
19: unknown:1F03 {1F03} internal, hidden
|
||||
20: LOWRES WHEEL {2130}
|
||||
21: POINTER SPEED {2205}
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
divertable, mse, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
divertable, mse, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
Battery: 70%, discharging.
|
||||
29
docs/devices/m325.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
Wireless Mouse M325
|
||||
Codename : M325
|
||||
Kind : mouse
|
||||
Wireless PID : 400A
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: ABB05E01
|
||||
Firmware: RQM 27.02.B0028
|
||||
The power switch is located on the base.
|
||||
Supports 13 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: WIRELESS DEVICE STATUS {1D4B}
|
||||
6: unknown:1DF3 {1DF3} hidden
|
||||
7: REPROG CONTROLS {1B00}
|
||||
8: unknown:1DF0 {1DF0} hidden
|
||||
9: unknown:1F03 {1F03} hidden
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HI RES SCROLLING {2120}
|
||||
12: MOUSE POINTER {2200}
|
||||
Has 5 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
3: BACK AS BUTTON 4 => Back mse, reprogrammable
|
||||
4: FORWARD AS BUTTON 5 => BrowserForward mse, reprogrammable
|
||||
@@ -40,17 +40,17 @@ Total number of HID++ 2.0 features: 12
|
||||
Firmware: RQM 27.02.B0028
|
||||
The power switch is located on the base.
|
||||
Supports 13 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: WIRELESS DEVICE STATUS {1D4B}
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: WIRELESS DEVICE STATUS {1D4B}
|
||||
6: unknown:1DF3 {1DF3} hidden
|
||||
7: REPROG CONTROLS {1B00}
|
||||
7: REPROG CONTROLS {1B00}
|
||||
8: unknown:1DF0 {1DF0} hidden
|
||||
9: unknown:1F03 {1F03} hidden
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HI RES SCROLLING {2120}
|
||||
12: MOUSE POINTER {2200}
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HI RES SCROLLING {2120}
|
||||
12: MOUSE POINTER {2200}
|
||||
Battery: 90%, discharging,
|
||||
|
||||
@@ -1,2 +1,56 @@
|
||||
No non-error messages received for GET_REG and GET_REG_LONG. Perhaps because
|
||||
this is a HID++ 2.0 device?
|
||||
Mouse
|
||||
(ltunify)
|
||||
HID++ version: 2.0
|
||||
Device index 1
|
||||
Mouse
|
||||
Name: M525
|
||||
Wireless Product ID: 4013
|
||||
Serial number: DAFA335E
|
||||
Device was unavailable, version information not available.
|
||||
Total number of HID++ 2.0 features: 12
|
||||
0: [0000] IRoot
|
||||
1: [0001] IFeatureSet
|
||||
2: [0003] IFirmwareInfo
|
||||
3: [0005] GetDeviceNameType
|
||||
4: [1000] batteryLevelStatus
|
||||
5: [1D4B] WirelessDeviceStatus
|
||||
6: [1DF3] H unknown
|
||||
7: [1B00] SpecialKeysMSEButtons
|
||||
8: [1DF0] H unknown
|
||||
9: [1F03] H unknown
|
||||
10: [2100] VerticalScrolling
|
||||
11: [2120] HiResScrolling
|
||||
12: [2200] MousePointer
|
||||
(O = obsolete feature; H = SW hidden feature;
|
||||
I = reserved for internal use)
|
||||
(solaar)
|
||||
1: Wireless Mouse M525
|
||||
Codename : M525
|
||||
Kind : mouse
|
||||
Wireless PID : 4013
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: DAFA335E
|
||||
Firmware: RQM 27.02.B0028
|
||||
The power switch is located on the base.
|
||||
Supports 13 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: WIRELESS DEVICE STATUS {1D4B}
|
||||
6: unknown:1DF3 {1DF3} hidden
|
||||
7: REPROG CONTROLS {1B00}
|
||||
8: unknown:1DF0 {1DF0} hidden
|
||||
9: unknown:1F03 {1F03} hidden
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HI RES SCROLLING {2120}
|
||||
12: MOUSE POINTER {2200}
|
||||
Has 5 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
3: BACK AS BUTTON 4 => Back mse, reprogrammable
|
||||
4: FORWARD AS BUTTON 5 => BrowserForward mse, reprogrammable
|
||||
Battery: 90%, discharging.
|
||||
|
||||
41
docs/devices/m560.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
1: Wireless Mouse M560
|
||||
Codename : M560
|
||||
Kind : mouse
|
||||
Wireless PID : 402D
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 16E6E42A
|
||||
Firmware: RQM 48.00.B0015
|
||||
The power switch is located on the base.
|
||||
Supports 22 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: unknown:1830 {1830} internal, hidden
|
||||
6: unknown:1850 {1850} internal, hidden
|
||||
7: unknown:1860 {1860} internal, hidden
|
||||
8: unknown:1890 {1890} internal, hidden
|
||||
9: unknown:18A0 {18A0} internal, hidden
|
||||
10: REPROG CONTROLS V3 {1B03}
|
||||
11: WIRELESS DEVICE STATUS {1D4B}
|
||||
12: unknown:1DF3 {1DF3} internal, hidden
|
||||
13: REPROG CONTROLS {1B00}
|
||||
14: unknown:1DF0 {1DF0} hidden
|
||||
15: unknown:1E00 {1E00} hidden
|
||||
16: unknown:18B0 {18B0} internal, hidden
|
||||
17: unknown:1E90 {1E90} internal, hidden
|
||||
18: unknown:1F03 {1F03} internal, hidden
|
||||
19: VERTICAL SCROLLING {2100}
|
||||
20: HI RES SCROLLING {2120}
|
||||
21: MOUSE POINTER {2200}
|
||||
Has 7 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: BACK AS BUTTON 4 => Win8BackHorzLeft mse, reprogrammable
|
||||
3: FORWARD AS BUTTON 5 => Win8ForwardHorzRight mse, reprogrammable
|
||||
4: unknown:00B0 => Win8MetroWin7Forward mse, reprogrammable
|
||||
5: SHOW DESKTOP HPP => Win8ShowDesktopWin7Back mse, reprogrammable
|
||||
6: unknown:00AF => Win8Charm Appswitch GifAnimation mse, reprogrammable, unknown:000020
|
||||
Battery: 70%, discharging.
|
||||
24
docs/devices/m570.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : BAFF9007
|
||||
Firmware : 12.03.B0025
|
||||
Bootloader : 02.15
|
||||
Other : AA.AA
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=221
|
||||
|
||||
1: Wireless Trackball M570
|
||||
Codename : M570
|
||||
Kind : mouse
|
||||
Wireless PID : 1028
|
||||
Protocol : HID++ 1.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 891DC05A
|
||||
Firmware: 26.00.B0003
|
||||
Bootloader: 02.06
|
||||
Other: 00.01
|
||||
The power switch is located on the base.
|
||||
Notifications: battery status (0x100000).
|
||||
Battery: 100%, discharging.
|
||||
82
docs/devices/m585.txt
Normal file
@@ -0,0 +1,82 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : E21FAD57
|
||||
Firmware : 24.06.B0030
|
||||
Bootloader : 01.08
|
||||
Other : AA.AC
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=190, 2=66
|
||||
|
||||
|
||||
2: Multi Device Silent Mouse M585/M590
|
||||
Codename : M585/M590
|
||||
Kind : mouse
|
||||
Wireless PID : 406B
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 22AEB299
|
||||
Bootloader: BOT 48.01.B0002
|
||||
Firmware: MPM 05.10.B0011
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 35 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: unknown:0021 {0021}
|
||||
7: unknown:0007 {0007}
|
||||
8: BATTERY STATUS {1000}
|
||||
9: CHANGE HOST {1814}
|
||||
10: unknown:1815 {1815}
|
||||
11: REPROG CONTROLS V4 {1B04}
|
||||
12: unknown:1C00 {1C00}
|
||||
13: POINTER SPEED {2205}
|
||||
Pointer Speed: 1.31640625
|
||||
14: VERTICAL SCROLLING {2100}
|
||||
Roller type: standard
|
||||
Ratchet per turn: 18
|
||||
Scroll lines: 0
|
||||
15: unknown:00C2 {00C2}
|
||||
16: unknown:1802 {1802} internal, hidden
|
||||
17: unknown:1803 {1803} internal, hidden
|
||||
18: unknown:1806 {1806} internal, hidden
|
||||
19: unknown:1805 {1805} internal, hidden
|
||||
20: unknown:1813 {1813} internal, hidden
|
||||
21: unknown:1830 {1830} internal, hidden
|
||||
22: unknown:1861 {1861} internal, hidden
|
||||
23: unknown:1890 {1890} internal, hidden
|
||||
24: unknown:1891 {1891} internal, hidden
|
||||
25: unknown:18A1 {18A1} internal, hidden
|
||||
26: unknown:1DF3 {1DF3} internal, hidden
|
||||
27: unknown:1E00 {1E00} hidden
|
||||
28: unknown:1EB0 {1EB0} internal, hidden
|
||||
29: unknown:18B1 {18B1} internal, hidden
|
||||
30: unknown:1850 {1850} internal, hidden
|
||||
31: unknown:1E22 {1E22}
|
||||
32: unknown:1F03 {1F03} internal, hidden
|
||||
33: unknown:18C0 {18C0} internal, hidden
|
||||
34: LOWRES WHEEL {2130}
|
||||
Wheel Reports: HID
|
||||
Has 8 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, reprogrammable, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, persistently divertable, reprogrammable, pos:0, group:2, gmask:3
|
||||
3: BACK AS BUTTON 4 , default: BackEx => BACK AS BUTTON 4
|
||||
divertable, mse, persistently divertable, reprogrammable, pos:0, group:2, gmask:3
|
||||
4: FORWARD AS BUTTON 5 , default: BrowserForwardEx => FORWARD AS BUTTON 5
|
||||
divertable, mse, persistently divertable, reprogrammable, pos:0, group:2, gmask:3
|
||||
5: LEFT SCROLL AS AC PAN , default: HorzScrollLeftSet => LEFT SCROLL AS AC PAN
|
||||
divertable, mse, persistently divertable, reprogrammable, pos:0, group:2, gmask:3
|
||||
6: RIGHT SCROLL AS AC PAN , default: HorzScrollRightSet => RIGHT SCROLL AS AC PAN
|
||||
divertable, mse, persistently divertable, reprogrammable, pos:0, group:2, gmask:3
|
||||
7: unknown:00D7 , default: unknown:00B4 => unknown:00D7
|
||||
divertable, virtual, pos:0, group:3, gmask:0
|
||||
Battery: 50%, discharging.
|
||||
@@ -36,7 +36,7 @@ registers:
|
||||
<< ( 259.270) [10 02 81F1 000000] '\x10\x02\x81\xf1\x00\x00\x00'
|
||||
>> ( 259.283) [10 02 8F81 F10300] '\x10\x02\x8f\x81\xf1\x03\x00'
|
||||
|
||||
# writing 01 here will trigger an avalance of events, most likely
|
||||
# writing 01 here will trigger an avalanche of events, most likely
|
||||
# raw input from the mouse; disable by writing 00
|
||||
<< ( 261.300) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00'
|
||||
>> ( 261.315) [10 02 81F3 000000] '\x10\x02\x81\xf3\x00\x00\x00'
|
||||
|
||||
81
docs/devices/mk220-new.txt
Normal file
@@ -0,0 +1,81 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw1
|
||||
USB id : 046d:c534
|
||||
Serial : 0
|
||||
Firmware : 29.00.B0015
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
|
||||
1: Wireless Keyboard MK270
|
||||
Codename : MK270
|
||||
Kind : keyboard
|
||||
Wireless PID : 4023
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQK 49.00.B0029
|
||||
Supports 18 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: REPROG CONTROLS {1B00}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: FN INVERSION {40A0}
|
||||
8: ENCRYPTION {4100}
|
||||
9: KEYBOARD LAYOUT {4520}
|
||||
10: unknown:1810 {1810} internal, hidden
|
||||
11: unknown:1830 {1830} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18A0 {18A0} internal, hidden
|
||||
14: unknown:18B0 {18B0} internal, hidden
|
||||
15: unknown:1DF3 {1DF3} internal, hidden
|
||||
16: unknown:1E00 {1E00} hidden
|
||||
17: unknown:1868 {1868} internal, hidden
|
||||
Has 11 reprogrammable keys:
|
||||
0: MY HOME => HomePage is FN, FN sensitive, reprogrammable
|
||||
1: Mail => Email is FN, FN sensitive, reprogrammable
|
||||
2: SEARCH => Search is FN, FN sensitive, reprogrammable
|
||||
3: Calculator => Calculator is FN, FN sensitive, reprogrammable
|
||||
4: MEDIA PLAYER => Music is FN, FN sensitive, reprogrammable
|
||||
5: Previous => Previous is FN, FN sensitive
|
||||
6: Play/Pause => Play/Pause is FN, FN sensitive
|
||||
7: Next => Next is FN, FN sensitive
|
||||
8: Mute => Mute is FN, FN sensitive
|
||||
9: Volume Down => Volume Down is FN, FN sensitive
|
||||
10: Volume Up => Volume Up is FN, FN sensitive
|
||||
Battery: 30%, discharging.
|
||||
|
||||
2: Wireless Mouse
|
||||
Codename :
|
||||
Kind : mouse
|
||||
Wireless PID : 4022
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQM 38.00.B0044
|
||||
Supports 18 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: REPROG CONTROLS {1B00}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: VERTICAL SCROLLING {2100}
|
||||
8: MOUSE POINTER {2200}
|
||||
9: unknown:1810 {1810} internal, hidden
|
||||
10: unknown:1830 {1830} internal, hidden
|
||||
11: unknown:1850 {1850} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18B0 {18B0} internal, hidden
|
||||
14: unknown:1DF3 {1DF3} internal, hidden
|
||||
15: unknown:1868 {1868} internal, hidden
|
||||
16: unknown:1869 {1869} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
Battery: 30%, discharging.
|
||||
77
docs/devices/mk220.txt
Normal file
@@ -0,0 +1,77 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw1
|
||||
USB id : 046d:c52e
|
||||
Serial : 758596BF
|
||||
Firmware : 23.01.B0006
|
||||
Has 2 paired device(s) out of a maximum of 2.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
|
||||
1: Wireless Mouse M150
|
||||
Codename : M150
|
||||
Kind : mouse
|
||||
Wireless PID : 400C
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 93850883
|
||||
Firmware: RQM 29.00.B0010
|
||||
The power switch is located on the base.
|
||||
Supports 16 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: unknown:1850 {1850} hidden
|
||||
5: unknown:1860 {1860} hidden
|
||||
6: BATTERY STATUS {1000}
|
||||
7: WIRELESS DEVICE STATUS {1D4B}
|
||||
8: unknown:1DF3 {1DF3} hidden
|
||||
9: REPROG CONTROLS {1B00}
|
||||
10: unknown:1DF0 {1DF0} hidden
|
||||
11: unknown:1E00 {1E00} hidden
|
||||
12: unknown:1E80 {1E80} hidden
|
||||
13: unknown:1F03 {1F03} hidden
|
||||
14: VERTICAL SCROLLING {2100}
|
||||
15: MOUSE POINTER {2200}
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
Battery: 90%, discharging.
|
||||
|
||||
2: Wireless Keyboard K220
|
||||
Codename : K220
|
||||
Kind : keyboard
|
||||
Wireless PID : 4005
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 5BB1D72E
|
||||
Firmware: RQK 37.00.B0011
|
||||
Supports 14 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: FEATURE INFO {0002}
|
||||
3: DEVICE FW VERSION {0003}
|
||||
4: DEVICE NAME {0005}
|
||||
5: BATTERY STATUS {1000}
|
||||
6: unknown:1820 {1820} hidden
|
||||
7: REPROG CONTROLS {1B00}
|
||||
8: REPROG CONTROLS V2 {1B01}
|
||||
9: WIRELESS DEVICE STATUS {1D4B}
|
||||
10: unknown:1DF0 {1DF0} hidden
|
||||
11: unknown:1DF3 {1DF3} hidden
|
||||
12: ENCRYPTION {4100}
|
||||
13: KEYBOARD LAYOUT {4520}
|
||||
Has 12 reprogrammable keys:
|
||||
0: FN F1 => Do Nothing One is FN, reprogrammable
|
||||
1: FN F2 => Do Nothing One is FN, reprogrammable
|
||||
2: FN F3 => Do Nothing One is FN, reprogrammable
|
||||
3: FN F4 => Do Nothing One is FN, reprogrammable
|
||||
4: FN F5 => Do Nothing One is FN, reprogrammable
|
||||
5: FN F6 => Do Nothing One is FN, reprogrammable
|
||||
6: FN F7 => Do Nothing One is FN, reprogrammable
|
||||
7: FN F8 => Do Nothing One is FN, reprogrammable
|
||||
8: FN F9 => Do Nothing One is FN, reprogrammable
|
||||
9: Mute => Mute is FN
|
||||
10: Volume Down => Volume Down is FN
|
||||
11: Volume Up => Volume Up is FN
|
||||
Battery: 90%, discharging.
|
||||
129
docs/devices/mk240-nano.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Logitech MK240 NANO Device Information
|
||||
## `solaar show all` Dump
|
||||
```
|
||||
Unifying Receiver (NOTE: NOT claimed to be supporting Unifying from the package box, the only advanced feature that may be related is 128-bit AES encryption) (M/N: C-U0010)
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c534
|
||||
Serial : 0
|
||||
Firmware : 29.01.B0016
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
|
||||
1: Wireless Keyboard MK270 (NOTE: The Product is actually "MK240 NANO Wireless Keyboard and Mouse Combo" with the "K240"(M/N: Y-R0036) keyboard model)
|
||||
Codename : MK270
|
||||
Kind : keyboard
|
||||
Wireless PID : 4023
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 4BBBBA4A
|
||||
Firmware: RQK 49.00.B0029
|
||||
Supports 18 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: REPROG CONTROLS {1B00}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: FN INVERSION {40A0}
|
||||
8: ENCRYPTION {4100}
|
||||
9: KEYBOARD LAYOUT {4520}
|
||||
10: unknown:1810 {1810} internal, hidden
|
||||
11: unknown:1830 {1830} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18A0 {18A0} internal, hidden
|
||||
14: unknown:18B0 {18B0} internal, hidden
|
||||
15: unknown:1DF3 {1DF3} internal, hidden
|
||||
16: unknown:1E00 {1E00} hidden
|
||||
17: unknown:1868 {1868} internal, hidden
|
||||
Has 11 reprogrammable keys:
|
||||
0: MY HOME => HomePage is FN, FN sensitive, reprogrammable
|
||||
1: Mail => Email is FN, FN sensitive, reprogrammable
|
||||
2: SEARCH => Search is FN, FN sensitive, reprogrammable
|
||||
3: Calculator => Calculator is FN, FN sensitive, reprogrammable
|
||||
4: MEDIA PLAYER => Music is FN, FN sensitive, reprogrammable
|
||||
5: Previous => Previous is FN, FN sensitive
|
||||
6: Play/Pause => Play/Pause is FN, FN sensitive
|
||||
7: Next => Next is FN, FN sensitive
|
||||
8: Mute => Mute is FN, FN sensitive
|
||||
9: Volume Down => Volume Down is FN, FN sensitive
|
||||
10: Volume Up => Volume Up is FN, FN sensitive
|
||||
Battery: 30%, discharging. (NOTE: Capacity readings appears to be faked, or in extremely low sensitivity)
|
||||
|
||||
2: Wireless Mouse M150 (NOTE: The Product is actually "MK240 NANO Wireless Keyboard and Mouse Combo" with the "M212"(M/N: M-R0041) mouse model)
|
||||
Codename : M150
|
||||
Kind : mouse
|
||||
Wireless PID : 4022
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQM 38.00.B0044
|
||||
Supports 18 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: REPROG CONTROLS {1B00}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: VERTICAL SCROLLING {2100}
|
||||
8: MOUSE POINTER {2200}
|
||||
9: unknown:1810 {1810} internal, hidden
|
||||
10: unknown:1830 {1830} internal, hidden
|
||||
11: unknown:1850 {1850} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18B0 {18B0} internal, hidden
|
||||
14: unknown:1DF3 {1DF3} internal, hidden
|
||||
15: unknown:1868 {1868} internal, hidden
|
||||
16: unknown:1869 {1869} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
Battery: 30%, discharging. (NOTE: Capacity readings appears to be faked, or in extremely low sensitivity, in the Logitech SetPoint utility battery level is displayed as "HIGH")
|
||||
```
|
||||
|
||||
## Connect Utility Report
|
||||
```
|
||||
Re-Connect Software Version : 2.00.3
|
||||
Dj Api Version : 2, 50, 25
|
||||
|
||||
接收器(Receiver)
|
||||
Name : 無線接收器(Wireless Receiver)
|
||||
ModelId : 0x46dc534
|
||||
Serial Number :
|
||||
Handle : 0xff000001
|
||||
Wireless Status : 0x3
|
||||
Firmware version : 029.001.00016
|
||||
Bootloader version :
|
||||
Dfu Status : 0x1
|
||||
Is Dfu Cancellable : Yes
|
||||
Max Device Capacity : 6
|
||||
|
||||
滑鼠(Mouse)
|
||||
Name :
|
||||
ModelId : 0x0
|
||||
Serial Number : 4022-00-00-00-00
|
||||
Handle : 0x2000003
|
||||
Wireless Status : 0x0
|
||||
Firmware version : 038.000.00044
|
||||
Bootloader version :
|
||||
Dfu Status : 0x1
|
||||
Is Dfu Cancellable : No
|
||||
Battery Status : 0x2
|
||||
Parent Handle : 0xff000001
|
||||
|
||||
鍵盤(Keyboard)
|
||||
Name :
|
||||
ModelId : 0x0
|
||||
Serial Number : 4023-4B-BB-BA-4A
|
||||
Handle : 0x1000002
|
||||
Wireless Status : 0x0
|
||||
Firmware version : 049.000.00029
|
||||
Bootloader version :
|
||||
Dfu Status : 0x1
|
||||
Is Dfu Cancellable : No
|
||||
Battery Status : 0x2
|
||||
Parent Handle : 0xff000001
|
||||
```
|
||||
85
docs/devices/mk270.txt
Normal file
@@ -0,0 +1,85 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw9
|
||||
USB id : 046d:c534
|
||||
Serial : 00000000
|
||||
Firmware : 29.00.B0015
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
|
||||
1: Wireless Keyboard
|
||||
Codename : MK270
|
||||
Kind : keyboard
|
||||
Wireless PID : 4023
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQK 49.00.B0029
|
||||
Supports 18 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: REPROG CONTROLS {1B00}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: FN INVERSION {40A0}
|
||||
8: ENCRYPTION {4100}
|
||||
9: KEYBOARD LAYOUT {4520}
|
||||
10: unknown:1810 {1810} internal, hidden
|
||||
11: unknown:1830 {1830} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18A0 {18A0} internal, hidden
|
||||
14: unknown:18B0 {18B0} internal, hidden
|
||||
15: unknown:1DF3 {1DF3} internal, hidden
|
||||
16: unknown:1E00 {1E00} hidden
|
||||
17: unknown:1868 {1868} internal, hidden
|
||||
Has 11 reprogrammable keys:
|
||||
0: MY HOME => HomePage FN sensitive, is FN, reprogrammable
|
||||
1: Mail => Email FN sensitive, is FN, reprogrammable
|
||||
2: SEARCH => Search FN sensitive, is FN, reprogrammable
|
||||
3: Calculator => Calculator FN sensitive, is FN, reprogrammable
|
||||
4: MEDIA PLAYER => Music FN sensitive, is FN, reprogrammable
|
||||
5: Previous => Previous FN sensitive, is FN
|
||||
6: Play/Pause => Play/Pause FN sensitive, is FN
|
||||
7: Next => Next FN sensitive, is FN
|
||||
8: Mute => Mute FN sensitive, is FN
|
||||
9: Volume Down => Volume Down FN sensitive, is FN
|
||||
10: Volume Up => Volume Up FN sensitive, is FN
|
||||
Battery: 30%, discharging.
|
||||
|
||||
2: Wireless Mouse
|
||||
Codename : M185
|
||||
Kind : mouse
|
||||
Wireless PID : 4038
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 00000000
|
||||
Firmware: RQM 54.00.B0004
|
||||
Supports 22 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: unknown:1830 {1830} internal, hidden
|
||||
6: unknown:1850 {1850} internal, hidden
|
||||
7: unknown:1860 {1860} internal, hidden
|
||||
8: unknown:1890 {1890} internal, hidden
|
||||
9: unknown:18A0 {18A0} internal, hidden
|
||||
10: unknown:18C0 {18C0} internal, hidden
|
||||
11: WIRELESS DEVICE STATUS {1D4B}
|
||||
12: unknown:1DF3 {1DF3} internal, hidden
|
||||
13: REPROG CONTROLS {1B00}
|
||||
14: unknown:1DF0 {1DF0} hidden
|
||||
15: unknown:1E00 {1E00} hidden
|
||||
16: unknown:1E80 {1E80} internal, hidden
|
||||
17: unknown:1E90 {1E90} internal, hidden
|
||||
18: unknown:1F03 {1F03} internal, hidden
|
||||
19: VERTICAL SCROLLING {2100}
|
||||
20: MOUSE POINTER {2200}
|
||||
21: unknown:18B0 {18B0} internal, hidden
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
Battery: 70%, discharging.
|
||||
60
docs/devices/mk520.txt
Normal file
@@ -0,0 +1,60 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw3
|
||||
USB id : 046d:c52b
|
||||
Serial : AF9C98A0
|
||||
Firmware : 12.03.B0025
|
||||
Bootloader : 02.15
|
||||
Other : AA.AA
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=22, 2=167
|
||||
|
||||
1: Wireless Mouse M310/M310t
|
||||
Codename : M310/M310t
|
||||
Kind : mouse
|
||||
Wireless PID : 4031
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 03E8C0B4
|
||||
Firmware: RQM 40.01.B0017
|
||||
The power switch is located on the base.
|
||||
Supports 21 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: BATTERY STATUS {1000}
|
||||
5: unknown:1830 {1830} internal, hidden
|
||||
6: unknown:1850 {1850} internal, hidden
|
||||
7: unknown:1860 {1860} internal, hidden
|
||||
8: unknown:1890 {1890} internal, hidden
|
||||
9: unknown:18A0 {18A0} internal, hidden
|
||||
10: unknown:18C0 {18C0} internal, hidden
|
||||
11: WIRELESS DEVICE STATUS {1D4B}
|
||||
12: unknown:1DF3 {1DF3} internal, hidden
|
||||
13: REPROG CONTROLS {1B00}
|
||||
14: unknown:1DF0 {1DF0} hidden
|
||||
15: unknown:1E00 {1E00} hidden
|
||||
16: unknown:1E80 {1E80} internal, hidden
|
||||
17: unknown:1E90 {1E90} internal, hidden
|
||||
18: unknown:1F03 {1F03} internal, hidden
|
||||
19: VERTICAL SCROLLING {2100}
|
||||
20: MOUSE POINTER {2200}
|
||||
Has 3 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
Battery: 70%, discharging.
|
||||
|
||||
2: Wireless Keyboard K520
|
||||
Codename : K520
|
||||
Kind : keyboard
|
||||
Wireless PID : 2011
|
||||
Protocol : HID++ 1.0
|
||||
Polling rate : 20 ms (50Hz)
|
||||
Serial number: ACDE97EF
|
||||
Firmware: 26.00.B0012
|
||||
Other: 00.07
|
||||
The power switch is located on the top case.
|
||||
Notifications: (none).
|
||||
Battery: full, discharging.
|
||||
84
docs/devices/mx-master-2s.txt
Normal file
@@ -0,0 +1,84 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw7
|
||||
USB id : 046d:c52b
|
||||
Serial : BB7D9447
|
||||
Firmware : 24.01.B0023
|
||||
Bootloader : 01.08
|
||||
Other : AA.AD
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 2=156
|
||||
|
||||
1: Wireless Mouse MX Master 2S
|
||||
Codename : MX Master 2S
|
||||
Kind : mouse
|
||||
Wireless PID : 4069
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: EAB71F76
|
||||
Bootloader: BOT 56.01.B0006
|
||||
Firmware: MPM 12.01.B0006
|
||||
Firmware: MPM 12.01.B0006
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 32 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: unknown:0021 {0021}
|
||||
7: BATTERY STATUS {1000}
|
||||
8: unknown:1806 {1806} internal, hidden
|
||||
9: CHANGE HOST {1814}
|
||||
10: REPROG CONTROLS V4 {1B04}
|
||||
11: ADJUSTABLE DPI {2201}
|
||||
12: VERTICAL SCROLLING {2100}
|
||||
Roller type: 3G
|
||||
Ratchet per turn: 24
|
||||
Scroll lines: 0
|
||||
13: SMART SHIFT {2110}
|
||||
14: HIRES WHEEL {2121}
|
||||
Multiplier: 8
|
||||
Has invert
|
||||
Normal wheel motion
|
||||
Has ratchet switch
|
||||
Normal wheel mode
|
||||
High resolution mode
|
||||
HID notification
|
||||
15: GESTURE 2 {6501}
|
||||
16: unknown:00C2 {00C2}
|
||||
17: unknown:1813 {1813} internal, hidden
|
||||
18: unknown:1830 {1830} internal, hidden
|
||||
19: unknown:1890 {1890} internal, hidden
|
||||
20: unknown:1891 {1891} internal, hidden
|
||||
21: unknown:18A1 {18A1} internal, hidden
|
||||
22: unknown:18C0 {18C0} internal, hidden
|
||||
23: unknown:1DF3 {1DF3} internal, hidden
|
||||
24: unknown:1E00 {1E00} hidden
|
||||
25: unknown:1EB0 {1EB0} internal, hidden
|
||||
26: unknown:1803 {1803} internal, hidden
|
||||
27: unknown:1861 {1861} internal, hidden
|
||||
28: unknown:9001 {9001} internal, hidden
|
||||
29: unknown:9200 {9200} internal, hidden
|
||||
30: unknown:9202 {9202} internal, hidden
|
||||
31: unknown:1805 {1805} internal, hidden
|
||||
Has 8 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
3: BACK AS BUTTON 4 , default: BackEx => BACK AS BUTTON 4
|
||||
mse, reprogrammable, divertable, pos:0, group:2, gmask:3
|
||||
4: FORWARD AS BUTTON 5 , default: BrowserForwardEx => FORWARD AS BUTTON 5
|
||||
mse, reprogrammable, divertable, pos:0, group:2, gmask:3
|
||||
5: unknown:00C3 , default: unknown:00A9 => unknown:00C3
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
6: unknown:00C4 , default: unknown:009D => unknown:00C4
|
||||
mse, reprogrammable, divertable, pos:0, group:3, gmask:7
|
||||
7: unknown:00D7 , default: unknown:00B4 => unknown:00D7
|
||||
divertable, virtual, pos:0, group:4, gmask:0
|
||||
Battery: 50%, discharging.
|
||||
82
docs/devices/mx-master-3.txt
Normal file
@@ -0,0 +1,82 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw0
|
||||
USB id : 046d:c52b
|
||||
Serial : 32A29DF4
|
||||
Firmware : 12.09.B0030
|
||||
Bootloader : 04.16
|
||||
Other : AA.AA
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless (0x000100)
|
||||
Device activity counters: 1=18
|
||||
|
||||
1: Wireless Mouse MX Master 3
|
||||
Codename : MX Master 3
|
||||
Kind : mouse
|
||||
Wireless PID : 4082
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: B0C9EC6E
|
||||
Bootloader: BOT 95.00.B0013
|
||||
Firmware: MPM 19.00.B0013
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 34 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: unknown:0021 {0021}
|
||||
7: BATTERY STATUS {1000}
|
||||
8: REPROG CONTROLS V4 {1B04}
|
||||
9: CHANGE HOST {1814}
|
||||
10: unknown:2250 {2250}
|
||||
11: ADJUSTABLE DPI {2201}
|
||||
12: SMART SHIFT {2110}
|
||||
13: HIRES WHEEL {2121}
|
||||
Multiplier: 15
|
||||
Has invert
|
||||
Normal wheel motion
|
||||
Has ratchet switch
|
||||
Normal wheel mode
|
||||
Low resolution mode
|
||||
HID notification
|
||||
14: unknown:2150 {2150}
|
||||
15: unknown:2251 {2251}
|
||||
16: unknown:00C2 {00C2}
|
||||
17: unknown:1802 {1802} internal, hidden
|
||||
18: unknown:1803 {1803} internal, hidden
|
||||
19: unknown:1806 {1806} internal, hidden
|
||||
20: unknown:1813 {1813} internal, hidden
|
||||
21: unknown:1805 {1805} internal, hidden
|
||||
22: unknown:1830 {1830} internal, hidden
|
||||
23: unknown:1890 {1890} internal, hidden
|
||||
24: unknown:1891 {1891} internal, hidden
|
||||
25: unknown:18A1 {18A1} internal, hidden
|
||||
26: unknown:1DF3 {1DF3} internal, hidden
|
||||
27: unknown:1E00 {1E00} hidden
|
||||
28: unknown:1EB0 {1EB0} internal, hidden
|
||||
29: unknown:1861 {1861} internal, hidden
|
||||
30: unknown:9001 {9001} internal, hidden
|
||||
31: unknown:9203 {9203} internal, hidden
|
||||
32: unknown:9205 {9205} internal, hidden
|
||||
33: unknown:9300 {9300} internal, hidden
|
||||
Has 8 reprogrammable keys:
|
||||
0: LEFT CLICK , default: LeftClick => LEFT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
1: RIGHT CLICK , default: RightClick => RIGHT CLICK
|
||||
mse, pos:0, group:1, gmask:1
|
||||
2: MIDDLE BUTTON , default: MiddleMouseButton => MIDDLE BUTTON
|
||||
divertable, mse, reprogrammable, pos:0, group:3, gmask:7
|
||||
3: BACK AS BUTTON 4 , default: BackEx => BACK AS BUTTON 4
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
4: FORWARD AS BUTTON 5 , default: BrowserForwardEx => FORWARD AS BUTTON 5
|
||||
divertable, mse, reprogrammable, pos:0, group:2, gmask:3
|
||||
5: unknown:00C3 , default: unknown:00A9 => unknown:00C3
|
||||
divertable, mse, reprogrammable, pos:0, group:3, gmask:7
|
||||
6: unknown:00C4 , default: unknown:009D => unknown:00C4
|
||||
divertable, mse, reprogrammable, pos:0, group:3, gmask:7
|
||||
7: unknown:00D7 , default: unknown:00B4 => unknown:00D7
|
||||
divertable, virtual, pos:0, group:4, gmask:0
|
||||
Battery: 100%, discharging.
|
||||
54
docs/devices/mx-master.txt
Normal file
@@ -0,0 +1,54 @@
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw3
|
||||
USB id : 046d:c52b
|
||||
Serial : 0E039B8F
|
||||
Firmware : 24.01.B0023
|
||||
Bootloader : 01.08
|
||||
Other : AA.AC
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless, software present (0x000900)
|
||||
Device activity counters: 1=134
|
||||
|
||||
1: Wireless Mouse MX Master
|
||||
Codename : MX Master
|
||||
Kind : mouse
|
||||
Wireless PID : 4041
|
||||
Protocol : HID++ 4.5
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: A975E230
|
||||
Bootloader: BOT 18.00.B0012
|
||||
Firmware: MPM 11.00.B0012
|
||||
Firmware: MPM 11.00.B0012
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 29 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: DEVICE FW VERSION {0003}
|
||||
3: DEVICE NAME {0005}
|
||||
4: WIRELESS DEVICE STATUS {1D4B}
|
||||
5: RESET {0020}
|
||||
6: BATTERY STATUS {1000}
|
||||
7: CHANGE HOST {1814}
|
||||
8: REPROG CONTROLS V4 {1B04}
|
||||
9: ADJUSTABLE DPI {2201}
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: SMART SHIFT {2110}
|
||||
12: HIRES WHEEL {2121}
|
||||
13: GESTURE 2 {6501}
|
||||
14: DFUCONTROL 2 {00C1}
|
||||
15: unknown:1813 {1813} internal, hidden
|
||||
16: unknown:1830 {1830} internal, hidden
|
||||
17: unknown:1890 {1890} internal, hidden
|
||||
18: unknown:18A1 {18A1} internal, hidden
|
||||
19: unknown:18C0 {18C0} internal, hidden
|
||||
20: unknown:1DF3 {1DF3} internal, hidden
|
||||
21: unknown:1E00 {1E00} hidden
|
||||
22: unknown:1EB0 {1EB0} internal, hidden
|
||||
23: unknown:1803 {1803} internal, hidden
|
||||
24: unknown:1861 {1861} internal, hidden
|
||||
25: unknown:9000 {9000} internal, hidden
|
||||
26: unknown:9200 {9200} internal, hidden
|
||||
27: unknown:9240 {9240} internal, hidden
|
||||
28: unknown:1805 {1805} internal, hidden
|
||||
Battery: 50%, discharging.
|
||||
@@ -7,7 +7,7 @@
|
||||
>> ( 1.097) [10 01 8101 020000] '\x10\x01\x81\x01\x02\x00\x00'
|
||||
|
||||
# battery (07 means full)
|
||||
<< ( 7.335) [10 01 8107 000000] '\x10\x01\x81\x07\x00\x00\x00'
|
||||
<< ( 7.335) [10 01 8107 000000] '\x10\x01\x81\x07\x00\x00\x00'
|
||||
>> ( 7.382) [10 01 8107 070000] '\x10\x01\x81\x07\x07\x00\x00'
|
||||
|
||||
# Set LEDS - ab cd 00, where a/b/c/d values are 1=off, 2=on, 3=flash
|
||||
|
||||
109
docs/devices/t400.txt
Normal file
@@ -0,0 +1,109 @@
|
||||
Receiver
|
||||
LZ2388S-DJ
|
||||
M/N:C-U0007
|
||||
(ltunify)
|
||||
Serial number: E6B794F8
|
||||
Firmware version: 012.001.00019
|
||||
Bootloader version: BL.002.014
|
||||
|
||||
Mouse
|
||||
(ltunify)
|
||||
HID++ version: 2.0
|
||||
Device index 1
|
||||
Mouse
|
||||
Name: T400
|
||||
Wireless Product ID: 4026
|
||||
Serial number: 131A3093
|
||||
Device was unavailable, version information not available.
|
||||
Total number of HID++ 2.0 features: 27
|
||||
0: [0000] IRoot
|
||||
1: [0001] IFeatureSet
|
||||
2: [0002] unknown
|
||||
3: [0003] IFirmwareInfo
|
||||
4: [0005] GetDeviceNameType
|
||||
5: [00C0] DFUControl
|
||||
6: [1000] batteryLevelStatus
|
||||
7: [1802] HI unknown
|
||||
8: [1810] HI unknown
|
||||
9: [1830] HI unknown
|
||||
10: [1850] HI unknown
|
||||
11: [1860] HI unknown
|
||||
12: [1890] HI unknown
|
||||
13: [18A0] HI unknown
|
||||
14: [18E3] HI unknown
|
||||
15: [1B00] SpecialKeysMSEButtons
|
||||
16: [1D4B] WirelessDeviceStatus
|
||||
17: [1DF3] HI unknown
|
||||
18: [1E00] H unknown
|
||||
19: [1E80] HI unknown
|
||||
20: [1F03] HI unknown
|
||||
21: [1F04] HI unknown
|
||||
22: [2100] VerticalScrolling
|
||||
23: [2101] H unknown
|
||||
24: [2120] HiResScrolling
|
||||
25: [2200] MousePointer
|
||||
26: [6110] H TouchmouseRawPoints
|
||||
27: [1B03] ReprogControlsV3
|
||||
(O = obsolete feature; H = SW hidden feature;
|
||||
I = reserved for internal use)
|
||||
(solaar)
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw2
|
||||
USB id : 046d:c52b
|
||||
Serial : E6B794F8
|
||||
Firmware : 12.01.B0019
|
||||
Bootloader : 02.14
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
Device activity counters: 1=134
|
||||
|
||||
1: Zone Touch Mouse T400
|
||||
Codename : T400
|
||||
Kind : mouse
|
||||
Wireless PID : 4026
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 131A3093
|
||||
Firmware: RQM 39.00.B0029
|
||||
Bootloader: BL 03.00
|
||||
Hardware: 72
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 28 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: FEATURE INFO {0002}
|
||||
3: DEVICE FW VERSION {0003}
|
||||
4: DEVICE NAME {0005}
|
||||
5: DFUCONTROL {00C0}
|
||||
6: BATTERY STATUS {1000}
|
||||
7: unknown:1802 {1802} internal, hidden
|
||||
8: unknown:1810 {1810} internal, hidden
|
||||
9: unknown:1830 {1830} internal, hidden
|
||||
10: unknown:1850 {1850} internal, hidden
|
||||
11: unknown:1860 {1860} internal, hidden
|
||||
12: unknown:1890 {1890} internal, hidden
|
||||
13: unknown:18A0 {18A0} internal, hidden
|
||||
14: unknown:18E3 {18E3} internal, hidden
|
||||
15: REPROG CONTROLS {1B00}
|
||||
16: WIRELESS DEVICE STATUS {1D4B}
|
||||
17: unknown:1DF3 {1DF3} internal, hidden
|
||||
18: unknown:1E00 {1E00} hidden
|
||||
19: unknown:1E80 {1E80} internal, hidden
|
||||
20: unknown:1F03 {1F03} internal, hidden
|
||||
21: unknown:1F04 {1F04} internal, hidden
|
||||
22: VERTICAL SCROLLING {2100}
|
||||
23: unknown:2101 {2101} hidden
|
||||
24: HI RES SCROLLING {2120}
|
||||
25: MOUSE POINTER {2200}
|
||||
26: TOUCHMOUSE RAW POINTS {6110} hidden
|
||||
27: REPROG CONTROLS V3 {1B03}
|
||||
Has 7 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
2: MIDDLE BUTTON => MiddleMouseButton mse, reprogrammable
|
||||
3: METRO START SCREEN => MetroStartScreen mse, reprogrammable
|
||||
4: ZOOMIN => Do Nothing mse, reprogrammable
|
||||
5: ZOOMOUT => Do Nothing mse, reprogrammable
|
||||
6: BACK HSCROLL => TouchBackForwardHorzScroll
|
||||
Battery: 100%, discharging.
|
||||
94
docs/devices/t650.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
Receiver
|
||||
LZ2458D-DJ
|
||||
M/N:C-U0008
|
||||
(ltunify)
|
||||
Serial number: 28E69A3E
|
||||
Firmware version: 024.000.00018
|
||||
Bootloader version: BL.000.006
|
||||
|
||||
Touchpad
|
||||
(ltunify)
|
||||
HID++ version: 2.0
|
||||
Device index 1
|
||||
Touchpad
|
||||
Name: T650
|
||||
Wireless Product ID: 4101
|
||||
Serial number: 22205A4D
|
||||
Device was unavailable, version information not available.
|
||||
Total number of HID++ 2.0 features: 22
|
||||
0: [0000] IRoot
|
||||
1: [0001] IFeatureSet
|
||||
2: [0002] unknown
|
||||
3: [0003] IFirmwareInfo
|
||||
4: [0005] GetDeviceNameType
|
||||
5: [1000] batteryLevelStatus
|
||||
6: [1D4B] WirelessDeviceStatus
|
||||
7: [1DF3] HI unknown
|
||||
8: [1B00] SpecialKeysMSEButtons
|
||||
9: [1F03] HI unknown
|
||||
10: [2100] VerticalScrolling
|
||||
11: [2120] HiResScrolling
|
||||
12: [2200] MousePointer
|
||||
13: [00C0] DFUControl
|
||||
14: [1E80] HI unknown
|
||||
15: [6100] TouchpadRawXy
|
||||
16: [1860] HI unknown
|
||||
17: [1E00] H unknown
|
||||
18: [1B01] ReprogControlsV2
|
||||
19: [1890] HI unknown
|
||||
20: [18E5] HI unknown
|
||||
21: [18A0] HI unknown
|
||||
22: [1830] HI unknown
|
||||
(O = obsolete feature; H = SW hidden feature;
|
||||
I = reserved for internal use)
|
||||
(solaar)
|
||||
Unifying Receiver
|
||||
Device path : /dev/hidraw2
|
||||
USB id : 046d:c52b
|
||||
Serial : 28E69A3E
|
||||
Firmware : 24.00.B0018
|
||||
Bootloader : 00.06
|
||||
Has 1 paired device(s) out of a maximum of 6.
|
||||
Notifications: (none)
|
||||
Device activity counters: 1=221
|
||||
|
||||
1: Wireless Rechargeable Touchpad T650
|
||||
Codename : T650
|
||||
Kind : touchpad
|
||||
Wireless PID : 4101
|
||||
Protocol : HID++ 2.0
|
||||
Polling rate : 8 ms (125Hz)
|
||||
Serial number: 22205A4D
|
||||
Firmware: RQM 41.01.B0037
|
||||
Bootloader: BL 03.00
|
||||
Hardware: 72
|
||||
Other:
|
||||
The power switch is located on the base.
|
||||
Supports 23 HID++ 2.0 features:
|
||||
0: ROOT {0000}
|
||||
1: FEATURE SET {0001}
|
||||
2: FEATURE INFO {0002}
|
||||
3: DEVICE FW VERSION {0003}
|
||||
4: DEVICE NAME {0005}
|
||||
5: BATTERY STATUS {1000}
|
||||
6: WIRELESS DEVICE STATUS {1D4B}
|
||||
7: unknown:1DF3 {1DF3} internal, hidden
|
||||
8: REPROG CONTROLS {1B00}
|
||||
9: unknown:1F03 {1F03} internal, hidden
|
||||
10: VERTICAL SCROLLING {2100}
|
||||
11: HI RES SCROLLING {2120}
|
||||
12: MOUSE POINTER {2200}
|
||||
13: DFUCONTROL {00C0}
|
||||
14: unknown:1E80 {1E80} internal, hidden
|
||||
15: TOUCHPAD RAW XY {6100}
|
||||
16: unknown:1860 {1860} internal, hidden
|
||||
17: unknown:1E00 {1E00} hidden
|
||||
18: REPROG CONTROLS V2 {1B01}
|
||||
19: unknown:1890 {1890} internal, hidden
|
||||
20: unknown:18E5 {18E5} internal, hidden
|
||||
21: unknown:18A0 {18A0} internal, hidden
|
||||
22: unknown:1830 {1830} internal, hidden
|
||||
Has 2 reprogrammable keys:
|
||||
0: LEFT CLICK => LeftClick mse, reprogrammable
|
||||
1: RIGHT CLICK => RightClick mse, reprogrammable
|
||||
Battery: 50%, discharging.
|
||||
214
docs/features.md
Normal file
@@ -0,0 +1,214 @@
|
||||
---
|
||||
title: List of HID++ 2.0 features
|
||||
layout: page
|
||||
---
|
||||
|
||||
# List of HID++ 2.0 features
|
||||
|
||||
## Feature status
|
||||
|
||||
See functions in `hidpp20.py` and `settings_templates.py`
|
||||
|
||||
Feature | ID | Status | Notes
|
||||
---------------------------------------|----------|:------------------:|------
|
||||
`ROOT` | `0x0000` | :heavy_check_mark: | System
|
||||
`FEATURE_SET` | `0x0001` | :heavy_check_mark: | System
|
||||
`FEATURE_INFO` | `0x0002` | :heavy_check_mark: | System
|
||||
`DEVICE_FW_VERSION` | `0x0003` | :heavy_check_mark: | `get_firmware`, read only
|
||||
`DEVICE_UNIT_ID` | `0x0004` | :x: |
|
||||
`DEVICE_NAME` | `0x0005` | :heavy_check_mark: | `get_kind`, `get_name`, read only
|
||||
`DEVICE_GROUPS` | `0x0006` | :x: |
|
||||
`DEVICE_FRIENDLY_NAME` | `0x0007` | :x: |
|
||||
`KEEP_ALIVE` | `0x0008` | :x: |
|
||||
`RESET` | `0x0020` | :x: | aka “Config Change”
|
||||
`CRYPTO_ID` | `0x0021` | :x: |
|
||||
`TARGET_SOFTWARE` | `0x0030` | :x: |
|
||||
`WIRELESS_SIGNAL_STRENGTH` | `0x0080` | :x: |
|
||||
`DFUCONTROL_LEGACY` | `0x00C0` | :x: |
|
||||
`DFUCONTROL_UNSIGNED` | `0x00C1` | :x: |
|
||||
`DFUCONTROL_SIGNED` | `0x00C2` | :x: |
|
||||
`DFU` | `0x00D0` | :x: |
|
||||
`BATTERY_STATUS` | `0x1000` | :heavy_check_mark: | `get_battery`, read only
|
||||
`BATTERY_VOLTAGE` | `0x1001` | :heavy_check_mark: | `get_voltage`, read only
|
||||
`CHARGING_CONTROL` | `0x1010` | :x: |
|
||||
`LED_CONTROL` | `0x1300` | :x: |
|
||||
`GENERIC_TEST` | `0x1800` | :x: |
|
||||
`DEVICE_RESET` | `0x1802` | :x: |
|
||||
`OOBSTATE` | `0x1805` | :x: |
|
||||
`CONFIG_DEVICE_PROPS` | `0x1806` | :x: |
|
||||
`CHANGE_HOST` | `0x1814` | :heavy_check_mark: |
|
||||
`HOSTS_INFO` | `0x1815` | :heavy_plus_sign: | `get_host_names`, partial listing only
|
||||
`BACKLIGHT` | `0x1981` | :x: |
|
||||
`BACKLIGHT2` | `0x1982` | :heavy_check_mark: | `_feature_backlight2`
|
||||
`BACKLIGHT3` | `0x1983` | :x: |
|
||||
`PRESENTER_CONTROL` | `0x1A00` | :x: |
|
||||
`SENSOR_3D` | `0x1A01` | :x: |
|
||||
`REPROG_CONTROLS` | `0x1B00` | :heavy_plus_sign: | `get_keys`, only listing
|
||||
`REPROG_CONTROLS_V2` | `0x1B01` | :x: |
|
||||
`REPROG_CONTROLS_V2_2` | `0x1B02` | :x: |
|
||||
`REPROG_CONTROLS_V3` | `0x1B03` | :x: |
|
||||
`REPROG_CONTROLS_V4` | `0x1B04` | :heavy_plus_sign: | `get_keys`, _feature_reprogrammable_keys
|
||||
`REPORT_HID_USAGE` | `0x1BC0` | :x: |
|
||||
`PERSISTENT_REMAPPABLE_ACTION` | `0x1C00` | :wrench: |
|
||||
`WIRELESS_DEVICE_STATUS` | `0x1D4B` | :heavy_plus_sign: | status reporting from device
|
||||
`REMAINING_PAIRING` | `0x1DF0` | :x: |
|
||||
`FIRMWARE_PROPERTIES` | `0x1F1F` | :x: |
|
||||
`ADC_MEASUREMENT` | `0x1F20` | :x: |
|
||||
`LEFT_RIGHT_SWAP` | `0x2001` | :x: |
|
||||
`SWAP_BUTTON_CANCEL` | `0x2005` | :x: |
|
||||
`POINTER_AXIS_ORIENTATION` | `0x2006` | :x: |
|
||||
`VERTICAL_SCROLLING` | `0x2100` | :heavy_check_mark: | `get_vertical_scrolling_info`, read only
|
||||
`SMART_SHIFT` | `0x2110` | :heavy_check_mark: | `_feature_smart_shift`
|
||||
`HI_RES_SCROLLING` | `0x2120` | :heavy_check_mark: | `get_hi_res_scrolling_info`, `_feature_hi_res_scroll`
|
||||
`HIRES_WHEEL` | `0x2121` | :heavy_check_mark: | `get_hires_wheel`, `_feature_hires_smooth_invert`, `_feature_hires_smooth_resolution`
|
||||
`LOWRES_WHEEL` | `0x2130` | :heavy_check_mark: | `get_lowres_wheel_status`, `_feature_lowres_smooth_scroll`
|
||||
`THUMB_WHEEL` | `0x2150` | :heavy_check_mark: | `_feature_thumb_mode`, `_feature_thumb_invert`
|
||||
`MOUSE_POINTER` | `0x2200` | :heavy_check_mark: | `get_mouse_pointer_info`, read only
|
||||
`ADJUSTABLE_DPI` | `0x2201` | :heavy_check_mark: | `_feature_adjustable_dpi`
|
||||
`POINTER_SPEED` | `0x2205` | :heavy_check_mark: | `get_pointer_speed_info`, `_feature_pointer_speed`
|
||||
`ANGLE_SNAPPING` | `0x2230` | :x: |
|
||||
`SURFACE_TUNING` | `0x2240` | :x: |
|
||||
`HYBRID_TRACKING` | `0x2400` | :x: |
|
||||
`FN_INVERSION` | `0x40A0` | :heavy_check_mark: | `_feature_fn_swap`
|
||||
`NEW_FN_INVERSION` | `0x40A2` | :heavy_check_mark: | `get_new_fn_inversion`, `_feature_new_fn_swap`
|
||||
`K375S_FN_INVERSION` | `0x40A3` | :heavy_check_mark: | `_feature_k375s_fn_swap`
|
||||
`ENCRYPTION` | `0x4100` | :x: |
|
||||
`LOCK_KEY_STATE` | `0x4220` | :x: |
|
||||
`SOLAR_DASHBOARD` | `0x4301` | :x: |
|
||||
`KEYBOARD_LAYOUT` | `0x4520` | :x: | read only
|
||||
`KEYBOARD_DISABLE_KEYS` | `0x4521` | :heavy_check_mark: | `_feature_disable_keyboard_keys`
|
||||
`KEYBOARD_DISABLE_BY_USAGE` | `0x4522` | :x: |
|
||||
`DUALPLATFORM` | `0x4530` | :heavy_check_mark: | `_feature_dualplatform`, untested
|
||||
`MULTIPLATFORM` | `0x4531` | :heavy_check_mark: | `_feature_multiplatform`
|
||||
`KEYBOARD_LAYOUT_2` | `0x4540` | :x: | read only
|
||||
`CROWN` | `0x4600` | :x: |
|
||||
`TOUCHPAD_FW_ITEMS` | `0x6010` | :x: |
|
||||
`TOUCHPAD_SW_ITEMS` | `0x6011` | :x: |
|
||||
`TOUCHPAD_WIN8_FW_ITEMS` | `0x6012` | :x: |
|
||||
`TAP_ENABLE` | `0x6020` | :x: |
|
||||
`TAP_ENABLE_EXTENDED` | `0x6021` | :x: |
|
||||
`CURSOR_BALLISTIC` | `0x6030` | :x: |
|
||||
`TOUCHPAD_RESOLUTION` | `0x6040` | :x: |
|
||||
`TOUCHPAD_RAW_XY` | `0x6100` | :x: |
|
||||
`TOUCHMOUSE_RAW_POINTS` | `0x6110` | :x: |
|
||||
`TOUCHMOUSE_6120` | `0x6120` | :x: |
|
||||
`GESTURE` | `0x6500` | :x: |
|
||||
`GESTURE_2` | `0x6501` | :heavy_plus_sign: | `_feature_gesture2_gestures`, `_feature_gesture2_params`
|
||||
`GKEY` | `0x8010` | :x: |
|
||||
`MKEYS` | `0x8020` | :x: |
|
||||
`MR` | `0x8030` | :x: |
|
||||
`BRIGHTNESS_CONTROL` | `0x8040` | :x: |
|
||||
`REPORT_RATE` | `0x8060` | :x: | in progress
|
||||
`COLOR_LED_EFFECTS` | `0x8070` | :x: |
|
||||
`RGB_EFFECTS` | `0X8071` | :x: |
|
||||
`PER_KEY_LIGHTING` | `0x8080` | :x: |
|
||||
`PER_KEY_LIGHTING_V2` | `0x8081` | :x: |
|
||||
`MODE_STATUS` | `0x8090` | :x: |
|
||||
`ONBOARD_PROFILES` | `0x8100` | :x: | in progress
|
||||
`MOUSE_BUTTON_SPY` | `0x8110` | :x: |
|
||||
`LATENCY_MONITORING` | `0x8111` | :x: |
|
||||
`GAMING_ATTACHMENTS` | `0x8120` | :x: |
|
||||
`FORCE_FEEDBACK` | `0x8123` | :x: |
|
||||
`SIDETONE` | `0x8300` | :x: |
|
||||
`EQUALIZER` | `0x8310` | :x: |
|
||||
`HEADSET_OUT` | `0x8320` | :x: |
|
||||
|
||||
A “read only” note means the feature is a read-only feature and cannot be changed.
|
||||
|
||||
## Implementing a feature
|
||||
|
||||
Features are implemented as settable features in
|
||||
lib/logitech_receiver/settings_templates.py
|
||||
some features also have direct implementation in
|
||||
lib/logitech_receiver/hidpp20.py
|
||||
|
||||
In most cases it should suffice to only implement the settable feature
|
||||
interface for each setting in the feature. That will add one or more
|
||||
widgets in the Solaar main window to show and change the setting,
|
||||
will permit storing and restoring changed settings, and
|
||||
will output the feature settings in `solaar show`.
|
||||
|
||||
Adding a setting implementation involves several steps, described here and
|
||||
illustrated by the pointer speed setting implementation.
|
||||
|
||||
First add a name, a label, and a description for the setting in the common strings section.
|
||||
The name is used in the persistent settings structure to store and restore changed settings and
|
||||
should be a valid Python identifier. (Some older settings have dashes.)
|
||||
The label is displayed in the Solaar main window and the description is used as a tooltip there.
|
||||
The label and description should be specified as translatable strings.
|
||||
|
||||
```python
|
||||
_POINTER_SPEED = ('pointer_speed',
|
||||
_("Sensitivity (Pointer Speed)"),
|
||||
_("How fast the pointer moves"))
|
||||
```
|
||||
|
||||
Next implement an interface for the setting by creating
|
||||
a reader/writer, a validator, and a setting instance for it.
|
||||
Most settings use device features and thus need feature interfaces.
|
||||
Some settings use device register and thus need register interfaces.
|
||||
Only implement a register interface for the setting if you are very brave and
|
||||
you have access to a device that has a register interface for the setting.
|
||||
Register interfaces cannot be auto-discovered and need to be stated in descriptors.py
|
||||
for each device with the register interface.
|
||||
|
||||
The reader/writer instance is responsible for reading raw values
|
||||
from the device and writing values to it.
|
||||
There are different classes for feature interfaces and register interfaces.
|
||||
Pointer speed is a feature so the _FeatureRW reader/writer is used.
|
||||
Reader/writers take the register or feature ID and the command numbers for reading and writing,
|
||||
plus other arguments for complex interfaces.
|
||||
|
||||
The validator instance is responsible for turning read raw values into Python data
|
||||
and Python data into raw values to be written and validating that the Python data is
|
||||
acceptable for the setting.
|
||||
There are several possible kinds of Python data for setting interfaces,
|
||||
ranging from simple toggles, to ranges, to fixed lists, to
|
||||
dynamic choices, to maps of dynamic choices.
|
||||
Pointer speed is a setting whose values are integers in a range so a _RangeV validator is used.
|
||||
The arguments to this class are the
|
||||
the minimum and maximum values for the value
|
||||
and the byte size of the value on the device.
|
||||
Settings that are toggles or choices work similarly,
|
||||
but their validators have different arguments.
|
||||
Map settings have more complicated validators.
|
||||
|
||||
The setting instance keeps everything together and provides control.
|
||||
It takes the strings for the setting, the reader/writer, the validator, and
|
||||
which kinds of devices can have this setting.
|
||||
(This last is no longer used because keyboards with integrated trackpads only
|
||||
report that they are keyboards.)
|
||||
|
||||
```python
|
||||
def _feature_pointer_speed():
|
||||
"""Pointer Speed feature"""
|
||||
# min and max values taken from usb traces of Win software
|
||||
validator = _RangeV(0x002e, 0x01ff, 2)
|
||||
rw = _FeatureRW(_F.POINTER_SPEED)
|
||||
return _Setting(_POINTER_SPEED, rw, validator, device_kind=(_DK.mouse, _DK.trackball))
|
||||
```
|
||||
|
||||
Settings where the acceptable values are determined from the device
|
||||
need an auxiliary function to receive and decipher the permissible choices.
|
||||
See `_feature_adjustable_dpi_choices` for an example.
|
||||
|
||||
Finally, add an element to _SETTINGS_TABLE with
|
||||
the common strings for the setting,
|
||||
the feature ID (if any),
|
||||
the feature implementation (if any),
|
||||
the register implementation (if any).
|
||||
and
|
||||
the identifier for the setting implementation if different from the setting name.
|
||||
The identifier is used in descriptors.py to say that a device has the register or feature implementation.
|
||||
This table is used to generate the data structures for describing devices in descriptors.py
|
||||
and is also used to auto-discover feature implementations.
|
||||
|
||||
```python
|
||||
_S( _POINTER_SPEED, _F.POINTER_SPEED, _feature_pointer_speed ),
|
||||
```
|
||||
|
||||
The values to be used need to be determined from documentation of the
|
||||
feature or from reverse-engineering behavior of Logitech software under
|
||||
Windows or MacOS.
|
||||
For more information on implementing feature settings
|
||||
see the comments in lib/logitech_receiver/settings_templates.py.
|
||||
2
docs/hidpp-documentation.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Documentation on HID++ can be found in the Google drive directory
|
||||
https://drive.google.com/drive/folders/0BxbRzx7vEV7eWmgwazJ3NUFfQ28
|
||||
42
docs/i18n.md
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: Translating Solaar
|
||||
layout: page
|
||||
---
|
||||
|
||||
# Translating Solaar
|
||||
|
||||
First, make sure you have installed the `gettext` package.
|
||||
@@ -5,15 +10,15 @@ First, make sure you have installed the `gettext` package.
|
||||
Here are the steps to add/update a translation (you should run all scripts from
|
||||
the source root):
|
||||
|
||||
1. Get an up-to-date copy of the source files. Preferrably, make a clone on
|
||||
1. Get an up-to-date copy of the source files. Preferably, make a clone on
|
||||
GitHub and clone it locally on your machine; this way you can later make a
|
||||
pull request to the main project.
|
||||
|
||||
2. Run `./tools/po-update.sh <language>`; it will create/update the file
|
||||
`./po/<language>.po`.
|
||||
|
||||
3. Edit `./po/<language>.po` with your favourite editor (just make sure it saves
|
||||
the file with the UTF-8 encoding). For each string in english (msgid), edit
|
||||
3. Edit `./po/<language>.po` with your favorite editor (just make sure it saves
|
||||
the file with the UTF-8 encoding). For each string in English (msgid), edit
|
||||
the translation (msgstr); if you leave msgstr empty, the string will remain
|
||||
untranslated.
|
||||
|
||||
@@ -22,9 +27,38 @@ the source root):
|
||||
4. Run `./tools/po-compile.sh`. It will bring up-to-date all the compiled
|
||||
language files, necessary at runtime.
|
||||
|
||||
5. Start Solaar (`./bin/solaar`). By default it will pick up the system languge
|
||||
5. Start Solaar (`./bin/solaar`). By default, it will pick up the system language
|
||||
from your environment; to start it in another language, run
|
||||
`LANGUAGE=<language> ./bin/solaar`.
|
||||
|
||||
You can edit the translation iteratively, just repeat from step 3.
|
||||
If the upstream changes, do a `git pull` and then repeat from step 2.
|
||||
|
||||
Before opening a pull request, please run `./tools/po-update.sh` again. It will
|
||||
format and sort the translation file, and ensure a minimal diff when updating
|
||||
a translation.
|
||||
|
||||
## Supported languages
|
||||
|
||||
Currently Solaar has been translated in the following languages:
|
||||
|
||||
- Français: [Papoteur][papoteur], [David Geiger][david-geiger],
|
||||
[Damien Lallement][damsweb]
|
||||
- Italiano: [Michele Olivo][micheleolivo]
|
||||
- Polski: [Adrian Piotrowicz][nexces]
|
||||
- Portuguese-BR: [Drovetto][drovetto], [Josenivaldo Benito Jr.][jrbenito]
|
||||
- Română: Daniel Pavel
|
||||
- Russian: [Dimitriy Ryazantcev][DJm00n]
|
||||
- Slovak: [Jose Riha][jose1711]
|
||||
- Svensk: [Daniel Zippert][zipperten], Emelie Snecker
|
||||
|
||||
[papoteur]: http://github.com/papoteur
|
||||
[david-geiger]: http://github.com/david-geiger
|
||||
[damsweb]: http://github.com/damsweb
|
||||
[DJm00n]: https://github.com/DJm00n
|
||||
[jose1711]: https://github.com/jose1711
|
||||
[nexces]: http://github.com/nexces
|
||||
[zipperten]: http://github.com/zipperten
|
||||
[micheleolivo]: http://github.com/micheleolivo
|
||||
[drovetto]: https://github.com/drovetto
|
||||
[jrbenito]: https://github.com/jrbenito/
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# battery icon names across various icon themes
|
||||
|
||||
B = 'battery'
|
||||
CG = 'charging'
|
||||
GB = 'gpm-battery'
|
||||
|
||||
theme (unknown) 0 0-CG 20 20-CG 40 40-CG 60 60-CG 80 80-CG 100 100-CG 100-full
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
gnome B-missing B-empty - B-caution B-caution-CG B-low B-low-CG - - B-good B-good-CG B-full B-full-CG B-full-charged
|
||||
|
||||
Humanity GB-missing GB-000 GB-000-CG GB-020 GB-020-CG GB-040 GB-040-CG GB-060 GB-060-CG GB-080 GB-080-CG GB-100 GB-100-CG GB-charged
|
||||
(gnome) - B_empty - B-caution - B-low - B_two_thirds - B_third_fourth - B_full B_plugged B_charged
|
||||
|
||||
elementary B-missing B-000 B-000-CG B-020 B-020-CG B-040 B-040-CG B-060 B-060-CG B-080 B-080-CG B-100 B-100-CG B-charged
|
||||
(gnome) - B-empty - B-caution B-caution-CG B-low B-low-CG - - B-good B-good-CG B-full B-full-CG B-full-charged
|
||||
- B_empty - - - - - B_two_thirds - B_third_fourth - B_full B_plugged B_charged
|
||||
|
||||
faenza - GB-000 GB-000-CG GB-020 GB-020-CG GB-040 GB-040-CG GB-060 GB-060-CG GB-080 GB-080-CG GB-100 GB-100-CG GB-charged
|
||||
(gnome) - B_empty - B_caution - B_low - B_two_thirds - B_third_fourth - B_full B_plugged B_charged
|
||||
|
||||
ubuntu-mono GB-missing GB-000 GB-000-CG GB-020 GB-020-CG GB-040 GB-040-CG GB-060 GB-060-CG GB-080 GB-080-CG GB-100 GB-100-CG GB-charged
|
||||
(Humanity) B-000 B-000-CG B-020 B-020-CG B-040 B-040-CG B-060 B-060-CG B-080 B-080-CG B-100 B-100-CG B-charged
|
||||
B_empty - B-caution - B-low - - - - - B_full - B_charged
|
||||
|
||||
oxygen B-missing B-low B-CG-low B-caution B-CG-caution B-040 B-CG-040 B-060 B-CG-060 B-080 B-CG-080 B-100 B-CG -
|
||||
|
||||
moblin - - - B-low - B-caution - - - B-good - B-full B-charging -
|
||||
|
||||
nuvola B-missing B-low - - - - - - - - - - B-charging -
|
||||
|
||||
|
||||
|
||||
# weather icons (for lux)
|
||||
174
docs/index.md
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
title: Solaar
|
||||
layout: default
|
||||
---
|
||||
|
||||
**Solaar** is a Linux manager for many Logitech keyboards, mice, and trackpads
|
||||
that connect wirelessly to a USB [Unifying][unifying], Lightspeed, or Nano receiver,
|
||||
connect directly via a USB cable, or connect via Bluetooth.
|
||||
Solaar does not work with peripherals from other companies.
|
||||
|
||||
Documentation here is for the current verison of Solaar.
|
||||
Some Linux distributions distribute old versions of Solaar.
|
||||
If you are using an old version and something described here does not work you should upgrade
|
||||
using one of the methods described below.
|
||||
|
||||
Solaar can be used as a GUI application or via its command-line interface.
|
||||
Both interfaces are able to list the connected devices and
|
||||
show information about each device, often including battery status.
|
||||
Solaar is able to pair and unpair devices with
|
||||
receivers as supported by the device and receiver.
|
||||
Solaar can also control some changeable features of devices,
|
||||
such as smooth scrolling or function key behavior.
|
||||
Solaar keeps track of these changed settings on a per-computer basis and the GUI application restores them whenever a device connects.
|
||||
(Devices forget most settings when powered down.)
|
||||
For more information on how to use Solaar see
|
||||
[the usage page](https://pwr-solaar.github.io/Solaar/usage),
|
||||
and for more information on its capabilities see
|
||||
[the capabilities page](https://pwr-solaar.github.io/Solaar/capabilities).
|
||||
|
||||
|
||||
Solaar's GUI normally uses an icon in the system tray and starts with its main window visible.
|
||||
This aspect of Solaar depends on having an active system tray, which is not the default
|
||||
situation for recent versions of Gnome. For information on to set up a system tray under Gnome see
|
||||
[the capabilities page](https://pwr-solaar.github.io/Solaar/capabilities).
|
||||
|
||||
Solaar's GUI can be started in several ways
|
||||
|
||||
- `--window=show` (the default) starts with its main window visible,
|
||||
- `--window=hide` starts with its main window hidden,
|
||||
- `--window=only` does not use the system tray, and starts with main window visible.
|
||||
|
||||
For more information on Solaar's command-line interface use the help option,
|
||||
as in `solaar --help`.
|
||||
|
||||
Solaar does not process normal input from devices. It is thus unable
|
||||
to fix problems that arise from incorrect handling of mouse movements or keycodes
|
||||
by Linux drivers or other software.
|
||||
|
||||
Solaar has progressed past version 1.0. Problems with earlier versions should
|
||||
not be reported as bugs. Instead, upgrade to a recent version or manually install
|
||||
the current version from [GitHub](https://github.com/pwr-Solaar/Solaar).
|
||||
Some capabilities of Solaar have been developed by observing the behavior of
|
||||
Logitech receivers and devices and generalizing from these observations.
|
||||
If your Logitech receiver or device behaves strangely this may be caused by
|
||||
an incorrect behavior generalization.
|
||||
Please report such experiences by creating an issue in
|
||||
[the Solaar repository](https://github.com/pwr-Solaar/Solaar/issues).
|
||||
|
||||
[unifying]: https://en.wikipedia.org/wiki/Logitech_Unifying_receiver
|
||||
|
||||
|
||||
## Supported Devices
|
||||
|
||||
Solaar will detect all devices paired with supported Unifying, Lightspeed, or Nano
|
||||
receivers, and at the very least display some basic information about them.
|
||||
Solaar will detect some Logitech devices that connect via a USB cable or Bluetooth.
|
||||
|
||||
Solaar can pair and unpair a Logitech device showing the Unifying logo
|
||||
(Solaar's version of the [logo][logo])
|
||||
with any Unifying receiver and
|
||||
can pair and unpair Lightspeed devices with Lightspeed receivers for the same model.
|
||||
Solaar can pair some Logitech devices with Logitech Nano receivers but not all Logitech
|
||||
devices can be paired with Nano receivers.
|
||||
Logitech devices without a Unifying logo
|
||||
generally cannot be paired with Unifying receivers.
|
||||
|
||||
Solaar does not handle connecting or disconnecting via Bluetooth,
|
||||
which is done using the usual Bluetooth mechanisms.
|
||||
|
||||
For a partial list of supported devices
|
||||
and their features, see [the devices page](https://pwr-solaar.github.io/Solaar/devices).
|
||||
|
||||
[logo]: https://pwr-solaar.github.io/Solaar/assets/solaar.svg
|
||||
|
||||
## Prebuilt packages
|
||||
|
||||
Up-to-date prebuilt packages are available for some Linux distros
|
||||
(e.g., Fedora 33+) in their standard repositories.
|
||||
If a recent version of Solaar is not
|
||||
available from the standard repositories for your distribution you can try
|
||||
one of these packages.
|
||||
|
||||
- Arch solaar package in the [community repository][arch]
|
||||
- Ubuntu/Kubuntu 16.04+: use the solaar package from [universe repository][universe repository]
|
||||
- Ubuntu/Kubuntu stable packages: use the [Solaar stable ppa][ppa2], courtesy of [gogo][ppa4]
|
||||
- Ubuntu/Kubuntu git build packages: use the [Solaar git ppa][ppa1], courtesy of [gogo][ppa4]
|
||||
- a [Gentoo package][gentoo], courtesy of Carlos Silva and Tim Harder
|
||||
- a [Mageia package][mageia], courtesy of David Geiger
|
||||
|
||||
Solaar uses a standard system tray implementation; solaar-gnome3 is no longer required for gnome or unity integration.
|
||||
|
||||
[ppa4]: https://launchpad.net/~trebelnik-stefina
|
||||
[ppa2]: https://launchpad.net/~solaar-unifying/+archive/ubuntu/stable
|
||||
[ppa1]: https://launchpad.net/~solaar-unifying/+archive/ubuntu/ppa
|
||||
[ppa]: http://launchpad.net/~daniel.pavel/+archive/solaar
|
||||
[arch]: https://www.archlinux.org/packages/community/any/solaar/
|
||||
[gentoo]: https://packages.gentoo.org/packages/app-misc/solaar
|
||||
[mageia]: http://mageia.madb.org/package/show/release/cauldron/application/0/name/solaar
|
||||
[universe repository]: http://packages.ubuntu.com/search?keywords=solaar&searchon=names&suite=all§ion=all
|
||||
|
||||
## Manual installation
|
||||
|
||||
See [the installation page](https://pwr-solaar.github.io/Solaar/installation)
|
||||
for the step-by-step procedure for manual installation.
|
||||
|
||||
## Known Issues
|
||||
|
||||
- If some icons appear broken in the application, make sure you've properly
|
||||
configured the Gtk theme and icon theme in your control panel.
|
||||
|
||||
- There are several implementations of the system tray. Some of these have problems
|
||||
that can result in missing or wrong-sized icons.
|
||||
|
||||
- The icon in the system tray can show up as 'black on black' in dark
|
||||
themes or as non-symbolic when the theme uses symbolic icons. This is due to problems
|
||||
in some system tray implementations. Changing to a different theme may help.
|
||||
The `--battery-icons=symbolic` option can be used to force symbolic icons.
|
||||
|
||||
- Sometimes the system tray icon does not show up. The cause of this is unknown.
|
||||
Either wait a while and try again or try with the `--window=hide` option.
|
||||
|
||||
- Running the command-line application while the GUI
|
||||
application is also running *may* occasionally cause either of them to become
|
||||
confused about the state of the devices.
|
||||
|
||||
- Some Linux drivers view or modify the setting Scroll Wheel Resolution to
|
||||
implement smooth scrolling. If Solaar changes this setting after the driver is
|
||||
set up scrolling can be either very fast or very slow. To fix this problem
|
||||
click on the icon at the right edge of the setting to set it to
|
||||
"Ignore this setting".
|
||||
The mouse has to be reset (e.g., by turning it off and on again) before this fix will take effect.
|
||||
|
||||
- Many gaming mice have both the ONBOARD PROFILES feature and the REPORT RATE feature.
|
||||
On these mice changing the Polling Rate setting requires modifying a setting in
|
||||
the ONBOARD PROFILES feature, which can modify how the mouse works. Changing the
|
||||
Polling Rate setting to "Ignore this setting" (see above) prevents Solaar from
|
||||
modifying the ONBOARD PROFILES feature.
|
||||
The mouse has to be reset (e.g., by turning it off and on again) before this fix will take effect.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This software is distributed under the terms of the
|
||||
[GNU Public License, v2](COPYING).
|
||||
|
||||
## Thanks
|
||||
|
||||
This project began as a third-hand clone of [Noah K. Tilton](https://github.com/noah)'s
|
||||
logitech-solar-k750 project on GitHub (no longer available). It was developed
|
||||
further thanks to the diggings in Logitech's HID++ protocol done by many other
|
||||
people:
|
||||
|
||||
- [Julien Danjou](http://julien.danjou.info/blog/2012/logitech-k750-linux-support),
|
||||
who also provided some internal
|
||||
[Logitech documentation](http://julien.danjou.info/blog/2012/logitech-unifying-upower)
|
||||
- [Lars-Dominik Braun](http://6xq.net/git/lars/lshidpp.git)
|
||||
- [Alexander Hofbauer](http://derhofbauer.at/blog/blog/2012/08/28/logitech-performance-mx)
|
||||
- [Clach04](http://bitbucket.org/clach04/logitech-unifying-receiver-tools)
|
||||
- [Peter Wu](https://lekensteyn.nl/logitech-unifying.html)
|
||||
- [Nestor Lopez Casado](http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28)
|
||||
provided some more Logitech specifications for the HID++ protocol
|
||||
|
||||
Also, thanks to Douglas Wagner, Julien Gascard, and Peter Wu for helping with
|
||||
application testing and supporting new devices.
|
||||
@@ -1,53 +1,112 @@
|
||||
---
|
||||
title: Manual Installation
|
||||
layout: page
|
||||
---
|
||||
|
||||
# Manual installation
|
||||
|
||||
### Requirements
|
||||
## Downloading
|
||||
|
||||
You should have a reasonably new kernel (3.2+), with the `logitech-djreceiver`
|
||||
driver enabled and loaded; also, the `udev` package must be installed and the
|
||||
daemon running. If you have a modern Linux distribution (2011+), you're most
|
||||
likely good to go.
|
||||
Clone Solaar from GitHub by `git clone https://github.com/pwr-Solaar/Solaar.git`
|
||||
|
||||
The command-line application (`bin/solaar-cli`) requires Python 2.7.3 or 3.2+
|
||||
(either version should work), and the `python-pyudev`/`python3-pyudev` package.
|
||||
## Requirements for Solaar
|
||||
|
||||
The GUI application (`bin/solaar`) also requires Gtk3, and its GObject
|
||||
Introspection bindings. The Debian/Ubuntu package names are
|
||||
`python-gi`/`python3-gi` and `gir1.2-gtk-3.0`; if you're using another
|
||||
distribution the required packages are most likely named something similar.
|
||||
If the desktop notifications bindings are also installed (`gir1.2-notify-0.7`),
|
||||
you will also get desktop notifications when devices come online/go offline.
|
||||
Installing Solaar from a repository should have set up all these requirements
|
||||
so in this situation you should be able to skip this section.
|
||||
|
||||
For gnome-shell/Unity support, you also need to have `gir1.2-appindicator3-0.1`
|
||||
installed.
|
||||
Solaar needs a reasonably new kernel with kernel modules `hid-logitech-dj`
|
||||
and `hid-logitech-hidpp` loaded.
|
||||
Most of Solaar should work fine with any kernel more recent than 5.2,
|
||||
but newer kernels might be needed for some devices to be correctly recognized and handled.
|
||||
The `udev` package must be installed and its daemon running.
|
||||
|
||||
Solaar requires Python 3.6+ and requires several packages to be installed.
|
||||
If you are running the system version of Python you should have the
|
||||
`python3-pyudev`, `python3-psutil`, `python3-xlib`,
|
||||
and `python3-yaml` or `python3-pyyaml` packages installed.
|
||||
To run the GUI Solaar also requires Gtk3 and its GObject introspection bindings.
|
||||
If you are running the system verison of Python
|
||||
the Debian/Ubuntu packages you should have
|
||||
`python3-gi` and `gir1.2-gtk-3.0` installed.
|
||||
in Fedora you need `gtk3` and `python3-gobject`.
|
||||
You may have to install `gcc` and the Python development package (`python3-dev` or `python3-devel`,
|
||||
depending on your distribution).
|
||||
|
||||
### Installation
|
||||
If you are running a version of Python different from the system version,
|
||||
you may need to use pip to install projects that provide the above Python packages.
|
||||
|
||||
Normally USB devices are not accessible for r/w by regular users, so you will
|
||||
need to do a one-time udev rule installation to allow access to the Logitech
|
||||
Unifying Receiver.
|
||||
If desktop notifications bindings are also installed
|
||||
(`gir1.2-notify-0.7` for Debian/Ubuntu),
|
||||
you will also see desktop notifications when devices come online/go offline.
|
||||
For GNOME Shell/Budgie Desktop/KDE/XFCE support, you also need to have
|
||||
`gir1.2-ayatanaappindicator3-0.1` installed in Debian/Ubuntu. Although it is
|
||||
recommended to install and use `gir1.2-ayatanaappindicator3-0.1` if it is
|
||||
available, you can also use `gir1.2-appindicator3-0.1` if necessary (e.g.,
|
||||
for Unity in Ubuntu).
|
||||
|
||||
You can run the `rules.d/install.sh` script from Solaar to do this installation
|
||||
automatically (make sure to run it as your regular desktop user, it will switch
|
||||
to root when necessary), or you can do all the required steps by hand, as the
|
||||
root user:
|
||||
### Installing Solaar's udev rule
|
||||
|
||||
1. Copy `rules.d/99-logitech-unifying-receiver.rules` from Solaar to
|
||||
`/etc/udev/rules.d/`. The `udev` daemon will automatically pick up this file
|
||||
using inotify.
|
||||
Solaar needs to write to HID devices for receivers and devices.
|
||||
To be able to do this without running as root requires a udev rule
|
||||
that gives seated users write access to the HID devices for Logitech receiver and devices.
|
||||
|
||||
By default, the rule allows all members of the `plugdev` group to have
|
||||
read/write access to the Unifying Receiver device. (standard Debian/Ubuntu
|
||||
group for pluggable devices). It may need changes, specific to your
|
||||
particular system's configuration. If in doubt, replacing `GROUP="plugdev"`
|
||||
with `GROUP="<your username>"` should just work.
|
||||
You can install this rule by copying, as root,
|
||||
`rules.d/42-logitech-unify-permissions.rules` from Solaar to
|
||||
`/etc/udev/rules.d`.
|
||||
You will probably also have to tell udev to reload its rule via
|
||||
`sudo udevadm control --reload-rules`.
|
||||
|
||||
2. Physically remove the Unifying Receiver and re-insert it.
|
||||
For this rule to set up the correct permissions for your receivers and devices
|
||||
you will then need to either disconnect your receivers and
|
||||
any USB-connected or Bluetooth-connected devices and
|
||||
re-connect them or reboot your computer.
|
||||
|
||||
This is necessary because if the receiver is already plugged-in, it already
|
||||
has a `/dev/hidrawX` device node, but with the old (`root:root`) permissions.
|
||||
Plugging it again will re-create the device node with the right permissions.
|
||||
## Running from the Download Directories
|
||||
|
||||
3. Make sure your desktop users are part of the `plugdev` group, by running
|
||||
`gpasswd <desktop username> plugdev`. If these users were not assigned to the
|
||||
group before, they must re-login for the changes to take effect.
|
||||
To run Solaar from the download directories, first install the Solaar udev rule if necessary.
|
||||
Then cd to the solaar directory and run `bin/solaar` for the GUI
|
||||
or `bin/solaar <command> <arguments>` for the CLI.
|
||||
|
||||
Do not run Solaar as root, you may encounter problems with X11 integration and with the system tray.
|
||||
|
||||
## Installing Solaar Using Pip
|
||||
|
||||
Python programs are usually installed using [pip][pip].
|
||||
The pip instructions for Solaar are in `setup.py`, the standard place to put such instructions.
|
||||
|
||||
To install Solaar for yourself only run `pip install --user .` from the solaar directory.
|
||||
This tells pip to install into your `.local` directory, but does not install Solaar's udev rule.
|
||||
(See above for installing the udev rule.)
|
||||
Once the udev rule has been installed you can then run Solaar as `~/.local/bin/solaar`.
|
||||
|
||||
Installing python programs to system directories using pip is generally frowned on both
|
||||
because this runs arbitrary code as root and because this can override existing python libraries
|
||||
that other users or even the system depend on. If you want to install solaar to /usr/local run
|
||||
`sudo bash -c 'umask 022 ; pip install .'` in the solaar directory.
|
||||
(The umask is needed so that the created files and directories can be read and executed by everyone.)
|
||||
Then solaar can be run as /usr/local/bin/solaar.
|
||||
You will also have to install the udev rule.
|
||||
|
||||
[pip]: https://en.wikipedia.org/wiki/Pip_(package_manager)
|
||||
|
||||
## Solaar in other languages
|
||||
|
||||
If you want to have Solaar's user messages in some other language you need to run
|
||||
`tools/po-compile.sh` to create the translation files before running or installing Solaar
|
||||
and set the LANGUAGE environment variable appropriately when running Solaar.
|
||||
|
||||
## Running Solaar at Startup
|
||||
|
||||
Distributions can cause Solaar can be run automatically at user login by installing a desktop file at
|
||||
`/etc/xdg/autostart/solaar.desktop`.
|
||||
|
||||
If you install Solaar yourself you may need to create or modify this file or install a startup file under your home directory.
|
||||
|
||||
## Installing from PyPI
|
||||
|
||||
As an alternative to downloading and installing you can install the most recent release
|
||||
(but not the current github version) of Solaar from PyPI.
|
||||
Just run `pip install --user solaar`.
|
||||
This will not install the Solaar udev rule, which you will need to copy from
|
||||
`~/.local/share/solaar/udev-rules.d/42-logitech-unify-permissions.rules`
|
||||
to `/etc/udev/rules.d` as root.
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
The battery/charging level and status is reported only if the related
|
||||
reporting flag in register 0x00 is enabled by the host. The
|
||||
"Battery/Charging Level" byte indicates the battery level if the
|
||||
"Charging State" indicates 0x00 ("Not Charging"). If "Charging State"
|
||||
indicates 0x21 to 0x23 ("Charging"), the "Battery/Charging Level" byte
|
||||
indicates the level of charging.
|
||||
|
||||
10 ix 07 r0 r1 r2 00
|
||||
r0 -> Battery/Charging Level
|
||||
0x00 = Reserved/Unknown
|
||||
0x01 = Critical
|
||||
0x02 = Critical (legacy value, don't use)
|
||||
0x03 = Low
|
||||
0x04 = Low (legacy value, don't use)
|
||||
0x05 = Good
|
||||
0x06 = Good (legacy value, don't use)
|
||||
0x07 = Full
|
||||
0x08..0xFF = Reserved
|
||||
r1 -> Charging state
|
||||
0x00 = Not charging
|
||||
0x01..0x1F = Reserved (not charging)
|
||||
0x20 = Unknown charging state
|
||||
0x21 = Charging
|
||||
0x22 = Charging complete
|
||||
0x23 = Charging error
|
||||
0x24..0xFF = Reserved
|
||||
@@ -1,278 +0,0 @@
|
||||
*Read short register command*
|
||||
|
||||
10 ix 81 02 00 00 00
|
||||
|
||||
ix
|
||||
|
||||
Index 0x0n: Device #n
|
||||
|
||||
0xFF: Transceiver
|
||||
|
||||
*Response to Read command (success)*
|
||||
|
||||
10 ix 81 02 00 r1 r2
|
||||
|
||||
ix
|
||||
|
||||
Index (same as command)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
r1
|
||||
|
||||
Number of Connected Devices
|
||||
|
||||
bit 0..7: Number of connected devices (receivers only)
|
||||
|
||||
r2
|
||||
|
||||
Number of Remaining Pairing Slots
|
||||
|
||||
bit 0..7: Number of remaining pairing slots
|
||||
|
||||
|
||||
*Read long register command*
|
||||
|
||||
10 ix 83 B5 nn 00 00
|
||||
|
||||
ix
|
||||
|
||||
Index 0xFF: Transceiver
|
||||
|
||||
nn
|
||||
|
||||
0x20 Device 1
|
||||
|
||||
0x21 Device 2
|
||||
|
||||
0x22 Device 3
|
||||
|
||||
0x23 Device 4
|
||||
|
||||
0x24 Device 5
|
||||
|
||||
0x25 Device 6
|
||||
|
||||
0x26..0x2F Reserved for future extensions
|
||||
|
||||
*Response to Read command (success)*
|
||||
|
||||
11 ix 83 B5 nn r1 r2 r3 r4 r5 r6 r7 r8 r9 ra rb rc rd 00 00
|
||||
|
||||
ix
|
||||
|
||||
Index (same as command)
|
||||
|
||||
nn
|
||||
|
||||
(same format as above)
|
||||
|
||||
r1
|
||||
|
||||
Destination ID
|
||||
|
||||
r2
|
||||
|
||||
Reserved
|
||||
|
||||
r3
|
||||
|
||||
Wireless PID MSB
|
||||
|
||||
r4
|
||||
|
||||
Wireless PID LSB
|
||||
|
||||
r5
|
||||
|
||||
Reserved
|
||||
|
||||
r6
|
||||
|
||||
Reserved
|
||||
|
||||
r7
|
||||
|
||||
Device type
|
||||
|
||||
0 undefined
|
||||
|
||||
1 keyboard
|
||||
|
||||
2 mouse
|
||||
|
||||
3 numpad
|
||||
|
||||
4 presenter
|
||||
|
||||
5 reserved
|
||||
|
||||
6 reserved
|
||||
|
||||
7 remote control
|
||||
|
||||
8 trackball
|
||||
|
||||
9 touchpad
|
||||
|
||||
a tablet
|
||||
|
||||
b gamepad
|
||||
|
||||
c joystick
|
||||
|
||||
r8
|
||||
|
||||
Reserved
|
||||
|
||||
r9
|
||||
|
||||
Reserved
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Alternatively, if enabled, you can also receive a notification when a new
|
||||
device is paired:
|
||||
|
||||
This message is sent by a receiver to the host SW to report a freshly
|
||||
connected device. Enable the HID++ connection reporting by setting the
|
||||
corresponding bit in register 0x00 via HID++ Set Register command.
|
||||
|
||||
*Notification*
|
||||
|
||||
10 ix 41 r0 r1 r2 r3
|
||||
|
||||
ix
|
||||
|
||||
Index
|
||||
|
||||
r0
|
||||
|
||||
bits [0..2] Protocol type
|
||||
|
||||
0x03 = eQUAD
|
||||
|
||||
0x04 = eQuad step 4 DJ
|
||||
|
||||
bits [3..7] Reserved
|
||||
|
||||
r1
|
||||
|
||||
Device Info
|
||||
|
||||
bit0..3 = Device Type
|
||||
|
||||
0x00 = Unknown
|
||||
|
||||
0x01 = Keyboard
|
||||
|
||||
0x02 = Mouse
|
||||
|
||||
0x03 = Numpad
|
||||
|
||||
0x04 = Presenter
|
||||
|
||||
|
||||
r2
|
||||
|
||||
Wireless PID LSB
|
||||
|
||||
r3
|
||||
|
||||
Wireless PID MSB
|
||||
|
||||
To enable the notifications:
|
||||
Enable HID++ Notifications:
|
||||
|
||||
This register defines a number of flags that allow the SW to turn on or off
|
||||
individual spontaneous HID++ reports. Not setting a flag means default
|
||||
reporting. See the table below for more details on each flag.
|
||||
|
||||
For all bits: *0 = disabled* (default value at power-up), 1 = enabled.
|
||||
|
||||
|
||||
|
||||
*Read short register command*
|
||||
|
||||
10 ix 81 00 00 00 00
|
||||
|
||||
ix
|
||||
|
||||
Index 0x0n: Device #n
|
||||
|
||||
0xFF: Transceiver
|
||||
|
||||
*Response to Read command (success)*
|
||||
|
||||
10 ix 81 00 r0 r1 r2
|
||||
|
||||
ix
|
||||
|
||||
Index (same as command)
|
||||
|
||||
r0
|
||||
|
||||
HID++ Reporting Flags (Devices)
|
||||
|
||||
bit 0..3. reserved
|
||||
|
||||
bit 4: Battery Status
|
||||
|
||||
bit 5..7 reserved
|
||||
|
||||
r1
|
||||
|
||||
HID++ Reporting Flags (Receiver)
|
||||
|
||||
bit 0: Wireless notifications
|
||||
|
||||
bit 1..7 reserved
|
||||
|
||||
r2
|
||||
|
||||
|
||||
|
||||
|
||||
*Write short register command*
|
||||
|
||||
10 ix 80 00 p0 p1 p2
|
||||
|
||||
ix
|
||||
|
||||
Index 0x0n: Device #n
|
||||
|
||||
0xFF: Transceiver
|
||||
|
||||
p0
|
||||
|
||||
HID++ Reporting Flags (Devices)
|
||||
|
||||
(same format as above)
|
||||
|
||||
p1
|
||||
|
||||
HID++ Reporting Flags (Receiver)
|
||||
|
||||
(same format as above)
|
||||
|
||||
p2
|
||||
|
||||
|
||||
*Response to Write command (success)*
|
||||
|
||||
10 ix 80 00 zz zz zz
|
||||
|
||||
ix
|
||||
|
||||
Index (same as command)
|
||||
|
||||
zz
|
||||
|
||||
(don't care, recommended to return 0)
|
||||
|
||||
162
docs/rules.md
Normal file
@@ -0,0 +1,162 @@
|
||||
---
|
||||
title: Rule Processing of HID++ Notifications
|
||||
layout: page
|
||||
---
|
||||
|
||||
Rule processing is an experimental feature. Significant changes might be made in response to problems.
|
||||
|
||||
Note that rule processing only fully works under X11.
|
||||
When running under Wayland with X11 libraries loaded most features will not be available and errors may result.
|
||||
Features known not to work under Wayland include process conditions and
|
||||
anything to do with simulating keyboard or mouse input.
|
||||
|
||||
Logitech devices that use HID++ version 2.0 or greater produce feature-based
|
||||
notifications that Solaar can process using a simple rule language. For
|
||||
example, using rules Solaar can emulate an `XF86_MonBrightnessDown` key tap
|
||||
in response to the pressing of the `Brightness Down` key on Craft keyboards,
|
||||
which normally does not produce any input at all when the keyboard is in
|
||||
Windows mode.
|
||||
|
||||
Solaar's rules only activate on HID++ notifications so device actions that
|
||||
normally produce HID output need rule processing have to be first be set to
|
||||
this mode. Currently Solaar can set (divert) some mouse scroll wheels, some
|
||||
mouse thumb wheels, the crown of Craft keyboards, and some keys to produce
|
||||
HID++ notifications. Look for `HID++` or `Diversion` settings to see what
|
||||
diversion can be done with your devices. Runing Solaar with the `-dd`
|
||||
option will show information about notifications, including their feature
|
||||
name, report number, and data.
|
||||
|
||||
In response to a feature-based HID++ notification Solaar runs a sequence of
|
||||
rules. A `Rule` is a sequence of components, which are either sub-rules,
|
||||
conditions, or actions. Conditions and actions are dictionaries with one
|
||||
entry whose key is the name of the condition or action and whose value is
|
||||
the argument of the action.
|
||||
|
||||
If the last thing that a rule does is execute an action, no more rules are
|
||||
processed for the notification.
|
||||
|
||||
Rules are evaluated by evaluating each of their components in order. The
|
||||
evaluation of a rule is terminated early if a condition component evaluates
|
||||
to false or the last evaluated sub-component of a component is an action. A
|
||||
rule is false if its last evaluated component evaluates to a false value.
|
||||
|
||||
`Not` conditions take a single component and are true if their component
|
||||
evaluates to a false value.
|
||||
`Or` conditions take a sequence of components and are evaluated by
|
||||
evaluating each of their components in order.
|
||||
An Or condition is terminated early if a component evaluates to true or the
|
||||
last evaluated sub-component of a component is an action.
|
||||
A Or condition is true if its last evaluated component evaluates to a true
|
||||
value. `And` conditions take a sequence of components are evaluted the same
|
||||
as rules.
|
||||
|
||||
`Process` conditions are true if the process for focus input window
|
||||
or the window's Window manager class or instance name starts with their string argument.
|
||||
`MouseProcess` conditions are true if the process for the window under the mouse
|
||||
or the window's Window manager class or instance name starts with their string argument.
|
||||
`Feature` conditions are if true if the name of the feature of the current
|
||||
notification is their string argument.
|
||||
`Report` conditions are if true if the report number in the current
|
||||
notification is their integer argument.
|
||||
`Modifiers` conditions take either a string or a sequence of strings, which
|
||||
can only be `Shift`, `Control`, `Alt`, and `Super`.
|
||||
Modifiers conditions are true if their argument is the current keyboard
|
||||
modifiers.
|
||||
`Key` conditions are true if the Logitech name of the last diverted key or button down is their
|
||||
string argument. Alternatively, if the argument is a list `[name, action]` where `action`
|
||||
is either `'pressed'` or `'released'`, the key down or key up events of `name` argument are
|
||||
matched, respectively. Logitech key and button names are shown in the `Key/Button Diversion`
|
||||
setting. Some keyboards have Gn keys, which are diverted using the 'Divert G Keys' setting.
|
||||
`Test` conditions are true if their test evaluates to true on the feature,
|
||||
report, and data of the current notification.
|
||||
Test conditions can return a number instead of a boolean.
|
||||
|
||||
Test conditions consisting of a sequence of three or four integers use the first
|
||||
two to select bytes of the notification data.
|
||||
Writing this kind of test condition is not trivial.
|
||||
Three-element test conditions are true if the selected bytes bit-wise anded
|
||||
with its third element is non-zero.
|
||||
The value of these test conditions is the result of the and.
|
||||
Four-element test conditions are true if the selected bytes form a signed
|
||||
integer between the third and fourth elements.
|
||||
The value of these test condition is the signed value of the selected bytes
|
||||
if that is non-zero otherwise True.
|
||||
|
||||
The other test conditions are mnemonic shorthands for meaningful feature,
|
||||
report, and data combinations.
|
||||
A `crown_right` test is the rotation amount of a `CROWN` right rotation.
|
||||
A `crown_left` test is the rotation amount of a `CROWN` left rotation.
|
||||
A `crown_right_ratchet` test is the ratchet amount of a `CROWN` right ratchet rotation.
|
||||
A `crown_left_ratchet` test is the ratchet amount of a `CROWN` left ratchet rotation.
|
||||
A `crown_tap` test is true for a `CROWN` tap.
|
||||
A `crown_start_press` test is true for the start of a `CROWN` press.
|
||||
A `crown_stop_press` test is true for the end of a `CROWN` press.
|
||||
A `crown_pressed` test is true for a `CROWN` notification with the Crown pressed.
|
||||
A `thumb_wheel_up` test is the rotation amount of a `THUMB WHEEL` upward rotation.
|
||||
A `thumb_wheel_down` test is the rotation amount of a `THUMB WHEEL` downward rotation.
|
||||
`lowres_wheel_up`, `lowres_wheel_down`, `hires_wheel_up`, `hires_wheel_down` are the
|
||||
same but for `LOWRES WHEEL` and `HIRES WHEEL`.
|
||||
`True` and `False` tests return True and False, respectively.
|
||||
|
||||
`Mouse Gesture` conditions are true if the actions taken while the mouse's 'Gesture' button is held match the configured list when the 'Gesture' button is released.
|
||||
The available actions are `Mouse Up`, `Mouse Down`, `Mouse Left`, `Mouse Right`, `Mouse Up-left`, `Mouse Up-Right`, `Mouse Down-left`, `Mouse Down-right`, and buttons that are diverted.
|
||||
An example would be mapping `Mouse Up` -> `Mouse Up`. To perform this gesture, you would hold down the 'Gesture' button, move the mouse upwards, pause momentarily, move the mouse upwards, and release the 'Gesture' button.
|
||||
Another example would be mapping `Back Button` -> `Back Button`. With this one, you would hold down the 'Gesture' button, double-tap the 'Back' button, and then release the 'Gesture' button.
|
||||
Mouse movements and buttons can be mixed and chained together however you like.
|
||||
It's possible to create a `No-op` gesture by clicking 'Delete' on the initial Action when you first create the rule. This gesture will trigger when you simply click the 'Gesture' button.
|
||||
|
||||
A `KeyPress` action takes a sequence of X11 key symbols and simulates a chorded keypress on the keyboard.
|
||||
Any key symbols that correspond to modifier keys that are in the current keyboard modifiers are ignored.
|
||||
A `MouseScroll` action takes a sequence of two numbers and simulates a horizontal and vertical mouse scroll of these amounts.
|
||||
If the previous condition in the parent rule returns a number the scroll amounts are multiplied by this number.
|
||||
A `MouseClick` action takes a mouse button name (`left`, `middle` or `right`) and a positive number, and simulates that number of clicks of the specified button.
|
||||
An `Execute` actions takes a program and arguments and executes it asynchronously.
|
||||
|
||||
Solaar has several built-in rules, which are run after user-created rules and so can be overridden by user-created rules.
|
||||
One rule turns
|
||||
`Brightness Down` key press notifications into `XF86_MonBrightnessDown` key taps
|
||||
and `Brightness Up` key press notifications into `XF86_MonBrightnessUp` key taps.
|
||||
Another rule makes Craft crown ratchet movements move between tabs when the crown is pressed
|
||||
and up and down otherwise.
|
||||
A third rule turns Craft crown ratchet movements into `XF86_AudioNext` or `XF86_AudioPrev` key taps when the crown is pressed and `XF86_AudioRaiseVolume` or `XF86_AudioLowerVolume` otherwise.
|
||||
A fourth rule doubles the speed of `THUMB WHEEL` movements unless the `Control` modifier is on.
|
||||
All of these rules are only active if the key or feature is diverted, of course.
|
||||
|
||||
Solaar reads rules from a YAML configuration file (normally `~/.config/solaar/rules.yaml`).
|
||||
This file contains zero or more documents, each a rule.
|
||||
|
||||
Here is a file with three rules:
|
||||
|
||||
```
|
||||
%YAML 1.3
|
||||
---
|
||||
- Feature: CROWN
|
||||
- Process: quodlibet
|
||||
- Rule: [ Test: crown_start_press, KeyPress: XF86_AudioMute ]
|
||||
- Rule: [ Test: crown_pressed, Test: crown_right_ratchet, KeyPress: XF86_AudioNext ]
|
||||
- Rule: [ Test: crown_pressed, Test: crown_left_ratchet, KeyPress: XF86_AudioPrev ]
|
||||
- Rule: [ Test: crown_right_ratchet, KeyPress: XF86_AudioRaiseVolume ]
|
||||
- Rule: [ Test: crown_left_ratchet, KeyPress: XF86_AudioLowerVolume ]
|
||||
...
|
||||
---
|
||||
- Feature: THUMB WHEEL
|
||||
- Rule: [ Modifiers: Control, Test: thumb_wheel_up, MouseScroll: [-2, 0] ]
|
||||
- Rule:
|
||||
- Modifiers: Control
|
||||
- Test: thumb_wheel_down
|
||||
- MouseScroll: [-2, 0]
|
||||
- Rule: [ Or: [ Test: thumb_wheel_up, Test: thumb_wheel_down ], MouseScroll: [-1, 0] ]
|
||||
...
|
||||
---
|
||||
- Feature: LOWRES WHEEL
|
||||
- Rule: [ Or: [ Test: lowres_wheel_up, Test: lowres_wheel_down ], MouseScroll: [0, 2] ]
|
||||
...
|
||||
```
|
||||
|
||||
Here is an example showing how to divert the Back Button on an MX Master 3 so that pressing
|
||||
the button will initiate rule processing and a rule that triggers on this notification and
|
||||
switches the mouse to host 3 after popping up a simple notification.
|
||||
|
||||

|
||||
|
||||

|
||||
164
docs/usage.md
Normal file
@@ -0,0 +1,164 @@
|
||||
---
|
||||
title: Solaar Usage
|
||||
layout: page
|
||||
---
|
||||
|
||||
# Solaar usage
|
||||
|
||||
Under normal usage Solaar creates an icon in your system tray. This icon is
|
||||
usually a battery icon showing the approximate battery level for your device
|
||||
with the lowest known battery level. If there are no devices with battery information,
|
||||
then the icon is one of the Solaar icons.
|
||||
|
||||
Solaar also has a main window. When the main window is invisible,
|
||||
click on the icon in the system tray to bring up the menu, and then
|
||||
click on a receiver or device in the menu to see information about
|
||||
that receiver or device.
|
||||
|
||||
The following is an image of the Solaar menu and the icon (the battery
|
||||
symbol is in the system tray at the left of the image). The icon can
|
||||
also be other battery icons or versions of the Logitech Unifying icon.
|
||||
|
||||

|
||||
|
||||
Clicking on “Quit” in the Solaar menu terminates the program, and “About Solaar” pops up a window with further information.
|
||||
|
||||
## Solaar options
|
||||
|
||||
There are several options that affect how Solaar behaves:
|
||||
|
||||
* `--help` shows a help message and then quits
|
||||
* `---window=show` starts Solaar with the main window showing
|
||||
* `---window=hide` starts Solaar with the main window not showing
|
||||
* `---window=only` starts Solaar with no system tray icon and the main window showing
|
||||
* `--battery-icons=symbolic` uses symbolic icons for battery levels
|
||||
|
||||
## Solaar main window
|
||||
|
||||
The Solaar main window shows your Logitech receivers and devices that Solaar
|
||||
knows about and can be used to pair new devices, unpair paired devices, and
|
||||
view and change some settings of the selected receiver or device.
|
||||
To select a receiver or device click on it in the left side of the window.
|
||||
|
||||
Closing the main window does not terminate Solaar (unless Solaar is not using the system tray).
|
||||
Clicking on “Quit Solaar” terminates the program, and “About Solaar” pops up a window with further information.
|
||||
The light bulb (or a similar icon) displays detailed information
|
||||
about the selected receiver or device (useful for debugging).
|
||||
|
||||
### Pairing and unpairing devices
|
||||
|
||||
When a receiver is selected in the main window, you can pair a new device by
|
||||
clicking on the “Pair new device” button.
|
||||
Then turn on the device and it should pair with the receiver if that is possible
|
||||
and the device is not already paired with another active receiver.
|
||||
For multi-host devices first select the host position that you want.
|
||||
Some Lightspeed devices may pair by pressing a special pairing button.
|
||||
|
||||

|
||||
|
||||
When a device is selected you can unpair the device if your receiver supports
|
||||
unpairing. To unpair the device, just click on the “Unpair” button and
|
||||
confirm in the window that pops up.
|
||||
|
||||
A receiver with the Unifying logo should be able to pair with any device
|
||||
with the Unifying logo. If there are no open pairing slots, however, you will
|
||||
not be able to pair a new device. In this case to pair a new device you
|
||||
first need to unpair a device.
|
||||
|
||||
Other receivers can only pair with certain kinds of devices.
|
||||
Most of these receivers do not allow unpairing - instead
|
||||
pairing a new device replaces an existing paired device of the same kind.
|
||||
Some receivers can only pair a limited number of times.
|
||||
|
||||
### Viewing and changing device settings
|
||||
|
||||
When a device is selected you can see the approximate battery level of the
|
||||
device, if that is reported by the device, and the status of the link
|
||||
between the device and its receiver.
|
||||
|
||||
You can also see and change the settings of devices.
|
||||
Changing settings is performed by clicking on buttons,
|
||||
moving sliders, or selecting from alternatives.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Device setings now have a clickable icon that determines whether the
|
||||
setting can be changed and whether the setting is ignored.
|
||||
|
||||

|
||||
|
||||
If the selected device that is connected to a receiver is powered down or
|
||||
otherwise disconnected its settings cannot be changed
|
||||
but it still can be unpaired if its receiver allows unpairing.
|
||||
|
||||

|
||||
|
||||
#### Remapping key and button actions
|
||||
|
||||
For many devices Solaar can remap some of their keys or buttons to
|
||||
perform a different action. (This changes the information that the
|
||||
device sends when the key or button is activated.) Only some keys on some
|
||||
devices can be remapped and they can only be remapped to a limited
|
||||
number of actions. The remapping is done by selecting a key
|
||||
or button in the left-hand box on the “Action” setting line and then
|
||||
selecting the action to be performed in the right-hand box. The default
|
||||
action is always the shown first in the list. As with all settings,
|
||||
Solaar will remember past action settings and restore them on the device
|
||||
from then on.
|
||||
|
||||

|
||||
|
||||
The names of the keys, buttons, and actions are mostly taken from Logitech
|
||||
documentation and may not be completely obvious.
|
||||
|
||||
It is possible to end up with an unusable system, for example by having no
|
||||
way to do a mouse left click, so exercise caution when remapping keys or
|
||||
buttons that are needed to operate your system.
|
||||
|
||||
## Solaar command line interface
|
||||
|
||||
Solaar also has a command line interface that can do most of what can be
|
||||
done using the main window. For more information on the
|
||||
command line interface, run `solaar --help` to see the commands and
|
||||
then `solaar <command> --help` to see the arguments to any of the commands.
|
||||
|
||||
## Solaar settings
|
||||
|
||||
Solaar supports at least the following settings:
|
||||
|
||||
Setting | Description
|
||||
---------------------------------|------------
|
||||
Hand Detection | Turn on backlight when your hands hover over the keyboard
|
||||
Scroll Wheel Smooth Scrolling | Higher-speed vertical scrolling
|
||||
Side Scrolling | When off, side scrolling sends custom button events
|
||||
Scroll Wheel High Resolution | Higher-speed vertical scrolling
|
||||
Scroll Wheel HID++ Scrolling | When on, vertical scrolling uses HID++ events
|
||||
Scroll Wheel Direction | Reverse direction of vertical scrolling
|
||||
Scroll Wheel Resolution | Higher-speed vertical scrolling (use with caution)
|
||||
Scroll Wheel Rachet | Shift wheel ratchet on and off based on wheel speed
|
||||
Thumb Wheel HID++ Scrolling | When on, thumb-wheel scrolling uses HID++ events
|
||||
Thumb Wheel Direction | Reverse direction of thumb-wheel scrolling
|
||||
Sensitivity (DPI) | Mouse movement sensitivity
|
||||
Sensitivity (Pointer Speed) | Mouse movement sensitivity
|
||||
Backlight | Turn on backlight
|
||||
Swap Fx function | Change Fn keys to normally do their special action
|
||||
DPI Sliding Adjustment | Change Sensitivity (DPI) by holding a button and moving the mouse
|
||||
Mouse Gestures | Create HID++ events by holding a button and moving the mouse
|
||||
Key/Button Actions | Change what a key or button does
|
||||
Key/Button Diversion | Divert keys and buttons to create HID++ events
|
||||
Divert crown events | Divert crown actions to create HID++ events
|
||||
Divert G Keys | Divert G keys to create HID++ events
|
||||
Disable keys | Disable one or more keys
|
||||
Set OS | Change keys to match OS
|
||||
Change Host | Connect to a different host
|
||||
Gestures | Turn on and off various (mostly touchpad) gestures
|
||||
Gesture params | Modify parameters for gestures
|
||||
|
||||
HID++ events are mostly not processed by Linux input drivers.
|
||||
Settings that involve sending HID++ events exist so that these events can be
|
||||
processed by Solaar rules instead of by Linux.
|
||||
|
||||
Different Logitech devices may implement the same functionality in different ways,
|
||||
thus the different settings that do the same thing.
|
||||
@@ -1,12 +0,0 @@
|
||||
Unifying receiver:
|
||||
046d:c52b interface: 2 driver: logitech-djreceiver
|
||||
046d:c532 interface: 2 driver: logitech-djreceiver
|
||||
|
||||
|
||||
Nano receiver, Advanced/Unifying ready:
|
||||
046d:c52f interface: 1 driver: hid-generic
|
||||
|
||||
|
||||
Nano receiver:
|
||||
046d:c51a interface: 1 driver: hid-generic
|
||||
046d:c526 interface: 1 driver: hid-generic
|
||||
@@ -9,8 +9,8 @@
|
||||
# The latest version can be obtained from
|
||||
# http://www.linux-usb.org/usb.ids
|
||||
#
|
||||
# Version: 2013.05.24
|
||||
# Date: 2013-05-24 20:34:03
|
||||
# Version: 2019.11.05
|
||||
# Date: 2019-11-05 20:34:06
|
||||
#
|
||||
|
||||
# Vendors, devices and interfaces. Please keep sorted.
|
||||
@@ -24,6 +24,7 @@
|
||||
0082 Acer Aspire 5672 Webcam
|
||||
0200 WingMan Extreme Joystick
|
||||
0203 M2452 Keyboard
|
||||
0242 Chillstream for Xbox 360
|
||||
0301 M4848 Mouse
|
||||
0401 HP PageScan
|
||||
0402 NEC PageScan
|
||||
@@ -40,16 +41,25 @@
|
||||
080f Webcam C120
|
||||
0810 QuickCam Pro
|
||||
0819 Webcam C210
|
||||
081a Webcam C260
|
||||
081b Webcam C310
|
||||
081d HD Webcam C510
|
||||
0820 QuickCam VC
|
||||
0821 HD Webcam C910
|
||||
0823 HD Webcam B910
|
||||
0825 Webcam C270
|
||||
0826 HD Webcam C525
|
||||
0828 HD Webcam B990
|
||||
082b Webcam C170
|
||||
082c HD Webcam C615
|
||||
082d HD Pro Webcam C920
|
||||
0830 QuickClip
|
||||
0836 B525 HD Webcam
|
||||
0837 BCC950 ConferenceCam
|
||||
0840 QuickCam Express
|
||||
0843 Webcam C930e
|
||||
0850 QuickCam Web
|
||||
085c C922 Pro Stream Webcam
|
||||
0870 QuickCam Express
|
||||
0890 QuickCam Traveler
|
||||
0892 OrbiCam
|
||||
@@ -96,7 +106,7 @@
|
||||
08d7 QuickCam Communicate STX
|
||||
08d8 QuickCam for Notebook Deluxe
|
||||
08d9 QuickCam IM/Connect
|
||||
08da QuickCam Messanger
|
||||
08da QuickCam Messenger
|
||||
08dd QuickCam for Notebooks
|
||||
08e0 QuickCam Express
|
||||
08e1 Labtec Webcam
|
||||
@@ -147,14 +157,25 @@
|
||||
0a0b ClearChat Pro USB
|
||||
0a0c Clear Chat Comfort USB Headset
|
||||
0a13 Z-5 Speakers
|
||||
0a14 USB Headset
|
||||
0a15 G35 Headset
|
||||
0a17 G330 Headset
|
||||
0a1f G930
|
||||
0a29 H600 [Wireless Headset]
|
||||
0a37 USB Headset H540
|
||||
0a38 Headset H340
|
||||
0a44 Headset H390
|
||||
0a45 960 Headset
|
||||
0a4d G430 Surround Sound Gaming Headset
|
||||
0a5b G933 Wireless Headset Dongle
|
||||
0a66 [G533 Wireless Headset Dongle]
|
||||
0b02 C-UV35 [Bluetooth Mini-Receiver] (HID proxy mode)
|
||||
8801 Video Camera
|
||||
b014 Bluetooth Mouse M336/M337/M535
|
||||
b305 BT Mini-Receiver
|
||||
bfe4 Premium Optical Wheel Mouse
|
||||
c000 N43 [Pilot Mouse]
|
||||
c001 N48/M-BB48 [FirstMouse Plus]
|
||||
c001 N48/M-BB48/M-UK96A [FirstMouse Plus]
|
||||
c002 M-BA47 [MouseMan Plus]
|
||||
c003 MouseMan
|
||||
c004 WingMan Gaming Mouse
|
||||
@@ -212,20 +233,32 @@
|
||||
c061 RX1500 Laser Mouse
|
||||
c062 M-UAS144 [LS1 Laser Mouse]
|
||||
c063 DELL Laser Mouse
|
||||
c064 M110 corded optical mouse (M-B0001)
|
||||
c066 G9x Laser Mouse
|
||||
c068 G500 Laser Mouse
|
||||
c069 M500 Laser Mouse
|
||||
c069 M-U0007 [Corded Mouse M500]
|
||||
c06a USB Optical Mouse
|
||||
c06b G700 Wireless Gaming Mouse
|
||||
c06c Optical Mouse
|
||||
c077 M105 Optical Mouse
|
||||
c07c M-R0017 [G700s Rechargeable Gaming Mouse]
|
||||
c07d G502 Mouse
|
||||
c07e G402 Gaming Mouse
|
||||
c080 G303 Gaming Mouse
|
||||
c083 G403 Prodigy Gaming Mouse
|
||||
c084 G203 Gaming Mouse
|
||||
c101 UltraX Media Remote
|
||||
c110 Harmony 785/885 Remote
|
||||
c110 Harmony 785/880/885 Remote
|
||||
c111 Harmony 525 Remote
|
||||
c112 Harmony 890 Remote
|
||||
c11f Harmony 900/1100 Remote
|
||||
c121 Harmony One Remote
|
||||
c122 Harmony 700 Remote
|
||||
c124 Harmony 300 Remote
|
||||
c122 Harmony 650/700 Remote
|
||||
c124 Harmony 300/350 Remote
|
||||
c125 Harmony 200 Remote
|
||||
c126 Harmony Link
|
||||
c129 Harmony Hub
|
||||
c12b Harmony Touch/Ultimate Remote
|
||||
c201 WingMan Extreme Joystick with Throttle
|
||||
c202 WingMan Formula
|
||||
c207 WingMan Extreme Digital 3D
|
||||
@@ -236,14 +269,14 @@
|
||||
c20c WingMan Precision
|
||||
c20d WingMan Attack 2
|
||||
c20e WingMan Formula GP
|
||||
c211 iTouch Cordless Reciever
|
||||
c211 iTouch Cordless Receiver
|
||||
c212 WingMan Extreme Digital 3D
|
||||
c213 J-UH16 (Freedom 2.4 Cordless Joystick)
|
||||
c214 ATK3 (Attack III Joystick)
|
||||
c215 Extreme 3D Pro
|
||||
c216 Dual Action Gamepad
|
||||
c218 Logitech RumblePad 2 USB
|
||||
c219 Cordless RumblePad 2
|
||||
c216 F310 Gamepad [DirectInput Mode]
|
||||
c218 F510 Gamepad [DirectInput Mode]
|
||||
c219 F710 Gamepad [DirectInput Mode]
|
||||
c21a Precision Gamepad
|
||||
c21c G13 Advanced Gameboard
|
||||
c21d F310 Gamepad [XInput Mode]
|
||||
@@ -255,12 +288,20 @@
|
||||
c225 G11/G15 Keyboard / G keys
|
||||
c226 G15 Refresh Keyboard
|
||||
c227 G15 Refresh Keyboard
|
||||
c228 G19 Gaming Keyboard
|
||||
c229 G19 Gaming Keyboard Macro Interface
|
||||
c22a Gaming Keyboard G110
|
||||
c22b Gaming Keyboard G110 G-keys
|
||||
c22d G510 Gaming Keyboard
|
||||
c22e G510 Gaming Keyboard onboard audio
|
||||
c231 G13 Virtual Mouse
|
||||
c245 G400 Optical Mouse
|
||||
c246 Gaming Mouse G300
|
||||
c248 G105 Gaming Keyboard
|
||||
c24a G600 Gaming Mouse
|
||||
c24c G400s Optical Mouse
|
||||
c24d G710 Gaming Keyboard
|
||||
c24e G500s Laser Gaming Mouse
|
||||
c281 WingMan Force
|
||||
c283 WingMan Force 3D
|
||||
c285 WingMan Strike Force 3D
|
||||
@@ -276,13 +317,14 @@
|
||||
c29c Speed Force Wireless Wheel for Wii
|
||||
c2a0 Wingman Force Feedback Mouse
|
||||
c2a1 WingMan Force Feedback Mouse
|
||||
c2ab G13 Joystick
|
||||
c301 iTouch Keyboard
|
||||
c302 iTouch Pro Keyboard
|
||||
c303 iTouch Keyboard
|
||||
c305 Internet Keyboard
|
||||
c307 Internet Keyboard
|
||||
c308 Internet Navigator Keyboard
|
||||
c309 Internet Keyboard
|
||||
c309 Y-BF37 [Internet Navigator Keyboard]
|
||||
c30a iTouch Composite
|
||||
c30b NetPlay Keyboard
|
||||
c30c Internet Keys (X)
|
||||
@@ -298,8 +340,14 @@
|
||||
c318 Illuminated Keyboard
|
||||
c31a Comfort Wave 450
|
||||
c31b Compact Keyboard K300
|
||||
c31c Keyboard K120 for Business
|
||||
c31c Keyboard K120
|
||||
c31d Media Keyboard K200
|
||||
c31f Comfort Keyboard K290
|
||||
c326 Washable Keyboard K310
|
||||
c328 Corded Keyboard K280e
|
||||
c332 G502 Proteus Spectrum Optical Mouse
|
||||
c335 G910 Orion Spectrum Mechanical Keyboard
|
||||
c33a G413 Gaming Keyboard
|
||||
c401 TrackMan Marble Wheel
|
||||
c402 Marble Mouse (2-button)
|
||||
c403 Turbo TrackMan Marble FX
|
||||
@@ -332,12 +380,24 @@
|
||||
c526 Nano Receiver
|
||||
c529 Logitech Keyboard + Mice
|
||||
c52b Unifying Receiver
|
||||
c52d R700 Remote Presenter receiver
|
||||
c52e MK260 Wireless Combo Receiver
|
||||
c52f Unifying Receiver
|
||||
c531 C-U0007 [Unifying Receiver]
|
||||
c532 Unifying Receiver
|
||||
c534 Unifying Receiver
|
||||
c603 3Dconnexion Spacemouse Plus XT
|
||||
c605 3Dconnexion CADman
|
||||
c606 3Dconnexion Spacemouse Classic
|
||||
c621 3Dconnexion Spaceball 5000
|
||||
c623 3Dconnexion Space Traveller 3D Mouse
|
||||
c625 3Dconnexion Space Pilot 3D Mouse
|
||||
c626 3Dconnexion Space Navigator 3D Mouse
|
||||
c627 3Dconnexion Space Explorer 3D Mouse
|
||||
c628 3Dconnexion Space Navigator for Notebooks
|
||||
c629 3Dconnexion SpacePilot Pro 3D Mouse
|
||||
c62b 3Dconnexion Space Mouse Pro
|
||||
c640 NuLOOQ navigator
|
||||
c702 Cordless Presenter
|
||||
c703 Elite Keyboard Y-RP20 + Mouse MX900 (Bluetooth)
|
||||
c704 diNovo Wireless Desktop
|
||||
@@ -360,5 +420,10 @@
|
||||
c720 Bluetooth wireless hub
|
||||
ca03 MOMO Racing
|
||||
ca04 Formula Vibration Feedback Wheel
|
||||
ca84 Cordless Controller for Xbox
|
||||
ca88 Thunderpad for Xbox
|
||||
ca8a Precision Vibration Feedback Wheel for Xbox
|
||||
caa3 DriveFX Racing Wheel
|
||||
cab1 Cordless Keyboard for Wii HID Receiver
|
||||
d001 QuickCam Pro
|
||||
f301 Controller
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
title: Solaar
|
||||
tagline: Linux devices manager for the Logitech Unifying Receiver.
|
||||
owner: pwr
|
||||
owner_url: https://github.com/pwr
|
||||
repository: https://github.com/pwr/Solaar
|
||||
version: 0.9.1
|
||||
tar_download: https://github.com/pwr/Solaar/archive/0.9.1.tar.gz
|
||||
ga_id: UA-36908718-1
|
||||
|
||||
pygments: true
|
||||
safe: true
|
||||
lsi: false
|
||||
@@ -1,14 +0,0 @@
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" style="width: 48px; height:48px; margin-bottom: -10px;">
|
||||
<defs>
|
||||
<linearGradient id="gradient_blue">
|
||||
<stop style="stop-color:#009099;stop-opacity:1" offset="0" />
|
||||
<stop style="stop-color:#00a899;stop-opacity:0.9" offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient x1="5" y1="50" x2="95" y2="50" id="gradient_rect" xlink:href="#gradient_blue" gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient x1="37" y1="50" x2="63" y2="50" id="gradient_dot" xlink:href="#gradient_blue" gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<g transform="scale(0.48)">
|
||||
<path d="M 21.5,5.5 C 12.636,5.5 5.5,12.636 5.5,21.5 L 5.5,78.5 C 5.5,87.364 12.636,94.5 21.5,94.5 L 78.5,94.5 C 87.364,94.5 94.5,87.364 94.5,78.5 L 94.5,21.5 C 94.5,12.636 87.364,5.5 78.5,5.5 L 21.5,5.5 z M 37.6875,16.6875 46.71875,32.3125 C 47.784179,32.115965 48.877705,32 50,32 51.122295,32 52.215821,32.115965 53.28125,32.3125 L 62.3125,16.6875 72.6875,22.6875 63.65625,38.3125 C 65.078123,39.972287 66.191785,41.898777 66.9375,44 L 85,44 85,56 66.9375,56 C 66.191785,58.101223 65.078123,60.027713 63.65625,61.6875 L 72.6875,77.3125 62.3125,83.3125 53.28125,67.6875 C 52.215821,67.884035 51.122295,68 50,68 48.877705,68 47.784179,67.884035 46.71875,67.6875 L 37.6875,83.3125 27.3125,77.3125 36.34375,61.6875 C 34.921877,60.027713 33.808215,58.101223 33.0625,56 L 15,56 15,44 33.0625,44 C 33.808215,41.898777 34.921877,39.972287 36.34375,38.3125 L 27.3125,22.6875 37.6875,16.6875 z" style="fill:url(#gradient_rect);fill-opacity:1;fill-rule:nonzero;stroke:#16161d;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" />
|
||||
<path d="M 62,50 A 12,12 0 1 1 38,50 12,12 0 1 1 62,50 z" style="fill:url(#gradient_dot);fill-opacity:1;fill-rule:nonzero;stroke:#16161d;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,56 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8' />
|
||||
<meta name="description" content="{{ site.tagline }}" />
|
||||
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="style/stylesheet.css">
|
||||
<link rel="icon" type="image/png" href="images/favicon.png" />
|
||||
|
||||
<title>{{ page.title }}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="header_wrap" class="outer">
|
||||
<header class="inner">
|
||||
<a id="forkme_banner" href="{{ site.repository }}">View on GitHub</a>
|
||||
|
||||
<h1 id="project_title">{% include solaar.svg %} {{ site.title }}</h1>
|
||||
<h2 id="project_tagline">{{ site.tagline }}</h2>
|
||||
|
||||
<section id="downloads">
|
||||
<a class="tar_download_link" href="{{ site.tar_download }}">Solaar {{ site.version }}</a>
|
||||
<p style="color: #fff">
|
||||
Latest version:<br/>
|
||||
<span style="float: right; font-weight: bolder;">{{ site.version }}</span>
|
||||
</p>
|
||||
</section>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div id="main_content_wrap" class="outer">
|
||||
<section id="main_content" class="inner">
|
||||
{{ content }}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div id="footer_wrap" class="outer">
|
||||
<footer class="inner">
|
||||
<p class="copyright"><a href="{{ site.repository }}">{{ site.title }}</a> maintained by <a href="{{ site.owner_url }}">{{ site.owner }}</a></p>
|
||||
<p><a href="https://github.com/jsncostello/slate">Slate</a> theme by <a href="https://github.com/jsncostello">Jason Costello</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
||||
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var pageTracker = _gat._getTracker("{{ site.ga_id }}");
|
||||
pageTracker._trackPageview();
|
||||
} catch(err) {}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,47 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8' />
|
||||
<meta name="description" content="{{ site.tagline }}" />
|
||||
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="style/stylesheet.css">
|
||||
<link rel="icon" type="image/png" href="images/favicon.png" />
|
||||
|
||||
<title>{{ page.title }}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="header_wrap" class="outer">
|
||||
<header class="inner">
|
||||
<h1 id="project_title">
|
||||
<a href="index.html">{% include solaar.svg %} {{ site.title }}</a>
|
||||
</h1>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div id="main_content_wrap" class="outer">
|
||||
<section id="main_content" class="inner">
|
||||
{{ content }}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div id="footer_wrap" class="outer">
|
||||
<footer class="inner">
|
||||
<p class="copyright"><a href="{{ site.repository }}">{{ site.title }}</a> maintained by <a href="{{ site.owner_url }}">{{ site.owner }}</a></p>
|
||||
<p><a href="https://github.com/jsncostello/slate">Slate</a> theme by <a href="https://github.com/jsncostello">Jason Costello</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
||||
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var pageTracker = _gat._getTracker("{{ site.ga_id }}");
|
||||
pageTracker._trackPageview();
|
||||
} catch(err) {}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 943 B After Width: | Height: | Size: 96 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 432 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 230 B |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 14 KiB |
@@ -1,70 +0,0 @@
|
||||
.highlight .hll { background-color: #ffffcc }
|
||||
.highlight { background: #f0f3f3; }
|
||||
.highlight .c { color: #0099FF; font-style: italic } /* Comment */
|
||||
.highlight .err { color: #AA0000; background-color: #FFAAAA } /* Error */
|
||||
.highlight .k { color: #006699; font-weight: bold } /* Keyword */
|
||||
.highlight .o { color: #555555 } /* Operator */
|
||||
.highlight .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */
|
||||
.highlight .cp { color: #009999 } /* Comment.Preproc */
|
||||
.highlight .c1 { color: #0099FF; font-style: italic } /* Comment.Single */
|
||||
.highlight .cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */
|
||||
.highlight .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */
|
||||
.highlight .ge { font-style: italic } /* Generic.Emph */
|
||||
.highlight .gr { color: #FF0000 } /* Generic.Error */
|
||||
.highlight .gh { color: #003300; font-weight: bold } /* Generic.Heading */
|
||||
.highlight .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */
|
||||
.highlight .go { color: #AAAAAA } /* Generic.Output */
|
||||
.highlight .gp { color: #000099; font-weight: bold } /* Generic.Prompt */
|
||||
.highlight .gs { font-weight: bold } /* Generic.Strong */
|
||||
.highlight .gu { color: #003300; font-weight: bold } /* Generic.Subheading */
|
||||
.highlight .gt { color: #99CC66 } /* Generic.Traceback */
|
||||
.highlight .kc { color: #006699; font-weight: bold } /* Keyword.Constant */
|
||||
.highlight .kd { color: #006699; font-weight: bold } /* Keyword.Declaration */
|
||||
.highlight .kn { color: #006699; font-weight: bold } /* Keyword.Namespace */
|
||||
.highlight .kp { color: #006699 } /* Keyword.Pseudo */
|
||||
.highlight .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */
|
||||
.highlight .kt { color: #007788; font-weight: bold } /* Keyword.Type */
|
||||
.highlight .m { color: #FF6600 } /* Literal.Number */
|
||||
.highlight .s { color: #CC3300 } /* Literal.String */
|
||||
.highlight .na { color: #330099 } /* Name.Attribute */
|
||||
.highlight .nb { color: #336666 } /* Name.Builtin */
|
||||
.highlight .nc { color: #00AA88; font-weight: bold } /* Name.Class */
|
||||
.highlight .no { color: #336600 } /* Name.Constant */
|
||||
.highlight .nd { color: #9999FF } /* Name.Decorator */
|
||||
.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
|
||||
.highlight .ne { color: #CC0000; font-weight: bold } /* Name.Exception */
|
||||
.highlight .nf { color: #CC00FF } /* Name.Function */
|
||||
.highlight .nl { color: #9999FF } /* Name.Label */
|
||||
.highlight .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */
|
||||
.highlight .nt { color: #330099; font-weight: bold } /* Name.Tag */
|
||||
.highlight .nv { color: #003333 } /* Name.Variable */
|
||||
.highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */
|
||||
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.highlight .mf { color: #FF6600 } /* Literal.Number.Float */
|
||||
.highlight .mh { color: #FF6600 } /* Literal.Number.Hex */
|
||||
.highlight .mi { color: #FF6600 } /* Literal.Number.Integer */
|
||||
.highlight .mo { color: #FF6600 } /* Literal.Number.Oct */
|
||||
.highlight .sb { color: #CC3300 } /* Literal.String.Backtick */
|
||||
.highlight .sc { color: #CC3300 } /* Literal.String.Char */
|
||||
.highlight .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */
|
||||
.highlight .s2 { color: #CC3300 } /* Literal.String.Double */
|
||||
.highlight .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */
|
||||
.highlight .sh { color: #CC3300 } /* Literal.String.Heredoc */
|
||||
.highlight .si { color: #AA0000 } /* Literal.String.Interpol */
|
||||
.highlight .sx { color: #CC3300 } /* Literal.String.Other */
|
||||
.highlight .sr { color: #33AAAA } /* Literal.String.Regex */
|
||||
.highlight .s1 { color: #CC3300 } /* Literal.String.Single */
|
||||
.highlight .ss { color: #FFCC33 } /* Literal.String.Symbol */
|
||||
.highlight .bp { color: #336666 } /* Name.Builtin.Pseudo */
|
||||
.highlight .vc { color: #003333 } /* Name.Variable.Class */
|
||||
.highlight .vg { color: #003333 } /* Name.Variable.Global */
|
||||
.highlight .vi { color: #003333 } /* Name.Variable.Instance */
|
||||
.highlight .il { color: #FF6600 } /* Literal.Number.Integer.Long */
|
||||
|
||||
.type-csharp .highlight .k { color: #0000FF }
|
||||
.type-csharp .highlight .kt { color: #0000FF }
|
||||
.type-csharp .highlight .nf { color: #000000; font-weight: normal }
|
||||
.type-csharp .highlight .nc { color: #2B91AF }
|
||||
.type-csharp .highlight .nn { color: #000000 }
|
||||
.type-csharp .highlight .s { color: #A31515 }
|
||||
.type-csharp .highlight .sc { color: #A31515 }
|
||||
@@ -1,442 +0,0 @@
|
||||
/*******************************************************************************
|
||||
Slate Theme for Github Pages
|
||||
by Jason Costello, @jsncostello
|
||||
*******************************************************************************/
|
||||
|
||||
@import url(pygment_trac.css);
|
||||
|
||||
/*******************************************************************************
|
||||
MeyerWeb Reset
|
||||
*******************************************************************************/
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
a:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Theme Styles
|
||||
*******************************************************************************/
|
||||
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
color:#373737;
|
||||
background: #212121;
|
||||
font-size: 18px;
|
||||
font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.4;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 20px 0;
|
||||
font-weight: 700;
|
||||
color:#222222;
|
||||
font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding-bottom: 0px;
|
||||
font-size: 32px;
|
||||
background: url('../images/bg_hr.png') repeat-x bottom;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 10px 0 15px 0;
|
||||
}
|
||||
|
||||
footer p {
|
||||
color: #f2f2f2;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #007edf;
|
||||
text-shadow: none;
|
||||
|
||||
transition: color 0.5s ease;
|
||||
transition: text-shadow 0.5s ease;
|
||||
-webkit-transition: color 0.5s ease;
|
||||
-webkit-transition: text-shadow 0.5s ease;
|
||||
-moz-transition: color 0.5s ease;
|
||||
-moz-transition: text-shadow 0.5s ease;
|
||||
-o-transition: color 0.5s ease;
|
||||
-o-transition: text-shadow 0.5s ease;
|
||||
-ms-transition: color 0.5s ease;
|
||||
-ms-transition: text-shadow 0.5s ease;
|
||||
}
|
||||
|
||||
#main_content a:hover {
|
||||
color: #0069ba;
|
||||
text-shadow: #0090ff 0px 0px 2px;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
color: #43adff;
|
||||
text-shadow: #0090ff 0px 0px 2px;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
max-width: 739px;
|
||||
padding: 5px;
|
||||
margin: 10px 0 10px 0;
|
||||
border: 1px solid #ebebeb;
|
||||
|
||||
box-shadow: 0 0 5px #ebebeb;
|
||||
-webkit-box-shadow: 0 0 5px #ebebeb;
|
||||
-moz-box-shadow: 0 0 5px #ebebeb;
|
||||
-o-box-shadow: 0 0 5px #ebebeb;
|
||||
-ms-box-shadow: 0 0 5px #ebebeb;
|
||||
}
|
||||
|
||||
img.logo {
|
||||
max-width: 48px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
bottom: -10px;
|
||||
|
||||
box-shadow: none;
|
||||
-webkit-box-shadow: 0;
|
||||
-moz-box-shadow: 0;
|
||||
-o-box-shadow: 0;
|
||||
-ms-box-shadow: 0;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
width: 100%;
|
||||
color: #222;
|
||||
background-color: #fff;
|
||||
|
||||
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
|
||||
font-size: 14px;
|
||||
|
||||
border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
}
|
||||
|
||||
pre {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,.1);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 3px;
|
||||
margin: 0 3px;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
pre code {
|
||||
display: block;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: #666;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 0 0 20px;
|
||||
border-left: 3px solid #bbb;
|
||||
}
|
||||
|
||||
ul, ol, dl {
|
||||
margin-bottom: 15px
|
||||
}
|
||||
|
||||
ul li {
|
||||
list-style: inside;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
ol li {
|
||||
list-style: decimal inside;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
dl dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
padding-left: 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
dl p {
|
||||
padding-left: 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
margin-bottom: 5px;
|
||||
border: none;
|
||||
background: url('../images/bg_hr.png') repeat-x center;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid #373737;
|
||||
margin-bottom: 20px;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
th {
|
||||
font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
padding: 10px;
|
||||
background: #373737;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 10px;
|
||||
border: 1px solid #373737;
|
||||
}
|
||||
|
||||
form {
|
||||
background: #f2f2f2;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Full-Width Styles
|
||||
*******************************************************************************/
|
||||
|
||||
.outer {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inner {
|
||||
position: relative;
|
||||
max-width: 940px;
|
||||
padding: 20px 10px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#forkme_banner {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top:0;
|
||||
right: 10px;
|
||||
z-index: 10;
|
||||
padding: 10px 50px 10px 10px;
|
||||
color: #fff;
|
||||
background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,.5);
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
#header_wrap {
|
||||
background: #212121;
|
||||
}
|
||||
|
||||
#header_wrap .inner {
|
||||
padding 50px 10px 30px 10px;
|
||||
}
|
||||
|
||||
#project_title {
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
font-size: 42px;
|
||||
font-weight: 700;
|
||||
text-shadow: #111 0px 0px 10px;
|
||||
}
|
||||
|
||||
#project_tagline {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
font-weight: 300;
|
||||
background: none;
|
||||
text-shadow: #111 0px 0px 10px;
|
||||
}
|
||||
|
||||
#downloads {
|
||||
position: absolute;
|
||||
width: 240px;
|
||||
z-index: 10;
|
||||
bottom: 0px;
|
||||
right: 0;
|
||||
height: 80px;
|
||||
background: url('../images/icon_download.png') no-repeat 15% 70%;
|
||||
}
|
||||
|
||||
.zip_download_link {
|
||||
display: block;
|
||||
float: right;
|
||||
width: 90px;
|
||||
height:70px;
|
||||
text-indent: -5000px;
|
||||
overflow: hidden;
|
||||
background: url(../images/sprite_download.png) no-repeat bottom left;
|
||||
}
|
||||
|
||||
.tar_download_link {
|
||||
display: block;
|
||||
float: right;
|
||||
width: 90px;
|
||||
height:70px;
|
||||
text-indent: -5000px;
|
||||
overflow: hidden;
|
||||
background: url(../images/sprite_download.png) no-repeat bottom right;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.zip_download_link:hover {
|
||||
background: url(../images/sprite_download.png) no-repeat top left;
|
||||
}
|
||||
|
||||
.tar_download_link:hover {
|
||||
background: url(../images/sprite_download.png) no-repeat top right;
|
||||
}
|
||||
|
||||
#main_content_wrap {
|
||||
background: #f2f2f2;
|
||||
border-top: 1px solid #111;
|
||||
border-bottom: 1px solid #111;
|
||||
}
|
||||
|
||||
#main_content {
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
#footer_wrap {
|
||||
background: #212121;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Small Device Styles
|
||||
*******************************************************************************/
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
body {
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
#downloads {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.inner {
|
||||
min-width: 320px;
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
#project_title {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
code, pre {
|
||||
min-width: 320px;
|
||||
max-width: 480px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,22 +16,21 @@
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
"""Generic Human Interface Device API."""
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
__version__ = '0.9'
|
||||
from hidapi.udev import close # noqa: F401
|
||||
from hidapi.udev import enumerate # noqa: F401
|
||||
from hidapi.udev import find_paired_node # noqa: F401
|
||||
from hidapi.udev import find_paired_node_wpid # noqa: F401
|
||||
from hidapi.udev import get_manufacturer # noqa: F401
|
||||
from hidapi.udev import get_product # noqa: F401
|
||||
from hidapi.udev import get_serial # noqa: F401
|
||||
from hidapi.udev import monitor_glib # noqa: F401
|
||||
from hidapi.udev import open # noqa: F401
|
||||
from hidapi.udev import open_path # noqa: F401
|
||||
from hidapi.udev import read # noqa: F401
|
||||
from hidapi.udev import write # noqa: F401
|
||||
|
||||
from hidapi.udev import (
|
||||
enumerate,
|
||||
open,
|
||||
close,
|
||||
open_path,
|
||||
monitor_glib,
|
||||
read,
|
||||
write,
|
||||
get_manufacturer,
|
||||
get_product,
|
||||
get_serial,
|
||||
)
|
||||
__version__ = '0.9'
|
||||
|
||||
@@ -21,9 +21,12 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||
|
||||
import os
|
||||
import sys
|
||||
from select import select as _select
|
||||
import time
|
||||
|
||||
from binascii import hexlify, unhexlify
|
||||
from select import select as _select
|
||||
from threading import Lock
|
||||
|
||||
import hidapi as _hid
|
||||
|
||||
#
|
||||
@@ -31,10 +34,10 @@ import hidapi as _hid
|
||||
#
|
||||
|
||||
try:
|
||||
read_packet = raw_input
|
||||
read_packet = raw_input
|
||||
except NameError:
|
||||
# Python 3 equivalent of raw_input
|
||||
read_packet = input
|
||||
# Python 3 equivalent of raw_input
|
||||
read_packet = input
|
||||
|
||||
interactive = os.isatty(0)
|
||||
prompt = '?? Input: ' if interactive else ''
|
||||
@@ -42,217 +45,227 @@ start_time = time.time()
|
||||
|
||||
strhex = lambda d: hexlify(d).decode('ascii').upper()
|
||||
try:
|
||||
unicode
|
||||
# this is certanly Python 2
|
||||
is_string = lambda d: isinstance(d, unicode)
|
||||
# no easy way to distinguish between b'' and '' :(
|
||||
# or (isinstance(d, str) \
|
||||
# and not any((chr(k) in d for k in range(0x00, 0x1F))) \
|
||||
# and not any((chr(k) in d for k in range(0x80, 0xFF))) \
|
||||
# )
|
||||
except:
|
||||
# this is certanly Python 3
|
||||
# In Py3, unicode and str are equal (the unicode object does not exist)
|
||||
is_string = lambda d: isinstance(d, str)
|
||||
unicode # noqa: F821
|
||||
# this is certanly Python 2
|
||||
is_string = lambda d: isinstance(d, unicode) # noqa: F821
|
||||
# no easy way to distinguish between b'' and '' :(
|
||||
# or (isinstance(d, str) \
|
||||
# and not any((chr(k) in d for k in range(0x00, 0x1F))) \
|
||||
# and not any((chr(k) in d for k in range(0x80, 0xFF))) \
|
||||
# )
|
||||
except Exception:
|
||||
# this is certanly Python 3
|
||||
# In Py3, unicode and str are equal (the unicode object does not exist)
|
||||
is_string = lambda d: isinstance(d, str)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from threading import Lock
|
||||
print_lock = Lock()
|
||||
del Lock
|
||||
|
||||
|
||||
def _print(marker, data, scroll=False):
|
||||
t = time.time() - start_time
|
||||
if is_string(data):
|
||||
s = marker + ' ' + data
|
||||
else:
|
||||
hexs = strhex(data)
|
||||
s = '%s (% 8.3f) [%s %s %s %s] %s' % (marker, t, hexs[0:2], hexs[2:4], hexs[4:8], hexs[8:], repr(data))
|
||||
t = time.time() - start_time
|
||||
if is_string(data):
|
||||
s = marker + ' ' + data
|
||||
else:
|
||||
hexs = strhex(data)
|
||||
s = '%s (% 8.3f) [%s %s %s %s] %s' % (marker, t, hexs[0:2], hexs[2:4], hexs[4:8], hexs[8:], repr(data))
|
||||
|
||||
with print_lock:
|
||||
# allow only one thread at a time to write to the console, otherwise
|
||||
# the output gets garbled, especially with ANSI codes.
|
||||
with print_lock:
|
||||
# allow only one thread at a time to write to the console, otherwise
|
||||
# the output gets garbled, especially with ANSI codes.
|
||||
|
||||
if interactive and scroll:
|
||||
# scroll the entire screen above the current line up by 1 line
|
||||
sys.stdout.write('\033[s' # save cursor position
|
||||
'\033[S' # scroll up
|
||||
'\033[A' # cursor up
|
||||
'\033[L' # insert 1 line
|
||||
'\033[G') # move cursor to column 1
|
||||
sys.stdout.write(s)
|
||||
if interactive and scroll:
|
||||
# restore cursor position
|
||||
sys.stdout.write('\033[u')
|
||||
else:
|
||||
sys.stdout.write('\n')
|
||||
if interactive and scroll:
|
||||
# scroll the entire screen above the current line up by 1 line
|
||||
sys.stdout.write(
|
||||
'\033[s' # save cursor position
|
||||
'\033[S' # scroll up
|
||||
'\033[A' # cursor up
|
||||
'\033[L' # insert 1 line
|
||||
'\033[G'
|
||||
) # move cursor to column 1
|
||||
sys.stdout.write(s)
|
||||
if interactive and scroll:
|
||||
# restore cursor position
|
||||
sys.stdout.write('\033[u')
|
||||
else:
|
||||
sys.stdout.write('\n')
|
||||
|
||||
# flush stdout manually...
|
||||
# because trying to open stdin/out unbuffered programatically
|
||||
# works much too differently in Python 2/3
|
||||
sys.stdout.flush()
|
||||
# flush stdout manually...
|
||||
# because trying to open stdin/out unbuffered programmatically
|
||||
# works much too differently in Python 2/3
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def _error(text, scroll=False):
|
||||
_print('!!', text, scroll)
|
||||
_print('!!', text, scroll)
|
||||
|
||||
|
||||
def _continuous_read(handle, timeout=2000):
|
||||
while True:
|
||||
try:
|
||||
reply = _hid.read(handle, 128, timeout)
|
||||
except OSError as e:
|
||||
_error("Read failed, aborting: " + str(e), True)
|
||||
break
|
||||
assert reply is not None
|
||||
if reply:
|
||||
_print('>>', reply, True)
|
||||
while True:
|
||||
try:
|
||||
reply = _hid.read(handle, 128, timeout)
|
||||
except OSError as e:
|
||||
_error('Read failed, aborting: ' + str(e), True)
|
||||
break
|
||||
assert reply is not None
|
||||
if reply:
|
||||
_print('>>', reply, True)
|
||||
|
||||
|
||||
def _validate_input(line, hidpp=False):
|
||||
try:
|
||||
data = unhexlify(line.encode('ascii'))
|
||||
except Exception as e:
|
||||
_error("Invalid input: " + str(e))
|
||||
return None
|
||||
try:
|
||||
data = unhexlify(line.encode('ascii'))
|
||||
except Exception as e:
|
||||
_error('Invalid input: ' + str(e))
|
||||
return None
|
||||
|
||||
if hidpp:
|
||||
if len(data) < 4:
|
||||
_error("Invalid HID++ request: need at least 4 bytes")
|
||||
return None
|
||||
if data[:1] not in b'\x10\x11':
|
||||
_error("Invalid HID++ request: first byte must be 0x10 or 0x11")
|
||||
return None
|
||||
if data[1:2] not in b'\xFF\x01\x02\x03\x04\x05\x06':
|
||||
_error("Invalid HID++ request: second byte must be 0xFF or one of 0x01..0x06")
|
||||
return None
|
||||
if data[:1] == b'\x10':
|
||||
if len(data) > 7:
|
||||
_error("Invalid HID++ request: maximum length of a 0x10 request is 7 bytes")
|
||||
return None
|
||||
while len(data) < 7:
|
||||
data = (data + b'\x00' * 7)[:7]
|
||||
elif data[:1] == b'\x11':
|
||||
if len(data) > 20:
|
||||
_error("Invalid HID++ request: maximum length of a 0x11 request is 20 bytes")
|
||||
return None
|
||||
while len(data) < 20:
|
||||
data = (data + b'\x00' * 20)[:20]
|
||||
if hidpp:
|
||||
if len(data) < 4:
|
||||
_error('Invalid HID++ request: need at least 4 bytes')
|
||||
return None
|
||||
if data[:1] not in b'\x10\x11':
|
||||
_error('Invalid HID++ request: first byte must be 0x10 or 0x11')
|
||||
return None
|
||||
if data[1:2] not in b'\xFF\x01\x02\x03\x04\x05\x06':
|
||||
_error('Invalid HID++ request: second byte must be 0xFF or one of 0x01..0x06')
|
||||
return None
|
||||
if data[:1] == b'\x10':
|
||||
if len(data) > 7:
|
||||
_error('Invalid HID++ request: maximum length of a 0x10 request is 7 bytes')
|
||||
return None
|
||||
while len(data) < 7:
|
||||
data = (data + b'\x00' * 7)[:7]
|
||||
elif data[:1] == b'\x11':
|
||||
if len(data) > 20:
|
||||
_error('Invalid HID++ request: maximum length of a 0x11 request is 20 bytes')
|
||||
return None
|
||||
while len(data) < 20:
|
||||
data = (data + b'\x00' * 20)[:20]
|
||||
|
||||
return data
|
||||
return data
|
||||
|
||||
|
||||
def _open(args):
|
||||
device = args.device
|
||||
if args.hidpp and not device:
|
||||
for d in _hid.enumerate(vendor_id=0x046d):
|
||||
if d.driver == 'logitech-djreceiver':
|
||||
device = d.path
|
||||
break
|
||||
if not device:
|
||||
sys.exit("!! No HID++ receiver found.")
|
||||
if not device:
|
||||
sys.exit("!! Device path required.")
|
||||
def matchfn(bid, vid, pid):
|
||||
if vid == 0x046d:
|
||||
return {'vid': 0x046d}
|
||||
|
||||
print (".. Opening device", device)
|
||||
handle = _hid.open_path(device)
|
||||
if not handle:
|
||||
sys.exit("!! Failed to open %s, aborting." % device)
|
||||
device = args.device
|
||||
if args.hidpp and not device:
|
||||
for d in _hid.enumerate(matchfn):
|
||||
if d.driver == 'logitech-djreceiver':
|
||||
device = d.path
|
||||
break
|
||||
if not device:
|
||||
sys.exit('!! No HID++ receiver found.')
|
||||
if not device:
|
||||
sys.exit('!! Device path required.')
|
||||
|
||||
print (".. Opened handle %r, vendor %r product %r serial %r." % (
|
||||
handle,
|
||||
_hid.get_manufacturer(handle),
|
||||
_hid.get_product(handle),
|
||||
_hid.get_serial(handle)))
|
||||
if args.hidpp:
|
||||
if _hid.get_manufacturer(handle) != b'Logitech':
|
||||
sys.exit("!! Only Logitech devices support the HID++ protocol.")
|
||||
print (".. HID++ validation enabled.")
|
||||
else:
|
||||
if (_hid.get_manufacturer(handle) == b'Logitech' and
|
||||
b'Receiver' in _hid.get_product(handle)):
|
||||
args.hidpp = True
|
||||
print (".. Logitech receiver detected, HID++ validation enabled.")
|
||||
print('.. Opening device', device)
|
||||
handle = _hid.open_path(device)
|
||||
if not handle:
|
||||
sys.exit('!! Failed to open %s, aborting.' % device)
|
||||
|
||||
print(
|
||||
'.. Opened handle %r, vendor %r product %r serial %r.' %
|
||||
(handle, _hid.get_manufacturer(handle), _hid.get_product(handle), _hid.get_serial(handle))
|
||||
)
|
||||
if args.hidpp:
|
||||
if _hid.get_manufacturer(handle) != b'Logitech':
|
||||
sys.exit('!! Only Logitech devices support the HID++ protocol.')
|
||||
print('.. HID++ validation enabled.')
|
||||
else:
|
||||
if (_hid.get_manufacturer(handle) == b'Logitech' and b'Receiver' in _hid.get_product(handle)):
|
||||
args.hidpp = True
|
||||
print('.. Logitech receiver detected, HID++ validation enabled.')
|
||||
|
||||
return handle
|
||||
|
||||
return handle
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
def _parse_arguments():
|
||||
import argparse
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
arg_parser.add_argument('--history', help="history file (default ~/.hidconsole-history)")
|
||||
arg_parser.add_argument('--hidpp', action='store_true', help="ensure input data is a valid HID++ request")
|
||||
arg_parser.add_argument('device', nargs='?', help="linux device to connect to (/dev/hidrawX); "
|
||||
"may be omitted if --hidpp is given, in which case it looks for the first Logitech receiver")
|
||||
return arg_parser.parse_args()
|
||||
import argparse
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
arg_parser.add_argument('--history', help='history file (default ~/.hidconsole-history)')
|
||||
arg_parser.add_argument('--hidpp', action='store_true', help='ensure input data is a valid HID++ request')
|
||||
arg_parser.add_argument(
|
||||
'device',
|
||||
nargs='?',
|
||||
help='linux device to connect to (/dev/hidrawX); '
|
||||
'may be omitted if --hidpp is given, in which case it looks for the first Logitech receiver'
|
||||
)
|
||||
return arg_parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = _parse_arguments()
|
||||
handle = _open(args)
|
||||
args = _parse_arguments()
|
||||
handle = _open(args)
|
||||
|
||||
if interactive:
|
||||
print (".. Press ^C/^D to exit, or type hex bytes to write to the device.")
|
||||
if interactive:
|
||||
print('.. Press ^C/^D to exit, or type hex bytes to write to the device.')
|
||||
|
||||
import readline
|
||||
if args.history is None:
|
||||
import os.path
|
||||
args.history = os.path.join(os.path.expanduser('~'), '.hidconsole-history')
|
||||
try:
|
||||
readline.read_history_file(args.history)
|
||||
except:
|
||||
# file may not exist yet
|
||||
pass
|
||||
import readline
|
||||
if args.history is None:
|
||||
import os.path
|
||||
args.history = os.path.join(os.path.expanduser('~'), '.hidconsole-history')
|
||||
try:
|
||||
readline.read_history_file(args.history)
|
||||
except Exception:
|
||||
# file may not exist yet
|
||||
pass
|
||||
|
||||
try:
|
||||
from threading import Thread
|
||||
t = Thread(target=_continuous_read, args=(handle,))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
try:
|
||||
from threading import Thread
|
||||
t = Thread(target=_continuous_read, args=(handle, ))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
if interactive:
|
||||
# move the cursor at the bottom of the screen
|
||||
sys.stdout.write('\033[300B') # move cusor at most 300 lines down, don't scroll
|
||||
if interactive:
|
||||
# move the cursor at the bottom of the screen
|
||||
sys.stdout.write('\033[300B') # move cusor at most 300 lines down, don't scroll
|
||||
|
||||
while t.is_alive():
|
||||
line = read_packet(prompt)
|
||||
line = line.strip().replace(' ', '')
|
||||
# print ("line", line)
|
||||
if not line:
|
||||
continue
|
||||
while t.is_alive():
|
||||
line = read_packet(prompt)
|
||||
line = line.strip().replace(' ', '')
|
||||
# print ("line", line)
|
||||
if not line:
|
||||
continue
|
||||
|
||||
data = _validate_input(line, args.hidpp)
|
||||
if data is None:
|
||||
continue
|
||||
data = _validate_input(line, args.hidpp)
|
||||
if data is None:
|
||||
continue
|
||||
|
||||
_print('<<', data)
|
||||
_hid.write(handle, data)
|
||||
# wait for some kind of reply
|
||||
if args.hidpp and not interactive:
|
||||
rlist, wlist, xlist = _select([handle], [], [], 1)
|
||||
if data[1:2] == b'\xFF':
|
||||
# the receiver will reply very fast, in a few milliseconds
|
||||
time.sleep(0.010)
|
||||
else:
|
||||
# the devices might reply quite slow
|
||||
time.sleep(0.700)
|
||||
except EOFError:
|
||||
if interactive:
|
||||
print ("")
|
||||
else:
|
||||
time.sleep(1)
|
||||
_print('<<', data)
|
||||
_hid.write(handle, data)
|
||||
# wait for some kind of reply
|
||||
if args.hidpp and not interactive:
|
||||
rlist, wlist, xlist = _select([handle], [], [], 1)
|
||||
if data[1:2] == b'\xFF':
|
||||
# the receiver will reply very fast, in a few milliseconds
|
||||
time.sleep(0.010)
|
||||
else:
|
||||
# the devices might reply quite slow
|
||||
time.sleep(0.700)
|
||||
except EOFError:
|
||||
if interactive:
|
||||
print('')
|
||||
else:
|
||||
time.sleep(1)
|
||||
|
||||
finally:
|
||||
print (".. Closing handle %r" % handle)
|
||||
_hid.close(handle)
|
||||
if interactive:
|
||||
readline.write_history_file(args.history)
|
||||
finally:
|
||||
print('.. Closing handle %r' % handle)
|
||||
_hid.close(handle)
|
||||
if interactive:
|
||||
readline.write_history_file(args.history)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
"""Generic Human Interface Device API.
|
||||
|
||||
It is currently a partial pure-Python implementation of the native HID API
|
||||
@@ -28,337 +27,422 @@ necessary.
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import os as _os
|
||||
import errno as _errno
|
||||
from select import select as _select
|
||||
from pyudev import Context as _Context, Monitor as _Monitor, Device as _Device
|
||||
|
||||
|
||||
native_implementation = 'udev'
|
||||
|
||||
import os as _os
|
||||
|
||||
# the tuple object we'll expose when enumerating devices
|
||||
from collections import namedtuple
|
||||
DeviceInfo = namedtuple('DeviceInfo', [
|
||||
'path',
|
||||
'vendor_id',
|
||||
'product_id',
|
||||
'serial',
|
||||
'release',
|
||||
'manufacturer',
|
||||
'product',
|
||||
'interface',
|
||||
'driver',
|
||||
])
|
||||
del namedtuple
|
||||
from logging import DEBUG as _DEBUG
|
||||
from logging import getLogger
|
||||
from select import select as _select
|
||||
from time import sleep
|
||||
from time import time as _timestamp
|
||||
|
||||
from pyudev import Context as _Context
|
||||
from pyudev import Device as _Device
|
||||
from pyudev import DeviceNotFoundError
|
||||
from pyudev import Devices as _Devices
|
||||
from pyudev import Monitor as _Monitor
|
||||
|
||||
_log = getLogger(__name__)
|
||||
del getLogger
|
||||
|
||||
native_implementation = 'udev'
|
||||
|
||||
DeviceInfo = namedtuple(
|
||||
'DeviceInfo', [
|
||||
'path',
|
||||
'vendor_id',
|
||||
'product_id',
|
||||
'serial',
|
||||
'release',
|
||||
'manufacturer',
|
||||
'product',
|
||||
'interface',
|
||||
'driver',
|
||||
'bus_id',
|
||||
'isDevice',
|
||||
]
|
||||
)
|
||||
del namedtuple
|
||||
|
||||
#
|
||||
# exposed API
|
||||
# docstrings mostly copied from hidapi.h
|
||||
#
|
||||
|
||||
def init():
|
||||
"""This function is a no-op, and exists only to match the native hidapi
|
||||
implementation.
|
||||
|
||||
:returns: ``True``.
|
||||
"""
|
||||
return True
|
||||
def init():
|
||||
"""This function is a no-op, and exists only to match the native hidapi
|
||||
implementation.
|
||||
|
||||
:returns: ``True``.
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
def exit():
|
||||
"""This function is a no-op, and exists only to match the native hidapi
|
||||
implementation.
|
||||
"""This function is a no-op, and exists only to match the native hidapi
|
||||
implementation.
|
||||
|
||||
:returns: ``True``.
|
||||
"""
|
||||
return True
|
||||
:returns: ``True``.
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
def _match(action, device, vendor_id=None, product_id=None, interface_number=None, hid_driver=None):
|
||||
usb_device = device.find_parent('usb', 'usb_device')
|
||||
# print ("* parent", action, device, "usb:", usb_device)
|
||||
if not usb_device:
|
||||
return
|
||||
# The filterfn is used to determine whether this is a device of interest to Solaar.
|
||||
# It is given the bus id, vendor id, and product id and returns a dictionary
|
||||
# with the required hid_driver and usb_interface and whether this is a receiver or device.
|
||||
def _match(action, device, filterfn):
|
||||
hid_device = device.find_parent('hid')
|
||||
if not hid_device:
|
||||
return
|
||||
hid_id = hid_device.get('HID_ID')
|
||||
if not hid_id:
|
||||
return # there are reports that sometimes the id isn't set up right so be defensive
|
||||
bid, vid, pid = hid_id.split(':')
|
||||
|
||||
vid = usb_device.get('ID_VENDOR_ID')
|
||||
pid = usb_device.get('ID_MODEL_ID')
|
||||
if not ((vendor_id is None or vendor_id == int(vid, 16)) and
|
||||
(product_id is None or product_id == int(pid, 16))):
|
||||
return
|
||||
filter = filterfn(int(bid, 16), int(vid, 16), int(pid, 16))
|
||||
if not filter:
|
||||
return
|
||||
|
||||
if action == 'add':
|
||||
hid_device = device.find_parent('hid')
|
||||
if not hid_device:
|
||||
return
|
||||
hid_driver_name = hid_device.get('DRIVER')
|
||||
# print ("** found hid", action, device, "hid:", hid_device, hid_driver_name)
|
||||
if hid_driver:
|
||||
if isinstance(hid_driver, tuple):
|
||||
if hid_driver_name not in hid_driver:
|
||||
return
|
||||
elif hid_driver_name != hid_driver:
|
||||
return
|
||||
hid_driver = filter.get('hid_driver')
|
||||
interface_number = filter.get('usb_interface')
|
||||
isDevice = filter.get('isDevice')
|
||||
|
||||
intf_device = device.find_parent('usb', 'usb_interface')
|
||||
# print ("*** usb interface", action, device, "usb_interface:", intf_device)
|
||||
if interface_number is None:
|
||||
usb_interface = None if intf_device is None else intf_device.attributes.asint('bInterfaceNumber')
|
||||
else:
|
||||
usb_interface = None if intf_device is None else intf_device.attributes.asint('bInterfaceNumber')
|
||||
if usb_interface is None or interface_number != usb_interface:
|
||||
return
|
||||
if action == 'add':
|
||||
hid_driver_name = hid_device.get('DRIVER')
|
||||
# print ("** found hid", action, device, "hid:", hid_device, hid_driver_name)
|
||||
if hid_driver:
|
||||
if isinstance(hid_driver, tuple):
|
||||
if hid_driver_name not in hid_driver:
|
||||
return
|
||||
elif hid_driver_name != hid_driver:
|
||||
return
|
||||
|
||||
attrs = usb_device.attributes
|
||||
d_info = DeviceInfo(path=device.device_node,
|
||||
vendor_id=vid[-4:],
|
||||
product_id=pid[-4:],
|
||||
serial=hid_device.get('HID_UNIQ'),
|
||||
release=attrs.get('bcdDevice'),
|
||||
manufacturer=attrs.get('manufacturer'),
|
||||
product=attrs.get('product'),
|
||||
interface=usb_interface,
|
||||
driver=hid_driver_name)
|
||||
return d_info
|
||||
intf_device = device.find_parent('usb', 'usb_interface')
|
||||
# print ("*** usb interface", action, device, "usb_interface:", intf_device)
|
||||
usb_interface = None if intf_device is None else intf_device.attributes.asint('bInterfaceNumber')
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug(
|
||||
'Found device BID %s VID %s PID %s INTERFACE %s FILTER %s', bid, vid, pid, usb_interface, interface_number
|
||||
)
|
||||
if not (interface_number is None or interface_number == usb_interface):
|
||||
return
|
||||
attrs = intf_device.attributes if intf_device else None
|
||||
|
||||
elif action == 'remove':
|
||||
# print (dict(device), dict(usb_device))
|
||||
d_info = DeviceInfo(
|
||||
path=device.device_node,
|
||||
bus_id=int(bid, 16),
|
||||
vendor_id=vid[-4:],
|
||||
product_id=pid[-4:],
|
||||
driver=hid_driver_name,
|
||||
interface=usb_interface,
|
||||
isDevice=isDevice,
|
||||
serial=hid_device.get('HID_UNIQ'),
|
||||
release=attrs.get('bcdDevice') if attrs else None,
|
||||
manufacturer=attrs.get('manufacturer') if attrs else None,
|
||||
product=attrs.get('product') if attrs else None
|
||||
)
|
||||
return d_info
|
||||
|
||||
d_info = DeviceInfo(path=device.device_node,
|
||||
vendor_id=vid[-4:],
|
||||
product_id=pid[-4:],
|
||||
serial=None,
|
||||
release=None,
|
||||
manufacturer=None,
|
||||
product=None,
|
||||
interface=None,
|
||||
driver=None)
|
||||
return d_info
|
||||
elif action == 'remove':
|
||||
# print (dict(device), dict(usb_device))
|
||||
|
||||
d_info = DeviceInfo(
|
||||
path=device.device_node,
|
||||
bus_id=None,
|
||||
vendor_id=vid[-4:],
|
||||
product_id=pid[-4:],
|
||||
driver=None,
|
||||
interface=None,
|
||||
isDevice=isDevice,
|
||||
serial=None,
|
||||
release=None,
|
||||
manufacturer=None,
|
||||
product=None
|
||||
)
|
||||
return d_info
|
||||
|
||||
|
||||
def monitor_glib(callback, *device_filters):
|
||||
from gi.repository import GLib
|
||||
def find_paired_node(receiver_path, index, timeout):
|
||||
"""Find the node of a device paired with a receiver"""
|
||||
context = _Context()
|
||||
receiver_phys = _Devices.from_device_file(context, receiver_path).find_parent('hid').get('HID_PHYS')
|
||||
|
||||
c = _Context()
|
||||
if not receiver_phys:
|
||||
return None
|
||||
|
||||
# already existing devices
|
||||
# for device in c.list_devices(subsystem='hidraw'):
|
||||
# # print (device, dict(device), dict(device.attributes))
|
||||
# for filter in device_filters:
|
||||
# d_info = _match('add', device, *filter)
|
||||
# if d_info:
|
||||
# GLib.idle_add(callback, 'add', d_info)
|
||||
# break
|
||||
phys = f'{receiver_phys}:{index}'
|
||||
timeout += _timestamp()
|
||||
delta = _timestamp()
|
||||
while delta < timeout:
|
||||
for dev in context.list_devices(subsystem='hidraw'):
|
||||
dev_phys = dev.find_parent('hid').get('HID_PHYS')
|
||||
if dev_phys and dev_phys == phys:
|
||||
return dev.device_node
|
||||
delta = _timestamp()
|
||||
|
||||
m = _Monitor.from_netlink(c)
|
||||
m.filter_by(subsystem='hidraw')
|
||||
|
||||
def _process_udev_event(monitor, condition, cb, filters):
|
||||
if condition == GLib.IO_IN:
|
||||
event = monitor.receive_device()
|
||||
if event:
|
||||
action, device = event
|
||||
# print ("***", action, device)
|
||||
if action == 'add':
|
||||
for filter in filters:
|
||||
d_info = _match(action, device, *filter)
|
||||
if d_info:
|
||||
GLib.idle_add(cb, action, d_info)
|
||||
break
|
||||
elif action == 'remove':
|
||||
# the GLib notification does _not_ match!
|
||||
pass
|
||||
return True
|
||||
|
||||
try:
|
||||
# io_add_watch_full may not be available...
|
||||
GLib.io_add_watch_full(m, GLib.PRIORITY_LOW, GLib.IO_IN, _process_udev_event, callback, device_filters)
|
||||
# print ("did io_add_watch_full")
|
||||
except AttributeError:
|
||||
try:
|
||||
# and the priority parameter appeared later in the API
|
||||
GLib.io_add_watch(m, GLib.PRIORITY_LOW, GLib.IO_IN, _process_udev_event, callback, device_filters)
|
||||
# print ("did io_add_watch with priority")
|
||||
except:
|
||||
GLib.io_add_watch(m, GLib.IO_IN, _process_udev_event, callback, device_filters)
|
||||
# print ("did io_add_watch")
|
||||
|
||||
m.start()
|
||||
return None
|
||||
|
||||
|
||||
def enumerate(vendor_id=None, product_id=None, interface_number=None, hid_driver=None):
|
||||
"""Enumerate the HID Devices.
|
||||
def find_paired_node_wpid(receiver_path, index):
|
||||
"""Find the node of a device paired with a receiver, get wpid from udev"""
|
||||
context = _Context()
|
||||
receiver_phys = _Devices.from_device_file(context, receiver_path).find_parent('hid').get('HID_PHYS')
|
||||
|
||||
List all the HID devices attached to the system, optionally filtering by
|
||||
vendor_id, product_id, and/or interface_number.
|
||||
if not receiver_phys:
|
||||
return None
|
||||
|
||||
:returns: a list of matching ``DeviceInfo`` tuples.
|
||||
"""
|
||||
for dev in _Context().list_devices(subsystem='hidraw'):
|
||||
dev_info = _match('add', dev, vendor_id, product_id, interface_number, hid_driver)
|
||||
if dev_info:
|
||||
yield dev_info
|
||||
phys = f'{receiver_phys}:{index}'
|
||||
for dev in context.list_devices(subsystem='hidraw'):
|
||||
dev_phys = dev.find_parent('hid').get('HID_PHYS')
|
||||
if dev_phys and dev_phys == phys:
|
||||
# get hid id like 0003:0000046D:00000065
|
||||
hid_id = dev.find_parent('hid').get('HID_ID')
|
||||
# get wpid - last 4 symbols
|
||||
udev_wpid = hid_id[-4:]
|
||||
return udev_wpid
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def monitor_glib(callback, filterfn):
|
||||
from gi.repository import GLib
|
||||
|
||||
c = _Context()
|
||||
|
||||
# already existing devices
|
||||
# for device in c.list_devices(subsystem='hidraw'):
|
||||
# # print (device, dict(device), dict(device.attributes))
|
||||
# for filter in device_filters:
|
||||
# d_info = _match('add', device, *filter)
|
||||
# if d_info:
|
||||
# GLib.idle_add(callback, 'add', d_info)
|
||||
# break
|
||||
|
||||
m = _Monitor.from_netlink(c)
|
||||
m.filter_by(subsystem='hidraw')
|
||||
|
||||
def _process_udev_event(monitor, condition, cb, filterfn):
|
||||
if condition == GLib.IO_IN:
|
||||
event = monitor.receive_device()
|
||||
if event:
|
||||
action, device = event
|
||||
# print ("***", action, device)
|
||||
if action == 'add':
|
||||
d_info = _match(action, device, filterfn)
|
||||
if d_info:
|
||||
GLib.idle_add(cb, action, d_info)
|
||||
elif action == 'remove':
|
||||
# the GLib notification does _not_ match!
|
||||
pass
|
||||
return True
|
||||
|
||||
try:
|
||||
# io_add_watch_full may not be available...
|
||||
GLib.io_add_watch_full(m, GLib.PRIORITY_LOW, GLib.IO_IN, _process_udev_event, callback, filterfn)
|
||||
# print ("did io_add_watch_full")
|
||||
except AttributeError:
|
||||
try:
|
||||
# and the priority parameter appeared later in the API
|
||||
GLib.io_add_watch(m, GLib.PRIORITY_LOW, GLib.IO_IN, _process_udev_event, callback, filterfn)
|
||||
# print ("did io_add_watch with priority")
|
||||
except Exception:
|
||||
GLib.io_add_watch(m, GLib.IO_IN, _process_udev_event, callback, filterfn)
|
||||
# print ("did io_add_watch")
|
||||
|
||||
m.start()
|
||||
|
||||
|
||||
def enumerate(filterfn):
|
||||
"""Enumerate the HID Devices.
|
||||
|
||||
List all the HID devices attached to the system, optionally filtering by
|
||||
vendor_id, product_id, and/or interface_number.
|
||||
|
||||
:returns: a list of matching ``DeviceInfo`` tuples.
|
||||
"""
|
||||
|
||||
for dev in _Context().list_devices(subsystem='hidraw'):
|
||||
dev_info = _match('add', dev, filterfn)
|
||||
if dev_info:
|
||||
yield dev_info
|
||||
|
||||
|
||||
def open(vendor_id, product_id, serial=None):
|
||||
"""Open a HID device by its Vendor ID, Product ID and optional serial number.
|
||||
"""Open a HID device by its Vendor ID, Product ID and optional serial number.
|
||||
|
||||
If no serial is provided, the first device with the specified IDs is opened.
|
||||
If no serial is provided, the first device with the specified IDs is opened.
|
||||
|
||||
:returns: an opaque device handle, or ``None``.
|
||||
"""
|
||||
for device in enumerate(vendor_id, product_id):
|
||||
if serial is None or serial == device.serial:
|
||||
return open_path(device.path)
|
||||
:returns: an opaque device handle, or ``None``.
|
||||
"""
|
||||
def matchfn(bid, vid, pid):
|
||||
return vid == vendor_id and pid == product_id
|
||||
|
||||
for device in enumerate(matchfn):
|
||||
if serial is None or serial == device.serial:
|
||||
return open_path(device.path)
|
||||
|
||||
|
||||
def open_path(device_path):
|
||||
"""Open a HID device by its path name.
|
||||
"""Open a HID device by its path name.
|
||||
|
||||
:param device_path: the path of a ``DeviceInfo`` tuple returned by
|
||||
enumerate().
|
||||
:param device_path: the path of a ``DeviceInfo`` tuple returned by enumerate().
|
||||
|
||||
:returns: an opaque device handle, or ``None``.
|
||||
"""
|
||||
assert device_path
|
||||
assert device_path.startswith('/dev/hidraw')
|
||||
return _os.open(device_path, _os.O_RDWR | _os.O_SYNC)
|
||||
:returns: an opaque device handle, or ``None``.
|
||||
"""
|
||||
assert device_path
|
||||
assert device_path.startswith('/dev/hidraw')
|
||||
return _os.open(device_path, _os.O_RDWR | _os.O_SYNC)
|
||||
|
||||
|
||||
def close(device_handle):
|
||||
"""Close a HID device.
|
||||
"""Close a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
assert device_handle
|
||||
_os.close(device_handle)
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
assert device_handle
|
||||
_os.close(device_handle)
|
||||
|
||||
|
||||
def write(device_handle, data):
|
||||
"""Write an Output report to a HID device.
|
||||
"""Write an Output report to a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param data: the data bytes to send including the report number as the
|
||||
first byte.
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param data: the data bytes to send including the report number as the
|
||||
first byte.
|
||||
|
||||
The first byte of data[] must contain the Report ID. For
|
||||
devices which only support a single report, this must be set
|
||||
to 0x0. The remaining bytes contain the report data. Since
|
||||
the Report ID is mandatory, calls to hid_write() will always
|
||||
contain one more byte than the report contains. For example,
|
||||
if a hid report is 16 bytes long, 17 bytes must be passed to
|
||||
hid_write(), the Report ID (or 0x0, for devices with a
|
||||
single report), followed by the report data (16 bytes). In
|
||||
this example, the length passed in would be 17.
|
||||
The first byte of data[] must contain the Report ID. For
|
||||
devices which only support a single report, this must be set
|
||||
to 0x0. The remaining bytes contain the report data. Since
|
||||
the Report ID is mandatory, calls to hid_write() will always
|
||||
contain one more byte than the report contains. For example,
|
||||
if a hid report is 16 bytes long, 17 bytes must be passed to
|
||||
hid_write(), the Report ID (or 0x0, for devices with a
|
||||
single report), followed by the report data (16 bytes). In
|
||||
this example, the length passed in would be 17.
|
||||
|
||||
write() will send the data on the first OUT endpoint, if
|
||||
one exists. If it does not, it will send the data through
|
||||
the Control Endpoint (Endpoint 0).
|
||||
"""
|
||||
assert device_handle
|
||||
assert data
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
bytes_written = _os.write(device_handle, data)
|
||||
if bytes_written != len(data):
|
||||
raise IOError(_errno.EIO, 'written %d bytes out of expected %d' % (bytes_written, len(data)))
|
||||
write() will send the data on the first OUT endpoint, if
|
||||
one exists. If it does not, it will send the data through
|
||||
the Control Endpoint (Endpoint 0).
|
||||
"""
|
||||
assert device_handle
|
||||
assert data
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
retrycount = 0
|
||||
bytes_written = 0
|
||||
while (retrycount < 3):
|
||||
try:
|
||||
retrycount += 1
|
||||
bytes_written = _os.write(device_handle, data)
|
||||
except IOError as e:
|
||||
if e.errno == _errno.EPIPE:
|
||||
sleep(0.1)
|
||||
else:
|
||||
break
|
||||
if bytes_written != len(data):
|
||||
raise IOError(_errno.EIO, 'written %d bytes out of expected %d' % (bytes_written, len(data)))
|
||||
|
||||
|
||||
def read(device_handle, bytes_count, timeout_ms=-1):
|
||||
"""Read an Input report from a HID device.
|
||||
"""Read an Input report from a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param bytes_count: maximum number of bytes to read.
|
||||
:param timeout_ms: can be -1 (default) to wait for data indefinitely, 0 to
|
||||
read whatever is in the device's input buffer, or a positive integer to
|
||||
wait that many milliseconds.
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param bytes_count: maximum number of bytes to read.
|
||||
:param timeout_ms: can be -1 (default) to wait for data indefinitely, 0 to
|
||||
read whatever is in the device's input buffer, or a positive integer to
|
||||
wait that many milliseconds.
|
||||
|
||||
Input reports are returned to the host through the INTERRUPT IN endpoint.
|
||||
The first byte will contain the Report number if the device uses numbered
|
||||
reports.
|
||||
Input reports are returned to the host through the INTERRUPT IN endpoint.
|
||||
The first byte will contain the Report number if the device uses numbered
|
||||
reports.
|
||||
|
||||
:returns: the data packet read, an empty bytes string if a timeout was
|
||||
reached, or None if there was an error while reading.
|
||||
"""
|
||||
assert device_handle
|
||||
timeout = None if timeout_ms < 0 else timeout_ms / 1000.0
|
||||
rlist, wlist, xlist = _select([device_handle], [], [device_handle], timeout)
|
||||
:returns: the data packet read, an empty bytes string if a timeout was
|
||||
reached, or None if there was an error while reading.
|
||||
"""
|
||||
assert device_handle
|
||||
timeout = None if timeout_ms < 0 else timeout_ms / 1000.0
|
||||
rlist, wlist, xlist = _select([device_handle], [], [device_handle], timeout)
|
||||
|
||||
if xlist:
|
||||
assert xlist == [device_handle]
|
||||
raise IOError(_errno.EIO, 'exception on file descriptor %d' % device_handle)
|
||||
if xlist:
|
||||
assert xlist == [device_handle]
|
||||
raise IOError(_errno.EIO, 'exception on file descriptor %d' % device_handle)
|
||||
|
||||
if rlist:
|
||||
assert rlist == [device_handle]
|
||||
data = _os.read(device_handle, bytes_count)
|
||||
assert data is not None
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
return data
|
||||
else:
|
||||
return b''
|
||||
if rlist:
|
||||
assert rlist == [device_handle]
|
||||
data = _os.read(device_handle, bytes_count)
|
||||
assert data is not None
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
return data
|
||||
else:
|
||||
return b''
|
||||
|
||||
|
||||
_DEVICE_STRINGS = {
|
||||
0: 'manufacturer',
|
||||
1: 'product',
|
||||
2: 'serial',
|
||||
0: 'manufacturer',
|
||||
1: 'product',
|
||||
2: 'serial',
|
||||
}
|
||||
|
||||
|
||||
def get_manufacturer(device_handle):
|
||||
"""Get the Manufacturer String from a HID device.
|
||||
"""Get the Manufacturer String from a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
return get_indexed_string(device_handle, 0)
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
return get_indexed_string(device_handle, 0)
|
||||
|
||||
|
||||
def get_product(device_handle):
|
||||
"""Get the Product String from a HID device.
|
||||
"""Get the Product String from a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
return get_indexed_string(device_handle, 1)
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
return get_indexed_string(device_handle, 1)
|
||||
|
||||
|
||||
def get_serial(device_handle):
|
||||
"""Get the serial number from a HID device.
|
||||
"""Get the serial number from a HID device.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
serial = get_indexed_string(device_handle, 2)
|
||||
if serial is not None:
|
||||
return ''.join(hex(ord(c)) for c in serial)
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
"""
|
||||
serial = get_indexed_string(device_handle, 2)
|
||||
return serial
|
||||
|
||||
|
||||
def get_indexed_string(device_handle, index):
|
||||
"""Get a string from a HID device, based on its string index.
|
||||
"""Get a string from a HID device, based on its string index.
|
||||
|
||||
Note: currently not working in the ``hidraw`` native implementation.
|
||||
Note: currently not working in the ``hidraw`` native implementation.
|
||||
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param index: the index of the string to get.
|
||||
"""
|
||||
if index not in _DEVICE_STRINGS:
|
||||
return None
|
||||
:param device_handle: a device handle returned by open() or open_path().
|
||||
:param index: the index of the string to get.
|
||||
:returns: the value corresponding to index, or None if no value found
|
||||
:rtype: bytes or NoneType
|
||||
"""
|
||||
try:
|
||||
key = _DEVICE_STRINGS[index]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
assert device_handle
|
||||
stat = _os.fstat(device_handle)
|
||||
dev = _Device.from_device_number(_Context(), 'char', stat.st_rdev)
|
||||
if dev:
|
||||
hid_dev = dev.find_parent('hid')
|
||||
if hid_dev:
|
||||
assert 'HID_ID' in hid_dev
|
||||
bus, _ignore, _ignore = hid_dev['HID_ID'].split(':')
|
||||
assert device_handle
|
||||
stat = _os.fstat(device_handle)
|
||||
try:
|
||||
dev = _Device.from_device_number(_Context(), 'char', stat.st_rdev)
|
||||
except (DeviceNotFoundError, ValueError):
|
||||
return None
|
||||
|
||||
if bus == '0003': # USB
|
||||
usb_dev = dev.find_parent('usb', 'usb_device')
|
||||
assert usb_dev
|
||||
key = _DEVICE_STRINGS[index]
|
||||
attrs = usb_dev.attributes
|
||||
if key in attrs:
|
||||
return attrs[key]
|
||||
hid_dev = dev.find_parent('hid')
|
||||
if hid_dev:
|
||||
assert 'HID_ID' in hid_dev
|
||||
bus, _ignore, _ignore = hid_dev['HID_ID'].split(':')
|
||||
|
||||
elif bus == '0005': # BLUETOOTH
|
||||
# TODO
|
||||
pass
|
||||
if bus == '0003': # USB
|
||||
usb_dev = dev.find_parent('usb', 'usb_device')
|
||||
assert usb_dev
|
||||
return usb_dev.attributes.get(key)
|
||||
|
||||
elif bus == '0005': # BLUETOOTH
|
||||
# TODO
|
||||
pass
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
"""Low-level interface for devices connected through a Logitech Universal
|
||||
Receiver (UR).
|
||||
|
||||
@@ -34,23 +33,20 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||
|
||||
import logging
|
||||
|
||||
from . import listener, status # noqa: F401
|
||||
from .base import DeviceUnreachable, NoReceiver, NoSuchDevice # noqa: F401
|
||||
from .common import strhex # noqa: F401
|
||||
from .device import Device # noqa: F401
|
||||
from .hidpp20 import FeatureCallError, FeatureNotSupported # noqa: F401
|
||||
from .receiver import Receiver # noqa: F401
|
||||
|
||||
_DEBUG = logging.DEBUG
|
||||
_log = logging.getLogger(__name__)
|
||||
_log.setLevel(logging.root.level)
|
||||
# if logging.root.level > logging.DEBUG:
|
||||
# _log.addHandler(logging.NullHandler())
|
||||
# _log.propagate = 0
|
||||
# _log.addHandler(logging.NullHandler())
|
||||
# _log.propagate = 0
|
||||
|
||||
del logging
|
||||
|
||||
|
||||
__version__ = '0.9'
|
||||
|
||||
|
||||
from .common import strhex
|
||||
from .base import NoReceiver, NoSuchDevice, DeviceUnreachable
|
||||
from .receiver import Receiver, PairedDevice
|
||||
from .hidpp20 import FeatureNotSupported, FeatureCallError
|
||||
|
||||
from . import listener
|
||||
from . import status
|
||||
|
||||
@@ -22,18 +22,29 @@
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from time import time as _timestamp
|
||||
import threading as _threading
|
||||
|
||||
from collections import namedtuple
|
||||
from contextlib import contextmanager
|
||||
from logging import DEBUG as _DEBUG
|
||||
from logging import INFO as _INFO
|
||||
from logging import getLogger
|
||||
from random import getrandbits as _random_bits
|
||||
from time import time as _timestamp
|
||||
|
||||
from logging import getLogger, DEBUG as _DEBUG
|
||||
_log = getLogger(__name__)
|
||||
del getLogger
|
||||
import hidapi as _hid
|
||||
|
||||
|
||||
from .common import strhex as _strhex, KwException as _KwException, pack as _pack
|
||||
from . import hidpp10 as _hidpp10
|
||||
from . import hidpp20 as _hidpp20
|
||||
import hidapi as _hid
|
||||
from .base_usb import ALL as _RECEIVER_USB_IDS
|
||||
from .base_usb import DEVICES as _DEVICE_IDS
|
||||
from .base_usb import other_device_check as _other_device_check
|
||||
from .common import KwException as _KwException
|
||||
from .common import pack as _pack
|
||||
from .common import strhex as _strhex
|
||||
|
||||
_log = getLogger(__name__)
|
||||
del getLogger
|
||||
|
||||
#
|
||||
#
|
||||
@@ -44,6 +55,17 @@ _LONG_MESSAGE_SIZE = 20
|
||||
_MEDIUM_MESSAGE_SIZE = 15
|
||||
_MAX_READ_SIZE = 32
|
||||
|
||||
HIDPP_SHORT_MESSAGE_ID = 0x10
|
||||
HIDPP_LONG_MESSAGE_ID = 0x11
|
||||
DJ_MESSAGE_ID = 0x20
|
||||
|
||||
# mapping from report_id to message length
|
||||
report_lengths = {
|
||||
HIDPP_SHORT_MESSAGE_ID: _SHORT_MESSAGE_SIZE,
|
||||
HIDPP_LONG_MESSAGE_ID: _LONG_MESSAGE_SIZE,
|
||||
DJ_MESSAGE_ID: _MEDIUM_MESSAGE_SIZE,
|
||||
0x21: _MAX_READ_SIZE
|
||||
}
|
||||
"""Default timeout on read (in seconds)."""
|
||||
DEFAULT_TIMEOUT = 4
|
||||
# the receiver itself should reply very fast, within 500ms
|
||||
@@ -57,239 +79,274 @@ _PING_TIMEOUT = DEFAULT_TIMEOUT * 2
|
||||
# Exceptions that may be raised by this API.
|
||||
#
|
||||
|
||||
|
||||
class NoReceiver(_KwException):
|
||||
"""Raised when trying to talk through a previously open handle, when the
|
||||
receiver is no longer available. Should only happen if the receiver is
|
||||
physically disconnected from the machine, or its kernel driver module is
|
||||
unloaded."""
|
||||
pass
|
||||
"""Raised when trying to talk through a previously open handle, when the
|
||||
receiver is no longer available. Should only happen if the receiver is
|
||||
physically disconnected from the machine, or its kernel driver module is
|
||||
unloaded."""
|
||||
pass
|
||||
|
||||
|
||||
class NoSuchDevice(_KwException):
|
||||
"""Raised when trying to reach a device number not paired to the receiver."""
|
||||
pass
|
||||
"""Raised when trying to reach a device number not paired to the receiver."""
|
||||
pass
|
||||
|
||||
|
||||
class DeviceUnreachable(_KwException):
|
||||
"""Raised when a request is made to an unreachable (turned off) device."""
|
||||
pass
|
||||
"""Raised when a request is made to an unreachable (turned off) device."""
|
||||
pass
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from .base_usb import ALL as _RECEIVER_USB_IDS
|
||||
|
||||
def match(record, bus_id, vendor_id, product_id):
|
||||
return ((record.get('bus_id') is None or record.get('bus_id') == bus_id)
|
||||
and (record.get('vendor_id') is None or record.get('vendor_id') == vendor_id)
|
||||
and (record.get('product_id') is None or record.get('product_id') == product_id))
|
||||
|
||||
|
||||
def filter_receivers(bus_id, vendor_id, product_id):
|
||||
"""Check that this product is a Logitech receiver and if so return the receiver record for further checking"""
|
||||
for record in _RECEIVER_USB_IDS: # known receivers
|
||||
if match(record, bus_id, vendor_id, product_id):
|
||||
return record
|
||||
|
||||
|
||||
def receivers():
|
||||
"""List all the Linux devices exposed by the UR attached to the machine."""
|
||||
for receiver_usb_id in _RECEIVER_USB_IDS:
|
||||
for d in _hid.enumerate(*receiver_usb_id):
|
||||
yield d
|
||||
"""Enumerate all the receivers attached to the machine."""
|
||||
for dev in _hid.enumerate(filter_receivers):
|
||||
yield dev
|
||||
|
||||
|
||||
def filter_devices(bus_id, vendor_id, product_id):
|
||||
"""Check that this product is of interest and if so return the device record for further checking"""
|
||||
for record in _DEVICE_IDS: # known devices
|
||||
if match(record, bus_id, vendor_id, product_id):
|
||||
return record
|
||||
return _other_device_check(bus_id, vendor_id, product_id) # USB and BT devices unknown to Solaar
|
||||
|
||||
|
||||
def wired_devices():
|
||||
"""Enumerate all the USB-connected and Bluetooth devices attached to the machine."""
|
||||
for dev in _hid.enumerate(filter_devices):
|
||||
yield dev
|
||||
|
||||
|
||||
def filter_either(bus_id, vendor_id, product_id):
|
||||
return filter_receivers(bus_id, vendor_id, product_id) or filter_devices(bus_id, vendor_id, product_id)
|
||||
|
||||
|
||||
def notify_on_receivers_glib(callback):
|
||||
"""Watch for matching devices and notifies the callback on the GLib thread."""
|
||||
_hid.monitor_glib(callback, *_RECEIVER_USB_IDS)
|
||||
"""Watch for matching devices and notifies the callback on the GLib thread."""
|
||||
return _hid.monitor_glib(callback, filter_either)
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
def open_path(path):
|
||||
"""Checks if the given Linux device path points to the right UR device.
|
||||
"""Checks if the given Linux device path points to the right UR device.
|
||||
|
||||
:param path: the Linux device path.
|
||||
:param path: the Linux device path.
|
||||
|
||||
The UR physical device may expose multiple linux devices with the same
|
||||
interface, so we have to check for the right one. At this moment the only
|
||||
way to distinguish betheen them is to do a test ping on an invalid
|
||||
(attached) device number (i.e., 0), expecting a 'ping failed' reply.
|
||||
The UR physical device may expose multiple linux devices with the same
|
||||
interface, so we have to check for the right one. At this moment the only
|
||||
way to distinguish betheen them is to do a test ping on an invalid
|
||||
(attached) device number (i.e., 0), expecting a 'ping failed' reply.
|
||||
|
||||
:returns: an open receiver handle if this is the right Linux device, or
|
||||
``None``.
|
||||
"""
|
||||
return _hid.open_path(path)
|
||||
:returns: an open receiver handle if this is the right Linux device, or
|
||||
``None``.
|
||||
"""
|
||||
return _hid.open_path(path)
|
||||
|
||||
|
||||
def open():
|
||||
"""Opens the first Logitech Unifying Receiver found attached to the machine.
|
||||
"""Opens the first Logitech Unifying Receiver found attached to the machine.
|
||||
|
||||
:returns: An open file handle for the found receiver, or ``None``.
|
||||
"""
|
||||
for rawdevice in receivers():
|
||||
handle = open_path(rawdevice.path)
|
||||
if handle:
|
||||
return handle
|
||||
:returns: An open file handle for the found receiver, or ``None``.
|
||||
"""
|
||||
for rawdevice in receivers():
|
||||
handle = open_path(rawdevice.path)
|
||||
if handle:
|
||||
return handle
|
||||
|
||||
|
||||
def close(handle):
|
||||
"""Closes a HID device handle."""
|
||||
if handle:
|
||||
try:
|
||||
if isinstance(handle, int):
|
||||
_hid.close(handle)
|
||||
else:
|
||||
handle.close()
|
||||
# _log.info("closed receiver handle %r", handle)
|
||||
return True
|
||||
except:
|
||||
# _log.exception("closing receiver handle %r", handle)
|
||||
pass
|
||||
"""Closes a HID device handle."""
|
||||
if handle:
|
||||
try:
|
||||
if isinstance(handle, int):
|
||||
_hid.close(handle)
|
||||
else:
|
||||
handle.close()
|
||||
# _log.info("closed receiver handle %r", handle)
|
||||
return True
|
||||
except Exception:
|
||||
# _log.exception("closing receiver handle %r", handle)
|
||||
pass
|
||||
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
def write(handle, devnumber, data):
|
||||
"""Writes some data to the receiver, addressed to a certain device.
|
||||
def write(handle, devnumber, data, long_message=False):
|
||||
"""Writes some data to the receiver, addressed to a certain device.
|
||||
|
||||
:param handle: an open UR handle.
|
||||
:param devnumber: attached device number.
|
||||
:param data: data to send, up to 5 bytes.
|
||||
:param handle: an open UR handle.
|
||||
:param devnumber: attached device number.
|
||||
:param data: data to send, up to 5 bytes.
|
||||
|
||||
The first two (required) bytes of data must be the SubId and address.
|
||||
The first two (required) bytes of data must be the SubId and address.
|
||||
|
||||
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
||||
been physically removed from the machine, or the kernel driver has been
|
||||
unloaded. The handle will be closed automatically.
|
||||
"""
|
||||
# the data is padded to either 5 or 18 bytes
|
||||
assert data is not None
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
||||
been physically removed from the machine, or the kernel driver has been
|
||||
unloaded. The handle will be closed automatically.
|
||||
"""
|
||||
# the data is padded to either 5 or 18 bytes
|
||||
assert data is not None
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
|
||||
if len(data) > _SHORT_MESSAGE_SIZE - 2 or data[:1] == b'\x82':
|
||||
wdata = _pack('!BB18s', 0x11, devnumber, data)
|
||||
else:
|
||||
wdata = _pack('!BB5s', 0x10, devnumber, data)
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("(%s) <= w[%02X %02X %s %s]", handle, ord(wdata[:1]), devnumber, _strhex(wdata[2:4]), _strhex(wdata[4:]))
|
||||
if long_message or len(data) > _SHORT_MESSAGE_SIZE - 2 or data[:1] == b'\x82':
|
||||
wdata = _pack('!BB18s', HIDPP_LONG_MESSAGE_ID, devnumber, data)
|
||||
else:
|
||||
wdata = _pack('!BB5s', HIDPP_SHORT_MESSAGE_ID, devnumber, data)
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug('(%s) <= w[%02X %02X %s %s]', handle, ord(wdata[:1]), devnumber, _strhex(wdata[2:4]), _strhex(wdata[4:]))
|
||||
|
||||
try:
|
||||
_hid.write(int(handle), wdata)
|
||||
except Exception as reason:
|
||||
_log.error("write failed, assuming handle %r no longer available", handle)
|
||||
close(handle)
|
||||
raise NoReceiver(reason=reason)
|
||||
try:
|
||||
_hid.write(int(handle), wdata)
|
||||
except Exception as reason:
|
||||
_log.error('write failed, assuming handle %r no longer available', handle)
|
||||
close(handle)
|
||||
raise NoReceiver(reason=reason)
|
||||
|
||||
|
||||
def read(handle, timeout=DEFAULT_TIMEOUT):
|
||||
"""Read some data from the receiver. Usually called after a write (feature
|
||||
call), to get the reply.
|
||||
"""Read some data from the receiver. Usually called after a write (feature
|
||||
call), to get the reply.
|
||||
|
||||
:param: handle open handle to the receiver
|
||||
:param: timeout how long to wait for a reply, in seconds
|
||||
:param: handle open handle to the receiver
|
||||
:param: timeout how long to wait for a reply, in seconds
|
||||
|
||||
:returns: a tuple of (devnumber, message data), or `None`
|
||||
:returns: a tuple of (devnumber, message data), or `None`
|
||||
|
||||
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
||||
been physically removed from the machine, or the kernel driver has been
|
||||
unloaded. The handle will be closed automatically.
|
||||
"""
|
||||
reply = _read(handle, timeout)
|
||||
if reply:
|
||||
return reply[1:]
|
||||
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
||||
been physically removed from the machine, or the kernel driver has been
|
||||
unloaded. The handle will be closed automatically.
|
||||
"""
|
||||
reply = _read(handle, timeout)
|
||||
if reply:
|
||||
return reply
|
||||
|
||||
|
||||
# sanity checks on message report id and size
|
||||
def check_message(data):
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
report_id = ord(data[:1])
|
||||
if report_id in report_lengths: # is this an HID++ or DJ message?
|
||||
if report_lengths.get(report_id) == len(data):
|
||||
return True
|
||||
else:
|
||||
_log.warn('unexpected message size: report_id %02X message %s' % (report_id, _strhex(data)))
|
||||
return False
|
||||
|
||||
|
||||
def _read(handle, timeout):
|
||||
"""Read an incoming packet from the receiver.
|
||||
"""Read an incoming packet from the receiver.
|
||||
|
||||
:returns: a tuple of (report_id, devnumber, data), or `None`.
|
||||
:returns: a tuple of (report_id, devnumber, data), or `None`.
|
||||
|
||||
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
||||
been physically removed from the machine, or the kernel driver has been
|
||||
unloaded. The handle will be closed automatically.
|
||||
"""
|
||||
try:
|
||||
# convert timeout to milliseconds, the hidapi expects it
|
||||
timeout = int(timeout * 1000)
|
||||
data = _hid.read(int(handle), _MAX_READ_SIZE, timeout)
|
||||
except Exception as reason:
|
||||
_log.error("read failed, assuming handle %r no longer available", handle)
|
||||
close(handle)
|
||||
raise NoReceiver(reason=reason)
|
||||
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
||||
been physically removed from the machine, or the kernel driver has been
|
||||
unloaded. The handle will be closed automatically.
|
||||
"""
|
||||
try:
|
||||
# convert timeout to milliseconds, the hidapi expects it
|
||||
timeout = int(timeout * 1000)
|
||||
data = _hid.read(int(handle), _MAX_READ_SIZE, timeout)
|
||||
except Exception as reason:
|
||||
_log.warn('read failed, assuming handle %r no longer available', handle)
|
||||
close(handle)
|
||||
raise NoReceiver(reason=reason)
|
||||
|
||||
if data:
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
report_id = ord(data[:1])
|
||||
assert ((report_id & 0xF0 == 0) or
|
||||
(report_id == 0x10 and len(data) == _SHORT_MESSAGE_SIZE) or
|
||||
(report_id == 0x11 and len(data) == _LONG_MESSAGE_SIZE) or
|
||||
(report_id == 0x20 and len(data) == _MEDIUM_MESSAGE_SIZE)), \
|
||||
"unexpected message size: report_id %02X message %s" % (report_id, _strhex(data))
|
||||
if report_id & 0xF0 == 0x00:
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("(%s) => r[%02X %s] ignoring unknown report", handle, report_id, _strhex(data[1:]))
|
||||
return
|
||||
devnumber = ord(data[1:2])
|
||||
if data and check_message(data): # ignore messages that fail check
|
||||
report_id = ord(data[:1])
|
||||
devnumber = ord(data[1:2])
|
||||
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("(%s) => r[%02X %02X %s %s]", handle, report_id, devnumber, _strhex(data[2:4]), _strhex(data[4:]))
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug('(%s) => r[%02X %02X %s %s]', handle, report_id, devnumber, _strhex(data[2:4]), _strhex(data[4:]))
|
||||
|
||||
return report_id, devnumber, data[2:]
|
||||
|
||||
return report_id, devnumber, data[2:]
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
def _skip_incoming(handle, ihandle, notifications_hook):
|
||||
"""Read anything already in the input buffer.
|
||||
"""Read anything already in the input buffer.
|
||||
|
||||
Used by request() and ping() before their write.
|
||||
"""
|
||||
Used by request() and ping() before their write.
|
||||
"""
|
||||
|
||||
while True:
|
||||
try:
|
||||
# read whatever is already in the buffer, if any
|
||||
data = _hid.read(ihandle, _MAX_READ_SIZE, 0)
|
||||
except Exception as reason:
|
||||
_log.error("read failed, assuming receiver %s no longer available", handle)
|
||||
close(handle)
|
||||
raise NoReceiver(reason=reason)
|
||||
while True:
|
||||
try:
|
||||
# read whatever is already in the buffer, if any
|
||||
data = _hid.read(ihandle, _MAX_READ_SIZE, 0)
|
||||
except Exception as reason:
|
||||
_log.error('read failed, assuming receiver %s no longer available', handle)
|
||||
close(handle)
|
||||
raise NoReceiver(reason=reason)
|
||||
|
||||
if data:
|
||||
assert isinstance(data, bytes), (repr(data), type(data))
|
||||
report_id = ord(data[:1])
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
assert ((report_id & 0xF0 == 0) or
|
||||
(report_id == 0x10 and len(data) == _SHORT_MESSAGE_SIZE) or
|
||||
(report_id == 0x11 and len(data) == _LONG_MESSAGE_SIZE) or
|
||||
(report_id == 0x20 and len(data) == _MEDIUM_MESSAGE_SIZE)), \
|
||||
"unexpected message size: report_id %02X message %s" % (report_id, _strhex(data))
|
||||
if notifications_hook and report_id & 0xF0:
|
||||
n = make_notification(ord(data[1:2]), data[2:])
|
||||
if n:
|
||||
notifications_hook(n)
|
||||
else:
|
||||
# nothing in the input buffer, we're done
|
||||
return
|
||||
if data:
|
||||
if check_message(data): # only process messages that pass check
|
||||
# report_id = ord(data[:1])
|
||||
if notifications_hook:
|
||||
n = make_notification(ord(data[:1]), ord(data[1:2]), data[2:])
|
||||
if n:
|
||||
notifications_hook(n)
|
||||
else:
|
||||
# nothing in the input buffer, we're done
|
||||
return
|
||||
|
||||
|
||||
def make_notification(devnumber, data):
|
||||
"""Guess if this is a notification (and not just a request reply), and
|
||||
return a Notification tuple if it is."""
|
||||
sub_id = ord(data[:1])
|
||||
if sub_id & 0x80 == 0x80:
|
||||
# this is either a HID++1.0 register r/w, or an error reply
|
||||
return
|
||||
def make_notification(report_id, devnumber, data):
|
||||
"""Guess if this is a notification (and not just a request reply), and
|
||||
return a Notification tuple if it is."""
|
||||
|
||||
address = ord(data[1:2])
|
||||
if (
|
||||
# standard HID++ 1.0 notification, SubId may be 0x40 - 0x7F
|
||||
(sub_id >= 0x40)
|
||||
or
|
||||
# custom HID++1.0 battery events, where SubId is 0x07/0x0D
|
||||
(sub_id in (0x07, 0x0D) and len(data) == 5 and data[4:5] == b'\x00')
|
||||
or
|
||||
# custom HID++1.0 illumination event, where SubId is 0x17
|
||||
(sub_id == 0x17 and len(data) == 5)
|
||||
or
|
||||
# HID++ 2.0 feature notifications have the SoftwareID 0
|
||||
(address & 0x0F == 0x00)
|
||||
):
|
||||
return _HIDPP_Notification(devnumber, sub_id, address, data[2:])
|
||||
sub_id = ord(data[:1])
|
||||
if sub_id & 0x80 == 0x80:
|
||||
# this is either a HID++1.0 register r/w, or an error reply
|
||||
return
|
||||
|
||||
from collections import namedtuple
|
||||
_HIDPP_Notification = namedtuple('_HIDPP_Notification', ('devnumber', 'sub_id', 'address', 'data'))
|
||||
_HIDPP_Notification.__str__ = lambda self: 'Notification(%d,%02X,%02X,%s)' % (self.devnumber, self.sub_id, self.address, _strhex(self.data))
|
||||
# DJ input records are not notifications
|
||||
if report_id == DJ_MESSAGE_ID and (sub_id < 0x10):
|
||||
return
|
||||
|
||||
address = ord(data[1:2])
|
||||
if (
|
||||
# standard HID++ 1.0 notification, SubId may be 0x40 - 0x7F
|
||||
(sub_id >= 0x40) or # noqa: E131
|
||||
# custom HID++1.0 battery events, where SubId is 0x07/0x0D
|
||||
(sub_id in (0x07, 0x0D) and len(data) == 5 and data[4:5] == b'\x00') or
|
||||
# custom HID++1.0 illumination event, where SubId is 0x17
|
||||
(sub_id == 0x17 and len(data) == 5) or
|
||||
# HID++ 2.0 feature notifications have the SoftwareID 0
|
||||
(address & 0x0F == 0x00)
|
||||
): # noqa: E129
|
||||
return _HIDPP_Notification(report_id, devnumber, sub_id, address, data[2:])
|
||||
|
||||
|
||||
_HIDPP_Notification = namedtuple('_HIDPP_Notification', ('report_id', 'devnumber', 'sub_id', 'address', 'data'))
|
||||
_HIDPP_Notification.__str__ = lambda self: 'Notification(%02x,%d,%02X,%02X,%s)' % (
|
||||
self.report_id, self.devnumber, self.sub_id, self.address, _strhex(self.data)
|
||||
)
|
||||
_HIDPP_Notification.__unicode__ = _HIDPP_Notification.__str__
|
||||
del namedtuple
|
||||
|
||||
@@ -297,186 +354,220 @@ del namedtuple
|
||||
#
|
||||
#
|
||||
|
||||
def request(handle, devnumber, request_id, *params):
|
||||
"""Makes a feature call to a device and waits for a matching reply.
|
||||
|
||||
This function will wait for a matching reply indefinitely.
|
||||
|
||||
:param handle: an open UR handle.
|
||||
:param devnumber: attached device number.
|
||||
:param request_id: a 16-bit integer.
|
||||
:param params: parameters for the feature call, 3 to 16 bytes.
|
||||
:returns: the reply data, or ``None`` if some error occured.
|
||||
"""
|
||||
|
||||
# import inspect as _inspect
|
||||
# print ('\n '.join(str(s) for s in _inspect.stack()))
|
||||
|
||||
assert isinstance(request_id, int)
|
||||
if devnumber != 0xFF and request_id < 0x8000:
|
||||
# For HID++ 2.0 feature requests, randomize the SoftwareId to make it
|
||||
# easier to recognize the reply for this request. also, always set the
|
||||
# most significant bit (8) in SoftwareId, to make notifications easier
|
||||
# to distinguish from request replies.
|
||||
# This only applies to peripheral requests, ofc.
|
||||
request_id = (request_id & 0xFFF0) | 0x08 | _random_bits(3)
|
||||
|
||||
timeout = _RECEIVER_REQUEST_TIMEOUT if devnumber == 0xFF else _DEVICE_REQUEST_TIMEOUT
|
||||
# be extra patient on long register read
|
||||
if request_id & 0xFF00 == 0x8300:
|
||||
timeout *= 2
|
||||
|
||||
if params:
|
||||
params = b''.join(_pack('B', p) if isinstance(p, int) else p for p in params)
|
||||
else:
|
||||
params = b''
|
||||
# if _log.isEnabledFor(_DEBUG):
|
||||
# _log.debug("(%s) device %d request_id {%04X} params [%s]", handle, devnumber, request_id, _strhex(params))
|
||||
request_data = _pack('!H', request_id) + params
|
||||
|
||||
ihandle = int(handle)
|
||||
notifications_hook = getattr(handle, 'notifications_hook', None)
|
||||
_skip_incoming(handle, ihandle, notifications_hook)
|
||||
write(ihandle, devnumber, request_data)
|
||||
|
||||
# we consider timeout from this point
|
||||
request_started = _timestamp()
|
||||
delta = 0
|
||||
|
||||
while delta < timeout:
|
||||
reply = _read(handle, timeout)
|
||||
|
||||
if reply:
|
||||
report_id, reply_devnumber, reply_data = reply
|
||||
if reply_devnumber == devnumber:
|
||||
if report_id == 0x10 and reply_data[:1] == b'\x8F' and reply_data[1:3] == request_data[:2]:
|
||||
error = ord(reply_data[3:4])
|
||||
|
||||
# if error == _hidpp10.ERROR.resource_error: # device unreachable
|
||||
# _log.warn("(%s) device %d error on request {%04X}: unknown device", handle, devnumber, request_id)
|
||||
# raise DeviceUnreachable(number=devnumber, request=request_id)
|
||||
|
||||
# if error == _hidpp10.ERROR.unknown_device: # unknown device
|
||||
# _log.error("(%s) device %d error on request {%04X}: unknown device", handle, devnumber, request_id)
|
||||
# raise NoSuchDevice(number=devnumber, request=request_id)
|
||||
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("(%s) device 0x%02X error on request {%04X}: %d = %s",
|
||||
handle, devnumber, request_id, error, _hidpp10.ERROR[error])
|
||||
return
|
||||
|
||||
if reply_data[:1] == b'\xFF' and reply_data[1:3] == request_data[:2]:
|
||||
# a HID++ 2.0 feature call returned with an error
|
||||
error = ord(reply_data[3:4])
|
||||
_log.error("(%s) device %d error on feature request {%04X}: %d = %s",
|
||||
handle, devnumber, request_id, error, _hidpp20.ERROR[error])
|
||||
raise _hidpp20.FeatureCallError(number=devnumber, request=request_id, error=error, params=params)
|
||||
|
||||
if reply_data[:2] == request_data[:2]:
|
||||
if request_id & 0xFE00 == 0x8200:
|
||||
# long registry r/w should return a long reply
|
||||
assert report_id == 0x11
|
||||
elif request_id & 0xFE00 == 0x8000:
|
||||
# short registry r/w should return a short reply
|
||||
assert report_id == 0x10
|
||||
|
||||
if devnumber == 0xFF:
|
||||
if request_id == 0x83B5 or request_id == 0x81F1:
|
||||
# these replies have to match the first parameter as well
|
||||
if reply_data[2:3] == params[:1]:
|
||||
return reply_data[2:]
|
||||
else:
|
||||
# hm, not mathing my request, and certainly not a notification
|
||||
continue
|
||||
else:
|
||||
return reply_data[2:]
|
||||
else:
|
||||
return reply_data[2:]
|
||||
else:
|
||||
# a reply was received, but did not match our request in any way
|
||||
# reset the timeout starting point
|
||||
request_started = _timestamp()
|
||||
|
||||
if notifications_hook:
|
||||
n = make_notification(reply_devnumber, reply_data)
|
||||
if n:
|
||||
notifications_hook(n)
|
||||
# elif _log.isEnabledFor(_DEBUG):
|
||||
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
||||
# elif _log.isEnabledFor(_DEBUG):
|
||||
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
||||
|
||||
delta = _timestamp() - request_started
|
||||
# if _log.isEnabledFor(_DEBUG):
|
||||
# _log.debug("(%s) still waiting for reply, delta %f", handle, delta)
|
||||
|
||||
_log.warn("timeout (%0.2f/%0.2f) on device %d request {%04X} params [%s]",
|
||||
delta, timeout, devnumber, request_id, _strhex(params))
|
||||
# raise DeviceUnreachable(number=devnumber, request=request_id)
|
||||
request_lock = _threading.Lock() # serialize all requests
|
||||
handles_lock = {}
|
||||
|
||||
|
||||
def ping(handle, devnumber):
|
||||
"""Check if a device is connected to the receiver.
|
||||
def handle_lock(handle):
|
||||
with request_lock:
|
||||
if handles_lock.get(handle) is None:
|
||||
if _log.isEnabledFor(_INFO):
|
||||
_log.info('New lock %s', repr(handle))
|
||||
handles_lock[handle] = _threading.Lock() # Serialize requests on the handle
|
||||
return handles_lock[handle]
|
||||
|
||||
:returns: The HID protocol supported by the device, as a floating point number, if the device is active.
|
||||
"""
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("(%s) pinging device %d", handle, devnumber)
|
||||
|
||||
# import inspect as _inspect
|
||||
# print ('\n '.join(str(s) for s in _inspect.stack()))
|
||||
# context manager for locks with a timeout
|
||||
@contextmanager
|
||||
def acquire_timeout(lock, handle, timeout):
|
||||
result = lock.acquire(timeout=timeout)
|
||||
try:
|
||||
if not result:
|
||||
_log.error('lock on handle %d not acquired, probably due to timeout', int(handle))
|
||||
yield result
|
||||
finally:
|
||||
if result:
|
||||
lock.release()
|
||||
|
||||
assert devnumber != 0xFF
|
||||
assert devnumber > 0x00
|
||||
assert devnumber < 0x0F
|
||||
|
||||
# randomize the SoftwareId and mark byte to be able to identify the ping
|
||||
# reply, and set most significant (0x8) bit in SoftwareId so that the reply
|
||||
# is always distinguishable from notifications
|
||||
request_id = 0x0018 | _random_bits(3)
|
||||
request_data = _pack('!HBBB', request_id, 0, 0, _random_bits(8))
|
||||
# a very few requests (e.g., host switching) do not expect a reply, but use no_reply=True with extreme caution
|
||||
def request(handle, devnumber, request_id, *params, no_reply=False, return_error=False, long_message=False, protocol=1.0):
|
||||
"""Makes a feature call to a device and waits for a matching reply.
|
||||
:param handle: an open UR handle.
|
||||
:param devnumber: attached device number.
|
||||
:param request_id: a 16-bit integer.
|
||||
:param params: parameters for the feature call, 3 to 16 bytes.
|
||||
:returns: the reply data, or ``None`` if some error occurred. or no reply expected
|
||||
"""
|
||||
|
||||
ihandle = int(handle)
|
||||
notifications_hook = getattr(handle, 'notifications_hook', None)
|
||||
_skip_incoming(handle, ihandle, notifications_hook)
|
||||
write(ihandle, devnumber, request_data)
|
||||
# import inspect as _inspect
|
||||
# print ('\n '.join(str(s) for s in _inspect.stack()))
|
||||
|
||||
# we consider timeout from this point
|
||||
request_started = _timestamp()
|
||||
delta = 0
|
||||
with acquire_timeout(handle_lock(handle), handle, 10.):
|
||||
assert isinstance(request_id, int)
|
||||
if (devnumber != 0xFF or protocol >= 2.0) and request_id < 0x8000:
|
||||
# For HID++ 2.0 feature requests, randomize the SoftwareId to make it
|
||||
# easier to recognize the reply for this request. also, always set the
|
||||
# most significant bit (8) in SoftwareId, to make notifications easier
|
||||
# to distinguish from request replies.
|
||||
# This only applies to peripheral requests, ofc.
|
||||
request_id = (request_id & 0xFFF0) | 0x08 | _random_bits(3)
|
||||
|
||||
while delta < _PING_TIMEOUT:
|
||||
reply = _read(handle, _PING_TIMEOUT)
|
||||
timeout = _RECEIVER_REQUEST_TIMEOUT if devnumber == 0xFF else _DEVICE_REQUEST_TIMEOUT
|
||||
# be extra patient on long register read
|
||||
if request_id & 0xFF00 == 0x8300:
|
||||
timeout *= 2
|
||||
|
||||
if reply:
|
||||
report_id, reply_devnumber, reply_data = reply
|
||||
if reply_devnumber == devnumber:
|
||||
if reply_data[:2] == request_data[:2] and reply_data[4:5] == request_data[-1:]:
|
||||
# HID++ 2.0+ device, currently connected
|
||||
return ord(reply_data[2:3]) + ord(reply_data[3:4]) / 10.0
|
||||
if params:
|
||||
params = b''.join(_pack('B', p) if isinstance(p, int) else p for p in params)
|
||||
else:
|
||||
params = b''
|
||||
# if _log.isEnabledFor(_DEBUG):
|
||||
# _log.debug("(%s) device %d request_id {%04X} params [%s]", handle, devnumber, request_id, _strhex(params))
|
||||
request_data = _pack('!H', request_id) + params
|
||||
|
||||
if report_id == 0x10 and reply_data[:1] == b'\x8F' and reply_data[1:3] == request_data[:2]:
|
||||
assert reply_data[-1:] == b'\x00'
|
||||
error = ord(reply_data[3:4])
|
||||
ihandle = int(handle)
|
||||
notifications_hook = getattr(handle, 'notifications_hook', None)
|
||||
try:
|
||||
_skip_incoming(handle, ihandle, notifications_hook)
|
||||
except NoReceiver:
|
||||
_log.warn('device or receiver disconnected')
|
||||
return None
|
||||
write(ihandle, devnumber, request_data, long_message)
|
||||
|
||||
if error == _hidpp10.ERROR.invalid_SubID__command: # a valid reply from a HID++ 1.0 device
|
||||
return 1.0
|
||||
if no_reply:
|
||||
return None
|
||||
|
||||
if error == _hidpp10.ERROR.resource_error: # device unreachable
|
||||
return
|
||||
# we consider timeout from this point
|
||||
request_started = _timestamp()
|
||||
delta = 0
|
||||
|
||||
if error == _hidpp10.ERROR.unknown_device: # no paired device with that number
|
||||
_log.error("(%s) device %d error on ping request: unknown device", handle, devnumber)
|
||||
raise NoSuchDevice(number=devnumber, request=request_id)
|
||||
while delta < timeout:
|
||||
reply = _read(handle, timeout)
|
||||
|
||||
if notifications_hook:
|
||||
n = make_notification(reply_devnumber, reply_data)
|
||||
if n:
|
||||
notifications_hook(n)
|
||||
# elif _log.isEnabledFor(_DEBUG):
|
||||
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
||||
if reply:
|
||||
report_id, reply_devnumber, reply_data = reply
|
||||
if reply_devnumber == devnumber:
|
||||
if report_id == HIDPP_SHORT_MESSAGE_ID and reply_data[:1] == b'\x8F' and reply_data[1:3] == request_data[:2
|
||||
]:
|
||||
error = ord(reply_data[3:4])
|
||||
|
||||
delta = _timestamp() - request_started
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug(
|
||||
'(%s) device 0x%02X error on request {%04X}: %d = %s', handle, devnumber, request_id, error,
|
||||
_hidpp10.ERROR[error]
|
||||
)
|
||||
return _hidpp10.ERROR[error] if return_error else None
|
||||
if reply_data[:1] == b'\xFF' and reply_data[1:3] == request_data[:2]:
|
||||
# a HID++ 2.0 feature call returned with an error
|
||||
error = ord(reply_data[3:4])
|
||||
_log.error(
|
||||
'(%s) device %d error on feature request {%04X}: %d = %s', handle, devnumber, request_id, error,
|
||||
_hidpp20.ERROR[error]
|
||||
)
|
||||
raise _hidpp20.FeatureCallError(number=devnumber, request=request_id, error=error, params=params)
|
||||
|
||||
_log.warn("(%s) timeout (%0.2f/%0.2f) on device %d ping", handle, delta, _PING_TIMEOUT, devnumber)
|
||||
# raise DeviceUnreachable(number=devnumber, request=request_id)
|
||||
if reply_data[:2] == request_data[:2]:
|
||||
if request_id & 0xFE00 == 0x8200:
|
||||
# long registry r/w should return a long reply
|
||||
assert report_id == HIDPP_LONG_MESSAGE_ID
|
||||
elif request_id & 0xFE00 == 0x8000:
|
||||
# short registry r/w should return a short reply
|
||||
assert report_id == HIDPP_SHORT_MESSAGE_ID
|
||||
|
||||
if devnumber == 0xFF:
|
||||
if request_id == 0x83B5 or request_id == 0x81F1:
|
||||
# these replies have to match the first parameter as well
|
||||
if reply_data[2:3] == params[:1]:
|
||||
return reply_data[2:]
|
||||
else:
|
||||
# hm, not matching my request, and certainly not a notification
|
||||
continue
|
||||
else:
|
||||
return reply_data[2:]
|
||||
else:
|
||||
return reply_data[2:]
|
||||
else:
|
||||
# a reply was received, but did not match our request in any way
|
||||
# reset the timeout starting point
|
||||
request_started = _timestamp()
|
||||
|
||||
if notifications_hook:
|
||||
n = make_notification(report_id, reply_devnumber, reply_data)
|
||||
if n:
|
||||
notifications_hook(n)
|
||||
# elif _log.isEnabledFor(_DEBUG):
|
||||
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
||||
# elif _log.isEnabledFor(_DEBUG):
|
||||
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
||||
|
||||
delta = _timestamp() - request_started
|
||||
# if _log.isEnabledFor(_DEBUG):
|
||||
# _log.debug("(%s) still waiting for reply, delta %f", handle, delta)
|
||||
|
||||
_log.warn(
|
||||
'timeout (%0.2f/%0.2f) on device %d request {%04X} params [%s]', delta, timeout, devnumber, request_id,
|
||||
_strhex(params)
|
||||
)
|
||||
# raise DeviceUnreachable(number=devnumber, request=request_id)
|
||||
|
||||
|
||||
def ping(handle, devnumber, long_message=False):
|
||||
"""Check if a device is connected to the receiver.
|
||||
|
||||
:returns: The HID protocol supported by the device, as a floating point number, if the device is active.
|
||||
"""
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug('(%s) pinging device %d', handle, devnumber)
|
||||
|
||||
# import inspect as _inspect
|
||||
# print ('\n '.join(str(s) for s in _inspect.stack()))
|
||||
|
||||
with acquire_timeout(handle_lock(handle), handle, 10.):
|
||||
|
||||
# randomize the SoftwareId and mark byte to be able to identify the ping
|
||||
# reply, and set most significant (0x8) bit in SoftwareId so that the reply
|
||||
# is always distinguishable from notifications
|
||||
request_id = 0x0018 | _random_bits(3)
|
||||
request_data = _pack('!HBBB', request_id, 0, 0, _random_bits(8))
|
||||
|
||||
ihandle = int(handle)
|
||||
notifications_hook = getattr(handle, 'notifications_hook', None)
|
||||
try:
|
||||
_skip_incoming(handle, ihandle, notifications_hook)
|
||||
except NoReceiver:
|
||||
_log.warn('device or receiver disconnected')
|
||||
return
|
||||
|
||||
write(ihandle, devnumber, request_data, long_message)
|
||||
|
||||
# we consider timeout from this point
|
||||
request_started = _timestamp()
|
||||
delta = 0
|
||||
|
||||
while delta < _PING_TIMEOUT:
|
||||
reply = _read(handle, _PING_TIMEOUT)
|
||||
|
||||
if reply:
|
||||
report_id, reply_devnumber, reply_data = reply
|
||||
if reply_devnumber == devnumber:
|
||||
if reply_data[:2] == request_data[:2] and reply_data[4:5] == request_data[-1:]:
|
||||
# HID++ 2.0+ device, currently connected
|
||||
return ord(reply_data[2:3]) + ord(reply_data[3:4]) / 10.0
|
||||
|
||||
if report_id == HIDPP_SHORT_MESSAGE_ID and reply_data[:1] == b'\x8F' and reply_data[1:3] == request_data[:2
|
||||
]:
|
||||
assert reply_data[-1:] == b'\x00'
|
||||
error = ord(reply_data[3:4])
|
||||
|
||||
if error == _hidpp10.ERROR.invalid_SubID__command: # a valid reply from a HID++ 1.0 device
|
||||
return 1.0
|
||||
|
||||
if error == _hidpp10.ERROR.resource_error: # device unreachable
|
||||
return
|
||||
|
||||
if error == _hidpp10.ERROR.unknown_device: # no paired device with that number
|
||||
_log.error('(%s) device %d error on ping request: unknown device', handle, devnumber)
|
||||
raise NoSuchDevice(number=devnumber, request=request_id)
|
||||
|
||||
if notifications_hook:
|
||||
n = make_notification(report_id, reply_devnumber, reply_data)
|
||||
if n:
|
||||
notifications_hook(n)
|
||||
# elif _log.isEnabledFor(_DEBUG):
|
||||
# _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data))
|
||||
|
||||
delta = _timestamp() - request_started
|
||||
|
||||
_log.warn('(%s) timeout (%0.2f/%0.2f) on device %d ping', handle, delta, _PING_TIMEOUT, devnumber)
|
||||
# raise DeviceUnreachable(number=devnumber, request=request_id)
|
||||
|
||||
@@ -17,47 +17,206 @@
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
## According to Logitech, they use the following product IDs (as of September 2020)
|
||||
## USB product IDs for receivers: 0xC526 - 0xC5xx
|
||||
## Wireless PIDs for hidpp10 devices: 0x2006 - 0x2019
|
||||
## Wireless PIDs for hidpp20 devices: 0x4002 - 0x4097, 0x4101 - 0x4102
|
||||
## USB product IDs for hidpp20 devices: 0xC07D - 0xC093, 0xC32B - 0xC344
|
||||
## Bluetooth product IDs (for hidpp20 devices): 0xB012 - 0xB0xx, 0xB32A - 0xB3xx
|
||||
|
||||
# USB ids of Logitech wireless receivers.
|
||||
# Only receivers supporting the HID++ protocol can go in here.
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from .descriptors import DEVICES as _DEVICES
|
||||
from .i18n import _
|
||||
|
||||
_UNIFYING_DRIVER = 'logitech-djreceiver'
|
||||
_GENERIC_DRIVER = ('hid-generic', 'generic-usb')
|
||||
# max_devices is only used for receivers that do not support reading from _R.receiver_info offset 0x03, default to 1
|
||||
# may_unpair is only used for receivers that do not support reading from _R.receiver_info offset 0x03, default to False
|
||||
# unpair is for receivers that do support reading from _R.receiver_info offset 0x03, no default
|
||||
## should this last be changed so that may_unpair is used for all receivers? writing to _R.receiver_pairing doesn't seem right
|
||||
# re_pairs determines whether a receiver pairs by replacing existing pairings, default to False
|
||||
## currently only one receiver is so marked - should there be more?
|
||||
# ex100_27mhz_wpid_fix enable workarounds for EX100 and possible other old 27Mhz receivers
|
||||
|
||||
_DRIVER = ('hid-generic', 'generic-usb', 'logitech-djreceiver')
|
||||
|
||||
# each tuple contains (vendor_id, product_id, usb interface number, hid driver)
|
||||
_unifying_receiver = lambda product_id: {
|
||||
'vendor_id': 0x046d,
|
||||
'product_id': product_id,
|
||||
'usb_interface': 2,
|
||||
'hid_driver': _DRIVER, # noqa: F821
|
||||
'name': _('Unifying Receiver')
|
||||
}
|
||||
|
||||
_nano_receiver = lambda product_id: {
|
||||
'vendor_id': 0x046d,
|
||||
'product_id': product_id,
|
||||
'usb_interface': 1,
|
||||
'hid_driver': _DRIVER, # noqa: F821
|
||||
'name': _('Nano Receiver'),
|
||||
'may_unpair': False,
|
||||
're_pairs': True
|
||||
}
|
||||
|
||||
_nano_receiver_no_unpair = lambda product_id: {
|
||||
'vendor_id': 0x046d,
|
||||
'product_id': product_id,
|
||||
'usb_interface': 1,
|
||||
'hid_driver': _DRIVER, # noqa: F821
|
||||
'name': _('Nano Receiver'),
|
||||
'may_unpair': False,
|
||||
'unpair': False,
|
||||
're_pairs': True
|
||||
}
|
||||
|
||||
_nano_receiver_max2 = lambda product_id: {
|
||||
'vendor_id': 0x046d,
|
||||
'product_id': product_id,
|
||||
'usb_interface': 1,
|
||||
'hid_driver': _DRIVER, # noqa: F821
|
||||
'name': _('Nano Receiver'),
|
||||
'max_devices': 2,
|
||||
'may_unpair': False,
|
||||
're_pairs': True
|
||||
}
|
||||
|
||||
_nano_receiver_maxn = lambda product_id, max: {
|
||||
'vendor_id': 0x046d,
|
||||
'product_id': product_id,
|
||||
'usb_interface': 1,
|
||||
'hid_driver': _DRIVER, # noqa: F821
|
||||
'name': _('Nano Receiver'),
|
||||
'max_devices': max,
|
||||
'may_unpair': False,
|
||||
're_pairs': True
|
||||
}
|
||||
|
||||
_lenovo_receiver = lambda product_id: {
|
||||
'vendor_id': 0x17ef,
|
||||
'product_id': product_id,
|
||||
'usb_interface': 1,
|
||||
'hid_driver': _DRIVER, # noqa: F821
|
||||
'name': _('Nano Receiver')
|
||||
}
|
||||
|
||||
_lightspeed_receiver = lambda product_id: {
|
||||
'vendor_id': 0x046d,
|
||||
'product_id': product_id,
|
||||
'usb_interface': 2,
|
||||
'hid_driver': _DRIVER, # noqa: F821
|
||||
'name': _('Lightspeed Receiver')
|
||||
}
|
||||
|
||||
_ex100_receiver = lambda product_id: {
|
||||
'vendor_id': 0x046d,
|
||||
'product_id': product_id,
|
||||
'usb_interface': 1,
|
||||
'hid_driver': _DRIVER, # noqa: F821
|
||||
'name': _('EX100 Receiver 27 Mhz'),
|
||||
'max_devices': 4,
|
||||
'may_unpair': False,
|
||||
're_pairs': True,
|
||||
'ex100_27mhz_wpid_fix': True
|
||||
}
|
||||
|
||||
# standard Unifying receivers (marked with the orange Unifying logo)
|
||||
UNIFYING_RECEIVER = (0x046d, 0xc52b, 2, _UNIFYING_DRIVER)
|
||||
UNIFYING_RECEIVER_2 = (0x046d, 0xc532, 2, _UNIFYING_DRIVER)
|
||||
|
||||
|
||||
UNIFYING_RECEIVER_C52B = _unifying_receiver(0xc52b)
|
||||
UNIFYING_RECEIVER_C532 = _unifying_receiver(0xc532)
|
||||
|
||||
# Nano receviers that support the Unifying protocol
|
||||
NANO_RECEIVER_ADVANCED = (0x046d, 0xc52f, 1, _GENERIC_DRIVER)
|
||||
NANO_RECEIVER_ADVANCED = _nano_receiver_no_unpair(0xc52f)
|
||||
|
||||
# ex100 old style receiver pre-unifyimg protocol
|
||||
EX100_27MHZ_RECEIVER_C517 = _ex100_receiver(0xc517)
|
||||
|
||||
# Nano receivers that don't support the Unifying protocol
|
||||
NANO_RECEIVER_C517 = (0x046d, 0xc517, 1, _GENERIC_DRIVER)
|
||||
NANO_RECEIVER_C518 = (0x046d, 0xc518, 1, _GENERIC_DRIVER)
|
||||
NANO_RECEIVER_C51A = (0x046d, 0xc51a, 1, _GENERIC_DRIVER)
|
||||
NANO_RECEIVER_C51B = (0x046d, 0xc51b, 1, _GENERIC_DRIVER)
|
||||
NANO_RECEIVER_C521 = (0x046d, 0xc521, 1, _GENERIC_DRIVER)
|
||||
NANO_RECEIVER_C525 = (0x046d, 0xc525, 1, _GENERIC_DRIVER)
|
||||
NANO_RECEIVER_C526 = (0x046d, 0xc526, 1, _GENERIC_DRIVER)
|
||||
|
||||
NANO_RECEIVER_C518 = _nano_receiver(0xc518)
|
||||
NANO_RECEIVER_C51A = _nano_receiver(0xc51a)
|
||||
NANO_RECEIVER_C51B = _nano_receiver(0xc51b)
|
||||
NANO_RECEIVER_C521 = _nano_receiver(0xc521)
|
||||
NANO_RECEIVER_C525 = _nano_receiver(0xc525)
|
||||
NANO_RECEIVER_C526 = _nano_receiver(0xc526)
|
||||
NANO_RECEIVER_C52e = _nano_receiver_no_unpair(0xc52e)
|
||||
NANO_RECEIVER_C531 = _nano_receiver(0xc531)
|
||||
NANO_RECEIVER_C534 = _nano_receiver_max2(0xc534)
|
||||
NANO_RECEIVER_C537 = _nano_receiver(0xc537)
|
||||
NANO_RECEIVER_6042 = _lenovo_receiver(0x6042)
|
||||
|
||||
# Lightspeed receivers
|
||||
LIGHTSPEED_RECEIVER_C539 = _lightspeed_receiver(0xc539)
|
||||
LIGHTSPEED_RECEIVER_C53a = _lightspeed_receiver(0xc53a)
|
||||
LIGHTSPEED_RECEIVER_C53f = _lightspeed_receiver(0xc53f)
|
||||
LIGHTSPEED_RECEIVER_C53d = _lightspeed_receiver(0xc53d)
|
||||
LIGHTSPEED_RECEIVER_C545 = _lightspeed_receiver(0xc545)
|
||||
LIGHTSPEED_RECEIVER_C541 = _lightspeed_receiver(0xc541)
|
||||
LIGHTSPEED_RECEIVER_C547 = _lightspeed_receiver(0xc547)
|
||||
|
||||
ALL = (
|
||||
UNIFYING_RECEIVER,
|
||||
UNIFYING_RECEIVER_2,
|
||||
NANO_RECEIVER_ADVANCED,
|
||||
NANO_RECEIVER_C517,
|
||||
NANO_RECEIVER_C518,
|
||||
NANO_RECEIVER_C51A,
|
||||
NANO_RECEIVER_C51B,
|
||||
NANO_RECEIVER_C521,
|
||||
NANO_RECEIVER_C525,
|
||||
NANO_RECEIVER_C526,
|
||||
)
|
||||
UNIFYING_RECEIVER_C52B,
|
||||
UNIFYING_RECEIVER_C532,
|
||||
NANO_RECEIVER_ADVANCED,
|
||||
EX100_27MHZ_RECEIVER_C517,
|
||||
NANO_RECEIVER_C518,
|
||||
NANO_RECEIVER_C51A,
|
||||
NANO_RECEIVER_C51B,
|
||||
NANO_RECEIVER_C521,
|
||||
NANO_RECEIVER_C525,
|
||||
NANO_RECEIVER_C526,
|
||||
NANO_RECEIVER_C52e,
|
||||
NANO_RECEIVER_C531,
|
||||
NANO_RECEIVER_C534,
|
||||
NANO_RECEIVER_C537,
|
||||
NANO_RECEIVER_6042,
|
||||
LIGHTSPEED_RECEIVER_C539,
|
||||
LIGHTSPEED_RECEIVER_C53a,
|
||||
LIGHTSPEED_RECEIVER_C53f,
|
||||
LIGHTSPEED_RECEIVER_C53d,
|
||||
LIGHTSPEED_RECEIVER_C545,
|
||||
LIGHTSPEED_RECEIVER_C541,
|
||||
LIGHTSPEED_RECEIVER_C547,
|
||||
)
|
||||
|
||||
_wired_device = lambda product_id, interface: {
|
||||
'vendor_id': 0x046d,
|
||||
'product_id': product_id,
|
||||
'bus_id': 0x3,
|
||||
'usb_interface': interface,
|
||||
'isDevice': True
|
||||
}
|
||||
|
||||
_bt_device = lambda product_id: {'vendor_id': 0x046d, 'product_id': product_id, 'bus_id': 0x5, 'isDevice': True}
|
||||
|
||||
DEVICES = []
|
||||
|
||||
for _ignore, d in _DEVICES.items():
|
||||
if d.usbid:
|
||||
DEVICES.append(_wired_device(d.usbid, d.interface if d.interface else 2))
|
||||
if d.btid:
|
||||
DEVICES.append(_bt_device(d.btid))
|
||||
|
||||
|
||||
def other_device_check(bus_id, vendor_id, product_id):
|
||||
"""Check whether product is a Logitech USB-connected or Bluetooth device based on bus, vendor, and product IDs
|
||||
This allows Solaar to support receiverless HID++ 2.0 devices that it knows nothing about"""
|
||||
if vendor_id != 0x46d: # Logitech
|
||||
return
|
||||
if bus_id == 0x3: # USB
|
||||
if (product_id >= 0xC07D and product_id <= 0xC093 or product_id >= 0xC32B and product_id <= 0xC344):
|
||||
return _wired_device(product_id, 2)
|
||||
elif bus_id == 0x5: # Bluetooth
|
||||
if (product_id >= 0xB012 and product_id <= 0xB0FF or product_id >= 0xB32A and product_id <= 0xB3FF):
|
||||
return _bt_device(product_id)
|
||||
|
||||
|
||||
def product_information(usb_id):
|
||||
if isinstance(usb_id, str):
|
||||
usb_id = int(usb_id, 16)
|
||||
for r in ALL:
|
||||
if usb_id == r.get('product_id'):
|
||||
return r
|
||||
return {}
|
||||
|
||||
|
||||
del _DRIVER, _unifying_receiver, _nano_receiver, _lenovo_receiver, _lightspeed_receiver
|
||||
|
||||
@@ -22,256 +22,255 @@
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from binascii import hexlify as _hexlify
|
||||
from collections import namedtuple
|
||||
from struct import pack, unpack
|
||||
|
||||
try:
|
||||
unicode
|
||||
# if Python2, unicode_literals will mess our first (un)pack() argument
|
||||
_pack_str = pack
|
||||
_unpack_str = unpack
|
||||
pack = lambda x, *args: _pack_str(str(x), *args)
|
||||
unpack = lambda x, *args: _unpack_str(str(x), *args)
|
||||
unicode # noqa: F821
|
||||
# if Python2, unicode_literals will mess our first (un)pack() argument
|
||||
_pack_str = pack
|
||||
_unpack_str = unpack
|
||||
pack = lambda x, *args: _pack_str(str(x), *args)
|
||||
unpack = lambda x, *args: _unpack_str(str(x), *args)
|
||||
|
||||
is_string = lambda d: isinstance(d, unicode) or isinstance(d, str)
|
||||
# no easy way to distinguish between b'' and '' :(
|
||||
# or (isinstance(d, str) \
|
||||
# and not any((chr(k) in d for k in range(0x00, 0x1F))) \
|
||||
# and not any((chr(k) in d for k in range(0x80, 0xFF))) \
|
||||
# )
|
||||
except:
|
||||
# this is certanly Python 3
|
||||
# In Py3, unicode and str are equal (the unicode object does not exist)
|
||||
is_string = lambda d: isinstance(d, str)
|
||||
is_string = lambda d: isinstance(d, unicode) or isinstance(d, str) # noqa: F821
|
||||
# no easy way to distinguish between b'' and '' :(
|
||||
# or (isinstance(d, str) \
|
||||
# and not any((chr(k) in d for k in range(0x00, 0x1F))) \
|
||||
# and not any((chr(k) in d for k in range(0x80, 0xFF))) \
|
||||
# )
|
||||
except Exception:
|
||||
# this is certainly Python 3
|
||||
# In Py3, unicode and str are equal (the unicode object does not exist)
|
||||
is_string = lambda d: isinstance(d, str)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
class NamedInt(int):
|
||||
"""An reqular Python integer with an attached name.
|
||||
"""A regular Python integer with an attached name.
|
||||
|
||||
Caution: comparison with strings will also match this NamedInt's name
|
||||
(case-insensitive)."""
|
||||
Caution: comparison with strings will also match this NamedInt's name
|
||||
(case-insensitive)."""
|
||||
def __new__(cls, value, name):
|
||||
assert is_string(name)
|
||||
obj = int.__new__(cls, value)
|
||||
obj.name = str(name)
|
||||
return obj
|
||||
|
||||
def __new__(cls, value, name):
|
||||
assert is_string(name)
|
||||
obj = int.__new__(cls, value)
|
||||
obj.name = str(name)
|
||||
return obj
|
||||
def bytes(self, count=2):
|
||||
return int2bytes(self, count)
|
||||
|
||||
def bytes(self, count=2):
|
||||
return int2bytes(self, count)
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, NamedInt):
|
||||
return int(self) == int(other) and self.name == other.name
|
||||
if isinstance(other, int):
|
||||
return int(self) == int(other)
|
||||
if is_string(other):
|
||||
return self.name.lower() == other.lower()
|
||||
# this should catch comparisons with bytes in Py3
|
||||
if other is not None:
|
||||
raise TypeError('Unsupported type ' + str(type(other)))
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, NamedInt):
|
||||
return int(self) == int(other) and self.name == other.name
|
||||
if isinstance(other, int):
|
||||
return int(self) == int(other)
|
||||
if is_string(other):
|
||||
return self.name.lower() == other.lower()
|
||||
# this should catch comparisons with bytes in Py3
|
||||
if other is not None:
|
||||
raise TypeError('Unsupported type ' + str(type(other)))
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
def __hash__(self):
|
||||
return int(self)
|
||||
|
||||
def __hash__(self):
|
||||
return int(self)
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
__unicode__ = __str__
|
||||
__unicode__ = __str__
|
||||
|
||||
def __repr__(self):
|
||||
return 'NamedInt(%d, %r)' % (int(self), self.name)
|
||||
def __repr__(self):
|
||||
return 'NamedInt(%d, %r)' % (int(self), self.name)
|
||||
|
||||
|
||||
class NamedInts(object):
|
||||
"""An ordered set of NamedInt values.
|
||||
"""An ordered set of NamedInt values.
|
||||
|
||||
Indexing can be made by int or string, and will return the corresponding
|
||||
NamedInt if it exists in this set, or `None`.
|
||||
Indexing can be made by int or string, and will return the corresponding
|
||||
NamedInt if it exists in this set, or `None`.
|
||||
|
||||
Extracting slices will return all present NamedInts in the given interval
|
||||
(extended slices are not supported).
|
||||
Extracting slices will return all present NamedInts in the given interval
|
||||
(extended slices are not supported).
|
||||
|
||||
Assigning a string to an indexed int will create a new NamedInt in this set;
|
||||
if the value already exists in the set (int or string), ValueError will be
|
||||
raised.
|
||||
"""
|
||||
__slots__ = ('__dict__', '_values', '_indexed', '_fallback')
|
||||
Assigning a string to an indexed int will create a new NamedInt in this set;
|
||||
if the value already exists in the set (int or string), ValueError will be
|
||||
raised.
|
||||
"""
|
||||
__slots__ = ('__dict__', '_values', '_indexed', '_fallback')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def _readable_name(n):
|
||||
if not is_string(n):
|
||||
raise TypeError("expected (unicode) string, got " + str(type(n)))
|
||||
return n.replace('__', '/').replace('_', ' ')
|
||||
def __init__(self, **kwargs):
|
||||
def _readable_name(n):
|
||||
if not is_string(n):
|
||||
raise TypeError('expected (unicode) string, got ' + str(type(n)))
|
||||
return n.replace('__', '/').replace('_', ' ')
|
||||
|
||||
# print (repr(kwargs))
|
||||
values = {k: NamedInt(v, _readable_name(k)) for (k, v) in kwargs.items()}
|
||||
self.__dict__ = values
|
||||
self._values = sorted(list(values.values()))
|
||||
self._indexed = {int(v): v for v in self._values}
|
||||
# assert len(values) == len(self._indexed), "(%d) %r\n=> (%d) %r" % (len(values), values, len(self._indexed), self._indexed)
|
||||
self._fallback = None
|
||||
# print (repr(kwargs))
|
||||
values = {k: NamedInt(v, _readable_name(k)) for (k, v) in kwargs.items()}
|
||||
self.__dict__ = values
|
||||
self._values = sorted(list(values.values()))
|
||||
self._indexed = {int(v): v for v in self._values}
|
||||
# assert len(values) == len(self._indexed)
|
||||
# "(%d) %r\n=> (%d) %r" % (len(values), values, len(self._indexed), self._indexed)
|
||||
self._fallback = None
|
||||
|
||||
@classmethod
|
||||
def range(cls, from_value, to_value, name_generator=lambda x: str(x), step=1):
|
||||
values = {name_generator(x): x for x in range(from_value, to_value + 1, step)}
|
||||
return NamedInts(**values)
|
||||
@classmethod
|
||||
def list(cls, items, name_generator=lambda x: str(x)):
|
||||
values = {name_generator(x): x for x in items}
|
||||
return NamedInts(**values)
|
||||
|
||||
def flag_names(self, value):
|
||||
unknown_bits = value
|
||||
for k in self._indexed:
|
||||
assert bin(k).count('1') == 1
|
||||
if k & value == k:
|
||||
unknown_bits &= ~k
|
||||
yield str(self._indexed[k])
|
||||
@classmethod
|
||||
def range(cls, from_value, to_value, name_generator=lambda x: str(x), step=1):
|
||||
values = {name_generator(x): x for x in range(from_value, to_value + 1, step)}
|
||||
return NamedInts(**values)
|
||||
|
||||
if unknown_bits:
|
||||
yield 'unknown:%06X' % unknown_bits
|
||||
def flag_names(self, value):
|
||||
unknown_bits = value
|
||||
for k in self._indexed:
|
||||
assert bin(k).count('1') == 1
|
||||
if k & value == k:
|
||||
unknown_bits &= ~k
|
||||
yield str(self._indexed[k])
|
||||
|
||||
def __getitem__(self, index):
|
||||
if isinstance(index, int):
|
||||
if index in self._indexed:
|
||||
return self._indexed[int(index)]
|
||||
if self._fallback and isinstance(index, int):
|
||||
value = NamedInt(index, self._fallback(index))
|
||||
self._indexed[index] = value
|
||||
self._values = sorted(self._values + [value])
|
||||
return value
|
||||
if unknown_bits:
|
||||
yield 'unknown:%06X' % unknown_bits
|
||||
|
||||
elif is_string(index):
|
||||
if index in self.__dict__:
|
||||
return self.__dict__[index]
|
||||
def __getitem__(self, index):
|
||||
if isinstance(index, int):
|
||||
if index in self._indexed:
|
||||
return self._indexed[int(index)]
|
||||
if self._fallback and isinstance(index, int):
|
||||
value = NamedInt(index, self._fallback(index))
|
||||
self._indexed[index] = value
|
||||
self._values = sorted(self._values + [value])
|
||||
return value
|
||||
|
||||
elif isinstance(index, slice):
|
||||
if index.start is None and index.stop is None:
|
||||
return self._values[:]
|
||||
elif is_string(index):
|
||||
if index in self.__dict__:
|
||||
return self.__dict__[index]
|
||||
return (next((x for x in self._values if str(x) == index), None))
|
||||
|
||||
v_start = int(self._values[0]) if index.start is None else int(index.start)
|
||||
v_stop = (self._values[-1] + 1) if index.stop is None else int(index.stop)
|
||||
elif isinstance(index, slice):
|
||||
if index.start is None and index.stop is None:
|
||||
return self._values[:]
|
||||
|
||||
if v_start > v_stop or v_start > self._values[-1] or v_stop <= self._values[0]:
|
||||
return []
|
||||
v_start = int(self._values[0]) if index.start is None else int(index.start)
|
||||
v_stop = (self._values[-1] + 1) if index.stop is None else int(index.stop)
|
||||
|
||||
if v_start <= self._values[0] and v_stop > self._values[-1]:
|
||||
return self._values[:]
|
||||
if v_start > v_stop or v_start > self._values[-1] or v_stop <= self._values[0]:
|
||||
return []
|
||||
|
||||
start_index = 0
|
||||
stop_index = len(self._values)
|
||||
for i, value in enumerate(self._values):
|
||||
if value < v_start:
|
||||
start_index = i + 1
|
||||
elif index.stop is None:
|
||||
break
|
||||
if value >= v_stop:
|
||||
stop_index = i
|
||||
break
|
||||
if v_start <= self._values[0] and v_stop > self._values[-1]:
|
||||
return self._values[:]
|
||||
|
||||
return self._values[start_index:stop_index]
|
||||
start_index = 0
|
||||
stop_index = len(self._values)
|
||||
for i, value in enumerate(self._values):
|
||||
if value < v_start:
|
||||
start_index = i + 1
|
||||
elif index.stop is None:
|
||||
break
|
||||
if value >= v_stop:
|
||||
stop_index = i
|
||||
break
|
||||
|
||||
def __setitem__(self, index, name):
|
||||
assert isinstance(index, int), type(index)
|
||||
if isinstance(name, NamedInt):
|
||||
assert int(index) == int(name), repr(index) + ' ' + repr(name)
|
||||
value = name
|
||||
elif is_string(name):
|
||||
value = NamedInt(index, name)
|
||||
else:
|
||||
raise TypeError('name must be a string')
|
||||
return self._values[start_index:stop_index]
|
||||
|
||||
if str(value) in self.__dict__:
|
||||
raise ValueError('%s (%d) already known' % (value, int(value)))
|
||||
if int(value) in self._indexed:
|
||||
raise ValueError('%d (%s) already known' % (int(value), value))
|
||||
def __setitem__(self, index, name):
|
||||
assert isinstance(index, int), type(index)
|
||||
if isinstance(name, NamedInt):
|
||||
assert int(index) == int(name), repr(index) + ' ' + repr(name)
|
||||
value = name
|
||||
elif is_string(name):
|
||||
value = NamedInt(index, name)
|
||||
else:
|
||||
raise TypeError('name must be a string')
|
||||
|
||||
self._values = sorted(self._values + [value])
|
||||
self.__dict__[str(value)] = value
|
||||
self._indexed[int(value)] = value
|
||||
if str(value) in self.__dict__:
|
||||
raise ValueError('%s (%d) already known' % (value, int(value)))
|
||||
if int(value) in self._indexed:
|
||||
raise ValueError('%d (%s) already known' % (int(value), value))
|
||||
|
||||
def __contains__(self, value):
|
||||
if isinstance(value, int):
|
||||
return value in self._indexed
|
||||
elif is_string(value):
|
||||
return value in self.__dict__
|
||||
self._values = sorted(self._values + [value])
|
||||
self.__dict__[str(value)] = value
|
||||
self._indexed[int(value)] = value
|
||||
|
||||
def __iter__(self):
|
||||
for v in self._values:
|
||||
yield v
|
||||
def __contains__(self, value):
|
||||
if isinstance(value, int):
|
||||
return value in self._indexed
|
||||
elif is_string(value):
|
||||
return value in self.__dict__ or value in self._values
|
||||
|
||||
def __len__(self):
|
||||
return len(self._values)
|
||||
def __iter__(self):
|
||||
for v in self._values:
|
||||
yield v
|
||||
|
||||
def __repr__(self):
|
||||
return 'NamedInts(%s)' % ', '.join(repr(v) for v in self._values)
|
||||
def __len__(self):
|
||||
return len(self._values)
|
||||
|
||||
def __repr__(self):
|
||||
return 'NamedInts(%s)' % ', '.join(repr(v) for v in self._values)
|
||||
|
||||
|
||||
def strhex(x):
|
||||
assert x is not None
|
||||
"""Produce a hex-string representation of a sequence of bytes."""
|
||||
return _hexlify(x).decode('ascii').upper()
|
||||
assert x is not None
|
||||
"""Produce a hex-string representation of a sequence of bytes."""
|
||||
return _hexlify(x).decode('ascii').upper()
|
||||
|
||||
|
||||
def bytes2int(x):
|
||||
"""Convert a bytes string to an int.
|
||||
The bytes are assumed to be in most-significant-first order.
|
||||
"""
|
||||
assert isinstance(x, bytes)
|
||||
assert len(x) < 9
|
||||
qx = (b'\x00' * 8) + x
|
||||
result, = unpack('!Q', qx[-8:])
|
||||
# assert x == int2bytes(result, len(x))
|
||||
return result
|
||||
"""Convert a bytes string to an int.
|
||||
The bytes are assumed to be in most-significant-first order.
|
||||
"""
|
||||
assert isinstance(x, bytes)
|
||||
assert len(x) < 9
|
||||
qx = (b'\x00' * 8) + x
|
||||
result, = unpack('!Q', qx[-8:])
|
||||
# assert x == int2bytes(result, len(x))
|
||||
return result
|
||||
|
||||
|
||||
def int2bytes(x, count=None):
|
||||
"""Convert an int to a bytes representation.
|
||||
The bytes are ordered in most-significant-first order.
|
||||
If 'count' is not given, the necessary number of bytes is computed.
|
||||
"""
|
||||
assert isinstance(x, int)
|
||||
result = pack('!Q', x)
|
||||
assert isinstance(result, bytes)
|
||||
# assert x == bytes2int(result)
|
||||
"""Convert an int to a bytes representation.
|
||||
The bytes are ordered in most-significant-first order.
|
||||
If 'count' is not given, the necessary number of bytes is computed.
|
||||
"""
|
||||
assert isinstance(x, int)
|
||||
result = pack('!Q', x)
|
||||
assert isinstance(result, bytes)
|
||||
# assert x == bytes2int(result)
|
||||
|
||||
if count is None:
|
||||
return result.lstrip(b'\x00')
|
||||
if count is None:
|
||||
return result.lstrip(b'\x00')
|
||||
|
||||
assert isinstance(count, int)
|
||||
assert count > 0
|
||||
assert x.bit_length() <= count * 8
|
||||
return result[-count:]
|
||||
assert isinstance(count, int)
|
||||
assert count > 0
|
||||
assert x.bit_length() <= count * 8
|
||||
return result[-count:]
|
||||
|
||||
|
||||
class KwException(Exception):
|
||||
"""An exception that remembers all arguments passed to the constructor.
|
||||
They can be later accessed by simple member access.
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
super(KwException, self).__init__(kwargs)
|
||||
"""An exception that remembers all arguments passed to the constructor.
|
||||
They can be later accessed by simple member access.
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
super(KwException, self).__init__(kwargs)
|
||||
|
||||
def __getattr__(self, k):
|
||||
try:
|
||||
return super(KwException, self).__getattr__(k)
|
||||
except AttributeError:
|
||||
return self.args[0][k]
|
||||
def __getattr__(self, k):
|
||||
try:
|
||||
return super(KwException, self).__getattr__(k)
|
||||
except AttributeError:
|
||||
return self.args[0][k]
|
||||
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
"""Firmware information."""
|
||||
FirmwareInfo = namedtuple('FirmwareInfo', [
|
||||
'kind',
|
||||
'name',
|
||||
'version',
|
||||
'extras'])
|
||||
FirmwareInfo = namedtuple('FirmwareInfo', ['kind', 'name', 'version', 'extras'])
|
||||
|
||||
"""Reprogrammable keys informations."""
|
||||
ReprogrammableKeyInfo = namedtuple('ReprogrammableKeyInfo', [
|
||||
'index',
|
||||
'key',
|
||||
'task',
|
||||
'flags'])
|
||||
BATTERY_APPROX = NamedInts(empty=0, critical=5, low=20, good=50, full=90)
|
||||
|
||||
del namedtuple
|
||||
|
||||