Не так давно я установил 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, который выполняет действие при нажатии.

Мы закончили, правда?

Апплеты и сигналы

К этому моменту я был вполне доволен своим первым апплетом. При нажатии он делал что-то, он был быстрым, у него были красивые значки, которые обновлялись должным образом. Было всего две проблемы:

  1. Сам апплет не запускается с помощью сочетания клавиш.
  2. Если я оставлю старый скрипт подключенным к сочетанию клавиш, значок апплета перестанет синхронизироваться.

Я не скажу вам, сколько времени я потратил, пытаясь выяснить, как заставить апплеты реагировать на глобальные сочетания клавиш. Достаточно сказать, что я пробовал много разных способов и был крайне разочарован, когда наконец нашел решение. Эврика! момент случился, когда я рыскал код встроенного звукового апплета. Я искал там, потому что значок громкости меняется в соответствии с текущей настройкой громкости; он должен иметь соединение с системой, клавиатурой или иным образом! Я наткнулся на эти строки кода:

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.