Журнал LinuxFormat - перейти на главную

LXF145:KWin

Материал из Linuxformat
Перейти к: навигация, поиск

Содержание

KWin: Порулим окнами

Коэн Вервлоесем не любит таскать окна и с ними валандаться, и рад был узнать, что последняя версия KWin поддерживает скрипты.
Lxf145-11g-tut-kwin 6.jpg
Наш эксперт

Коэн Вервлоесем пользуется свободными программами с 2000 года, и ему нравятся те, которые легко автоматизировать.

В KDE 4.6 оконный менеджер KWin получил новую прекрасную возможность: интерфейс скриптов. А значит, поведение окон можно настроить по своему вкусу. Пара примеров: все окна, кроме активного, можно затенить; можно сохранять расположение окна «поверх других», пока вы не развернете его и оно не будет считаться обычным окном. Также можно «склеить» два окна: при изменении размера одного размер второго будет автоматически меняться так, чтобы их контуры оставались вместе. Возможности безграничны. В этой статье мы покажем, что можно сделать с помощью скриптов KWin, и после ее прочтения вы сможете написать свои скрипты Kwin и оптимизировать поведение окон на своем рабочем столе.

Привет, мир

Прежде всего следует узнать, где KWin ищет скрипты – а это каталог ~ /.kde4/share/apps/kwin/scripts/ . Все файлы с расширениями .kwinscript, .kws и .kwinqs, у которых установлен бит выполнения, будут распознаны KWin как файлы скриптов. По умолчанию при запуске KWin выполняет все обнаруженные им скрипты в этом каталоге. Проверим это. Создайте файл helloworld.kwinscript со следующим содержимым в каталоге scripts:

print(“Hello world!”);

Затем сделайте файл исполняемым:

chmod +x ~/.kde4/share/apps/kwin/scripts/helloworld.kwinscript

Теперь перезагрузите KWin в Konsole; при этом будет отображаться вывод скрипта, а также некоторая отладочная информация:

kwin --replace &

Если вы не видите слов “Hello world!”, возможно, вы забыли сделать скрипт исполняемым или у вас более старая версия KDE. Также, возможно, вы допустили синтаксическую ошибку; но тогда KWin выдает сообщение об ошибке в консоли. Если все работает, можно двигаться дальше.

Различные объекты

Язык скриптов KWin – ECMAScript, стандартизированная версия JavaScript. Если у вас есть опыт программирования на JavaScript, то проблем с написанием скриптов KWin не будет. В дальнейшем мы будем считать, что вы знаете JavaScript, но объясним некоторые основные концепции для тех, у кого есть опыт программирования на других языках, чтобы они не отставали. Прежде всего, знайте, что существуют три вида объектов:

  • Инициализируемые [instantiable] Они создаются с помощью конструкции var foo = new Foo();. Объектов такого типа можно создавать сколько угодно.
  • Одиночные [singleton] Для этих объектов существует только один экземпляр заданного типа. Их нельзя создавать самим, но они предоставляются автоматически в скриптах Kwin.
  • Плавающие [floating] Для этих объектов существует множество экземпляров заданного типа, но их нельзя создавать самим: они получаются в качестве возвращаемого значения метода.

Скрипты KWin управляются событиями: KWin генерирует определенные события, с которыми можно связать функции, вызываемые при наступлении события. Этот подход – ключ к изменению поведения программы согласно своим потребностям: нужно просто назначить собственные функции подходящим событиям. Следующий пример прояснит это:

workspace.clientMinimized.connect(function(client) {
print(“Minimized: [“ + client.caption() + “]”);
});

Объект workspace здесь – одиночный; у него есть множество событий, инициируемых на уровне системы. Это основной источник событий в KWin, и вы будете пользоваться им во многих скриптах для инициализации взаимодействия с оконным менеджером. Например, событие clientMinimized, которым мы здесь воспользовались, возникает каждый раз при сворачивании окна. С помощью метода connect мы связываем с этим событием функцию. В данном случае функция безымянная; ей передается один параметр, клиент (представляющий собой окно, плавающий объект). В этой функции мы выводим сообщение о том, что клиент свернут, и применяем метод caption() объекта-клиента для получения заголовка окна. Сохраните этот скрипт, сделайте его исполняемым, перезагрузите KWin в Konsole и наблюдайте за выводом команды при сворачивании окна. Безымянной функцией пользоваться не обязательно. Следующий код эквивалентен предыдущему:

function printCaption(client) {
print(“Minimized: [“ + client.caption() + “]”);
}
workspace.clientMinimized.connect(printCaption);

Если функция у вас небольшая и используется только в одном месте, она может быть безымянной: меньше набора кода. Однако, когда функция разрастается или нужно вызывать ее в нескольких местах, лучше определить ее явно, чтобы код оставался понятным.

(thumbnail)
Когда вы заменяете KWin в Konsole, вы получаете всю информацию, которую вывели

Методы и свойства

Добыть информацию об окнах можно и не дожидаясь наступления определенного события – для этого достаточно воспользоваться методами и свойствами. Метод – это функция, у которой есть возвращаемое значение. У объекта каждого типа есть несколько заданных методов. Например, для получения списка всех клиентов можно воспользоваться методом getAllClients() объекта workspace, который возвращает массив всех клиентов:

var clients = workspace.getAllClients();
for(var i = 0; i < clients.length; i++) {
print(“[“ + clients[i].caption() + “]”);
}

Здесь мы создаем инициализируемый объект clients – в нем хранится массив всех клиентов. Затем в цикле for мы перебираем клиентов и выводим их заголовки. Размер массива мы можем получить с помощью свойства length.

Если вы захотите написать собственный скрипт, загляните в документацию по API скриптов KWin на http://rohanprabhu.com/files/kwsapi.html. На этой странице приведен список всех объектов, которые KWin распознает в своих скриптах – например, workspace, toplevel, client, clientgroup, windowinfo и config. Для каждого объекта в документации приведен список всех событий, методов и свойств с коротким описанием их назначения. Эта информация поможет вам понять, чего именно можно достичь с помощью скриптов. Нужно знать, что toplevel — родительский класс по отношению к client, поэтому все методы объектов toplevel также применимы к объектам клиентов.

(thumbnail)
Склейте окна навсегда с помощью скрипта KWin, и вам незачем будет насильственно совмещать их контуры.

Склеиваем окна вместе

В принципе, ваших знаний уже достаточно для того, чтобы менять поведение окон по своему вкусу с помощью KWin. Чтобы придать вам вдохновения, приведем простой, но полезный пример: этот скрипт предназначен web-разработчикам, желающим, чтобы консоль ошибок всегда показывалась поверх главного окна Firefox и они видели бы в нем все ошибки и предупреждения. Вот как он выглядит:

var main_present = 0;
var console_present = 0;
var console_height = 200;
var main;
var console;
function viewportHeight() {
return (workspace.dimensions().h)/(workspace.desktopGridSize().h);
}
function viewportWidth() {
return (workspace.dimensions().w)/(workspace.desktopGridSize().w);
}
function adjustMain() {
var geom = console.geometry();
main.move(0, geom.height);
main.resize(viewportWidth(), viewportHeight() - geom.height);
}
workspace.clientAdded.connect(function(client) {
var z = client.getWindowInfo();
if(z.windowClassClass == “Firefox”) {
if(client.caption() == “Error Console”) {
console = client;
console_present = 1;
console.move(0, 0);
console.resize(viewportWidth(), console_height);
if(main_present == 1) {
adjustMain();
}
} else if(z.windowClassName == “Navigator”) {
main = client;
main_present = 1;
if(console_present == 1) {
adjustMain();
}
}
}
});

Код начинается с объявления некоторых переменных: мы объявляем переменные для хранения главного окна и консоли окна ошибок. Мы также объявляем переменные main и console, которые будут содержать ссылки на клиентов главного окна и окна консоли ошибок. Затем мы определяем две вспомогательных функции для получения ширины и высоты экрана рабочего стола. В функции adjustMain мы изменяем ширину главного окна так, чтобы оно располагалось прямо под окном консоли ошибок.

Скорая помощь

При проверке скрипта KWin для просмотра значений переменных, особенно заголовка клиента, класса windowclass и имени окна, в целях отладки пользуйтесь командой print.

Скрипты в других оконных менеджерах

KWin – не единственный оконный менеджер, позволяющий изменять поведение окон с помощью скриптов. Многие из так называемых «мозаичных» оконных менеджеров полностью поддерживают скрипты. Например, оконный менеджер Awesome допускает значительное расширение с помощью языка программирования Lua и обладает обширным и хорошо задокументированным API, с помощью которого можно тонко изменить поведение всех окон. Тот же подход использует Xmonad, но он написан на Haskell, чисто функциональном языке программирования, который не слишком просто освоить.

Существует несколько сторонних программ, которые «подцепляются» к оконному менеджеру и предоставляют вам контроль за местоположением и параметрами окон. Многие из них используют спецификацию расширенных подсказок оконного менеджера (Extended Window Manager Hints – EWMH), которой следует большинство популярных файловых менеджеров, в том числе KWin, Metacity, Awesome, Xmonad и Openbox. Например, существует программа Devil’s Pie, которая считывает скрипты, позволяющие вам задавать правила для своих любимых приложений.

Команда wmctrl также предоставляет вам полный контроль над своими окнами, и она прекрасно подходит для использования в собственных скриптах оболочки. И если вам удобнее писать скрипты в Bash, чем программы на JavaScript, wmctrl определенно лучший выбор.

Большая часть кода – безымянная функция, которая будет вызываться при каждом при наступлении события clientAdded. Сначала мы записываем информацию об окне в переменную z. Windowinfo — объект, дающий доступ к обширной низкоуровневой информации X Window об окне. В данном случае нас интересуют класс windowclass и имя окна: с их помощью мы определим, является ли клиентом Firefox и какое это окно: у каждого окна браузера его классом windowclass является Firefox. Кроме того, наш код отличает главное окно от консоли ошибок по значению имени windowclass: Navigator и Error Console соответственно.

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

Пинг-понг с вашими окнами

Для иллюстрации возможностей скриптов KWin Рохан Прабху даже написал пинг-понг в скрипте KWin. Не то чтобы было сильно удобно, когда окна летают по экрану, но это хороший пример, с которым вам непременно стоит познакомиться.

Со страницы http://rohanprabhu.com/?p=56, установите скрипт в каталог скриптов KWin и сделайте его исполняемым, затем перезагрузите KWin в Konsole, после чего скрипт в Konsole попросит вас выбрать клавиши управления.

Например, можно устроить войну браузеров, выбрав своей ракеткой Firefox, а ракеткой компьютера – Chrome или Konqueror. Затем выберите другое окно в качестве мяча, а в окне Konsole вы увидите счет матча.

(thumbnail)
Война браузеров в виде пинг-понга на вашем рабочем столе.

События для конкретных клиентов

Впрочем, можно сделать и лучше: нельзя ли нам автоматически изменять размер главного окна при изменении размера окна консоли, чтобы они всегда были «приклеены» друг к другу? Для этого свяжем безымянную функцию с событием clientMoved, генерируемым для консоли. Добавьте следующие строки в конец кода, который выполняется при добавлении окна консоли (до } else if…):

console.clientMoved.connect(function() {
if(main_present == 1) {
adjustMain();
}
});

Разница между тем, что мы делали раньше, и тем, что делаем сейчас, в том, что clientMoved — событие, генерируемое не на уровне системы для объекта workspace с клиентом в качестве параметра, а для конкретного клиента; в данном случае, для окна консоли. И когда мы связываем безымянную функцию с этим событием, функции не нужен клиент в качестве параметра, потому что мы уже знаем, что это за клиент. В результате при каждом перемещении (в том числе и при изменении размеров) окна консоли положение главного окна изменяется соответствующим образом.

Однако иногда было бы интересно распознать клиента внутри функции. В данном случае нужно указать объект следующим образом:

console.clientMoved.connect(client, function() {
if(main_present == 1) {
adjustMain();
print(client.caption() + “ is moved”);
}
});

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

Вкладки

Здесь мы работали только с отдельными окнами, но с помощью объекта clientgroup можно получить доступ к вкладкам. Чтобы создать новую группу вкладок, просто объявите переменную var group = new ClientGroup(client); где client – первое окно, которое вы хотите добавить. Остальные окна можно добавить с помощью функции clientgroup.add, удалить – с помощью метода clientgroup.remove, удалить все – с помощью метода clientgroup.removeAll.

У объекта clientgroup есть множество интересных методов для работы с клиентами в группе. Например: clientgroup.clients возвращает массив всех клиентов, являющихся членами группы; с помощью clientgroup.contains(client) можно проверить, является ли конкретный клиент членом группы.

Clientgroup.indexOf(client) возвращает положение клиента в группе вкладок, а пользуясь clientgroup.move, можно переместить окно клиента в другое положение. Таким образом, с помощью объекта clientgroup можно написать собственный оконный менеджер с вкладками поверх KWin.

Файл настройки скрипта

Если вы хотите задавать для файла скрипта настройки, просто создайте файл с тем же именем, но с расширением .kwscfg в каталоге ~ /.kde4/share/apps/kwin/scripts/ . Этот файл должен иметь формат INI-файла с парами ключ–значение в следующем виде:

consoleHeight=200

Как вы видите, в этом файле настройки мы задаем ширину по умолчанию для окна консоли из нашего скрипта «склеивания» окон. Теперь нам остается только воспользоваться этим значением в скрипте, изменив объявление переменной console_height следующим образом:

var console_height = config.get(“consoleHeight”);

Здесь используется одиночный объект config, и в дальнейшем, если в скрипте нужно использовать некоторые заданные значения, лучше задавать их в файле настройки и получать в своем скрипте функцией config.get. Вы всегда можете захотеть изменить эти значения, и проще и менее рискованно сделать это в файле настройки, чем вторгаясь в код.

Если в файле настройки больше одной пары ключ –значение, можно получить их так же, как в этом примере, но также можно вызвать config.get() без параметров и получить все ключи и значения в ассоциативном массиве. Или с помощью вызова config.get(“key1”, “key2”, “key3”) можно получить ассоциативный массив заданных ключей и их значений.

Если в файле настройки нужно проверить только наличие ключа, воспользуйтесь вызовом config.exists(“key”). При отладке можно воспользоваться методом config.loaded, который вернет true, если файл настройки был найден и загружен, и false в противном случае. Учтите, что у каждого скрипта KWin есть доступ только к своему файлу настройки.

Скорая помощь

При проверке сложных скриптов KWin проявляйте осторожность. Например, если вы сделаете ошибку в поведении активных окон, вы даже не сможете вернуться в Konsole, чтобы перезагрузить программу.

Что дальше?

Поддержка скриптов в KWin все еще находится на начальном уровне, и это ясно из отсутствия многих событий, методов и свойств, способных представлять интерес. Например, у объекта client есть методы isShade() и isShadeable() для определения, затенен ли клиент и можно ли его затенить, но нет метода setShade(), чтобы изменить состояние затененности клиента. У объекта клиента также есть метод isMaximizable() и событие maximizeSet, которое генерируется при разворачивании окна, но (все еще) нельзя программно развернуть клиента, например, методом setMaximize().

Это означает что на практике при появлении той или иной идеи вы часто не сможете ее воплотить, потому что скрипты не поддерживают необходимых вам функций. Но не забывайте, что это первая версия KDE с поддержкой KWin. В следующих версиях непременно появятся новые функции, и вы в конце концов сможете автоматизировать почти все свои задачи управления окнами в KDE. Управление событиями и тот факт, что скрипты KWin можно писать на обычном JavaScript, делают их доступными большому количеству пользователей.

Рохан Прабху [Rohan Prabhu], главный разработчик поддержки скриптов KWin, написал руководство, доступное в его блоге: http://rohanprabhu.com/?p=116. В нем он пишет скрипт, сохраняющий положение окна «поверх всех окон», пока оно не развернуто. Когда окно разворачиваются, оно становится обычным, и его можно накрыть другими окнами, когда оно развернуто. Когда окно неразвернуто, скрипт снова автоматически располагает его «поверх всех окон».

Прабху регулярно пишет о различных аспектах написания скриптов KWin в своем блоге; ознакомиться с этими записями можно по ссылке http://rohanprabhu.com/?tag=kwin_scripting. С его блогом, документацией по API и вдохновением, которое, надеюсь, у вас появилось, у вас есть все необходимое для успешного программирования...

Персональные инструменты
купить
подписаться
Яндекс.Метрика