概要

Hello World のビルドで作成したプログラムは HOME ボタン(PS ボタン)で終了する機能が実装されておらず、代わりに電源を切ってアプリケーションを終了する必要がありました。

今回はスレッドを作成して HOME ボタンが押されたときの処理をシステムに登録することで、 HOME ボタンを押したときに以下の「ゲームを終了しますか?」の画面を出して終了するようにしてみます。

programming psp home menu

ソースコード

今回のソースコードです。コピーして実行してみてください。

#include <pspkernel.h>
#include <pspdebug.h>
#include <pspdisplay.h>

PSP_MODULE_INFO("Hello World", PSP_MODULE_USER, 1, 1);
PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER);

static volatile int running = 1;

int ExitCallback(int arg1, int arg2, void *arg)
{
    running = 0;
    return 0;
}

SceUID CallbackThread(unsigned int args, void *argp)
{
    // 関数ポインターからコールバック関数を作成
    int callbackId = sceKernelCreateCallback("ExitCallback", ExitCallback, NULL);
    if (callbackId < 0) {
        return callbackId;
    }

    // 終了コールバックにコールバック関数を登録
    sceKernelRegisterExitCallback(callbackId);
    sceKernelSleepThreadCB();

    return 0;
}

SceUID SetupCallbacks(void)
{
    // 関数ポインターからスレッドを作成
    int threadId = sceKernelCreateThread("CallbackThread", CallbackThread, 0x11, 0xFA0, 0, NULL);
    if (threadId < 0) {
        return threadId;
    }

    // イベント監視スレッドを開始
    sceKernelStartThread(threadId, 0, NULL);

    return 0;
}

int main(int argc, char *argv[])
{
    // コールバック関数の読み込み
    SetupCallbacks();
    pspDebugScreenInit();

    while (running) {
        pspDebugScreenSetXY(0, 0);
        pspDebugScreenPrintf("Hello World!!\n");
        sceDisplayWaitVblankStart();
    }

    sceKernelExitGame();

    return 0;
}

仕組み

PSP のアプリケーションを HOME ボタンによる終了に対応させるためには、終了処理のためのコールバックを sceKernelRegisterExitCallback() 関数を使ってシステムに登録する必要があります。この関数に渡すコールバックは sceKernelCreateCallback() 関数を用いて作成することができ、今回の例ではユーザーが終了を選択したときにシステムによって呼び出されるようになっています。

また、これらの終了コールバックの登録処理は新規作成したスレッド上で行い、作成したスレッドもコールバックが呼び出されるまでは sceKernelSleepThreadCB() 関数を使用して待機状態にしておきます。こうしておくことで、メインスレッドでゲームのメインループを処理しながら終了コールバックを待ち受けておくことができるようになります。

ユーザーが終了を選択すると、終了コールバックに登録した ExitCallback() 関数が変数 running0 に変更します。これによってメインループの while (running) { ... } の継続条件から外れてループが終了し、sceKernelExitGame() 関数がゲームを終了するという流れになります。

終了コールバックを登録しただけではゲームの終了は行われず、あくまでもプログラマーが終了を管理する必要がある点には注意が必要です。

API リファレンス

SceUID sceKernelCreateCallback(
    const char *name, 
    int (*func)(int arg1, int arg2, void *arg), 
    void *arg
);
コールバック関数を作成します。成功するとコールバック ID、失敗すると負の値を返します。
  • name-コールバック関数の名前
  • func-呼び出される関数へのポインター
  • arg-現時点では不明。コールバックの引数?
int sceKernelRegisterExitCallback(SceUID cbid);
コールバック関数を終了コールバックとして登録します。成功すると 0、失敗すると負の値を返します。
  • cbid-コールバック ID
int sceKernelSleepThreadCB(void);
スレッドをスリープさせてイベント待機状態にします。
SceUID sceKernelCreateThread(
    const char *name, 
    int (*entry)(unsigned int args, void *argp), 
    int initPriority, 
    int stackSize, 
    unsigned int attr, 
    SceKernelThreadOptParam *option
);
スレッドを作成します。成功するとスレッド ID、失敗すると負の値を返します。
  • name-スレッドの名前
  • entry-実行される関数のポインター
  • initPriority-スレッドを実行する優先度。値が小さいほど優先度が高い。(main = 0x32)
  • stackSize-スタックサイズの初期値
  • attr-スレッドの種類
  • option-不明

スレッドの種類は Hello World のビルドで紹介した PSP_MAIN_THREAD_ATTR() のものと同じです。

int sceKernelStartThread(
    SceUID thid, 
    unsigned int arglen, 
    void *argp
);
スレッドを開始します。
  • thid-スレッド ID
  • arglen-関数の引数のサイズをバイト数で指定
  • argp-実行する関数に渡す引数
int sceDisplayWaitVblankStart(void);
垂直方向の描画が終わるのを待ってから表示します。(チラつき防止)
void sceKernelExitGame(void);
ゲームを終了し XMB へ戻ります。