Не так давно я установил Arch Linux. Законы Вселенной гласят, что я кричу с горных вершин, вот и мы. Когда я решал, какую среду рабочего стола использовать, я хотел попробовать что-то новое; Я довольно часто использовал Ubuntu, поэтому я хотел держаться подальше от Unity и Gnome. Введите Корица. Он достаточно популярен, и я слышал о нем хорошие отзывы, поэтому решил попробовать.
Я никогда раньше не разрабатывал для Gnome или Cinnamon, поэтому создание апплета было для меня новой областью. Это было интересное путешествие, поэтому я поделюсь своим опытом здесь в надежде помочь другим, желающим сделать то же самое.
Базовый вариант использования
Когда я использую настольный компьютер дома, мне часто нужно переключать аудиовыход между динамиками и наушниками. В Windows это довольно тривиально; Я могу щелкнуть значок громкости на панели задач, щелкнуть раскрывающееся меню устройства и выбрать нужное устройство вывода.
Чтобы сделать это с помощью сочетания клавиш, я использую SoundSwitch, который также полезен, потому что я могу сказать ему, какие устройства я хочу включить в ротацию, удаляя беспорядок, такой как цифровые или виртуальные устройства вывода. Это означает, что я могу быстро переключаться между колонками и наушниками из любого приложения с помощью сочетания клавиш Ctrl + Alt + F11.
Как я объясню в следующем разделе, в Linux все не так просто. С другой стороны, это означает, что я могу что-то создать! Моя первоначальная цель была проста: переключить устройство вывода звука с помощью сочетания клавиш.
Поиск решения
Зачем мне вообще нужно было создавать что-то нестандартное? В случае моей звуковой карты динамики и наушники отображаются как два приемника на одном устройстве. Активный приемник запускается при физическом подключении и отключении наушников, и, по крайней мере, в Gnome и Cinnamon, невозможно заставить один или другой активироваться.
Более точный контроль можно получить, используя alsamixer
. Он предоставляет интерфейс микшера для устройств и раковин, а также некоторые настройки звука низкого уровня. После выбора звуковой карты с помощью F6, мне предоставляется микшер.
Headphones
и Front
- раковины, за которыми я охотлюсь. После переключения параметра Auto-Mute Mode
на Disabled
я могу переключить выходы, установив один ползунок на 0, а другой на 100. Здесь есть только одна проблема: alsamixer - это интерактивный терминал приложение, и поэтому не подходит для написания сценариев.
К счастью, amixer
предоставляет аналогичные функции, но с неинтерактивными командами.
amixer -c0 set "Auto-Mute Mode" Disabled # Disable auto-muting amixer -c0 set 'old-device-name' 0% # Mute the old device amixer -c0 set 'new-device-name' 100% # Enable the new device
Кроме того, мне нужно использовать pactl
, чтобы сообщить ОС, какой приемник мы активно используем.
pactl set-sink-port 0 'sink-name' # Set the desired sink
Все идет нормально. Если я запустил их с правильными именами устройств и приемников, я могу переключить вывод на желаемое устройство.
Создание сценария
Чтобы приблизиться к моей цели на один шаг, мне нужен был сценарий, который запускал бы указанные выше команды таким образом, чтобы устройство вывода переключалось взад и вперед. Я также решил использовать notify-send
для отображения временного уведомления рабочего стола, давая мне знать, что вывод был успешно изменен. Вот что я придумал:
Вот оно! Каждый раз, когда запускается этот скрипт, он будет переключаться между наушниками и динамиками.
Сочетание клавиш
Теперь, когда у меня есть сценарий для моего решения, мне нужно было сказать Cinnamon, чтобы он запускал его при нажатии нужного сочетания клавиш. К счастью, это несложно. В меню Клавиатура есть вкладка для добавления пользовательских сочетаний клавиш. Установив желаемую комбинацию клавиш, я указал ее на написанный мной скрипт.
Казалось, это соответствовало моим требованиям! Нажатие Ctrl + F11 вызовет скрипт, который переключит аудиоустройство и отправит небольшое приятное уведомление.
Поднимая его на ступеньку выше
На самом деле я какое-то время использовал приведенный выше сценарий в своем рабочем процессе, но мне казалось, что чего-то не хватает. Я хотел иметь индикатор того, какое устройство выбрано в данный момент, а также возможность переключения устройств щелчком мыши. Панель задач казалась мне наиболее подходящей для этого. Какое-то время я пытался найти руководства по написанию приложений, которые находятся в системном трее на Cinnamon, но с удивительно небольшими результатами. В конце концов я понял, почему: у приложений в системном трее на Cinnamon есть название: Апплеты.
Написание нашего первого апплета
Как пользователь, я до сих пор очень доволен корицей. Это просто, красиво и настраивается. С другой стороны, я, как разработчик, очень разочарован этим.
Корица - это вилка Гнома. Из-за этого он наследует от него множество функций. Это хорошо: Gnome очень хорошо документирован (с некоторыми заметными исключениями) и популярен, что делает его относительно привлекательным для новичков, таких как я.
Проблема в том, что утечка Cinnamon из Gnome недостаточно задокументирована и, по крайней мере, для меня, очень трудно отследить, а в некоторых случаях вообще не существует.
Они действительно предоставляют краткое руководство по написанию простого апплета. Я использовал это как отправную точку, чтобы начать возиться. Еще более полезной была эта запись в блоге Эли Биллауэра. С помощью этих ресурсов я смог создать апплет, который при нажатии на панели задач запускал те же команды, что и наш сценарий выше. Два необходимых файла - это metadata.json
и applet.js
. Вот как выглядел мой первый applet.js
:
При щелчке по апплету вызывается функция on_applet_clicked()
. Оттуда я вызываю вспомогательные функции switchDevices()
и updateIcon()
.
Основное руководство Cinnamon по апплетам начало для меня сбиваться, когда мне нужно было записать вывод системной команды, а именно pactl
,, чтобы получить текущее устройство вывода. Библиотека util
Cinnamon не предоставляет такой возможности, поэтому мне пришлось взглянуть на базовую реализацию и напрямую использовать команды GLib
.
Примечание: использование
spawn_command_line_sync()
GLib - одна из немногих функций, которые вызывают появление предупреждения при попытке установить апплет, утверждая, что это может вызвать проблемы с Cinnamon. Я обнаружил, что это верно, когда некоторые команды, которые я выполнял, выполнялись дольше, чем ожидалось, что приводило к зависанию всего рабочего стола.
Вы также можете видеть, что я устанавливаю значок в зависимости от текущего устройства вывода. Как упоминалось в руководстве по Cinnamon, название значка ссылается на установленные значки в /usr/share/icons
. Я обнаружил, что значки audio-headphones
и multimedia-volume-control
будут хорошими аналогами для наушников и динамиков соответственно. Обратите внимание, что я использую символические значки, потому что значки на панели задач должны быть простыми и монохромными.
В целом, приведенный выше код не слишком отличается от примера Applet Cinnamon. Это IconApplet
, который выполняет действие при нажатии.
Мы закончили, правда?
Апплеты и сигналы
К этому моменту я был вполне доволен своим первым апплетом. При нажатии он делал что-то, он был быстрым, у него были красивые значки, которые обновлялись должным образом. Было всего две проблемы:
- Сам апплет не запускается с помощью сочетания клавиш.
- Если я оставлю старый скрипт подключенным к сочетанию клавиш, значок апплета перестанет синхронизироваться.
Я не скажу вам, сколько времени я потратил, пытаясь выяснить, как заставить апплеты реагировать на глобальные сочетания клавиш. Достаточно сказать, что я пробовал много разных способов и был крайне разочарован, когда наконец нашел решение. Эврика! момент случился, когда я рыскал код встроенного звукового апплета. Я искал там, потому что значок громкости меняется в соответствии с текущей настройкой громкости; он должен иметь соединение с системой, клавиатурой или иным образом! Я наткнулся на эти строки кода:
this._sound_settings = new Gio.Settings({ schema_id: CINNAMON_DESKTOP_SOUNDS }); this._sound_settings.connect("changed::" + MAXIMUM_VOLUME_KEY, Lang.bind(this, this._on_sound_settings_change));
Давайте разберемся с этим. Возможно, самая важная часть этих строк - вызов функции connect
. Gnome и GTK используют сигналы, чтобы разрешить любому объекту запускать события для любого другого объекта, при этом передатчику не нужно ничего знать о приемнике. Я немного поработал с Qt, поэтому эта концепция была мне знакома. В документации Gnome и Cinnamon вы заметите, что у многих объектов есть список сигналов, точно так же, как у них есть список свойств и функций. Вот чем мы собираемся воспользоваться.
К чему подключается звуковой апплет? GSettings. Gio.Settings
позволяет мне подключить мой апплет к сигналу changed
ключа GSettings. Это то соединение, которое мне было нужно!
Это был мой новый план: подключить апплет к созданному мной ключу GSettings и обновлять устройство и значок при изменении настроек. Давай сделаем это!
Создание схемы GSettings
Чтобы скомпилировать мою схему GSettings, мне нужно было создать .gschema.xml
файл в /usr/share/glib-2.0/schemas
. Вот как выглядит мой:
Свойства id
и path
schema
должны быть уникальными. По соглашению для id
используется ваше перевернутое доменное имя, за которым следует уникальный идентификатор для вашей схемы. Тот же самый контент можно использовать для path
, за исключением использования слэшей в качестве разделителей. Обратите внимание, что path
должен заканчиваться косой чертой в конце.
Я указал параметр device
для строки типа (отсюда "s"
). Как только это было сделано, мне нужно было сказать GLib перекомпилировать схемы:
glib-compile-schemas /usr/share/glib-2.0/schemas
Как только это будет сделано, я смогу использовать настройку в своем апплете.
Рефакторинг апплета для использования GSettings
Теперь, когда я настроил GSettings, я мог провести рефакторинг своего апплета. Я подключил его к объекту Gio.Settings
, указывающему на мою недавно созданную схему. При щелчке по апплету вместо явного выполнения команд он просто меняет значение параметров и позволяет потоку событий подбирать его. Это гарантирует, что независимо от того, где было внесено изменение, оно всегда будет принято апплетом.
Теперь апплет делает то же самое, что и раньше, за исключением того, что теперь он делает это, получая сигнал от нашего объекта GSettings. Итак, что осталось?
Активация апплета с помощью сочетания клавиш
Все, что мне оставалось сделать, это использовать сочетание клавиш, чтобы вызвать изменение настроек. Поскольку у меня уже был Ctrl + F11, связанный со сценарием bash, я просто изменил сценарий, чтобы использовать gsettings
CLI для внесения изменений.
Вот и все! Теперь я могу переключить свое устройство вывода звука, щелкнув значок на панели задач или используя комбинацию клавиш. В обоих случаях значок будет обновлен, и будет показано уведомление. Миссия выполнена!
Полный код можно найти на GitHub.