Laser pointer and screen spotlight for Linux on X11, driven by a Logitech Spotlight 2 presenter — including the button's two pressure levels, which no other Linux tool currently supports.
Hold the presenter button softly and a spotlight follows your pointer. Press harder and you get a laser dot. Keyboard shortcuts do the same without the remote.
Projecteur solves this problem well and you should use it if you can. But its current development line is Wayland-only: it requires KDE Plasma 6.7+, Qt 6.10+ and a Wayland session. The Qt5/X11 code lives on a separate legacy branch that predates the Spotlight 2 and does not know its device IDs.
So if you run XFCE, MATE, Cinnamon or GNOME on X11 and own a Spotlight 2, there is currently nothing. That is the gap this fills.
| Device | ID | Tested |
|---|---|---|
| Logitech Spotlight 2 (Bluetooth) | 046d:b506 |
yes |
| Logitech Spotlight 2 (Logi Bolt receiver) | 046d:c548 |
not yet |
| Logitech Spotlight (Bluetooth) | 046d:b503 |
not yet |
| Logitech Spotlight (Unifying receiver) | 046d:c53e |
not yet |
Reports from the untested ones are very welcome.
git clone https://github.com/fmesasc/foco
cd foco
sudo make install
sudo usermod -aG input $USER # if you are not already in it
# log out and back inDependencies (Debian/Ubuntu):
sudo apt install python3-gi python3-evdev python3-pyudev \
gir1.2-gtk-3.0 python3-cairoYou also need a running compositor — the overlay is a translucent window. On XFCE: Settings → Window Manager Tweaks → Compositor.
With the presenter:
| Action | Effect |
|---|---|
| Press the button softly and hold | Spotlight follows the pointer |
| Press hard | Laser dot |
| Release | Effect disappears |
Each pressure level always shows the same effect. This is deliberate: an earlier version cycled through effects on a hard press, and it felt broken because the current mode was invisible state that changed under your fingers.
With the keyboard (bind these to foco --enviar ...):
| Command | Effect |
|---|---|
foco --enviar alternar |
Toggle on/off (stays until toggled) |
foco --enviar siguiente |
Cycle spotlight → laser → both |
foco --enviar mas / menos |
Grow / shrink the spotlight |
foco --enviar apagar |
Turn off |
foco --recuperar-cursor |
Rescue: bring the mouse pointer back |
Options:
foco --modo-suave spotlight --modo-fuerte laser # effect per pressure level
foco --radio 180 # spotlight radius (px)
foco --radio-laser 13 # laser dot radius (px)
foco --oscurecer 0.72 # dim opacity, 0..1
foco --ocultar-cursor laser # laser | siempre | nunca
foco --listar # list input devices
foco -v # explain what it is doing
None of this is documented by Logitech. It was measured against the real device, and it is the part that may be useful to other projects.
Logitech's reprogrammable controls report nothing over HID++ by default.
You must explicitly ask for them with 0x1B04 setCidReporting. Logi Options+
does this on Windows at startup; on Linux nobody did, which is exactly why the
button "does nothing".
| CID | Meaning |
|---|---|
0x00D8 |
First level — soft press |
0x01A8 |
Second level — hard press (the one that vibrates on Windows) |
Both are declared force-sensitive by the device itself. Releasing from
level 2 passes briefly through level 1, so 0x01A8 → 0x00D8 → none is a
single release, not two presses.
- The Spotlight 2 does not accept short HID++ reports. Its descriptor
declares report IDs
0x01, 0x02, 0x03and0x11only. Sending the short0x10report gives you silence, not an error, which is far harder to debug. - Every field has a "valid" bit. Sending flags
0x03sets divert but leavesrawXYuntouched, because you did not validate that field. To turnrawXYoff you must send0x23. - Do not divert
rawXY. If you do, gyro motion stops arriving on the mouse channel and the cursor freezes. Let the kernel keep moving the pointer; you only need the buttons. - The device silently drops commands sent back to back. Ten
setCidReportingin a row: some are applied, some are not, with no error. Space them out.
| Feature | Index | Notes |
|---|---|---|
0x1B04 ReprogrammableKeysV4 |
11 | The buttons |
0x1A01 PresenterHaptic |
17 | Vibration motor |
0x1004 UnifiedBattery |
9 | Battery level |
0x18A1 LEDControl |
27 | hidden/engineering |
0x1814 ChangeHost / 0x1815 HostsInfo |
12 / 13 | Multi-host pairing |
Power. Idle CPU is 0.0%: the process blocks in the GLib loop on the device descriptors, and the kernel wakes it. Reconnection is detected through udev rather than polling, so when the presenter sleeps the program sleeps too. The overlay window is hidden, not destroyed, and repaints are coalesced to at most 60 Hz while visible.
The gyroscope is not decoded. The kernel already delivers it as ordinary mouse motion, so the cursor moves by itself and we just draw where it already is. Much less code and much less to go wrong.
Cursor hiding uses XFixes through ctypes, with no extra dependency. Setting a blank cursor only affects your own windows, and ours is click-through, so the pointer is never "over" it. The hide is re-asserted a few times a second because crossing a window that defines its own cursor brings it back.
Working and in daily use, but young. Untested on anything but XFCE/X11 with a Bluetooth Spotlight 2.
The most useful contributions right now: reports from the other device IDs,
the correct 0x1A01 haptic command (the current one is a guess), and Wayland
support.
MIT. See LICENSE.
Thanks to Projecteur for the device IDs and for solving this properly on Wayland.