improve device detection when plugging/unplugging
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 19s
JS/TS / lint (pull_request) Successful in 19s
Python / fmt (pull_request) Successful in 36s
Python / lint (pull_request) Failing after 34s
Python / test (pull_request) Failing after 1m2s
Python / typecheck (pull_request) Failing after 1m11s
C++ / build (pull_request) Successful in 2m11s
Rust / fmt (pull_request) Successful in 52s
Rust / build (pull_request) Successful in 1m45s
Rust / clippy (pull_request) Successful in 1m35s
Python / buildcheck (pull_request) Successful in 2m44s
C++ / clang-tidy (pull_request) Successful in 3m37s
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 19s
JS/TS / lint (pull_request) Successful in 19s
Python / fmt (pull_request) Successful in 36s
Python / lint (pull_request) Failing after 34s
Python / test (pull_request) Failing after 1m2s
Python / typecheck (pull_request) Failing after 1m11s
C++ / build (pull_request) Successful in 2m11s
Rust / fmt (pull_request) Successful in 52s
Rust / build (pull_request) Successful in 1m45s
Rust / clippy (pull_request) Successful in 1m35s
Python / buildcheck (pull_request) Successful in 2m44s
C++ / clang-tidy (pull_request) Successful in 3m37s
This commit is contained in:
@@ -292,6 +292,7 @@ class _ListenerRegistry:
|
||||
self._rescan_lock = threading.Lock()
|
||||
self._min_rescan_interval = 1.0
|
||||
self._last_rescan_time = 0.0
|
||||
|
||||
self._hidpp_io_lock = threading.RLock()
|
||||
|
||||
def _touch_and_notify(self, dev_or_receiver, *args, **kwargs):
|
||||
@@ -309,6 +310,7 @@ class _ListenerRegistry:
|
||||
return
|
||||
if not self._rescan_lock.acquire(blocking=False):
|
||||
return
|
||||
|
||||
try:
|
||||
self._last_rescan_time = now
|
||||
with self._hidpp_io_lock:
|
||||
@@ -321,9 +323,38 @@ class _ListenerRegistry:
|
||||
if not listener._active
|
||||
]
|
||||
for p in dead:
|
||||
dying_obj, _dying_listener = self._entries[p]
|
||||
|
||||
if dying_obj.isDevice:
|
||||
ident = getattr(
|
||||
dying_obj, "unitId", None
|
||||
) or getattr(dying_obj, "serial", None)
|
||||
info = getattr(dying_obj, "battery_info", None)
|
||||
if ident and info is not None:
|
||||
for (
|
||||
other_path,
|
||||
(other_obj, _other_listener),
|
||||
) in self._entries.items():
|
||||
if other_path == p or other_obj.isDevice:
|
||||
continue
|
||||
for child in other_obj:
|
||||
child_ident = getattr(
|
||||
child, "unitId", None
|
||||
) or getattr(child, "serial", None)
|
||||
if child_ident == ident:
|
||||
child.set_battery_info(info)
|
||||
break
|
||||
del self._entries[p]
|
||||
known_paths = set(self._entries.keys())
|
||||
|
||||
with self._lock:
|
||||
id_to_entry = {}
|
||||
for path, (obj, listener) in self._entries.items():
|
||||
if obj.isDevice:
|
||||
ident = obj.unitId or obj.serial
|
||||
if ident:
|
||||
id_to_entry[ident] = (path, obj, listener)
|
||||
|
||||
for dev_info in base.receivers_and_devices():
|
||||
if dev_info.path in known_paths:
|
||||
continue
|
||||
@@ -350,17 +381,108 @@ class _ListenerRegistry:
|
||||
continue
|
||||
|
||||
listener = ListenerClass(obj, self._touch_and_notify)
|
||||
listener.start()
|
||||
|
||||
if obj.isDevice:
|
||||
ident = None
|
||||
try:
|
||||
if obj.protocol >= 2.0:
|
||||
obj.get_ids()
|
||||
ident = obj.unitId or obj.serial
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if ident:
|
||||
with self._lock:
|
||||
existing = id_to_entry.get(ident)
|
||||
if existing:
|
||||
old_path, old_obj, old_listener = existing
|
||||
|
||||
if old_path != dev_info.path:
|
||||
logger.info(
|
||||
"replacing device %s (old path %s) with new path %s",
|
||||
ident,
|
||||
old_path,
|
||||
dev_info.path,
|
||||
)
|
||||
|
||||
if (
|
||||
getattr(obj, "battery_info", None)
|
||||
is None
|
||||
and getattr(
|
||||
old_obj, "battery_info", None
|
||||
)
|
||||
is not None
|
||||
):
|
||||
obj.set_battery_info(
|
||||
old_obj.battery_info
|
||||
)
|
||||
|
||||
old_listener.stop()
|
||||
|
||||
del self._entries[old_path]
|
||||
|
||||
self._entries[dev_info.path] = (
|
||||
obj,
|
||||
listener,
|
||||
)
|
||||
self._last_change[id(obj)] = time.time()
|
||||
|
||||
to_join = old_listener
|
||||
break
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
if (
|
||||
getattr(obj, "battery_info", None)
|
||||
is None
|
||||
):
|
||||
for (
|
||||
other_path,
|
||||
(other_obj, _other_listener),
|
||||
) in self._entries.items():
|
||||
if other_obj.isDevice:
|
||||
continue
|
||||
for child in other_obj:
|
||||
child_ident = getattr(
|
||||
child, "unitId", None
|
||||
) or getattr(
|
||||
child, "serial", None
|
||||
)
|
||||
if child_ident == ident:
|
||||
obj.set_battery_info(
|
||||
child.battery_info
|
||||
)
|
||||
break
|
||||
if (
|
||||
getattr(
|
||||
obj,
|
||||
"battery_info",
|
||||
None,
|
||||
)
|
||||
is not None
|
||||
):
|
||||
break
|
||||
|
||||
with self._lock:
|
||||
self._entries[dev_info.path] = (obj, listener)
|
||||
self._last_change[id(obj)] = time.time()
|
||||
if dev_info.path not in self._entries:
|
||||
self._entries[dev_info.path] = (obj, listener)
|
||||
self._last_change[id(obj)] = time.time()
|
||||
|
||||
listener.start()
|
||||
|
||||
logger.info(
|
||||
"listening on %s (%s)",
|
||||
dev_info.path,
|
||||
"device" if dev_info.isDevice else "receiver",
|
||||
)
|
||||
|
||||
if "to_join" in locals():
|
||||
try:
|
||||
to_join.join(timeout=1.0)
|
||||
except Exception:
|
||||
logger.exception("error joining replaced listener")
|
||||
del to_join
|
||||
|
||||
finally:
|
||||
self._rescan_lock.release()
|
||||
|
||||
@@ -610,10 +732,22 @@ def daemon(
|
||||
pending_write_lock = threading.Lock()
|
||||
WRITE_SETTLE_SECONDS = 0.3
|
||||
|
||||
write_seq_lock = threading.Lock()
|
||||
write_seq = 0
|
||||
last_written_seq = 0
|
||||
|
||||
def write_if_changed():
|
||||
nonlocal last_payload
|
||||
nonlocal last_payload, write_seq, last_written_seq
|
||||
with write_seq_lock:
|
||||
write_seq += 1
|
||||
my_seq = write_seq
|
||||
|
||||
results = _snapshot(registry)
|
||||
with write_lock:
|
||||
results = _snapshot(registry)
|
||||
with write_seq_lock:
|
||||
if my_seq <= last_written_seq:
|
||||
return
|
||||
last_written_seq = my_seq
|
||||
payload = [asdict(r) for r in results]
|
||||
if payload != last_payload:
|
||||
_write_json_atomic(out, results)
|
||||
|
||||
Reference in New Issue
Block a user