Как Я Разработал Плагин Для Pidgin 2



Преамбула Как поклонник многопротокольных IM-клиентов, я уже довольно давно использую Miranda NG. Но кривая поддержка некоторых современных протоколов типа Discord затрудняла использование только его, хотя возможности его настройки очень широки.

В конце концов практичность взяла верх над перфекционизмом, и я установил Pidgin 2.14. Несмотря на то, что программа была немного пугающей, программа оказалась очень практичной.

Однако в этом была ложка дегтя.

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

Разумеется, это привело к тому, что даже при моем скромном круге общения в списке контактов образовалось 30+ групп — помимо групп из других протоколов.

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

Однако быстрый поиск выдал старый трекер ошибок Пиджин , где эта возможность была упомянута.

и помечена как неисправимая.

Упс.

Ну а где наши не пропадали, так и сам сделаю имитацию!



Полезные ресурсы



Концепция

Pidgin в основном поддерживает плагины, написанные на C. Существуют загрузчики плагинов Perl, а также механизм взаимодействия с DBus, но основным инструментом разработки является C. Более того, из всех рассмотренных языков он мне наименее знаком.

Далее ключевая идея плагина была сформулирована так: скрыть или объединить группы в списке контактов по определенному набору правил.

При этом наборы правил должны быть заданы пользователем, иметь читаемое название и желательно иконку.

Поскольку плагин для Discord генерировал группы вида «Имя сервера:Категория», сама собой напрашивалась идея использовать в плагинах подстановочные знаки, чтобы не приводить отдельно все категории серверов.

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

Таким образом, задача была разложена на составляющие:

  1. Узнайте, как работает плагин Pidgin.
  2. Определите, какой инструмент использовать для сопоставления с подстановочными знаками, и реализуйте логику применения правил к группе.

  3. Узнайте, как работает список контактов libpurple (серверная часть клиента Pidgin) и как получить информацию о группах.

  4. Узнайте, как список контактов отображается в Pidgin и как скрыть или показать контакт в списке.

  5. Узнайте, как работает GUI Pidgin в целом и как добавить к нему собственную панель инструментов.

  6. Разберитесь с сохранением настроек и созданием диалогового окна настроек для вашего плагина.



Простой плагин для Pidgin.

К счастью, в репозитории был довольно понятный привет-world.c , который описывает базовую структуру плагина Pidgin. Все плагины можно разделить на несколько категорий:
  • core — плагины, которые работают только с libpurple и никак не влияют на пользовательский интерфейс.

  • prpl — плагины, реализующие протоколы обмена сообщениями.

  • lopl — загрузчики плагинов на скриптовых языках.

  • gtk, gtk-x11 и gtk-win32 — плагины, работающие с графическим интерфейсом — универсальные, специфичные для Linux и для Windows.
  • gnt — плагин для консольной версии клиента (Finch).

Очевидно, нас интересует плагин gtk, на худой конец gtk-win32. И сам Pidgin, и лежащая в его основе библиотека libpurple построены на основе GLib и графический инструментарий ГТК - правда, довольно старые версии.

Отсюда следует несколько выводов.

Во-первых, для управления памятью часто используется механизм счетчика ссылок, что несколько упрощает вопросы, связанные со временем жизни объектов.

Во-вторых, GLib предоставляет механизм сигналов, позволяющий достаточно прозрачно реализовать обработку событий, включая события интерфейса.

libpurple активно использует сигналы для уведомления клиентского кода о событиях, поступающих из сетей обмена мгновенными сообщениями, а также об операциях с сохраненными данными, такими как список контактов.

Поэтому вопрос реакции на то или иное событие будет сводиться к поиску нужного сигнала и подписке на него.

При этом была решена проблема реализации Windcard-Matching — GLib предоставляет соответствующие инструменты, над которыми требовалась лишь небольшая логическая надстройка.



BuddyList и все, все, все

По результатам вдумчивого чтения документации (и внимательного опроса на полуофициальном Discord-сервере проекта) выяснилось следующее.

Формально список друзей libpurple представляет собой дерево разнородных узлов, подобное известному шаблону «линкер».

Это дерево обернуто в объект ПурпурныйСписок Приятелей .

Однако на практике это дерево образует фиксированную трехуровневую иерархическую структуру.

На верхнем уровне расположены группы контактов, представленные объектами.

Пурпурнаягруппа , внизу - отдельные участники ( ФиолетовыйБадди ), а в промежуточном - чаты ( ФиолетовыйЧат ) и контакты ( ФиолетовыйКонтакт ) как таковой.

«Элементы на каждом уровне дерева связаны в двусвязный список – таким образом, родительский элемент сохраняет только ссылку на своего первого дочернего элемента.

По сути, контакты в libpurple аналогичны метаконтактам в других клиентах — они позволяют объединить несколько аккаунтов одного человека в одну сущность, что довольно удобно.

Однако это никак не помогло в решении проблемы группировки групп контактов.

Пришлось отказаться от идеи группировки и остановиться на варианте сокрытия «ненужных» групп.

Для этого мне пришлось прочесать исходный код, отвечающий за отображение контакта в графическом интерфейсе Pidgin. А потом всплыли так называемые контактные флаги, среди которых был флаг НЕВИДИМЫЙ.

ЭПредметы с этим флагом не отображались в списке контактов, независимо от их типа.

Бинго? Почти.

Как быстро выяснилось, скрытие контакта в группе может привести к сокрытию всей группы.

Такое поведение было следствием логики функции pidgin_blist_update_group() , который скрывал всю группу, если в ней обновлялся скрытый контакт. Почему так, я до сих пор не понимаю.

Однако это не означало, что данный механизм непригоден — он лишь ограничивал плагин сокрытием целых групп.



графический интерфейс

Сам графический элемент списка контактов оказался одновременно проще и сложнее, чем я ожидал.

Проще, потому что это оказался всего один компонент - GtkTreeView .

Более сложный, поскольку этот компонент имеет дело с отдельным объектом модели (скрытым за интерфейсом).

GtkTreeМодель ), и имеет хитрый механизм настройки отрисовки элементов модели на экране.

Как следствие, чтобы вмешаться в него в достаточной степени, мне пришлось бы повторить реализацию значительной части списка контактов Pidgin, который был бы длинным, сложным и плохо совместимым с другими плагинами.

В результате для реализации сокрытия нам пришлось использовать описанный выше флаг INVISIBLE. Однако я нашел GtkTreeView полезным при реализации диалога редактирования правил.

Что касается остального графического интерфейса, то все оказалось довольно просто.

TreeView упакован в несколько контейнеров, ссылки на которые хранятся в общедоступный структура данных.

Таким образом, создав свой контейнер, вы сможете добавить его к уже существующим.

Но этот подход серьезно ограничен в своих возможностях.

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

Плагин мойстатусбокс из фиолетового пакета плагинов добавляет панель изменяемого размера, но для этого приходится переделывать половину интерфейса.

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



Сохранение настроек

Для хранения настроек в libpurple реализован собственный механизм — своего рода иерархическая база данных (на практике она сохраняется в XML-документе).

К счастью, я нашел хороший пример работаю с ней.

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

Прежде всего, проверка ситуации, когда параметр существует, но имеет неправильный тип, требует довольно громоздкого кода.

Добавить недостающую настройку гораздо проще.

Вопрос перехода с одной схемы настроек на другую, например, при обновлении плагина, также остаётся открытым.

Кроме того, наиболее сложным стандартным типом данных является список строк.

Поскольку в моем случае мне нужно было иметь дело со списком структур данных, мне пришлось написать умеренно сложный код для десериализации этой структуры.

Наконец, Pidgin предоставляет три механизма изменения настроек плагина.

Первый В упрощенном виде позволяет указать список записей, содержащих имя параметра и его желаемое представление.

На основе этого списка Pidgin автоматически сгенерирует диалог настроек и привяжет их к элементам управления.

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

Третий, наиболее универсальный, — использовать механизм действия , то есть дополнительные пункты меню, добавляемые плагинами.

В рамках действий можно выполнять практически любые операции, включая отображение необходимых диалоговых окон.

Единственное ограничение состоит в том, что эти окна будут работать параллельно с остальной частью программы.

В результате настройки плагина были разделены на два раздела.

Один описывает только правила скрытия/показа групп и вынесен в меню действий плагина – так как пользователь будет использовать его чаще.

Второй описывает внешний вид и размещение селектора правил и скрыт в стандартном диалоге настроек плагина.

По идее, достаточно один раз им воспользоваться, и потом о нем можно забыть.



Результат

Интерфейс выбора групп для отображения выглядит примерно так:

Как я разработал плагин для Pidgin 2

Ниже главного меню расположена панель выбора наборов правил.

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

Например, даны три набора — один показывает все, второй показывает только группу «Общие», третий показывает все, кроме «Общего».

Правила настраиваются через диалог настроек, показанный ниже.

Это не очень красиво, но свою работу выполняет.

Как я разработал плагин для Pidgin 2

Сам плагин можно найти по адресу Гитхаб .



Результаты проекта

Считаю ли я плагин полезным? Мне это было полезно.

Если сам плагин или информация, представленная в статье, будет полезна кому-то еще, это будет здорово.

Насколько сложно было вникнуть в эту тему? Удивительно просто, несмотря на то, что у меня не было опыта разработки ни на чистом C, ни на GTK. Кодовая база пиджина довольно хорошо организована, хотя документация иногда оставляет желать лучшего.

Что было самым сложным в разработке? Как ни странно, настроил среду для сборки проекта на Windows. Под Linux это оказалось намного проще, поэтому почти вся разработка велась на домашнем сервере под управлением Debian. Руководство по сборке есть, но требует адаптации.

В результате мне пришлось обратиться за помощью на сервер Pidgin Discord. Почему я почувствовал необходимость написать эту статью? Потому что мне приходилось повозиться, собирать обрывки документации и лазить по мертвым ссылкам, и я хотел сохранить полученные знания.

И хотя сейчас Pidgin переживает переход на версию 3.0 с сопутствующей переработкой кодовой базы, версия 2.x.y будет актуальна еще долгое время.

Да и в целом ситуация выглядит так, будто мультипротокольные клиенты находятся в некотором упадке.

Если это не так, напишите в комментариях — буду рад ошибаться.

Теги: #мессенджеры #плагин #pidgin

Вместе с данным постом часто просматривают: