История изменений
Исправление LightDiver, (текущая версия) :
#!/usr/bin/env python3
import subprocess
import time
import re
import logging
# Настройка логирования
logging.basicConfig(
filename="kbd.log",
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
)
# Таблица соответствия класс окна-раскладка (0 - английская, 1 - русская)
WINDOW_CLASS_LAYOUT_MAP = {
"firefox": 0,
"gedit": 1,
"terminal": 0,
"AyuGram": None,
"wow": 1,
}
def switch_layout(layout_index):
"""Простое переключение раскладки по индексу"""
try:
subprocess.run(["xkblayout-state", "set", str(layout_index)], check=True)
logging.info(f"Layout switched to index: {layout_index}")
return True
except Exception as e:
logging.error(f"Failed to switch layout: {e}")
return False
def get_active_window_class():
"""Получаем класс активного окна"""
try:
# Получаем ID активного окна
result = subprocess.run(
["xdotool", "getactivewindow"], capture_output=True, text=True
)
window_id = result.stdout.strip()
# Получаем класс окна
result = subprocess.run(
["xprop", "-id", window_id, "WM_CLASS"], capture_output=True, text=True
)
# Парсим вывод
match = re.search(r'WM_CLASS.*?=.*?"[^"]*",\s*"([^"]*)"', result.stdout)
if match:
return match.group(1).lower()
except Exception as e:
logging.error(f"Window class error: {e}")
return None
def main():
last_window_class = None
while True:
try:
current_class = get_active_window_class()
if not current_class:
time.sleep(0.5)
continue
if current_class != last_window_class:
last_window_class = current_class
logging.info(f"Active window: {current_class}")
if current_class in WINDOW_CLASS_LAYOUT_MAP:
layout_index = WINDOW_CLASS_LAYOUT_MAP[current_class]
if layout_index is not None:
switch_layout(layout_index)
# Дублируем команду для надежности
time.sleep(0.1)
switch_layout(layout_index)
time.sleep(0.3)
except KeyboardInterrupt:
logging.info("Script stopped")
break
except Exception as e:
logging.error(f"Error: {e}")
time.sleep(1)
if __name__ == "__main__":
print("Keyboard layout switcher started")
main()
С этим можно работать, это можно дорабатывать.
Сами они нихера не умеют. Нужно понимать что делаешь. Несколько часов пытался без подсказок добиться хоть чего то. Только ломают все. Будем ждать и наблюдать.
Исправление LightDiver, :
Короче. Нихера они не умеют, если ты сам не вникаешьи не разбираешься в теме.
#!/usr/bin/env python3
import subprocess
import time
from ewmh import EWMH
import logging
# Настройка логирования
logging.basicConfig(
filename="kbd.log",
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
)
# Таблица соответствия класс окна-раскладка (0 - английская, 1 - русская)
WINDOW_CLASS_LAYOUT_MAP = {
"firefox": 0, # английская
"gedit": 1, # русская
"gnome-terminal": 0,
"AyuGram": None, # игнорировать
"wow": 1,
}
def get_current_layout_index():
"""Получает текущую раскладку как индекс (0 или 1)"""
try:
result = subprocess.run(
["xkblayout-state", "print", "%n"],
capture_output=True,
text=True,
check=True
)
return int(result.stdout.strip())
except Exception as e:
logging.error(f"Failed to get current layout: {e}")
return None
def switch_layout(layout_index):
"""Переключает раскладку на указанный индекс (0 или 1)"""
try:
subprocess.run(
["xkblayout-state", "set", str(layout_index)],
check=True
)
logging.info(f"Switched to layout index: {layout_index}")
return True
except Exception as e:
logging.error(f"Failed to switch layout: {e}")
return False
def get_active_window_class(ewmh):
"""Получает класс активного окна"""
try:
active_window = ewmh.getActiveWindow()
if active_window:
wm_class = ewmh.getWmClass(active_window)
if wm_class:
return wm_class[1].lower() if isinstance(wm_class, tuple) else wm_class.lower()
except Exception as e:
logging.error(f"Error getting window class: {e}")
return None
def main():
ewmh = EWMH()
last_window_class = None
while True:
try:
current_class = get_active_window_class(ewmh)
if not current_class:
time.sleep(0.5)
continue
if current_class != last_window_class:
last_window_class = current_class
logging.info(f"Active window changed to: {current_class}")
# Проверяем, есть ли правило для этого класса окна
if current_class in WINDOW_CLASS_LAYOUT_MAP:
target_layout = WINDOW_CLASS_LAYOUT_MAP[current_class]
# Если None - пропускаем это окно
if target_layout is None:
continue
current_layout = get_current_layout_index()
if current_layout is not None and current_layout != target_layout:
logging.info(f"Switching layout from {current_layout} to {target_layout}")
switch_layout(target_layout)
time.sleep(0.3)
except KeyboardInterrupt:
logging.info("Script stopped by user")
break
except Exception as e:
logging.error(f"Unexpected error: {e}")
time.sleep(1)
if __name__ == "__main__":
main()
Есть такое рабочий(?) вариант.
Исходная версия LightDiver, :
Никаких нервов не хватит. Нихера они не умеют, что хоть чуть уходит за пределы ширпотреба массового. Сделал вот такую кривую заглушку. Может работать ненадежно:
#!/usr/bin/env python3
import subprocess
import time
from ewmh import EWMH
import logging
# Настройка логирования
logging.basicConfig(
filename="kbd.log", # Файл для записи логов
level=logging.INFO, # Уровень логирования
format="%(asctime)s - %(levelname)s - %(message)s",
)
# Таблица соответствия окно-раскладка
WINDOW_LAYOUT_MAP = {
"Firefox": "us",
"gedit": "ru",
"Terminal": "us",
"AyuGram": None, # Игнорировать это окно
"World of Warcraft": "ru", # Принудительно использовать русскую раскладку
}
# Получение текущей раскладки
def get_current_layout():
try:
result = subprocess.run(["setxkbmap", "-query"], capture_output=True, text=True)
for line in result.stdout.splitlines():
if line.startswith("layout:"):
current_layout = line.split(":")[1].strip().split(",")[0]
logging.info(f"Current layout: {current_layout}")
return current_layout
return None
except Exception as e:
logging.error(f"Failed to get current layout: {e}")
return None
# Программное нажатие CapsLock через xdotool
def press_capslock():
try:
# Программное нажатие CapsLock через xdotool
subprocess.run(["xdotool", "key", "Caps_Lock"], check=True)
logging.info("Pressed CapsLock programmatically.")
# Добавляем задержку для стабилизации состояния клавиш
time.sleep(0.5)
# Проверяем текущую раскладку
current_layout = get_current_layout()
logging.info(f"Current layout after pressing CapsLock: {current_layout}")
# Если раскладка не изменилась, повторяем нажатие
if current_layout == "us": # Предполагаем, что мы хотели переключиться на 'ru'
logging.warning("CapsLock did not switch layout. Retrying...")
subprocess.run(["xdotool", "key", "Caps_Lock"], check=True)
time.sleep(0.5)
new_layout = get_current_layout()
logging.info(f"New layout after retrying CapsLock: {new_layout}")
except Exception as e:
logging.error(f"Failed to press CapsLock: {e}")
# Получение имени активного окна
def get_active_window_name(ewmh):
active_window = ewmh.getActiveWindow()
if active_window:
window_name = ewmh.getWmName(active_window)
# Преобразуем байты в строку
return (
window_name.decode("utf-8")
if isinstance(window_name, bytes)
else window_name
)
return None
# Основной цикл
def main():
ewmh = EWMH() # Инициализация EWMH для работы с окнами
last_active_window = None
while True:
try:
# Получаем имя активного окна
active_window = get_active_window_name(ewmh)
if not active_window:
logging.warning("Active window name is empty or not available.")
time.sleep(0.5)
continue
# Если окно изменилось
if active_window != last_active_window:
last_active_window = active_window
logging.info(f"Active window changed to: {active_window}")
# Проверяем, есть ли окно в таблице соответствия
for window_name, layout in WINDOW_LAYOUT_MAP.items():
if window_name in active_window:
# Получаем текущую раскладку
current_layout = get_current_layout()
# Если текущая раскладка не совпадает с нужной
if layout and current_layout != layout:
logging.info(
f"Mismatch detected: Current layout '{current_layout}', desired layout '{layout}'."
)
press_capslock()
else:
logging.info(
f"Layout matches: Current layout '{current_layout}'."
)
break
else:
logging.info(
f"No specific layout for window '{active_window}'. Using default behavior."
)
# Ждем немного перед следующей проверкой
time.sleep(0.1)
except KeyboardInterrupt:
logging.info("Script interrupted by user. Exiting...")
break
except Exception as e:
logging.error(f"Unexpected error: {e}")
if __name__ == "__main__":
main()
Предложи варианты. Может как через дбас переключать.