Sylpheed プラグイン仕様
Sylpheed のプラグイン機構の構成は以下のようになっています。
+----------+ +----------------------+ +-----------+
| Sylpheed |----| libsylpheed-plugin-0 |--+--| Plug-in A |
+----------+ +----------------------+ | +-----------+
Sylpheed 本体 プラグインインタフェース | プラグイン DLL
ライブラリ +--+
| +------------+ | | +-----------+
+--------| libsylph-0 |---------+ +--| Plug-in B |
+------------+ +-----------+
LibSylph メールライブラリ
Sylpheed は起動時にプラグインディレクトリにインストールされている プラグイン DLL をメモリにロードします。
起動時に読み込まれるプラグインディレクトリの場所は以下になります。ユーザのプラグインディレクトリのほうが優先されます。
(Sylpheed の設定ファイルディレクトリ)/plugins/
(sylpheed.exe がインストールされているディレクトリ)/plugins/ (※ Windows)
(Sylpheed インストール時の PREFIX)/lib/sylpheed/plugins/ (※ Linux/Unix)
プラグインは libsylpheed-plugin-0 と libsylph-0 ライブラリで 提供されている API を通してのみ Sylpheed の機能にアクセスできます。 また、 Sylpheed 内の構造体の各メンバに直接アクセスすることはできません。
プラグイン API には、プラグインが直接呼び出す関数群と、 GObject のシグナル機構を利用して、特定のイベントが発生した場合に コールバック関数を呼び出すものの2種類があります。
プラグイン機構は libsylph/sylmain.[ch] と src/plugin.[ch] で実装されて います。
プラグインから呼び出せる Sylpheed の機能はまだ少ないですが、バージョンアップ毎に増やしていく予定です。バージョンアップの際は基本的にバイナリ互換性は保持されます。
void syl_plugin_signal_connect (const gchar *name, GCallback callback,
gpointer data);
SylPlugin オブジェクト(ライブラリ内部で保持)で利用できるシグナルに 接続します。シグナルを受け取るコールバック関数の仕様は通常の GObject と 同様です。 利用できるシグナルに関してはシグナルの一覧を参照してください。
void syl_plugin_signal_disconnect(gpointer func, gpointer data);
syl_plugin_signal_connect() で接続したシグナルを解除します。
void syl_plugin_signal_emit(const gchar *name, ...);
SylPlugin オブジェクトのシグナルを発行します。
gint syl_plugin_init_lib (void);
libsylpheed-plugin-0 ライブラリの初期化を行います。
gint syl_plugin_load (const gchar *file);
プラグイン DLL ファイルをメモリにロードします。
gint syl_plugin_load_all (const gchar *dir);
指定したディレクトリ内のプラグイン DLL ファイルをメモリにロードします。
void syl_plugin_unload_all (void);
ロードしたすべてのプラグインをアンロードします。
GSList *syl_plugin_get_module_list (void);
現在メモリにロードされているプラグインのリストを取得します。 GModule 構造体へのポインタのリストが返ります。 リストはライブラリ内部で保持しているため、解放できません。
SylPluginInfo *syl_plugin_get_info (GModule *module);
プラグインの情報を取得します。情報は SylPluginInfo 構造体で返ります。
gboolean syl_plugin_check_version (GModule *module);
プラグインインタフェースのバージョンを比較し、互換性があるかどうかを 確認します。バージョンが一致する場合は TRUE 、一致しない場合は FALSE が返ります。
gint syl_plugin_add_symbol (const gchar *name, gpointer sym);
ライブラリにシンボル名とそれに関連付けられるポインタ値を登録します。
gpointer syl_plugin_lookup_symbol (const gchar *name);
syl_plugin_add_symbol() で登録したシンボルを検索し、ポインタ値を返します。
void plugin_load(void)
プラグインのロード時に Sylpheed から呼び出されます。 ここでプラグインの初期化処理などを行います。
void plugin_unload(void)
プラグインのアンロード時に Sylpheed から呼び出されます。 ここでプラグインの後処理などを行います。
SylPluginInfo *plugin_info(void)
プラグインの情報を格納する構造体を Sylpheed に返すための関数です。 通常は静的な構造体へのポインタを返します。
gint plugin_interface_version(void)
プラグイン API のインタフェースのバージョンを Sylpheed に返すための 関数です。プラグインでは通常は定数 SYL_PLUGIN_INTERFACE_VERSION を返し、 Sylpheed ではこの値を Sylpheed 本体側の値と比較し、互換性のあるバージョン かどうかをチェックします。 Sylpheed 本体のプラグインインタフェースバージョン はプラグインのプラグインインタフェースバージョン以上である必要があります。 また、インタフェースバージョンのメジャーバージョンが異なる場合も互換性は なくなります。
例1: Sylpheed のプラグインインタフェースバージョンが 0x0102 で
プラグインのプラグインインタフェースバージョンが 0x0100 の場合 OK
例2: Sylpheed のプラグインインタフェースバージョンが 0x0102 で
プラグインのプラグインインタフェースバージョンが 0x0103 の場合 NG
関数の一覧は src/plugin.h もしくは libsylph/*.h を参照してください。
以下のシグナルは syl_plugin_signal_connect() を呼び出して使用します。
例:
static void plugin_load_cb(GObject *obj, GModule *module, gpointer data)
{
...
}
syl_plugin_signal_connect("plugin-load", G_CALLBACK(plugin_load_cb), data);
void (* plugin_load) (GObject *obj, GModule *module);
syl_plugin_load() でプラグインをロードしたときに発行されるシグナルです。
void (* plugin_unload) (GObject *obj, GModule *module);
syl_plugin_unload_all() でプラグインをアンロードしたときに発行される シグナルです。
void (* folderview_menu_popup) (GObject *obj, gpointer ifactory);
FolderView でコンテキストメニューをポップアップしたときに発行される シグナルです。
void (* summaryview_menu_popup) (GObject *obj, gpointer ifactory);
SummaryView でコンテキストメニューをポップアップしたときに発行される シグナルです。
void (* compose_created) (GObject *obj, gpointer compose);
メッセージ作成ウィンドウが作成されたときに発行されるシグナルです。 取得した compose ポインタは syl_plugin_compose_*() 関数で使用します。
void (* compose_destroy) (GObject *obj, gpointer compose);
メッセージ作成ウィンドウが破棄される直前に発行されるシグナルです。
void (* textview_menu_popup) (GObject *obj,
GtkMenu *menu,
GtkTextView *textview,
const gchar *uri,
const gchar *selected_text,
MsgInfo *msginfo);
TextView でコンテキストメニューをポップアップするときに発行される シグナルです。ここで渡された GtkMenu に対して任意のメニュー項目を 追加することができます。
メニューオブジェクトはメニューを開くたびに作成され、閉じられると自動的に 破棄されるため、毎回メニュー項目を追加する必要があります。
menu: コンテキストメニューオブジェクト
textview: GtkTextView オブジェクト
uri: URI の上でメニューを表示した場合その URI 文字列
selected_text: テキストビューでテキストが選択されている場合、その文字列
msginfo: テキストビューで表示されているメッセージの MsgInfo オブジェクト
以下のシグナルは g_signal_connect() の第一引数に syl_app_get() で得られる GObject を渡して使用します。
例:
static void init_done_cb(GObject *obj, gpointer data)
{
...
}
g_signal_connect(syl_app_get(), "init-done", G_CALLBACK(init_done_cb),
data);
void (* init_done) (GObject *obj)
アプリケーションの初期化が完了した時点で発行されます。
void (* app_exit) (GObject *obj)
アプリケーションが終了する時に発行されます。
void (* app_force_exit) (GObject *obj)
アプリケーションが強制的(確認なし)に終了するときに発行されます。 (例: sylpheed --exit)
void (* add_msg) (GObject *obj, FolderItem *item, const gchar *file, guint num)
フォルダ item に番号 num のメッセージが追加された時に発行されます。
void (* remove_msg) (GObject *obj, FolderItem *item, const gchar *file, guint num)
フォルダ item から番号 num のメッセージが削除される時に発行されます。
void (* remove_all_msg) (GObject *obj, FolderItem *item)
フォルダ item からすべてのメッセージが削除されるときに発行されます。
void (* remove_folder) (GObject *obj, FolderItem *item)
フォルダ item が削除されるときに発行されます。
void (* move_folder) (GObject *obj, FolderItem *item, const gchar *old_id,
const gchar *new_id)
フォルダ item が old_id から new_id に移動(リネーム)されるときに 発行されます。 old_id, new_id はフォルダ識別子文字列です。
void (* folderlist_updated) (GObject *obj)
フォルダ情報が変更され、フォルダリストを格納した folderlist.xml ファイルが 更新されたときに発行されます。
void (* account_updated) (GObject *obj)
アカウント情報が更新されたときに発行されるシグナルです。 ただし、 account_update_lock() によってロックされている場合は 発行されません。
プラグインをビルドするには、まず Sylpheed がビルドできる環境が必要です。そして、 GTK+, libsylph, libsylpheed-plugin のヘッダファイルをインクルードし、ライブラリとリンクし、共有ライブラリ(DLL)を作成する必要があります。
Windows で Sylpheed のビルド環境を構築するには、Sylpheed/Win32 - ビルド方法 を参照してください。
Sylpheed のインクルードファイルはデフォルトで /usr/local/include/sylpheed/ 以下に、ライブラリファイルは /usr/local/lib/ 以下にインストールされます(MSYS 上で makewin32.sh でビルドした場合は $HOME/dist/include/sylpheed/, $HOME/dist/lib/ 以下になります)。
付属の test.c を例に挙げると、手動でコンパイル、インストールを行うには以下のようにします。
(Linux の場合)
$ gcc `pkg-config --cflags gtk+-2.0` -I/usr/local/include/sylpheed -I/usr/local/include/sylpheed/sylph -c test.c -o test.o
$ gcc -shared test.o `pkg-config --libs gtk+-2.0` -L/usr/local/lib -lsylpheed-plugin-0 -lsylph-0 -o test.so
$ sudo cp test.so /usr/local/lib/sylpheed/plugins/
(Windows の場合)
$ gcc `pkg-config --cflags gtk+-2.0` -I$HOME/dist/include/sylpheed -I$HOME/dist/include/sylpheed/sylph -c test.c -o test.o
$ gcc -shared test.o `pkg-config --libs gtk+-2.0` -L$HOME/dist/lib -lsylpheed-plugin-0 -lsylph-0 -o test.dll
$ cp test.dll $HOME/dist/lib/sylpheed/plugins/
自動化する場合は、 configure.ac, Makefile.am を作成し、 autoconf, automake, libtool を使用してビルドを行います。サンプルプラグインの Makefile.am などを参考にしてみてください。
plugin/test にサンプルプラグインがあります。このプラグインは make install ではインストールされません。インストールするには plugin/test ディレクトリに入って make install-plugin を実行してください。
test プラグインは Sylpheed プラグインの基本的な構造に加え、以下の処理を 行います。
- ロード時に標準出力に "test plug-in loaded!" という文字列を出力
- フォルダの一覧を取得し、標準出力に表示
- Sylpheed のバージョン文字列を取得し、標準出力に表示
- メインウィンドウを取得し、前面に出す
- 「ツール」メニューに「Plugin test」メニュー項目を追加
- 「Plugin test」メニューを選択すると、「Click this button」というボタンのみのウィンドウを表示し、クリックするとメッセージを出力
- アプリケーション初期化、終了、フォルダビューのコンテキストメニューポップアップ、メッセージ作成ウィンドウ作成、メッセージ作成ウィンドウ破棄のイベントを捕捉してメッセージを表示
/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
* Copyright (C) 1999-2010 Hiroyuki Yamamoto
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <glib.h>
#include <gtk/gtk.h>
#include "sylmain.h"
#include "plugin.h"
#include "test.h" /* test.h は空 */
#include "folder.h"
static SylPluginInfo info = {
"Test Plugin",
"1.0.0",
"Hiroyuki Yamamoto",
"Test plug-in for Sylpheed plug-in system"
};
static void init_done_cb(GObject *obj, gpointer data);
static void app_exit_cb(GObject *obj, gpointer data);
static void menu_popup_cb(GObject *obj, GtkItemFactory *ifactory,
gpointer data);
static void compose_created_cb(GObject *obj, gpointer compose);
static void compose_destroy_cb(GObject *obj, gpointer compose);
static void create_window(void);
void plugin_load(void)
{
GList *list, *cur;
const gchar *ver;
gpointer mainwin;
g_print("test plug-in loaded!\n");
list = folder_get_list();
g_print("folder list = %p\n", list);
for (cur = list; cur != NULL; cur = cur->next) {
Folder *folder = FOLDER(cur->data);
gchar *id = folder_get_identifier(folder);
g_print("folder id = %s\n", id);
}
ver = syl_plugin_get_prog_version();
g_print("program ver: %s\n", ver);
mainwin = syl_plugin_main_window_get();
g_print("mainwin: %p\n", mainwin);
syl_plugin_main_window_popup(mainwin);
syl_plugin_add_menuitem("/Tools", NULL, NULL, NULL);
syl_plugin_add_menuitem("/Tools", "Plugin test", create_window, NULL);
g_signal_connect(syl_app_get(), "init-done", G_CALLBACK(init_done_cb),
NULL);
g_signal_connect(syl_app_get(), "app-exit", G_CALLBACK(app_exit_cb),
NULL);
syl_plugin_signal_connect("folderview-menu-popup",
G_CALLBACK(menu_popup_cb), NULL);
syl_plugin_signal_connect("compose-created",
G_CALLBACK(compose_created_cb), NULL);
syl_plugin_signal_connect("compose-destroy",
G_CALLBACK(compose_destroy_cb), NULL);
g_print("test plug-in loading done\n");
}
void plugin_unload(void)
{
g_print("test plug-in unloaded!\n");
}
SylPluginInfo *plugin_info(void)
{
return &info;
}
gint plugin_interface_version(void)
{
return SYL_PLUGIN_INTERFACE_VERSION;
}
static void init_done_cb(GObject *obj, gpointer data)
{
g_print("test: %p: app init done\n", obj);
}
static void app_exit_cb(GObject *obj, gpointer data)
{
g_print("test: %p: app will exit\n", obj);
}
static void menu_popup_cb(GObject *obj, GtkItemFactory *ifactory,
gpointer data)
{
g_print("test: %p: folderview menu popup\n", obj);
}
static void compose_created_cb(GObject *obj, gpointer compose)
{
gchar *text;
g_print("test: %p: compose created (%p)\n", obj, compose);
text = syl_plugin_compose_entry_get_text(compose, 0);
g_print("test: compose To: %s\n", text);
g_free(text);
#if 0
syl_plugin_compose_entry_set(compose, "test-plugin@test", 1);
syl_plugin_compose_entry_append(compose, "second@test", 1);
#endif
}
static void compose_destroy_cb(GObject *obj, gpointer compose)
{
g_print("test: %p: compose will be destroyed (%p)\n", obj, compose);
}
static void button_clicked(GtkWidget *widget, gpointer data)
{
g_print("button_clicked\n");
/* syl_plugin_app_will_exit(TRUE); */
}
static void create_window(void)
{
GtkWidget *window;
GtkWidget *button;
g_print("creating window\n");
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label("Click this button");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 200);
gtk_container_add(GTK_CONTAINER(window), button);
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(button_clicked), NULL);
gtk_widget_show_all(window);
}
Sylpheed 本体のライセンスは GPL であるため、 Sylpheed から動的に 読み込まれるプラグイン DLL は、 GPL の規定に基づき、 GPL または GPL と互換性のあるライセンス(修正 BSD ライセンスなど)である必要が あります。
プラグインに商用ライセンスなど他のライセンスを適用したい場合は、 そのモジュールを独立した実行ファイルにして、 DLL とプロセス間通信で 連携して動作させる必要があります。