LINUX.ORG.RU

luastatus — генератор данных для панелей состояния, поддерживающий i3bar и dwm

 , , , ,


5

4

Вышла первая версия luastatus — универсального генератора данных для панелей состояния, поддерживающего i3bar и dwm. Программа написана на C и распространяется под лицензией GNU LGPL v3.

Большинство генераторов данных для панелей состояния тайловых WM либо обновляют информацию по таймеру (например, conky), либо требуют сигнал для перерисовки (например, i3status). Панели же в составе окружений рабочего стола, как правило, обновляют информацию мгновенно и автоматически, как и luastatus.

luastatus работает с загружаемыми модулями двух типов: плагины (отвечают за работу с каким-либо источником событий) и так называемые barlib’ы (отвечают за работу с конкретной панелью состояния).

Схема работы luastatus следующая:

  • Один из плагинов сообщает, что произошло какое-то событие, и генерирует описывающий его Lua-объект.
  • Вызывается пользовательская функция (widget.cb) с этим объектом в качестве аргумента.
  • Возвращённое ей значение передаётся в barlib.

Также barlib может оповестить программу-виджет о том, что с виджетом произошло какое-либо событие. В таком случае barlib также генерирует Lua-объект, описывающий это событие, и пользовательская функция (widget.event) вызывается с ним в качестве аргумента. Например, barlib для i3 сообщает, когда пользователь нажал на виджет, а также передаёт информацию о том, какой кнопкой было сделано нажатие, и его координаты.

На данный момент реализованы следующие плагины:

  • alsa — следит за изменением громкости канала ALSA.
  • fs — генерирует события по таймеру, передаёт виджету информацию о количестве занятого места в файловой системе. Также поддерживается wake-up FIFO.
  • mpd — следит за громкостью и текущим плейлистом mpd.
  • pipe — запускает процесс и генерирует событие каждый раз, когда он выводит в stdout строку (можно указать другой разделитель вывода). Эта строка передаётся виджету.
  • timer — просто генерирует события по таймеру (например, для виджета-часов или виджетов, читающих какие-то файлы в /proc). Также поддерживается wake-up FIFO.
  • xkb — следит за текущей раскладкой клавиатуры и передаёт виджету номер группы активной раскладки и её имя.
  • xtitle — следит за текущим активным окном и сообщает его заголовок.

И следующие barlib’ы:

  • i3;
  • dwm.

>>> Страница проекта на GitHub

>>> Страница релиза



Проверено: anonymous_incognito ()
Последнее исправление: sudopacman (всего исправлений: 2)

Ответ на: комментарий от shdown

Если решение объективно лучше, почему бы не использовать его?

В треде слишком много объективных фактов. Уже и fork(), работающий в сотни раз медленнее создания потока, и мертвый CGI вместе с никому ненужными энжиниксами, и объективно лучшие архитектуры, и объективно лучший луа. Для такого количества объективных фактов хотелось бы видеть соответствующее количество бумажек с исследованиями и критериями объективности.

Freyr69 ★★★
()
Ответ на: комментарий от Freyr69

энжиниксами

Лол. Так nginx плодит процессы-то один раз, а не на каждый чих форкается.

Мне сейчас лень что-то бенчить. Как-нибудь потом.

shdown
() автор топика
Ответ на: комментарий от Freyr69

Я исправил сообщение — не потоки, а процессы. Я читал эту статью — ещё раз: он не форкается на каждый чих, а спавнит процессы на старте.

Процессы-то ты все равно должен создать.

И что? Я не понимаю. Мы сравниваем два подхода:

(1) Сначала наплодить потоков по количеству виджетов, после этого ничего не плодить; а по каждому событию просто дёргать Lua-функцию;

(2) По каждому событию форкаться и exec'аться несколько раз (19 и больше, как показал анализ скриптов i3blocks).

Что ты тут вообще бенчить можно, я не понимаю? Понятно, что первое эффективней.

shdown
() автор топика
Ответ на: комментарий от shdown

Вы сейчас на полном серьёзе обсуждаете эффективность в рамках топика о приложении, выводящем на экран часы и название играющего трэка? Вот прямо аж тельняшки рвёте и бенчмарки гоняете? ЛОР такой ЛОР.

anonymous
()
Ответ на: комментарий от shdown

(1) Сначала наплодить потоков по количеству виджетов, после этого ничего не плодить; а по каждому событию просто дёргать Lua-функцию;

Что ты тут вообще бенчить можно, я не понимаю? Понятно, что первое эффективней.

Что значит эффективней? Процитирую себя:

Каждый раз — это раз в минуты. Очень даже достойная цена за гибкость, ящитаю. Все-таки писать плагины для панельки на сях/луа — то еще занятие.

Сколько времени занимает этот форк по сравнению с временем исполнения процесса? Сколько именно времени ты сэкономил на спичках, заменив простую и элегантную архитектуру с вызовом стороннего процесса на монолитное луаподелие?

19 и больше

Это на шелл скрипте и с красивым выводом. Никто не мешает тебе писать на си. У меня, допустим, для вывода информации о громкости довольно простой пайп.

Freyr69 ★★★
()
Ответ на: комментарий от Freyr69

Сколько времени занимает этот форк по сравнению с временем исполнения процесса?

Это зависит от того, насколько часто нужно перерисовывать индикаторы.

Сколько именно времени ты сэкономил на спичках, заменив простую и элегантную архитектуру с вызовом стороннего процесса на монолитное луаподелие?

простую и элегантную архитектуру

Вот это: amixer get Master | grep -E -o '[0-9]{1,3}?%' | head -1 — уже смешно. На скрипты для чего-то посложнее (https://github.com/deterenkelt/dotfiles/tree/master/.i3 — типичный пример) без слёз не взглянешь.

TIMEOUT_STEP=0 # counts seconds till TIMEOUT_MAX
TIMEOUT_MAX=30 # the actual timeout is one second; TIMEOUT_MAX introduces
               # a period functions may rely on; functions have their own
               # local timeouts, ‘wait_time’ variable.

Т.е. некоторые функции не отрабатывают каждую секунду — проблемы с производительностью всё-таки есть.

shdown
() автор топика
Ответ на: комментарий от Freyr69

Никто не мешает тебе писать на си.

Этим никто не заморачивается, как показывает практика — все пишут подобный ужос на шелле.

shdown
() автор топика
Ответ на: комментарий от shdown

19 форков для простого вывода звука

Мда, что ж с тобой будет, дорогой оптимизатор, если ты бенчмарк dbus-а запилишь, а также оверхед для всяких gvalue и проч. в гномах и кде. Там жеж вообще оптимизировать-переоптимизировать.

Можно вообще сделать глобальный ipc в виде одного огромного куска шареной памяти. А еще этот дорогой вызов функций... Архитектура > оптимизаций.

Dennis Ritchie encouraged modularity by telling all and sundry that function calls were really, really cheap in C. Everybody started writing small functions and modularizing. Years later we found out that function calls were still expensive on the PDP-11, and VAX code was often spending 50% of its time in the CALLS instruction. Dennis had lied to us! But it was too late; we were all hooked...

Freyr69 ★★★
()
Ответ на: комментарий от Freyr69

Ну, я решил проблему для себя — все «статусы» для i3 и подобных представляли из себя нечто несъедобное.

Ну и мне кажется, что ниша у этой поделки есть. Не нужно писать какие-то форкающиеся-чёрт-знает-сколько-раз костыли на шелле, не нужно писать на сях, не нужно никому посылать сигналы. Бери и пользуйся.

shdown
() автор топика
Ответ на: комментарий от shdown

Не нужно писать какие-то форкающиеся-чёрт-знает-сколько-раз костыли на шелле, не нужно писать на сях, не нужно никому посылать сигналы. Бери и пользуйся.

Поттеринг. Начало.

Но нужно писать костыли на луа. И только на луа. В отличие от i3blocks, где можно писать хоть так:

https://github.com/chrismamo1/show-volume/tree/master

не нужно никому посылать сигналы

И нужно городить ipc, или как ты с внешними процессами будешь общаться? С сервером звука? Ты уверен, что твой ipc механизм быстрее сигналов? К слову ты оверхед луа интерпретаторов-то измерил? Оверхед GC и рантайма?

Freyr69 ★★★
()
Последнее исправление: Freyr69 (всего исправлений: 1)
Ответ на: комментарий от Freyr69

Поттеринг. Начало.

Будто что-то плохое (2).

Но нужно писать костыли на луа.

Почему «костыли»? Вот шеллы ужасны в качестве ЯП, а писать на них — извращение, я считаю.

И только на луа.

Я не вижу в этом большой проблемы. На Lua пишется исключительно код, превращающий то, что прислал плагин, в какую-то форму, понимаемую barlib’ом.

https://github.com/chrismamo1/show-volume/tree/master

Не знал про i3cat. Прикольная вещь, но неуниверсальная; к тому же, для него ещё нужно на чём-то писать виджеты.

И нужно городить ipc, или как ты с внешними процессами будешь общаться? С сервером звука? Ты уверен, что твой ipc механизм быстрее сигналов?

Быстрым ему нужно быть ровно настолько, чтобы человек не заметил лага. А твои костыли сломаются, если ты будешь изменять громкость не хоткеями (alsamixer’ом, например).

К слову ты оверхед луа интерпретаторов-то измерил? Оверхед GC и рантайма?

Да, 4Mb с четырьмя плагинами у меня ело, по процессору — вообще фигня. Форкаться (и exec’аться) несколько раз каждую секунду гораздо затратнее же.

shdown
() автор топика
Ответ на: комментарий от shdown

чтобы человек не заметил лага

То мы форки считаем, то лаги замечаем.

Форкаться (и exec’аться) несколько раз каждую секунду гораздо затратнее же.

Ты, разумеется, проводил измерения и можешь показать результаты? Или очередной очевидный факт?

Freyr69 ★★★
()
Ответ на: комментарий от shdown

Не подскажешь ещё, почему такое не работает? Вернее работает, но только один раз, меняет '-' на '+', когда включаю вайфай. И больше не реагирует, постоянно '+'.

wifi_file = io.open('/sys/class/net/wlan0/carrier', 'r')

function get_wifi_st()
    if not wifi_file then
        return ' [-?-]'
    end
    wifi_file:seek('set', 0) -- yes, seeking on sysfs files is OK
  	local status
	status = wifi_file:read("*number")
    local sym 
    sym = '-'
    if status == 1 then
        sym = '+'
    end
    return string.format('%s', sym)
end

widget = {
    plugin = 'timer',
    opts = { period = 2 },
    cb = function(t)
        return get_wifi_st() end,
}

ashot ★★★★
()
Ответ на: комментарий от ashot

Могу предположить, что файл пропадает и нужно каждый раз открывать заново. Удали глобальную переменную и добавь в начало функции

local wifi_file = io.open('/sys/class/net/wlan0/carrier', 'r')
И замени
wifi_file:seek('set', 0) -- yes, seeking on sysfs files is OK
на
wifi_file:setvbuf('no')
Всю остальнуб логику, конечно, можно переписать попроще.

anonymous
()
Ответ на: комментарий от anonymous

Могу предположить, что файл пропадает и нужно каждый раз открывать заново.

Точно, в этом проблема была. Спасибо.

ashot ★★★★
()

В Wayland без прослойки XWayland умеет работать? В Sway например...

И вопрос небольшой, нет в планах написания нотификатора? Что-то типа https://github.com/sboli/twmn только чтобы в Wayland без XWayland работал. Было бы замечательно.

Таки нотификаторы пробовал искать, но чисто в Wayland вроде ни один не умеет работать :-(

Sorcus
()
Ответ на: комментарий от Freyr69

То мы форки считаем, то лаги замечаем.

Форки я считал не из-за того, что они «лагают», а из-за того, что жрут процессор и зарядку.

Ты, разумеется, проводил измерения и можешь показать результаты? Или очередной очевидный факт?

А то.

$ LUALIB=luajit; gcc $(pkg-config --cflags --libs $LUALIB) -Wall -Wextra -O2 bench.c -o bench && rm -f log && ./bench && wc -l ./log
1000 spawns in 3.74 seconds: 267.64 forks per second
1000 calls in 0.00 seconds: 322609.89 calls per second
1000 ./log
$ LUALIB=lua5.2; gcc $(pkg-config --cflags --libs $LUALIB) -Wall -Wextra -O2 bench.c -o bench && rm -f log && ./bench && wc -l ./log
1000 spawns in 3.63 seconds: 275.76 forks per second
1000 calls in 0.01 seconds: 176636.38 calls per second
1000 ./log

bench.c

file.lua

Предварительно нужно положить в текущий каталог исполняемый файл my_true, который ничего не делает. Можешь скопировать /bin/true, если в твоей системе это ELF-файл (а не шелл-скрипт), можешь скомпилировать int main() {}.

shdown
() автор топика
Ответ на: комментарий от Sorcus

В Wayland без прослойки XWayland умеет работать?

Сам luastatus вообще ни про какие иксы с вейландами не знает. Он просто загружает плагины и barlib и склеивает их.

В Sway например...

swaybar совместим на уровне протокола с i3bar? Судя по тому, что я вижу тут — да. Тогда barlib “i3” будет работать с этим вашим swaybar. Плагины xkb и xtitle без xwayland, конечно, не заработают.

И вопрос небольшой, нет в планах написания нотификатора? Что-то типа https://github.com/sboli/twmn только чтобы в Wayland без XWayland работал. Было бы замечательно.

Нет.

shdown
() автор топика
Ответ на: комментарий от ashot

Держи нормальный код.

function get_wifi_st()
    local wifi_file = io.open('/sys/class/net/wlan0/carrier', 'r')
    if not wifi_file then
        return 'x' -- файла нет
    end
    wifi_file:setvbuf('no')
    if wifi_file:read('*number') == 1 then
        return '+' -- в файле “1”
    else
        return '-' -- в файле “0”
    end
end
shdown
() автор топика
Ответ на: комментарий от shdown

Спасибо. Переехал на эту прогу. До этого dwmstatus обновлял раз в секунду, поэтому задержки при смене раскладки были, а тут всё как надо. Примеров виджетов бы побольше (а то с луа совсем никак, хоть язык вроде понятный) особенно интересно, как обработать щелчёк мышью по виджету.

ashot ★★★★
()
Ответ на: комментарий от ashot

Примеров виджетов бы побольше

Окей, будут попозже.

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

Насколько я знаю, dwm без сторонних патчей этого не умеет.

shdown
() автор топика
Ответ на: комментарий от shdown

народ подскажите еще как удалить сей пакет?

make uninstall
пишет чо не найдена цель. тоесть install цель есть а uninstall нету. удалять только в ручную?

и кстати почему то luastatus-i3-wrapper копируется без аттрибута «х»(исполняемый файл). пришлось самому чмод делать...

safocl ★★
()
Ответ на: комментарий от safocl

удалять только в ручную?

Пока — да:

By default, CMake does not provide the «make uninstall» target, so you cannot do this. We do not want «make uninstall» to remove useful files from the system.

и кстати почему то luastatus-i3-wrapper копируется без аттрибута «х»(исполняемый файл). пришлось самому чмод делать...

Это уже исправлено.

shdown
() автор топика
Ответ на: комментарий от shdown

кстати не работал виджет из примера измерения громкости. только мут работал, а в нормальном режиме просто пишет [ERROR].

safocl ★★
()
Ответ на: комментарий от safocl

когда?? я тока вчера скачал и поставил и было все так.

Вот этим коммитом. Ты, наверное, релиз скачал. git clone https://github.com/shdown/luastatus сделай.

shdown
() автор топика
Ответ на: комментарий от safocl

Логи давай. Возможно, ты не для того barlib’а примеры используешь.

shdown
() автор топика
Ответ на: комментарий от shdown

сделал я все как ты сказал, склонировал гит, из него установил, из ветки мастер, и ничего не изменилось.

в строке конфига i3wm пишу:

status_command cd ~/.config/luastatus && exec luastatus-i3-wrapper -B no_separators time-battery.lua alsa.lua xkb.lua

содержимое файлов:

cat ~/.config/luastatus/alsa.lua 
widget = {
    plugin = 'alsa',
    cb = function(t)
        if t.mute then
            return {full_text='[mute]', color='#e03838'}
        else
            local percent = (t.vol.cur - t.vol.min) / (t.vol.max - t.vol.min) * 100
            return {full_text=string.format('[%3d%%]', 0.5 + percent), color='#718ba6'}
        end
    end,
}

cat ~/.config/luastatus/time-battery.lua 
bat_uev = io.open('/sys/class/power_supply/BAT0/uevent', 'r')

function get_bat_seg()
    if not bat_uev then
        return {full_text='[-?-]'}
    end
    bat_uev:seek('set', 0) -- yes, seeking on sysfs files is OK
    local status, capa
    for line in bat_uev:lines() do
        local key, value = string.match(line, '(.-)=(.*)')
        if key == 'POWER_SUPPLY_STATUS' then
            status = value
        elseif key == 'POWER_SUPPLY_CAPACITY' then
            capa = tonumber(value)
        end
    end
    if status == 'Unknown' or status == 'Full' then
        return nil
    end
    local sym, color = '?', '#dcdcdc'
    if status == 'Discharging' then
        sym = '↓'
        color = '#dca3a3'
    elseif status == 'Charging' then
        sym = '↑'
        color = '#60b48a'
    end
    return {full_text=string.format('[%3d%%%s]', capa, sym), color=color}
end

widget = {
    plugin = 'timer',
    opts = { period = 2 },
    cb = function(t)
        return {{full_text=os.date('[%H:%M]'), color='#dc8cc3'}, get_bat_seg()}
    end,
}
cat ~/.config/luastatus/xkb.lua 
widget = {
    plugin = 'xkb',
    cb = function(t)
        if t.name == 'us' then
            return {full_text='[En]', color='#9c9c9c'}
        elseif t.name == 'ru' then
            return {full_text='[Ru]', color='#eab93d'}
        else
            return {full_text='[' .. t.id .. ']'}
        end
    end,
}

переключалка языка и время отображаются нормально... (переключался языка при чем переключается моментально, в отличии от соньковского)

safocl ★★
()
Ответ на: комментарий от safocl

Это странно. Покажи вывод

cd ~/.config/luastatus && luastatus-i3-wrapper -B no_separators time-battery.lua alsa.lua xkb.lua

anonymous
()
Ответ на: Ping? от shdown

вот вывод:

--> cd ~/.config/luastatus && luastatus-i3-wrapper -B no_separators time-battery.lua alsa.lua xkb.lua
{"version":1,"click_events":true,"stop_signal":0,"cont_signal":0}
[
[{"name":"0","full_text":"[13:44]","color":"#dc8cc3","separator":false},{"name":"0","full_text":"[-?-]","separator":false}],
[{"name":"0","full_text":"[13:44]","color":"#dc8cc3","separator":false},{"name":"0","full_text":"[-?-]","separator":false},{"name":"2","full_text":"[En]","color":"#9c9c9c","separator":false}],
luastatus: error: (lua) alsa.lua:8: bad argument #2 to 'format' (number has no integer representation)
[{"name":"0","full_text":"[13:44]","color":"#dc8cc3","separator":false},{"name":"0","full_text":"[-?-]","separator":false},{"full_text":"(Error)","color":"#ff0000","separator":false},{"name":"2","full_text":"[En]","color":"#9c9c9c","separator":false}],
[{"name":"0","full_text":"[13:44]","color":"#dc8cc3","separator":false},{"name":"0","full_text":"[-?-]","separator":false},{"full_text":"(Error)","color":"#ff0000","separator":false},{"name":"2","full_text":"[En]","color":"#9c9c9c","separator":false}],
[{"name":"0","full_text":"[13:44]","color":"#dc8cc3","separator":false},{"name":"0","full_text":"[-?-]","separator":false},{"full_text":"(Error)","color":"#ff0000","separator":false},{"name":"2","full_text":"[En]","color":"#9c9c9c","separator":false}],
[{"name":"0","full_text":"[13:44]","color":"#dc8cc3","separator":false},{"name":"0","full_text":"[-?-]","separator":false},{"full_text":"(Error)","color":"#ff0000","separator":false},{"name":"2","full_text":"[En]","color":"#9c9c9c","separator":false}],
[{"name":"0","full_text":"[13:44]","color":"#dc8cc3","separator":false},{"name":"0","full_text":"[-?-]","separator":false},{"full_text":"(Error)","color":"#ff0000","separator":false},{"name":"2","full_text":"[En]","color":"#9c9c9c","separator":false}],

safocl ★★
()
Ответ на: комментарий от vertexua

Ну, там плагины, написанные на сишке, которые мгновенно передают твоему виджету инфу, и barlib’ы, написанные на сишке, которые мгновенно обновляют, вместо вот таких вот костылей (из README.md i3blocks):

[volume]
label=Volume:
command=amixer get Master | grep -E -o '[0-9]{1,3}?%' | head -1
interval=once
signal=1
# use 'pkill -RTMIN+1 i3blocks' after changing the volume
shdown
() автор топика
Ответ на: комментарий от shdown

http://vivien.github.io/i3blocks/

interval

If it is a positive integer, then the block is spawned on startup and the value is used as a time interval in seconds to schedule future updates. If unspecified or 0, the block won't be executed on startup (which is useful to simulate buttons). If «once» (or -1), the block will be executed only on startup (note that a click or signal will still trigger an update). If «repeat» (or -2), the block will be spawned on startup, and as soon as it terminates (useful to repeat blocking commands). Use with caution! If «persist» (or -3), the block will be executed only on startup, and updated as soon as it outputs a line. Thus limited to single line updates.

Все режимы. persist - то, что ты называешь «мгновенно». Сам процесс можешь писать «на сишке»

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

И что — каждый для себя будет заново писать «на сишке»? Никто так не делает же. Лучше уж один раз плагины написать, которые просто инфу виджету передают, который уже говорит, как её показать.

И, кстати, luastatus не ограничен i3.

shdown
() автор топика
Ответ на: комментарий от vertexua

Там вообще какой-то трешак лежит, выше по треду было уже:

$ wget -nv https://raw.githubusercontent.com/vivien/i3blocks/master/scripts/volume
2017-01-18 15:29:48 URL:https://raw.githubusercontent.com/vivien/i3blocks/master/scripts/volume [2738/2738] -> "volume" [1]
$ countforks bash volume
18

И если тебе нужно будет какую-то нетривиальную логику в виджете делать, то с luastatus это сделать проще. А так тебе нужно будет ковырять саму сишную программу, или что там.

shdown
() автор топика
Ответ на: комментарий от shdown

И если тебе нужно будет какую-то нетривиальную логику в виджете делать, то с luastatus это сделать проще.

Напиши i3blocks скрипт на Lua

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

Модули не для всего есть же. Как написать для скрипт для раскладки клавиатуры, или для заголовка текущего окна?

shdown
() автор топика
Ответ на: комментарий от vertexua

Мда, там плагины на С, которые юзают Lua API

Внезапно, для того, чтобы передать Lua-виджету Lua-объект, нужно использовать Lua API.

Проблевался

От чего?

shdown
() автор топика
Ответ на: комментарий от shdown

Внезапно, для того, чтобы передать Lua-виджету Lua-объект, нужно использовать Lua API.

Или как в i3blocks, просто Unix stdout

От чего?

От усилий необходимых для написания плагина. В i3blocks хоть однострочечник на баше, хоть скрипт на Python/Lua, хоть програму на С++, хоть демон какой-то. Ему пофиг, выведи текст на экран

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 2)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.