2008-02-22

C による Tcl/Tk 拡張 : カスタマイズした wish (1)


日 koji さんから、Linux 上の MinGW クロスコンパイラ環境を利用した、C による Tcl/Tk 拡張に関するご質問を、コメントとして頂きました【コメント】。いい機会ですので、C による Tcl/Tk の拡張の話題を、クロスコンパイルする話題に絡めて、しばらく取り上げていきたいと思います。
最初の話題として、koji さんがコメント欄に掲載したサンプルをベースに、Linux における Tcl/Tk の拡張例を紹介したいと思います。

koji さんが掲載したサンプルは、Tcl のスクリプトを wish に埋め込んだ wish アプリケーションです。Tcl/Tk では、標準で wish (windowing shell) という Tcl のコマンドシェルが利用できますが、Tcl/Tk の C API を利用して、標準の wish とは別に、カスタマイズした wish を作成できます。
カスタマイズできることは、koji さんが示したサンプルのように、スクリプトを埋め込んで、wish アプリケーションを即起動できるようにしたり、あるいは Tcl のコマンドを追加、あるいは拡張して、標準の wish にない機能を備えた拡張 wish をビルドすることです。

ここでは koji さんのサンプルコードをベースにして、kwish という名前の、スクリプトが埋め込まれた拡張 wish をビルドしてみましょう。

コンパイル環境


コンパイル環境は、以下の通り Fedora 8 に収録されている Tcl/Tk のパッケージを使用しています。なお、一般的な gcc の開発環境が整っていることを前提にしています。

Fedora 8 (i386)
tcl-8.4.17-1.fc8
tcl-devel-8.4.17-1.fc8
tk-8.4.17-2.fc8
tk-devel-8.4.17-2.fc8

プログラム


まず main 関数を含んだソース kwish_main.c を示します。このプログラムは、Tcl/Tk のインタープリタを初期化し、Tcl のスクリプトを処理する関数 app_custom を呼び出したあとに、Tk_MainLoop を実行して GUI のためのイベントループ状態に入ります。

/* kwish_main.c */
#include "kwish.h"

int
main (int argc, char *argv[])
{
Tcl_Interp *interp;

interp = Tcl_CreateInterp ();
Tcl_FindExecutable (argv[0]);

if (Tcl_Init (interp) != TCL_OK)
{
fprintf (stderr, "FATAL: Tcl initialization error: %s\n",
Tcl_GetStringResult (interp));
return EXIT_FAILURE;
}

if (Tk_Init (interp) != TCL_OK)
{
fprintf (stderr, "FATAL: Tk initialization error: %s\n",
Tcl_GetStringResult (interp));
return EXIT_FAILURE;
}

if (app_custom (interp) != TCL_OK)
{
printf ("%s\n", "Program End with script error!");
return EXIT_FAILURE;
}

Tk_MainLoop ();

return EXIT_SUCCESS;
}
/* kwish_main.c */

次の kwish_sub.c では、kwish を起動したときに実行するスクリプトを処理する関数 app_custom を記述しています。koji さんのサンプルでは、定義したスクリプトを UTF-8 にエンコードしています。これはスクリプトに日本語文字など ASCII 以外の文字を含み、かつ、(MS Windows のように)システムのエンコーディングが UTF-8 と異なる場合に役に立ちます。今回使用するスクリプトでは関係がありませんが、そのまま残しました。

/* kwish_sub.c */
#include "kwish.h"

int
app_custom (Tcl_Interp * interp)
{
Tcl_DString ds;
char script[] = "\n\
wm title . {kwish}\n\
label .1lbl -text {Enter your name (and click OK):}\n\
entry .ent2\n\
button .cmd3 -text OK -command ok_clicked\n\
button .cmd4 -text {Exit this program} -command exit\n\
pack .1lbl -anc w\n\
pack .ent2 -fill x\n\
pack .cmd3 -fill x\n\
pack .cmd4 -fill x\n\
proc ok_clicked {} {\n\
tk_messageBox -message \"[.ent2 get], you are here!\"\n\
}\n\
";
char *script_utf = Tcl_ExternalToUtfDString (NULL, script, -1, &ds);

if (Tcl_Eval (interp, script_utf) != TCL_OK)
{
fprintf (stderr, "Error: %s\n", Tcl_GetStringResult (interp));
return TCL_ERROR;
}

Tcl_DStringFree (&ds);

return TCL_OK;
}
/* kwish_sub.c */

インクルードファイル kwish.h には、このプログラムで使用するインクルードファイルやプロトタイプ関数宣言をまとめました。

/* kwish.h */
#if !defined(KWISH_H)
#define KWISH_H

#include <stdio.h>
#include <stdlib.h>
#include <tcl.h>
#include <tk.h>

extern int app_custom (Tcl_Interp *);

#endif /* KWISH_H */
/* kwish.h */

最後に Makefile を示します。リンクでは意図的に、-ltcl -ltk として、Tcl/Tk のバージョンに依存しないようにしました。そのため、tcl-develtk-devel パッケージがビルド時に必要になります。
【注意】本ブログ上では、Makefile におけるコマンド行の先頭部分のタブが表現できないので、コピーして利用する場合はタブ文字に変更してください。

# Makefile

CC = gcc
RM = rm -f

TARGET = kwish

OBJEXT = .o
TARGETEXT =

DEFS =
INCLUDE = -I/usr/include
CFLAGS = -Wall -O2 $(DEFS) $(INCLUDE)
LDFLAGS = -L/usr/lib
TK_LIBS = -ltk -ltcl
LIBS = $(TK_LIBS) -lX11 -lm

OBJS = \
kwish_main$(OBJEXT) \
kwish_sub$(OBJEXT)

all: kwish$(TARGETEXT)

kwish$(TARGETEXT): $(OBJS)
$(CC) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)

.c.$(OBJEXT):
$(CC) -c $(CFLAGS) $< -o $@

$(OBJS): kwish.h

clean:
$(RM) kwish$(TARGETEXT) $(OBJS) *~

# Makefile

ビルドと実行


以上のソースファイルと Makefile を、同じディレクトリ内に保存し、make コマンドで kwish をビルドします。以下に実行例を示しました。

$ make
gcc -Wall -O2 -I/usr/include -c -o kwish_main.o kwish_main.c
gcc -Wall -O2 -I/usr/include -c -o kwish_sub.o kwish_sub.c
gcc -o kwish kwish_main.o kwish_sub.o -L/usr/lib -ltk -ltcl -lX11 -lm
$ ./kwish


kwish の実行例

このように、簡単に wish にスクリプトを埋め込んだ、拡張 wish をビルドすることができます。次のステップとして、Linux 上の MinGW クロスコンパイル環境下で、同じソースファイルを使って、Winodws 用のバイナリをビルドしてみたいと思います。


wine を利用した Windows 版 kwish の実行例

参考資料


[1] Tcl Library
[2] Tk Library
 

2 件のコメント:

匿名 さんのコメント...

解説をありがとうございます。

できれば、windows用クロスコンパイルのMakefileも示していただけるとありがたいです。

bitWalk さんのコメント...

koji さん、コメントをありがとうございます。クロスコンパイル環境における Makefile については次回に説明する予定です。暫くお待ちください。