enfle plugin interface version 1 specification (draft) (C)Copyright 1999 by Hiroshi Takekawa Last Modified: Fri Apr 21 21:33:50 2000. $Id: Plugin.txt,v 1.18 2000/04/22 09:38:43 sian Exp $ Sorry, this document is available in Japanese only. この文書は、enfleのpluginの書き方を解説した文書です。使用するだけの場 合には読む必要はありません。自分で新たにプラグインを作る場合にお読みく ださい。チュートリアルっぽく書きつつ、仕様を記述しようと思います。ただ し敢えて詳述を避けた部分もあります。特にアニメーション関連は書いてあり ません。 なお、まだ草稿の段階ですので、不備、変更等が数多くあると思います。プラ グインのソースそのものも参考にされるとよいかと思います。 プラグインには現在3種類存在します。 ローダ(画像読みこんでをenfle内部形式に変換する) セーバ(enfle内部形式の画像を保存する) アーカイバ(アーカイブをとりあつかう) どちらにも共通した手続きがあります。 int get_plugininfo(PluginInfo *); という関数を用意して、その中で、その プラグインの情報を設定し、そのプラグインを使用可能にする場合には1を返 し、そうでない場合は0を返します。そのため、多くのプラグインは次のよう なコードで始まります。 -- /* この2つは必須 */ #include "enfle.h" #include "plugin.h" /* * この中でjpeg_load_image()のプロトタイプ宣言と * FORMAT_NAMEの定義をしている */ #include "jpeg.h" /* * プラグインとしてコンパイルされる場合、 * libtoolを使っているので、enfleのツリーの中だと-DPICされる */ #ifdef PIC int get_plugininfo(PluginInfo *p) #else int loader_jpeg_get_plugininfo(PluginInfo *p) #endif { /* versionの設定。1のみ */ p->version = 1; /* pluginの種類の設定。_Loader、_Saver、_Archiverのいずれか */ p->type = _Loader; /* pluginの正式名 */ p->pluginname = "JPEG Format Loader Plugin version 0.1"; /* pluginの略称 */ p->pluginshortname = FORMAT_NAME; /* pluginの作者 */ p->author = "Hiroshi Takekawa"; /* * 後でunloadできるようにするためのもの * plugin_load()という関数の方が設定するのでNULLでもなんでもよい */ p->dlhandle = NULL; /* set by plugin_load */ /* ここは種類によって違う。以下はローダの場合 */ p->functions.loader.load_image = jpeg_load_image; return 1; } -- enfleはpluginの検索pathからファイル名が.so(.数字.数字)で終わるものを探 し、そのファイルをdlopen()します。その後、get_plugininfo()というエント リを探し、メモリ確保済のPluginInfo構造体へのポインタを渡し、1が返って きたら、そのプラグインを登録します。この例では必ず登録するために0は返 してません。 # いままで書いたpluginで0を返した例はまだありません…。 loader_jpeg_get_plugininfo()というのはプラグインをEnfle本体に組みこむ 時に必要なものです。 実際の機能の実装は、p->functions〜で差した先の関数となります。この場合、 int jpeg_load_image(Info *, Image *); が呼びだされます。ローダは、 loader.load_image、アーカイバはarchiver.openに指定します。 -- int jpeg_load_image(Info *info, Image *p) { if (!jpeg_decode_image(info, p)) return info->format == NULL ? PROC_NOT : PROC_ERROR; info->npics = 1; /* set handler */ info->handler.destroy = NULL; info->handler.alarmset = NULL; info->handler.alarm = NULL; info->handler.alarmoff = NULL; return PROC_OK; } -- loader.load_image は、PROC_OK(正常終了),PROC_NOT(フォーマットが違う), PROC_ERROR(フォーマットは正しいがエラーが発生した)のいずれかを返します。 実際の画像データ等は、Info構造体や、Image構造体にセットします。 Info,Imageともにメモリは確保済です。もちろんImage構造体のimageメンバに よって指されるイメージ本体のメモリはpluginが確保する必要があります。ロー ダは渡されたArchive構造体を使ってarchive layer経由でファイルにアクセス してロードします。 以下にInfo構造体の中身を示します。Enfle本体が設定するものにはe、loader が設定するものにはlをつけてあります。archiverにはInfo構造体、Image構造 体はわたされず、かわりにArchive構造体が渡されます。 -- typedef struct _info Info; struct _info { char *filename; /* e: 現在処理中のファイル名 */ Archive *ar; /* e: アーカイブ情報構造体(openの引数) */ Dlist *pluginlist; /* e: 登録されているプラグインのリスト */ char *format; /* l: 画像のフォーマット名 */ Handler handler; /* l: ハンドラ */ Display *disp; /* e: XのDisplay ID */ Visual *visual; /* e: XのVisual ID */ Window root, win, win_save; /* e: XのWindow ID */ GC gc; /* e: XのGC ID */ Cursor normal_cursor, wait_cursor; /* e: XのCursor */ unsigned char *comment; /* l: 画像のコメント */ Image *image, *top_image, *shown_image; /* e: 画像データ構造体 */ unsigned char *xdata; /* e: レンダリングバッファ */ int xdata_size; /* e: レンダリングバッファサイズ */ int if_animated, if_quantize; /* 順に e: アニメーションするかどうか、減色するかどうか */ int xwidth, xheight; /* e: xdata's width, height */ int mswidth, msheight; /* e: magnified screen's width, height */ int swidth, sheight; /* e: screen's width, height */ int depth; /* e: Xのdepth */ int npics; /* l: ひとつのファイルに何枚の絵があるか */ int index; /* e: いま何番目の絵を見ているか */ Transparent transparent_disposal; /* e: 透過色の処理 */ Rerender_method rerender; /* l: 再表示方法指定(アニメーション用、普通は設定しなくてよい) */ double delay_factor; /* e: アニメーションの時間のスケール */ }; -- とこのようにかなりごちゃごちゃしていますが、これはグローバル変数を避け たためです。プラグインが設定しなければならない項目はそんなにありません。 char *format; /* l: 画像のフォーマット名 */ "JPEG"などの文字列をいれます。pluginshortnameに設定する値を同じにする とよいでしょう。 Handler handler; /* l: ハンドラ */ ハンドラを設定します。普通は全部NULLです。destroyは画像を破壊する時に 呼ばれます。alarmset、alarm、alarmoffはアニメーションなどのために用意 されたものです。使われないので詳述はしません。 unsigned char *comment; /* l: 画像のコメント */ コメントがあればいれます。コメントがない場合はNULLのままにしておいてく ださい。NULL以外の場合はそこをfree()します。destroyハンドラが面倒を見 る必要はありません。 int npics; /* l: ひとつのファイルに何枚の絵があるか */ 一つのファイルにあった画像の数をいれます。アニメーション以外では1です。 Rerender_method rerender; /* l: 再表示方法(アニメーション用、普通は設定しなくてよい) */ alarmで呼ばれた関数が指定します。設定しなくていいです。 ローダがそのほとんどを設定しなければならないのはImage構造体の方です。 以下にその内容を示します。 -- typedef struct _image Image; struct _image { int left, top; int width, height; int ncolors; Transparent transparent_disposal; Disposal image_disposal; Color transparent; Color background; int delay; unsigned char colormap[256][3]; int image_size; int bytes_per_line; ImageType type; unsigned char *image; #ifdef SHAPE unsigned char *mask; int mask_size; Pixmap pixmask; #endif Image *next; }; -- ひとつひとつ説明していきます。 int left, top; 画像の表示オフセットを設定します。なければ(0, 0)でいいです。 int width, height; 画像のサイズを設定します。 int ncolors; 画像の色数を設定します。わからない(あるいは面倒な)時はそのフォーマット での最大数(fullcolorの時は1 << 24など)に決めうちしてもかまいません。減 色が必要かどうかなどの判別に使われます。 Transparent transparent_disposal; 透過処理の処理方法を設定します。なにもしない場合は_DONOTHING、透過処理 する場合は、_TRANSPARENT、ShapeExtensionが使える場合は_SHAPEで透過表示 に設定します。Info構造体のtransparent_disposalがoptionでユーザによって 指定された値です。 Disposal image_disposal; 表示後の画像の処理を指定します。アニメーションで使用されます。普通使わ ないので_NOTHING_DISPOSALのままでいいです。 Color transparent; Color background; 透過色、背景色を設定します。 indexed colorの場合 transparent.index = 255; background.index = 0; RGBの場合 transparent.red = 0; transparent.green = 0; transparent.blue = 0; background.red = 255; background.green = 255; background.blue = 255; int delay; 次の絵を表示するまでの待ち時間を設定します。アニメーションにしか使いま せん。 unsigned char colormap[256][3]; パレットを設定します。0から255までRGBの順にならんでいます。 int image_size; imageの指した先のメモリのサイズを設定します。 int bytes_per_line; 1ラインが何bytesになるかを設定します。 ImageType type; 画像のメモリ上での形式を表します。pixelは左から右、上から下に格納され ます。 _MONO: 2値画像。左から右にMSBからつめる _GRAY: グレースケール。256段階の濃淡であらわす _INDEX: indexed color。パレットを使用 _RGB16: 16bit RGB。MSBからRRRRRGGG GGGBBBBBのフォーマット _RGB16のbyte orderのendiannessはマシンに依存してかまいません。 _RGB24: 24bit RGB。RGBの順。 unsigned char *image; 肝心の画像データへのポインタ。pluginは必要なメモリを確保しなければなら ない。表示された画像の解放はEnfleが面倒をみるが、ロード中にエラーなど で中止する場合は、pluginが解放してPROC_ERRORを返さなければなりません。 メモリリークにくれぐれも注意してください。 unsigned char *mask; int mask_size; Pixmap pixmask; ShapeExtensionが有効の時に使用。maskはMSBFirstなbitmapデータ。 mask_sizeはmaskが指すメモリの大きさ。pixmaskは設定しなくてよい。 Image *next; 次の画像へのポインタ。アニメーションをする時に使用。普通はNULLのままで よい。 次にセーバの説明をします。セーバはInfo構造体とImage構造体、出力ファイ ル名を受けとって、ファイルを書きだします。plugininfoは functions.saver.save_imageとなる他はローダと一緒です。 次にアーカイバの説明をします。アーカイバは、ファイルが開かれる時に archiver.openに設定した関数int open(Archive *); が呼ばれます。 open()はArchive構造体を受けとり、ar->filenameで示されるファイルが対応 しているフォーマットであるかを調べ、対応していればmethodを設定して1、 してなければ0を返します。0を返す場合には開いたファイルをcloseしてくだ さい。また0を返す時、(mallocなどで)infoに値を入れていたら、(freeした上 で)NULLに戻すようにしてください。必要に応じてArchive構造体に値を設定し ます。以下にArchive構造体の中身を示します。 -- typedef struct _archive { char *format; char *filename; char ifname[16]; /* stands for internal filename */ FILE *fp; int nfiles; int asize; /* stands for archive filesize */ int index; int offset; int size; /* unique plugin info */ void *info; METHOD(int, select, (struct _archive *, int)); METHOD(int, seek , (struct _archive *, long, int)); METHOD(int, tell , (struct _archive *)); METHOD(int, read , (struct _archive *, unsigned char *, int)); METHOD(int, close , (struct _archive *)); } Archive; -- char *format; フォーマット名。おなじくpluginshortnameと一緒にしておくとよいでしょう。 char *filename; アーカイブそのもののファイル名がはいっています。書きかえてはいけません。 char ifname[16]; /* stands for internal filename */ アーカイブの中身につけられたファイル名を設定します。そのうちポインタに 置きかえられるかもしれません。 FILE *fp; アーカイブファイルのFILE構造体へのポインタをいれます。 int nfiles; アーカイブの中にいくつファイルがあるかを設定します。 int asize; /* stands for archive filesize */ アーカイブファイル全体のサイズをいれます。 int index; 現在アーカイブ中のどのファイルを開いているかを示します。 int offset; アーカイブ中のどの部分から、現在開かれているファイルがはじまるかを設定 します。 int size; 現在開かれているファイルのサイズを設定します。 void *info; プラグイン特有のデータを保持します。たとえばファイルテーブルなどを持ち たい場合などに利用します。メモリを確保した場合にはcloseする時に解放し なければなりません。 METHOD(int, select, (struct _archive *, int)); METHOD(int, seek , (struct _archive *, long, int)); METHOD(int, tell , (struct _archive *)); METHOD(int, read , (struct _archive *, unsigned char *, int)); METHOD(int, close , (struct _archive *)); methodを指定します。必要に応じてopen()が設定しなければなりません。デフォ ルトの動作でいい場合はNULLを設定します。以下でそれぞれの動作の説明をし ます。これらはarchive_(select|seek|tell|read|close)という関数名でロー ダから呼びだされます。 int select(Archive *ar, int index); アーカイブ内のファイルを選択して開きます。成功したら1、失敗したら0を返 します。select()はar->sizeとar->offsetを設定しなければなりません。デフォ ルトは、ar->indexにindexの値を設定するだけです。 int seek(Archive *ar, long offset, int whence); seekします。アーカイブ内のファイルに対するseekで普通のfseek()とは違い ます。これも成功したら0、失敗したら-1を返します。デフォルトはar->offset を基準としたseekとなります。 int tell(Archive *ar); ファイル内での現在の場所を返します。デフォルトはftell() - ar->offsetの 値を返します。 int read(Archive *ar, unsigned char *buf, int size); bufにsize bytesだけファイルから読みこみ、実際に読みこんだbyte数を返し ます。デフォルトはreturn fread(buf, 1, size, ar->fp);します。 int close(Archive *ar); アーカイブ自体を閉じて、Archive構造体を破棄します。この時、ar->infoに 確保したメモリを解放しなければなりません。デフォルトではファイルをただ 閉じてArchive構造体を破棄します。 最後にpluginをEnfleのツリーの中に組みこむ方法を書いておきます。 まず、plugins/(loader|saver|archiver)/NAME/ というdirectoryをつくります。 本体は、NAME/NAME.c というファイルにしてください。次にMakefile.amを作ります。 -- lib_LTLIBRARIES = NAME.la NAME_la_LDFLAGS = -module libdir = @Plugins_Loader_dir@ # If needed # LIBS = -ljpeg NAME_la_SOURCES = \ NAME.c NAME.h etc.... -- 後はmake distcleanをしてautogen.shでMakefile.inを作り、configureしなお せばcompileできると思います。 完成したプラグインは是非作者へcontributeしてください。