Могу ли я автоматически отправить окно определенного приложения в самое низкое z-положение, когда оно теряет фокус?

Я делаю большую часть своей работы в двух приложениях: мой веб-браузер и мой редактор. Я часто переключаюсь между ними с помощью Alt-Tab. У меня также всегда есть клиент для обмена мгновенными сообщениями (Hipchat), но я взаимодействую с ним очень редко по сравнению с двумя другими приложениями.

Периодически раздражает то, что после того, как я взаимодействую с окном Hipchat и Alt-Tab обратно, скажем, в мой редактор, моя мышечная память настроена так, чтобы ожидать, что другая Alt-Tab сконцентрируется на моем браузере, но я снова оказываюсь в Hipchat.

Есть ли какой-нибудь способ заставить Hipchat отправляться в конец списка стеков или последних или чего-то еще, после того, как он потерял фокус каким-либо образом?

1 ответ

Решение

На самом деле вы просите, чтобы окно определенного приложения отображалось только в первой или последней позиции, по оси z.

Когда окно gedit (в этом примере) теряет фокус, оно отправляется в последнюю позицию (по z, под полупрозрачным окном терминала) вместо того, чтобы опускаться только на одну позицию:


Z- положение окна

Хотя это может быть сделано, нам все еще нужно преодолеть некоторые серьезные осложнения; Когда окно отправлено в самую последнюю позицию, вы захотите сохранить z-порядок всех остальных окон. Однако в настоящее время нет инструментов, которые могли бы дать нам этот z-порядок окон. И то и другое xdotool а также wmctrl не дают нам никакой информации по этому вопросу.

Однако мы можем отслеживать историю фокусировки (всех) окон. Поскольку окно опускается на одну позицию, если другое окно получает фокус, мы можем заключить z-порядок окон, если запустим фоновый скрипт для просмотра истории фокуса окон.

Решение двух небольших фоновых скриптов

Приведенное ниже решение состоит из двух небольших фоновых сценариев, запускаемых одновременно.

  1. Скрипт для отслеживания истории фокуса: focus_history.py
  2. Скрипт для отправки окна целевого приложения на последнюю позицию, если оно теряет фокус: set_z.py

Сценарий 1

focus-history.py

#!/usr/bin/env python3
import subprocess
import time
import os

rootdata = os.environ["HOME"]+"/.focus_history"
open(rootdata, "wt").write("This is an empty line")

def current_windows():
    try:
        return subprocess.check_output(["wmctrl", "-lp"]).decode("utf-8")
    except subprocess.CalledProcessError:
        pass

def convert_format(w_id):
    return w_id[:2]+(10-len(w_id))*"0"+w_id[2:]

def read_data():
    return open(rootdata).read().splitlines()

def get_top(wlist):
    try:
        top = convert_format(
            [l.split("#")[-1].strip() for l in subprocess.check_output(
                ["xprop", "-root"]
                ).decode("utf-8").splitlines() \
               if "_NET_ACTIVE_WINDOW(WINDOW)" in l][0])
        return [l for l in wlist if top in l][0]
    except IndexError:
        pass

if __name__ == "__main__":
    while True:
        time.sleep(1)
        wdata = current_windows()
        if wdata != None:
            wlist = wdata.splitlines()
            # get frontmost window (as in wmctrl -lG)
            top = get_top(wlist)
            oldlist = read_data()
            if not any([top == oldlist[0], top == None]):
                # clean up closed windows
                [oldlist.remove(l) for l in oldlist if not l.split()[0] in wdata]
                # remove possible other mentions of the active window
                [oldlist.remove(l) for l in oldlist if l.startswith(top.split()[0])]
                open(rootdata, "wt").write(("\n").join([top]+oldlist))

Сценарий 2

#!/usr/bin/python3
import subprocess
import time
import focus_history

# --- set the process name of your application below
proc = "gedit"
# ---

focus_hist = focus_history.rootdata

def get(val):
    try:
        return subprocess.check_output(val).decode("utf-8").strip()
    except subprocess.CalledProcessError:
        pass

def front_w():
    get_front = str(hex(int(get(["xdotool", "getactivewindow"]))))
    return get_front[:2]+(10-len(get_front))*"0"+get_front[2:]

while True:
    time.sleep(1)
    pid = get(["pgrep", proc])
    front1 = ""
    while pid:
        time.sleep(1)
        frontpid = get(["xdotool", "getactivewindow", "getwindowpid"])
        front2 = frontpid == pid
        if front2 != front1:
            if front2 == False:
                zdata = [l for l in open(focus_hist).read().splitlines()]
                wins = list(reversed([l.split()[0] for l in zdata if not pid in l]))
                for w in wins+[front_w()]:
                    cmd = ["xdotool", "windowraise", w]
                    subprocess.call(cmd)
        pid = get(["pgrep", proc])            
        front1 = front2

Как настроить

  1. Скрипт использует оба wmctrl а также xdotool

    sudo apt-get install wmctrl xdotool
    
  2. Скопируйте скрипт 1 в пустой файл, сохраните его (точно!) Как focus_history.py

  3. Скопируйте скрипт 2 в пустой файл, сохраните его как set_z.py в том же каталоге, что и скрипт 1.

    В разделе заголовка скрипта, в строке:

    proc = "gedit"
    

    замещать "gedit" по имени процесса вашего приложения (между кавычками)

  4. Выполните тестовый запуск сценария. Прежде чем открывать какие-либо (дополнительные) окна, запустите сценарий 1 командой:

    python3 /path/to/focus_history.py & python3 /path/to/set_z.py
    

    [Скрипт распознает окна, которые были сфокусированы хотя бы один раз. Это будет иметь место, если скрипт запускается при входе в систему]

    Как уже упоминалось, сценарии должны находиться в одном и том же каталоге, на одном уровне.

  5. Теперь начните открывать окна и посмотрите, как они себя ведут. Ваше приложение должно перейти на (очень) фон, если оно теряет фокус.

  6. Если все работает нормально, добавьте его в Startup Applications: Dash > Startup Applications > Add. Добавьте команду:

    /bin/bash -c "sleep 15 && python3 /path/to/focus_history.py & python3 /path/to/set_z.py"
    

Заметки

  • установка предполагает, что у вас есть одно окно, открытое целевого приложения. Из вашего вопроса я понимаю, что это так.

альтернативно

В качестве альтернативы, вы можете установить комбинацию клавиш, чтобы вызвать окно определенного приложения, если оно существует, как описано здесь.

Однако для этого потребуется другой ярлык, чтобы вернуться к окну первого приложения,

Если...,

Вы должны настроить один ярлык для переключения между двумя приложениями. Это, однако, выходит за рамки этого вопроса...

Другие вопросы по тегам