LINUX.ORG.RU

История изменений

Исправление 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()

Предложи варианты. Может как через дбас переключать.