wxWidgetsとイベントループ


どうも、イベントループを置換するための汎用的な手法は2.9以降にしか無いようだ。。
もっとも、GUI版はコンパイル済バイナリの配布が基本になるので大きな問題では無いと判断。(パッケージとしては2.8しか提供していない)Ubuntuのために、Gtkのためのイベントループは自前で持つかもしれない。WindowsMac OS Xでは2.9を使う。
wxWidgetsのイベントループを置換するには、

  • 自前のAppTraitsを提供する
  • 自前のEventLoopを提供する

必要がある。(ただし2.9以降でしかこのテクニックは使用できない。)

class moshEventLoop : public wxGUIEventLoop {
public:
	moshEventLoop(wxEventLoopBase* loop){myloop = loop;};
	virtual int Run();
	wxEventLoopBase* myloop;
};

class skyTraits : public wxGUIAppTraits {
public:
	skyTraits() : wxGUIAppTraits(){}; // TODO
	wxEventLoopBase* CreateEventLoop(){
		me = new moshEventLoop(wxGUIAppTraits::CreateEventLoop());
		return me;
	};

	moshEventLoop* me;
};

class skyMosh : public wxApp {
public: 
	virtual bool OnInit();
	virtual wxAppTraits* CreateTraits(){
		return &S;
	};
	skyTraits S;
};

あとは、イベントを処理したいタイミングで、EventLoopから継承したDispatch()を呼べば良い。ここでは、Scheme手続き%wx_dispatchを呼ぶとDispatchされるようにした。

Object
stub_wx_dispatch(VM* theVM,int argc,const Object* argv){
	DeclareProcedureName("%wx_dispatch");
	checkArgumentLength(0);
	myapp->S.me->Dispatch(); // Dispatch event
	return Object::True;
}

nmoshはreplから簡単にバックエンド手続きを呼べるので、このような定義をmosh本体に追加したら、REPLから、

(import (primitives %wx_frame_new %wx_frame_show %wx_dispatch))
(define p (%wx_frame_new))
(%wx_frame_show p)
(define (q) (%wx_dispatch) (q))
(q)

のような式を入力することで直ぐに試すことができる。便利。
このままだと、例えば%wx_frame_show手続きに関係の無いオブジェクトのポインタやNULLポインタを渡すと単純にクラッシュしてしまう。今回の実装では、可能な限りC++コードを減らすため、型チェック等もScheme側で行うことにする。