Поиск решения описанной проблемы результатов не дал, лень победила, я удалил всё лишнее из панели, установил conky и не стал заморачиваться, но через пару недель всё равно почему-то вспомнил этот случай, решив разобраться. К сожалению, в С/C++ я абсолютный ноль, исправить сабж напрямую не могу, поэтому дальнейшее содержание поста — простой в реализации костыль.
Сначала отмечу, что вышеизложенная проблема касается только кода, выполняемого прямо в awesome любым возможным способом. Если действие выполняется сторонним скриптом ( и строго через awful.util.spawn ), то почти никаких задержек не будет, чем мы и воспользуемся. Наша задача сводится к тому, чтобы вынести ‘тяжелый’ код за пределы awesome.
В нашем распоряжении dbus и любой удобный вам инструмент ( в моём случае — scala ).
В добавок к уже хвалёным вещам, разработчики позаботились о слегка упрощающем процесс управления менеджером скрипте — awesome-client. Я использовал его, но можно и, наверное, будет правильней пользоваться dbus напрямую, подключив соответствующую библиотеку. В любом случае, не забудьте в rc.lua сделать глобальными переменные / функции, которые вы в дальнейшем будете использовать.
Первым примером будет виджет, показывающий количество доступных обновлений ПО. Lua-версия порой заметно тормозила весь менеджер и выглядела примерно вот так:
pacwidget = wibox.widget.textbox() pacwidget.list = "" pacwidget.timer = timer({ timeout = 600 }) pacwidget.timer:connect_signal("timeout", function() local io = { popen = io.popen } local s = io.popen("pacman -Qu") local str = '' local count = 0 for line in s:lines() do count = count + 1 str = str .. line .. "\n" end pacwidget:set_text(tostring(count)) pacwidget.list = str s:close() end) pacwidget.timer:start() pacwidget.notify = nil pacwidget:connect_signal("mouse::enter", function() pacwidget.notify = naughty.notify({ text = pacwidget.list, title = "Available updates", timeout = 0 }) end) pacwidget:connect_signal("mouse::leave", function () if pacwidget.notify then naughty.destroy(pacwidget.notify) pacwidget.notify = nil end end)
Нас интересует только функция, выполняющаяся каждый раз по истечению опеределенного времени ( см. ‘connect_signal timeout’ ).
Для удобства создадим простую иерархию классов. Пока ограничимся только нашими потребностями.
import sys.process._ trait LuaObject { val name: String def eval(code: String) = ("echo "+code) #| "awesome-client" !! }
Трейт LuaObject ( да, я знаю, что в lua нет объектов ), который реализуют все классы-‘обёртки’ таблиц lua
abstract class Widget(val name: String) extends LuaObject { }
Абстрактный класс, который ничего не делает и будет родителем для всех виджетов
abstract class TextWidget(name: String) extends Widget(name) { private var _text = "" def text = _text def text_=(arg: String) { _text = arg eval(f"$name:set_text('$arg')") } }
Абстрактный класс, который якобы наследует наш pacwidget из rc.lua
import sys.process._ object Pacman extends App { val pacman = new Pacman(args.head) } class Pacman(name: String) extends TextWidget(name) { val result = "pacman -Qu" !!; text = result.split('\n').length.toString eval(f"$name.list = '${result.replace("\n", "\\n")}'") }
Наш обработчик, который при запуске получает в качестве аргумента название виджета в rc.lua и заполняет его нужной информацией.
И, в принципе, всё. Код предельно прост и в подробном объяснении, наверное, не нуждается. Запакуем результат в .jar и поместим в любую угодную вам папку ( в моём случае — ‘jars/’ в директории с rc.lua ). Создадим функцию, запускающую скрипт, и поставим её на таймер вместо аналогичной lua-функции.
creator = function(jar, arg, time) local path = awful.util.getdir("config").."/jars/" local timer = timer({ timeout = time }) local updater = function() awful.util.spawn_with_shell("java -jar "..path..jar.." "..arg) end timer:connect_signal("timeout", updater) updater() return timer end pacwidget.timer = creator("pacman.jar", "pacwidget", 60)
Принимает на вход название файла-обработчика, название виджета и период; возвращает таймер.
Теперь сделаем вариант чуть сложнеё — будем слушать новые письма на gmail. Добавим скромную обёртку для nauhgty, метод apply в LuaObject и, собственно, сам обработчик события.
class Naughty extends LuaObject { val name = "naughty" def notify(arg: String) = eval(f"$name.notify({ text = '$arg' })") }
Можно добавить различные параметры всплывающего окна, но мне это не нужно, меня устраивают дефолтные.
def apply(arg: String) = { val result = eval(f"return $name.$arg") result.trim match { case r if r.startsWith("string") => _.splitAt(_.indexOf(' '))._2.drop(2).dropRight(1) case _ => "" } }
Такая реализация не совсем правильная, но нам подойдёт.
В scala object.apply(arg) — это то же самое, что и object(arg)
object Gmail extends App { val gmail = new Gmail(args.head, args.tail.head.split(":")) } class Gmail(name: String, account: Array[String]) extends TextWidget(name) { Authenticator.setDefault(new Authenticator() { override def getPasswordAuthentication = new PasswordAuthentication(account(0), account(1).toCharArray) } ) { var stream:BufferedSource = null try { stream = io.Source.fromURL("https://mail.google.com/mail/feed/atom/") val result = stream.getLines().mkString val count = XML.loadString(result).child.find(_.label == "fullcount") match { case Some(x) => x.text case None => "-1" } val value = count.toInt val message = if(value == 0) "No unread mail" else if(value == -1) "Connection error" else { if(this("count") < count) Globals.naughty.notify("New e-mail") value+" unread mails" } eval(f"$name.count = '$count'") eval(f"$name.list = '$message'") } finally { if(stream != null) stream.close() } } }
Вкратце опишу, что тут происходит: конструктор принимает название поля в rc.lua и массив из двух элементов — логин и пароль пользователя, затем происходит известная из java аутентификация, подключение по url и парсинг текущего кол-ва непрочитанных писем; Остальное, я думаю, всем понятно.
Пакуем в .jar, перемещаем в нашу папку, добавляем виджет в rc.lua
gmailwidget = wibox.widget.imagebox() gmailwidget:set_image(beautiful.mail) gmailwidget.list = "" gmailwidget.count = "0" gmailwidget.timer = creator("gmail.jar", "gmailwidget user:password", 60) gmailwidget.timer:start() gmailwidget.notify = nil gmailwidget:connect_signal("mouse::enter", function() gmailwidget.notify = naughty.notify({ text = gmailwidget.list, title = "Mail", timeout = 0 }) end) gmailwidget:connect_signal("mouse::leave", function () if gmailwidget.notify then naughty.destroy(gmailwidget.notify) gmailwidget.notify = nil end end)
Перезапускаем менеджер, вуаля.
Однако и этот костыль не без изъяна, по крайней мере, если вы будете использовать jvm языки, — если порожденный вами процесс не успевает завершиться до начала следующего, обновляющего тот же самый виджет, то может произойти следующая ситуация:
Но на практике такое вероятно только если вы будете обновлять что-либо каждые n < *время работы предыдущего процесса* секунд. Тут уже потребуются какие-либо оптимизации, которые лично мне делать уже лень.
Дальше можно расширить нашу иерархию, возможности, установить более тесное взаимодействие с менеджером, обработку ошибок и так далее. Заодно сделаете один пункт из TODO.
Спасибо за внимание.
ссылка на оригинал статьи http://habrahabr.ru/post/207782/
Добавить комментарий