--- PROJECT EXPORT ---
Working Directory: /home/tonda/Dokumenty/Clanek-ZEnbook-Duo/asus-screen-toggle

📁 PROJECT STRUCTURE:
.
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── TODO.txt
├── assets
│   ├── menu_items.png
│   ├── settings_hw.png
│   ├── settings_main.png
│   ├── settings_services.png
│   └── statbar_icon.png
├── asus-screen-toggle
│   ├── Makefile
│   ├── build.conf
│   ├── src
│   │   ├── bin
│   │   │   ├── asus-check-keyboard-system.sh.in
│   │   │   ├── channels
│   │   │   │   ├── dbus.sh
│   │   │   │   ├── direct.sh
│   │   │   │   ├── noop-debug.sh
│   │   │   │   ├── signal.sh
│   │   │   │   └── systemd.sh
│   │   │   └── helpers.sh
│   │   └── lib
│   │       └── systemd
│   │           └── user
│   │               ├── asus-screen-toggle.service
│   │               └── asus-user-agent.service
│   └── usr
│       ├── bin
│       │   ├── asus-check-keyboard-genrules.sh
│       │   ├── asus-check-keyboard-user.sh
│       │   ├── asus-check-rotation.sh
│       │   ├── asus-screen-settings.py
│       │   ├── asus-screen-toggle-launcher.sh
│       │   └── asus-user-agent.py
│       ├── lib
│       │   └── systemd
│       │       ├── system
│       │       │   └── asus-bottom-screen-init.service
│       │       ├── system-sleep
│       │       │   └── 99-asus-screen-resume
│       │       └── user
│       └── share
│           ├── 99-asus-keyboard.rules.template
│           ├── applications
│           │   ├── asus-screen-settings.desktop
│           │   └── asus-screen-toggle-agent.desktop
│           ├── asus-screen-toggle
│           │   ├── icon-blue.svg
│           │   ├── icon-green.svg
│           │   └── icon-red.svg
│           └── man
│               └── man1
│                   ├── asus-check-keyboard-genrules.1
│                   ├── asus-check-keyboard-system.1
│                   ├── asus-check-keyboard-user.1
│                   ├── asus-check-rotation.1
│                   ├── asus-screen-settings.1
│                   ├── asus-screen-toggle-launcher.1
│                   ├── asus-user-agent.1
│                   └── cs
│                       ├── asus-check-keyboard-genrules.1
│                       ├── asus-check-keyboard-system.1
│                       ├── asus-check-keyboard-user.1
│                       ├── asus-check-rotation.1
│                       ├── asus-screen-settings.1
│                       ├── asus-screen-toggle-launcher.1
│                       └── asus-user-agent.1
├── asus-screen-toggle.cflags
├── asus-screen-toggle.config
├── asus-screen-toggle.creator
├── asus-screen-toggle.creator.user
├── asus-screen-toggle.cxxflags
├── asus-screen-toggle.files
├── asus-screen-toggle.includes
├── asus-screen-toggle_1.1.1.orig.tar.gz
├── backup&devutils
│   ├── asus-bottom-screen-loged.service
│   ├── asus-check-keyboard-kscreen-and-xrandr.sh
│   ├── asus-check-keyboard-kscreen.sh
│   ├── asus-check-keyboard-wlr-randr.sh
│   ├── asus-check-keyboard-xrandr.sh
│   ├── asus-check-keyboard.sh
│   ├── asus-user-agent.sh
│   ├── detect-sessions-message.sh
│   ├── detect-sessions.sh
│   └── screen_size.sh
├── build
│   └── locale
│       ├── ar
│       │   └── LC_MESSAGES
│       │       └── asus-screen-toggle.mo
│       ├── cs
│       │   └── LC_MESSAGES
│       │       └── asus-screen-toggle.mo
│       ├── en
│       │   └── LC_MESSAGES
│       │       └── asus-screen-toggle.mo
│       ├── es
│       │   └── LC_MESSAGES
│       │       └── asus-screen-toggle.mo
│       ├── fr
│       │   └── LC_MESSAGES
│       │       └── asus-screen-toggle.mo
│       ├── ja
│       │   └── LC_MESSAGES
│       │       └── asus-screen-toggle.mo
│       ├── pt
│       │   └── LC_MESSAGES
│       │       └── asus-screen-toggle.mo
│       └── zh
│           └── LC_MESSAGES
│               └── asus-screen-toggle.mo
├── compile_locales.sh
├── debian
│   ├── asus-screen-toggle
│   │   └── usr
│   │       └── bin
│   │           ├── asus-check-keyboard-user
│   │           ├── asus-screen-settings
│   │           └── asus-user-agent
│   ├── changelog
│   ├── clean
│   ├── config
│   ├── control
│   ├── copyright
│   ├── debhelper-build-stamp
│   ├── install
│   ├── patches
│   ├── po
│   │   ├── POTFILES.in
│   │   ├── cs.po
│   │   └── templates.pot
│   ├── postinst
│   ├── postrm
│   ├── rules
│   ├── source
│   │   ├── format
│   │   └── options
│   ├── templates
│   ├── upstream
│   │   └── metadata
│   └── watch
├── docs
│   ├── RELEASE.md
│   ├── asus-screen-settings.html
│   ├── asus-user-agent.html
│   ├── index.cs.html
│   └── index.html
├── full.txt
├── gen_locales.sh
├── po
│   ├── ar.po
│   ├── asus-screen-toggle.pot
│   ├── cs.po
│   ├── en.po
│   ├── es.po
│   ├── fr.po
│   ├── ja.po
│   ├── pt.po
│   └── zh.po
├── release.sh
└── release_local.sh


================================================================================

📄 PATH: README.md
--------------------------------------------------------------------------------
Asus Screen Toggle

Automatic secondary display management for Asus Zenbook Duo laptops on Linux (KDE/Debian).

Debian Mentors: https://mentors.debian.net/package/asus-screen-toggle/ ITP Bug: https://bugs.debian.org/1124010
Overview

This utility monitors your hardware state (lid status and external keyboard connection) to automatically enable or disable the secondary screen. It is specifically designed for the Asus Zenbook Duo (2024) UX8406.
Key Features

    Automatic Toggle: Enables/disables the bottom screen based on the keyboard connection.

    Smart Orientation: Full support for screen rotation via iio-sensor-proxy.

    User Interface: A GTK3 settings app and a system tray agent for manual overrides.

    Debian Integrated: Developed following Debian packaging standards.

Installation (Debian/KDE)

The package is currently in the process of being included in the official Debian repositories. You can download the latest version from Debian Mentors.
Build from source

    sudo apt install devscripts build-essential

    git clone https://github.com/antoninmicka/asus-screen-toggle

    cd asus-screen-toggle

    mk-build-deps -ir

    dpkg-buildpackage -us -uc

    sudo apt install ../asus-screen-toggle_*.deb

Documentation

Detailed documentation is available via manual pages:

    man asus-user-agent

    man asus-screen-settings

Or visit the Project Website: https://antoninmicka.github.io/asus-screen-toggle/
Compatibility

    Environment: Debian Sid/Testing/12 with KDE Plasma.

    Hardware: Asus Zenbook Duo (2024) UX8406.

    Dependencies: python3-gi, pydbus, gir1.2-appindicator3-0.1, iio-sensor-proxy.

License

This project is licensed under the GPL-3.0 License. Part of the Asus-Linux (https://asus-linux.org) community efforts.


================================================================================

📄 PATH: gen_locales.sh
--------------------------------------------------------------------------------
#!/bin/bash
# Skript pro generování a aktualizaci překladů (gettext)

DOMAIN="asus-screen-toggle"
LOCALE_DIR="po"
FILES_PY="asus-screen-toggle/usr/bin/*.py"
FILES_SH="asus-screen-toggle/usr/bin/*.sh"

mkdir -p "$LOCALE_DIR"

echo "1. Extrahování řetězců z Pythonu a Shellu..."
# Vytvoření šablony (.pot)
# -k_: Klíčové slovo pro python je _
# --from-code=UTF-8: Kódování
xgettext -L Python -k_ -o "$LOCALE_DIR/$DOMAIN.pot" $FILES_PY --from-code=UTF-8
# Připojíme řetězce ze Shell skriptů (-j = join)
xgettext -L Shell -k_ -j -o "$LOCALE_DIR/$DOMAIN.pot" $FILES_SH --from-code=UTF-8

echo "Šablona vytvořena: $LOCALE_DIR/$DOMAIN.pot"

# Seznam jazyků
LANGS=("en" "cs" "fr" "es" "pt" "zh" "ja" "ar")

for lang in "${LANGS[@]}"; do
    PO_FILE="$LOCALE_DIR/$lang.po"

    if [ -f "$PO_FILE" ]; then
        echo "Aktualizuji $lang.po ..."
        msgmerge -U "$PO_FILE" "$LOCALE_DIR/$DOMAIN.pot"
    else
        echo "Vytvářím nový $lang.po ..."
        msginit --no-translator -l "$lang" -o "$PO_FILE" -i "$LOCALE_DIR/$DOMAIN.pot"
    fi
done

echo "Hotovo. Nyní upravte soubory .po v adresáři $LOCALE_DIR/ a doplňte překlady."
echo "Pro kompilaci (test) spusťte: bash compile_locales.sh"


================================================================================

📄 PATH: release.sh
--------------------------------------------------------------------------------
#!/usr/bin/env bash
set -euo pipefail

die() { echo "ERROR: $*" >&2; exit 1; }

MODE=""
VERSION=""

while [[ $# -gt 0 ]]; do
  case "$1" in
    --patch|--minor|--major) MODE="${1#--}" ;;
    --version) VERSION="$2"; shift ;;
    *) die "Unknown argument $1" ;;
  esac
  shift
done

git diff --quiet || die "Working tree not clean"

LAST=$(git tag --sort=-v:refname | grep '^v' | head -n1 | sed 's/^v//')

if [[ -z "$VERSION" ]]; then
  [[ -z "$MODE" ]] && die "Use --patch / --minor / --major or --version"
  IFS=. read -r M m p <<<"$LAST"
  case "$MODE" in
    patch) p=$((p+1)) ;;
    minor) m=$((m+1)); p=0 ;;
    major) M=$((M+1)); m=0; p=0 ;;
  esac
  VERSION="$M.$m.$p"
fi

DEB_VERSION="$VERSION-1"

echo "Releasing $VERSION"

dch -v "$DEB_VERSION"
git commit -am "Release $VERSION"

git tag "v$VERSION"
git push origin main --tags

echo "Tag v$VERSION pushed – GitHub CI will validate the source."


================================================================================

📄 PATH: compile_locales.sh
--------------------------------------------------------------------------------
#!/bin/bash
# gen_locales.sh
# Skript pro extrakci textů a aktualizaci .po souborů

DOMAIN="asus-screen-toggle"
PO_DIR="po"
SRC_DIR="asus-screen-toggle/usr/bin"

# Seznam podporovaných jazyků
LANGS=("en" "cs" "fr" "es" "pt" "zh" "ja" "ar")

mkdir -p "$PO_DIR"

echo "1. Extrahování řetězců..."

# Vytvoření šablony (.pot)
# Python soubory
xgettext -L Python -k_ --from-code=UTF-8 -o "$PO_DIR/$DOMAIN.pot" "$SRC_DIR"/*.py

# Bash soubory (připojíme k existující šabloně pomocí -j)
xgettext -L Shell -k_ --from-code=UTF-8 -j -o "$PO_DIR/$DOMAIN.pot" "$SRC_DIR"/*.sh

echo "Šablona vytvořena: $PO_DIR/$DOMAIN.pot"

# Aktualizace/Vytvoření .po souborů pro jednotlivé jazyky
for lang in "${LANGS[@]}"; do
    PO_FILE="$PO_DIR/$lang.po"

    if [ -f "$PO_FILE" ]; then
        echo "🔄 Aktualizuji $lang.po ..."
        msgmerge -U --backup=none "$PO_FILE" "$PO_DIR/$DOMAIN.pot"
    else
        echo "✨ Vytvářím nový $lang.po ..."
        msginit --no-translator -l "$lang" -o "$PO_FILE" -i "$PO_DIR/$DOMAIN.pot"

        # Oprava charsetu, msginit někdy nastaví ASCII
        sed -i 's/Content-Type: text\/plain; charset=ASCII/Content-Type: text\/plain; charset=UTF-8/' "$PO_FILE"
    fi
done

echo "✅ Hotovo. Nyní přeložte soubory v adresáři $PO_DIR/"


================================================================================

📄 PATH: CONTRIBUTING.md
--------------------------------------------------------------------------------
# Contributing to Asus Screen Toggle

First of all, thank you for considering contributing to this project! It's people like you that make the Linux ecosystem on Asus laptops better.

## How Can I Contribute?

### Reporting Bugs
- Check the [Issues](../../issues) tab to see if the bug has already been reported.
- If not, open a new issue. Include your laptop model, Linux distribution, and desktop environment (KDE/GNOME/etc.).
- Provide logs if possible: `journalctl -u asus-screen-toggle`.

### Suggesting Enhancements
- Open an issue with the tag `enhancement`.
- Describe the feature and why it would be useful.

### Pull Requests
1. Fork the repository.
2. Create a new branch for your feature (`git checkout -b feature/amazing-feature`).
3. Commit your changes.
4. Push to the branch.
5. Open a Pull Request.

## Code Style
- **Python:** Please follow PEP 8.
- **Shell:** Use `shellcheck` before submitting scripts.
- **Translations:** If you want to add a new language, look into the `po/` directory.

## License
By contributing, you agree that your contributions will be licensed under its GPL-3.0 License.


================================================================================

📄 PATH: release_local.sh
--------------------------------------------------------------------------------
#!/usr/bin/env bash
set -euo pipefail

PROJECT="asus-screen-toggle"

# ---------- helpers ----------
die() { echo "ERROR: $*" >&2; exit 1; }

require_clean_git() {
    git diff --quiet || die "Working tree is dirty"
    git diff --cached --quiet || die "Index is dirty"
}

get_last_version() {
    git tag --sort=-v:refname | grep '^v' | head -n1 | sed 's/^v//'
}

bump_version() {
    local v=$1 mode=$2
    IFS=. read -r major minor patch <<<"$v"
    case "$mode" in
        patch) patch=$((patch+1)) ;;
        minor) minor=$((minor+1)); patch=0 ;;
        major) major=$((major+1)); minor=0; patch=0 ;;
        *) die "Unknown bump mode: $mode" ;;
    esac
    echo "$major.$minor.$patch"
}

pick_gpg_key() {
    mapfile -t KEYS < <(gpg --list-secret-keys --keyid-format LONG \
        | awk '/^sec/{print $2}' | cut -d/ -f2)

    [[ ${#KEYS[@]} -eq 0 ]] && die "No GPG keys found"

    if [[ ${#KEYS[@]} -eq 1 ]]; then
        echo "${KEYS[0]}"
        return
    fi

    echo "Select GPG key:"
    select k in "${KEYS[@]}"; do
        echo "$k"
        return
    done
}

# ---------- args ----------
MODE=""
VERSION=""

while [[ $# -gt 0 ]]; do
    case "$1" in
        --patch|--minor|--major) MODE="${1#--}" ;;
        --version) VERSION="$2"; shift ;;
        *) die "Unknown argument $1" ;;
    esac
    shift
done

# ---------- main ----------
require_clean_git

LAST=$(get_last_version)
[[ -z "$LAST" && -z "$VERSION" ]] && die "No previous tag, use --version"

if [[ -z "$VERSION" ]]; then
    [[ -z "$MODE" ]] && die "Use --patch / --minor / --major or --version"
    VERSION=$(bump_version "$LAST" "$MODE")
fi

DEB_VERSION="${VERSION}-1"
TAG="v$VERSION"

echo "Releasing version $VERSION"

git tag "$TAG"

ORIG="../${PROJECT}_${VERSION}.orig.tar.gz"

git archive \
  --format=tar.gz \
  --prefix="${PROJECT}-${VERSION}/" \
  "$TAG" > "$ORIG"

dch -v "$DEB_VERSION"

rm -rf .pc debian/patches/*.rej || true

KEY=$(pick_gpg_key)

dpkg-buildpackage -S -sa "-k$KEY"

dput mentors "../${PROJECT}_${DEB_VERSION}_source.changes"

echo "Release $VERSION uploaded successfully"


================================================================================

📄 PATH: docs/RELEASE.md
--------------------------------------------------------------------------------
# 📌 Release workflow – základní pravidla

## Význam větví
- develop
 - běžný vývoj
 - experimenty, feature větve
 - nikdy se z ní nevydává
- release/*
 - příprava vydání
 - stabilizace, ladění
 - CI ověřuje releasovatelnost
- hotfix/*
 - opravy vydaných verzí
 - CI ověřuje releasovatelnost
- main
 - obsahuje pouze vydané verze
 - každý commit odpovídá jednomu vydání
 - vždy označený tagem

- Release a hotfix větve mají tvar release/X.Y nebo hotfix/X.Y.
- Název větve neurčuje konkrétní verzi balíku.
- Konkrétní upstream verze je určena výhradně git tagem.
 
## Pravidla pro vydání
- vydání probíhá výhradně merge do main
- do main se:
 - nepushuje přímo
 - merguje se pouze přes PR
- stav v main:
 - prošel CI
 - je stabilní
 - je vhodný k vydání

- Release a hotfix větve mají tvar release/X.Y nebo hotfix/X.Y.
- CI odmítne jakoukoli větev s jiným názvem.
- Při vydání se kontroluje, že upstream verze odpovídá větvi (X.Y.Z ∈ X.Y.x).

## Tagování
- každý release commit v main:
 - má odpovídající git tag vX.Y.Z
- tagy:
 - se vytváří pouze nad main
 - nikdy nad develop nebo release/*

## Debian balení
- main je jediný zdroj pravdy pro:
 - orig.tar.gz
 - Debian source balík
- upstream tarball:
 - vzniká z git tagu
 - ne z pracovního stromu

## Automatizace (zatím neřeší detaily)
- CI:
 - ověřuje každý commit v release/* a hotfix/*
- upload:
 - probíhá lokálně z maintainerova stroje
 - po merge do main

# 🥈 CI jako gate pro release/* a hotfix/*
- Každý commit v release/* a hotfix/* musí projít CI.
- CI ověřuje, že zdroj lze zabalit a znovu rozbalit jako Debian source balík.
- Pokud CI selže, změna nesmí být mergnuta do main.

# 🥉 Lokální publish krok (maintainer-controlled release)
- Vydání balíku se provádí lokálně z počítače maintainera.
- Publish krok probíhá pouze po merge do main.
- Publish zahrnuje tagování, vytvoření Debian source balíku, podpis a upload.
- CI ani GitHub nikdy balík nepodepisují ani nenahrávají.

# 🏷️ Git tag jako jediný zdroj pravdy
- Každé vydání je identifikováno git tagem vX.Y.Z.
- Upstream tarball (orig.tar.gz) musí být vytvořen výhradně z tohoto tagu.
- Debian source balík musí odpovídat tomuto tarballu.
- Pracovní strom ani jiné větve nejsou zdrojem vydání.

# 🔢 Význam verzí (upstream vs Debian)
- Upstream verze (X.Y.Z) popisuje stav projektu a je reprezentována git tagem.
- Debian revize (-N) popisuje změny v balení a nemění upstream kód.
- Změna upstream verze vždy znamená nový tag.
- Změna Debian revize nikdy neznamená nový tag.

# 🩹 Hotfix větve a backporty
- Hotfix větve se vytvářejí z vydané verze (main / tag).
- Slouží pouze k opravám chyb ve vydaném kódu.
- Hotfix zvyšuje upstream patch verzi.
- Hotfix se po vydání merguje zpět do develop.

# 🛡️ Ochrana větví a role maintainerů
- Vydání balíků provádí pouze maintainer.
- Větev main je chráněná a obsahuje pouze vydané verze.
- Přímé pushování do main není povoleno.
- Každé vydání je vědomé rozhodnutí maintainera.

# 🔄 Chyby, opravy a rollback
- Vydané verze se nikdy nemění ani nepřepisují.
- Chyby po vydání se řeší novým vydáním (hotfix nebo nová Debian revize).
- Tagy a historie se nikdy nemažou ani nepřepisují.
- Rollback znamená vydání opravené verze, nikoli návrat v historii.


================================================================================

📄 PATH: asus-screen-toggle/usr/bin/asus-screen-settings.py
--------------------------------------------------------------------------------
#!/usr/bin/env python3
import sys
import os
import subprocess
import gi
import gettext
import locale
import shutil

# Nastavení lokalizace
APP_NAME = "asus-screen-toggle"
LOCALE_DIR = "/usr/share/locale"

try:
    # Pokusíme se nastavit systémovou locale
    locale.setlocale(locale.LC_ALL, '')

    # Inicializace gettext
    gettext.bindtextdomain(APP_NAME, LOCALE_DIR)
    gettext.textdomain(APP_NAME)
    _ = gettext.gettext
except Exception as e:
    # Fallback, pokud gettext selže (např. při vývoji mimo instalaci)
    print(f"Warning: Localization not loaded: {e}")
    _ = lambda s: s

try:
    gi.require_version('Gtk', '3.0')
    from gi.repository import Gtk, GLib, GdkPixbuf
except ValueError:
    print(_("Error: Gtk 3.0 not found."))
    sys.exit(1)

# Pokus o import pydbus pro komunikaci s Agentem
try:
    from pydbus import SessionBus
    DBUS_AVAILABLE = True
except ImportError:
    DBUS_AVAILABLE = False
    print(_("Warning: pydbus not found. Direct D-Bus communication disabled."))

# --- Konstanty ---
APP_TITLE = _("Nastavení Asus Screen Toggle")
BUS_NAME = "org.asus.ScreenToggle" # D-Bus jméno agenta
USER_SERVICE = "asus-screen-toggle.service"
SYSTEM_SERVICE = "asus-bottom-screen-init.service"
SYSTRAY_SERVICE = "asus-user-agent.service"

# Cesty ke konfiguracím
SYSTEM_CONFIG_FILE = "/etc/asus-screen-toggle.conf"
USER_CONFIG_FILE = os.path.expanduser("~/.config/asus-screen-toggle/user.conf")

# Cesty pro logiku přepínání (stejné jako v User Agent)
STATE_DIR = os.path.expanduser("~/.local/state/asus-check-keyboard")
STATE_FILE = os.path.join(STATE_DIR, "state")
SCRIPT_PATH = shutil.which("asus-check-keyboard-user") or "/usr/bin/asus-check-keyboard-user"

GENRULES_PATH = shutil.which("asus-check-keyboard-genrules") or "/usr/bin/asus-check-keyboard-genrules"

# Cesty k ikonám
ICON_PATH = "/usr/share/asus-screen-toggle"
ICON_AUTO = os.path.join(ICON_PATH, "icon-green.svg")
ICON_PRIMARY = os.path.join(ICON_PATH, "icon-red.svg")
ICON_DESKTOP = os.path.join(ICON_PATH, "icon-blue.svg")

class AsusSettingsApp(Gtk.Window):
    def __init__(self):
        super().__init__(title=APP_TITLE)
        self.set_border_width(10)
        self.set_default_size(650, 600)
        self.set_position(Gtk.WindowPosition.CENTER)

        # Notebook s kartami
        self.notebook = Gtk.Notebook()

        # Layout
        main_layout = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.add(main_layout)
        main_layout.pack_start(self.notebook, True, True, 0)

        # --- KARTA 0: DOMŮ (Rychlé ovládání) ---
        self.page_home = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=20)
        self.page_home.set_border_width(20)
        self.notebook.append_page(self.page_home, Gtk.Label(label="Domů"))

        # Nadpis
        lbl_welcome = Gtk.Label(label=_("<span size='x-large' weight='bold'>Rychlé ovládání</span>"))
        lbl_welcome.set_use_markup(True)
        self.page_home.pack_start(lbl_welcome, False, False, 10)

        hbox_modes = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
        hbox_modes.set_halign(Gtk.Align.CENTER)
        self.page_home.pack_start(hbox_modes, True, True, 0)

        # 1. Tlačítko AUTO
        self.btn_mode_auto = self.create_mode_button(_("Automaticky"), ICON_AUTO, _("Senzory"), "automatic-enabled")
        hbox_modes.pack_start(self.btn_mode_auto, True, True, 0)

        # 2. Tlačítko PRIMARY
        self.btn_mode_primary = self.create_mode_button(_("Jen Hlavní"), ICON_PRIMARY, _("Vypnout spodní"), "enforce-primary-only")
        hbox_modes.pack_start(self.btn_mode_primary, True, True, 0)

        # 3. Tlačítko DESKTOP
        self.btn_mode_desktop = self.create_mode_button(_("Oba Displeje"), ICON_DESKTOP, _("Vynutit zapnutí"), "enforce-desktop")
        hbox_modes.pack_start(self.btn_mode_desktop, True, True, 0)

        # Oddělovač
        self.page_home.pack_start(Gtk.Separator(), False, False, 10)

        # Tlačítko Kontrola
        btn_check = Gtk.Button(label=_("🔄 Spustit okamžitou kontrolu"))
        btn_check.set_property("width-request", 300)
        btn_check.set_halign(Gtk.Align.CENTER)
        btn_check.get_style_context().add_class("suggested-action") # Modré zvýraznění
        btn_check.connect("clicked", lambda x: self.run_check())
        self.page_home.pack_start(btn_check, False, False, 10)

        # --- KARTA 1: OBECNÉ (Služby a uživatelské chování) ---
        self.page_general = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.page_general.set_border_width(10)
        self.notebook.append_page(self.page_general, Gtk.Label(label=_("Služby & Config")))

        # 1. Sekce: Správa Služeb
        frame_services = Gtk.Frame(label=_("Stav Služeb"))
        self.page_general.pack_start(frame_services, False, False, 0)

        vbox_services = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
        vbox_services.set_border_width(10)
        frame_services.add(vbox_services)

        # User Service
        self.status_user = Gtk.Label(label="...")
        self.btn_user_toggle = Gtk.Button(label="...")
        self.btn_user_toggle.connect("clicked", self.on_user_service_toggle)
        self.switch_user_enable = Gtk.Switch()
        self.switch_user_enable.connect("notify::active", self.on_user_enable_toggle)
        vbox_services.pack_start(self.create_service_row(_("Uživatelská služba (Agent)"), self.status_user, self.btn_user_toggle, self.switch_user_enable), False, False, 0)

        vbox_services.pack_start(Gtk.Separator(), False, False, 5)
        self.status_systray = Gtk.Label(label="...")
        self.btn_systray_toggle = Gtk.Button(label="...")
        self.btn_systray_toggle.connect("clicked", self.on_systray_service_toggle)
        self.switch_systray_enable = Gtk.Switch()
        self.switch_systray_enable.connect("notify::active", self.on_systray_enable_toggle)
        vbox_services.pack_start(self.create_service_row(_("Indikátor stauu (Systray)"), self.status_systray, self.btn_systray_toggle, self.switch_systray_enable), False, False, 0)

        vbox_services.pack_start(Gtk.Separator(), False, False, 5)

        # System Service
        self.status_system = Gtk.Label(label="...")
        self.btn_system_toggle = Gtk.Button(label="...")
        self.btn_system_toggle.connect("clicked", self.on_system_service_toggle)
        self.switch_system_enable = Gtk.Switch()
        self.switch_system_enable.connect("notify::active", self.on_system_enable_toggle)
        vbox_services.pack_start(self.create_service_row(_("Systémová služba (Init)"), self.status_system, self.btn_system_toggle, self.switch_system_enable), False, False, 0)

        # 2. Sekce: Uživatelská konfigurace
        frame_user = Gtk.Frame(label=_("Uživatelská konfigurace"))
        self.page_general.pack_start(frame_user, False, False, 0)
        vbox_user = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
        vbox_user.set_border_width(10)
        frame_user.add(vbox_user)

        lbl_info_user = Gtk.Label(label=_("<i>Ukládá se do ~/.config/asus-screen-toggle/config.conf</i>"), use_markup=True, xalign=0)
        vbox_user.pack_start(lbl_info_user, False, False, 5)

        self.user_chk_dbus = Gtk.CheckButton(label=_("Povolit D-Bus ovládání (ENABLE_DBUS)"))
        self.user_chk_signal = Gtk.CheckButton(label=_("Povolit reakci na signály/rotaci (ENABLE_SIGNAL)"))

        vbox_user.pack_start(self.user_chk_dbus, False, False, 0)
        vbox_user.pack_start(self.user_chk_signal, False, False, 0)


        # --- KARTA 2: HARDWARE (Systémová konfigurace) ---
        self.page_hw = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        self.page_hw.set_border_width(10)
        self.notebook.append_page(self.page_hw, Gtk.Label(label=_("Hardware (Systém)")))

        lbl_hw_info = Gtk.Label(label=_("<i>Tyto změny se zapíší do /etc/asus-screen-toggle.conf (vyžaduje root).</i>"), use_markup=True, xalign=0)
        self.page_hw.pack_start(lbl_hw_info, False, False, 0)

        # Checkbox pro systémové chování
        self.sys_chk_systemd = Gtk.CheckButton(label=_("Povolit Systemd ovládání (ENABLE_SYSTEMD_CALL)"))
        self.sys_chk_dbus = Gtk.CheckButton(label=_("Povolit D-Bus ovládání (ENABLE_DBUS)"))
        self.sys_chk_signal = Gtk.CheckButton(label=_("Povolit reakci na signály/rotaci (ENABLE_SIGNAL)"))
        self.sys_chk_direct = Gtk.CheckButton(label=_("Povolit přímé volání z Udev (ENABLE_DIRECT_CALL)"))
        self.page_hw.pack_start(self.sys_chk_dbus, False, False, 0)
        self.page_hw.pack_start(self.sys_chk_signal, False, False, 0)
        self.page_hw.pack_start(self.sys_chk_direct, False, False, 0)
        self.page_hw.pack_start(self.sys_chk_systemd, False, False, 0)
        self.page_hw.pack_start(Gtk.Separator(), False, False, 5)

        grid_hw = Gtk.Grid()
        grid_hw.set_column_spacing(10)
        grid_hw.set_row_spacing(10)
        self.page_hw.pack_start(grid_hw, False, False, 0)

        # Vytvoření vstupních polí
        self.entry_vendor = Gtk.Entry()
        self.entry_product = Gtk.Entry()
        self.entry_primary = Gtk.Entry()
        self.entry_secondary = Gtk.Entry()
        self.entry_lid = Gtk.Entry()

        row = 0
        self.add_config_row(grid_hw, row, _("Vendor ID:"), self.entry_vendor, _("Např. 0b05")); row+=1
        self.add_config_row(grid_hw, row, _("Product ID:"), self.entry_product, _("Např. 1bf2")); row+=1
        self.add_config_row(grid_hw, row, _("Hlavní displej:"), self.entry_primary, _("Např. eDP-1")); row+=1
        self.add_config_row(grid_hw, row, _("Sekundární displej:"), self.entry_secondary, _("Např. eDP-2")); row+=1
        self.add_config_row(grid_hw, row, _("Senzor víka:"), self.entry_lid, _("Např. LID nebo LID0")); row+=1

        # Propojení checkboxů pro okamžitou odezvu v UI
        self.sys_chk_dbus.connect("toggled", lambda w: self.user_chk_dbus.set_sensitive(w.get_active()))
        self.sys_chk_signal.connect("toggled", lambda w: self.user_chk_signal.set_sensitive(w.get_active()))

        # --- Spodní lišta tlačítek ---
        bbox = Gtk.ButtonBox(layout_style=Gtk.ButtonBoxStyle.END)
        main_layout.pack_end(bbox, False, False, 0)

        btn_refresh = Gtk.Button(label=_("Obnovit"))
        btn_refresh.connect("clicked", self.refresh_all)
        bbox.add(btn_refresh)

        btn_save = Gtk.Button(label=_("Uložit vše"))
        btn_save.get_style_context().add_class("suggested-action")
        btn_save.connect("clicked", self.on_save_clicked)
        bbox.add(btn_save)

        # Start
        self.current_mode_in_ui = None # Pro sledování stavu UI
        self.refresh_all()
        GLib.timeout_add_seconds(3, self.refresh_services_only)

    # --- UI Helpers pro Domovskou stránku ---

    def create_mode_button(self, title, icon_path, subtitle, mode_id):
        """Vytvoří velké tlačítko s ikonou pro výběr režimu."""
        btn = Gtk.Button()
        btn.set_relief(Gtk.ReliefStyle.NORMAL)
        btn.set_size_request(140, 160) # Pevná velikost

        # Obsah tlačítka (Vertical Box)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
        vbox.set_valign(Gtk.Align.CENTER)

        # Ikona
        try:
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(icon_path, 64, 64, True)
            img = Gtk.Image.new_from_pixbuf(pixbuf)
        except:
            img = Gtk.Image.new_from_icon_name("image-missing", Gtk.IconSize.DIALOG)

        vbox.pack_start(img, True, True, 5)

        # Texty
        lbl_title = Gtk.Label(label=f"<b>{title}</b>")
        lbl_title.set_use_markup(True)
        vbox.pack_start(lbl_title, False, False, 0)

        lbl_sub = Gtk.Label(label=f"<small>{subtitle}</small>")
        lbl_sub.set_use_markup(True)
        lbl_sub.get_style_context().add_class("dim-label")
        vbox.pack_start(lbl_sub, False, False, 0)

        btn.add(vbox)

        # Uložíme ID režimu do tlačítka pro callback
        btn.mode_id = mode_id
        btn.connect("clicked", self.on_mode_clicked)

        return btn

    def update_home_ui_state(self, current_mode):
        """Zvýrazní aktivní tlačítko podle režimu."""
        self.current_mode_in_ui = current_mode

        # Reset stylů (pomocí citlivosti - aktivní režim bude 'insensitive', tedy zamáčknutý)
        # Nebo lépe: Všechny sensitive, ale aktivnímu dáme jiný styl nebo relief.
        # Zde použijeme logiku: Aktivní tlačítko je "deaktivované" (nejde na něj znovu kliknout) a vypadá zamáčkle.

        for btn, mid in [(self.btn_mode_auto, "automatic-enabled"),
                         (self.btn_mode_primary, "enforce-primary-only"),
                         (self.btn_mode_desktop, "enforce-desktop")]:
            if mid == current_mode:
                btn.set_sensitive(False) # Vizuálně indikuje "vybráno"
                # btn.get_style_context().add_class("suggested-action") # Alternativa pro GTK CSS
            else:
                btn.set_sensitive(True)

    def on_mode_clicked(self, btn):
        mode = btn.mode_id
        print(_(f"UI: Požadavek na změnu režimu -> {mode}"))

        success = False

        # 1. Zkusit D-Bus (synchronizace s Agentem)
        if DBUS_AVAILABLE:
            try:
                bus = SessionBus()
                # Získáme proxy objekt
                agent_proxy = bus.get(BUS_NAME) # Získá hlavní object path
                # Voláme metodu SetMode
                resp = agent_proxy.SetMode(mode)
                print(_(f"D-Bus odpověď: {resp}"))
                success = True
            except Exception as e:
                print(_(f"D-Bus chyba (Agent neběží?): {e}"))

        # 2. Fallback: Zápis do souboru (pokud D-Bus selhal)
        if not success:
            print(_("Fallback: Zapisuji přímo do souboru..."))
            try:
                os.makedirs(STATE_DIR, exist_ok=True)
                with open(STATE_FILE, 'w') as f:
                    f.write(mode)
                self.run_check() # Spustíme script manuálně
            except Exception as e:
                self.show_error(_(f"Nepodařilo se zapsat stav: {e}"))
                return

        # UI aktualizujeme hned pro odezvu (timer to pak potvrdí)
        self.update_home_ui_state(mode)

    def run_check(self):
        try:
            subprocess.Popen([SCRIPT_PATH])
        except Exception as e:
            self.show_error(_(f"Chyba při spouštění skriptu: {e}"))

    def periodic_refresh(self):
        self.refresh_all()
        return True # Pokračovat v timeru

    def refresh_all(self, widget=None):
        # 1. Režim (čtení ze souboru - synchronizace od Agenta k Aplikaci)
        if os.path.exists(STATE_FILE):
            try:
                with open(STATE_FILE, 'r') as f:
                    mode = f.read().strip()
                    if mode != self.current_mode_in_ui:
                        self.update_home_ui_state(mode)
            except: pass

        # 2. Configy
        self.load_configs()
        # 3. Služby
        self.refresh_services_only()

    # ... (Zbytek kódu: create_service_row, load_configs, on_save_clicked atd. zůstává stejný) ...

    # Pro úplnost kopíruji zkráceně zbytek metod, aby soubor byl kompletní:

    def create_service_row(self, title, label_status, btn_toggle, switch_enable):
        row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        lbl = Gtk.Label(label=title, xalign=0)
        lbl.set_size_request(200, -1)
        row.pack_start(lbl, False, False, 0)
        row.pack_start(label_status, True, True, 0)
        row.pack_start(btn_toggle, False, False, 0)
        row.pack_start(Gtk.Label(label=_("Boot:")), False, False, 0)
        row.pack_start(switch_enable, False, False, 0)
        return row

    def add_config_row(self, grid, row, label_text, entry_widget, placeholder):
        lbl = Gtk.Label(label=label_text, xalign=1)
        entry_widget.set_placeholder_text(placeholder)
        entry_widget.set_hexpand(True)
        grid.attach(lbl, 0, row, 1, 1)
        grid.attach(entry_widget, 1, row, 1, 1)

    def refresh_services_only(self):
        active, enabled = self.get_service_status(USER_SERVICE, user=True)
        self.update_service_ui(self.status_user, self.btn_user_toggle, self.switch_user_enable, active, enabled)

        active, enabled = self.get_service_status(SYSTRAY_SERVICE, user=True)
        self.update_service_ui(self.status_systray, self.btn_systray_toggle, self.switch_systray_enable, active, enabled)

        active, enabled = self.get_service_status(SYSTEM_SERVICE, user=False)
        self.update_service_ui(self.status_system, self.btn_system_toggle, self.switch_system_enable, active, enabled)
        return True

    def get_service_status(self, service, user=True):
        cmd = ["systemctl"]
        if user: cmd.append("--user")

        res_act = subprocess.run(cmd + ["is-active", service], stdout=subprocess.PIPE, text=True)
        res_en = subprocess.run(cmd + ["is-enabled", service], stdout=subprocess.PIPE, text=True)
        return (res_act.stdout.strip() == _("active"), res_en.stdout.strip() == _("enabled"))

    def update_service_ui(self, label, button, switch, active, enabled):
        switch.handler_block_by_func(self.on_user_enable_toggle if switch == self.switch_user_enable else self.on_system_enable_toggle if switch == self.switch_system_enable else self.on_systray_enable_toggle)
        switch.set_active(enabled)
        switch.handler_unblock_by_func(self.on_user_enable_toggle if switch == self.switch_user_enable else self.on_system_enable_toggle if switch == self.switch_system_enable else self.on_systray_enable_toggle)

        if active:
            label.set_markup(_("<span foreground='green'><b>Běží</b></span>"))
            button.set_label(_("Zastavit"))
        else:
            label.set_markup(_("<span foreground='red'>Zastaveno</span>"))
            button.set_label(_("Spustit"))

    def load_configs(self):
        # 1. Načíst SYSTÉMOVÉ nastavení (defaulty + /etc)
        sys_data = {
            "VENDOR_ID": "0b05", "PRODUCT_ID": "1bf2",
            "PRIMARY_DISPLAY_NAME": "eDP-1", "SECONDARY_DISPLAY_NAME": "eDP-2",
            "LID": "LID",
            "ENABLE_DIRECT_CALL": True,
            "ENABLE_DBUS": True, "ENABLE_SIGNAL": True, "ENABLE_SYSTEMD_CALL": True
        }

        if os.path.exists(SYSTEM_CONFIG_FILE):
            sys_data.update(self._parse_config_file(SYSTEM_CONFIG_FILE))

        # Aplikace do GUI - Hardware a Systém
        self.entry_vendor.set_text(str(sys_data.get("VENDOR_ID", "")))
        self.entry_product.set_text(str(sys_data.get("PRODUCT_ID", "")))
        self.entry_primary.set_text(str(sys_data.get("PRIMARY_DISPLAY_NAME", "")))
        self.entry_secondary.set_text(str(sys_data.get("SECONDARY_DISPLAY_NAME", "")))
        self.entry_lid.set_text(str(sys_data.get("LID", "")))

        # Systémové checkboxy
        sys_dbus_active = sys_data.get("ENABLE_DBUS") is True
        sys_signal_active = sys_data.get("ENABLE_SIGNAL") is True
        sys_systemd_active = sys_data.get("ENABLE_SYSTEMD_CALL") is True

        self.sys_chk_direct.set_active(sys_data.get("ENABLE_DIRECT_CALL") is True)
        self.sys_chk_dbus.set_active(sys_dbus_active)
        self.sys_chk_signal.set_active(sys_signal_active)
        self.sys_chk_systemd.set_active(sys_systemd_active)

        # 2. Načíst UŽIVATELSKÉ nastavení
        user_data = {}
        if os.path.exists(USER_CONFIG_FILE):
            user_data = self._parse_config_file(USER_CONFIG_FILE)

        # Logika AND detekovaná přímo v UI:
        # Pokud je SYSTÉM False, uživatel nesmí zapnout.

        # DBUS
        user_dbus_val = user_data.get("ENABLE_DBUS", sys_dbus_active)
        self.user_chk_dbus.set_active(user_dbus_val and sys_dbus_active)
        self.user_chk_dbus.set_sensitive(sys_dbus_active) # Zašedne, pokud systém zakázal

        # SIGNAL
        user_signal_val = user_data.get("ENABLE_SIGNAL", sys_signal_active)
        self.user_chk_signal.set_active(user_signal_val and sys_signal_active)
        self.user_chk_signal.set_sensitive(sys_signal_active) # Zašedne, pokud systém zakázal

        # Bonus: tooltip pro vysvětlení
        if not sys_signal_active:
            self.user_chk_signal.set_tooltip_text(_("Zakázáno správcem v /etc/asus-screen-toggle.conf"))


    def _parse_config_file(self, filepath):
        data = {}
        try:
            with open(filepath, 'r') as f:
                for line in f:
                    line = line.strip()
                    if not line or line.startswith("#") or "=" not in line: continue
                    key, val = line.split("=", 1)
                    key = key.strip()
                    val = val.strip().replace('"', '')

                    if val.lower() == "true": data[key] = True
                    elif val.lower() == "false": data[key] = False
                    else: data[key] = val
        except: pass
        return data

    # --- Logika ukládání ---
    def on_save_clicked(self, widget):
        # 1. ULOŽENÍ UŽIVATELSKÉHO CONFIGU (~/.config)
        # Zde ukládáme POUZE preference chování
        try:
            os.makedirs(os.path.dirname(USER_CONFIG_FILE), exist_ok=True)
            with open(USER_CONFIG_FILE, 'w') as f:
                f.write(_("# Uživatelská konfigurace Asus Screen Toggle\n"))
                f.write(f"ENABLE_DBUS={'true' if self.user_chk_dbus.get_active() else 'false'}\n")
                f.write(f"ENABLE_SIGNAL={'true' if self.user_chk_signal.get_active() else 'false'}\n")
        except Exception as e:
            self.show_error(_(f"Nepodařilo se uložit uživatelský config: {e}"))
            return

        # 2. ULOŽENÍ SYSTÉMOVÉHO CONFIGU (/etc)
        # Zde ukládáme Hardware a Direct Call
        sys_content = [
            _("# Vygenerováno Asus Screen Settings - Systémová konfigurace"),
            f'VENDOR_ID="{self.entry_vendor.get_text()}"',
            f'PRODUCT_ID="{self.entry_product.get_text()}"',
            f'PRIMARY_DISPLAY_NAME="{self.entry_primary.get_text()}"',
            f'SECONDARY_DISPLAY_NAME="{self.entry_secondary.get_text()}"',
            f'LID="{self.entry_lid.get_text()}"',
            "",
            f'ENABLE_DIRECT_CALL={"true" if self.sys_chk_direct.get_active() else "false"}',
            f'ENABLE_DBUS={"true" if self.sys_chk_dbus.get_active() else "false"}',
            f'ENABLE_SIGNAL={"true" if self.sys_chk_signal.get_active() else "false"}',
            f'ENABLE_SYSTEMD_CALL={"true" if self.sys_chk_systemd.get_active() else "false"}',
        ]

        file_content = "\n".join(sys_content)

        # Uložení přes pkexec
        cmd = f"cat <<EOF > /tmp/asus_conf_tmp\n{file_content}\nEOF\n"
        cmd += f"pkexec mv /tmp/asus_conf_tmp {SYSTEM_CONFIG_FILE}"

        try:
            # A) Samotné uložení souboru
            subprocess.run(["bash", "-c", cmd], check=True)

            # B) Dotaz na přegenerování pravidel (pouze pokud se uložení povedlo)
            confirm = Gtk.MessageDialog(transient_for=self, flags=0,
                                      message_type=Gtk.MessageType.QUESTION,
                                      buttons=Gtk.ButtonsType.YES_NO,
                                      text=_("Aktualizovat Udev pravidla?"))
            confirm.format_secondary_text(
                _("Změnili jste systémové nastavení. Pro správnou funkčnost detekce hardwaru "
                "je třeba přegenerovat a načíst pravidla Udev.\n\n"
                "Chcete to provést nyní? (Vyžaduje heslo)")
            )
            response = confirm.run()
            confirm.destroy()

            if response == Gtk.ResponseType.YES:
                try:
                    # Sestavíme řetězec příkazů:
                    # 1. Spustit generovací skript
                    # 2. Reloadnout pravidla (pokud 1. prošla)
                    # 3. Triggerovat události (pokud 2. prošla)
                    full_cmd = (
                        "&GENRULES_PATH && "
                        "udevadm control --reload-rules && "
                        "udevadm trigger"
                    )

                    # Spustíme vše pod jedním pkexec (jedno heslo)
                    subprocess.run(["pkexec", "bash", "-c", full_cmd], check=True)

                except subprocess.CalledProcessError:
                     self.show_error(_("Nepodařilo se přegenerovat a aplikovat pravidla."))

            # C) Restart agenta (aby načetl případné změny v logice)
            subprocess.run(["systemctl", "--user", "kill", "-s", "HUP", USER_SERVICE])

            # Finální info
            msg = Gtk.MessageDialog(transient_for=self, flags=0, message_type=Gtk.MessageType.INFO,
                                  buttons=Gtk.ButtonsType.OK, text=_("Hotovo"))
            msg.format_secondary_text(_("Konfigurace byla úspěšně uložena."))
            msg.run()
            msg.destroy()

        except subprocess.CalledProcessError:
            self.show_error(_("Nepodařilo se uložit systémovou konfiguraci (zamítnuto)."))

    # --- Handlery Služeb ---
    def on_user_service_toggle(self, btn):
        action = "stop" if btn.get_label() == _("Zastavit") else "start"
        subprocess.run(["systemctl", "--user", action, USER_SERVICE])
        self.refresh_services_only()

    def on_systray_service_toggle(self, btn):
        action = "stop" if btn.get_label() == _("Zastavit") else "start"
        subprocess.run(["systemctl", "--user", action, SYSTRAY_SERVICE])
        self.refresh_services_only()

    def on_user_enable_toggle(self, switch, gparam):
        action = "enable" if switch.get_active() else "disable"
        subprocess.run(["systemctl", "--user", action, USER_SERVICE])
        self.refresh_services_only()

    def on_systray_enable_toggle(self, switch, gparam):
        action = "enable" if switch.get_active() else "disable"
        subprocess.run(["systemctl", "--user", action, SYSTRAY_SERVICE])
        self.refresh_services_only()

    def on_system_service_toggle(self, btn):
        action = "stop" if btn.get_label() == "Zastavit" else "start"
        subprocess.run(["pkexec", "systemctl", action, SYSTEM_SERVICE])
        self.refresh_services_only()

    def on_system_enable_toggle(self, switch, gparam):
        action = "enable" if switch.get_active() else "disable"
        subprocess.run(["pkexec", "systemctl", action, SYSTEM_SERVICE])
        self.refresh_services_only()

    def show_error(self, message):
        dialog = Gtk.MessageDialog(transient_for=self, flags=0, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text=_("Chyba"))
        dialog.format_secondary_text(message)
        dialog.run()
        dialog.destroy()

if __name__ == "__main__":
    app = AsusSettingsApp()
    app.connect("destroy", Gtk.main_quit)
    app.show_all()
    Gtk.main()


================================================================================

📄 PATH: asus-screen-toggle/usr/bin/asus-check-keyboard-genrules.sh
--------------------------------------------------------------------------------
#!/bin/bash

source /etc/asus-screen-toggle.conf

export VENDOR_ID PRODUCT_ID PRIMARY_DISPLAY_NAME SECONDARY_DISPLAY_NAME LID

echo "vid $VENDOR_ID"
echo "pid $PRODUCT_ID"
echo "pd $PRIMARY_DISPLAY_NAME"
echo "sd $SECONDARY_DISPLAY_NAME"
echo "lid $LID"

#envsubst < /usr/share/asus-screen-toggle/99-asus-keyboard.rules.template > /usr/lib/udev/rules.d/99-asus-keyboard.rules
envsubst < /usr/share/asus-screen-toggle/99-asus-keyboard.rules.template > /etc/udev/rules.d/99-asus-keyboard.rules



================================================================================

📄 PATH: asus-screen-toggle/usr/bin/asus-check-keyboard-user.sh
--------------------------------------------------------------------------------
#!/bin/bash

export TEXTDOMAIN="asus-screen-toggle"
export TEXTDOMAINDIR="/usr/share/locale"

# Funkce pro zjednodušení (alias)
function _() {
    gettext "$1"
}

# --- 1. Načtení konfigurace ---
if [[ -f /etc/asus-screen-toggle.conf ]]; then
    source /etc/asus-screen-toggle.conf
else
    # Pokud konfig neexistuje, nastavíme defaulty, aby skript nespadl
    VENDOR_ID="0b05"
    PRODUCT_ID="1bf2"
    PRIMARY_DISPLAY_NAME="eDP-1"
    SECONDARY_DISPLAY_NAME="eDP-2"
fi

# Pokud máme rotaci z monitor-sensor (uloženou v tmp), načteme ji
if [[ -f /tmp/asus-rotation ]]; then
    source /tmp/asus-rotation
else
    # Fallback, pokud neexistuje temp soubor (např. ruční spuštění)
    # timeout zajistí, že se nezasekne, pokud senzor není dostupný
    DIR=$(timeout 0.5 monitor-sensor --accel | grep -m 1 orientation)
fi

# --- 2. Zjištění požadavku uživatele (State) ---
APP_NAME="asus-check-keyboard"
STATE_FILE="$HOME/.local/state/$APP_NAME/state"
USER_STATE="automatic-enabled"

if [[ -f "$STATE_FILE" ]]; then
    USER_STATE=$(<"$STATE_FILE")
fi

printf "$(_ "VID: %s, PID: %s")\n" "$VENDOR_ID" "$PRODUCT_ID"
printf "$(_ "User mode: %s")\n" "$USER_STATE"
printf "$(_ "Sensor: %s")\n" "$DIR"

# --- 3. Rozhodovací logika (Matrix) ---

# A) Je fyzicky připojená klávesnice?
PHYSICAL_KEYBOARD_CONNECTED=false
if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
    PHYSICAL_KEYBOARD_CONNECTED=true
    echo "$(_ "Fyzická klávesnice: PŘIPOJENA")"
else
    echo "$(_ "Fyzická klávesnice: ODPOJENA")"
fi

# B) Výpočet finálního stavu spodního displeje
# Defaultně (Auto) platí: Klávesnice je připojená => Vypnout spodek.
ENABLE_BOTTOM_SCREEN=true
if [[ "$PHYSICAL_KEYBOARD_CONNECTED" == "true" ]]; then
    ENABLE_BOTTOM_SCREEN=false
fi

# C) Aplikace vynucených režimů (Overrides)
case "$USER_STATE" in
    enforce-primary-only)
        echo "$(_ "Vynuceno: Jen primární displej")"
        ENABLE_BOTTOM_SCREEN=false
        ;;
    enforce-desktop)
        echo "$(_ "Vynuceno: Desktop mód (oba displeje)")"
        ENABLE_BOTTOM_SCREEN=true
        ;;
    *)
        echo "$(_ "Režim Auto: Ponechávám vypočtený stav.")"
        ;;
esac


# --- 4. Příprava proměnných pro rotaci (KDE/X11) ---
# Ponecháme logiku pro DISPLAY_ROTATION beze změny, jen ji sem zkopírujte
# nebo nechte, pokud editujete soubor. Zde pro úplnost:

DISPLAY_ROTATION="normal"
case "$DIR" in
*normal*)    DISPLAY_ROTATION="normal"   ;;
*bottom-up*) DISPLAY_ROTATION="inverted" ;;
*left-up*)   DISPLAY_ROTATION="left"     ;;
*right-up*)  DISPLAY_ROTATION="right"    ;;
esac

# --- 5. Aplikace nastavení (X11 / KDE / Wayland) ---

user=$USER
type=$XDG_SESSION_TYPE
desktop_env=$XDG_CURRENT_DESKTOP

if [[ "$user" == "sddm" ]]; then exit 0; fi

# === X11 ===
if [[ "$type" == "x11" ]]; then
    # Zde používáme naši vypočtenou proměnnou ENABLE_BOTTOM_SCREEN
    if [[ "$ENABLE_BOTTOM_SCREEN" == "false" ]]; then
        echo "$(_ "Aplikuji: Single Screen (X11)")"
        xrandr --output ${PRIMARY_DISPLAY_NAME} --rotate normal --output ${SECONDARY_DISPLAY_NAME} --off
    else
        printf "$(_ "Aplikuji: Dual Screen (X11) - %s")\n" "$DISPLAY_ROTATION"
        case "$DISPLAY_ROTATION" in
            *left*) xrandr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --right-of ${SECONDARY_DISPLAY_NAME} --output ${SECONDARY_DISPLAY_NAME} --auto --rotate ${DISPLAY_ROTATION} ;;
            *right*) xrandr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --left-of ${SECONDARY_DISPLAY_NAME} --output ${SECONDARY_DISPLAY_NAME} --auto --rotate ${DISPLAY_ROTATION} ;;
            *inverted*) xrandr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --below ${SECONDARY_DISPLAY_NAME} --output ${SECONDARY_DISPLAY_NAME} --auto --rotate ${DISPLAY_ROTATION} ;;
            *normal*) xrandr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --above ${SECONDARY_DISPLAY_NAME} --output ${SECONDARY_DISPLAY_NAME} --auto --rotate ${DISPLAY_ROTATION} ;;
        esac
    fi
    exit 0
fi

# === Wayland (KDE) ===
if [[ "$type" == "wayland" && "$desktop_env" == "KDE" ]]; then
    if [[ "$ENABLE_BOTTOM_SCREEN" == "false" ]]; then
        printf "%s\n" "$(_ "Aplikuji: Single Screen (KDE)")"
        /usr/bin/kscreen-doctor output.${PRIMARY_DISPLAY_NAME}.rotation.normal
        /usr/bin/kscreen-doctor output.${SECONDARY_DISPLAY_NAME}.disable
    else
        printf "$(_ "Aplikuji: Dual Screen (KDE) - %s")\n" "$DISPLAY_ROTATION"
        /usr/bin/kscreen-doctor output.${PRIMARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}
        /usr/bin/kscreen-doctor output.${SECONDARY_DISPLAY_NAME}.enable output.${SECONDARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}

        # --- Získání geometrie primárního výstupu ---
        read PX PY PW PH <<< $(kscreen-doctor -o | awk -v out="$PRIMARY_DISPLAY_NAME" '$0 ~ "Output: " && $0 ~ out { in_block=1; next } in_block && $0 ~ "Geometry:" { split($3, pos, ","); split($4, res, "x"); print pos[1], pos[2], res[1], res[2]; exit }')
        echo "$PRIMARY_DISPLAY_NAME $PX,$PY,$PW,$PH"

        # --- Získání velikosti sekundárního výstupu ---
        read SX SY SW SH <<< $(kscreen-doctor -o | awk -v out="$SECONDARY_DISPLAY_NAME" '$0 ~ "Output: " && $0 ~ out { in_block=1; next } in_block && $0 ~ "Geometry:" { split($3, pos, ","); split($4, res, "x"); print pos[1], pos[2], res[1], res[2]; exit }')
        echo "$SECONDARY_DISPLAY_NAME $SX,$SY,$SW,$SH"

        PX=0
        PY=0

        # --- Výpočet nové pozice ---
        case "$DISPLAY_ROTATION" in
            *left*)
                PX=$(echo "$PX" | tr -dc '0-9')
                SW=$(echo "$SW" | tr -dc '0-9')
                SX=$((PX - SW))
                SY=$PY
                ;;
            *right*)
                PX=$(echo "$PX" | tr -dc '0-9')
                PW=$(echo "$PW" | tr -dc '0-9')
                SX=$((PX + PW))
                SY=$PY
                ;;
            *inverted*)
                SX=$PX
                SY=$((PY - SH))
                ;;
            *normal*)
                SX=$PX
                SY=$((PY + PH))
                ;;
            *)
                printf "$(_ "Neplatná orientace: %s")\n" "$DISPLAY_ROTATION"
                exit 1
                ;;
        esac

        echo "$PRIMARY_DISPLAY_NAME $PX,$PY,$PW,$PH"
        echo "$SECONDARY_DISPLAY_NAME $SX,$SY,$SW,$SH"

        # --- Výstup a nastavení ---
        printf "$(_ "Umísťuji %s (%s) od %s na souřadnice %s,%s")\n" "$SECONDARY_DISPLAY_NAME" "$DISPLAY_ROTATION" "$PRIMARY_DISPLAY_NAME" "$SX" "$SY"

        kscreen-doctor output.$PRIMARY_DISPLAY_NAME.position.$PX,$PY output.${PRIMARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}
        kscreen-doctor output.$SECONDARY_DISPLAY_NAME.position.$SX,$SY output.${SECONDARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}

    fi
    exit 0
fi

# === Wayland (Generic / wlroots) ===
if [[ "$type" == "wayland" ]]; then
    if [[ "$ENABLE_BOTTOM_SCREEN" == "false" ]]; then
        echo "$(_ "Aplikuji: Single Screen (Wlr)")"
        wlr-randr --output ${PRIMARY_DISPLAY_NAME} --rotate normal
        wlr-randr --output ${SECONDARY_DISPLAY_NAME} --off
    else
        printf "$(_ "Aplikuji: Dual Screen (Wlr) - %s")\n" "$DISPLAY_ROTATION"
        wlr-randr --output ${SECONDARY_DISPLAY_NAME} --auto --rotate ${DISPLAY_ROTATION}
        case "$DISPLAY_ROTATION" in
            *left*) wlr-randr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --right-of ${SECONDARY_DISPLAY_NAME} ;;
            *right*) wlr-randr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --left-of ${SECONDARY_DISPLAY_NAME} ;;
            *inverted*) wlr-randr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --below ${SECONDARY_DISPLAY_NAME} ;;
            *normal*) wlr-randr --output ${PRIMARY_DISPLAY_NAME} --rotate ${DISPLAY_ROTATION} --above ${SECONDARY_DISPLAY_NAME} ;;
        esac
    fi
    exit 0
fi


================================================================================

📄 PATH: asus-screen-toggle/usr/bin/asus-screen-toggle-launcher.sh
--------------------------------------------------------------------------------
#!/bin/bash
set -e

# Seznam služeb: worker (oneshot) a agent (systray)
SERVICES=("asus-screen-toggle.service" "asus-user-agent.service")

echo "▶️ Asus Screen Toggle – launcher"

# Kontrola, aby neběželo pod rootem (uživatelské služby spravuje uživatel)
if [[ $EUID -eq 0 ]]; then
    echo "❌ Tento launcher nesmí běžet jako root."
    exit 1
fi

# 1️⃣ Obnovení konfigurace systemd (načte změny v .service souborech)
systemctl --user daemon-reload

# 2️⃣ Aktivace a spuštění obou komponent
for SERVICE in "${SERVICES[@]}"; do
    echo "--- Správa jednotky: $SERVICE ---"

    # Povolení služby (aby se spouštěla automaticky při startu graphical-session)
    if ! systemctl --user is-enabled --quiet "$SERVICE"; then
        echo "🔔 Povoluji $SERVICE"
        systemctl --user enable "$SERVICE"
    else
        echo "✅ $SERVICE je povolena"
    fi

    # Spuštění služby
    # U oneshotu (worker) to provede nastavení, u simple (agent) to spustí tray ikonu
    echo "▶️ Spouštím/Restartuji $SERVICE"
    systemctl --user restart "$SERVICE"
done

echo "🎉 Hotovo – nastavení aplikováno a agent běží."


================================================================================

📄 PATH: asus-screen-toggle/usr/bin/asus-user-agent.py
--------------------------------------------------------------------------------
#!/usr/bin/env python3
import sys
import os
import signal
import subprocess
import warnings
import time # Nový import pro čas
import gettext
import locale

# Nastavení lokalizace
APP_NAME = "asus-screen-toggle"
LOCALE_DIR = "/usr/share/locale"

try:
    # Pokusíme se nastavit systémovou locale
    locale.setlocale(locale.LC_ALL, '')

    # Inicializace gettext
    gettext.bindtextdomain(APP_NAME, LOCALE_DIR)
    gettext.textdomain(APP_NAME)
    _ = gettext.gettext
except Exception as e:
    # Fallback, pokud gettext selže (např. při vývoji mimo instalaci)
    print(f"Warning: Localization not loaded: {e}")
    _ = lambda s: s

warnings.filterwarnings("ignore")
from pydbus.generic import signal as Signal

# --- Importy knihoven ---
print(_("DEBUG: Načítám knihovny..."))
try:
    import gi
    try:
        gi.require_version('AyatanaAppIndicator3', '0.1')
        from gi.repository import AyatanaAppIndicator3 as AppIndicator
    except (ValueError, ImportError):
        try:
            gi.require_version('AppIndicator3', '0.1')
            from gi.repository import AppIndicator3 as AppIndicator
        except (ValueError, ImportError):
            print(_("CHYBA: Nenalezena knihovna AppIndicator."))
            sys.exit(1)

    gi.require_version('Gtk', '3.0')
    from gi.repository import GLib, Gtk
    from pydbus import SessionBus
except Exception as e:
    print(_(f"CHYBA při importu knihoven: {e}"))
    sys.exit(1)

# --- Konfigurace ---
BUS_NAME = "org.asus.ScreenToggle"
SCRIPT_PATH = "/usr/bin/asus-check-keyboard-user"
APP_ID = "asus-screen-toggler"
ICON_NAME = "input-tablet"
ICON_PATH = "/usr/share/asus-screen-toggle"

ICON_AUTO_NAME = "icon-green.svg"
ICON_PRIMARY_NAME = "icon-red.svg"
ICON_DESKTOP_NAME = "icon-blue.svg"
ICON_AUTO = os.path.join(ICON_PATH, ICON_AUTO_NAME)
ICON_PRIMARY = os.path.join(ICON_PATH, ICON_PRIMARY_NAME)
ICON_DESKTOP = os.path.join(ICON_PATH, ICON_DESKTOP_NAME)

STATE_DIR = os.path.expanduser("~/.local/state/asus-check-keyboard")
STATE_FILE = os.path.join(STATE_DIR, "state")
CONFIG_FILE = os.path.expanduser("~/.config/asus-screen-toggle/config.conf")

class StatusNotifierItem:
    """
    <node>
      <interface name="org.kde.StatusNotifierItem">
        <property name="Category" type="s" access="read"/>
        <property name="Id" type="s" access="read"/>
        <property name="Title" type="s" access="read"/>
        <property name="Status" type="s" access="read"/>
        <property name="IconName" type="s" access="read"/>
        <property name="IconThemePath" type="s" access="read"/>
        <property name="ItemIsMenu" type="b" access="read"/>
        <property name="ToolTip" type="(sa(iiay)ss)" access="read"/>

        <method name="Activate">
          <arg type="i" direction="in"/>
          <arg type="i" direction="in"/>
        </method>

        <method name="ContextMenu">
          <arg type="i" direction="in"/>
          <arg type="i" direction="in"/>
        </method>

        <method name="SecondaryActivate">
          <arg type="i" direction="in"/>
          <arg type="i" direction="in"/>
        </method>

        <signal name="NewIcon"/>
        <signal name="NewStatus"/>
        <signal name="NewToolTip"/>
      </interface>
    </node>
    """
    NewIcon = Signal()
    NewStatus = Signal()
    NewToolTip = Signal()

    def __init__(self, agent):
        self.agent = agent
        self.icon_name = ICON_AUTO_NAME
        self.status = "Active"

    @property
    def Category(self): return "Hardware"
    @property
    def Id(self): return "asus-screen-toggle"
    @property
    def Title(self): return _("Asus Screen Toggle")
    @property
    def Status(self): return self.status
    @property
    def IconName(self): return self.icon_name
    @property
    def IconThemePath(self): return ICON_PATH
    @property
    def ItemIsMenu(self): return False
    @property
    def Menu(self): return "/StatusNotifierItem"
    @property
    def ToolTip(self): return (self.icon_name, [], _("Asus Screen Toggle"), _(f"Režim: {self.agent.mode}"))

    def Activate(self, x, y):
        """Levý klik (SNI): Spustí přímo nastavení."""
        # Voláme pomocnou metodu agenta
        GLib.idle_add(self.agent._launch_settings)

    def ContextMenu(self, x, y):
        GLib.idle_add(self.agent._show_gtk_menu, 3)

    def SecondaryActivate(self, x, y):
        self.agent._run_check("SNI_MiddleClick")

    def set_icon(self, name):
        base_name = os.path.splitext(os.path.basename(name))[0]
        if self.icon_name != base_name:
            self.icon_name = base_name
            self.NewIcon()
            self.NewToolTip()

    def set_status(self, status):
        self.status = status
        self.NewStatus()

class AsusAgent:
    """
    <node>
      <interface name="org.asus.ScreenToggle">
        <method name="Trigger"/>
        <method name="SetMode">
          <arg type="s" name="mode" direction="in"/>
        </method>
        <method name="ReloadConfig"/>
        <method name="Quit"/>
      </interface>
    </node>
    """

    def __init__(self, quit_callback, bus):
        self.quit_callback = quit_callback
        self.mode = self._load_mode()
        self.config = self._load_config()
        self.bus = bus
        self.indicator = None
        self.tray_backend = None
        self.menu = None

        # Pro sledování změn souboru
        self.last_file_mtime = 0
        if os.path.exists(STATE_FILE):
            self.last_file_mtime = os.stat(STATE_FILE).st_mtime

        if is_kde():
            try:
                self._setup_sni()
            except Exception as e:
                print(f_("SNI failed, fallback na AppIndicator: {e}"))
                self._setup_appindicator()
                self.tray_backend = "appindicator"
        else:
            self._setup_appindicator()
            self.tray_backend = "appindicator"

        # Timer pro sledování externích změn souboru (každé 2s)
        GLib.timeout_add_seconds(2, self._monitor_file_change)

    # --- Konfigurace ---
    def _load_config(self):
        """
        Načte konfiguraci s prioritou:
        1. Defaultní hodnoty (v kódu)
        2. Systémová konfigurace (/etc/asus-screen-toggle.conf)
        3. Uživatelská konfigurace (~/.config/asus-screen-toggle/config.conf)
        """
        # 1. Defaultní hodnoty
        cfg = {"enable_dbus": True, "enable_signal": True}

        # Seznam souborů v pořadí, jak se mají aplikovat (poslední vyhrává)
        config_paths = [
            "/etc/asus-screen-toggle.conf",
            os.path.expanduser("~/.config/asus-screen-toggle/config.conf")
        ]

        for path in config_paths:
            if os.path.exists(path):
                try:
                    print(_(f"⚙️ Načítám soubor: {path}"))
                    with open(path, 'r') as f:
                        for line in f:
                            if "=" in line and not line.strip().startswith("#"):
                                key, val = line.strip().split("=", 1)
                                if key.strip().upper() == "ENABLE_DBUS": cfg["enable_dbus"] = (val.strip().lower() == "true")
                                if key.strip().upper() == "ENABLE_SIGNAL": cfg["enable_signal"] = (val.strip().lower() == "true")
                except: pass
        return cfg

    def _monitor_file_change(self):
        """Kontroluje, zda se soubor nezměnil externě (např. přes GUI Settings)."""
        if os.path.exists(STATE_FILE):
            try:
                mtime = os.stat(STATE_FILE).st_mtime
                if mtime != self.last_file_mtime:
                    # Soubor se změnil!
                    self.last_file_mtime = mtime
                    new_mode = self._load_mode(silent=True)
                    if new_mode != self.mode:
                        print(_(f"🔄 Detekována externí změna stavu -> {new_mode}"))
                        self.mode = new_mode
                        self._set_icon_by_mode()
                        # Zde nespouštíme _run_check, protože předpokládáme,
                        # že ten kdo soubor změnil (Settings App), už skript spustil nebo spustí.
                        # Jen aktualizujeme ikonu.
            except: pass
        return True # Pokračovat v timeru

    def _load_mode(self, silent=False):
        if os.path.exists(STATE_FILE):
            try:
                with open(STATE_FILE, 'r') as f:
                    mode = f.read().strip()
                    if mode in ["automatic-enabled", "enforce-primary-only", "enforce-desktop"]:
                        print(_(f"📂 Načten režim ze souboru: {mode}"))
                        return mode
            except: pass
        return "automatic-enabled"

    def _save_mode(self, mode):
        try:
            os.makedirs(STATE_DIR, exist_ok=True)
            with open(STATE_FILE, 'w') as f:
                f.write(mode)
            print(_(f"💾 Režim '{mode}' uložen do {STATE_FILE}"))
        except Exception as e:
            print(_(f"❌ Chyba configu: {e}"))

    # --- D-Bus Metody ---
    def Trigger(self):
        if not self.config["enable_dbus"]: return "DISABLED_BY_CONFIG"
        if self.mode != "automatic-enabled": return f"IGNORED: Mode is {self.mode}"
        self._run_check("D-Bus")
        return "OK"

    def SetMode(self, mode_str):
        if mode_str not in ["automatic-enabled", "enforce-primary-only", "enforce-desktop"]: return "ERROR"
        print(_(f"📨 D-Bus SetMode: {mode_str}"))
        self.mode = mode_str
        self._save_mode(mode_str)
        self._set_icon_by_mode()
        self._run_check("D-Bus_SetMode")
        return _(f"OK: Switched to {mode_str}")

    def Quit(self):
        print("🛑 Požadavek na ukončení...")
        self.quit_callback()

    def _launch_settings(self):
        try: subprocess.Popen(["/usr/bin/asus-screen-settings"])
        except: pass
        return False

    def _run_check(self, source="Internal"):
        print(_(f"🚀 Spouštím logiku ({source})..."))
        try: subprocess.Popen([SCRIPT_PATH])
        except: pass

    def _set_icon_by_mode(self):
        if self.tray_backend == "sni":
            if self.mode == "automatic-enabled": self.sni.set_icon(ICON_AUTO_NAME)
            elif self.mode == "enforce-primary-only": self.sni.set_icon(ICON_PRIMARY_NAME)
            else: self.sni.set_icon(ICON_DESKTOP_NAME)
        elif self.indicator:
            icon_to_set = ICON_NAME
            if self.mode == "automatic-enabled":
                if os.path.exists(ICON_AUTO): icon_to_set = ICON_AUTO
                self.indicator.set_status(AppIndicator.IndicatorStatus.ACTIVE)
            elif self.mode == "enforce-primary-only":
                if os.path.exists(ICON_PRIMARY): icon_to_set = ICON_PRIMARY
                self.indicator.set_status(AppIndicator.IndicatorStatus.ATTENTION)
            else:
                if os.path.exists(ICON_DESKTOP): icon_to_set = ICON_DESKTOP
                self.indicator.set_status(AppIndicator.IndicatorStatus.ATTENTION)
            try: self.indicator.set_icon(icon_to_set)
            except: self.indicator.set_icon(ICON_NAME)

    def _on_mode_change(self, widget, mode_name):
        if widget.get_active():
            self.mode = mode_name
            self._save_mode(mode_name)
            self._set_icon_by_mode()
            self._run_check("MenuChange")

    def _build_menu(self):
        menu = Gtk.Menu()

        item = Gtk.MenuItem(label=_("Asus Screen Control"))
        item.set_sensitive(False)
        menu.append(item)
        menu.append(Gtk.SeparatorMenuItem())

        r_auto = Gtk.RadioMenuItem(label=_("🤖 Automaticky"))
        r_auto.connect("toggled", self._on_mode_change, "automatic-enabled")
        menu.append(r_auto)

        group = r_auto.get_group()
        r_prim = Gtk.RadioMenuItem(label=_("💻 Jen hlavní displej"), group=group[0])
        r_prim.connect("toggled", self._on_mode_change, "enforce-primary-only")
        menu.append(r_prim)

        r_both = Gtk.RadioMenuItem(label=_("🖥️🖥️ Oba displeje"), group=group[0])
        r_both.connect("toggled", self._on_mode_change, "enforce-desktop")
        menu.append(r_both)

        if self.mode == "automatic-enabled": r_auto.set_active(True)
        elif self.mode == "enforce-primary-only": r_prim.set_active(True)
        elif self.mode == "enforce-desktop": r_both.set_active(True)

        menu.append(Gtk.SeparatorMenuItem())
        item_sets = Gtk.MenuItem(label=_("⚙️ Nastavení"))
        item_sets.connect("activate", lambda _: self._launch_settings())
        menu.append(item_sets)

        item_check = Gtk.MenuItem(label=_("Zkontrolovat"))
        item_check.connect("activate", lambda _: self._run_check())
        menu.append(item_check)

        menu.append(Gtk.SeparatorMenuItem())

        item_quit = Gtk.MenuItem(label=_("Ukončit"))
        item_quit.connect("activate", lambda _: self.Quit())
        menu.append(item_quit)

        menu.show_all()
        return menu

    def _setup_appindicator(self):
        self.indicator = AppIndicator.Indicator.new(
            APP_ID, ICON_NAME, AppIndicator.IndicatorCategory.HARDWARE
        )
        self._set_icon_by_mode()
        self.indicator.set_menu(self._build_menu())

    def _setup_sni(self):
        print(_("🔵 Inicializuji KDE StatusNotifierItem (SNI)"))
        self.sni = StatusNotifierItem(self)
        try:
            self.bus.register_object("/StatusNotifierItem", self.sni, None)
            self.tray_backend = "sni"
            self._set_icon_by_mode()
            print(_("✅ SNI objekt vytvořen."))
        except Exception as e:
            print(_(f"❌ Chyba SNI: {e}"))
            raise e

    def register_sni_watcher(self):
        if self.tray_backend == "sni":
            try:
                watcher = self.bus.get("org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher")
                watcher.RegisterStatusNotifierItem(BUS_NAME)
                print(_("✅ SNI registrováno u KDE Watchera."))
                self.sni.NewIcon()
                self.sni.NewStatus()
            except Exception as e:
                print(_(f"⚠️ Watcher error: {e}"))

    def _show_gtk_menu(self, button):
        try:
            self.menu = self._build_menu()
            self.menu.show_all()
            self.menu.popup(None, None, None, None, 0, 0)
        except Exception as e:
            print(_(f"❌ Chyba při zobrazování menu: {e}"))
        return False


def is_kde():
    return os.environ.get("XDG_CURRENT_DESKTOP", "").lower() == "kde"

# --- Main Boilerplate ---
loop = None
publication = None

def quit_app(*args):
    global publication, loop
    print(_("\n🧹 Ukončuji..."))
    if publication:
        try: publication.unpublish()
        except: pass
    if loop: Gtk.main_quit()
    sys.exit(0)

def signal_handler():
    # <--- NOVÉ: Kontrola konfigurace pro signály (včetně systémového skriptu)
    if not agent.config["enable_signal"]:
        print(_("📩 Signál SIGUSR1 ZAMÍTNUT (vypnuto v configu)."))
        return True

    if agent.mode == "automatic-enabled":
        print(_("📩 Signál SIGUSR1 přijat!"))
        agent._run_check("Signal")
    else:
        print(_(f"📩 Signál SIGUSR1 ignorován (Režim ze souboru: {agent.mode})."))
    return True

def sighup_handler():
    """Obsluha signálu SIGHUP - Reload konfigurace."""
    print(_("🔄 Signál SIGHUP přijat: Znovunačítám konfiguraci..."))
    # Zavoláme metodu agenta, která načte soubory znovu
    agent.config = agent._load_config()
    return True # Musí vracet True, aby naslouchání pokračovalo

if __name__ == "__main__":
    bus = SessionBus()

    dbus_sys = bus.get("org.freedesktop.DBus", "/org/freedesktop/DBus")
    if dbus_sys.NameHasOwner(BUS_NAME):
        print(_(f"⚠️ Agent už běží."))
        sys.exit(0)

    agent = AsusAgent(quit_callback=quit_app, bus=bus)

    try:
        publication = bus.publish(BUS_NAME, agent)
        print(_(f"✅ D-Bus jméno {BUS_NAME} získáno."))
        agent.register_sni_watcher()
    except Exception as e:
        print(_(f"❌ Start selhal: {e}"))
        sys.exit(1)

    GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGUSR1, signal_handler)
    GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGHUP, sighup_handler)
    GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGTERM, quit_app)
    GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, quit_app)

    print(_(f"✅ Asus Agent GUI spuštěn."))
    print(_(f"   Režim: {agent.mode}"))
    print(_(f"   PID: {os.getpid()}"))

    # Hlavní smyčka v bloku try/finally pro jistotu
    try:
        loop = Gtk.main()
    except KeyboardInterrupt:
        quit_app()
    finally:
        # Záchranná brzda, kdyby Gtk.main() spadlo jinak
        pass


================================================================================

📄 PATH: asus-screen-toggle/usr/bin/asus-check-rotation.sh
--------------------------------------------------------------------------------
#!/bin/bash

ROTATION=""

CHECH_BIN=$(command -v asus-check-keyboard-system || echo "/usr/bin/asus-check-keyboard-system")

monitor-sensor | while read -r line; do
  case "$line" in
    *"normal"*)      NEW_ROT="normal" ;;
    *"bottom-up"*)   NEW_ROT="inverted" ;;
    *"left-up"*)     NEW_ROT="left" ;;
    *"right-up"*)    NEW_ROT="right" ;;
    *) continue ;;  # jiné informace ignorujeme
  esac

  if [[ "$NEW_ROT" != "$ROTATION" ]]; then
    ROTATION="$NEW_ROT"
    echo "Nová orientace: $ROTATION"
    echo "DIR=\"$line\"" > /tmp/asus-rotation
    $CHECH_BIN
  fi

  sleep 3
done


================================================================================

📄 PATH: asus-screen-toggle/src/bin/helpers.sh
--------------------------------------------------------------------------------
prepare_user_context() {
    local sid="$1"

    user=$(loginctl show-session "$sid" -p Name --value)
    type=$(loginctl show-session "$sid" -p Type --value)
    desktop=$(loginctl show-session "$sid" -p Desktop --value)
    state=$(loginctl show-session "$sid" -p State --value)

    [[ "$state" == "active" ]] || return 1
    [[ "$user" != "sddm" && "$user" != "gdm" && "$user" != "lightdm" ]] || return 1
    [[ "$type" == "x11" || "$type" == "wayland" ]] || return 1

    USER_UID=$(loginctl show-session "$sid" -p User --value)

    runtime_path=$(loginctl show-session "$sid" -p RuntimePath --value)
    [[ -z "$runtime_path" ]] && runtime_path="/run/user/$USER_UID"

    dbus_address="unix:path=$runtime_path/bus"

    USER_BIN=$(command -v asus-check-keyboard-user || echo "/usr/bin/asus-check-keyboard-user")

    return 0
}


================================================================================

📄 PATH: asus-screen-toggle/src/bin/channels/direct.sh
--------------------------------------------------------------------------------
if [[ "${ENABLE_DIRECT_CALL:-false}" == "true" ]]; then

    if [[ "$type" == "x11" ]]; then
        display=$(loginctl show-session "$sid" -p Display --value)
        xauth_file="/home/$user/.Xauthority"
        [[ -f "$xauth_file" ]] || return 1

        sudo -u "$user" \
            env DISPLAY="$display" \
                XDG_SESSION_ID="$sid" \
                XDG_SESSION_TYPE="$type" \
                XDG_CURRENT_DESKTOP="$desktop" \
                XDG_RUNTIME_DIR="$runtime_path" \
                DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
            "$USER_BIN"
    fi

    if [[ "$type" == "wayland" ]]; then
        wayland_disp=$(loginctl show-session "$sid" -p WaylandDisplay --value)
        [[ -z "$wayland_disp" ]] && wayland_disp="wayland-0"

        sudo -u "$user" \
            env WAYLAND_DISPLAY="$wayland_disp" \
                XDG_SESSION_ID="$sid" \
                XDG_SESSION_TYPE="$type" \
                XDG_CURRENT_DESKTOP="$desktop" \
                XDG_RUNTIME_DIR="$runtime_path" \
                DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
            "$USER_BIN"
    fi
fi


================================================================================

📄 PATH: asus-screen-toggle/src/bin/channels/signal.sh
--------------------------------------------------------------------------------
if [[ "${ENABLE_SIGNAL:-false}" == "true" ]]; then
    AGENT_PID=$(pgrep -u "$user" -f "asus-user-agent" | head -n 1)

    if [[ -n "$AGENT_PID" ]]; then
        kill -SIGUSR1 "$AGENT_PID"
        exit 0
    fi
fi


================================================================================

📄 PATH: asus-screen-toggle/src/bin/channels/dbus.sh
--------------------------------------------------------------------------------
if [[ "${ENABLE_DBUS:-false}" == "true" ]]; then
    if sudo -u "$user" DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
        dbus-send --session --print-reply --reply-timeout=1000 \
        --dest=org.asus.ScreenToggle \
        /org/asus/ScreenToggle org.asus.ScreenToggle.Trigger \
        > /dev/null 2>&1; then
        exit 0
    fi
fi


================================================================================

📄 PATH: asus-screen-toggle/src/bin/channels/systemd.sh
--------------------------------------------------------------------------------
if [[ "${ENABLE_SYSTEMD_CALL:-false}" == "true" ]]; then
    if systemctl --user --machine="$USER_UID@.host" \
        start asus-screen-toggle.service \
        > /dev/null 2>&1; then
        exit 0
    fi
fi


================================================================================

📄 PATH: asus-screen-toggle/src/bin/channels/noop-debug.sh
--------------------------------------------------------------------------------
log "NOOP dispatcher invoked with reason: $REASON"


================================================================================

📄 PATH: .qtc_clangd/compile_commands.json
--------------------------------------------------------------------------------
[]

================================================================================

📄 PATH: backup&devutils/detect-sessions.sh
--------------------------------------------------------------------------------
#!/bin/bash

echo "Uživatel | Session | Typ | Prostředí"

loginctl list-sessions --no-legend | while read sid user seat rest; do
    type=$(loginctl show-session "$sid" -p Type --value)
    display=$(loginctl show-session "$sid" -p Display --value)
    wayland=$(loginctl show-session "$sid" -p WaylandDisplay --value)

    env_type=0
    env_info="nelze zjistit"

    if [ "$type" = "tty" ]; then
        env_type=1
        env_info="textová konzole"
    elif [ "$type" = "x11" ]; then
        env_type=2
        env_info="DISPLAY=$display"
    elif [ "$type" = "wayland" ]; then
        env_type=3
        env_info="WAYLAND_DISPLAY=$wayland"
    fi

    echo "$user | $sid | $env_type | $env_info"
done

echo ""
echo "Uživatelé v grafickém prostředí (X11/Wayland):"
loginctl list-sessions --no-legend | while read sid user seat rest; do
    type=$(loginctl show-session "$sid" -p Type --value)
    if [[ "$type" == "x11" || "$type" == "wayland" ]]; then
        display=$(loginctl show-session "$sid" -p Display --value)
        wayland=$(loginctl show-session "$sid" -p WaylandDisplay --value)
        echo "$user | $sid | $type | DISPLAY=$display WAYLAND_DISPLAY=$wayland"
    fi
done


================================================================================

📄 PATH: backup&devutils/asus-check-keyboard.sh
--------------------------------------------------------------------------------
#!/bin/bash

max_tries=1
delay=15
attempt=0

# Identifikace klávesnice (uprav podle potřeby)
VENDOR_ID="0B05"
PRODUCT_ID="1BF2"

# Výstupní jméno displeje (změň podle `kscreen-doctor -o`)
PRIMARY_DISPLAY_NAME="eDP-1"
SECONDARY_DISPLAY_NAME="eDP-2"

DIR=$(timeout 2 monitor-sensor --accel | grep orientation)
DISPLAY_ROTATION="normal"

KSCREEN_BIN=$(command -v kscreen-doctor || echo "/usr/bin/kscreen-doctor")

case "$DIR" in
*normal*)
    DISPLAY_ROTATION="normal"
    echo "qdbus org.kde.KWin /KWin setScreenRotation 0"
    ;;
*bottom-up*)
    DISPLAY_ROTATION="inverted"
    echo "qdbus org.kde.KWin /KWin setScreenRotation 180"
    ;;
*left-up*)
    DISPLAY_ROTATION="left"
    echo "qdbus org.kde.KWin /KWin setScreenRotation 270"
    ;;
*right-up*)
    DISPLAY_ROTATION="right"
    echo "qdbus org.kde.KWin /KWin setScreenRotation 90"
    ;;
esac

# while (( attempt < max_tries )); do
#     echo "⏳ Pokus $((attempt+1)) / $max_tries"

    sessions=$(loginctl list-sessions --no-legend | awk '{print $1}')
    for sid in $sessions; do
        user=$(loginctl show-session "$sid" -p Name --value)
        type=$(loginctl show-session "$sid" -p Type --value)

        if [[ "$type" == "x11" || "$user" == "sddm" ]]; then
            exit 0
        fi

        if [[ "$type" == "x11" || "$type" == "wayland" ]]; then
            USER_UID=$(loginctl show-session "$sid" -p User --value)
            runtime_dir=$(loginctl show-session "$sid" -p RuntimePath --value)
            runtime_dir="/run/user/$USER_UID"
            dbus_address="unix:path=$runtime_dir/bus"

            if [[ "$type" == "x11" ]]; then
                display=$(loginctl show-session "$sid" -p Display --value)
                xauth_file="/home/$user/.Xauthority"

                if [[ ! -f "$xauth_file" ]]; then
                    echo "⚠️  XAUTHORITY nenalezen pro uživatele $user. Přeskočeno."
                    continue
                fi

                echo "🟢 Nalezen X11 uživatel: $user"
                echo "sid=$sid"
                echo "DISPLAY=$display"
                echo "XDG_RUNTIME_DIR=$runtime_dir"

                # 💡 Tady můžeš dát X11-specifický příkaz
                #sudo -u "$user" \
                #    env DISPLAY="$display" \
                #         XDG_RUNTIME_DIR="$runtime_dir" \
                #         DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                #    notify-send "X11 sezení" "Uživatel $user je aktivní v X11"

                if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
                    echo "Klávesnice detekována, vypínám spodní displej..."
                    sudo -u "$user" \
                        env DISPLAY="$display" \
                            XDG_RUNTIME_DIR="$runtime_dir" \
                            DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                            XAUTHORITY="$xauth_file" \
                            xrandr --output ${SECONDARY_DISPLAY_NAME} --off
                else
                    echo "Klávesnice není připojena, zapínám spodní displej..."
                    sudo -u "$user" \
                        env DISPLAY="$display" \
                            XDG_RUNTIME_DIR="$runtime_dir" \
                            DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                            XAUTHORITY="$xauth_file" \
                            xrandr --output ${SECONDARY_DISPLAY_NAME} --auto
                fi
            fi

            if [[ "$type" == "wayland" ]]; then
                wayland=$(loginctl show-session "$sid" -p WaylandDisplay --value)
                wayland=wayland-0
                echo "🟢 Nalezen Wayland uživatel: $user"
                echo "sid=$sid"
                echo "WAYLAND_DISPLAY=$wayland"
                echo "XDG_RUNTIME_DIR=$runtime_dir"

                # 💡 Tady můžeš dát Wayland-specifický příkaz
                #sudo -u "$user" \
                #    env WAYLAND_DISPLAY="$wayland" \
                #         XDG_RUNTIME_DIR="$runtime_dir" \
                #         DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                #    notify-send "Wayland sezení" "Uživatel $user je aktivní ve Waylandu"
                sudo -u "$user" \
                    env WAYLAND_DISPLAY="$wayland" \
                        XDG_RUNTIME_DIR="$runtime_dir" \
                        DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                        $KSCREEN_BIN output.${PRIMARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}

                if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
                    echo "Klávesnice detekována, vypínám spodní displej..."
                    sudo -u "$user" \
                        env WAYLAND_DISPLAY="$wayland" \
                            XDG_RUNTIME_DIR="$runtime_dir" \
                            DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                            $KSCREEN_BIN output.${SECONDARY_DISPLAY_NAME}.disable
                else
                    echo "Klávesnice není připojena, zapínám spodní displej..."
                    sudo -u "$user" \
                        env WAYLAND_DISPLAY="$wayland" \
                            XDG_RUNTIME_DIR="$runtime_dir" \
                            DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                            $KSCREEN_BIN output.${SECONDARY_DISPLAY_NAME}.enable output.${SECONDARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}
                    # --- Získání geometrie primárního výstupu ---
                    read PX PY PW PH <<< $(
                        sudo -u "$user" \
                            env WAYLAND_DISPLAY="$wayland" \
                                XDG_RUNTIME_DIR="$runtime_dir" \
                                DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                                kscreen-doctor -o | awk -v out="$output" '$0 ~ "Output: " && $0 ~ out { in_block=1; next } in_block && $0 ~ "Geometry:" { split($3, pos, ","); split($4, res, "x"); print pos[1], pos[2], res[1], res[2]; exit }')
                        echo "$PRIMARY_DISPLAY_NAME $PX,$PY,$PW,$PH"

                    # --- Získání velikosti sekundárního výstupu ---
                    read SX SY SW SH <<< $(
                        sudo -u "$user" \
                            env WAYLAND_DISPLAY="$wayland" \
                                XDG_RUNTIME_DIR="$runtime_dir" \
                                DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                                kscreen-doctor -o | awk -v out="$output" '$0 ~ "Output: " && $0 ~ out { in_block=1; next } in_block && $0 ~ "Geometry:" { split($3, pos, ","); split($4, res, "x"); print pos[1], pos[2], res[1], res[2]; exit }')
                    echo "$SECONDARY_DISPLAY_NAME $SX,$SY,$SW,$SH"

                    PX=0
                    PY=0

                    # --- Výpočet nové pozice ---
                    case "$DISPLAY_ROTATION" in
                        *left*)
                            PX=$(echo "$PX" | tr -dc '0-9')
                            SW=$(echo "$SW" | tr -dc '0-9')
                            SX=$((PX - SW))
                            SY=$PY
                            ;;
                        *right*)
                            PX=$(echo "$PX" | tr -dc '0-9')
                            PW=$(echo "$PW" | tr -dc '0-9')
                            SX=$((PX + PW))
                            SY=$PY
                            ;;
                        *inverted*)
                            SX=$PX
                            SY=$((PY - SH))
                            ;;
                        *normal*)
                            SX=$PX
                            SY=$((PY + PH))
                            ;;
                        *)
                            echo "Neplatná orientace: $DISPLAY_ROTATION"
                            exit 1
                            ;;
                    esac

                    echo "$PRIMARY_DISPLAY_NAME $PX,$PY,$PW,$PH"
                    echo "$SECONDARY_DISPLAY_NAME $SX,$SY,$SW,$SH"

                    # --- Výstup a nastavení ---
                    echo "Umísťuji $SECONDARY_DISPLAY_NAME $DISPLAY_ROTATIONod $PRIMARY_DISPLAY_NAME na souřadnice $SX,$SY"
                    sudo -u "$user" \
                        env WAYLAND_DISPLAY="$wayland" \
                            XDG_RUNTIME_DIR="$runtime_dir" \
                            DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                            $KSCREEN_BIN output.$PRIMARY_DISPLAY_NAME.position.$PX,$PY output.${PRIMARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}
                    sudo -u "$user" \
                        env WAYLAND_DISPLAY="$wayland" \
                            XDG_RUNTIME_DIR="$runtime_dir" \
                            DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                            $KSCREEN_BIN output.$SECONDARY_DISPLAY_NAME.position.$SX,$SY output.${SECONDARY_DISPLAY_NAME}.rotation.${DISPLAY_ROTATION}
                fi
            fi

            exit 0  # Ukončit skript po prvním nalezeném GUI uživateli
        fi
    done

#     if [[  (attempt  + 1) < max_tries ]]; then
#         echo "❌ Žádný aktivní X11/Wayland uživatel. Čekám $delay s..."
#         (( attempt++ ))
#         sleep "$delay"
#     fi
# done

echo "❌ Nepodařilo se najít žádného uživatele v GUI po $max_tries pokusech."
exit 0


================================================================================

📄 PATH: backup&devutils/asus-check-keyboard-wlr-randr.sh
--------------------------------------------------------------------------------
#!/bin/bash

# Identifikace klávesnice (uprav podle potřeby)
VENDOR_ID="0B05"
PRODUCT_ID="1BF2"

# Výstupní jméno displeje (změň podle `kscreen-doctor -o`)
DISPLAY_NAME="eDP-2"

# UID uživatele (uprav podle svého systému)
USER_UID=1000
XDG_SESSION_TYPE=wayland
WAYLAND_DISPLAY=wayland-0

# Nastavení prostředí
export XDG_RUNTIME_DIR="/run/user/$USER_UID"
export XDG_SESSION_TYPE=wayland
export WAYLAND_DISPLAY=wayland-0
export DISPLAY=:0

if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
    echo "Klávesnice detekována, vypínám spodní displej..."
    wlr-randr --output ${DISPLAY_NAME} --off
else
    echo "Klávesnice není připojena, zapínám spodní displej..."
    wlr-randr --output ${DISPLAY_NAME} --on
fi


================================================================================

📄 PATH: backup&devutils/detect-sessions-message.sh
--------------------------------------------------------------------------------
#!/bin/bash

max_tries=5
delay=15
attempt=0

while (( attempt < max_tries )); do
    echo "⏳ Pokus $((attempt+1)) / $max_tries"

    sessions=$(loginctl list-sessions --no-legend | awk '{print $1}')
    for sid in $sessions; do
        user=$(loginctl show-session "$sid" -p Name --value)
        type=$(loginctl show-session "$sid" -p Type --value)

        if [[ "$type" == "x11" || "$type" == "wayland" ]]; then
            runtime_dir=$(loginctl show-session "$sid" -p RuntimePath --value)
            dbus_address="unix:path=$runtime_dir/bus"

            if [[ "$type" == "x11" ]]; then
                display=$(loginctl show-session "$sid" -p Display --value)

                echo "🟢 Nalezen X11 uživatel: $user"
                echo "sid=$sid"
                echo "DISPLAY=$display"
                echo "XDG_RUNTIME_DIR=$runtime_dir"

                # 💡 Tady můžeš dát X11-specifický příkaz
                #sudo -u "$user" \
                #    env DISPLAY="$display" \
                #         XDG_RUNTIME_DIR="$runtime_dir" \
                #         DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                #    notify-send "X11 sezení" "Uživatel $user je aktivní v X11"
            fi

            if [[ "$type" == "wayland" ]]; then
                wayland=$(loginctl show-session "$sid" -p WaylandDisplay --value)

                echo "🟢 Nalezen Wayland uživatel: $user"
                echo "sid=$sid"
                echo "WAYLAND_DISPLAY=$wayland"
                echo "XDG_RUNTIME_DIR=$runtime_dir"

                # 💡 Tady můžeš dát Wayland-specifický příkaz
                #sudo -u "$user" \
                #    env WAYLAND_DISPLAY="$wayland" \
                #         XDG_RUNTIME_DIR="$runtime_dir" \
                #         DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                #    notify-send "Wayland sezení" "Uživatel $user je aktivní ve Waylandu"
            fi

            exit 0  # Ukončit skript po prvním nalezeném GUI uživateli
        fi
    done

    echo "❌ Žádný aktivní X11/Wayland uživatel. Čekám $delay s..."
    (( attempt++ ))
    sleep "$delay"
done

echo "❌ Nepodařilo se najít žádného uživatele v GUI po $max_tries pokusech."
exit 0


================================================================================

📄 PATH: backup&devutils/asus-check-keyboard-xrandr.sh
--------------------------------------------------------------------------------
#!/bin/bash

# Identifikace klávesnice (uprav podle potřeby)
VENDOR_ID="0B05"
PRODUCT_ID="1BF2"

# Výstupní jméno displeje (změň podle `kscreen-doctor -o`)
DISPLAY_NAME="eDP-2"

# UID uživatele (uprav podle svého systému)
USER_UID=1000
XDG_SESSION_TYPE=wayland
WAYLAND_DISPLAY=wayland-0

# Nastavení prostředí
export XDG_RUNTIME_DIR="/run/user/$USER_UID"
export XDG_SESSION_TYPE=wayland
export WAYLAND_DISPLAY=wayland-0
export DISPLAY=:0

if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
    echo "Klávesnice detekována, vypínám spodní displej..."
    xrandr --output ${DISPLAY_NAME} --off
else
    echo "Klávesnice není připojena, zapínám spodní displej..."
    xrandr --output ${DISPLAY_NAME} --auto
fi


================================================================================

📄 PATH: backup&devutils/asus-user-agent.sh
--------------------------------------------------------------------------------
#!/bin/bash

### 1. Singleton Check (Robustní verze s flock)
# Otevřeme soubor zámku na deskriptoru 200
LOCKFILE="/tmp/asus-user-agent.lock"
exec 200> "$LOCKFILE"

# Pokusíme se získat exkluzivní zámek (-x) bez čekání (-n).
# Pokud to nejde (jiná instance už ho má), skončíme.
if ! flock -n -x 200; then
    echo "Agent už běží (zámek je aktivní)."
    exit 1
fi

### 2. Konfigurace a Cesty
APP_NAME="asus-check-keyboard"
STATE_DIR="$HOME/.local/state/$APP_NAME"
STATE_FILE="$STATE_DIR/state"
LOGIC_SCRIPT="/usr/bin/asus-check-keyboard-user.sh"
ICON_PATH="/usr/share/asus-screen-toggle"

# Ikony (ujistěte se, že existují, jinak yad nezobrazí nic)
ICON_AUTO="$ICON_PATH/icon-green.png"
ICON_PRIMARY="$ICON_PATH/icon-red.png"
ICON_DESKTOP="$ICON_PATH/icon-blue.png"
# Fallback ikony ze systému, kdyby vaše neexistovaly
[ -f "$ICON_AUTO" ] || ICON_AUTO="input-tablet"
[ -f "$ICON_PRIMARY" ] || ICON_PRIMARY="video-display"
[ -f "$ICON_DESKTOP" ] || ICON_DESKTOP="computer"

# Načtení configu (pokud existuje)
[ -f /etc/asus-check-keyboard.cfg ] && source /etc/asus-check-keyboard.cfg

### 3. Příprava roury (Pipe) pro YAD
PIPE=$(mktemp -u /tmp/asus_tray_XXXX.fifo)
mkfifo "$PIPE"
# Trik: Otevřeme rouru na deskriptoru 3 pro čtení i zápis.
# To zajistí, že roura zůstane "živá", i když do ní nikdo zrovna nepíše.
exec 3<> "$PIPE"

# Úklid při ukončení
cleanup() {
    rm -f "$PIPE"
    kill $YAD_PID 2>/dev/null
    exit 0
}
trap cleanup SIGINT SIGTERM EXIT

### 4. Funkce pro nastavení stavu a ikony
update_tray() {
    # 1. Přečíst stav (buď z argumentu, nebo ze souboru)
    if [ -n "$1" ]; then
        CURRENT_STATE="$1"
        # Uložit nový stav
        mkdir -p "$STATE_DIR"
        echo "$CURRENT_STATE" > "$STATE_FILE"
    elif [ -f "$STATE_FILE" ]; then
        CURRENT_STATE=$(<"$STATE_FILE")
    else
        CURRENT_STATE="automatic-enabled"
    fi

    echo "Stav: $CURRENT_STATE"

    # 2. Poslat příkazy do YADu přes rouru (fd 3)
    case "$CURRENT_STATE" in
        automatic-enabled)
            echo "icon:$ICON_AUTO" >&3
            echo "tooltip:Automaticky režim" >&3
            ;;
        enforce-primary-only)
            echo "icon:$ICON_PRIMARY" >&3
            echo "tooltip:Vynuceno: Jen primární" >&3
            ;;
        enforce-desktop)
            echo "icon:$ICON_DESKTOP" >&3
            echo "tooltip:Vynuceno: Desktop mód" >&3
            ;;
    esac
}

# Funkce, která provede logiku přepnutí obrazovek
apply_logic() {
    echo "Spouštím logiku obrazovek..."
    bash "$LOGIC_SCRIPT" &
}

### 5. Zpracování kliknutí z menu (Běží na pozadí)
handle_click() {
    local action="$1"
    echo "Kliknuto: $action"
    case "$action" in
        set_auto)
            update_tray "automatic-enabled"
            apply_logic
            ;;
        set_primary)
            update_tray "enforce-primary-only"
            apply_logic
            ;;
        set_desktop)
            update_tray "enforce-desktop"
            apply_logic
            ;;
        trigger_check)
            # Jen spustíme kontrolu (např. signál od Udevu)
            apply_logic
            # Po kontrole se může změnit stav (např. detekce rotace),
            # ale to by měl řešit logic script zápisem do state file?
            # Pro jistotu jen překreslíme ikonu dle aktuálního souboru
            update_tray
            ;;
        quit)
            cleanup
            exit 0
            ;;
    esac
}

### 6. Spuštění YAD (Tray Icon)
# GDK_BACKEND=x11: Nutné pro Wayland, jinak se nezobrazí ikona
# <&3 : Čte příkazy z naší roury
# > >(...) : Výstup (kliknutí) posíláme rovnou do smyčky, která volá handle_click
env GDK_BACKEND=x11 yad --notification --listen \
    --image="$ICON_AUTO" \
    --text="ASUS Control" \
    --menu="Automaticky ! echo set_auto | \
            Jen primární ! echo set_primary | \
            Desktop mód ! echo set_desktop | \
            Zkontrolovat ! echo trigger_check | \
            Ukončit ! echo quit" \
    <&3 > >(while read -r line; do handle_click "$line"; done) &

YAD_PID=$!

### 7. Hlavní smyčka a Signály
# Když Udev pošle SIGUSR1, překreslíme tray a spustíme logiku
trap 'update_tray; apply_logic' SIGUSR1

echo "Agent spuštěn (PID $$). YAD PID: $YAD_PID"

# Inicializace stavu po startu
update_tray

# HLAVNÍ SMYČKA
# 'wait' se ukončí při každém signálu (SIGUSR1).
# Proto ho voláme ve smyčce, dokud proces YAD skutečně běží.
while kill -0 $YAD_PID 2>/dev/null; do
    wait $YAD_PID
done

# Úklid, pokud YAD spadne sám od sebe
rm -f "$PIPE


================================================================================

📄 PATH: backup&devutils/asus-check-keyboard-kscreen.sh
--------------------------------------------------------------------------------
#!/bin/bash

# Identifikace klávesnice (uprav podle potřeby)
VENDOR_ID="0B05"
PRODUCT_ID="1BF2"

# Výstupní jméno displeje (změň podle `kscreen-doctor -o`)
DISPLAY_NAME="eDP-2"

# UID uživatele (uprav podle svého systému)
USER_UID=1000
XDG_SESSION_TYPE=wayland
WAYLAND_DISPLAY=wayland-0

# Nastavení prostředí
export XDG_RUNTIME_DIR="/run/user/$USER_UID"
export XDG_SESSION_TYPE=wayland
export WAYLAND_DISPLAY=wayland-0
export DISPLAY=:0

if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
    echo "Klávesnice detekována, vypínám spodní displej..."
    /usr/bin/kscreen-doctor output.${DISPLAY_NAME}.disable
else
    echo "Klávesnice není připojena, zapínám spodní displej..."
    /usr/bin/kscreen-doctor output.${DISPLAY_NAME}.enable
fi


================================================================================

📄 PATH: backup&devutils/screen_size.sh
--------------------------------------------------------------------------------
#!/bin/bash

# --- Nastavení ---
PRIMARY="eDP-1"
SECONDARY="DP-10"
ORIENTATION="$1"  # left, right, above, below

if [[ -z "$ORIENTATION" ]]; then
    echo "Použití: $0 [left|right|above|below]"
    #exit 1
fi

# --- Funkce pro získání geometrie z blokového výpisu ---
get_geometry_block() {
    local output="$1"
    #kscreen-doctor -o | awk -v out="eDP-1" '$0 ~ "Output: " && $0 ~ out { in_block=1; next } in_block && $0 ~ "Geometry:" { print; exit }'
    #kscreen-doctor -o | awk -v out="eDP-1" '$0 ~ "Output: " && $0 ~ out { in_block=1; next } in_block && $0 ~ "Geometry:" { print; split($2, pos, ","); split($3, res, "x"); print pos[1], pos[2], res[1], res[2]; exit }'
    kscreen-doctor -o | awk -v out="$output" '$0 ~ "Output: " && $0 ~ out { in_block=1; next } in_block && $0 ~ "Geometry:" { split($3, pos, ","); split($4, res, "x"); print pos[1], pos[2], res[1], res[2]; exit }'
}

# --- Získání geometrie ---
read PX PY PW PH <<< "$(get_geometry_block "$PRIMARY")" || {
    echo "Chyba: nelze zjistit geometrii primárního výstupu $PRIMARY"
    exit 1
}

read SX SY SW SH <<< "$(get_geometry_block "$SECONDARY")"
if [[ -z "$SW" || -z "$SH" ]]; then
    echo "Upozornění: Sekundární výstup $SECONDARY není aktivní, použiji 1440x900"
    SW=1440
    SH=900
fi

echo "Primary   >$PX<, >$PY<, >$PW<, >$PH<"
echo "Secondary >$SX<, >$SY<, >$SW<, >$SH<"

# --- Výpočet nové pozice ---
case "$ORIENTATION" in
    left)
        NX=$((PX - SW))
        NY=$PY
        ;;
    right)
        NX=$((PX + PW))
        NY=$PY
        ;;
    above)
        NX=$PX
        NY=$((PY - SH))
        ;;
    below)
        NX=$PX
        NY=$((PY + PH))
        ;;
    *)
        echo "Neplatná orientace: $ORIENTATION"
        exit 1
        ;;
esac

# --- Výstup a aplikace ---
echo "Umísťuji $SECONDARY $ORIENTATION od $PRIMARY na pozici $NX,$NY"
#kscreen-doctor output.$SECONDARY.enable output.$SECONDARY.position.$NX,$NY


================================================================================

📄 PATH: backup&devutils/asus-check-keyboard-kscreen-and-xrandr.sh
--------------------------------------------------------------------------------
#!/bin/bash

max_tries=1
delay=15
attempt=0

# Identifikace klávesnice (uprav podle potřeby)
VENDOR_ID="0B05"
PRODUCT_ID="1BF2"

# Výstupní jméno displeje (změň podle `kscreen-doctor -o`)
DISPLAY_NAME="eDP-2"

# while (( attempt < max_tries )); do
#     echo "⏳ Pokus $((attempt+1)) / $max_tries"

    sessions=$(loginctl list-sessions --no-legend | awk '{print $1}')
    for sid in $sessions; do
        user=$(loginctl show-session "$sid" -p Name --value)
        type=$(loginctl show-session "$sid" -p Type --value)

        if [[ "$type" == "x11" || "$user" == "sddm" ]]; then
            exit 0
        fi

        if [[ "$type" == "x11" || "$type" == "wayland" ]]; then
            USER_UID=$(loginctl show-session "$sid" -p User --value)
            runtime_dir=$(loginctl show-session "$sid" -p RuntimePath --value)
            runtime_dir="/run/user/$USER_UID"
            dbus_address="unix:path=$runtime_dir/bus"

            if [[ "$type" == "x11" ]]; then
                display=$(loginctl show-session "$sid" -p Display --value)
                xauth_file="/home/$user/.Xauthority"

                if [[ ! -f "$xauth_file" ]]; then
                    echo "⚠️  XAUTHORITY nenalezen pro uživatele $user. Přeskočeno."
                    continue
                fi

                echo "🟢 Nalezen X11 uživatel: $user"
                echo "sid=$sid"
                echo "DISPLAY=$display"
                echo "XDG_RUNTIME_DIR=$runtime_dir"

                # 💡 Tady můžeš dát X11-specifický příkaz
                #sudo -u "$user" \
                #    env DISPLAY="$display" \
                #         XDG_RUNTIME_DIR="$runtime_dir" \
                #         DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                #    notify-send "X11 sezení" "Uživatel $user je aktivní v X11"

                if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
                    echo "Klávesnice detekována, vypínám spodní displej..."
                    sudo -u "$user" \
                        env DISPLAY="$display" \
                            XDG_RUNTIME_DIR="$runtime_dir" \
                            DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                            XAUTHORITY="$xauth_file" \
                            xrandr --output ${DISPLAY_NAME} --off
                else
                    echo "Klávesnice není připojena, zapínám spodní displej..."
                    sudo -u "$user" \
                        env DISPLAY="$display" \
                            XDG_RUNTIME_DIR="$runtime_dir" \
                            DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                            XAUTHORITY="$xauth_file" \
                            xrandr --output ${DISPLAY_NAME} --auto
                fi
            fi

            if [[ "$type" == "wayland" ]]; then
                wayland=$(loginctl show-session "$sid" -p WaylandDisplay --value)
                wayland=wayland-0
                echo "🟢 Nalezen Wayland uživatel: $user"
                echo "sid=$sid"
                echo "WAYLAND_DISPLAY=$wayland"
                echo "XDG_RUNTIME_DIR=$runtime_dir"

                # 💡 Tady můžeš dát Wayland-specifický příkaz
                #sudo -u "$user" \
                #    env WAYLAND_DISPLAY="$wayland" \
                #         XDG_RUNTIME_DIR="$runtime_dir" \
                #         DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                #    notify-send "Wayland sezení" "Uživatel $user je aktivní ve Waylandu"
                if lsusb | grep -iq "${VENDOR_ID}:${PRODUCT_ID}"; then
                    echo "Klávesnice detekována, vypínám spodní displej..."
                    sudo -u "$user" \
                        env WAYLAND_DISPLAY="$wayland" \
                            XDG_RUNTIME_DIR="$runtime_dir" \
                            DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                            /usr/bin/kscreen-doctor output.${DISPLAY_NAME}.disable
                else
                    echo "Klávesnice není připojena, zapínám spodní displej..."
                    sudo -u "$user" \
                        env WAYLAND_DISPLAY="$wayland" \
                            XDG_RUNTIME_DIR="$runtime_dir" \
                            DBUS_SESSION_BUS_ADDRESS="$dbus_address" \
                            /usr/bin/kscreen-doctor output.${DISPLAY_NAME}.enable
                fi
            fi

            exit 0  # Ukončit skript po prvním nalezeném GUI uživateli
        fi
    done

#     if [[  (attempt  + 1) < max_tries ]]; then
#         echo "❌ Žádný aktivní X11/Wayland uživatel. Čekám $delay s..."
#         (( attempt++ ))
#         sleep "$delay"
#     fi
# done

echo "❌ Nepodařilo se najít žádného uživatele v GUI po $max_tries pokusech."
exit 0


================================================================================

