このページは、Software Design 2010年8月号の第2特集 「達人に学ぶきれいなコードを書くための17の約束」の サンプルプログラムのダウンロードページです。
このサンプルプログラムはWindows上で動作します。 他のプラットフォームの方にも ヘッダファイルの作り方等で参考になる可能性はあります。
詳細はSoftware Design 8月号の方を見ていただきたいのですが、 このサンプルプログラムが提供しているのは、 ウィンドウに対し、実数のユーザ座標系による描画を可能にするライブラリです。
ライブラリだけでは何ですので、最低限のテストドライバも付けています。
私のところでは、Windows上のCコンパイラとしてMinGWを使用しています。 Visual Studio等でも動作すると思いますが、ビルドの方法等については ここでは詳述しません。提供しているのは所詮小規模なライブラリなので、 自由にビルドしていただければよろしいかと。
MinGWの公式ページはこちらです。
上記リンクからからダウンロードできるフォルダの内容物は 下記のとおりです。
DRW │ DRW.h │ DRW_windows.h │ main.c │ main_win.c │ Makefile └─DRW create_window.c draw.c drw_pri.h mainloop.c Makefile
まず、下の階層のDRWでmake(私の場合、MinGWでデフォルトで インストールされる「mingw32-make.exe」を「gmake.exe」 にリネームして使用しています)を実行すると、「drw.o」が 生成されます。
上層のDRWでmakeを実行すると、 「drwtest.exe」という実行可能プログラムが生成されます。
テスト用のメインルーチンはmain.cとmain_win.cの2種類が存在します。 main.cはDRWの機能を使用してウインドウを開くもの、 main_win.cは自力でウインドウを作ってからそれを DRW_Windowに割り当てるものです。 Makefileを書き換えて作り変えてください。
記事中にも説明はありますが、 Windowsのようなイベントドリブンのプログラムに固有の話については 本筋から外れるので説明を省いています。 ここではそれについて簡単に説明します。
DRWの機能を使用して新規ウインドウを開く場合、以下のように記述します (DRW.zipに添付されたテストドライバからの抜粋です)。
DRW_Window *win; /* 800×600ピクセルのウインドウを作る */ win = DRW_create_window(800, 600, -1, -1, 1, 1); /* ウインドウに対し、再描画用のコールバックルーチンを指定する */ DRW_set_redraw_callback(win, redraw_proc); /* メインループに入る */ DRW_main_loop();
イベントドリブンのプログラミングでは、通常、プログラムは最終的に 「メインループ」に入ります。DRWの場合、DRW_main_loop()関数が メインループの関数です。 DRWの目的はウインドウにいろいろな図形を描画することなのですが、 そのためには、描画の関数をDRW_set_redraw_callback()関数により 事前登録することになります。
DRWをこの使用法で利用する限り、DRWの利用者は、 Windowsが提供する各種関数を呼び出す必要がありません (言い換えれば、windows.hを#includeする必要がありません)。
とはいえ場合によってはWindowsの機能を直接使って作成したウインドウに DRWの機能を使用して描画したい場合もあるでしょう。 そのような場合は、windows.hとDRW_windows.hを#includeしたうえで、 以下のように書きます。
(前略) /* Windowsの機能でウインドウを開く */ hwnd = CreateWindow(TEXT("TestWindowClass"), TEXT("DRW Test Driver"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL); if (hwnd == NULL) { return -1; } /* Windowsの機能で作成したウインドウとDRW_Windowを対応付ける */ g_window = DRW_set_canvas(hwnd, -1, -1, 1, 1); DRW_set_redraw_callback(g_window, redraw_proc); /* メインループは自分で書く */ while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); }
上記のようにした上で、ウインドウプロシージャを以下のように書きます。 まず、メッセージをDRWに引き渡し、 処理されなかったメッセージについてのみ自力で処理をする、 という流れになります。
LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) { if (DRW_window_procedure(g_window, msg, wp, lp) == DRW_MESSAGE_IGNORED) { switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; } } return DefWindowProc(hwnd , msg , wp , lp); }
ついでに、再描画ルーチンの例も貼っておきます。
void redraw_proc(DRW_Window *window) { int i; double angle; double radius; DRW_Point points[1000]; DRW_draw_line(window, 0, 0, 0.5, 0.5); angle = 0; radius = 0; for (i = 0; i < 1000; i++) { points[i].x = cos(angle) * radius; points[i].y = sin(angle) * radius; angle += 0.1; radius += 0.001; } DRW_draw_polyline(window, 1000, points); for (i = 0; i < 10; i++) { DRW_draw_circle(window, 0, 0, i * 0.1); } DRW_draw_rect(window, 0, 0, -0.5, 0.5); DRW_fill_circle(window, 0.5, 0.5, 0.5); DRW_fill_rect(window, 0, 0, 0.5, -0.5); }
これの実行結果は以下のようになります。
ご意見、ご質問、不具合連絡等は掲示板にお願いいたします。