Дисклеймер: Автор не специалист, соответственно ошибки и неточности могут иметь место. Также отмечу, что статья в целом ориентирована на непрофессионалов и новичков в оконных менеджерах.
Предыстория
Некоторое время назад я, в силу ряда причин, решил перейти со стандартного DE на один из оконных менеджеров (для тех, кому интересно, archwiki), изначальным дистрибутивом был выбран ArcoLinux, так как он обладал расширенным репозиторием Arch, который включал в себя собственные пакеты команды ArcoLinux (в т.ч. меню archlinux-logout для выхода в спящий режим, выхода из системы и т.д.), а также предоставлял большие возможности по выбору пакетов, изначально установленных в системе, и настроенных оконных менеджеров (т.к. настраивать их с нуля, зачастую, очень долго и муторно, на мой взгляд).
Изначально выбор пал на Hyperland, который работал уже с Wayland, однако затем вскрылись некоторые проблемы в работе программ, работающих на Electron (такие программы как Obsidian, VSCode и др.), эта проблема решалась, однако муторно, но, помимо этой проблемы, были и другие. В общем, я решил, как итог, перейти на Qtile, который имеет как Wayland версию (не пробовал), так и версию для X.org
Спустя некоторое время я прешёл на Manjaro, соответственно пакеты из ArcoLinux мне стали недоступны через репозиторий (либо же доступны посредством AUR или GitHub, обе версии для меня нежелательны). Мне, по итогу, показалось более интересным самому сделать для себя приложение, и посмотреть, как можно его интегрировать в свою систему, максимально незаметно и удобно.
Работа над меню выхода
Для начала я решил, что хочу иметь следующие возможности вызвать меню выхода:
Через rofi (меню вызова приложений)
Через кнопку на верхней панели Qtile
Через комбинацию клавиш или кнопку питания ноутбука
Но перед этим, мне нужно было написать скрипт, который будет выполнять всю работу и отвечать за визуализацию. Я использовал Python и его встроенные библиотеки Tkinter (GUI) и os (для вызова команд Линукс). Мой код и другие файлы (иконки и .desktop файл) можно увидеть здесь (репозиторий будет обновляться, по мере необходимости), но я, пожалуй, скопирую нынешний код сюда:
#!/usr/bin/env python
from tkinter import *
from tkinter import ttk
import os
def button_click(command:int):
match command:
case 0:
os.system("systemctl reboot")
case 1:
os.system("systemctl poweroff")
case 2:
os.system("systemctl suspend; i3lock -n")
case 3:
os.system("pkill -KILL -u $USER")
root.destroy()
root = Tk()
root.title("Power Menu")
root.geometry("400x200")
root.resizable(False, False)
root.attributes("-alpha", 0.5)
ttk.Button(text="Reboot", command=lambda: button_click(0)).pack(expand=True, side="right")
ttk.Button(text="Shut Down", command=lambda: button_click(1)).pack(expand=True, side="right")
ttk.Button(text="Suspend", command=lambda :button_click(2)).pack(expand=True, side="right")
ttk.Button(text="Log out", command=lambda: button_click(3)).pack(expand=True, side="right")
root.mainloop()
Первая линия играет здесь важную роль, так как позволяет сделать скрипт напрямую исполняемым из командной строки (как bash скрипты), это позволяет упростить написание .desktop файла в последующем, и избежать написания отдельного скрипта bash, для вызова скрипта Python, т.к. я не хочу писать полный путь к исполняемому файлу, а хочу просто закинуть его в PATH и использовать также, как любое другое приложение в системе.
Чтобы закинуть файл в PATH системы, для начала посмотрим какие опции у нас есть. Для этого используем echo $PATH
и ищем среди представленных директорий, те которые принадлежат нам, как обычному пользователю (не администратору). В моем случае, это $HOME/.local/bin
, которую надо ещё создать. После создания, просто копируем туда скрипт, теперь он должен стать доступным через терминал. Не забываем сделать файл исполняемым через проводник или chmod
, а также добавить класс нашей программы (который можно узнать с помощью xprop | grep CLASS
), в список плавающих окон (можно также использовать название программы, вместо класса):
floating_layout = layout.Floating(float_rules=[
# ...
Match(wm_class='tk'),
# ...
] fullscreen_border_width = 0, border_width = 0) # Последние два аргумента
необязательны
Теперь можно перейти к следующему этапу.
Через меню rofi
Для этого этапа нужно сделать .Desktop файл и закинуть его в $HOME/.local/share/applications
. Код простой:
[Desktop Entry]
Name=Power-Menu
Exec=power-menu.py
Type=Application
Icon=shutdown-icon
Как вы можете видеть, я также указал иконку для приложения, для этого я закинул SVG файл в $HOME/.local/share/icons
, чтобы не указывать полный путь к файлу в .Desktop файле.
Через кнопку на верхней панели Qtile
Для этого идём в документацию Qtile (весьма неплохую, кстати, хотя иногда и приходится прибегать к помощи пользователей), в разделе встроенных виджетов, можем выбирать любой, который имеет у себя следующий аттрибут mouse_callbacks
(практически все), т.е. если вы хотите, чтобы меню открывалось при нажатии на календарь, это вполне возможно. Я предпочел иметь отельный виджет с иконкой, соответсвенно добалвляю виджет в соответсвующее место в config.py
, в .config/qtile
:
widget.Image(
filename="/home/user/.local/share/icons/shutdown-icon.png",
margin=5,
mouse_callbacks={"Button1":lambda:qtile.cmd_spawn("power-menu.py"),}
),
Да, для этого я положил в директорию с иконками PNG файл, и нет, я не знаю, будет ли мой .Desktop файл отдавать предпочтение PNG или SVG файлу, в любом случае это легко исправить простым переименованием файла. Остается только перезапустить Qtile, и посмотреть на результат.
Через комбинацию клавиш или кнопку питания ноутбука
Здесь стоит уточнить, что в моей изначальной версии Qtile (т.е. версии ArcoLinux), все комбинации клавиш, которые запускают программы, сделаны через демон sxhkd. Я решил, что это более удобный вариант, так как если я захочу перейти на другой менеджер, часть работы будет уже сделана. Поэтому я здесь приведу код из файла sxhkdrc
:
#PowerButton
XF86PowerOff
power-menu.py
Помимо этого, необходимо, чтобы система не отключалась при коротком нажатии кнопки питания. Для этого через режим администратора редактируем файл /etc/systemd/logind.conf
и в нем добавляем (или снимаем комменты и изменяем) следующую строку:
HandlePowerKey=ignore
Конец
На этом разработка и внедрение приложения в систему закончены. Конечно, некоторые этапы будут разниться в зависимости от следующих факторов:
Выбранный язык программирования (например с C/C++, очевидно, что для начала придется компилировать бинарник и работать уже с ним, а с bash проблем будет ещё меньше, чем с Python)
Выбранный оконный менеджер и отсутствие/присутствие sxhkd или альтернативы. Если последнего нет, то нужно понимать, что не все менеджеры воспринимают нажатие кнопки питания. Также для Wayland менеджеров, использование
xprop
(как иsxhkd
) не актуальноЛинукс дистрибутива. На некоторых, придется дополнять PATH, если есть задача разместить все файлы в директориях пользователя, а не администратора
Эта моя первая статья, поэтому буду рад аргументированной критике или дополнениям и хорошим источникам.