Skip to content

Commit

Permalink
Merge branch 'master' into code_improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
MattHag authored May 18, 2024
2 parents cbc45c5 + a9ce033 commit 8bc5af0
Show file tree
Hide file tree
Showing 18 changed files with 95 additions and 67 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ as well as many Logitech devices that connect via a USB cable or Bluetooth.
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2+-blue.svg)](../LICENSE.txt)

<p align="center">
<img src="https://pwr-solaar.github.io/Solaar/Solaar-main-window-multiple.png" width="54%"/>
<img src="https://pwr-solaar.github.io/Solaar/screenshots/Solaar-main-window-multiple.png" width="54%"/>
&#160;
<img src="https://pwr-solaar.github.io/Solaar/Solaar-main-window-receiver.png" width="43%"/>
<img src="https://pwr-solaar.github.io/Solaar/screenshots/Solaar-main-window-receiver.png" width="43%"/>
</p>

<p align="center">
<img src="https://pwr-solaar.github.io/Solaar/Solaar-main-window-back-divert.png" width="49%"/>
<img src="https://pwr-solaar.github.io/Solaar/screenshots/Solaar-main-window-back-divert.png" width="49%"/>
&#160;
<img src="https://pwr-solaar.github.io/Solaar/Solaar-rule-editor.png" width="48%"/>
<img src="https://pwr-solaar.github.io/Solaar/screenshots/Solaar-rule-editor.png" width="48%"/>
</p>

Solaar supports:
Expand Down
30 changes: 30 additions & 0 deletions docs/implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,36 @@ TODO: improve the callback mechanism(s) to support the explicit calls of the UI

Solaar has three main components: code mostly about receivers and devices, code for the command line interface, and code for the graphical user interface.

The following graph shows the main components of Solaar and how they interact.
```mermaid
graph TD
subgraph User interface
U[UI]
C[CLI]
end
subgraph Core
U --> S{Solaar}
C --> S
S --> L[Logitech receiver]
L --> R[Receiver]
L --> D[Device]
S --> B[dbus]
end
subgraph Hardware interface
R --> A
D --> A
A[hidapi]--> P[hid parser]
end
subgraph Peripherals
P <-.-> M[Logitech mouse]
P <-.-> K[Logitech keyboard]
end
```


## Receivers and Devices

The code in `logitech_receiver` is responsible for creating and maintaining receiver (`receiver/Receiver`) and device (`device/Device`) objects for each device on the computer that uses the Logitech HID++ protocol. These objects are discovered in Linux by interacting with the Linux `udev` system using code in `hidapi`.
Expand Down
4 changes: 2 additions & 2 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,6 @@ Here is an example showing how to divert the Back Button on an MX Master 3 so th
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.

![Solaar-divert-back](Solaar-main-window-back-divert.png)
![Solaar-divert-back](screenshots/Solaar-main-window-back-divert.png)

![Solaar-rule-back-host](Solaar-rule-editor.png)
![Solaar-rule-back-host](screenshots/Solaar-rule-editor.png)
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
16 changes: 8 additions & 8 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ 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.

![Solaar-menu](Solaar-menu.png)
![Solaar-menu](screenshots/Solaar-menu.png)

Clicking on “Quit” in the Solaar menu terminates the program.
Clicking on “About Solaar” pops up a window with further information about Solaar.
Expand Down Expand Up @@ -64,7 +64,7 @@ To pair with a Bolt receiver you have to type a passcode followed by enter
or click the left and right buttons in the correct sequence followed by
clicking both buttons simultaneously.

![Solaar-main-window-receiver](Solaar-main-window-receiver.png)
![Solaar-main-window-receiver](screenshots/Solaar-main-window-receiver.png)

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
Expand Down Expand Up @@ -93,26 +93,26 @@ You can also see and change the settings of devices.
Changing settings is performed by clicking on buttons,
moving sliders, or selecting from alternatives.

![Solaar-main-window-keyboard](Solaar-main-window-keyboard.png)
![Solaar-main-window-keyboard](screenshots/Solaar-main-window-keyboard.png)

![Solaar-main-window-mouse](Solaar-main-window-mouse.png)
![Solaar-main-window-mouse](screenshots/Solaar-main-window-mouse.png)

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

![Solaar-divert-back](Solaar-main-window-back-divert.png)
![Solaar-divert-back](screenshots/Solaar-main-window-back-divert.png)

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

![Solaar-main-window-offline](Solaar-main-window-offline.png)
![Solaar-main-window-offline](screenshots/Solaar-main-window-offline.png)

If a device is paired with a receiver but directly connected via USB or Bluetooth
the receiver pairing will show up as well as the direct connection.
The device can only be manipulated using the direct connection.

![Solaar-main-window-multiple](Solaar-main-window-multiple.png)
![Solaar-main-window-multiple](screenshots/Solaar-main-window-multiple.png)

#### Remapping key and button actions

Expand All @@ -127,7 +127,7 @@ action is always the one shown first in the list. As with all settings,
Solaar will remember past action settings and restore them on the device
from then on.

![Solaar-main-window-actions](Solaar-main-window-button-actions.png)
![Solaar-main-window-actions](screenshots/Solaar-main-window-button-actions.png)

The names of the keys, buttons, and actions are mostly taken from Logitech
documentation and may not be completely obvious.
Expand Down
4 changes: 2 additions & 2 deletions lib/hidapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""Generic Human Interface Device API."""

import platform as _platform
import platform

if _platform.system() in ("Darwin", "Windows"):
if platform.system() in ("Darwin", "Windows"):
from hidapi.hidapi import close # noqa: F401
from hidapi.hidapi import enumerate # noqa: F401
from hidapi.hidapi import find_paired_node # noqa: F401
Expand Down
5 changes: 3 additions & 2 deletions lib/hidapi/hidapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
Parts of this code are adapted from https://github.com/apmorton/pyhidapi
which is MIT licensed.
"""

import atexit
import ctypes
import logging
import platform as _platform
import platform

from threading import Thread
from time import sleep
Expand Down Expand Up @@ -152,7 +153,7 @@ def as_dict(self):
# Solaar opens the same device more than once which will fail unless we
# allow non-exclusive opening. On windows opening with shared access is
# the default, for macOS we need to set it explicitly.
if _platform.system() == "Darwin":
if platform.system() == "Darwin":
_hidapi.hid_darwin_set_open_exclusive.argtypes = [ctypes.c_int]
_hidapi.hid_darwin_set_open_exclusive.restype = None
_hidapi.hid_darwin_set_open_exclusive(0)
Expand Down
26 changes: 13 additions & 13 deletions lib/hidapi/hidconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@

from binascii import hexlify
from binascii import unhexlify
from select import select as _select
from select import select
from threading import Lock
from threading import Thread

import hidapi as _hid
import hidapi

interactive = os.isatty(0)
prompt = "?? Input: " if interactive else ""
Expand Down Expand Up @@ -86,7 +86,7 @@ def _error(text, scroll=False):
def _continuous_read(handle, timeout=2000):
while True:
try:
reply = _hid.read(handle, 128, timeout)
reply = hidapi.read(handle, 128, timeout)
except OSError as e:
_error("Read failed, aborting: " + str(e), True)
break
Expand All @@ -109,7 +109,7 @@ def _validate_input(line, hidpp=False):
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\x00\x01\x02\x03\x04\x05\x06\x07":
if data[1:2] not in b"\xff\x00\x01\x02\x03\x04\x05\x06\x07":
_error("Invalid HID++ request: second byte must be 0xFF or one of 0x00..0x07")
return None
if data[:1] == b"\x10":
Expand All @@ -135,7 +135,7 @@ def matchfn(bid, vid, pid, _a, _b):

device = args.device
if args.hidpp and not device:
for d in _hid.enumerate(matchfn):
for d in hidapi.enumerate(matchfn):
if d.driver == "logitech-djreceiver":
device = d.path
break
Expand All @@ -145,19 +145,19 @@ def matchfn(bid, vid, pid, _a, _b):
sys.exit("!! Device path required.")

print(".. Opening device", device)
handle = _hid.open_path(device)
handle = hidapi.open_path(device)
if not handle:
sys.exit(f"!! Failed to open {device}, aborting.")
print(
".. Opened handle %r, vendor %r product %r serial %r."
% (handle, _hid.get_manufacturer(handle), _hid.get_product(handle), _hid.get_serial(handle))
% (handle, hidapi.get_manufacturer(handle), hidapi.get_product(handle), hidapi.get_serial(handle))
)
if args.hidpp:
if _hid.get_manufacturer(handle) is not None and _hid.get_manufacturer(handle) != b"Logitech":
if hidapi.get_manufacturer(handle) is not None and hidapi.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):
if hidapi.get_manufacturer(handle) == b"Logitech" and b"Receiver" in hidapi.get_product(handle):
args.hidpp = True
print(".. Logitech receiver detected, HID++ validation enabled.")

Expand Down Expand Up @@ -218,11 +218,11 @@ def main():
continue

_print("<<", data)
_hid.write(handle, data)
hidapi.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":
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:
Expand All @@ -236,7 +236,7 @@ def main():

finally:
print(f".. Closing handle {handle!r}")
_hid.close(handle)
hidapi.close(handle)
if interactive:
readline.write_history_file(args.history)

Expand Down
Loading

0 comments on commit 8bc5af0

Please sign in to comment.