Кунг-фу стиля Linux: программное управление окнами
Операционными системами, которые основаны на Linux и Unix и работают в текстовом режиме, очень легко управлять. Учитывая то, как работает подсистема ввода-вывода Unix, программам, ожидающих на входе то, что вводится с клавиатуры, можно передавать любые данные. А то, что программы обычно выдают на экран, можно перехватить и подвергнуть дальнейшей обработке. Вся операционная система устроена именно так. А вот графические программы, использующие возможности X11, это — уже совсем другое дело. Можно ли управлять графическими программами так же, как управляют программами с текстовым интерфейсом? Точный ответ на этот вопрос зависит от того, что именно понимать под «управлением программами». Но если не вдаваться в детали, то на этот вопрос можно дать положительный ответ.
Как это обычно бывает в Linux и Unix, существует множество способов решения одной и той же задачи. И наша задача — не исключение. Если вам нужны средства для точного управления программами, то можно сказать, что добиться этого можно с помощью утилит, задействующих специальный механизм, называемый D-Bus. Этот механизм позволяет программам так оформлять данные и методы, что ими могут пользоваться другие программы. В идеальном мире программы, которыми нужно управлять, применяют D-Bus, но в реальности всё далеко не так просто. Поэтому сегодня мы поговорим о том, как управлять самыми разными графическими программами в Linux.
Существует несколько утилит, которые позволяют каким-то способом управлять X-окнами. Например, есть инструмент xdo, о котором вы, наверняка слышите не особенно часто. Более популярным средством из этой сферы является утилита xdotool, о которой я расскажу. Кроме того, похожим функционалом обладает wmctrl. Есть ещё программа autokey, которая родственна популярной Windows-программе AutoHotKey.
Утилита xdotool
Пожалуй, утилита xdotool является самой популярной программой, используемой в тех случаях, когда надо управлять приложениями с графическим интерфейсом. Это — нечто вроде «швейцарского ножа» для системы X. Но эта утилита использует достаточно сложные конструкции командной строки. Это, вероятно, является следствием того, что она очень много всего умеет. Я чаще всего прибегал к её помощи в ситуациях, когда мне надо было организовать перемещение окон и изменение их размеров. Но она умеет, например, имитировать ввод данных с клавиатуры и воспроизводить действия, которые обычно выполняют с помощью мыши. Она позволяет привязывать выполнение неких действий к событиям, имеющим отношение к перемещениям мыши и к событиям, связанным с окнами программ.
Хотя xdotool можно запускать, указывая файл, содержащий необходимые команды, чаще всего можно видеть передачу аргументов этой утилите из командной строки. При её использовании сначала находят нужное окно, а потом выполняют с ним какие-то действия. Найти окно можно, зная его имя. Можно поступить и иначе. Например — выбрать то окно, по которому щелкнет пользователь.
Рассмотрим следующий пример:
echo Pick Window; xdotool selectwindow type "Hackaday"
Если ввести это в командной строке, то можно щёлкнуть по окну, а потом увидеть, как в нём появится заданный текст. Причём, выглядит это так, будто текст введён пользователем. Наш инструмент, кроме того, может имитировать действия, выполняемые с помощью мыши. Он способен выполнять множество операций с окнами. Среди них, например, изменение активного окна и переключение между рабочими столами.
Кстати, надо отметить, что для того чтобы пользоваться некоторыми из возможностей xdotool, нужно чтобы было установлено расширение X-сервера XTest. Я обычно обнаруживаю это расширение уже установленным. Но если вы столкнулись с тем, что у вас что-то не работает — проверьте X-сервер и узнайте о том, загружено ли это расширение.
Утилита wmctrl
Программа wmctrl обладает возможностями, похожими на возможности xdotool, но она, в основном, взаимодействует с оконным менеджером. Единственная проблема тут заключается в том, что она использует стандартный интерфейс к оконному менеджеру. Не все оконные менеджеры поддерживают полный набор возможностей wmctrl. Это, кстати, одна из тех особенностей Linux, которая является причиной того, что распространение программ для этой ОС может превратиться в настоящее приключение. Нет двух одинаковых систем, а некоторые системы даже и близко друг на друга не похожи.
Утилита wmctrl особенно хороша для решения таких задач, как переключение рабочих столов и максимизация окон. Но она способна и на многое из того, что умеет xdotool.
Использование большого монитора
Я недавно поменял свою конструкцию из трёх мониторов на очень большой 4K-монитор. Это 43-дюймовый монстр обладает разрешением в 3840×2160 пикселей. Всё это очень хорошо, но мне не хватало возможности поместить окно одной программы на один монитор, а окно другой (или третьей) программы — на другой монитор.
Решением этой проблемы было использование механизма, позволяющего сдвинуть окно в определённую позицию на экране. Нечто подобное можно сделать, используя стандартные возможности менеджера окон, но я использую KDE, а там нужной мне возможности по расстановке окон больше нет. А именно, окна привязываются к определённым позициям при перетаскивании их в нужное место, но делается это не особенно быстро. Кроме того, не все места привязки окон находятся там, где мне хотелось бы их видеть.
Работа на большом мониторе
Я сразу подумал о том, чтобы использовать для решения этой задачи xdotool и привязать соответствующие действия к клавиатурным сокращениям, используя возможности KDE. Например, сочетание клавиш CTRL+ALT+1 могло бы закреплять текущее окно в правом верхнем углу экрана, а сочетание CTRL+ALT+0 могло бы максимизировать окно. Сочетание CTRL+ALT+6 позволяло бы окну занять всю правую половину экрана, а сочетание CTRL+ALT+8 должно было размещать окно в верхней половине экрана.
Вот как выглядела моя первая попытка создания команды, соответствующей клавиатурному сокращению CTRL+ALT+1 :
xdotool getwindowfocus windowmove 0 0 windowsize 1920 1080
Тут реализована следующая идея: находим текущее окно, перемещаем его в позицию 0,0, а потом меняем его размер так, чтобы оно заняло бы четверть экрана. Числа, жёстко заданные в коде, это, конечно, не очень хорошо. Но если речь идёт о работе на одном компьютере, то это вполне допустимо. Размер окна, если вам так удобнее, можно установить и по-другому. Например — в виде 50% 50% . Причём, этот параметр можно настроить с использованием подобного значения, а вот в других макросах, где окно перед изменением его размера перемещается в позицию, отличающуюся от 0,0, всё равно придётся использовать жёстко заданные значения.
Первая проблема, с которой я столкнулся, заключалась в том, что в некоторых случаях окна не перемещались. Правда, для решения этой проблемы можно попытаться поменять местами команды изменения размера и перемещения окна.
Но проблемы на этом не кончались. Если максимизировать окно, то значения, касающиеся размеров и позиции окна, ни на что не влияют. Вот как эту проблему можно решить с привлечением wmctrl:
wmctrl -r :ACTIVE: -b remove,maximized_horz,maximized_vert ; xdotool getwindowfocus windowsize 1920 1080 windowmove 0 0
Тут, с помощью wmctrl, отключается максимизация активного окна, а после этого в дело вступает xdotool. Правда, если хочется использовать исключительно wmctrl, то можно применить такую команду:
wmctrl -r :ACTIVE: -b remove,maximized_horz,maximized_vert -e 0,0,0,1920,1080
В опции -e задаются параметры перемещения окна. Причём, обратите внимание на то, что первый 0 — это не опечатка. Значение этого параметра позволяет задавать свойство окна «gravity», которое обычно устанавливается в 0. Следующие четыре числа — это позиция и размер окна. Но тут можно заметить то, что если xdotool перемещает в заданную позицию верхний левый угол окна, wmctrl перемещает верхний левый угол внутреннего содержимого окна (сюда не входят элементы оформления окна). Это приводит к различным результатам.
Конечно, можно написать простой bash-скрипт, предназначенный для решения всех этих задач. Можно предусмотреть в нём механизмы, позволяющие программе адаптироваться к изменению размеров экрана без вмешательства пользователя в код программы. В скрипте можно даже сделать так, чтобы, например, окна не перекрывались бы, можно и воспользоваться другими специальными эффектами.
Вот пример подобного скрипта:
#!/bin/bash # Это надо изменить вручную или воспользоваться данными xrandr #SCREENX=3840 #SCREENY=2160 # Если у вас нет xrandr, awk, или, если вы не смогли подобрать правильные команды, # просто задайте нужные параметры вручную SCREENX=`xrandr -q | awk -F'[ ,]+' '/current/ < print $8 >'` SCREENY=`xrandr -q | awk -F'[ ,]+' '/current/ < print $10 >'` # Тут, если надо, можно настроить точную позицию окна HALFX=$(( SCREENX/2 )) HALFY=$(( SCREENY/2 )) if [ $# -ne 1 ] then ARG="?" else ARG="$1" fi case "$ARG" in nw) TOP=0 LEFT=0 W=$HALFX H=$HALFY ;; n) TOP=0 LEFT=0 W=$SCREENX H=$HALFY ;; ne) TOP=0 LEFT=$HALFX W=$HALFX H=$HALFY ;; w) TOP=0 LEFT=0 W=$HALFX H=$SCREENY ;; center) TOP=$(( $SCREENY/4 )) LEFT=$(( $SCREENX/4 )) W=$HALFX H=$HALFY ;; e) TOP=0 LEFT=$HALFX W=$HALFX H=$SCREENY ;; sw) TOP=$HALFY LEFT=0 W=$HALFX H=$HALFY ;; s) TOP=$HALFY LEFT=0 W=$SCREENX H=$HALFY ;; se) TOP=$HALFY LEFT=$HALFX W=$HALFX H=$HALFY ;; *) echo "Usage: winpos (nw, n, ne, w, center, e, sw, s, se)" exit 1 ;; esac # выполнить команду # wmctrl -r :ACTIVE:-b remove,maximized_horz,maximized_vert -e 0,$LEFT,$TOP,$W,$H # есть и другой способ (при таком подходе, без дополнительных настроек, будут видны заголовки # окон, а предыдущий метод скрывает их за пределами экрана) wmctrl -r :ACTIVE: -b remove,maximized_horz,maximized_vert xdotool getwindowfocus windowsize $W $H windowmove $LEFT $TOP exit 0
Клавиатурные макросы, в результате, можно использовать для вызова этого скрипта с тегом вроде ne (Northeast, северо-восток) или center (центр), устанавливая таким образом позицию окна. Изменения легче вносить в один скрипт, а не редактировать несколько клавиатурных макросов.
Итоги
В сериале «Звёздный путь» (в настоящем, с Уильямом Шетнером) есть момент, когда Кирк говорит кому-то о том, что нужно изучить то, как всё работает на космическом корабле. ОС семейства Linux очень похожи на этот корабль. Нельзя многого достичь, лишь догадываясь о том, как сделать то, что нужно, и продираясь через огромное количество инструментов, которые, возможно, способны решить задачу. Иногда достичь желаемого можно с помощью комбинации из нескольких инструментов. Нелегко испытывать самые разные конфигурации в поисках желаемого. Но обычно, если попытаться, всё можно сделать так, как нужно.
Пользуетесь ли вы программными средствами для работы с окнами в Linux?
Ограничить программу графического интерфейса в Linux только одним экземпляром
Есть некоторые графические программы, в которых, если вы выберете их из меню, если оно уже открыто, вместо открытия нового экземпляра, это окно станет главным. Тем не менее, есть некоторые программы, которые открывают несколько экземпляров самих себя.
Есть определенная программа, которая демонстрирует последнее поведение, но я хочу, чтобы это делало первое. Как это может быть сделано?
3 ответа 3
Я бы порекомендовал заменить ссылку на программу ссылкой на скрипт оболочки, который проверяет, запущена ли программа, и если это так, он использует функцию оконного менеджера, чтобы вывести программу в первую очередь, и, если это не так, запускает ее. ,
Я нашел эту ветку и реализовал ее, поделившись своей версией.
Я создал исполняемый файл /usr/local/bin/run_once.sh содержащий
#! /bin/bash application=$1 if wmctrl -xl | grep "$" > /dev/null ; then # Already running, raising to front wmctrl -x -R "$application" else # Not running: starting $@ fi
Это проверяет с помощью wmctrl , что приложение, которое вы пытаетесь запустить, уже имеет открытое окно (некоторые программы с графическим интерфейсом поддерживают рабочих без графического интерфейса ) вместо использования ps , используя -x для использования WM_CLASS вместо имени строки заголовка.
Для каждой программы , которую я только хочу один окно я скопировал систему .desktop файл в ~/.local/share/applications и изменил exec поле от exec=program —arguments для exec=/usr/local/bin/run_once.sh program —arguments