Clean up imports (#2537)
* Remove import as _ in solaar startup Related #2273 * Remove import as _ in listener Related #2273 * Remove import as _ in cli init Related #2273 * Remove import as _ in gtk Related #2273 * Remove import as _ in show Related #2273 * Remove import as _ in tray Related #2273 * Remove import as _ in profiles Related #2273 * Remove import as _ in config Related #2273 * Remove import as _ in config panel Related #2273 * Remove import as _ in window Related #2273 * Remove import as _ in pair Related #2273 * Remove import as _ in pair window Related #2273 * Remove import as _ in cli package Related #2273 * Remove import as _ in ui package Related #2273 * Remove commented out code Related #2273 * Use constant for Logitech ID
This commit is contained in:
parent
d9d67ed738
commit
67829c5807
18
bin/solaar
18
bin/solaar
|
|
@ -21,10 +21,9 @@
|
||||||
|
|
||||||
def init_paths():
|
def init_paths():
|
||||||
"""Make the app work in the source tree."""
|
"""Make the app work in the source tree."""
|
||||||
import os.path as _path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# Python 2 need conversion from utf-8 filenames
|
|
||||||
# Python 3 might have problems converting back to UTF-8 in case of Unicode surrogates
|
# Python 3 might have problems converting back to UTF-8 in case of Unicode surrogates
|
||||||
try:
|
try:
|
||||||
decoded_path = sys.path[0]
|
decoded_path = sys.path[0]
|
||||||
|
|
@ -33,18 +32,17 @@ def init_paths():
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
"ERROR: Solaar cannot recognize encoding of filesystem path, "
|
"ERROR: Solaar cannot recognize encoding of filesystem path, "
|
||||||
"this may happen because non UTF-8 characters in the pathname.\n"
|
"this may happen due to non UTF-8 characters in the pathname.\n"
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
prefix = _path.normpath(_path.join(_path.realpath(decoded_path), ".."))
|
root = os.path.join(os.path.realpath(decoded_path), "..")
|
||||||
src_lib = _path.join(prefix, "lib")
|
prefix = os.path.normpath(root)
|
||||||
share_lib = _path.join(prefix, "share", "solaar", "lib")
|
src_lib = os.path.join(prefix, "lib")
|
||||||
|
share_lib = os.path.join(prefix, "share", "solaar", "lib")
|
||||||
for location in src_lib, share_lib:
|
for location in src_lib, share_lib:
|
||||||
init_py = _path.join(location, "solaar", "__init__.py")
|
init_py = os.path.join(location, "solaar", "__init__.py")
|
||||||
# print ("sys.path[0]: checking", init_py)
|
if os.path.exists(init_py):
|
||||||
if _path.exists(init_py):
|
|
||||||
# print ("sys.path[0]: found", location, "replacing", sys.path[0])
|
|
||||||
sys.path[0] = location
|
sys.path[0] = location
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ from threading import Thread
|
||||||
|
|
||||||
import hidapi
|
import hidapi
|
||||||
|
|
||||||
|
LOGITECH_VENDOR_ID = 0x046D
|
||||||
|
|
||||||
interactive = os.isatty(0)
|
interactive = os.isatty(0)
|
||||||
prompt = "?? Input: " if interactive else ""
|
prompt = "?? Input: " if interactive else ""
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
@ -126,8 +128,8 @@ def _validate_input(line, hidpp=False):
|
||||||
|
|
||||||
def _open(args):
|
def _open(args):
|
||||||
def matchfn(bid, vid, pid, _a, _b):
|
def matchfn(bid, vid, pid, _a, _b):
|
||||||
if vid == 0x046D:
|
if vid == LOGITECH_VENDOR_ID:
|
||||||
return {"vid": 0x046D}
|
return {"vid": vid}
|
||||||
|
|
||||||
device = args.device
|
device = args.device
|
||||||
if args.hidpp and not device:
|
if args.hidpp and not device:
|
||||||
|
|
|
||||||
|
|
@ -153,8 +153,6 @@ def _match(action, device, filterfn):
|
||||||
return d_info
|
return d_info
|
||||||
|
|
||||||
elif action == "remove":
|
elif action == "remove":
|
||||||
# print (dict(device), dict(usb_device))
|
|
||||||
|
|
||||||
d_info = DeviceInfo(
|
d_info = DeviceInfo(
|
||||||
path=device.device_node,
|
path=device.device_node,
|
||||||
bus_id=None,
|
bus_id=None,
|
||||||
|
|
@ -217,16 +215,6 @@ def find_paired_node_wpid(receiver_path, index):
|
||||||
|
|
||||||
def monitor_glib(callback, filterfn):
|
def monitor_glib(callback, filterfn):
|
||||||
c = pyudev.Context()
|
c = pyudev.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 = pyudev.Monitor.from_netlink(c)
|
m = pyudev.Monitor.from_netlink(c)
|
||||||
m.filter_by(subsystem="hidraw")
|
m.filter_by(subsystem="hidraw")
|
||||||
|
|
||||||
|
|
@ -248,15 +236,12 @@ def monitor_glib(callback, filterfn):
|
||||||
try:
|
try:
|
||||||
# io_add_watch_full may not be available...
|
# 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)
|
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:
|
except AttributeError:
|
||||||
try:
|
try:
|
||||||
# and the priority parameter appeared later in the API
|
# 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)
|
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:
|
except Exception:
|
||||||
GLib.io_add_watch(m, GLib.IO_IN, _process_udev_event, callback, filterfn)
|
GLib.io_add_watch(m, GLib.IO_IN, _process_udev_event, callback, filterfn)
|
||||||
# print ("did io_add_watch")
|
|
||||||
|
|
||||||
if logger.isEnabledFor(logging.DEBUG):
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
logger.debug("Starting dbus monitoring")
|
logger.debug("Starting dbus monitoring")
|
||||||
|
|
|
||||||
|
|
@ -14,20 +14,18 @@
|
||||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
import pkgutil as _pkgutil
|
import pkgutil
|
||||||
import subprocess as _subprocess
|
import subprocess
|
||||||
import sys as _sys
|
import sys
|
||||||
|
|
||||||
NAME = "Solaar"
|
NAME = "Solaar"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
__version__ = (
|
__version__ = (
|
||||||
_subprocess.check_output(["git", "describe", "--always"], cwd=_sys.path[0], stderr=_subprocess.DEVNULL)
|
subprocess.check_output(["git", "describe", "--always"], cwd=sys.path[0], stderr=subprocess.DEVNULL).strip().decode()
|
||||||
.strip()
|
|
||||||
.decode()
|
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
try:
|
try:
|
||||||
__version__ = _pkgutil.get_data("solaar", "commit").strip().decode()
|
__version__ = pkgutil.get_data("solaar", "commit").strip().decode()
|
||||||
except Exception:
|
except Exception:
|
||||||
__version__ = _pkgutil.get_data("solaar", "version").strip().decode()
|
__version__ = pkgutil.get_data("solaar", "version").strip().decode()
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,17 @@
|
||||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
import argparse as _argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import sys as _sys
|
import sys
|
||||||
|
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from traceback import extract_tb
|
from traceback import extract_tb
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
|
|
||||||
import logitech_receiver.device as _device
|
|
||||||
import logitech_receiver.receiver as _receiver
|
|
||||||
|
|
||||||
from logitech_receiver import base
|
from logitech_receiver import base
|
||||||
|
from logitech_receiver import device
|
||||||
|
from logitech_receiver import receiver
|
||||||
|
|
||||||
from solaar import NAME
|
from solaar import NAME
|
||||||
|
|
||||||
|
|
@ -33,7 +32,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _create_parser():
|
def _create_parser():
|
||||||
parser = _argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
prog=NAME.lower(), add_help=False, epilog=f"For details on individual actions, run `{NAME.lower()} <action> --help`."
|
prog=NAME.lower(), add_help=False, epilog=f"For details on individual actions, run `{NAME.lower()} <action> --help`."
|
||||||
)
|
)
|
||||||
subparsers = parser.add_subparsers(title="actions", help="optional action to perform")
|
subparsers = parser.add_subparsers(title="actions", help="optional action to perform")
|
||||||
|
|
@ -107,14 +106,14 @@ def _receivers(dev_path=None):
|
||||||
if dev_path is not None and dev_path != dev_info.path:
|
if dev_path is not None and dev_path != dev_info.path:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
r = _receiver.ReceiverFactory.create_receiver(dev_info)
|
r = receiver.ReceiverFactory.create_receiver(dev_info)
|
||||||
if logger.isEnabledFor(logging.DEBUG):
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
logger.debug("[%s] => %s", dev_info.path, r)
|
logger.debug("[%s] => %s", dev_info.path, r)
|
||||||
if r:
|
if r:
|
||||||
yield r
|
yield r
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("opening " + str(dev_info))
|
logger.exception("opening " + str(dev_info))
|
||||||
_sys.exit(f"{NAME.lower()}: error: {str(e)}")
|
sys.exit(f"{NAME.lower()}: error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
def _receivers_and_devices(dev_path=None):
|
def _receivers_and_devices(dev_path=None):
|
||||||
|
|
@ -123,9 +122,9 @@ def _receivers_and_devices(dev_path=None):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
if dev_info.isDevice:
|
if dev_info.isDevice:
|
||||||
d = _device.DeviceFactory.create_device(base, dev_info)
|
d = device.DeviceFactory.create_device(base, dev_info)
|
||||||
else:
|
else:
|
||||||
d = _receiver.ReceiverFactory.create_receiver(dev_info)
|
d = receiver.ReceiverFactory.create_receiver(dev_info)
|
||||||
|
|
||||||
if logger.isEnabledFor(logging.DEBUG):
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
logger.debug("[%s] => %s", dev_info.path, d)
|
logger.debug("[%s] => %s", dev_info.path, d)
|
||||||
|
|
@ -133,7 +132,7 @@ def _receivers_and_devices(dev_path=None):
|
||||||
yield d
|
yield d
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("opening " + str(dev_info))
|
logger.exception("opening " + str(dev_info))
|
||||||
_sys.exit(f"{NAME.lower()}: error: {str(e)}")
|
sys.exit(f"{NAME.lower()}: error: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
def _find_receiver(receivers, name):
|
def _find_receiver(receivers, name):
|
||||||
|
|
@ -185,9 +184,6 @@ def _find_device(receivers, name):
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
# raise Exception("no device found matching '%s'" % name)
|
|
||||||
|
|
||||||
|
|
||||||
def run(cli_args=None, hidraw_path=None):
|
def run(cli_args=None, hidraw_path=None):
|
||||||
if cli_args:
|
if cli_args:
|
||||||
action = cli_args[0]
|
action = cli_args[0]
|
||||||
|
|
@ -197,9 +193,9 @@ def run(cli_args=None, hidraw_path=None):
|
||||||
# Python 3 has an undocumented 'feature' that breaks parsing empty args
|
# Python 3 has an undocumented 'feature' that breaks parsing empty args
|
||||||
# http://bugs.python.org/issue16308
|
# http://bugs.python.org/issue16308
|
||||||
if "cmd" not in args:
|
if "cmd" not in args:
|
||||||
_cli_parser.print_usage(_sys.stderr)
|
_cli_parser.print_usage(sys.stderr)
|
||||||
_sys.stderr.write(f"{NAME.lower()}: error: too few arguments\n")
|
sys.stderr.write(f"{NAME.lower()}: error: too few arguments\n")
|
||||||
_sys.exit(2)
|
sys.exit(2)
|
||||||
action = args.action
|
action = args.action
|
||||||
assert action in actions
|
assert action in actions
|
||||||
|
|
||||||
|
|
@ -215,7 +211,7 @@ def run(cli_args=None, hidraw_path=None):
|
||||||
m = import_module("." + action, package=__name__)
|
m = import_module("." + action, package=__name__)
|
||||||
m.run(c, args, _find_receiver, _find_device)
|
m.run(c, args, _find_receiver, _find_device)
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
tb_last = extract_tb(_sys.exc_info()[2])[-1]
|
tb_last = extract_tb(sys.exc_info()[2])[-1]
|
||||||
_sys.exit(f"{NAME.lower()}: assertion failed: {tb_last[0]} line {int(tb_last[1])}")
|
sys.exit(f"{NAME.lower()}: assertion failed: {tb_last[0]} line {int(tb_last[1])}")
|
||||||
except Exception:
|
except Exception:
|
||||||
_sys.exit(f"{NAME.lower()}: error: {format_exc()}")
|
sys.exit(f"{NAME.lower()}: error: {format_exc()}")
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,13 @@
|
||||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
import yaml as _yaml
|
import yaml
|
||||||
|
|
||||||
from logitech_receiver import settings as _settings
|
from logitech_receiver import settings
|
||||||
from logitech_receiver import settings_templates as _settings_templates
|
from logitech_receiver import settings_templates
|
||||||
from logitech_receiver.common import NamedInts as _NamedInts
|
from logitech_receiver.common import NamedInts
|
||||||
|
|
||||||
from solaar import configuration as _configuration
|
from solaar import configuration
|
||||||
|
|
||||||
|
|
||||||
def _print_setting(s, verbose=True):
|
def _print_setting(s, verbose=True):
|
||||||
|
|
@ -28,9 +28,9 @@ def _print_setting(s, verbose=True):
|
||||||
if verbose:
|
if verbose:
|
||||||
if s.description:
|
if s.description:
|
||||||
print("#", s.description.replace("\n", " "))
|
print("#", s.description.replace("\n", " "))
|
||||||
if s.kind == _settings.KIND.toggle:
|
if s.kind == settings.KIND.toggle:
|
||||||
print("# possible values: on/true/t/yes/y/1 or off/false/f/no/n/0 or Toggle/~")
|
print("# possible values: on/true/t/yes/y/1 or off/false/f/no/n/0 or Toggle/~")
|
||||||
elif s.kind == _settings.KIND.choice:
|
elif s.kind == settings.KIND.choice:
|
||||||
print(
|
print(
|
||||||
"# possible values: one of [",
|
"# possible values: one of [",
|
||||||
", ".join(str(v) for v in s.choices),
|
", ".join(str(v) for v in s.choices),
|
||||||
|
|
@ -51,7 +51,7 @@ def _print_setting_keyed(s, key, verbose=True):
|
||||||
if verbose:
|
if verbose:
|
||||||
if s.description:
|
if s.description:
|
||||||
print("#", s.description.replace("\n", " "))
|
print("#", s.description.replace("\n", " "))
|
||||||
if s.kind == _settings.KIND.multiple_toggle:
|
if s.kind == settings.KIND.multiple_toggle:
|
||||||
k = next((k for k in s._labels if key == k), None)
|
k = next((k for k in s._labels if key == k), None)
|
||||||
if k is None:
|
if k is None:
|
||||||
print(s.name, "=? (key not found)")
|
print(s.name, "=? (key not found)")
|
||||||
|
|
@ -62,7 +62,7 @@ def _print_setting_keyed(s, key, verbose=True):
|
||||||
print(s.name, "= ? (failed to read from device)")
|
print(s.name, "= ? (failed to read from device)")
|
||||||
else:
|
else:
|
||||||
print(s.name, s.val_to_string({k: value[str(int(k))]}))
|
print(s.name, s.val_to_string({k: value[str(int(k))]}))
|
||||||
elif s.kind == _settings.KIND.map_choice:
|
elif s.kind == settings.KIND.map_choice:
|
||||||
k = next((k for k in s.choices.keys() if key == k), None)
|
k = next((k for k in s.choices.keys() if key == k), None)
|
||||||
if k is None:
|
if k is None:
|
||||||
print(s.name, "=? (key not found)")
|
print(s.name, "=? (key not found)")
|
||||||
|
|
@ -158,8 +158,7 @@ def run(receivers, args, find_receiver, find_device):
|
||||||
if not args.setting: # print all settings, so first set them all up
|
if not args.setting: # print all settings, so first set them all up
|
||||||
if not dev.settings:
|
if not dev.settings:
|
||||||
raise Exception(f"no settings for {dev.name}")
|
raise Exception(f"no settings for {dev.name}")
|
||||||
_configuration.attach_to(dev)
|
configuration.attach_to(dev)
|
||||||
# _settings.apply_all_settings(dev)
|
|
||||||
print(dev.name, f"({dev.codename}) [{dev.wpid}:{dev.serial}]")
|
print(dev.name, f"({dev.codename}) [{dev.wpid}:{dev.serial}]")
|
||||||
for s in dev.settings:
|
for s in dev.settings:
|
||||||
print("")
|
print("")
|
||||||
|
|
@ -167,7 +166,7 @@ def run(receivers, args, find_receiver, find_device):
|
||||||
return
|
return
|
||||||
|
|
||||||
setting_name = args.setting.lower()
|
setting_name = args.setting.lower()
|
||||||
setting = _settings_templates.check_feature_setting(dev, setting_name)
|
setting = settings_templates.check_feature_setting(dev, setting_name)
|
||||||
if not setting and dev.descriptor and dev.descriptor.settings:
|
if not setting and dev.descriptor and dev.descriptor.settings:
|
||||||
for sclass in dev.descriptor.settings:
|
for sclass in dev.descriptor.settings:
|
||||||
if sclass.register and sclass.name == setting_name:
|
if sclass.register and sclass.name == setting_name:
|
||||||
|
|
@ -179,7 +178,6 @@ def run(receivers, args, find_receiver, find_device):
|
||||||
raise Exception(f"no setting '{args.setting}' for {dev.name}")
|
raise Exception(f"no setting '{args.setting}' for {dev.name}")
|
||||||
|
|
||||||
if args.value_key is None:
|
if args.value_key is None:
|
||||||
# setting.apply()
|
|
||||||
_print_setting(setting)
|
_print_setting(setting)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -210,32 +208,32 @@ def run(receivers, args, find_receiver, find_device):
|
||||||
if remote:
|
if remote:
|
||||||
argl = ["config", dev.serial or dev.unitId, setting.name]
|
argl = ["config", dev.serial or dev.unitId, setting.name]
|
||||||
argl.extend([a for a in [args.value_key, args.extra_subkey, args.extra2] if a is not None])
|
argl.extend([a for a in [args.value_key, args.extra_subkey, args.extra2] if a is not None])
|
||||||
application.run(_yaml.safe_dump(argl))
|
application.run(yaml.safe_dump(argl))
|
||||||
else:
|
else:
|
||||||
if dev.persister and setting.persist:
|
if dev.persister and setting.persist:
|
||||||
dev.persister[setting.name] = setting._value
|
dev.persister[setting.name] = setting._value
|
||||||
|
|
||||||
|
|
||||||
def set(dev, setting, args, save):
|
def set(dev, setting, args, save):
|
||||||
if setting.kind == _settings.KIND.toggle:
|
if setting.kind == settings.KIND.toggle:
|
||||||
value = select_toggle(args.value_key, setting)
|
value = select_toggle(args.value_key, setting)
|
||||||
args.value_key = value
|
args.value_key = value
|
||||||
message = f"Setting {setting.name} of {dev.name} to {value}"
|
message = f"Setting {setting.name} of {dev.name} to {value}"
|
||||||
result = setting.write(value, save=save)
|
result = setting.write(value, save=save)
|
||||||
|
|
||||||
elif setting.kind == _settings.KIND.range:
|
elif setting.kind == settings.KIND.range:
|
||||||
value = select_range(args.value_key, setting)
|
value = select_range(args.value_key, setting)
|
||||||
args.value_key = value
|
args.value_key = value
|
||||||
message = f"Setting {setting.name} of {dev.name} to {value}"
|
message = f"Setting {setting.name} of {dev.name} to {value}"
|
||||||
result = setting.write(value, save=save)
|
result = setting.write(value, save=save)
|
||||||
|
|
||||||
elif setting.kind == _settings.KIND.choice:
|
elif setting.kind == settings.KIND.choice:
|
||||||
value = select_choice(args.value_key, setting.choices, setting, None)
|
value = select_choice(args.value_key, setting.choices, setting, None)
|
||||||
args.value_key = int(value)
|
args.value_key = int(value)
|
||||||
message = f"Setting {setting.name} of {dev.name} to {value}"
|
message = f"Setting {setting.name} of {dev.name} to {value}"
|
||||||
result = setting.write(value, save=save)
|
result = setting.write(value, save=save)
|
||||||
|
|
||||||
elif setting.kind == _settings.KIND.map_choice:
|
elif setting.kind == settings.KIND.map_choice:
|
||||||
if args.extra_subkey is None:
|
if args.extra_subkey is None:
|
||||||
_print_setting_keyed(setting, args.value_key)
|
_print_setting_keyed(setting, args.value_key)
|
||||||
return (None, None, None)
|
return (None, None, None)
|
||||||
|
|
@ -253,13 +251,13 @@ def set(dev, setting, args, save):
|
||||||
message = f"Setting {setting.name} of {dev.name} key {k!r} to {value!r}"
|
message = f"Setting {setting.name} of {dev.name} key {k!r} to {value!r}"
|
||||||
result = setting.write_key_value(int(k), value, save=save)
|
result = setting.write_key_value(int(k), value, save=save)
|
||||||
|
|
||||||
elif setting.kind == _settings.KIND.multiple_toggle:
|
elif setting.kind == settings.KIND.multiple_toggle:
|
||||||
if args.extra_subkey is None:
|
if args.extra_subkey is None:
|
||||||
_print_setting_keyed(setting, args.value_key)
|
_print_setting_keyed(setting, args.value_key)
|
||||||
return (None, None, None)
|
return (None, None, None)
|
||||||
key = args.value_key
|
key = args.value_key
|
||||||
all_keys = getattr(setting, "choices_universe", None)
|
all_keys = getattr(setting, "choices_universe", None)
|
||||||
ikey = all_keys[int(key) if key.isdigit() else key] if isinstance(all_keys, _NamedInts) else to_int(key)
|
ikey = all_keys[int(key) if key.isdigit() else key] if isinstance(all_keys, NamedInts) else to_int(key)
|
||||||
k = next((k for k in setting._labels if key == k), None)
|
k = next((k for k in setting._labels if key == k), None)
|
||||||
if k is None and ikey is not None:
|
if k is None and ikey is not None:
|
||||||
k = next((k for k in setting._labels if ikey == k), None)
|
k = next((k for k in setting._labels if ikey == k), None)
|
||||||
|
|
@ -272,12 +270,12 @@ def set(dev, setting, args, save):
|
||||||
message = f"Setting {setting.name} key {k!r} to {value!r}"
|
message = f"Setting {setting.name} key {k!r} to {value!r}"
|
||||||
result = setting.write_key_value(str(int(k)), value, save=save)
|
result = setting.write_key_value(str(int(k)), value, save=save)
|
||||||
|
|
||||||
elif setting.kind == _settings.KIND.multiple_range:
|
elif setting.kind == settings.KIND.multiple_range:
|
||||||
if args.extra_subkey is None:
|
if args.extra_subkey is None:
|
||||||
raise Exception(f"{setting.name}: setting needs both key and value to set")
|
raise Exception(f"{setting.name}: setting needs both key and value to set")
|
||||||
key = args.value_key
|
key = args.value_key
|
||||||
all_keys = getattr(setting, "choices_universe", None)
|
all_keys = getattr(setting, "choices_universe", None)
|
||||||
ikey = all_keys[int(key) if key.isdigit() else key] if isinstance(all_keys, _NamedInts) else to_int(key)
|
ikey = all_keys[int(key) if key.isdigit() else key] if isinstance(all_keys, NamedInts) else to_int(key)
|
||||||
if args.extra2 is None or to_int(args.extra2) is None:
|
if args.extra2 is None or to_int(args.extra2) is None:
|
||||||
raise Exception(f"{setting.name}: setting needs an integer value, not {args.extra2}")
|
raise Exception(f"{setting.name}: setting needs an integer value, not {args.extra2}")
|
||||||
if not setting._value: # ensure that there are values to look through
|
if not setting._value: # ensure that there are values to look through
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@
|
||||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
from time import time as _timestamp
|
from time import time
|
||||||
|
|
||||||
from logitech_receiver import base as _base
|
from logitech_receiver import base
|
||||||
from logitech_receiver import hidpp10
|
from logitech_receiver import hidpp10
|
||||||
from logitech_receiver import hidpp10_constants as _hidpp10_constants
|
from logitech_receiver import hidpp10_constants
|
||||||
from logitech_receiver import notifications as _notifications
|
from logitech_receiver import notifications
|
||||||
|
|
||||||
_hidpp10 = hidpp10.Hidpp10()
|
_hidpp10 = hidpp10.Hidpp10()
|
||||||
|
|
||||||
|
|
@ -39,8 +39,8 @@ def run(receivers, args, find_receiver, _ignore):
|
||||||
|
|
||||||
# check if it's necessary to set the notification flags
|
# check if it's necessary to set the notification flags
|
||||||
old_notification_flags = _hidpp10.get_notification_flags(receiver) or 0
|
old_notification_flags = _hidpp10.get_notification_flags(receiver) or 0
|
||||||
if not (old_notification_flags & _hidpp10_constants.NOTIFICATION_FLAG.wireless):
|
if not (old_notification_flags & hidpp10_constants.NOTIFICATION_FLAG.wireless):
|
||||||
_hidpp10.set_notification_flags(receiver, old_notification_flags | _hidpp10_constants.NOTIFICATION_FLAG.wireless)
|
_hidpp10.set_notification_flags(receiver, old_notification_flags | hidpp10_constants.NOTIFICATION_FLAG.wireless)
|
||||||
|
|
||||||
# get all current devices
|
# get all current devices
|
||||||
known_devices = [dev.number for dev in receiver]
|
known_devices = [dev.number for dev in receiver]
|
||||||
|
|
@ -50,8 +50,8 @@ def run(receivers, args, find_receiver, _ignore):
|
||||||
nonlocal known_devices
|
nonlocal known_devices
|
||||||
assert n
|
assert n
|
||||||
if n.devnumber == 0xFF:
|
if n.devnumber == 0xFF:
|
||||||
_notifications.process(receiver, n)
|
notifications.process(receiver, n)
|
||||||
elif n.sub_id == 0x41 and len(n.data) == _base._SHORT_MESSAGE_SIZE - 4:
|
elif n.sub_id == 0x41 and len(n.data) == base._SHORT_MESSAGE_SIZE - 4:
|
||||||
kd, known_devices = known_devices, None # only process one connection notification
|
kd, known_devices = known_devices, None # only process one connection notification
|
||||||
if kd is not None:
|
if kd is not None:
|
||||||
if n.devnumber not in kd:
|
if n.devnumber not in kd:
|
||||||
|
|
@ -66,13 +66,13 @@ def run(receivers, args, find_receiver, _ignore):
|
||||||
if receiver.receiver_kind == "bolt": # Bolt receivers require authentication to pair a device
|
if receiver.receiver_kind == "bolt": # Bolt receivers require authentication to pair a device
|
||||||
receiver.discover(timeout=timeout)
|
receiver.discover(timeout=timeout)
|
||||||
print("Bolt Pairing: long-press the pairing key or button on your device (timing out in", timeout, "seconds).")
|
print("Bolt Pairing: long-press the pairing key or button on your device (timing out in", timeout, "seconds).")
|
||||||
pairing_start = _timestamp()
|
pairing_start = time()
|
||||||
patience = 5 # the discovering notification may come slightly later, so be patient
|
patience = 5 # the discovering notification may come slightly later, so be patient
|
||||||
while receiver.pairing.discovering or _timestamp() - pairing_start < patience:
|
while receiver.pairing.discovering or time() - pairing_start < patience:
|
||||||
if receiver.pairing.device_address and receiver.pairing.device_authentication and receiver.pairing.device_name:
|
if receiver.pairing.device_address and receiver.pairing.device_authentication and receiver.pairing.device_name:
|
||||||
break
|
break
|
||||||
n = _base.read(receiver.handle)
|
n = base.read(receiver.handle)
|
||||||
n = _base.make_notification(*n) if n else None
|
n = base.make_notification(*n) if n else None
|
||||||
if n:
|
if n:
|
||||||
receiver.handle.notifications_hook(n)
|
receiver.handle.notifications_hook(n)
|
||||||
address = receiver.pairing.device_address
|
address = receiver.pairing.device_address
|
||||||
|
|
@ -83,15 +83,15 @@ def run(receivers, args, find_receiver, _ignore):
|
||||||
receiver.pair_device(
|
receiver.pair_device(
|
||||||
address=address,
|
address=address,
|
||||||
authentication=authentication,
|
authentication=authentication,
|
||||||
entropy=20 if kind == _hidpp10_constants.DEVICE_KIND.keyboard else 10,
|
entropy=20 if kind == hidpp10_constants.DEVICE_KIND.keyboard else 10,
|
||||||
)
|
)
|
||||||
pairing_start = _timestamp()
|
pairing_start = time()
|
||||||
patience = 5 # the discovering notification may come slightly later, so be patient
|
patience = 5 # the discovering notification may come slightly later, so be patient
|
||||||
while receiver.pairing.lock_open or _timestamp() - pairing_start < patience:
|
while receiver.pairing.lock_open or time() - pairing_start < patience:
|
||||||
if receiver.pairing.device_passkey:
|
if receiver.pairing.device_passkey:
|
||||||
break
|
break
|
||||||
n = _base.read(receiver.handle)
|
n = base.read(receiver.handle)
|
||||||
n = _base.make_notification(*n) if n else None
|
n = base.make_notification(*n) if n else None
|
||||||
if n:
|
if n:
|
||||||
receiver.handle.notifications_hook(n)
|
receiver.handle.notifications_hook(n)
|
||||||
if authentication & 0x01:
|
if authentication & 0x01:
|
||||||
|
|
@ -102,24 +102,24 @@ def run(receivers, args, find_receiver, _ignore):
|
||||||
print(f"Bolt Pairing: press {passkey}")
|
print(f"Bolt Pairing: press {passkey}")
|
||||||
print("and then press left and right buttons simultaneously")
|
print("and then press left and right buttons simultaneously")
|
||||||
while receiver.pairing.lock_open:
|
while receiver.pairing.lock_open:
|
||||||
n = _base.read(receiver.handle)
|
n = base.read(receiver.handle)
|
||||||
n = _base.make_notification(*n) if n else None
|
n = base.make_notification(*n) if n else None
|
||||||
if n:
|
if n:
|
||||||
receiver.handle.notifications_hook(n)
|
receiver.handle.notifications_hook(n)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
receiver.set_lock(False, timeout=timeout)
|
receiver.set_lock(False, timeout=timeout)
|
||||||
print("Pairing: turn your new device on (timing out in", timeout, "seconds).")
|
print("Pairing: turn your new device on (timing out in", timeout, "seconds).")
|
||||||
pairing_start = _timestamp()
|
pairing_start = time()
|
||||||
patience = 5 # the lock-open notification may come slightly later, wait for it a bit
|
patience = 5 # the lock-open notification may come slightly later, wait for it a bit
|
||||||
while receiver.pairing.lock_open or _timestamp() - pairing_start < patience:
|
while receiver.pairing.lock_open or time() - pairing_start < patience:
|
||||||
n = _base.read(receiver.handle)
|
n = base.read(receiver.handle)
|
||||||
if n:
|
if n:
|
||||||
n = _base.make_notification(*n)
|
n = base.make_notification(*n)
|
||||||
if n:
|
if n:
|
||||||
receiver.handle.notifications_hook(n)
|
receiver.handle.notifications_hook(n)
|
||||||
|
|
||||||
if not (old_notification_flags & _hidpp10_constants.NOTIFICATION_FLAG.wireless):
|
if not (old_notification_flags & hidpp10_constants.NOTIFICATION_FLAG.wireless):
|
||||||
# only clear the flags if they weren't set before, otherwise a
|
# only clear the flags if they weren't set before, otherwise a
|
||||||
# concurrently running Solaar app might stop working properly
|
# concurrently running Solaar app might stop working properly
|
||||||
_hidpp10.set_notification_flags(receiver, old_notification_flags)
|
_hidpp10.set_notification_flags(receiver, old_notification_flags)
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,10 @@
|
||||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
from logitech_receiver import base as _base
|
from logitech_receiver import base
|
||||||
from logitech_receiver import hidpp10_constants as _hidpp10_constants
|
from logitech_receiver import hidpp10_constants
|
||||||
from logitech_receiver.common import strhex as _strhex
|
from logitech_receiver.common import strhex
|
||||||
from logitech_receiver.hidpp10_constants import Registers as Reg
|
from logitech_receiver.hidpp10_constants import Registers
|
||||||
|
|
||||||
from solaar.cli.show import _print_device
|
from solaar.cli.show import _print_device
|
||||||
from solaar.cli.show import _print_receiver
|
from solaar.cli.show import _print_receiver
|
||||||
|
|
@ -44,37 +44,42 @@ def run(receivers, args, find_receiver, _ignore):
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
print(" Register Dump")
|
print(" Register Dump")
|
||||||
rgst = receiver.read_register(Reg.NOTIFICATIONS)
|
rgst = receiver.read_register(Registers.NOTIFICATIONS)
|
||||||
print(" Notifications %#04x: %s" % (Reg.NOTIFICATIONS % 0x100, "0x" + _strhex(rgst) if rgst else "None"))
|
print(" Notifications %#04x: %s" % (Registers.NOTIFICATIONS % 0x100, "0x" + strhex(rgst) if rgst else "None"))
|
||||||
rgst = receiver.read_register(Reg.RECEIVER_CONNECTION)
|
rgst = receiver.read_register(Registers.RECEIVER_CONNECTION)
|
||||||
print(" Connection State %#04x: %s" % (Reg.RECEIVER_CONNECTION % 0x100, "0x" + _strhex(rgst) if rgst else "None"))
|
print(
|
||||||
rgst = receiver.read_register(Reg.DEVICES_ACTIVITY)
|
" Connection State %#04x: %s"
|
||||||
print(" Device Activity %#04x: %s" % (Reg.DEVICES_ACTIVITY % 0x100, "0x" + _strhex(rgst) if rgst else "None"))
|
% (Registers.RECEIVER_CONNECTION % 0x100, "0x" + strhex(rgst) if rgst else "None")
|
||||||
|
)
|
||||||
|
rgst = receiver.read_register(Registers.DEVICES_ACTIVITY)
|
||||||
|
print(
|
||||||
|
" Device Activity %#04x: %s" % (Registers.DEVICES_ACTIVITY % 0x100, "0x" + strhex(rgst) if rgst else "None")
|
||||||
|
)
|
||||||
|
|
||||||
for sub_reg in range(0, 16):
|
for sub_reg in range(0, 16):
|
||||||
rgst = receiver.read_register(Reg.RECEIVER_INFO, sub_reg)
|
rgst = receiver.read_register(Registers.RECEIVER_INFO, sub_reg)
|
||||||
print(
|
print(
|
||||||
" Pairing Register %#04x %#04x: %s"
|
" Pairing Register %#04x %#04x: %s"
|
||||||
% (Reg.RECEIVER_INFO % 0x100, sub_reg, "0x" + _strhex(rgst) if rgst else "None")
|
% (Registers.RECEIVER_INFO % 0x100, sub_reg, "0x" + strhex(rgst) if rgst else "None")
|
||||||
)
|
)
|
||||||
for device in range(0, 7):
|
for device in range(0, 7):
|
||||||
for sub_reg in [0x10, 0x20, 0x30, 0x50]:
|
for sub_reg in [0x10, 0x20, 0x30, 0x50]:
|
||||||
rgst = receiver.read_register(Reg.RECEIVER_INFO, sub_reg + device)
|
rgst = receiver.read_register(Registers.RECEIVER_INFO, sub_reg + device)
|
||||||
print(
|
print(
|
||||||
" Pairing Register %#04x %#04x: %s"
|
" Pairing Register %#04x %#04x: %s"
|
||||||
% (Reg.RECEIVER_INFO % 0x100, sub_reg + device, "0x" + _strhex(rgst) if rgst else "None")
|
% (Registers.RECEIVER_INFO % 0x100, sub_reg + device, "0x" + strhex(rgst) if rgst else "None")
|
||||||
)
|
)
|
||||||
rgst = receiver.read_register(Reg.RECEIVER_INFO, 0x40 + device)
|
rgst = receiver.read_register(Registers.RECEIVER_INFO, 0x40 + device)
|
||||||
print(
|
print(
|
||||||
" Pairing Name %#04x %#02x: %s"
|
" Pairing Name %#04x %#02x: %s"
|
||||||
% (Reg.RECEIVER_INFO % 0x100, 0x40 + device, rgst[2 : 2 + ord(rgst[1:2])] if rgst else "None")
|
% (Registers.RECEIVER_INFO % 0x100, 0x40 + device, rgst[2 : 2 + ord(rgst[1:2])] if rgst else "None")
|
||||||
)
|
)
|
||||||
for part in range(1, 4):
|
for part in range(1, 4):
|
||||||
rgst = receiver.read_register(Reg.RECEIVER_INFO, 0x60 + device, part)
|
rgst = receiver.read_register(Registers.RECEIVER_INFO, 0x60 + device, part)
|
||||||
print(
|
print(
|
||||||
" Pairing Name %#04x %#02x %#02x: %2d %s"
|
" Pairing Name %#04x %#02x %#02x: %2d %s"
|
||||||
% (
|
% (
|
||||||
Reg.RECEIVER_INFO % 0x100,
|
Registers.RECEIVER_INFO % 0x100,
|
||||||
0x60 + device,
|
0x60 + device,
|
||||||
part,
|
part,
|
||||||
ord(rgst[2:3]) if rgst else 0,
|
ord(rgst[2:3]) if rgst else 0,
|
||||||
|
|
@ -82,39 +87,39 @@ def run(receivers, args, find_receiver, _ignore):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
for sub_reg in range(0, 5):
|
for sub_reg in range(0, 5):
|
||||||
rgst = receiver.read_register(Reg.FIRMWARE, sub_reg)
|
rgst = receiver.read_register(Registers.FIRMWARE, sub_reg)
|
||||||
print(
|
print(
|
||||||
" Firmware %#04x %#04x: %s"
|
" Firmware %#04x %#04x: %s"
|
||||||
% (Reg.FIRMWARE % 0x100, sub_reg, "0x" + _strhex(rgst) if rgst is not None else "None")
|
% (Registers.FIRMWARE % 0x100, sub_reg, "0x" + strhex(rgst) if rgst is not None else "None")
|
||||||
)
|
)
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
for reg in range(0, 0xFF):
|
for reg in range(0, 0xFF):
|
||||||
last = None
|
last = None
|
||||||
for sub in range(0, 0xFF):
|
for sub in range(0, 0xFF):
|
||||||
rgst = _base.request(receiver.handle, 0xFF, 0x8100 | reg, sub, return_error=True)
|
rgst = base.request(receiver.handle, 0xFF, 0x8100 | reg, sub, return_error=True)
|
||||||
if isinstance(rgst, int) and rgst == _hidpp10_constants.ERROR.invalid_address:
|
if isinstance(rgst, int) and rgst == hidpp10_constants.ERROR.invalid_address:
|
||||||
break
|
break
|
||||||
elif isinstance(rgst, int) and rgst == _hidpp10_constants.ERROR.invalid_value:
|
elif isinstance(rgst, int) and rgst == hidpp10_constants.ERROR.invalid_value:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst:
|
if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst:
|
||||||
print(
|
print(
|
||||||
" Register Short %#04x %#04x: %s"
|
" Register Short %#04x %#04x: %s"
|
||||||
% (reg, sub, "0x" + _strhex(rgst) if isinstance(rgst, bytes) else str(rgst))
|
% (reg, sub, "0x" + strhex(rgst) if isinstance(rgst, bytes) else str(rgst))
|
||||||
)
|
)
|
||||||
last = rgst
|
last = rgst
|
||||||
last = None
|
last = None
|
||||||
for sub in range(0, 0xFF):
|
for sub in range(0, 0xFF):
|
||||||
rgst = _base.request(receiver.handle, 0xFF, 0x8100 | (0x200 + reg), sub, return_error=True)
|
rgst = base.request(receiver.handle, 0xFF, 0x8100 | (0x200 + reg), sub, return_error=True)
|
||||||
if isinstance(rgst, int) and rgst == _hidpp10_constants.ERROR.invalid_address:
|
if isinstance(rgst, int) and rgst == hidpp10_constants.ERROR.invalid_address:
|
||||||
break
|
break
|
||||||
elif isinstance(rgst, int) and rgst == _hidpp10_constants.ERROR.invalid_value:
|
elif isinstance(rgst, int) and rgst == hidpp10_constants.ERROR.invalid_value:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst:
|
if not isinstance(last, bytes) or not isinstance(rgst, bytes) or last != rgst:
|
||||||
print(
|
print(
|
||||||
" Register Long %#04x %#04x: %s"
|
" Register Long %#04x %#04x: %s"
|
||||||
% (reg, sub, "0x" + _strhex(rgst) if isinstance(rgst, bytes) else str(rgst))
|
% (reg, sub, "0x" + strhex(rgst) if isinstance(rgst, bytes) else str(rgst))
|
||||||
)
|
)
|
||||||
last = rgst
|
last = rgst
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@
|
||||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
import traceback as _traceback
|
import traceback
|
||||||
|
|
||||||
import yaml as _yaml
|
import yaml
|
||||||
|
|
||||||
from logitech_receiver.hidpp20 import OnboardProfiles as _OnboardProfiles
|
from logitech_receiver.hidpp20 import OnboardProfiles
|
||||||
from logitech_receiver.hidpp20 import OnboardProfilesVersion as _OnboardProfilesVersion
|
from logitech_receiver.hidpp20 import OnboardProfilesVersion
|
||||||
|
|
||||||
|
|
||||||
def run(receivers, args, find_receiver, find_device):
|
def run(receivers, args, find_receiver, find_device):
|
||||||
|
|
@ -42,15 +42,15 @@ def run(receivers, args, find_receiver, find_device):
|
||||||
print(f"Device {dev.name} is either offline or has no onboard profiles")
|
print(f"Device {dev.name} is either offline or has no onboard profiles")
|
||||||
elif not profiles_file:
|
elif not profiles_file:
|
||||||
print(f"#Dumping profiles from {dev.name}")
|
print(f"#Dumping profiles from {dev.name}")
|
||||||
print(_yaml.dump(dev.profiles))
|
print(yaml.dump(dev.profiles))
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
with open(profiles_file, "r") as f:
|
with open(profiles_file, "r") as f:
|
||||||
print(f"Reading profiles from {profiles_file}")
|
print(f"Reading profiles from {profiles_file}")
|
||||||
profiles = _yaml.safe_load(f)
|
profiles = yaml.safe_load(f)
|
||||||
if not isinstance(profiles, _OnboardProfiles):
|
if not isinstance(profiles, OnboardProfiles):
|
||||||
print("Profiles file does not contain current onboard profiles")
|
print("Profiles file does not contain current onboard profiles")
|
||||||
elif getattr(profiles, "version", None) != _OnboardProfilesVersion:
|
elif getattr(profiles, "version", None) != OnboardProfilesVersion:
|
||||||
version = getattr(profiles, "version", None)
|
version = getattr(profiles, "version", None)
|
||||||
print(f"Missing or incorrect profile version {version} in loaded profile")
|
print(f"Missing or incorrect profile version {version} in loaded profile")
|
||||||
elif getattr(profiles, "name", None) != dev.name:
|
elif getattr(profiles, "name", None) != dev.name:
|
||||||
|
|
@ -62,4 +62,4 @@ def run(receivers, args, find_receiver, find_device):
|
||||||
print(f"Wrote {written} sectors to {dev.name}")
|
print(f"Wrote {written} sectors to {dev.name}")
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print("Profiles not written:", exc)
|
print("Profiles not written:", exc)
|
||||||
print(_traceback.format_exc())
|
print(traceback.format_exc())
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,14 @@
|
||||||
|
|
||||||
from logitech_receiver import exceptions
|
from logitech_receiver import exceptions
|
||||||
from logitech_receiver import hidpp10
|
from logitech_receiver import hidpp10
|
||||||
from logitech_receiver import hidpp10_constants as _hidpp10_constants
|
from logitech_receiver import hidpp10_constants
|
||||||
from logitech_receiver import hidpp20
|
from logitech_receiver import hidpp20
|
||||||
from logitech_receiver import hidpp20_constants as _hidpp20_constants
|
from logitech_receiver import hidpp20_constants
|
||||||
from logitech_receiver import receiver as _receiver
|
from logitech_receiver import receiver
|
||||||
from logitech_receiver import settings_templates as _settings_templates
|
from logitech_receiver import settings_templates
|
||||||
from logitech_receiver.common import NamedInt as _NamedInt
|
from logitech_receiver.common import LOGITECH_VENDOR_ID
|
||||||
from logitech_receiver.common import strhex as _strhex
|
from logitech_receiver.common import NamedInt
|
||||||
|
from logitech_receiver.common import strhex
|
||||||
|
|
||||||
from solaar import NAME
|
from solaar import NAME
|
||||||
from solaar import __version__
|
from solaar import __version__
|
||||||
|
|
@ -36,7 +37,7 @@ def _print_receiver(receiver):
|
||||||
|
|
||||||
print(receiver.name)
|
print(receiver.name)
|
||||||
print(" Device path :", receiver.path)
|
print(" Device path :", receiver.path)
|
||||||
print(f" USB id : 046d:{receiver.product_id}")
|
print(f" USB id : {LOGITECH_VENDOR_ID:04x}:{receiver.product_id}")
|
||||||
print(" Serial :", receiver.serial)
|
print(" Serial :", receiver.serial)
|
||||||
pending = hidpp10.get_configuration_pending_flags(receiver)
|
pending = hidpp10.get_configuration_pending_flags(receiver)
|
||||||
if pending:
|
if pending:
|
||||||
|
|
@ -52,12 +53,12 @@ def _print_receiver(receiver):
|
||||||
notification_flags = _hidpp10.get_notification_flags(receiver)
|
notification_flags = _hidpp10.get_notification_flags(receiver)
|
||||||
if notification_flags is not None:
|
if notification_flags is not None:
|
||||||
if notification_flags:
|
if notification_flags:
|
||||||
notification_names = _hidpp10_constants.NOTIFICATION_FLAG.flag_names(notification_flags)
|
notification_names = hidpp10_constants.NOTIFICATION_FLAG.flag_names(notification_flags)
|
||||||
print(f" Notifications: {', '.join(notification_names)} (0x{notification_flags:06X})")
|
print(f" Notifications: {', '.join(notification_names)} (0x{notification_flags:06X})")
|
||||||
else:
|
else:
|
||||||
print(" Notifications: (none)")
|
print(" Notifications: (none)")
|
||||||
|
|
||||||
activity = receiver.read_register(_hidpp10_constants.Registers.DEVICES_ACTIVITY)
|
activity = receiver.read_register(hidpp10_constants.Registers.DEVICES_ACTIVITY)
|
||||||
if activity:
|
if activity:
|
||||||
activity = [(d, ord(activity[d - 1 : d])) for d in range(1, receiver.max_devices)]
|
activity = [(d, ord(activity[d - 1 : d])) for d in range(1, receiver.max_devices)]
|
||||||
activity_text = ", ".join(f"{int(d)}={int(a)}" for d, a in activity if a > 0)
|
activity_text = ", ".join(f"{int(d)}={int(a)}" for d, a in activity if a > 0)
|
||||||
|
|
@ -67,7 +68,7 @@ def _print_receiver(receiver):
|
||||||
def _battery_text(level) -> str:
|
def _battery_text(level) -> str:
|
||||||
if level is None:
|
if level is None:
|
||||||
return "N/A"
|
return "N/A"
|
||||||
elif isinstance(level, _NamedInt):
|
elif isinstance(level, NamedInt):
|
||||||
return str(level)
|
return str(level)
|
||||||
else:
|
else:
|
||||||
return f"{int(level)}%"
|
return f"{int(level)}%"
|
||||||
|
|
@ -103,7 +104,7 @@ def _print_device(dev, num=None):
|
||||||
if dev.wpid:
|
if dev.wpid:
|
||||||
print(f" WPID : {dev.wpid}")
|
print(f" WPID : {dev.wpid}")
|
||||||
if dev.product_id:
|
if dev.product_id:
|
||||||
print(f" USB id : 046d:{dev.product_id}")
|
print(f" USB id : {LOGITECH_VENDOR_ID:04x}:{dev.product_id}")
|
||||||
print(" Codename :", dev.codename)
|
print(" Codename :", dev.codename)
|
||||||
print(" Kind :", dev.kind)
|
print(" Kind :", dev.kind)
|
||||||
if dev.protocol:
|
if dev.protocol:
|
||||||
|
|
@ -128,14 +129,14 @@ def _print_device(dev, num=None):
|
||||||
notification_flags = _hidpp10.get_notification_flags(dev)
|
notification_flags = _hidpp10.get_notification_flags(dev)
|
||||||
if notification_flags is not None:
|
if notification_flags is not None:
|
||||||
if notification_flags:
|
if notification_flags:
|
||||||
notification_names = _hidpp10_constants.NOTIFICATION_FLAG.flag_names(notification_flags)
|
notification_names = hidpp10_constants.NOTIFICATION_FLAG.flag_names(notification_flags)
|
||||||
print(f" Notifications: {', '.join(notification_names)} (0x{notification_flags:06X}).")
|
print(f" Notifications: {', '.join(notification_names)} (0x{notification_flags:06X}).")
|
||||||
else:
|
else:
|
||||||
print(" Notifications: (none).")
|
print(" Notifications: (none).")
|
||||||
device_features = _hidpp10.get_device_features(dev)
|
device_features = _hidpp10.get_device_features(dev)
|
||||||
if device_features is not None:
|
if device_features is not None:
|
||||||
if device_features:
|
if device_features:
|
||||||
device_features_names = _hidpp10_constants.DEVICE_FEATURES.flag_names(device_features)
|
device_features_names = hidpp10_constants.DEVICE_FEATURES.flag_names(device_features)
|
||||||
print(f" Features: {', '.join(device_features_names)} (0x{device_features:06X})")
|
print(f" Features: {', '.join(device_features_names)} (0x{device_features:06X})")
|
||||||
else:
|
else:
|
||||||
print(" Features: (none)")
|
print(" Features: (none)")
|
||||||
|
|
@ -143,15 +144,15 @@ def _print_device(dev, num=None):
|
||||||
if dev.online and dev.features:
|
if dev.online and dev.features:
|
||||||
print(f" Supports {len(dev.features)} HID++ 2.0 features:")
|
print(f" Supports {len(dev.features)} HID++ 2.0 features:")
|
||||||
dev_settings = []
|
dev_settings = []
|
||||||
_settings_templates.check_feature_settings(dev, dev_settings)
|
settings_templates.check_feature_settings(dev, dev_settings)
|
||||||
for feature, index in dev.features.enumerate():
|
for feature, index in dev.features.enumerate():
|
||||||
flags = dev.request(0x0000, feature.bytes(2))
|
flags = dev.request(0x0000, feature.bytes(2))
|
||||||
flags = 0 if flags is None else ord(flags[1:2])
|
flags = 0 if flags is None else ord(flags[1:2])
|
||||||
flags = _hidpp20_constants.FEATURE_FLAG.flag_names(flags)
|
flags = hidpp20_constants.FEATURE_FLAG.flag_names(flags)
|
||||||
version = dev.features.get_feature_version(int(feature))
|
version = dev.features.get_feature_version(int(feature))
|
||||||
version = version if version else 0
|
version = version if version else 0
|
||||||
print(" %2d: %-22s {%04X} V%s %s " % (index, feature, feature, version, ", ".join(flags)))
|
print(" %2d: %-22s {%04X} V%s %s " % (index, feature, feature, version, ", ".join(flags)))
|
||||||
if feature == _hidpp20_constants.FEATURE.HIRES_WHEEL:
|
if feature == hidpp20_constants.FEATURE.HIRES_WHEEL:
|
||||||
wheel = _hidpp20.get_hires_wheel(dev)
|
wheel = _hidpp20.get_hires_wheel(dev)
|
||||||
if wheel:
|
if wheel:
|
||||||
multi, has_invert, has_switch, inv, res, target, ratchet = wheel
|
multi, has_invert, has_switch, inv, res, target, ratchet = wheel
|
||||||
|
|
@ -168,7 +169,7 @@ def _print_device(dev, num=None):
|
||||||
print(" HID++ notification")
|
print(" HID++ notification")
|
||||||
else:
|
else:
|
||||||
print(" HID notification")
|
print(" HID notification")
|
||||||
elif feature == _hidpp20_constants.FEATURE.MOUSE_POINTER:
|
elif feature == hidpp20_constants.FEATURE.MOUSE_POINTER:
|
||||||
mouse_pointer = _hidpp20.get_mouse_pointer_info(dev)
|
mouse_pointer = _hidpp20.get_mouse_pointer_info(dev)
|
||||||
if mouse_pointer:
|
if mouse_pointer:
|
||||||
print(f" DPI: {mouse_pointer['dpi']}")
|
print(f" DPI: {mouse_pointer['dpi']}")
|
||||||
|
|
@ -181,13 +182,13 @@ def _print_device(dev, num=None):
|
||||||
print(" Provide vertical tuning, trackball")
|
print(" Provide vertical tuning, trackball")
|
||||||
else:
|
else:
|
||||||
print(" No vertical tuning, standard mice")
|
print(" No vertical tuning, standard mice")
|
||||||
elif feature == _hidpp20_constants.FEATURE.VERTICAL_SCROLLING:
|
elif feature == hidpp20_constants.FEATURE.VERTICAL_SCROLLING:
|
||||||
vertical_scrolling_info = _hidpp20.get_vertical_scrolling_info(dev)
|
vertical_scrolling_info = _hidpp20.get_vertical_scrolling_info(dev)
|
||||||
if vertical_scrolling_info:
|
if vertical_scrolling_info:
|
||||||
print(f" Roller type: {vertical_scrolling_info['roller']}")
|
print(f" Roller type: {vertical_scrolling_info['roller']}")
|
||||||
print(f" Ratchet per turn: {vertical_scrolling_info['ratchet']}")
|
print(f" Ratchet per turn: {vertical_scrolling_info['ratchet']}")
|
||||||
print(f" Scroll lines: {vertical_scrolling_info['lines']}")
|
print(f" Scroll lines: {vertical_scrolling_info['lines']}")
|
||||||
elif feature == _hidpp20_constants.FEATURE.HI_RES_SCROLLING:
|
elif feature == hidpp20_constants.FEATURE.HI_RES_SCROLLING:
|
||||||
scrolling_mode, scrolling_resolution = _hidpp20.get_hi_res_scrolling_info(dev)
|
scrolling_mode, scrolling_resolution = _hidpp20.get_hi_res_scrolling_info(dev)
|
||||||
if scrolling_mode:
|
if scrolling_mode:
|
||||||
print(" Hi-res scrolling enabled")
|
print(" Hi-res scrolling enabled")
|
||||||
|
|
@ -195,49 +196,49 @@ def _print_device(dev, num=None):
|
||||||
print(" Hi-res scrolling disabled")
|
print(" Hi-res scrolling disabled")
|
||||||
if scrolling_resolution:
|
if scrolling_resolution:
|
||||||
print(f" Hi-res scrolling multiplier: {scrolling_resolution}")
|
print(f" Hi-res scrolling multiplier: {scrolling_resolution}")
|
||||||
elif feature == _hidpp20_constants.FEATURE.POINTER_SPEED:
|
elif feature == hidpp20_constants.FEATURE.POINTER_SPEED:
|
||||||
pointer_speed = _hidpp20.get_pointer_speed_info(dev)
|
pointer_speed = _hidpp20.get_pointer_speed_info(dev)
|
||||||
if pointer_speed:
|
if pointer_speed:
|
||||||
print(f" Pointer Speed: {pointer_speed}")
|
print(f" Pointer Speed: {pointer_speed}")
|
||||||
elif feature == _hidpp20_constants.FEATURE.LOWRES_WHEEL:
|
elif feature == hidpp20_constants.FEATURE.LOWRES_WHEEL:
|
||||||
wheel_status = _hidpp20.get_lowres_wheel_status(dev)
|
wheel_status = _hidpp20.get_lowres_wheel_status(dev)
|
||||||
if wheel_status:
|
if wheel_status:
|
||||||
print(f" Wheel Reports: {wheel_status}")
|
print(f" Wheel Reports: {wheel_status}")
|
||||||
elif feature == _hidpp20_constants.FEATURE.NEW_FN_INVERSION:
|
elif feature == hidpp20_constants.FEATURE.NEW_FN_INVERSION:
|
||||||
inversion = _hidpp20.get_new_fn_inversion(dev)
|
inversion = _hidpp20.get_new_fn_inversion(dev)
|
||||||
if inversion:
|
if inversion:
|
||||||
inverted, default_inverted = inversion
|
inverted, default_inverted = inversion
|
||||||
print(" Fn-swap:", "enabled" if inverted else "disabled")
|
print(" Fn-swap:", "enabled" if inverted else "disabled")
|
||||||
print(" Fn-swap default:", "enabled" if default_inverted else "disabled")
|
print(" Fn-swap default:", "enabled" if default_inverted else "disabled")
|
||||||
elif feature == _hidpp20_constants.FEATURE.HOSTS_INFO:
|
elif feature == hidpp20_constants.FEATURE.HOSTS_INFO:
|
||||||
host_names = _hidpp20.get_host_names(dev)
|
host_names = _hidpp20.get_host_names(dev)
|
||||||
for host, (paired, name) in host_names.items():
|
for host, (paired, name) in host_names.items():
|
||||||
print(f" Host {host} ({'paired' if paired else 'unpaired'}): {name}")
|
print(f" Host {host} ({'paired' if paired else 'unpaired'}): {name}")
|
||||||
elif feature == _hidpp20_constants.FEATURE.DEVICE_NAME:
|
elif feature == hidpp20_constants.FEATURE.DEVICE_NAME:
|
||||||
print(f" Name: {_hidpp20.get_name(dev)}")
|
print(f" Name: {_hidpp20.get_name(dev)}")
|
||||||
print(f" Kind: {_hidpp20.get_kind(dev)}")
|
print(f" Kind: {_hidpp20.get_kind(dev)}")
|
||||||
elif feature == _hidpp20_constants.FEATURE.DEVICE_FRIENDLY_NAME:
|
elif feature == hidpp20_constants.FEATURE.DEVICE_FRIENDLY_NAME:
|
||||||
print(f" Friendly Name: {_hidpp20.get_friendly_name(dev)}")
|
print(f" Friendly Name: {_hidpp20.get_friendly_name(dev)}")
|
||||||
elif feature == _hidpp20_constants.FEATURE.DEVICE_FW_VERSION:
|
elif feature == hidpp20_constants.FEATURE.DEVICE_FW_VERSION:
|
||||||
for fw in _hidpp20.get_firmware(dev):
|
for fw in _hidpp20.get_firmware(dev):
|
||||||
extras = _strhex(fw.extras) if fw.extras else ""
|
extras = strhex(fw.extras) if fw.extras else ""
|
||||||
print(f" Firmware: {fw.kind} {fw.name} {fw.version} {extras}")
|
print(f" Firmware: {fw.kind} {fw.name} {fw.version} {extras}")
|
||||||
ids = _hidpp20.get_ids(dev)
|
ids = _hidpp20.get_ids(dev)
|
||||||
if ids:
|
if ids:
|
||||||
unitId, modelId, tid_map = ids
|
unitId, modelId, tid_map = ids
|
||||||
print(f" Unit ID: {unitId} Model ID: {modelId} Transport IDs: {tid_map}")
|
print(f" Unit ID: {unitId} Model ID: {modelId} Transport IDs: {tid_map}")
|
||||||
elif (
|
elif (
|
||||||
feature == _hidpp20_constants.FEATURE.REPORT_RATE
|
feature == hidpp20_constants.FEATURE.REPORT_RATE
|
||||||
or feature == _hidpp20_constants.FEATURE.EXTENDED_ADJUSTABLE_REPORT_RATE
|
or feature == hidpp20_constants.FEATURE.EXTENDED_ADJUSTABLE_REPORT_RATE
|
||||||
):
|
):
|
||||||
print(f" Report Rate: {_hidpp20.get_polling_rate(dev)}")
|
print(f" Report Rate: {_hidpp20.get_polling_rate(dev)}")
|
||||||
elif feature == _hidpp20_constants.FEATURE.CONFIG_CHANGE:
|
elif feature == hidpp20_constants.FEATURE.CONFIG_CHANGE:
|
||||||
response = dev.feature_request(_hidpp20_constants.FEATURE.CONFIG_CHANGE, 0x00)
|
response = dev.feature_request(hidpp20_constants.FEATURE.CONFIG_CHANGE, 0x00)
|
||||||
print(f" Configuration: {response.hex()}")
|
print(f" Configuration: {response.hex()}")
|
||||||
elif feature == _hidpp20_constants.FEATURE.REMAINING_PAIRING:
|
elif feature == hidpp20_constants.FEATURE.REMAINING_PAIRING:
|
||||||
print(f" Remaining Pairings: {int(_hidpp20.get_remaining_pairing(dev))}")
|
print(f" Remaining Pairings: {int(_hidpp20.get_remaining_pairing(dev))}")
|
||||||
elif feature == _hidpp20_constants.FEATURE.ONBOARD_PROFILES:
|
elif feature == hidpp20_constants.FEATURE.ONBOARD_PROFILES:
|
||||||
if _hidpp20.get_onboard_mode(dev) == _hidpp20_constants.ONBOARD_MODES.MODE_HOST:
|
if _hidpp20.get_onboard_mode(dev) == hidpp20_constants.ONBOARD_MODES.MODE_HOST:
|
||||||
mode = "Host"
|
mode = "Host"
|
||||||
else:
|
else:
|
||||||
mode = "On-Board"
|
mode = "On-Board"
|
||||||
|
|
@ -266,9 +267,9 @@ def _print_device(dev, num=None):
|
||||||
print(f" Has {len(dev.keys)} reprogrammable keys:")
|
print(f" Has {len(dev.keys)} reprogrammable keys:")
|
||||||
for k in dev.keys:
|
for k in dev.keys:
|
||||||
# TODO: add here additional variants for other REPROG_CONTROLS
|
# TODO: add here additional variants for other REPROG_CONTROLS
|
||||||
if dev.keys.keyversion == _hidpp20_constants.FEATURE.REPROG_CONTROLS_V2:
|
if dev.keys.keyversion == hidpp20_constants.FEATURE.REPROG_CONTROLS_V2:
|
||||||
print(" %2d: %-26s => %-27s %s" % (k.index, k.key, k.default_task, ", ".join(k.flags)))
|
print(" %2d: %-26s => %-27s %s" % (k.index, k.key, k.default_task, ", ".join(k.flags)))
|
||||||
if dev.keys.keyversion == _hidpp20_constants.FEATURE.REPROG_CONTROLS_V4:
|
if dev.keys.keyversion == hidpp20_constants.FEATURE.REPROG_CONTROLS_V4:
|
||||||
print(" %2d: %-26s, default: %-27s => %-26s" % (k.index, k.key, k.default_task, k.mapped_to))
|
print(" %2d: %-26s, default: %-27s => %-26s" % (k.index, k.key, k.default_task, k.mapped_to))
|
||||||
gmask_fmt = ",".join(k.group_mask)
|
gmask_fmt = ",".join(k.group_mask)
|
||||||
gmask_fmt = gmask_fmt if gmask_fmt else "empty"
|
gmask_fmt = gmask_fmt if gmask_fmt else "empty"
|
||||||
|
|
@ -311,7 +312,7 @@ def run(devices, args, find_receiver, find_device):
|
||||||
|
|
||||||
if device_name == "all":
|
if device_name == "all":
|
||||||
for d in devices:
|
for d in devices:
|
||||||
if isinstance(d, _receiver.Receiver):
|
if isinstance(d, receiver.Receiver):
|
||||||
_print_receiver(d)
|
_print_receiver(d)
|
||||||
count = d.count()
|
count = d.count()
|
||||||
if count:
|
if count:
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@
|
||||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
import json as _json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os as _os
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
@ -28,9 +28,9 @@ from solaar import __version__
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_XDG_CONFIG_HOME = _os.environ.get("XDG_CONFIG_HOME") or _os.path.expanduser(_os.path.join("~", ".config"))
|
_XDG_CONFIG_HOME = os.environ.get("XDG_CONFIG_HOME") or os.path.expanduser(os.path.join("~", ".config"))
|
||||||
_yaml_file_path = _os.path.join(_XDG_CONFIG_HOME, "solaar", "config.yaml")
|
_yaml_file_path = os.path.join(_XDG_CONFIG_HOME, "solaar", "config.yaml")
|
||||||
_json_file_path = _os.path.join(_XDG_CONFIG_HOME, "solaar", "config.json")
|
_json_file_path = os.path.join(_XDG_CONFIG_HOME, "solaar", "config.json")
|
||||||
|
|
||||||
_KEY_VERSION = "_version"
|
_KEY_VERSION = "_version"
|
||||||
_KEY_NAME = "_NAME"
|
_KEY_NAME = "_NAME"
|
||||||
|
|
@ -45,18 +45,18 @@ _config = []
|
||||||
|
|
||||||
def _load():
|
def _load():
|
||||||
loaded_config = []
|
loaded_config = []
|
||||||
if _os.path.isfile(_yaml_file_path):
|
if os.path.isfile(_yaml_file_path):
|
||||||
path = _yaml_file_path
|
path = _yaml_file_path
|
||||||
try:
|
try:
|
||||||
with open(_yaml_file_path) as config_file:
|
with open(_yaml_file_path) as config_file:
|
||||||
loaded_config = yaml.safe_load(config_file)
|
loaded_config = yaml.safe_load(config_file)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("failed to load from %s: %s", _yaml_file_path, e)
|
logger.error("failed to load from %s: %s", _yaml_file_path, e)
|
||||||
elif _os.path.isfile(_json_file_path):
|
elif os.path.isfile(_json_file_path):
|
||||||
path = _json_file_path
|
path = _json_file_path
|
||||||
try:
|
try:
|
||||||
with open(_json_file_path) as config_file:
|
with open(_json_file_path) as config_file:
|
||||||
loaded_config = _json.load(config_file)
|
loaded_config = json.load(config_file)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("failed to load from %s: %s", _json_file_path, e)
|
logger.error("failed to load from %s: %s", _json_file_path, e)
|
||||||
loaded_config = _convert_json(loaded_config)
|
loaded_config = _convert_json(loaded_config)
|
||||||
|
|
@ -129,10 +129,10 @@ def save(defer=False):
|
||||||
global save_timer
|
global save_timer
|
||||||
if not _config:
|
if not _config:
|
||||||
return
|
return
|
||||||
dirname = _os.path.dirname(_yaml_file_path)
|
dirname = os.path.dirname(_yaml_file_path)
|
||||||
if not _os.path.isdir(dirname):
|
if not os.path.isdir(dirname):
|
||||||
try:
|
try:
|
||||||
_os.makedirs(dirname)
|
os.makedirs(dirname)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("failed to create %s", dirname)
|
logger.error("failed to create %s", dirname)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -29,15 +29,13 @@ import tempfile
|
||||||
|
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
|
|
||||||
import solaar.cli as _cli
|
|
||||||
import solaar.configuration as _configuration
|
|
||||||
import solaar.dbus as _dbus
|
|
||||||
import solaar.listener as _listener
|
|
||||||
import solaar.ui as _ui
|
|
||||||
import solaar.ui.common as _common
|
|
||||||
|
|
||||||
from solaar import NAME
|
from solaar import NAME
|
||||||
from solaar import __version__
|
from solaar import __version__
|
||||||
|
from solaar import cli
|
||||||
|
from solaar import configuration
|
||||||
|
from solaar import dbus
|
||||||
|
from solaar import listener
|
||||||
|
from solaar import ui
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -87,12 +85,12 @@ def _parse_arguments():
|
||||||
arg_parser.add_argument("--tray-icon-size", type=int, help="explicit size for tray icons")
|
arg_parser.add_argument("--tray-icon-size", type=int, help="explicit size for tray icons")
|
||||||
arg_parser.add_argument("-V", "--version", action="version", version="%(prog)s " + __version__)
|
arg_parser.add_argument("-V", "--version", action="version", version="%(prog)s " + __version__)
|
||||||
arg_parser.add_argument("--help-actions", action="store_true", help="print help for the optional actions")
|
arg_parser.add_argument("--help-actions", action="store_true", help="print help for the optional actions")
|
||||||
arg_parser.add_argument("action", nargs=argparse.REMAINDER, choices=_cli.actions, help="optional actions to perform")
|
arg_parser.add_argument("action", nargs=argparse.REMAINDER, choices=cli.actions, help="optional actions to perform")
|
||||||
|
|
||||||
args = arg_parser.parse_args()
|
args = arg_parser.parse_args()
|
||||||
|
|
||||||
if args.help_actions:
|
if args.help_actions:
|
||||||
_cli.print_help()
|
cli.print_help()
|
||||||
return
|
return
|
||||||
|
|
||||||
if args.window is None:
|
if args.window is None:
|
||||||
|
|
@ -146,7 +144,7 @@ def main():
|
||||||
return
|
return
|
||||||
if args.action:
|
if args.action:
|
||||||
# if any argument, run comandline and exit
|
# if any argument, run comandline and exit
|
||||||
return _cli.run(args.action, args.hidraw_path)
|
return cli.run(args.action, args.hidraw_path)
|
||||||
|
|
||||||
gi = _require("gi", "python3-gi (in Ubuntu) or python3-gobject (in Fedora)")
|
gi = _require("gi", "python3-gi (in Ubuntu) or python3-gobject (in Fedora)")
|
||||||
_require("gi.repository.Gtk", "gir1.2-gtk-3.0", gi, "Gtk", "3.0")
|
_require("gi.repository.Gtk", "gir1.2-gtk-3.0", gi, "Gtk", "3.0")
|
||||||
|
|
@ -166,17 +164,17 @@ def main():
|
||||||
logger.warning("Solaar udev file not found in expected location")
|
logger.warning("Solaar udev file not found in expected location")
|
||||||
logger.warning("See https://pwr-solaar.github.io/Solaar/installation for more information")
|
logger.warning("See https://pwr-solaar.github.io/Solaar/installation for more information")
|
||||||
try:
|
try:
|
||||||
_listener.setup_scanner(_ui.status_changed, _ui.setting_changed, _common.error_dialog)
|
listener.setup_scanner(ui.status_changed, ui.setting_changed, ui.common.error_dialog)
|
||||||
|
|
||||||
if args.restart_on_wake_up:
|
if args.restart_on_wake_up:
|
||||||
_dbus.watch_suspend_resume(_listener.start_all, _listener.stop_all)
|
dbus.watch_suspend_resume(listener.start_all, listener.stop_all)
|
||||||
else:
|
else:
|
||||||
_dbus.watch_suspend_resume(lambda: _listener.ping_all(True))
|
dbus.watch_suspend_resume(lambda: listener.ping_all(True))
|
||||||
|
|
||||||
_configuration.defer_saves = True # allow configuration saves to be deferred
|
configuration.defer_saves = True # allow configuration saves to be deferred
|
||||||
|
|
||||||
# main UI event loop
|
# main UI event loop
|
||||||
_ui.run_loop(_listener.start_all, _listener.stop_all, args.window != "only", args.window != "hide")
|
ui.run_loop(listener.start_all, listener.stop_all, args.window != "only", args.window != "hide")
|
||||||
except Exception:
|
except Exception:
|
||||||
sys.exit(f"{NAME.lower()}: error: {format_exc()}")
|
sys.exit(f"{NAME.lower()}: error: {format_exc()}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
import errno as _errno
|
import errno
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
|
@ -24,15 +24,13 @@ from collections import namedtuple
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
import logitech_receiver.device as _device
|
import logitech_receiver
|
||||||
import logitech_receiver.receiver as _receiver
|
|
||||||
|
|
||||||
from logitech_receiver import base as _base
|
from logitech_receiver import base
|
||||||
from logitech_receiver import exceptions
|
from logitech_receiver import exceptions
|
||||||
from logitech_receiver import hidpp10_constants as _hidpp10_constants
|
from logitech_receiver import hidpp10_constants
|
||||||
from logitech_receiver import listener as _listener
|
from logitech_receiver import listener
|
||||||
from logitech_receiver import notifications as _notifications
|
from logitech_receiver import notifications
|
||||||
from logitech_receiver.hidpp10_constants import Registers
|
|
||||||
|
|
||||||
from . import configuration
|
from . import configuration
|
||||||
from . import dbus
|
from . import dbus
|
||||||
|
|
@ -43,9 +41,6 @@ from gi.repository import GLib # NOQA: E402 # isort:skip
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_IR = _hidpp10_constants.INFO_SUBREGISTERS
|
|
||||||
|
|
||||||
|
|
||||||
_GHOST_DEVICE = namedtuple("_GHOST_DEVICE", ("receiver", "number", "name", "kind", "online"))
|
_GHOST_DEVICE = namedtuple("_GHOST_DEVICE", ("receiver", "number", "name", "kind", "online"))
|
||||||
_GHOST_DEVICE.__bool__ = lambda self: False
|
_GHOST_DEVICE.__bool__ = lambda self: False
|
||||||
_GHOST_DEVICE.__nonzero__ = _GHOST_DEVICE.__bool__
|
_GHOST_DEVICE.__nonzero__ = _GHOST_DEVICE.__bool__
|
||||||
|
|
@ -55,7 +50,7 @@ def _ghost(device):
|
||||||
return _GHOST_DEVICE(receiver=device.receiver, number=device.number, name=device.name, kind=device.kind, online=False)
|
return _GHOST_DEVICE(receiver=device.receiver, number=device.number, name=device.name, kind=device.kind, online=False)
|
||||||
|
|
||||||
|
|
||||||
class SolaarListener(_listener.EventsListener):
|
class SolaarListener(listener.EventsListener):
|
||||||
"""Keeps the status of a Receiver or Device (member name is receiver but it can also be a device)."""
|
"""Keeps the status of a Receiver or Device (member name is receiver but it can also be a device)."""
|
||||||
|
|
||||||
def __init__(self, receiver, status_changed_callback):
|
def __init__(self, receiver, status_changed_callback):
|
||||||
|
|
@ -69,7 +64,7 @@ class SolaarListener(_listener.EventsListener):
|
||||||
logger.info("%s: notifications listener has started (%s)", self.receiver, self.receiver.handle)
|
logger.info("%s: notifications listener has started (%s)", self.receiver, self.receiver.handle)
|
||||||
nfs = self.receiver.enable_connection_notifications()
|
nfs = self.receiver.enable_connection_notifications()
|
||||||
if logger.isEnabledFor(logging.WARNING):
|
if logger.isEnabledFor(logging.WARNING):
|
||||||
if not self.receiver.isDevice and not ((nfs if nfs else 0) & _hidpp10_constants.NOTIFICATION_FLAG.wireless):
|
if not self.receiver.isDevice and not ((nfs if nfs else 0) & hidpp10_constants.NOTIFICATION_FLAG.wireless):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Receiver on %s might not support connection notifications, GUI might not show its devices",
|
"Receiver on %s might not support connection notifications, GUI might not show its devices",
|
||||||
self.receiver.path,
|
self.receiver.path,
|
||||||
|
|
@ -142,11 +137,9 @@ class SolaarListener(_listener.EventsListener):
|
||||||
|
|
||||||
def _notifications_handler(self, n):
|
def _notifications_handler(self, n):
|
||||||
assert self.receiver
|
assert self.receiver
|
||||||
# if logger.isEnabledFor(logging.DEBUG):
|
|
||||||
# logger.debug("%s: handling %s", self.receiver, n)
|
|
||||||
if n.devnumber == 0xFF:
|
if n.devnumber == 0xFF:
|
||||||
# a receiver notification
|
# a receiver notification
|
||||||
_notifications.process(self.receiver, n)
|
notifications.process(self.receiver, n)
|
||||||
return
|
return
|
||||||
|
|
||||||
# a notification that came in to the device listener - strange, but nothing needs to be done here
|
# a notification that came in to the device listener - strange, but nothing needs to be done here
|
||||||
|
|
@ -156,7 +149,7 @@ class SolaarListener(_listener.EventsListener):
|
||||||
return
|
return
|
||||||
|
|
||||||
# DJ pairing notification - ignore - hid++ 1.0 pairing notification is all that is needed
|
# DJ pairing notification - ignore - hid++ 1.0 pairing notification is all that is needed
|
||||||
if n.sub_id == 0x41 and n.report_id == _base.DJ_MESSAGE_ID:
|
if n.sub_id == 0x41 and n.report_id == base.DJ_MESSAGE_ID:
|
||||||
if logger.isEnabledFor(logging.INFO):
|
if logger.isEnabledFor(logging.INFO):
|
||||||
logger.info("ignoring DJ pairing notification %s", n)
|
logger.info("ignoring DJ pairing notification %s", n)
|
||||||
return
|
return
|
||||||
|
|
@ -170,7 +163,7 @@ class SolaarListener(_listener.EventsListener):
|
||||||
|
|
||||||
# FIXME: hacky fix for kernel/hardware race condition
|
# FIXME: hacky fix for kernel/hardware race condition
|
||||||
# If the device was just turned on or woken up from sleep, it may not be ready to receive commands.
|
# If the device was just turned on or woken up from sleep, it may not be ready to receive commands.
|
||||||
# The "payload" bit of the wireless tatus notification seems to tell us this. If this is the case, we
|
# The "payload" bit of the wireless status notification seems to tell us this. If this is the case, we
|
||||||
# must wait a short amount of time to avoid causing a broken pipe error.
|
# must wait a short amount of time to avoid causing a broken pipe error.
|
||||||
device_ready = not bool(ord(n.data[0:1]) & 0x80) or n.sub_id != 0x41
|
device_ready = not bool(ord(n.data[0:1]) & 0x80) or n.sub_id != 0x41
|
||||||
if not device_ready:
|
if not device_ready:
|
||||||
|
|
@ -183,7 +176,13 @@ class SolaarListener(_listener.EventsListener):
|
||||||
if not already_known:
|
if not already_known:
|
||||||
if n.address == 0x0A and not self.receiver.receiver_kind == "bolt":
|
if n.address == 0x0A and not self.receiver.receiver_kind == "bolt":
|
||||||
# some Nanos send a notification even if no new pairing - check that there really is a device there
|
# some Nanos send a notification even if no new pairing - check that there really is a device there
|
||||||
if self.receiver.read_register(Registers.RECEIVER_INFO, _IR.pairing_information + n.devnumber - 1) is None:
|
if (
|
||||||
|
self.receiver.read_register(
|
||||||
|
hidpp10_constants.Registers.RECEIVER_INFO,
|
||||||
|
hidpp10_constants.INFO_SUBREGISTERS.pairing_information + n.devnumber - 1,
|
||||||
|
)
|
||||||
|
is None
|
||||||
|
):
|
||||||
return
|
return
|
||||||
dev = self.receiver.register_new_device(n.devnumber, n)
|
dev = self.receiver.register_new_device(n.devnumber, n)
|
||||||
elif self.receiver.pairing.lock_open and self.receiver.re_pairs and not ord(n.data[0:1]) & 0x40:
|
elif self.receiver.pairing.lock_open and self.receiver.re_pairs and not ord(n.data[0:1]) & 0x40:
|
||||||
|
|
@ -212,7 +211,7 @@ class SolaarListener(_listener.EventsListener):
|
||||||
# the receiver changed status as well
|
# the receiver changed status as well
|
||||||
self._status_changed(self.receiver)
|
self._status_changed(self.receiver)
|
||||||
|
|
||||||
_notifications.process(dev, n)
|
notifications.process(dev, n)
|
||||||
|
|
||||||
if self.receiver.pairing.lock_open and not already_known:
|
if self.receiver.pairing.lock_open and not already_known:
|
||||||
# this should be the first notification after a device was paired
|
# this should be the first notification after a device was paired
|
||||||
|
|
@ -256,17 +255,17 @@ def _start(device_info):
|
||||||
assert _status_callback and _setting_callback
|
assert _status_callback and _setting_callback
|
||||||
isDevice = device_info.isDevice
|
isDevice = device_info.isDevice
|
||||||
if not isDevice:
|
if not isDevice:
|
||||||
receiver = _receiver.ReceiverFactory.create_receiver(device_info, _setting_callback)
|
receiver_ = logitech_receiver.receiver.ReceiverFactory.create_receiver(device_info, _setting_callback)
|
||||||
else:
|
else:
|
||||||
receiver = _device.DeviceFactory.create_device(_base, device_info, _setting_callback)
|
receiver_ = logitech_receiver.device.DeviceFactory.create_device(base, device_info, _setting_callback)
|
||||||
if receiver:
|
if receiver_:
|
||||||
configuration.attach_to(receiver)
|
configuration.attach_to(receiver_)
|
||||||
if receiver.bluetooth and receiver.hid_serial:
|
if receiver_.bluetooth and receiver_.hid_serial:
|
||||||
dbus.watch_bluez_connect(receiver.hid_serial, partial(_process_bluez_dbus, receiver))
|
dbus.watch_bluez_connect(receiver_.hid_serial, partial(_process_bluez_dbus, receiver_))
|
||||||
receiver.cleanups.append(_cleanup_bluez_dbus)
|
receiver_.cleanups.append(_cleanup_bluez_dbus)
|
||||||
|
|
||||||
if receiver:
|
if receiver_:
|
||||||
rl = SolaarListener(receiver, _status_callback)
|
rl = SolaarListener(receiver_, _status_callback)
|
||||||
rl.start()
|
rl.start()
|
||||||
_all_listeners[device_info.path] = rl
|
_all_listeners[device_info.path] = rl
|
||||||
return rl
|
return rl
|
||||||
|
|
@ -278,7 +277,7 @@ def start_all():
|
||||||
stop_all() # just in case this it called twice in a row...
|
stop_all() # just in case this it called twice in a row...
|
||||||
if logger.isEnabledFor(logging.INFO):
|
if logger.isEnabledFor(logging.INFO):
|
||||||
logger.info("starting receiver listening threads")
|
logger.info("starting receiver listening threads")
|
||||||
for device_info in _base.receivers_and_devices():
|
for device_info in base.receivers_and_devices():
|
||||||
_process_receiver_event("add", device_info)
|
_process_receiver_event("add", device_info)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -333,14 +332,14 @@ def setup_scanner(status_changed_callback, setting_changed_callback, error_callb
|
||||||
_status_callback = status_changed_callback
|
_status_callback = status_changed_callback
|
||||||
_setting_callback = setting_changed_callback
|
_setting_callback = setting_changed_callback
|
||||||
_error_callback = error_callback
|
_error_callback = error_callback
|
||||||
_base.notify_on_receivers_glib(_process_receiver_event)
|
base.notify_on_receivers_glib(_process_receiver_event)
|
||||||
|
|
||||||
|
|
||||||
def _process_add(device_info, retry):
|
def _process_add(device_info, retry):
|
||||||
try:
|
try:
|
||||||
_start(device_info)
|
_start(device_info)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno == _errno.EACCES:
|
if e.errno == errno.EACCES:
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output(["/usr/bin/getfacl", "-p", device_info.path], text=True)
|
output = subprocess.check_output(["/usr/bin/getfacl", "-p", device_info.path], text=True)
|
||||||
if logger.isEnabledFor(logging.WARNING):
|
if logger.isEnabledFor(logging.WARNING):
|
||||||
|
|
|
||||||
|
|
@ -18,21 +18,21 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from threading import Thread as _Thread
|
from threading import Thread
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from Queue import Queue as _Queue
|
from Queue import Queue
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from queue import Queue as _Queue
|
from queue import Queue
|
||||||
|
|
||||||
|
|
||||||
class TaskRunner(_Thread):
|
class TaskRunner(Thread):
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
super().__init__(name=name)
|
super().__init__(name=name)
|
||||||
self.daemon = True
|
self.daemon = True
|
||||||
self.queue = _Queue(16)
|
self.queue = Queue(16)
|
||||||
self.alive = False
|
self.alive = False
|
||||||
|
|
||||||
def __call__(self, function, *args, **kwargs):
|
def __call__(self, function, *args, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
import yaml as _yaml
|
import yaml
|
||||||
|
|
||||||
from logitech_receiver.common import Alert
|
from logitech_receiver.common import Alert
|
||||||
|
|
||||||
|
|
@ -65,7 +65,7 @@ def _activate(app):
|
||||||
|
|
||||||
def _command_line(app, command_line):
|
def _command_line(app, command_line):
|
||||||
args = command_line.get_arguments()
|
args = command_line.get_arguments()
|
||||||
args = _yaml.safe_load("".join(args)) if args else args
|
args = yaml.safe_load("".join(args)) if args else args
|
||||||
if not args:
|
if not args:
|
||||||
_activate(app)
|
_activate(app)
|
||||||
elif args[0] == "config": # config call from remote instance
|
elif args[0] == "config": # config call from remote instance
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import logging
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
from solaar.i18n import _
|
from solaar.i18n import _
|
||||||
from solaar.tasks import TaskRunner as _TaskRunner
|
from solaar.tasks import TaskRunner
|
||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
gi.require_version("Gtk", "3.0")
|
||||||
from gi.repository import GLib # NOQA: E402
|
from gi.repository import GLib # NOQA: E402
|
||||||
|
|
@ -74,7 +74,7 @@ _task_runner = None
|
||||||
|
|
||||||
def start_async():
|
def start_async():
|
||||||
global _task_runner
|
global _task_runner
|
||||||
_task_runner = _TaskRunner("AsyncUI")
|
_task_runner = TaskRunner("AsyncUI")
|
||||||
_task_runner.start()
|
_task_runner.start()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,17 @@
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from threading import Timer as _Timer
|
from threading import Timer
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
from logitech_receiver.hidpp20 import LEDEffectSetting as _LEDEffectSetting
|
from logitech_receiver import hidpp20
|
||||||
from logitech_receiver.settings import KIND as _SETTING_KIND
|
from logitech_receiver import settings
|
||||||
from logitech_receiver.settings import SENSITIVITY_IGNORE as _SENSITIVITY_IGNORE
|
|
||||||
|
|
||||||
from solaar.i18n import _
|
from solaar.i18n import _
|
||||||
from solaar.i18n import ngettext
|
from solaar.i18n import ngettext
|
||||||
|
|
||||||
from .common import ui_async as _ui_async
|
from .common import ui_async
|
||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
gi.require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gdk # NOQA: E402
|
from gi.repository import Gdk # NOQA: E402
|
||||||
|
|
@ -49,7 +48,7 @@ def _read_async(setting, force_read, sbox, device_is_online, sensitive):
|
||||||
logger.warning("%s: error reading so use None (%s): %s", s.name, s._device, repr(e))
|
logger.warning("%s: error reading so use None (%s): %s", s.name, s._device, repr(e))
|
||||||
GLib.idle_add(_update_setting_item, sb, v, online, sensitive, True, priority=99)
|
GLib.idle_add(_update_setting_item, sb, v, online, sensitive, True, priority=99)
|
||||||
|
|
||||||
_ui_async(_do_read, setting, force_read, sbox, device_is_online, sensitive)
|
ui_async(_do_read, setting, force_read, sbox, device_is_online, sensitive)
|
||||||
|
|
||||||
|
|
||||||
def _write_async(setting, value, sbox, sensitive=True, key=None):
|
def _write_async(setting, value, sbox, sensitive=True, key=None):
|
||||||
|
|
@ -71,7 +70,7 @@ def _write_async(setting, value, sbox, sensitive=True, key=None):
|
||||||
sbox._failed.set_visible(False)
|
sbox._failed.set_visible(False)
|
||||||
sbox._spinner.set_visible(True)
|
sbox._spinner.set_visible(True)
|
||||||
sbox._spinner.start()
|
sbox._spinner.start()
|
||||||
_ui_async(_do_write, setting, value, sbox, key)
|
ui_async(_do_write, setting, value, sbox, key)
|
||||||
|
|
||||||
|
|
||||||
class ComboBoxText(Gtk.ComboBoxText):
|
class ComboBoxText(Gtk.ComboBoxText):
|
||||||
|
|
@ -105,7 +104,7 @@ class Control:
|
||||||
def layout(self, sbox, label, change, spinner, failed):
|
def layout(self, sbox, label, change, spinner, failed):
|
||||||
sbox.pack_start(label, False, False, 0)
|
sbox.pack_start(label, False, False, 0)
|
||||||
sbox.pack_end(change, False, False, 0)
|
sbox.pack_end(change, False, False, 0)
|
||||||
fill = sbox.setting.kind == _SETTING_KIND.range or sbox.setting.kind == _SETTING_KIND.hetero
|
fill = sbox.setting.kind == settings.KIND.range or sbox.setting.kind == settings.KIND.hetero
|
||||||
sbox.pack_end(self, fill, fill, 0)
|
sbox.pack_end(self, fill, fill, 0)
|
||||||
sbox.pack_end(spinner, False, False, 0)
|
sbox.pack_end(spinner, False, False, 0)
|
||||||
sbox.pack_end(failed, False, False, 0)
|
sbox.pack_end(failed, False, False, 0)
|
||||||
|
|
@ -144,7 +143,7 @@ class SliderControl(Gtk.Scale, Control):
|
||||||
if self.get_sensitive():
|
if self.get_sensitive():
|
||||||
if self.timer:
|
if self.timer:
|
||||||
self.timer.cancel()
|
self.timer.cancel()
|
||||||
self.timer = _Timer(0.5, lambda: GLib.idle_add(self.do_change))
|
self.timer = Timer(0.5, lambda: GLib.idle_add(self.do_change))
|
||||||
self.timer.start()
|
self.timer.start()
|
||||||
|
|
||||||
def do_change(self):
|
def do_change(self):
|
||||||
|
|
@ -435,7 +434,7 @@ class MultipleRangeControl(MultipleControl):
|
||||||
if control.get_sensitive():
|
if control.get_sensitive():
|
||||||
if hasattr(control, "_timer"):
|
if hasattr(control, "_timer"):
|
||||||
control._timer.cancel()
|
control._timer.cancel()
|
||||||
control._timer = _Timer(0.5, lambda: GLib.idle_add(self._write, control, item, sub_item))
|
control._timer = Timer(0.5, lambda: GLib.idle_add(self._write, control, item, sub_item))
|
||||||
control._timer.start()
|
control._timer.start()
|
||||||
|
|
||||||
def _write(self, control, item, sub_item):
|
def _write(self, control, item, sub_item):
|
||||||
|
|
@ -495,7 +494,7 @@ class PackedRangeControl(MultipleRangeControl):
|
||||||
if control.get_sensitive():
|
if control.get_sensitive():
|
||||||
if hasattr(control, "_timer"):
|
if hasattr(control, "_timer"):
|
||||||
control._timer.cancel()
|
control._timer.cancel()
|
||||||
control._timer = _Timer(0.5, lambda: GLib.idle_add(self._write, control, item))
|
control._timer = Timer(0.5, lambda: GLib.idle_add(self._write, control, item))
|
||||||
control._timer.start()
|
control._timer.start()
|
||||||
|
|
||||||
def _write(self, control, item):
|
def _write(self, control, item):
|
||||||
|
|
@ -537,14 +536,14 @@ class HeteroKeyControl(Gtk.HBox, Control):
|
||||||
item_lblbox.set_visible(False)
|
item_lblbox.set_visible(False)
|
||||||
else:
|
else:
|
||||||
item_lblbox = None
|
item_lblbox = None
|
||||||
if item["kind"] == _SETTING_KIND.choice:
|
if item["kind"] == settings.KIND.choice:
|
||||||
item_box = ComboBoxText()
|
item_box = ComboBoxText()
|
||||||
for entry in item["choices"]:
|
for entry in item["choices"]:
|
||||||
item_box.append(str(int(entry)), str(entry))
|
item_box.append(str(int(entry)), str(entry))
|
||||||
item_box.set_active(0)
|
item_box.set_active(0)
|
||||||
item_box.connect("changed", self.changed)
|
item_box.connect("changed", self.changed)
|
||||||
self.pack_start(item_box, False, False, 0)
|
self.pack_start(item_box, False, False, 0)
|
||||||
elif item["kind"] == _SETTING_KIND.range:
|
elif item["kind"] == settings.KIND.range:
|
||||||
item_box = Scale()
|
item_box = Scale()
|
||||||
item_box.set_range(item["min"], item["max"])
|
item_box.set_range(item["min"], item["max"])
|
||||||
item_box.set_round_digits(0)
|
item_box.set_round_digits(0)
|
||||||
|
|
@ -559,7 +558,7 @@ class HeteroKeyControl(Gtk.HBox, Control):
|
||||||
result = {}
|
result = {}
|
||||||
for k, (_lblbox, box) in self._items.items():
|
for k, (_lblbox, box) in self._items.items():
|
||||||
result[str(k)] = box.get_value()
|
result[str(k)] = box.get_value()
|
||||||
result = _LEDEffectSetting(**result)
|
result = hidpp20.LEDEffectSetting(**result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def set_value(self, value):
|
def set_value(self, value):
|
||||||
|
|
@ -587,7 +586,7 @@ class HeteroKeyControl(Gtk.HBox, Control):
|
||||||
self.setup_visibles(int(self._items["ID"][1].get_value()))
|
self.setup_visibles(int(self._items["ID"][1].get_value()))
|
||||||
if hasattr(control, "_timer"):
|
if hasattr(control, "_timer"):
|
||||||
control._timer.cancel()
|
control._timer.cancel()
|
||||||
control._timer = _Timer(0.3, lambda: GLib.idle_add(self._write, control))
|
control._timer = Timer(0.3, lambda: GLib.idle_add(self._write, control))
|
||||||
control._timer.start()
|
control._timer.start()
|
||||||
|
|
||||||
def _write(self, control):
|
def _write(self, control):
|
||||||
|
|
@ -598,13 +597,13 @@ class HeteroKeyControl(Gtk.HBox, Control):
|
||||||
_write_async(self.sbox.setting, new_state, self.sbox)
|
_write_async(self.sbox.setting, new_state, self.sbox)
|
||||||
|
|
||||||
|
|
||||||
_allowables_icons = {True: "changes-allow", False: "changes-prevent", _SENSITIVITY_IGNORE: "dialog-error"}
|
_allowables_icons = {True: "changes-allow", False: "changes-prevent", settings.SENSITIVITY_IGNORE: "dialog-error"}
|
||||||
_allowables_tooltips = {
|
_allowables_tooltips = {
|
||||||
True: _("Changes allowed"),
|
True: _("Changes allowed"),
|
||||||
False: _("No changes allowed"),
|
False: _("No changes allowed"),
|
||||||
_SENSITIVITY_IGNORE: _("Ignore this setting"),
|
settings.SENSITIVITY_IGNORE: _("Ignore this setting"),
|
||||||
}
|
}
|
||||||
_next_allowable = {True: False, False: _SENSITIVITY_IGNORE, _SENSITIVITY_IGNORE: True}
|
_next_allowable = {True: False, False: settings.SENSITIVITY_IGNORE, settings.SENSITIVITY_IGNORE: True}
|
||||||
_icons_allowables = {v: k for k, v in _allowables_icons.items()}
|
_icons_allowables = {v: k for k, v in _allowables_icons.items()}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -618,7 +617,7 @@ def _change_click(button, sbox):
|
||||||
_change_icon(new_allowed, icon)
|
_change_icon(new_allowed, icon)
|
||||||
if sbox.setting._device.persister: # remember the new setting sensitivity
|
if sbox.setting._device.persister: # remember the new setting sensitivity
|
||||||
sbox.setting._device.persister.set_sensitivity(sbox.setting.name, new_allowed)
|
sbox.setting._device.persister.set_sensitivity(sbox.setting.name, new_allowed)
|
||||||
if allowed == _SENSITIVITY_IGNORE: # update setting if it was being ignored
|
if allowed == settings.SENSITIVITY_IGNORE: # update setting if it was being ignored
|
||||||
setting = next((s for s in sbox.setting._device.settings if s.name == sbox.setting.name), None)
|
setting = next((s for s in sbox.setting._device.settings if s.name == sbox.setting.name), None)
|
||||||
if setting:
|
if setting:
|
||||||
persisted = sbox.setting._device.persister.get(setting.name) if sbox.setting._device.persister else None
|
persisted = sbox.setting._device.persister.get(setting.name) if sbox.setting._device.persister else None
|
||||||
|
|
@ -660,21 +659,21 @@ def _create_sbox(s, device):
|
||||||
change.set_sensitive(True)
|
change.set_sensitive(True)
|
||||||
change.connect("clicked", _change_click, sbox)
|
change.connect("clicked", _change_click, sbox)
|
||||||
|
|
||||||
if s.kind == _SETTING_KIND.toggle:
|
if s.kind == settings.KIND.toggle:
|
||||||
control = ToggleControl(sbox)
|
control = ToggleControl(sbox)
|
||||||
elif s.kind == _SETTING_KIND.range:
|
elif s.kind == settings.KIND.range:
|
||||||
control = SliderControl(sbox)
|
control = SliderControl(sbox)
|
||||||
elif s.kind == _SETTING_KIND.choice:
|
elif s.kind == settings.KIND.choice:
|
||||||
control = _create_choice_control(sbox)
|
control = _create_choice_control(sbox)
|
||||||
elif s.kind == _SETTING_KIND.map_choice:
|
elif s.kind == settings.KIND.map_choice:
|
||||||
control = MapChoiceControl(sbox)
|
control = MapChoiceControl(sbox)
|
||||||
elif s.kind == _SETTING_KIND.multiple_toggle:
|
elif s.kind == settings.KIND.multiple_toggle:
|
||||||
control = MultipleToggleControl(sbox, change)
|
control = MultipleToggleControl(sbox, change)
|
||||||
elif s.kind == _SETTING_KIND.multiple_range:
|
elif s.kind == settings.KIND.multiple_range:
|
||||||
control = MultipleRangeControl(sbox, change)
|
control = MultipleRangeControl(sbox, change)
|
||||||
elif s.kind == _SETTING_KIND.packed_range:
|
elif s.kind == settings.KIND.packed_range:
|
||||||
control = PackedRangeControl(sbox, change)
|
control = PackedRangeControl(sbox, change)
|
||||||
elif s.kind == _SETTING_KIND.hetero:
|
elif s.kind == settings.KIND.hetero:
|
||||||
control = HeteroKeyControl(sbox, change)
|
control = HeteroKeyControl(sbox, change)
|
||||||
else:
|
else:
|
||||||
if logger.isEnabledFor(logging.WARNING):
|
if logger.isEnabledFor(logging.WARNING):
|
||||||
|
|
@ -691,7 +690,6 @@ def _create_sbox(s, device):
|
||||||
|
|
||||||
|
|
||||||
def _update_setting_item(sbox, value, is_online=True, sensitive=True, nullOK=False):
|
def _update_setting_item(sbox, value, is_online=True, sensitive=True, nullOK=False):
|
||||||
# sbox._spinner.set_visible(False) # don't repack item box
|
|
||||||
sbox._spinner.stop()
|
sbox._spinner.stop()
|
||||||
sensitive = sbox._change_icon._allowed if sensitive is None else sensitive
|
sensitive = sbox._change_icon._allowed if sensitive is None else sensitive
|
||||||
if value is None and not nullOK:
|
if value is None and not nullOK:
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,8 @@ from logitech_receiver.common import NamedInt
|
||||||
from logitech_receiver.common import NamedInts
|
from logitech_receiver.common import NamedInts
|
||||||
from logitech_receiver.common import UnsortedNamedInts
|
from logitech_receiver.common import UnsortedNamedInts
|
||||||
from logitech_receiver.settings import KIND as _SKIND
|
from logitech_receiver.settings import KIND as _SKIND
|
||||||
from logitech_receiver.settings import Setting as _Setting
|
from logitech_receiver.settings import Setting
|
||||||
from logitech_receiver.settings_templates import SETTINGS as _SETTINGS
|
from logitech_receiver.settings_templates import SETTINGS
|
||||||
|
|
||||||
from solaar.i18n import _
|
from solaar.i18n import _
|
||||||
from solaar.ui import rule_actions
|
from solaar.ui import rule_actions
|
||||||
|
|
@ -931,7 +931,7 @@ class DeviceInfo:
|
||||||
serial: str = ""
|
serial: str = ""
|
||||||
unitId: str = ""
|
unitId: str = ""
|
||||||
codename: str = ""
|
codename: str = ""
|
||||||
settings: Dict[str, _Setting] = field(default_factory=dict)
|
settings: Dict[str, Setting] = field(default_factory=dict)
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.serial is None or self.serial == "?":
|
if self.serial is None or self.serial == "?":
|
||||||
|
|
@ -1281,7 +1281,7 @@ class SetValueControl(Gtk.HBox):
|
||||||
|
|
||||||
def _all_settings():
|
def _all_settings():
|
||||||
settings = {}
|
settings = {}
|
||||||
for s in sorted(_SETTINGS, key=lambda setting: setting.label):
|
for s in sorted(SETTINGS, key=lambda setting: setting.label):
|
||||||
if s.name not in settings:
|
if s.name not in settings:
|
||||||
settings[s.name] = [s]
|
settings[s.name] = [s]
|
||||||
else:
|
else:
|
||||||
|
|
@ -1326,7 +1326,6 @@ class _DeviceUI:
|
||||||
self.device_field.set_value("")
|
self.device_field.set_value("")
|
||||||
self.device_field.set_valign(Gtk.Align.CENTER)
|
self.device_field.set_valign(Gtk.Align.CENTER)
|
||||||
self.device_field.set_size_request(400, 0)
|
self.device_field.set_size_request(400, 0)
|
||||||
# self.device_field.connect('changed', self._changed_device)
|
|
||||||
self.device_field.connect("changed", self._on_update)
|
self.device_field.connect("changed", self._on_update)
|
||||||
self.widgets[self.device_field] = (1, 1, 1, 1)
|
self.widgets[self.device_field] = (1, 1, 1, 1)
|
||||||
|
|
||||||
|
|
@ -1477,9 +1476,9 @@ class _SettingWithValueUI:
|
||||||
(including the extra value if it exists) and the second element is the extra value to be pinned to
|
(including the extra value if it exists) and the second element is the extra value to be pinned to
|
||||||
the start of the list (or `None` if there is no extra value).
|
the start of the list (or `None` if there is no extra value).
|
||||||
"""
|
"""
|
||||||
if isinstance(setting, _Setting):
|
if isinstance(setting, Setting):
|
||||||
setting = type(setting)
|
setting = type(setting)
|
||||||
if isinstance(setting, type) and issubclass(setting, _Setting):
|
if isinstance(setting, type) and issubclass(setting, Setting):
|
||||||
choices = UnsortedNamedInts()
|
choices = UnsortedNamedInts()
|
||||||
universe = getattr(setting, "choices_universe", None)
|
universe = getattr(setting, "choices_universe", None)
|
||||||
if universe:
|
if universe:
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import logging
|
||||||
from solaar import NAME
|
from solaar import NAME
|
||||||
from solaar.i18n import _
|
from solaar.i18n import _
|
||||||
|
|
||||||
from . import icons as _icons
|
from . import icons
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -66,14 +66,6 @@ if available:
|
||||||
_notifications.clear()
|
_notifications.clear()
|
||||||
Notify.uninit()
|
Notify.uninit()
|
||||||
|
|
||||||
# def toggle(action):
|
|
||||||
# if action.get_active():
|
|
||||||
# init()
|
|
||||||
# else:
|
|
||||||
# uninit()
|
|
||||||
# action.set_sensitive(available)
|
|
||||||
# return action.get_active()
|
|
||||||
|
|
||||||
def alert(reason, icon=None):
|
def alert(reason, icon=None):
|
||||||
assert reason
|
assert reason
|
||||||
|
|
||||||
|
|
@ -84,15 +76,13 @@ if available:
|
||||||
|
|
||||||
# we need to use the filename here because the notifications daemon
|
# we need to use the filename here because the notifications daemon
|
||||||
# is an external application that does not know about our icon sets
|
# is an external application that does not know about our icon sets
|
||||||
icon_file = _icons.icon_file(NAME.lower()) if icon is None else _icons.icon_file(icon)
|
icon_file = icons.icon_file(NAME.lower()) if icon is None else icons.icon_file(icon)
|
||||||
|
|
||||||
n.update(NAME.lower(), reason, icon_file)
|
n.update(NAME.lower(), reason, icon_file)
|
||||||
n.set_urgency(Notify.Urgency.NORMAL)
|
n.set_urgency(Notify.Urgency.NORMAL)
|
||||||
n.set_hint("desktop-entry", GLib.Variant("s", NAME.lower()))
|
n.set_hint("desktop-entry", GLib.Variant("s", NAME.lower()))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# if logger.isEnabledFor(logging.DEBUG):
|
|
||||||
# logger.debug("showing %s", n)
|
|
||||||
n.show()
|
n.show()
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("showing %s", n)
|
logger.exception("showing %s", n)
|
||||||
|
|
@ -113,16 +103,10 @@ if available:
|
||||||
message = reason
|
message = reason
|
||||||
else:
|
else:
|
||||||
message = _("unspecified reason")
|
message = _("unspecified reason")
|
||||||
# elif dev.status is None:
|
|
||||||
# message = _("unpaired")
|
|
||||||
# elif bool(dev.status):
|
|
||||||
# message = dev.status_string() or _("connected")
|
|
||||||
# else:
|
|
||||||
# message = _("offline")
|
|
||||||
|
|
||||||
# we need to use the filename here because the notifications daemon
|
# we need to use the filename here because the notifications daemon
|
||||||
# is an external application that does not know about our icon sets
|
# is an external application that does not know about our icon sets
|
||||||
icon_file = _icons.device_icon_file(dev.name, dev.kind) if icon is None else _icons.icon_file(icon)
|
icon_file = icons.device_icon_file(dev.name, dev.kind) if icon is None else icons.icon_file(icon)
|
||||||
|
|
||||||
n.update(summary, message, icon_file)
|
n.update(summary, message, icon_file)
|
||||||
n.set_urgency(Notify.Urgency.NORMAL)
|
n.set_urgency(Notify.Urgency.NORMAL)
|
||||||
|
|
@ -131,8 +115,6 @@ if available:
|
||||||
n.set_hint("value", GLib.Variant("i", progress))
|
n.set_hint("value", GLib.Variant("i", progress))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# if logger.isEnabledFor(logging.DEBUG):
|
|
||||||
# logger.debug("showing %s", n)
|
|
||||||
n.show()
|
n.show()
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("showing %s", n)
|
logger.exception("showing %s", n)
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,12 @@ import logging
|
||||||
|
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from logitech_receiver import hidpp10_constants as _hidpp10_constants
|
from logitech_receiver import hidpp10_constants
|
||||||
|
|
||||||
from solaar.i18n import _
|
from solaar.i18n import _
|
||||||
from solaar.i18n import ngettext
|
from solaar.i18n import ngettext
|
||||||
|
|
||||||
from . import icons as _icons
|
from . import icons
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -108,7 +108,7 @@ def _check_lock_state(assistant, receiver, count):
|
||||||
return True
|
return True
|
||||||
elif receiver.pairing.discovering and receiver.pairing.device_address and receiver.pairing.device_name:
|
elif receiver.pairing.discovering and receiver.pairing.device_address and receiver.pairing.device_name:
|
||||||
add = receiver.pairing.device_address
|
add = receiver.pairing.device_address
|
||||||
ent = 20 if receiver.pairing.device_kind == _hidpp10_constants.DEVICE_KIND.keyboard else 10
|
ent = 20 if receiver.pairing.device_kind == hidpp10_constants.DEVICE_KIND.keyboard else 10
|
||||||
if receiver.pair_device(address=add, authentication=receiver.pairing.device_authentication, entropy=ent):
|
if receiver.pair_device(address=add, authentication=receiver.pairing.device_authentication, entropy=ent):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
|
@ -200,8 +200,8 @@ def _create_success_page(assistant, device):
|
||||||
header = Gtk.Label(label=_("Found a new device:"))
|
header = Gtk.Label(label=_("Found a new device:"))
|
||||||
page.pack_start(header, False, False, 0)
|
page.pack_start(header, False, False, 0)
|
||||||
device_icon = Gtk.Image()
|
device_icon = Gtk.Image()
|
||||||
icon_name = _icons.device_icon_name(device.name, device.kind)
|
icon_name = icons.device_icon_name(device.name, device.kind)
|
||||||
device_icon.set_from_icon_name(icon_name, _icons.LARGE_SIZE)
|
device_icon.set_from_icon_name(icon_name, icons.LARGE_SIZE)
|
||||||
page.pack_start(device_icon, True, True, 0)
|
page.pack_start(device_icon, True, True, 0)
|
||||||
device_label = Gtk.Label()
|
device_label = Gtk.Label()
|
||||||
device_label.set_markup(f"<b>{device.name}</b>")
|
device_label.set_markup(f"<b>{device.name}</b>")
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,12 @@
|
||||||
from shlex import quote as shlex_quote
|
from shlex import quote as shlex_quote
|
||||||
|
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from logitech_receiver import diversion as _DIV
|
from logitech_receiver import diversion
|
||||||
from logitech_receiver.diversion import CLICK
|
from logitech_receiver.diversion import CLICK
|
||||||
from logitech_receiver.diversion import DEPRESS
|
from logitech_receiver.diversion import DEPRESS
|
||||||
from logitech_receiver.diversion import RELEASE
|
from logitech_receiver.diversion import RELEASE
|
||||||
from logitech_receiver.diversion import XK_KEYS as _XK_KEYS
|
from logitech_receiver.diversion import XK_KEYS
|
||||||
from logitech_receiver.diversion import buttons as _buttons
|
from logitech_receiver.diversion import buttons
|
||||||
|
|
||||||
from solaar.i18n import _
|
from solaar.i18n import _
|
||||||
from solaar.ui.rule_base import CompletionEntry
|
from solaar.ui.rule_base import CompletionEntry
|
||||||
|
|
@ -30,7 +30,7 @@ from solaar.ui.rule_base import RuleComponentUI
|
||||||
|
|
||||||
|
|
||||||
class ActionUI(RuleComponentUI):
|
class ActionUI(RuleComponentUI):
|
||||||
CLASS = _DIV.Action
|
CLASS = diversion.Action
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def icon_name(cls):
|
def icon_name(cls):
|
||||||
|
|
@ -38,8 +38,8 @@ class ActionUI(RuleComponentUI):
|
||||||
|
|
||||||
|
|
||||||
class KeyPressUI(ActionUI):
|
class KeyPressUI(ActionUI):
|
||||||
CLASS = _DIV.KeyPress
|
CLASS = diversion.KeyPress
|
||||||
KEY_NAMES = [k[3:] if k.startswith("XK_") else k for k, v in _XK_KEYS.items() if isinstance(v, int)]
|
KEY_NAMES = [k[3:] if k.startswith("XK_") else k for k, v in XK_KEYS.items() if isinstance(v, int)]
|
||||||
|
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
@ -107,7 +107,6 @@ class KeyPressUI(ActionUI):
|
||||||
self._create_field()
|
self._create_field()
|
||||||
self._create_del_btn()
|
self._create_del_btn()
|
||||||
|
|
||||||
# self.widgets[self.add_btn] = (n + 1, 0, 1, 1)
|
|
||||||
self.widgets[self.add_btn] = (n, 1, 1, 1)
|
self.widgets[self.add_btn] = (n, 1, 1, 1)
|
||||||
super().show(component, editable)
|
super().show(component, editable)
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
|
|
@ -137,7 +136,7 @@ class KeyPressUI(ActionUI):
|
||||||
|
|
||||||
|
|
||||||
class MouseScrollUI(ActionUI):
|
class MouseScrollUI(ActionUI):
|
||||||
CLASS = _DIV.MouseScroll
|
CLASS = diversion.MouseScroll
|
||||||
MIN_VALUE = -2000
|
MIN_VALUE = -2000
|
||||||
MAX_VALUE = 2000
|
MAX_VALUE = 2000
|
||||||
|
|
||||||
|
|
@ -191,10 +190,10 @@ class MouseScrollUI(ActionUI):
|
||||||
|
|
||||||
|
|
||||||
class MouseClickUI(ActionUI):
|
class MouseClickUI(ActionUI):
|
||||||
CLASS = _DIV.MouseClick
|
CLASS = diversion.MouseClick
|
||||||
MIN_VALUE = 1
|
MIN_VALUE = 1
|
||||||
MAX_VALUE = 9
|
MAX_VALUE = 9
|
||||||
BUTTONS = list(_buttons.keys())
|
BUTTONS = list(buttons.keys())
|
||||||
ACTIONS = [CLICK, DEPRESS, RELEASE]
|
ACTIONS = [CLICK, DEPRESS, RELEASE]
|
||||||
|
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
|
|
@ -249,7 +248,7 @@ class MouseClickUI(ActionUI):
|
||||||
|
|
||||||
|
|
||||||
class ExecuteUI(ActionUI):
|
class ExecuteUI(ActionUI):
|
||||||
CLASS = _DIV.Execute
|
CLASS = diversion.Execute
|
||||||
|
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
from contextlib import contextmanager as contextlib_contextmanager
|
from contextlib import contextmanager as contextlib_contextmanager
|
||||||
|
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from logitech_receiver import diversion as _DIV
|
from logitech_receiver import diversion
|
||||||
|
|
||||||
|
|
||||||
def norm(s):
|
def norm(s):
|
||||||
|
|
@ -47,7 +47,7 @@ class CompletionEntry(Gtk.Entry):
|
||||||
|
|
||||||
|
|
||||||
class RuleComponentUI:
|
class RuleComponentUI:
|
||||||
CLASS = _DIV.RuleComponent
|
CLASS = diversion.RuleComponent
|
||||||
|
|
||||||
def __init__(self, panel, on_update=None):
|
def __init__(self, panel, on_update=None):
|
||||||
self.panel = panel
|
self.panel = panel
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from logitech_receiver import diversion as _DIV
|
from logitech_receiver import diversion
|
||||||
from logitech_receiver.diversion import Key as _Key
|
from logitech_receiver.diversion import Key
|
||||||
from logitech_receiver.hidpp20 import FEATURE as _ALL_FEATURES
|
from logitech_receiver.hidpp20 import FEATURE
|
||||||
from logitech_receiver.special_keys import CONTROL as _CONTROL
|
from logitech_receiver.special_keys import CONTROL
|
||||||
|
|
||||||
from solaar.i18n import _
|
from solaar.i18n import _
|
||||||
from solaar.ui.rule_base import CompletionEntry
|
from solaar.ui.rule_base import CompletionEntry
|
||||||
|
|
@ -27,7 +27,7 @@ from solaar.ui.rule_base import RuleComponentUI
|
||||||
|
|
||||||
|
|
||||||
class ConditionUI(RuleComponentUI):
|
class ConditionUI(RuleComponentUI):
|
||||||
CLASS = _DIV.Condition
|
CLASS = diversion.Condition
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def icon_name(cls):
|
def icon_name(cls):
|
||||||
|
|
@ -35,7 +35,7 @@ class ConditionUI(RuleComponentUI):
|
||||||
|
|
||||||
|
|
||||||
class ProcessUI(ConditionUI):
|
class ProcessUI(ConditionUI):
|
||||||
CLASS = _DIV.Process
|
CLASS = diversion.Process
|
||||||
|
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
@ -65,7 +65,7 @@ class ProcessUI(ConditionUI):
|
||||||
|
|
||||||
|
|
||||||
class MouseProcessUI(ConditionUI):
|
class MouseProcessUI(ConditionUI):
|
||||||
CLASS = _DIV.MouseProcess
|
CLASS = diversion.MouseProcess
|
||||||
|
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
@ -95,17 +95,17 @@ class MouseProcessUI(ConditionUI):
|
||||||
|
|
||||||
|
|
||||||
class FeatureUI(ConditionUI):
|
class FeatureUI(ConditionUI):
|
||||||
CLASS = _DIV.Feature
|
CLASS = diversion.Feature
|
||||||
FEATURES_WITH_DIVERSION = [
|
FEATURES_WITH_DIVERSION = [
|
||||||
str(_ALL_FEATURES.CROWN),
|
str(FEATURE.CROWN),
|
||||||
str(_ALL_FEATURES.THUMB_WHEEL),
|
str(FEATURE.THUMB_WHEEL),
|
||||||
str(_ALL_FEATURES.LOWRES_WHEEL),
|
str(FEATURE.LOWRES_WHEEL),
|
||||||
str(_ALL_FEATURES.HIRES_WHEEL),
|
str(FEATURE.HIRES_WHEEL),
|
||||||
str(_ALL_FEATURES.GESTURE_2),
|
str(FEATURE.GESTURE_2),
|
||||||
str(_ALL_FEATURES.REPROG_CONTROLS_V4),
|
str(FEATURE.REPROG_CONTROLS_V4),
|
||||||
str(_ALL_FEATURES.GKEY),
|
str(FEATURE.GKEY),
|
||||||
str(_ALL_FEATURES.MKEYS),
|
str(FEATURE.MKEYS),
|
||||||
str(_ALL_FEATURES.MR),
|
str(FEATURE.MR),
|
||||||
]
|
]
|
||||||
|
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
|
|
@ -118,10 +118,9 @@ class FeatureUI(ConditionUI):
|
||||||
for feature in self.FEATURES_WITH_DIVERSION:
|
for feature in self.FEATURES_WITH_DIVERSION:
|
||||||
self.field.append(feature, feature)
|
self.field.append(feature, feature)
|
||||||
self.field.set_valign(Gtk.Align.CENTER)
|
self.field.set_valign(Gtk.Align.CENTER)
|
||||||
# self.field.set_vexpand(True)
|
|
||||||
self.field.set_size_request(600, 0)
|
self.field.set_size_request(600, 0)
|
||||||
self.field.connect("changed", self._on_update)
|
self.field.connect("changed", self._on_update)
|
||||||
all_features = [str(f) for f in _ALL_FEATURES]
|
all_features = [str(f) for f in FEATURE]
|
||||||
CompletionEntry.add_completion_to_entry(self.field.get_child(), all_features)
|
CompletionEntry.add_completion_to_entry(self.field.get_child(), all_features)
|
||||||
self.widgets[self.field] = (0, 1, 1, 1)
|
self.widgets[self.field] = (0, 1, 1, 1)
|
||||||
|
|
||||||
|
|
@ -151,7 +150,7 @@ class FeatureUI(ConditionUI):
|
||||||
|
|
||||||
|
|
||||||
class ReportUI(ConditionUI):
|
class ReportUI(ConditionUI):
|
||||||
CLASS = _DIV.Report
|
CLASS = diversion.Report
|
||||||
MIN_VALUE = -1 # for invalid values
|
MIN_VALUE = -1 # for invalid values
|
||||||
MAX_VALUE = 15
|
MAX_VALUE = 15
|
||||||
|
|
||||||
|
|
@ -164,7 +163,6 @@ class ReportUI(ConditionUI):
|
||||||
self.field.set_halign(Gtk.Align.CENTER)
|
self.field.set_halign(Gtk.Align.CENTER)
|
||||||
self.field.set_valign(Gtk.Align.CENTER)
|
self.field.set_valign(Gtk.Align.CENTER)
|
||||||
self.field.set_hexpand(True)
|
self.field.set_hexpand(True)
|
||||||
# self.field.set_vexpand(True)
|
|
||||||
self.field.connect("changed", self._on_update)
|
self.field.connect("changed", self._on_update)
|
||||||
self.widgets[self.field] = (0, 1, 1, 1)
|
self.widgets[self.field] = (0, 1, 1, 1)
|
||||||
|
|
||||||
|
|
@ -186,7 +184,7 @@ class ReportUI(ConditionUI):
|
||||||
|
|
||||||
|
|
||||||
class ModifiersUI(ConditionUI):
|
class ModifiersUI(ConditionUI):
|
||||||
CLASS = _DIV.Modifiers
|
CLASS = diversion.Modifiers
|
||||||
|
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
@ -195,7 +193,7 @@ class ModifiersUI(ConditionUI):
|
||||||
self.widgets[self.label] = (0, 0, 5, 1)
|
self.widgets[self.label] = (0, 0, 5, 1)
|
||||||
self.labels = {}
|
self.labels = {}
|
||||||
self.switches = {}
|
self.switches = {}
|
||||||
for i, m in enumerate(_DIV.MODIFIERS):
|
for i, m in enumerate(diversion.MODIFIERS):
|
||||||
switch = Gtk.Switch(halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True)
|
switch = Gtk.Switch(halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True)
|
||||||
label = Gtk.Label(label=m, halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True)
|
label = Gtk.Label(label=m, halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True)
|
||||||
self.widgets[label] = (i, 1, 1, 1)
|
self.widgets[label] = (i, 1, 1, 1)
|
||||||
|
|
@ -207,7 +205,7 @@ class ModifiersUI(ConditionUI):
|
||||||
def show(self, component, editable):
|
def show(self, component, editable):
|
||||||
super().show(component, editable)
|
super().show(component, editable)
|
||||||
with self.ignore_changes():
|
with self.ignore_changes():
|
||||||
for m in _DIV.MODIFIERS:
|
for m in diversion.MODIFIERS:
|
||||||
self.switches[m].set_active(m in component.modifiers)
|
self.switches[m].set_active(m in component.modifiers)
|
||||||
|
|
||||||
def collect_value(self):
|
def collect_value(self):
|
||||||
|
|
@ -223,8 +221,8 @@ class ModifiersUI(ConditionUI):
|
||||||
|
|
||||||
|
|
||||||
class KeyUI(ConditionUI):
|
class KeyUI(ConditionUI):
|
||||||
CLASS = _DIV.Key
|
CLASS = diversion.Key
|
||||||
KEY_NAMES = map(str, _CONTROL)
|
KEY_NAMES = map(str, CONTROL)
|
||||||
|
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
@ -241,23 +239,23 @@ class KeyUI(ConditionUI):
|
||||||
self.key_field.connect("changed", self._on_update)
|
self.key_field.connect("changed", self._on_update)
|
||||||
self.widgets[self.key_field] = (0, 1, 2, 1)
|
self.widgets[self.key_field] = (0, 1, 2, 1)
|
||||||
self.action_pressed_radio = Gtk.RadioButton.new_with_label_from_widget(None, _("Key down"))
|
self.action_pressed_radio = Gtk.RadioButton.new_with_label_from_widget(None, _("Key down"))
|
||||||
self.action_pressed_radio.connect("toggled", self._on_update, _Key.DOWN)
|
self.action_pressed_radio.connect("toggled", self._on_update, Key.DOWN)
|
||||||
self.widgets[self.action_pressed_radio] = (2, 1, 1, 1)
|
self.widgets[self.action_pressed_radio] = (2, 1, 1, 1)
|
||||||
self.action_released_radio = Gtk.RadioButton.new_with_label_from_widget(self.action_pressed_radio, _("Key up"))
|
self.action_released_radio = Gtk.RadioButton.new_with_label_from_widget(self.action_pressed_radio, _("Key up"))
|
||||||
self.action_released_radio.connect("toggled", self._on_update, _Key.UP)
|
self.action_released_radio.connect("toggled", self._on_update, Key.UP)
|
||||||
self.widgets[self.action_released_radio] = (3, 1, 1, 1)
|
self.widgets[self.action_released_radio] = (3, 1, 1, 1)
|
||||||
|
|
||||||
def show(self, component, editable):
|
def show(self, component, editable):
|
||||||
super().show(component, editable)
|
super().show(component, editable)
|
||||||
with self.ignore_changes():
|
with self.ignore_changes():
|
||||||
self.key_field.set_text(str(component.key) if self.component.key else "")
|
self.key_field.set_text(str(component.key) if self.component.key else "")
|
||||||
if not component.action or component.action == _Key.DOWN:
|
if not component.action or component.action == Key.DOWN:
|
||||||
self.action_pressed_radio.set_active(True)
|
self.action_pressed_radio.set_active(True)
|
||||||
else:
|
else:
|
||||||
self.action_released_radio.set_active(True)
|
self.action_released_radio.set_active(True)
|
||||||
|
|
||||||
def collect_value(self):
|
def collect_value(self):
|
||||||
action = _Key.UP if self.action_released_radio.get_active() else _Key.DOWN
|
action = Key.UP if self.action_released_radio.get_active() else Key.DOWN
|
||||||
return [self.key_field.get_text(), action]
|
return [self.key_field.get_text(), action]
|
||||||
|
|
||||||
def _on_update(self, *args):
|
def _on_update(self, *args):
|
||||||
|
|
@ -275,8 +273,8 @@ class KeyUI(ConditionUI):
|
||||||
|
|
||||||
|
|
||||||
class KeyIsDownUI(ConditionUI):
|
class KeyIsDownUI(ConditionUI):
|
||||||
CLASS = _DIV.KeyIsDown
|
CLASS = diversion.KeyIsDown
|
||||||
KEY_NAMES = map(str, _CONTROL)
|
KEY_NAMES = map(str, CONTROL)
|
||||||
|
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
@ -316,7 +314,7 @@ class KeyIsDownUI(ConditionUI):
|
||||||
|
|
||||||
|
|
||||||
class TestUI(ConditionUI):
|
class TestUI(ConditionUI):
|
||||||
CLASS = _DIV.Test
|
CLASS = diversion.Test
|
||||||
|
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
@ -330,13 +328,13 @@ class TestUI(ConditionUI):
|
||||||
|
|
||||||
self.test = Gtk.ComboBoxText.new_with_entry()
|
self.test = Gtk.ComboBoxText.new_with_entry()
|
||||||
self.test.append("", "")
|
self.test.append("", "")
|
||||||
for t in _DIV.TESTS:
|
for t in diversion.TESTS:
|
||||||
self.test.append(t, t)
|
self.test.append(t, t)
|
||||||
self.test.set_halign(Gtk.Align.END)
|
self.test.set_halign(Gtk.Align.END)
|
||||||
self.test.set_valign(Gtk.Align.CENTER)
|
self.test.set_valign(Gtk.Align.CENTER)
|
||||||
self.test.set_hexpand(False)
|
self.test.set_hexpand(False)
|
||||||
self.test.set_size_request(300, 0)
|
self.test.set_size_request(300, 0)
|
||||||
CompletionEntry.add_completion_to_entry(self.test.get_child(), _DIV.TESTS)
|
CompletionEntry.add_completion_to_entry(self.test.get_child(), diversion.TESTS)
|
||||||
self.test.connect("changed", self._on_update)
|
self.test.connect("changed", self._on_update)
|
||||||
self.widgets[self.test] = (1, 1, 1, 1)
|
self.widgets[self.test] = (1, 1, 1, 1)
|
||||||
|
|
||||||
|
|
@ -350,7 +348,7 @@ class TestUI(ConditionUI):
|
||||||
with self.ignore_changes():
|
with self.ignore_changes():
|
||||||
self.test.set_active_id(component.test)
|
self.test.set_active_id(component.test)
|
||||||
self.parameter.set_text(str(component.parameter) if component.parameter is not None else "")
|
self.parameter.set_text(str(component.parameter) if component.parameter is not None else "")
|
||||||
if component.test not in _DIV.TESTS:
|
if component.test not in diversion.TESTS:
|
||||||
self.test.get_child().set_text(component.test)
|
self.test.get_child().set_text(component.test)
|
||||||
self._change_status_icon()
|
self._change_status_icon()
|
||||||
|
|
||||||
|
|
@ -367,7 +365,7 @@ class TestUI(ConditionUI):
|
||||||
self._change_status_icon()
|
self._change_status_icon()
|
||||||
|
|
||||||
def _change_status_icon(self):
|
def _change_status_icon(self):
|
||||||
icon = "dialog-warning" if (self.test.get_active_text() or "").strip() not in _DIV.TESTS else ""
|
icon = "dialog-warning" if (self.test.get_active_text() or "").strip() not in diversion.TESTS else ""
|
||||||
self.test.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon)
|
self.test.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -395,7 +393,7 @@ class TestBytesMode:
|
||||||
|
|
||||||
|
|
||||||
class TestBytesUI(ConditionUI):
|
class TestBytesUI(ConditionUI):
|
||||||
CLASS = _DIV.TestBytes
|
CLASS = diversion.TestBytes
|
||||||
|
|
||||||
_common_elements = [
|
_common_elements = [
|
||||||
TestBytesElement("begin", _("begin (inclusive)"), 0, 16),
|
TestBytesElement("begin", _("begin (inclusive)"), 0, 16),
|
||||||
|
|
@ -508,7 +506,7 @@ class TestBytesUI(ConditionUI):
|
||||||
|
|
||||||
|
|
||||||
class MouseGestureUI(ConditionUI):
|
class MouseGestureUI(ConditionUI):
|
||||||
CLASS = _DIV.MouseGesture
|
CLASS = diversion.MouseGesture
|
||||||
MOUSE_GESTURE_NAMES = [
|
MOUSE_GESTURE_NAMES = [
|
||||||
"Mouse Up",
|
"Mouse Up",
|
||||||
"Mouse Down",
|
"Mouse Down",
|
||||||
|
|
@ -519,7 +517,7 @@ class MouseGestureUI(ConditionUI):
|
||||||
"Mouse Down-left",
|
"Mouse Down-left",
|
||||||
"Mouse Down-right",
|
"Mouse Down-right",
|
||||||
]
|
]
|
||||||
MOVE_NAMES = list(map(str, _CONTROL)) + MOUSE_GESTURE_NAMES
|
MOVE_NAMES = list(map(str, CONTROL)) + MOUSE_GESTURE_NAMES
|
||||||
|
|
||||||
def create_widgets(self):
|
def create_widgets(self):
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from time import time as _timestamp
|
from time import time
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
|
|
@ -31,11 +31,10 @@ import solaar.gtk as gtk
|
||||||
from solaar import NAME
|
from solaar import NAME
|
||||||
from solaar.i18n import _
|
from solaar.i18n import _
|
||||||
|
|
||||||
from . import icons as _icons
|
from . import about
|
||||||
from .about import show_window as _show_about_window
|
from . import action
|
||||||
from .action import make_image_menu_item
|
from . import icons
|
||||||
from .window import popup as _window_popup
|
from . import window
|
||||||
from .window import toggle as _window_toggle
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -52,8 +51,8 @@ def _create_menu(quit_handler):
|
||||||
menu.append(no_receiver)
|
menu.append(no_receiver)
|
||||||
menu.append(Gtk.SeparatorMenuItem.new())
|
menu.append(Gtk.SeparatorMenuItem.new())
|
||||||
|
|
||||||
menu.append(make_image_menu_item(_("About %s") % NAME, "help-about", _show_about_window))
|
menu.append(action.make_image_menu_item(_("About %s") % NAME, "help-about", about.show_window))
|
||||||
menu.append(make_image_menu_item(_("Quit %s") % NAME, "application-exit", quit_handler))
|
menu.append(action.make_image_menu_item(_("Quit %s") % NAME, "application-exit", quit_handler))
|
||||||
|
|
||||||
menu.show_all()
|
menu.show_all()
|
||||||
return menu
|
return menu
|
||||||
|
|
@ -78,7 +77,7 @@ def _scroll(tray_icon, event, direction=None):
|
||||||
|
|
||||||
# scroll events come way too fast (at least 5-6 at once) so take a little break between them
|
# scroll events come way too fast (at least 5-6 at once) so take a little break between them
|
||||||
global _last_scroll
|
global _last_scroll
|
||||||
now = now or _timestamp()
|
now = now or time()
|
||||||
if now - _last_scroll < 0.33: # seconds
|
if now - _last_scroll < 0.33: # seconds
|
||||||
return
|
return
|
||||||
_last_scroll = now
|
_last_scroll = now
|
||||||
|
|
@ -162,13 +161,13 @@ try:
|
||||||
return icon_info.get_filename() if icon_info else icon_name
|
return icon_info.get_filename() if icon_info else icon_name
|
||||||
|
|
||||||
def _create(menu):
|
def _create(menu):
|
||||||
_icons._init_icon_paths()
|
icons._init_icon_paths()
|
||||||
ind = AppIndicator3.Indicator.new(
|
ind = AppIndicator3.Indicator.new(
|
||||||
"indicator-solaar", _icon_file(_icons.TRAY_INIT), AppIndicator3.IndicatorCategory.HARDWARE
|
"indicator-solaar", _icon_file(icons.TRAY_INIT), AppIndicator3.IndicatorCategory.HARDWARE
|
||||||
)
|
)
|
||||||
ind.set_title(NAME)
|
ind.set_title(NAME)
|
||||||
ind.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
|
ind.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
|
||||||
# ind.set_attention_icon_full(_icon_file(_icons.TRAY_ATTENTION), '') # works poorly for XFCE 16
|
# ind.set_attention_icon_full(_icon_file(icons.TRAY_ATTENTION), '') # works poorly for XFCE 16
|
||||||
# ind.set_label(NAME.lower(), NAME.lower())
|
# ind.set_label(NAME.lower(), NAME.lower())
|
||||||
|
|
||||||
ind.set_menu(menu)
|
ind.set_menu(menu)
|
||||||
|
|
@ -187,21 +186,21 @@ try:
|
||||||
_ignore, _ignore, name, device = _picked_device
|
_ignore, _ignore, name, device = _picked_device
|
||||||
battery_level = device.battery_info.level if device.battery_info is not None else None
|
battery_level = device.battery_info.level if device.battery_info is not None else None
|
||||||
battery_charging = device.battery_info.charging() if device.battery_info is not None else None
|
battery_charging = device.battery_info.charging() if device.battery_info is not None else None
|
||||||
tray_icon_name = _icons.battery(battery_level, battery_charging)
|
tray_icon_name = icons.battery(battery_level, battery_charging)
|
||||||
description = f"{name}: {device.status_string()}"
|
description = f"{name}: {device.status_string()}"
|
||||||
else:
|
else:
|
||||||
# there may be a receiver, but no peripherals
|
# there may be a receiver, but no peripherals
|
||||||
tray_icon_name = _icons.TRAY_OKAY if _devices_info else _icons.TRAY_INIT
|
tray_icon_name = icons.TRAY_OKAY if _devices_info else icons.TRAY_INIT
|
||||||
|
|
||||||
description_lines = _generate_description_lines()
|
description_lines = _generate_description_lines()
|
||||||
description = "\n".join(description_lines).rstrip("\n")
|
description = "\n".join(description_lines).rstrip("\n")
|
||||||
|
|
||||||
# icon_file = _icons.icon_file(icon_name, _TRAY_ICON_SIZE)
|
# icon_file = icons.icon_file(icon_name, _TRAY_ICON_SIZE)
|
||||||
_icon.set_icon_full(_icon_file(tray_icon_name), description)
|
_icon.set_icon_full(_icon_file(tray_icon_name), description)
|
||||||
|
|
||||||
def attention(reason=None):
|
def attention(reason=None):
|
||||||
if _icon.get_status() != AppIndicator3.IndicatorStatus.ATTENTION:
|
if _icon.get_status() != AppIndicator3.IndicatorStatus.ATTENTION:
|
||||||
# _icon.set_attention_icon_full(_icon_file(_icons.TRAY_ATTENTION), reason or '') # works poorly for XFCe 16
|
# _icon.set_attention_icon_full(_icon_file(icons.TRAY_ATTENTION), reason or '') # works poorly for XFCe 16
|
||||||
_icon.set_status(AppIndicator3.IndicatorStatus.ATTENTION)
|
_icon.set_status(AppIndicator3.IndicatorStatus.ATTENTION)
|
||||||
GLib.timeout_add(10 * 1000, _icon.set_status, AppIndicator3.IndicatorStatus.ACTIVE)
|
GLib.timeout_add(10 * 1000, _icon.set_status, AppIndicator3.IndicatorStatus.ACTIVE)
|
||||||
|
|
||||||
|
|
@ -210,11 +209,11 @@ except ImportError:
|
||||||
logger.debug("using StatusIcon")
|
logger.debug("using StatusIcon")
|
||||||
|
|
||||||
def _create(menu):
|
def _create(menu):
|
||||||
icon = Gtk.StatusIcon.new_from_icon_name(_icons.TRAY_INIT)
|
icon = Gtk.StatusIcon.new_from_icon_name(icons.TRAY_INIT)
|
||||||
icon.set_name(NAME.lower())
|
icon.set_name(NAME.lower())
|
||||||
icon.set_title(NAME)
|
icon.set_title(NAME)
|
||||||
icon.set_tooltip_text(NAME)
|
icon.set_tooltip_text(NAME)
|
||||||
icon.connect("activate", _window_toggle)
|
icon.connect("activate", window.toggle)
|
||||||
icon.connect("scroll-event", _scroll)
|
icon.connect("scroll-event", _scroll)
|
||||||
icon.connect("popup-menu", lambda icon, button, time: menu.popup(None, None, icon.position_menu, icon, button, time))
|
icon.connect("popup-menu", lambda icon, button, time: menu.popup(None, None, icon.position_menu, icon, button, time))
|
||||||
|
|
||||||
|
|
@ -235,10 +234,10 @@ except ImportError:
|
||||||
_ignore, _ignore, name, device = _picked_device
|
_ignore, _ignore, name, device = _picked_device
|
||||||
battery_level = device.battery_info.level if device.battery_info is not None else None
|
battery_level = device.battery_info.level if device.battery_info is not None else None
|
||||||
battery_charging = device.battery_info.charging() if device.battery_info is not None else None
|
battery_charging = device.battery_info.charging() if device.battery_info is not None else None
|
||||||
tray_icon_name = _icons.battery(battery_level, battery_charging)
|
tray_icon_name = icons.battery(battery_level, battery_charging)
|
||||||
else:
|
else:
|
||||||
# there may be a receiver, but no peripherals
|
# there may be a receiver, but no peripherals
|
||||||
tray_icon_name = _icons.TRAY_OKAY if _devices_info else _icons.TRAY_ATTENTION
|
tray_icon_name = icons.TRAY_OKAY if _devices_info else icons.TRAY_ATTENTION
|
||||||
_icon.set_from_icon_name(tray_icon_name)
|
_icon.set_from_icon_name(tray_icon_name)
|
||||||
|
|
||||||
_icon_before_attention = None
|
_icon_before_attention = None
|
||||||
|
|
@ -246,7 +245,7 @@ except ImportError:
|
||||||
def _blink(count):
|
def _blink(count):
|
||||||
global _icon_before_attention
|
global _icon_before_attention
|
||||||
if count % 2:
|
if count % 2:
|
||||||
_icon.set_from_icon_name(_icons.TRAY_ATTENTION)
|
_icon.set_from_icon_name(icons.TRAY_ATTENTION)
|
||||||
else:
|
else:
|
||||||
_icon.set_from_icon_name(_icon_before_attention)
|
_icon.set_from_icon_name(_icon_before_attention)
|
||||||
|
|
||||||
|
|
@ -337,7 +336,7 @@ def _add_device(device):
|
||||||
_devices_info.insert(index, new_device_info)
|
_devices_info.insert(index, new_device_info)
|
||||||
|
|
||||||
label = (" " if device.number else "") + device.name
|
label = (" " if device.number else "") + device.name
|
||||||
new_menu_item = make_image_menu_item(label, None, _window_popup, receiver_path, device.number)
|
new_menu_item = action.make_image_menu_item(label, None, window.popup, receiver_path, device.number)
|
||||||
_menu.insert(new_menu_item, index)
|
_menu.insert(new_menu_item, index)
|
||||||
|
|
||||||
return index
|
return index
|
||||||
|
|
@ -360,8 +359,8 @@ def _add_receiver(receiver):
|
||||||
index = len(_devices_info)
|
index = len(_devices_info)
|
||||||
new_receiver_info = (receiver.path, None, receiver.name, None)
|
new_receiver_info = (receiver.path, None, receiver.name, None)
|
||||||
_devices_info.insert(index, new_receiver_info)
|
_devices_info.insert(index, new_receiver_info)
|
||||||
icon_name = _icons.device_icon_name(receiver.name, receiver.kind)
|
icon_name = icons.device_icon_name(receiver.name, receiver.kind)
|
||||||
new_menu_item = make_image_menu_item(receiver.name, icon_name, _window_popup, receiver.path)
|
new_menu_item = action.make_image_menu_item(receiver.name, icon_name, window.popup, receiver.path)
|
||||||
_menu.insert(new_menu_item, index)
|
_menu.insert(new_menu_item, index)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
@ -385,7 +384,7 @@ def _update_menu_item(index, device):
|
||||||
menu_item = menu_items[index]
|
menu_item = menu_items[index]
|
||||||
level = device.battery_info.level if device.battery_info is not None else None
|
level = device.battery_info.level if device.battery_info is not None else None
|
||||||
charging = device.battery_info.charging() if device.battery_info is not None else None
|
charging = device.battery_info.charging() if device.battery_info is not None else None
|
||||||
icon_name = _icons.battery(level, charging)
|
icon_name = icons.battery(level, charging)
|
||||||
menu_item.label.set_label((" " if 0 < device.number <= 6 else "") + device.name + ": " + device.status_string())
|
menu_item.label.set_label((" " if 0 < device.number <= 6 else "") + device.name + ": " + device.status_string())
|
||||||
image_widget = menu_item.icon
|
image_widget = menu_item.icon
|
||||||
image_widget.set_sensitive(bool(device.online))
|
image_widget.set_sensitive(bool(device.online))
|
||||||
|
|
|
||||||
|
|
@ -20,22 +20,21 @@ import logging
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
from gi.repository.GObject import TYPE_PYOBJECT
|
from gi.repository.GObject import TYPE_PYOBJECT
|
||||||
from logitech_receiver import hidpp10_constants as _hidpp10_constants
|
from logitech_receiver import hidpp10_constants
|
||||||
from logitech_receiver.common import NamedInt as _NamedInt
|
from logitech_receiver.common import LOGITECH_VENDOR_ID
|
||||||
from logitech_receiver.common import NamedInts as _NamedInts
|
from logitech_receiver.common import NamedInt
|
||||||
|
from logitech_receiver.common import NamedInts
|
||||||
|
|
||||||
from solaar import NAME
|
from solaar import NAME
|
||||||
from solaar.i18n import _
|
from solaar.i18n import _
|
||||||
from solaar.i18n import ngettext
|
from solaar.i18n import ngettext
|
||||||
|
|
||||||
from . import action as _action
|
from . import about
|
||||||
from . import config_panel as _config_panel
|
from . import action
|
||||||
from . import icons as _icons
|
from . import config_panel
|
||||||
from .about import show_window as _show_about_window
|
from . import diversion_rules
|
||||||
from .common import ui_async as _ui_async
|
from . import icons
|
||||||
from .diversion_rules import show_window as _show_diversion_window
|
from .common import ui_async
|
||||||
|
|
||||||
# from solaar import __version__ as VERSION
|
|
||||||
|
|
||||||
gi.require_version("Gdk", "3.0")
|
gi.require_version("Gdk", "3.0")
|
||||||
from gi.repository import Gdk # NOQA: E402
|
from gi.repository import Gdk # NOQA: E402
|
||||||
|
|
@ -56,7 +55,7 @@ except (ValueError, AttributeError):
|
||||||
_CAN_SET_ROW_NONE = ""
|
_CAN_SET_ROW_NONE = ""
|
||||||
|
|
||||||
# tree model columns
|
# tree model columns
|
||||||
_COLUMN = _NamedInts(PATH=0, NUMBER=1, ACTIVE=2, NAME=3, ICON=4, STATUS_TEXT=5, STATUS_ICON=6, DEVICE=7)
|
_COLUMN = NamedInts(PATH=0, NUMBER=1, ACTIVE=2, NAME=3, ICON=4, STATUS_TEXT=5, STATUS_ICON=6, DEVICE=7)
|
||||||
_COLUMN_TYPES = (str, int, bool, str, str, str, str, TYPE_PYOBJECT)
|
_COLUMN_TYPES = (str, int, bool, str, str, str, str, TYPE_PYOBJECT)
|
||||||
_TREE_SEPATATOR = (None, 0, False, None, None, None, None, None)
|
_TREE_SEPATATOR = (None, 0, False, None, None, None, None, None)
|
||||||
assert len(_TREE_SEPATATOR) == len(_COLUMN_TYPES)
|
assert len(_TREE_SEPATATOR) == len(_COLUMN_TYPES)
|
||||||
|
|
@ -131,7 +130,7 @@ def _create_device_panel():
|
||||||
|
|
||||||
p.pack_start(Gtk.Separator.new(Gtk.Orientation.HORIZONTAL), False, False, 0) # spacer
|
p.pack_start(Gtk.Separator.new(Gtk.Orientation.HORIZONTAL), False, False, 0) # spacer
|
||||||
|
|
||||||
p._config = _config_panel.create()
|
p._config = config_panel.create()
|
||||||
p.pack_end(p._config, True, True, 4)
|
p.pack_end(p._config, True, True, 4)
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
@ -174,7 +173,7 @@ def _create_buttons_box():
|
||||||
assert receiver is not None
|
assert receiver is not None
|
||||||
assert bool(receiver)
|
assert bool(receiver)
|
||||||
assert receiver.kind is None
|
assert receiver.kind is None
|
||||||
_action.pair(_window, receiver)
|
action.pair(_window, receiver)
|
||||||
|
|
||||||
bb._pair = _new_button(_("Pair new device"), "list-add", clicked=_pair_new_device)
|
bb._pair = _new_button(_("Pair new device"), "list-add", clicked=_pair_new_device)
|
||||||
bb.add(bb._pair)
|
bb.add(bb._pair)
|
||||||
|
|
@ -184,7 +183,7 @@ def _create_buttons_box():
|
||||||
device = _find_selected_device()
|
device = _find_selected_device()
|
||||||
assert device is not None
|
assert device is not None
|
||||||
assert device.kind is not None
|
assert device.kind is not None
|
||||||
_action.unpair(_window, device)
|
action.unpair(_window, device)
|
||||||
|
|
||||||
bb._unpair = _new_button(_("Unpair"), "edit-delete", clicked=_unpair_current_device)
|
bb._unpair = _new_button(_("Unpair"), "edit-delete", clicked=_unpair_current_device)
|
||||||
bb.add(bb._unpair)
|
bb.add(bb._unpair)
|
||||||
|
|
@ -233,7 +232,6 @@ def _create_tree(model):
|
||||||
tree.set_headers_visible(False)
|
tree.set_headers_visible(False)
|
||||||
tree.set_show_expanders(False)
|
tree.set_show_expanders(False)
|
||||||
tree.set_level_indentation(20)
|
tree.set_level_indentation(20)
|
||||||
# tree.set_fixed_height_mode(True)
|
|
||||||
tree.set_enable_tree_lines(True)
|
tree.set_enable_tree_lines(True)
|
||||||
tree.set_reorderable(False)
|
tree.set_reorderable(False)
|
||||||
tree.set_enable_search(False)
|
tree.set_enable_search(False)
|
||||||
|
|
@ -307,19 +305,14 @@ def _create_window_layout():
|
||||||
bottom_buttons_box.set_spacing(20)
|
bottom_buttons_box.set_spacing(20)
|
||||||
quit_button = _new_button(_("Quit %s") % NAME, "application-exit", _SMALL_BUTTON_ICON_SIZE, clicked=destroy)
|
quit_button = _new_button(_("Quit %s") % NAME, "application-exit", _SMALL_BUTTON_ICON_SIZE, clicked=destroy)
|
||||||
bottom_buttons_box.add(quit_button)
|
bottom_buttons_box.add(quit_button)
|
||||||
about_button = _new_button(_("About %s") % NAME, "help-about", _SMALL_BUTTON_ICON_SIZE, clicked=_show_about_window)
|
about_button = _new_button(_("About %s") % NAME, "help-about", _SMALL_BUTTON_ICON_SIZE, clicked=about.show_window)
|
||||||
bottom_buttons_box.add(about_button)
|
bottom_buttons_box.add(about_button)
|
||||||
diversion_button = _new_button(
|
diversion_button = _new_button(
|
||||||
_("Rule Editor"), "", _SMALL_BUTTON_ICON_SIZE, clicked=lambda *_trigger: _show_diversion_window(_model)
|
_("Rule Editor"), "", _SMALL_BUTTON_ICON_SIZE, clicked=lambda *_trigger: diversion_rules.show_window(_model)
|
||||||
)
|
)
|
||||||
bottom_buttons_box.add(diversion_button)
|
bottom_buttons_box.add(diversion_button)
|
||||||
bottom_buttons_box.set_child_secondary(diversion_button, True)
|
bottom_buttons_box.set_child_secondary(diversion_button, True)
|
||||||
|
|
||||||
# solaar_version = Gtk.Label()
|
|
||||||
# solaar_version.set_markup('<small>' + NAME + ' v' + VERSION + '</small>')
|
|
||||||
# bottom_buttons_box.add(solaar_version)
|
|
||||||
# bottom_buttons_box.set_child_secondary(solaar_version, True)
|
|
||||||
|
|
||||||
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 8)
|
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 8)
|
||||||
vbox.set_border_width(8)
|
vbox.set_border_width(8)
|
||||||
vbox.pack_start(panel, True, True, 0)
|
vbox.pack_start(panel, True, True, 0)
|
||||||
|
|
@ -335,10 +328,6 @@ def _create(delete_action):
|
||||||
window = Gtk.Window()
|
window = Gtk.Window()
|
||||||
window.set_title(NAME)
|
window.set_title(NAME)
|
||||||
window.set_role("status-window")
|
window.set_role("status-window")
|
||||||
|
|
||||||
# window.set_type_hint(Gdk.WindowTypeHint.UTILITY)
|
|
||||||
# window.set_skip_taskbar_hint(True)
|
|
||||||
# window.set_skip_pager_hint(True)
|
|
||||||
window.connect("delete-event", delete_action)
|
window.connect("delete-event", delete_action)
|
||||||
|
|
||||||
vbox = _create_window_layout()
|
vbox = _create_window_layout()
|
||||||
|
|
@ -373,8 +362,6 @@ def _find_selected_device_id():
|
||||||
def _device_selected(selection):
|
def _device_selected(selection):
|
||||||
model, item = selection.get_selected()
|
model, item = selection.get_selected()
|
||||||
device = model.get_value(item, _COLUMN.DEVICE) if item else None
|
device = model.get_value(item, _COLUMN.DEVICE) if item else None
|
||||||
# if logger.isEnabledFor(logging.DEBUG):
|
|
||||||
# logger.debug("window tree selected device %s", device)
|
|
||||||
if device:
|
if device:
|
||||||
_update_info_panel(device, full=True)
|
_update_info_panel(device, full=True)
|
||||||
else:
|
else:
|
||||||
|
|
@ -399,7 +386,7 @@ def _receiver_row(receiver_path, receiver=None):
|
||||||
item = _model.iter_next(item)
|
item = _model.iter_next(item)
|
||||||
|
|
||||||
if not item and receiver:
|
if not item and receiver:
|
||||||
icon_name = _icons.device_icon_name(receiver.name)
|
icon_name = icons.device_icon_name(receiver.name)
|
||||||
status_text = None
|
status_text = None
|
||||||
status_icon = None
|
status_icon = None
|
||||||
row_data = (receiver_path, 0, True, receiver.name, icon_name, status_text, status_icon, receiver)
|
row_data = (receiver_path, 0, True, receiver.name, icon_name, status_text, status_icon, receiver)
|
||||||
|
|
@ -444,7 +431,7 @@ def _device_row(receiver_path, device_number, device=None):
|
||||||
item = _model.iter_next(item)
|
item = _model.iter_next(item)
|
||||||
|
|
||||||
if not item and device:
|
if not item and device:
|
||||||
icon_name = _icons.device_icon_name(device.name, device.kind)
|
icon_name = icons.device_icon_name(device.name, device.kind)
|
||||||
status_text = None
|
status_text = None
|
||||||
status_icon = None
|
status_icon = None
|
||||||
row_data = (
|
row_data = (
|
||||||
|
|
@ -516,8 +503,7 @@ def _update_details(button):
|
||||||
|
|
||||||
yield (_("Path"), device.path)
|
yield (_("Path"), device.path)
|
||||||
if device.kind is None:
|
if device.kind is None:
|
||||||
# 046d is the Logitech vendor id
|
yield (_("USB ID"), f"{LOGITECH_VENDOR_ID:04x}:" + device.product_id)
|
||||||
yield (_("USB ID"), "046d:" + device.product_id)
|
|
||||||
|
|
||||||
if read_all:
|
if read_all:
|
||||||
yield (_("Serial"), device.serial)
|
yield (_("Serial"), device.serial)
|
||||||
|
|
@ -530,7 +516,7 @@ def _update_details(button):
|
||||||
if device.wpid:
|
if device.wpid:
|
||||||
yield (_("Wireless PID"), device.wpid)
|
yield (_("Wireless PID"), device.wpid)
|
||||||
if device.product_id:
|
if device.product_id:
|
||||||
yield (_("Product ID"), "046d:" + device.product_id)
|
yield (_("Product ID"), f"{LOGITECH_VENDOR_ID:04x}:" + device.product_id)
|
||||||
hid_version = device.protocol
|
hid_version = device.protocol
|
||||||
yield (_("Protocol"), f"HID++ {hid_version:1.1f}" if hid_version else _("Unknown"))
|
yield (_("Protocol"), f"HID++ {hid_version:1.1f}" if hid_version else _("Unknown"))
|
||||||
if read_all and device.polling_rate:
|
if read_all and device.polling_rate:
|
||||||
|
|
@ -553,7 +539,7 @@ def _update_details(button):
|
||||||
flag_bits = device.notification_flags
|
flag_bits = device.notification_flags
|
||||||
if flag_bits is not None:
|
if flag_bits is not None:
|
||||||
flag_names = (
|
flag_names = (
|
||||||
(f"({_('none')})",) if flag_bits == 0 else _hidpp10_constants.NOTIFICATION_FLAG.flag_names(flag_bits)
|
(f"({_('none')})",) if flag_bits == 0 else hidpp10_constants.NOTIFICATION_FLAG.flag_names(flag_bits)
|
||||||
)
|
)
|
||||||
yield (_("Notifications"), (f"\n{' ':15}").join(flag_names))
|
yield (_("Notifications"), (f"\n{' ':15}").join(flag_names))
|
||||||
|
|
||||||
|
|
@ -588,7 +574,7 @@ def _update_details(button):
|
||||||
if read_all:
|
if read_all:
|
||||||
_details._current_device = None
|
_details._current_device = None
|
||||||
else:
|
else:
|
||||||
_ui_async(_read_slow, selected_device)
|
ui_async(_read_slow, selected_device)
|
||||||
|
|
||||||
_details.set_visible(visible)
|
_details.set_visible(visible)
|
||||||
|
|
||||||
|
|
@ -670,7 +656,7 @@ def _update_device_panel(device, panel, buttons, full=False):
|
||||||
panel._battery.set_visible(True)
|
panel._battery.set_visible(True)
|
||||||
battery_next_level = device.battery_info.next_level
|
battery_next_level = device.battery_info.next_level
|
||||||
charging = device.battery_info.charging() if device.battery_info is not None else None
|
charging = device.battery_info.charging() if device.battery_info is not None else None
|
||||||
icon_name = _icons.battery(battery_level, charging)
|
icon_name = icons.battery(battery_level, charging)
|
||||||
panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE)
|
panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE)
|
||||||
panel._battery._icon.set_sensitive(True)
|
panel._battery._icon.set_sensitive(True)
|
||||||
panel._battery._text.set_sensitive(is_online)
|
panel._battery._text.set_sensitive(is_online)
|
||||||
|
|
@ -686,9 +672,9 @@ def _update_device_panel(device, panel, buttons, full=False):
|
||||||
if battery_voltage is not None and battery_level is not None:
|
if battery_voltage is not None and battery_level is not None:
|
||||||
text += ", "
|
text += ", "
|
||||||
if battery_level is not None:
|
if battery_level is not None:
|
||||||
text += _(str(battery_level)) if isinstance(battery_level, _NamedInt) else f"{int(battery_level)}%"
|
text += _(str(battery_level)) if isinstance(battery_level, NamedInt) else f"{int(battery_level)}%"
|
||||||
if battery_next_level is not None and not charging:
|
if battery_next_level is not None and not charging:
|
||||||
if isinstance(battery_next_level, _NamedInt):
|
if isinstance(battery_next_level, NamedInt):
|
||||||
text += "<small> (" + _("next reported ") + _(str(battery_next_level)) + ")</small>"
|
text += "<small> (" + _("next reported ") + _(str(battery_next_level)) + ")</small>"
|
||||||
else:
|
else:
|
||||||
text += "<small> (" + _("next reported ") + f"{int(battery_next_level)}%" + ")</small>"
|
text += "<small> (" + _("next reported ") + f"{int(battery_next_level)}%" + ")</small>"
|
||||||
|
|
@ -731,7 +717,7 @@ def _update_device_panel(device, panel, buttons, full=False):
|
||||||
if light_level is None:
|
if light_level is None:
|
||||||
panel._lux.set_visible(False)
|
panel._lux.set_visible(False)
|
||||||
else:
|
else:
|
||||||
panel._lux._icon.set_from_icon_name(_icons.lux(light_level), _INFO_ICON_SIZE)
|
panel._lux._icon.set_from_icon_name(icons.lux(light_level), _INFO_ICON_SIZE)
|
||||||
panel._lux._text.set_text(_("%(light_level)d lux") % {"light_level": light_level})
|
panel._lux._text.set_text(_("%(light_level)d lux") % {"light_level": light_level})
|
||||||
panel._lux.set_visible(True)
|
panel._lux.set_visible(True)
|
||||||
else:
|
else:
|
||||||
|
|
@ -744,7 +730,7 @@ def _update_device_panel(device, panel, buttons, full=False):
|
||||||
panel.set_visible(True)
|
panel.set_visible(True)
|
||||||
|
|
||||||
if full:
|
if full:
|
||||||
_config_panel.update(device, is_online)
|
config_panel.update(device, is_online)
|
||||||
|
|
||||||
|
|
||||||
def _update_info_panel(device, full=False):
|
def _update_info_panel(device, full=False):
|
||||||
|
|
@ -760,7 +746,7 @@ def _update_info_panel(device, full=False):
|
||||||
assert device
|
assert device
|
||||||
|
|
||||||
_info._title.set_markup(f"<b>{device.name}</b>")
|
_info._title.set_markup(f"<b>{device.name}</b>")
|
||||||
icon_name = _icons.device_icon_name(device.name, device.kind)
|
icon_name = icons.device_icon_name(device.name, device.kind)
|
||||||
_info._icon.set_from_icon_name(icon_name, _DEVICE_ICON_SIZE)
|
_info._icon.set_from_icon_name(icon_name, _DEVICE_ICON_SIZE)
|
||||||
|
|
||||||
if device.kind is None:
|
if device.kind is None:
|
||||||
|
|
@ -821,7 +807,7 @@ def destroy(_ignore1=None, _ignore2=None):
|
||||||
w, _window = _window, None
|
w, _window = _window, None
|
||||||
w.destroy()
|
w.destroy()
|
||||||
w = None
|
w = None
|
||||||
_config_panel.destroy()
|
config_panel.destroy()
|
||||||
|
|
||||||
_empty = None
|
_empty = None
|
||||||
_info = None
|
_info = None
|
||||||
|
|
@ -871,7 +857,7 @@ def update(device, need_popup=False, refresh=False):
|
||||||
update_device(device, item, selected_device_id, need_popup, full=refresh)
|
update_device(device, item, selected_device_id, need_popup, full=refresh)
|
||||||
elif item:
|
elif item:
|
||||||
_model.remove(item)
|
_model.remove(item)
|
||||||
_config_panel.clean(device)
|
config_panel.clean(device)
|
||||||
|
|
||||||
# make sure all rows are visible
|
# make sure all rows are visible
|
||||||
_tree.expand_all()
|
_tree.expand_all()
|
||||||
|
|
@ -890,14 +876,14 @@ def update_device(device, item, selected_device_id, need_popup, full=False):
|
||||||
else:
|
else:
|
||||||
if battery_voltage is not None and False: # Use levels instead of voltage here
|
if battery_voltage is not None and False: # Use levels instead of voltage here
|
||||||
status_text = f"{int(battery_voltage)}mV"
|
status_text = f"{int(battery_voltage)}mV"
|
||||||
elif isinstance(battery_level, _NamedInt):
|
elif isinstance(battery_level, NamedInt):
|
||||||
status_text = _(str(battery_level))
|
status_text = _(str(battery_level))
|
||||||
else:
|
else:
|
||||||
status_text = f"{int(battery_level)}%"
|
status_text = f"{int(battery_level)}%"
|
||||||
_model.set_value(item, _COLUMN.STATUS_TEXT, status_text)
|
_model.set_value(item, _COLUMN.STATUS_TEXT, status_text)
|
||||||
|
|
||||||
charging = device.battery_info.charging() if device.battery_info is not None else None
|
charging = device.battery_info.charging() if device.battery_info is not None else None
|
||||||
icon_name = _icons.battery(battery_level, charging)
|
icon_name = icons.battery(battery_level, charging)
|
||||||
_model.set_value(item, _COLUMN.STATUS_ICON, icon_name)
|
_model.set_value(item, _COLUMN.STATUS_ICON, icon_name)
|
||||||
|
|
||||||
_model.set_value(item, _COLUMN.NAME, device.codename)
|
_model.set_value(item, _COLUMN.NAME, device.codename)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue