fltk

fltk 1.1.8rc2を試してみた。
別スレッドで何か処理をして、その結果をGUIのスレッドで表示するというのがよくあるが、1.xだとFl::awake()とFl::add_fd()の二通りでできる。
まずawakeでやってみた。こちらはコールバック関数を直接指定することができ、コールバック関数がGUIスレッドのキューに入って、アイドル時にGUIのスレッドで呼び出されるようになっている。

2.x系だとFltk::awakeはメッセージだけを渡すようになっている。処理スレッドでGUIをlock()してから変更を加え、awake()を呼び出すと(スレッドメッセージが通知されて)必要なら再描画してくれるという感じぽい。

LinuxEUCで試したのだが、fltk本体にパッチを当てないと(XLoadQueryFont/XDrawStringの代わりにXCreateFontSet/XmbDrawStringにするとか)日本語が表示できない。ソースのコメントに

  • XRender extensionありのXFree86 4
  • FreeType2サポートありのlibXft

だと直接UTF-8が描画できるとなっているんだけど試してない。
Linuxの日本語周りはちんぷんかんぷん。SetWindowText("ああああ")とかで日本語が表示できるWindowsはえらい。

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <locale.h>

#ifdef WIN32
#include <Windows.h>
#include <Process.h>
#pragma comment(lib, "ws2_32")
#pragma comment(lib, "comctl32")
inline static bool create_thread( void *(*function)(Fl_Window *), void *param )
{
    return _beginthread( (void (*)(void *))function, 0, param ) != (UINT_PTR)-1;
}
#else
#include <unistd.h>
#include <pthread.h>
#define Sleep(ms) usleep((ms) * 1000)
inline static bool create_thread( void *(*function)(Fl_Window *), void *param )
{
    pthread_t network_thread;
    return pthread_create( &network_thread, NULL, (void *(*)(void *))function, param ) == 0;
}
#endif

// GUIのスレッドから呼び出されるコールバック
static void MainThreadCallback( Fl_Window *parent )
{
    Fl_Widget *button = parent->child( 0 );
#ifdef WIN32
    LOGFONT LogFont;
    GetObject( ::GetStockObject( DEFAULT_GUI_FONT ), sizeof LogFont, &LogFont );
    Fl::set_font( FL_FREE_FONT, LogFont.lfFaceName );
#else
    Fl::set_font( FL_FREE_FONT, "-misc-vl pgothic-medium-*-*--*-*-*-*-*-*-*-0" );
#endif
    button->labelfont(FL_FREE_FONT);
    button->label( "こんにちは世界!" );
}

// 別スレッド
static void *WorkerThread( Fl_Window *parent )
{
    Sleep( 2000 );
    Fl::awake( (Fl_Awake_Handler)MainThreadCallback, parent );
    return NULL;
}

int main(int argc, char* argv[])
{
    setlocale( LC_ALL, "" );

    // ウィンドウとボタンの作成
    Fl_Window window(320, 120);
    Fl_Button button( 20,20, 280, 60, "Hello, world!");
    window.show(argc, argv);

    // 別スレッドの作成
    if ( !create_thread( WorkerThread, &window ) ) return 1;

    // メッセージループ
    Fl::lock();
    return Fl::run();
}

GUIのコードがものすごく少ない。この単純さは素晴らしい。