前のページ次のページ上に戻るホーム SophiaFramework UNIVERSE 5.2

3.1. SFY アプリ

SFY アプリは、 SFYApplication クラスを継承するアプリケーションクラスを利用します。 SFYApplication クラスでは、SFY GUI フレームワークを利用するための各種設定がデフォルトで実装されています。

この節では SFY アプリの起動から終了までの処理の流れを解説します。

サンプルコードはこちらにあります。

[Note] SFC アプリ

SFC アプリは、 SFCApplication クラスを継承するアプリケーションクラスを利用します。 SFCApplication クラスには、 GUI フレームワークを利用するための各種設定がデフォルトで実装されていません。

GUI フレームワークを利用しない場合は、このアプリケーションクラスを利用してアプリを作成します。

[Note] レスポンダとは

GUI コンポーネントのことを「レスポンダ」(SFYResponder)と呼びます。

3.1.1. SFY アプリの起動

SFY アプリの開発は、 ブートローダ(SFCApplet::Boot 関数) とファクトリ関数(helloworld::Factory 関数)の実装から始まります。

ブートローダ(SFCApplet::Boot 関数)は、 アプリで最初に実行される関数です。

ブートローダ(SFCApplet::Boot 関数)には、 ライセンスコードを設定し、 引数 id が AEECLSID_HELLOWORLD であるとき helloworld インスタンスを生成するファクトリ関数 (helloworld::Factory 関数)を返す処理を記述します。

次に実行されるのは、ファクトリ関数(helloworld::Factory 関数)です。

ファクトリ関数(helloworld::Factory 関数)には、 helloworld アプリケーションクラスのインスタンスを生成する処理を記述します。

[Tip] Tip

アプリケーションクラスのインスタンスは、 1 アプリにつき 1 つだけ生成します。

[Note] ClassID とライセンスコード

BREW 実機(BREW 移動機)では、 アプリの ClassID に対応した正規ライセンスコードを設定しなければアプリは起動しません。

開発時は Example / Tutorial フォルダ内にあるサンプルアプリの ClassID とライセンスコードをテスト用に使用してください

BREW シミュレータでは、ライセンスコードを設定しなくてもアプリは起動します。

参照: ライセンスコード

例 3.1. ブートローダとファクトリ関数

// 
//  helloworld.cpp
//

#include "helloworld.hpp"

// ブートローダ
SFCApplet::FactorySPP SFCApplet::Boot(AEECLSID id, SFXAnsiStringPtr license)
{
    *license = "heap://"
               "TIXDRQXNU5WHU8Y3Z9WOHWQR6Z3VPSDHDV5CR1S4XASPWLUHWAS7Z5Z2TGS3XMSAT3UPUQTLTARCYPSF"
               "UEJZ6ROSJWGUQSEYKR6V2U4VESMTQLHKZ6X7Y2VKXHWIX3XBU0Z7VHWHXIZBSGT5SPU3XLX0Z1Y4R3TC"
               "U6WGT9WHWIVNYHYCUCR9T3SMTEWPRNVAX1Y4VPW2YCY9YQV5R7Z9UIVHT6SDUPU2SIW6VCRCWBR2S4WQ"
               "UPYFWCYGT4VIT1WHXGYPTQSFYPWNV3ULRNWFW7RBRFVKUKS2YQSQYHW1TPUPXBZ6UEY2WOYKR7S3TAU4"
               "TQS6UHVFVEVLU3R5SDSKW7RPTNTPVQU2T4R8Z4VLUGEW3U98TLDR8/";

    return (id == AEECLSID_HELLOWORLD) ? (&helloworld::Factory) : (null);
}

// ファクトリ関数
SFCInvokerPtr helloworld::Factory(Void)
{
    return ::new helloworld; // helloworld アプリケーションクラスのインスタンスを生成する
}

3.1.2. アプリケーションクラスの定義

helloworld アプリケーションクラスは、 SFYApplication クラスを継承します。

helloworld アプリケーションクラスの定義の中で、 XANDLER_DECLARE_VOIDRENDER(OnRenderRequest)は描画ハンドラ、 XANDLER_DECLARE_BOOLEVENT(OnKey)はキーハンドラを宣言しています。

[Note] ハンドラとは...

ハンドラとは、 レスポンダがイベントを受信したときに起動する関数のことです。

ハンドラには、 描画イベントを受信する描画ハンドラキーイベント[SFEVT_KEY]を受信するキーハンドラなどがあります。

参照: ハンドラ | ハンドラ一覧 | イベント | イベント一覧

例 3.2. helloworld アプリケーションクラスの定義

////  helloworld.hpp//

#define __HELLOWORLD_HPP

#include <SophiaFramework.hpp>
#include "helloworld.bid"

//
//  helloworld アプリケーションクラスの定義
//

SFMTYPEDEFCLASS(helloworld)  //  便利な型を生成するマクロ
class helloworld : public SFYApplication //  helloworld アプリケーションクラスは SFYApplication クラスを継承する
{
    SFMSEALCOPY(helloworld)  // インスタンスのコピーを禁止するマクロ
public:
    static SFCInvokerPtr Factory(Void);
private:
    explicit helloworld(Void) static_throws;
    virtual ~helloworld(Void);
    XANDLER_DECLARE_VOIDRENDER(OnRenderRequest)  // 描画ハンドラ
    XANDLER_DECLARE_BOOLEVENT(OnKey)             // キーハンドラ
};

#endif // __HELLOWORLD_HPP //
[Caution] 大文字アルファベットを含むアプリケーションクラス名(BREW 4.0 から)

通常、helloworld などのアプリケーションクラスの名前はアプリ本体と同じ名前にします。

BREW 4.0 からアプリの名前に大文字アルファベットが使えなくなりました。

HelloWorld や USRApplication などアプリケーションクラスの名前に大文字アルファベットを使う場合、 大文字アルファベットを小文字アルファベットに変換する必要があります。

[Tip] SFMTYPEDEFCLASS マクロ

SFMTYPEDEFCLASS は、 引数に指定したクラスに関する便利なユーザー定義型を自動生成するマクロです。

[Tip] SFMSEALCOPY マクロ

SFMSEALCOPY は、 インスタンスのコピーを禁止するマクロです。 上の例の場合、helloworld インスタンスはコピーしてはいけません。

[Tip] XANDLER_DECLARE_VOIDRENDER マクロ

XANDLER_DECLARE_VOIDRENDER は、 描画ハンドラを宣言するためのマクロです。

[Tip] XANDLER_DECLARE_BOOLEVENT マクロ

XANDLER_DECLARE_BOOLEVENT は、 Bool 型を返すハンドラを宣言するための汎用マクロです。

3.1.3. コンストラクタの実装

helloworld アプリケーションクラスのコンストラクタでは、 SFYApplication::RegisterHandler 関数を使用して描画ハンドラとキーハンドラを登録します。 この操作により、 helloworld アプリケーションクラスが内部で保持するルートSFZRoot)が 描画イベントキーイベント[SFEVT_KEY]を受信すると、 登録した描画ハンドラやキーハンドラが呼び出されます。

SFYApplication::RegisterHandler 関数を使用してハンドラを登録するには、 SFXEventRange クラスを使用してこのハンドラが受信するイベントの範囲と、 XANDLER_INTERNAL マクロを使用してこのハンドラへのポインタを指定します。

下記の例では、描画イベントのハンドラとして OnRenderRequest 関数、 キーイベント[SFEVT_KEY]のハンドラとして OnKey 関数を登録しています。

OnRenderRequest 関数では、 (SFEVT_RESPONDER_RENDER, SFP16_RENDER_REQUEST)という描画イベントが指定されています。

OnKey 関数では、 (SFEVT_KEY, SFP16_BEGIN)から(SFEVT_KEY, SFP16_END)までの範囲のキーイベントが指定されています。

例 3.3. コンストラクタの実装(ハンドラの登録)

// コンストラクタ
helloworld::helloworld(Void) static_throws
{
    if (static_try()) {
        // 描画ハンドラを登録する
        static_throw(RegisterHandler(
            SFXEventRange(SFEVT_RESPONDER_RENDER, SFEVT_RESPONDER_RENDER, SFP16_RENDER_REQUEST, SFP16_RENDER_REQUEST),
            XANDLER_INTERNAL(OnRenderRequest)
        ));
        if (static_try()) {
            // キーハンドラを登録する
            static_throw(RegisterHandler(
                SFXEventRange(SFEVT_KEY, SFEVT_KEY, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnKey)
            ));
        }
    }

    // return;  ← NG(×) コンストラクタでは return 文を記述してはいけない
}
[Tip] SFYApplication::RegisterHandler 関数

SFYApplication::RegisterHandler 関数は、 アプリケーションクラスがデフォルトで保持するルートSFZRoot)にハンドラを登録するための関数です。

簡略化のため、 helloworld アプリではルートにハンドラを登録していますが、 一般にハンドラはレスポンダに登録します。

[Note] イベント

イベントSFXEvent)は、 イベントタイプと 2 つのパラメータ(P16 パラメータと P32 パラメータ)から構成されます。

イベントを処理するハンドラは、 SFYApplication::RegisterHandler または SFYResponder::RegisterHandler 関数を使用してレスポンダに登録します。

以下の 3 種類のイベントがあります。

  1. BREW SDK で定義されるBREW イベント
  2. SophiaFramework UNIVERSE 独自のレスポンダイベント
  3. ユーザー独自のユーザーイベント

BREW イベントのイベントタイプ名は、BREW SDK で定義されるイベントタイプ名の先頭に SF 接頭語が付加された名前になります。 SFEVT_KEY イベント(キーイベント[SFEVT_KEY])は BREW イベントです。

一方、レスポンダイベントのイベントタイプ名は、 SFEVT_RESPONDER_ 接頭語が付加された名前になります。 SFEVT_RESPONDER_RENDER イベント(描画イベント)は レスポンダイベントです。

[Note] イベントの表現

このリファレンスでは、状況に応じてイベントを以下の 3 通りの方法で表現しています。

  1. イベントタイプ名
  2. (イベントタイプ名, P16 パラメータ)
  3. (イベントタイプ名, P16 パラメータ, P32 パラメータ)、またはSFXEvent(イベントタイプ名, P16 パラメータ, P32 パラメータ)]

[Tip] SFXEventRange クラス

ハンドラは、 RegisterHandler 関数の引数で SFXEventRange を利用してイベントの範囲を指定してレスポンダに登録します。

(sEventType, sP16, *)から(eEventType, eP16, *)までの範囲のイベントは、

// (sEventType, sP16)から(sEventType, sP16)までの範囲のイベント
SFXEventRange(sEventType, eEventType, sP16, eP16)

と記述します。

例えば、(SFEVT_KEY, SFP16_BEGIN)から(SFEVT_KEY, SFP16_END) までのすべてのキーイベント[SFEVT_KEY]は、

// すべてのキーイベント
SFXEventRange(SFEVT_KEY, SFEVT_KEY, SFP16_BEGIN, SFP16_END)

と記述します。

イベントが(EventType, P16)の 1 つだけである場合は、

// (EventType, P16) イベント
SFXEventRange(EventType, EventType, P16, P16)

と記述します。

例えば、描画イベントは、

// (SFEVT_RESPONDER_RENDER, SFP16_RENDER_REQUEST)描画イベント
SFXEventRange(SFEVT_RESPONDER_RENDER, SFEVT_RESPONDER_RENDER, SFP16_RENDER_REQUEST, SFP16_RENDER_REQUEST)

と記述します。

[Tip] XANDLER_INTERNAL マクロ

内部のインスタンス(this インスタンス) を参照するハンドラを取得する XANDLER_INTERNAL マクロは、 RegisterHandler 関数のハンドラ関数の引数の指定に使用されます。

詳細は、 「SFY レスポンダシステム用マクロ:その他のマクロ」を参照してください。

[Caution] return 文

C++ ではコンストラクタやデストラクタでは return 文を記述しません。

GCC を使う場合、コンストラクタやデストラクタ内で return 文を記述すると、 特定の継承関係になっているときにコンパイラがフリーズするバグが確認されています。

3.1.4. デストラクタの実装

アプリケーションクラスのデストラクタには、 アプリの終了処理を記述します。 アプリケーションクラスのデストラクタは、 SFCApplication::Terminate 関数を実行した後に呼び出されます。

例 3.4. デストラクタの実装

// デストラクタ
helloworld::~helloworld(Void)
{
    // return;  ← NG(×) デストラクタでは return 文を記述してはいけない
}
[Caution] コンストラクタやデストラクタ内の return 文

C++ ではコンストラクタやデストラクタでは return 文を記述しません。

GCC を使う場合、コンストラクタやデストラクタ内で return 文を記述すると、 特定の継承関係になっているときにコンパイラがフリーズするバグが確認されています。

3.1.5. ハンドラの実装

helloworld アプリケーションクラス定義で宣言した描画ハンドラとキーハンドラを実装します。

3.1.5.1. 描画ハンドラの実装

SFXGraphics::DrawSingleText 関数を呼び出して文字列 "Hello World" を画面に描画するハンドラを実装します。

[Important] 描画処理の記述

GUI フレームワークを使う SFY アプリでは、 描画処理はすべて描画ハンドラ内に記述します。

例 3.5. 描画ハンドラの実装

// 描画ハンドラの実装
XANDLER_IMPLEMENT_VOIDRENDER(helloworld, OnRenderRequest, invoker, reason, graphics)
{
    unused(invoker);
    unused(reason);

    // レスポンダの描画はローカル領域内で行う(描画の原点はローカル領域の左上端)
    // アプリケーションクラスの場合、ルートのローカル領域への描画となる

    // ローカル領域の背景は SetBackgroundColor 関数で設定した色(デフォルト: 白色)で塗り潰される

    // "Hello World" を画面中央に黒色で描画する
    // ※ ルートのローカル領域(GetLocalBound 関数の戻り値)は画面領域
    graphics->DrawSingleText("Hello World", 
                              GetLocalBound(), 
                              SFXRGBColor(0x00, 0x00, 0x00, 0x00));

    // "Right Bottom" を画面右下に青色で描画する
    graphics->DrawSingleText("RightBottom", 
                              GetLocalBound(), 
                              SFXRGBColor(0x00, 0x00, 0xAA, 0x00), 
                              IDF_ALIGN_BOTTOM | IDF_ALIGN_RIGHT);

    // "Left Top+(10,30)" を (10, 30) の位置に緑色で下線付で描画する
    graphics->DrawSingleText("LeftTop+(10,30)", 
                              SFXGrid(10, 30), 
                              SFXRGBColor(0x00, 0xAA, 0x00, 0x00), 
                              IDF_TEXT_UNDERLINE);

    return;
}
[Tip] XANDLER_IMPLEMENT_VOIDRENDER マクロ

XANDLER_IMPLEMENT_VOIDRENDER は、 描画ハンドラを実装するためのマクロです。 描画イベント を受信すると、 ここで実装した描画ハンドラが呼び出されます。

invoker はハンドラの呼び出し元のレスポンダ、 reason は P16 イベントパラメータ、 graphics はグラフィックスオブジェクトを表しています。

この例では ハンドラの呼び出し元 invokerSFYResponderPtr 型の helloworld アプリケーションクラスが保持するルートSFZRoot)へのポインタ、 P16 イベントパラメータ reason は SFP16_RENDER_REQUEST です。

[Tip] unused マクロ

未使用変数の警告を防ぐために unused マクロを利用しています。

[Tip] SFXGraphics::DrawSingleText 関数

SFXGraphics::DrawSingleText 関数は、 指定した場所に 1 行の文字列を描画します。

複数行の文字列を表示する場合は、 SFXGraphics::DrawMultipleTextCenter / SFXGraphics::DrawMultipleTextLeft / SFXGraphics::DrawMultipleTextRight 関数を使います。

[Tip] SFYApplication::GetLocalBound 関数

SFYApplication クラスを継承するアプリケーションクラスは、 ルートSFZRoot)を 1 つ保持します。

SFYApplication::GetLocalBound 関数で得られる領域は、 ルートのローカル領域です。 この領域は SFXGraphics::GetDeviceRectangle 関数で得られる携帯電話の画面領域に初期設定されています。

この例ではルートのローカル領域に描画を行っています。

[Note] レスポンダの背景色

SFYWidget クラスを継承するレスポンダでは、 SFYWidget::SetBackgroundColor 関数を使用して背景色を設定できます。

SFYResponder::SetPropertyTransparent 関数を使用して透過属性を設定していない場合、 SFYWidget クラスに登録されたレスポンダのデフォルトの描画ハンドラにより SFYWidget::SetBackgroundColor 関数で設定した背景色(デフォルト: 白色)で レスポンダのローカル領域は塗り潰されます。

上の例では、アプリケーションクラスが内部で保持するルートSFZRoot) に透過属性も背景色も設定していませんので、 白色の背景に文字列 "Hello World " が黒色で描画されます。

[Note] 4 種類のレスポンダ領域

レスポンダには以下の 4 種類の領域が存在します。

  1. 実領域. レスポンダの可視領域を親レスポンダのローカル領域の左上端を原点とする相対座標系で表した矩形領域です。 サイズはグローバル領域と同じです。

    参照: 実領域 | SFYResponder::SetRealBound

  2. 仮想領域. レスポンダの領域を実領域の左上端を原点とする相対座標系で表した矩形領域です。 サイズはローカル領域と同じです。

    参照: 仮想領域 | SFYResponder::SetVirtualBound

  3. ローカル領域. レスポンダの領域の左上端を原点とする相対座標系で表した矩形領域です。 サイズは仮想領域と同じです。

    参照: ローカル領域 | SFYResponder::GetLocalBound

  4. グローバル領域. レスポンダの可視領域を画面の左上端を原点とする絶対座標系で表した矩形領域です。 サイズは実領域と同じです。 BREW API の引数に絶対座標系で表したレスポンダの可視領域を渡すときに利用します。

    参照: グローバル領域 | SFYResponder::GetGlobalBound

[Note] 描画イベントの処理

以下の何れかの状況で SFYResponder::Render 関数を呼び出すと、 再描画が必要な可視領域を持つレスポンダは、 描画イベントを受信します。

  1. アプリの開始時とレジューム時に全画面再描画を行う SFYApplication::HandleRender 関数内
  2. イベントループの最後のタイミング
  3. コールバック内(コールバックはイベントループ外の処理)

描画イベントを受信すると、 レスポンダは描画ハンドラを起動します。

描画ハンドラは、 graphics 引数(SFXGraphics 型)を利用してローカル領域に描画処理を行います。

描画ハンドラである、上記の実装コードにある OnRenderRequest 関数は、 アプリ開始時に全画面再描画を行う SFYApplication::HandleRender 関数の内部処理によって呼び出されます。

詳細は、描画処理を参照してください。

図 3.1. 実行例

実行例

3.1.5.2. キーハンドラの実装

セレクトキーの SFEVT_KEY イベントを受信したとき、 アプリを終了させるキーハンドラを実装します。

例 3.6. キーハンドラの実装

// キーハンドラの実装
XANDLER_IMPLEMENT_BOOLEVENT(helloworld, OnKey, invoker, event)
{
    unused(invoker);

    switch (event.GetP16()) {

        case AVK_SELECT: // セレクトキーの SFEVT_KEY イベントを受信したとき

            Terminate(); // helloworld アプリを終了する

            return true; // キーイベントを処理したので true を返す
    }

    return false; // キーイベントを処理していないので false を返す
}
[Tip] XANDLER_IMPLEMENT_BOOLEVENT マクロ

XANDLER_IMPLEMENT_BOOLEVENT は、 Bool 型を返すハンドラを実装するための汎用マクロです。

ここでは、 キーイベントのハンドラを実装しています。

[Note] アプリケーションクラスへのレスポンダ操作

アプリケーションクラスに対するレスポンダ操作は、 アプリケーションクラスが保持するルートSFZRoot)に委譲されます。

[Note] アプリの終了処理

SFCApplication::Terminate 関数を呼び出すと、 SFYApplication::~SFYApplication デストラクタが呼び出されてアプリは終了します。

SFYApplication::~SFYApplication デストラクタでは、 レスポンダツリー描画エンジン配信エンジンの順に終了処理が行われ、 アプリが使用していたリソースはすべて解放されます。

レスポンダの終了処理を行う SFYResponder::Terminate 関数とは処理内容の範囲が異なります。

[Note] キーイベントの処理

キーイベント[SFEVT_KEY]は、 携帯電話のキーを押したタイミングで発生し、 トレーサの規則によりフォーカス状態のレスポンダに配信されます。

最初に呼び出されるキーハンドラは、 最前面にあるフォーカス状態のレスポンダに 1 番目に登録されたキーハンドラです。

キーハンドラがキーイベントを処理したときは、そこで終了します。 処理しなかったとき、2 番目に登録されたキーハンドラが呼び出されます。 呼び出すキーハンドラが存在しない場合は、 1 つだけ背面にあるフォーカス状態のレスポンダで同様に処理を行います。

[Important] キーハンドラの戻り値

キーハンドラは、 キーイベントを処理した場合は true、 処理しなかった場合は false を返します。

3.1.6. グローバル変数の定義とアクセス

アプリケーションクラスのインスタンスはアプリ内のどこからでも SFYApplication::GetInstance 関数を使用してアクセスできますので、 グローバルな変数はアプリケーションクラスに定義します。

例 3.7. グローバルな変数の定義とアクセス

//
//  helloworld アプリケーションクラス
//
SFMTYPEDEFCLASS(helloworld)  //  便利な型を生成するマクロ
class helloworld : public SFYApplication
{
    SFMSEALCOPY(helloworld)  // インスタンスのコピーを禁止するマクロ
public:
    static SFCInvokerPtr Factory(Void);
private:
    explicit helloworld(Void) static_throws;
    virtual ~helloworld(Void);

    // グローバル変数 global_something_val を定義する
    SInt32 global_something_val;
public:
    // グローバル変数 global_something_val にアクセスするための GetGlobalSomethingVal 関数を宣言する
    SInt32 GetGlobalSomethingVal() const;
};

// グローバル変数 global_something_val にアクセスするための GetGlobalSomethingVal 関数を定義する
SInt32 helloworld::GetGlobalSomethingVal(Void) const
{
    return global_something_val;
}

// GetGlobalSomethingVal 関数経由でグローバル変数 global_something_val の値を取得する
static_cast<helloworldPtr>(SFYApplication::GetInstance())->GetGlobalSomethingVal();

3.1.7. エラー処理の機構

コンストラクタで使用している static_exception::static_throw / static_exception::static_try 関数は、 エラー値を設定/検証するための関数です。

設定されたエラー値は static_exception::static_catch 関数で取得できます。 通常、static_exception::static_catch 関数はエラー値を throw するクラスを利用する側で使います。

[Note] コンストラクタ内で発生したエラーの処理

コンストラクタはエラー値を返せませんが、 この機構を使えばコンストラクタ内で発生したエラーを外部に通知できます。

例 3.8. コンストラクタ内エラー処理

 // MyResponder コンストラクタ内で発生したエラーを MyResponder クラスの外部で処理するコード
myResponder = MyResponder::NewInstance()
switch( myResponder->static_catch() ){

        // エラー値に応じてエラー処理をする

       // ...(省略)...

};

static_exception はエラー値を管理する変数(テンプレート引数)を持つテンプレートクラスです。 この変数(テンプレート引数)には、SFCError 型が使用されます。

static_exception の存在意義は、 例外処理をサポートしない C++ コンパイラ(RVCT for BREW 1.2 など) でコンストラクタ内でのエラー値を外部に搬出できる点にあります。

[Note] 注意

SFYResponder クラスは、 static_exception クラスを継承します。

参照: アプリケーションクラス(基礎編)