Skip to content

v0.1.23

Choose a tag to compare

@github-actions github-actions released this 25 Oct 14:44
· 31 commits to main since this release

Five changes on the Fusion2 line — the X2A, X2H, X2 Wired, Xlite Wired, Xlite v4, X3 and the 1K dongle — and one on the Nordic family. The dongle work was found and measured by @f-o-o-g-s on a Pulsar X3, with the X2A side confirmed here.

The X3's lift-off distance

Register 07/02/03 turns out to have two shapes. On the X2A it is one byte, taken from the second payload position, echoed back at byte 7, and it stores whatever it is given. On the X3 byte 7 is a mode and byte 8 the distance in tenths of a millimetre: 02 07, 02 0a and 02 14 for 0.7, 1 and 2 mm, which is what Fusion2 1.0.5.8 writes for those three settings.

So the app had been reading the mode byte as the value. An X3 on its factory 1 mm (02 0a) showed "2 mm", and choosing "1 mm" wrote 00 00 — a pair no Fusion2 version writes. A coin lift test put 00 00 at or near 1 mm, so nothing was broken by it, but the mouse's own 0.7 mm was unreachable and the page disagreed with the mouse.

The X3 now offers 0.7, 1 and 2 mm on its cable, and through the 1K dongle it follows whichever mouse the dongle says it is carrying — asked of the dongle's pairing memory, which answers in about a millisecond whether the mouse is awake, asleep or on its cable. The dongle's own list is still 1 and 2 mm, because capabilities are read before anything is opened.

Exports now carry version: 3. A version 1 or 2 file restores the mouse's 1 mm rather than a value that was saved under the old reading.

A dongle that will not say which mouse it has reads nothing

The lift-off encoding above depends on that pairing answer, so a read and the write after it have to agree about which mouse is there. The first version of this remembered the answer for the open and assumed the X2A encoding when the dongle would not say — which @f-o-o-g-s then showed was still wrong, because the GUI reads the page in one open and applies in a later one. An X3 holding 02 0a with the page read's ask lost showed 2 mm and the Apply after it wrote 02 14; when only the Apply's ask failed, an untouched 1 mm row wrote 00 00. Both ended on "Settings applied".

It no longer guesses. A refused ask makes lift-off a field that did not come back, which stops the page being applied at all, and an answer is still remembered for the rest of the open. In 64 asks in his logs and 421 more in the resend runs, every one was answered, inside half a millisecond.

The page knows when it is only part-read

v0.1.21 stopped Apply writing widgets that had never been read. That guarded a window's first read and nothing after it, so a later pass that lost a field kept whatever the pass before it left — which on a profile switch is another profile's values, with no banner, because the warning was suppressed once the page had been read. Measured over the window's own methods: profile 1 read in full, then profile 2 with its lift-off lost, and Apply wrote 1 mm into profile 2 — over the 0.7 mm that was there, on an X3 — with nothing said. With the DPI read lost instead, Apply wrote profile 1's stage list into profile 2.

Now a pass that loses anything Apply writes leaves the page refusing to apply until a reload answers in full, and says so. A value the driver cannot name counts as lost in the same way: an X3 set to 0.7 mm and read through the dongle, which can only name 1 and 2, used to show 1 mm and write it back.

A request the dongle let go is sent again

With the mouse moving, the 1K dongle sometimes shows a request pending and then goes back to an answer it had already given, and never answers the request: 216 of 5155 exchanges on an X3, up to 147 ms in, none of the 67 watched for a second or more ever answered. The same frame sent again was answered every time, 150 of 211 on the first poll after it. None of 2295 exchanges with the mouse still was lost, and a 10 or 30 ms gap before each request did not prevent it.

_transact() now sends the frame again once the reply buffer has shown such an answer for five polls in a row — any answer in place of ours after the dongle showed ours pending, or the previous request's answer. An answer that was there before ours showed belongs to another program and still runs to the deadline. Two resends per request, writes included, none in the last 350 ms before the deadline.

Button and stage-colour answers now also have to echo byte 7, the button or stage they are for. Six of each go out back to back with the same category and register, so a lost one that went back to the previous one's answer passed for ours: three button reads came back carrying the previous button's binding. Confirmed on both ends of the family — 1047 button and 240 stage-colour answers through the dongle, and every button and every stage colour on a cabled X2A.

The Xlite V3 is named in the driver it was already sharing

3554:f507 and 3554:f508 are the X2A Wireless's two PIDs and the Xlite V3's, and @bluesceada found that Pulsar's own Cfg.ini differs between the two models in the key matrix alone. One driver has served both all along — the keyboard-shortcut writes in it were verified on an Xlite V3 — and it now says so instead of telling half its users they own an X2A.

Nothing on the bus tells the two apart, so the right-hand thumb pair it offers is still the X2A's. On a six-button mouse of the family those two slots hold something else, so leave Thumb 3 and Thumb 4 alone there; #30 has the detail and the read that would let the driver decide for itself.


Also in this release: docs/protocol-fusion2-macros.md records the Fusion2 macro protocol — the category 0x06 storage commands, the 14-byte macro format and the three binding types — from a capture by @f-o-o-g-s. Macros recorded in Fusion2 already work on Linux: the mouse plays them itself, as a keyboard report.

The dongle work was found and measured by @f-o-o-g-s on a Pulsar X3, on its cable and behind the 1K dongle, with the X2A measurements here. Full attribution is in the README.