mda
This commit is contained in:
parent
ae558f1362
commit
9bb3c1b2df
@ -1,10 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import select
|
import subprocess
|
||||||
import struct
|
import re
|
||||||
import threading
|
import threading
|
||||||
import logging
|
import logging
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
@ -122,70 +123,110 @@ class IrRemoteService(QObject):
|
|||||||
self._thread = None
|
self._thread = None
|
||||||
|
|
||||||
def _event_loop(self) -> None:
|
def _event_loop(self) -> None:
|
||||||
"""Цикл обработки событий."""
|
"""Цикл обработки событий через ir-keytable."""
|
||||||
logger.info(f"Event loop started for {self._device_path}")
|
logger.info(f"Starting ir-keytable event loop")
|
||||||
|
|
||||||
|
last_scancode = None
|
||||||
|
last_time = 0
|
||||||
|
min_delay = 0.3 # Минимальная задержка между нажатиями (сек)
|
||||||
|
key_held = False # Флаг: кнопка зажата
|
||||||
|
|
||||||
while self._running:
|
while self._running:
|
||||||
try:
|
try:
|
||||||
with open(self._device_path, "rb") as f:
|
# Запускаем ir-keytable -t для чтения событий
|
||||||
logger.info(f"Opened {self._device_path} successfully")
|
# Пробуем без sudo, если не работает - добавь правило в sudoers
|
||||||
while self._running:
|
proc = subprocess.Popen(
|
||||||
# Используем select для неблокирующего чтения
|
["ir-keytable", "-t"],
|
||||||
ready, _, _ = select.select([f], [], [], 0.5)
|
stdout=subprocess.PIPE,
|
||||||
if not ready:
|
stderr=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
bufsize=1
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("ir-keytable started")
|
||||||
|
|
||||||
|
while self._running and proc.poll() is None:
|
||||||
|
line = proc.stdout.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
# Игнорируем EV_SYN события (это просто разделители)
|
||||||
|
if "EV_SYN" in line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.debug(f"ir-keytable output: {line}")
|
||||||
|
|
||||||
|
# Проверяем отпускание кнопки (EV_KEY с value=0)
|
||||||
|
if "EV_KEY" in line and "value=0" in line:
|
||||||
|
logger.debug("Key release detected")
|
||||||
|
key_held = False
|
||||||
|
last_scancode = None
|
||||||
|
self.reset_button_state()
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Парсим строку вида:
|
||||||
|
# 23567.604034: lirc protocol(nec): scancode = 0x19
|
||||||
|
# 23567.604034: event type EV_MSC(0x04): scancode = 0x19
|
||||||
|
match = re.search(r'scancode\s*=\s*(0x[0-9a-fA-F]+)', line)
|
||||||
|
if match:
|
||||||
|
current_time = time.time()
|
||||||
|
scancode = match.group(1).lower()
|
||||||
|
|
||||||
|
# Проверяем есть ли "repeat" в строке
|
||||||
|
is_repeat = "repeat" in line.lower()
|
||||||
|
|
||||||
|
# Если пришла другая кнопка — сбрасываем состояние предыдущей
|
||||||
|
if last_scancode and scancode != last_scancode and not is_repeat:
|
||||||
|
logger.debug(f"New button pressed, resetting previous: {last_scancode} -> {scancode}")
|
||||||
|
key_held = False
|
||||||
|
self._repeat_counts.pop(last_scancode, None)
|
||||||
|
self._hold_triggered.pop(last_scancode, None)
|
||||||
|
|
||||||
|
# Если кнопка была отпущена и пришло новое нажатие
|
||||||
|
if not key_held and not is_repeat:
|
||||||
|
key_held = True
|
||||||
|
self._repeat_counts[scancode] = 1
|
||||||
|
logger.debug(f"New key press: {scancode}")
|
||||||
|
# Если кнопка зажата и пришло repeat
|
||||||
|
elif key_held and is_repeat:
|
||||||
|
self._repeat_counts[scancode] = self._repeat_counts.get(scancode, 1) + 1
|
||||||
|
logger.debug(f"Key repeat: {scancode}, count={self._repeat_counts[scancode]}")
|
||||||
|
# Игнорируем дубли
|
||||||
|
else:
|
||||||
|
logger.debug(f"Skipping: key_held={key_held}, is_repeat={is_repeat}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Читаем событие (24 байта: tv_sec, tv_usec, type, code, value)
|
# Проверяем задержку между разными нажатиями
|
||||||
data = f.read(24)
|
if scancode != last_scancode and (current_time - last_time) < min_delay:
|
||||||
if len(data) < 24:
|
logger.debug(f"Skipping too fast: {current_time - last_time:.3f}s < {min_delay}s")
|
||||||
logger.warning(f"Read incomplete data: {len(data)} bytes")
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Логируем сырые байты для отладки
|
logger.debug(f"Processing scancode: {scancode}, is_repeat: {is_repeat}")
|
||||||
logger.debug(f"Raw bytes: {data.hex()}")
|
self._handle_scancode(scancode, is_repeat)
|
||||||
|
last_scancode = scancode
|
||||||
tv_sec, tv_usec, ev_type, ev_code, ev_value = struct.unpack("llHHI", data)
|
last_time = current_time
|
||||||
|
|
||||||
logger.debug(f"Raw event: type={ev_type} (0x{ev_type:02x}), code={ev_code} (0x{ev_code:02x}), value={ev_value} (0x{ev_value:02x})")
|
proc.terminate()
|
||||||
|
proc.wait(timeout=1)
|
||||||
# Обрабатываем MSC_SCAN события (скан-код)
|
|
||||||
if ev_type == EV_MSC and ev_code == MSC_SCAN:
|
except Exception as e:
|
||||||
scancode = f"0x{ev_value:02x}"
|
logger.warning(f"ir-keytable error: {e}, retrying in 1s...")
|
||||||
logger.debug(f"MSC_SCAN event: scancode={scancode}")
|
|
||||||
self._handle_scancode(scancode)
|
|
||||||
|
|
||||||
# Обрабатываем EV_KEY события (отпускание кнопки)
|
|
||||||
elif ev_type == EV_KEY and ev_value == KEY_UP:
|
|
||||||
logger.debug(f"KEY_UP event: code={ev_code}")
|
|
||||||
# Кнопка отпущена - сбрасываем состояние
|
|
||||||
self.reset_button_state()
|
|
||||||
|
|
||||||
except (OSError, IOError) as e:
|
|
||||||
# Устройство недоступно, ждём и пробуем снова
|
|
||||||
logger.warning(f"Device error: {e}, retrying...")
|
|
||||||
import time
|
|
||||||
time.sleep(1.0)
|
time.sleep(1.0)
|
||||||
|
|
||||||
def _handle_scancode(self, scancode: str) -> None:
|
def _handle_scancode(self, scancode: str, is_repeat: bool = False) -> None:
|
||||||
"""Обработать скан-код кнопки."""
|
"""Обработать скан-код кнопки."""
|
||||||
action = self._scancode_to_action.get(scancode)
|
action = self._scancode_to_action.get(scancode)
|
||||||
if not action:
|
if not action:
|
||||||
logger.debug(f"Unknown scancode: {scancode}")
|
logger.debug(f"Unknown scancode: {scancode}")
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.debug(f"Scancode: {scancode}, action: {action}")
|
repeat_count = self._repeat_counts.get(scancode, 1)
|
||||||
|
|
||||||
# Увеличиваем счётчик повторений
|
|
||||||
self._repeat_counts[scancode] = self._repeat_counts.get(scancode, 0) + 1
|
|
||||||
repeat_count = self._repeat_counts[scancode]
|
|
||||||
|
|
||||||
# Проверяем, было ли зажатие уже обработано
|
|
||||||
hold_triggered = self._hold_triggered.get(scancode, False)
|
hold_triggered = self._hold_triggered.get(scancode, False)
|
||||||
|
|
||||||
# Определяем тип события
|
|
||||||
is_repeat = repeat_count > 1
|
|
||||||
is_hold = repeat_count >= build_info.IR_REPEAT_COUNT_FOR_HOLD
|
is_hold = repeat_count >= build_info.IR_REPEAT_COUNT_FOR_HOLD
|
||||||
|
|
||||||
logger.debug(f" repeat_count={repeat_count}, is_repeat={is_repeat}, is_hold={is_hold}")
|
logger.debug(f"Scancode: {scancode}, action: {action}, repeat_count={repeat_count}, is_hold={is_hold}")
|
||||||
|
|
||||||
# Отправляем сигналы
|
# Отправляем сигналы
|
||||||
if action == "play_pause":
|
if action == "play_pause":
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user