VitaでZAVASやりたくて(10)

前回までのあらすじ

念願のソフトウェアキーボードを探し求めて三千里、ついにRetroarchの改造へと手を染めるのであった。
あらすぎのあらすじおわり。

Retroarch改造計画~前編

いじる場所にあたりをつける

以前に改造した時のソース自体は残していたんですけど、なんでそれで良かったのかほぼ忘れてしまっているので改めて調べなおしてみました。

まずはどのへんをいじれば良いのかあたりをつけなければなりません。
きっと「touch」というワードは入っていると予想されるのでひとまずソース全体をgrepしてみます。
結構色々引っ掛かりますが、プログラムとして意味を持ちそうなものをピックアップしていくと「gfx/」以下と「input/」以下のファイルで多く引っ掛かります。
特に「input/」の方は多くのプログラムコードが引っ掛かっていて、名前からしても恐らくそのへんで入力系の処理をしているに違いない。
「input/drivers/android_input.c」とか「input/drivers/switch_input.c」とか、いかにも機種ごとの実装が書かれてる感じ。
ただしVitaに関係しそうなのがぱっと見では見つかりません。
ちょっと関係ありそうなのは「input/drivers_joypad/psp_joypad.c」とやらが引っ掛かる程度。
PSP?と思いつつもちょっと内容を確認してみるとその中でVita用の処理を書き分けているのがわかります。

static void *psp_joypad_init(void *data)
{
   unsigned i;
   unsigned players_count = DEFAULT_MAX_PADS;

#if defined(VITA)
   psp2_model = sceCtrlIsMultiControllerSupported()? SCE_KERNEL_MODEL_VITATV : SCE_KERNEL_MODEL_VITA;

   if (psp2_model != SCE_KERNEL_MODEL_VITATV)
      players_count = 1;
   if (sceKernelGetModelForCDialog() != SCE_KERNEL_MODEL_VITATV)
   {
      sceTouchSetSamplingState(SCE_TOUCH_PORT_BACK,
            SCE_TOUCH_SAMPLING_STATE_START);
      sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT,
            SCE_TOUCH_SAMPLING_STATE_START);
   }
   sceCtrlGetControllerPortInfo(&curr_ctrl_info);
   memcpy(&old_ctrl_info, &curr_ctrl_info, sizeof(SceCtrlPortInfo));
#endif

   for (i = 0; i < players_count; i++)
      input_autoconfigure_connect(
            psp_joypad_name(i),
            NULL,
            psp_joypad.ident,
            i,
            0,
            0
            );

   return (void*)-1;
}

ちなみにソースをみるとやたら「PSP2」という呼び方が出てくるけども、Vitaの開発コードか何かが「PSP2」なんだろうか。謎。
てなわけで、ずいぶんざっくりとした予想ではありますが、たぶんこの辺に何かを書けばいいんだろうと予想をつけてもう少しソースを追ってみることに。
上のソースをみると「sceCtrlIsMultiControllerSupported」みたいに「sceなんとか」という関数がたくさん出てきます。
ググってみると、おっと、これはVitaSDKの関数ではないか。
訳も分からずインストールしたVitaSDKとやっとつながりました。

折角なのでドキュメントを少し眺めてみると、Touch Libraryなるものを発見。
「sceTouchRead」とか「sceTouchPeek」とかありますね。これはあやしい。

全部見るほど元気はないので、ひとまずなるほどねーと軽く頭にとめておいて、ソースに戻ってもう少し見ていくと、案の定ありましたぞ!この真ん中ちょい下あたり!

static void psp_joypad_poll(void)
{
   unsigned player;
   unsigned players_count      = DEFAULT_MAX_PADS;
#if defined(VITA)
   settings_t *settings        = config_get_ptr();
   bool input_backtouch_enable = settings->bools.input_backtouch_enable;
   bool input_backtouch_toggle = settings->bools.input_backtouch_toggle;
#endif

#ifdef PSP
   sceCtrlSetSamplingCycle(0);
#endif

#ifdef VITA
   if (psp2_model != SCE_KERNEL_MODEL_VITATV)
      players_count = 1;
   else
   {
      sceCtrlGetControllerPortInfo(&curr_ctrl_info);
      for (player = 0; player < players_count; player++)
      {
         if (old_ctrl_info.port[player + 1] == curr_ctrl_info.port[player + 1])
            continue;

         if (old_ctrl_info.port[player + 1] != SCE_CTRL_TYPE_UNPAIRED &&
               curr_ctrl_info.port[player + 1] == SCE_CTRL_TYPE_UNPAIRED)
         {
            memset(&actuators[player], 0, sizeof(SceCtrlActuator));
            input_autoconfigure_disconnect(player, psp_joypad.ident);
         }

         if (old_ctrl_info.port[player + 1] == SCE_CTRL_TYPE_UNPAIRED &&
               curr_ctrl_info.port[player + 1] != SCE_CTRL_TYPE_UNPAIRED)
            input_autoconfigure_connect(
                  psp_joypad_name(player),
                  NULL,
                  psp_joypad.ident,
                  player,
                  0,
                  0
                  );
      }
      memcpy(&old_ctrl_info, &curr_ctrl_info, sizeof(SceCtrlPortInfo));
   }
#endif

   CtrlSetSamplingMode(DEFAULT_SAMPLING_MODE);

   BIT64_CLEAR(lifecycle_state, RARCH_MENU_TOGGLE);

   for (player = 0; player < players_count; player++)
   {
      unsigned j, k;
      SceCtrlData state_tmp;
      unsigned i  = player;
#if defined(VITA)
      unsigned p = (psp2_model == SCE_KERNEL_MODEL_VITATV) 
         ? player + 1 : player;
      if (curr_ctrl_info.port[p] == SCE_CTRL_TYPE_UNPAIRED)
         continue;
#elif defined(SN_TARGET_PSP2)
      /* Dumb hack, but here's the explanation -
       * sceCtrlPeekBufferPositive's port parameter
       * can be 0 or 1 to read the first controller on
       * a PSTV, but HAS to be 0 for a real VITA and 2
       * for the 2nd controller on a PSTV */
      unsigned p  = (player > 0) ? player+1 : player;
#else
      unsigned p  = player;
#endif
      int32_t ret = CtrlPeekBufferPositive(p, &state_tmp, 1);

      pad_state[i] = 0;
      analog_state[i][0][0] = analog_state[i][0][1] =
         analog_state[i][1][0] = analog_state[i][1][1] = 0;

#if defined(SN_TARGET_PSP2) || defined(VITA)
      if (ret < 0)
         continue;
#endif
#if defined(VITA)
      if (sceKernelGetModelForCDialog() == SCE_KERNEL_MODEL_VITA
         && input_backtouch_enable)
      {
         unsigned i;
         SceTouchData touch_surface = {0};
         sceTouchPeek(input_backtouch_toggle
               ? SCE_TOUCH_PORT_FRONT 
               : SCE_TOUCH_PORT_BACK,
               &touch_surface, 1);

         for (i = 0; i < touch_surface.reportNum; i++)
         {
            int x = LERP(touch_surface.report[i].x,
                  TOUCH_MAX_WIDTH, SCREEN_WIDTH);
            int y = LERP(touch_surface.report[i].y,
                  TOUCH_MAX_HEIGHT, SCREEN_HEIGHT);
            if (NW_AREA(x, y))
               state_tmp.buttons |= PSP_CTRL_L2;
            if (NE_AREA(x, y))
               state_tmp.buttons |= PSP_CTRL_R2;
            if (SW_AREA(x, y))
               state_tmp.buttons |= PSP_CTRL_L3;
            if (SE_AREA(x, y))
               state_tmp.buttons |= PSP_CTRL_R3;
         }
      }
#endif

      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_LEFT) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_LEFT) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_DOWN) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_DOWN) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_RIGHT) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_RIGHT) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_UP) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_UP) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_START) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_START) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_SELECT) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_SELECT) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_TRIANGLE) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_X) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_SQUARE) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_Y) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_CROSS) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_B) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_CIRCLE) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_A) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_R) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_R) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_L) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_L) : 0;
#if defined(VITA)
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_R2) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_R2) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_L2) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_L2) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_R3) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_R3) : 0;
      pad_state[i] |= (STATE_BUTTON(state_tmp) & PSP_CTRL_L3) ? (UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_L3) : 0;
#endif

      analog_state[i][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_X] = (int16_t)(STATE_ANALOGLX(state_tmp)-128) * 256;
      analog_state[i][RETRO_DEVICE_INDEX_ANALOG_LEFT] [RETRO_DEVICE_ID_ANALOG_Y] = (int16_t)(STATE_ANALOGLY(state_tmp)-128) * 256;
#if defined(SN_TARGET_PSP2) || defined(VITA)
      analog_state[i][RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_X] = (int16_t)(STATE_ANALOGRX(state_tmp)-128) * 256;
      analog_state[i][RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_Y] = (int16_t)(STATE_ANALOGRY(state_tmp)-128) * 256;
#endif
      for (j = 0; j < 2; j++)
         for (k = 0; k < 2; k++)
            if (analog_state[i][j][k] == -0x8000)
               analog_state[i][j][k] = -0x7fff;
   }
}

完全に「sceTouchPeek」しとるよこれは。
さてタッチ機能の何かを処理してるのは間違いないようですけど何でしょう。
その下を見ると読み取ったタッチ座標と思しきパラメータをもとに「NW_AREA」とかのマクロを呼んで分岐しています。なにこれ。

//#define PSP_FB_WIDTH        960  // from psp_defines.h
//#define PSP_FB_HEIGHT       544  // from psp_defines.h

#define LERP(p, f, t) ((((p * 10) * (t * 10)) / (f * 10)) / 10)
#define AREA(lx, ly, rx, ry, x, y) (lx <= x && x < rx && ly <= y && y < ry)
#define TOUCH_MAX_WIDTH 1919
#define TOUCH_MAX_HEIGHT 1087
#define SCREEN_WIDTH PSP_FB_WIDTH
#define SCREEN_HEIGHT PSP_FB_HEIGHT
#define SCREEN_HALF_WIDTH SCREEN_WIDTH / 2
#define SCREEN_HALF_HEIGHT SCREEN_HEIGHT / 2
#define NW_AREA(x, y) AREA(0, 0, SCREEN_HALF_WIDTH, SCREEN_HALF_HEIGHT, (x), (y))
#define NE_AREA(x, y) AREA(SCREEN_HALF_WIDTH, 0, SCREEN_WIDTH, SCREEN_HALF_HEIGHT, (x), (y))
#define SW_AREA(x, y) AREA(0, SCREEN_HALF_HEIGHT, SCREEN_HALF_WIDTH, SCREEN_HEIGHT, (x), (y))
#define SE_AREA(x, y) AREA(SCREEN_HALF_WIDTH, SCREEN_HALF_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, (x), (y))

NW=北西、NE=北東、SW=南西、SE=南東、という意味っぽい。
LEAPが何をやってるか若干不透明ではありますけど、タッチの解像度がスクリーンの解像度の2倍あるような定義から推測すると、タッチ座標(x:0-1919, y:0-1087)をスクリーン座標(x:0-959, y:543)に変換する処理をしているのかなきっと。
それを踏まえて先ほどのソースを見直すと、おそらくだけど、スクリーンを4分割した際にどの範囲をタッチしたかを判別し、「NW=L2」「NE=R2」「SW=L3」「SE=R3」ボタンに割り当てる、という処理をやってる感じがするきっと。
どうやらこれが前回ちょっと触れた、一部だけタッチでボタン割り当てできる、を実装してるところみたいですねきっと。

これでなんとなくだけどタッチを検知してどう扱えばいいか雰囲気はわかりました。
とはいえこれからどうしよう。

Android版を参考にする

タッチかーなんか参考になるのないかなーとしばし思案。
そういえばAndroid版があったなとあれなら多分タッチで色々出来るに違いないとピンときて、さっそくインストールしてソフトウェアキーボードを試してみます。

結果。
うごくっちゃうごく。
なんかバグってる感じするけど一応反応はしている。

まあとりあえずタッチに反応してる事は確認できたので参考にはなろう。
ということでAndroid版でどういう作りになってるかを見ていくことに。

タッチ座標を記録してるとこ

「touch」の検索でやたら引っ掛かる「input/drivers/android_input.c」があからさまに怪しいので中身をみてみると先頭当たりに「MAX_TOUCH」が定義されてます。
普通に考えるとタッチできる数の上限という意味。
参照箇所を辿るといきなり「android_input_t」なる構造体がみつかります。

#define MAX_TOUCH 16

...

struct input_pointer
{
   int16_t x, y;
   int16_t full_x, full_y;
};

...

typedef struct android_input
{
   int64_t quick_tap_time;
   state_device_t pad_states[MAX_USERS];        /* int alignment */
   int mouse_x_delta, mouse_y_delta;
   int mouse_l, mouse_r, mouse_m, mouse_wu, mouse_wd;
   unsigned pads_connected;
   unsigned pointer_count;
   sensor_t accelerometer_state;                /* float alignment */
   sensor_t gyroscope_state;                    /* float alignment */
   float mouse_x_prev, mouse_y_prev;
   struct input_pointer pointer[MAX_TOUCH];     /* int16_t alignment */
   char device_model[256];
} android_input_t;

なにやらMAX_TOUCH個の座標を格納するようになってる。
やはりタッチした座標と考えて良さそうな感じです。
この「pointer」とやらにどうやって値を入れているか見てみます。
代入箇所を探すと「android_input_poll_event_type_motion」内の2か所だけヒット。
(長いので該当箇所付近だけ抜粋)


static INLINE void android_input_poll_event_type_motion(
      android_input_t *android, AInputEvent *event,
      int port, int source, bool vibrate_on_keypress)
{

...

   if (keyup && motion_ptr < MAX_TOUCH)
   {
      if (action == AMOTION_EVENT_ACTION_UP && ENABLE_TOUCH_SCREEN_MOUSE)
      {
         /* If touchscreen was pressed for less than 200ms
          * then register time stamp of a quick tap */
         if ((AMotionEvent_getEventTime(event)-AMotionEvent_getDownTime(event))/1000000 < 200)
            android->quick_tap_time = AMotionEvent_getEventTime(event);
         android->mouse_l = 0;
      }

      memmove(android->pointer + motion_ptr,
            android->pointer + motion_ptr + 1,
            (MAX_TOUCH - motion_ptr - 1) * sizeof(struct input_pointer));
      if (android->pointer_count > 0)
         android->pointer_count--;
   }

...

      for (motion_ptr = 0; motion_ptr < pointer_max; motion_ptr++)
      {
         struct video_viewport vp;
         float x = AMotionEvent_getX(event, motion_ptr);
         float y = AMotionEvent_getY(event, motion_ptr);

         vp.x                        = 0;
         vp.y                        = 0;
         vp.width                    = 0;
         vp.height                   = 0;
         vp.full_width               = 0;
         vp.full_height              = 0;

         video_driver_translate_coord_viewport_wrap(
               &vp,
               x, y,
               &android->pointer[motion_ptr].x,
               &android->pointer[motion_ptr].y,
               &android->pointer[motion_ptr].full_x,
               &android->pointer[motion_ptr].full_y);

         android->pointer_count = MAX(
               android->pointer_count,
               motion_ptr + 1);
      }

1個目のmemmoveはちょっと見ただけでは何やらよくわかりません。
2個目の方は「AMotionEvent_getX」「AMotionEvent_getY」で何かの座標を取得してそれをもとにpointerの値をセットしてるのかな?という感じ。
「AMotionEvent_getX」をググってみるとAndroidのNDK版のAPIらしく何やらイベント発生時の座標を取得してくるものだそうな。NDK版なんて滅多にお目にかかれないしここ以外で一生お目にかからないだろうな。
それはさておき「android_input_poll_event_type_motion」のコール元を見るとタッチイベントの場合にコールするようになっているのでつまりタッチ座標を取得しているという意味のようです。
では「video_driver_translate_coord_viewport_wrap」は何をしとるんだと。
探してみると次のマクロです。

#define video_driver_translate_coord_viewport_wrap(vp, mouse_x, mouse_y, res_x, res_y, res_screen_x, res_screen_y) \
   (video_driver_get_viewport_info(vp) ? video_driver_translate_coord_viewport(vp, mouse_x, mouse_y, res_x, res_y, res_screen_x, res_screen_y) : false)

2つの関数を呼んでる。
1個目はこれ。

bool video_driver_get_viewport_info(struct video_viewport *viewport)
{
   video_driver_state_t *video_st  = &video_driver_st;
   if (!video_st->current_video || !video_st->current_video->viewport_info)
      return false;
   video_st->current_video->viewport_info(video_st->data, viewport);
   return true;
}

この先を辿るとちょっと長すぎるので結論だけ言うと、Retroarchでは端末の画面全体とその中のエミュレータ画面部分(縮小表示されたりアスペクト比によって縦横が伸び縮みしたり)との位置関係をビューポートという概念で管理しているらしく、そのビューポート情報を取得してくる、という処理をしています。
要は画面のどこにエミュレータが出てるかこれで分かった状態。

2個目がこれ。これは正直しんどい。

bool video_driver_translate_coord_viewport(
      struct video_viewport *vp,
      int mouse_x,           int mouse_y,
      int16_t *res_x,        int16_t *res_y,
      int16_t *res_screen_x, int16_t *res_screen_y)
{
   int norm_vp_width         = (int)vp->width;
   int norm_vp_height        = (int)vp->height;
   int norm_full_vp_width    = (int)vp->full_width;
   int norm_full_vp_height   = (int)vp->full_height;
   int scaled_screen_x       = -0x8000; /* OOB */
   int scaled_screen_y       = -0x8000; /* OOB */
   int scaled_x              = -0x8000; /* OOB */
   int scaled_y              = -0x8000; /* OOB */
   if (norm_vp_width       <= 0 ||
       norm_vp_height      <= 0 ||
       norm_full_vp_width  <= 0 ||
       norm_full_vp_height <= 0)
      return false;

   if (mouse_x >= 0 && mouse_x <= norm_full_vp_width)
      scaled_screen_x = ((2 * mouse_x * 0x7fff)
            / norm_full_vp_width)  - 0x7fff;

   if (mouse_y >= 0 && mouse_y <= norm_full_vp_height)
      scaled_screen_y = ((2 * mouse_y * 0x7fff)
            / norm_full_vp_height) - 0x7fff;

   mouse_x           -= vp->x;
   mouse_y           -= vp->y;

   if (mouse_x >= 0 && mouse_x <= norm_vp_width)
      scaled_x        = ((2 * mouse_x * 0x7fff)
            / norm_vp_width) - 0x7fff;
   else
      scaled_x        = -0x8000; /* OOB */

   if (mouse_y >= 0 && mouse_y <= norm_vp_height)
      scaled_y        = ((2 * mouse_y * 0x7fff)
            / norm_vp_height) - 0x7fff;

   *res_x             = scaled_x;
   *res_y             = scaled_y;
   *res_screen_x      = scaled_screen_x;
   *res_screen_y      = scaled_screen_y;
   return true;
}

なにがなにやらですけど気を取り直してみてみましょう。
まずvpのメンバはこういう意味です。

vp->x, vp->y                    ... エミュレータ画面部分の開始座標
vp->width, vp->height           ... エミュレータ画面部分のサイズ
vp->full_width, vp->full_height ... 端末画面全体のサイズ

冒頭でnormなんちゃらにこのへんの値を代入しています。ちなみにここでいうnormは正規化の意味のnormみたい。
エラーリターンの判定があって、さあ次が問題。

   if (mouse_x >= 0 && mouse_x <= norm_full_vp_width)
      scaled_screen_x = ((2 * mouse_x * 0x7fff)
            / norm_full_vp_width)  - 0x7fff;

頭痛を覚えながらもしばし心の目で眺めると…閃いた!

「座標が0」→「-32767」
「座標がnorm_full_vp_width(端末画面横幅)」→「32767」(※)

※理屈上の上限。実際の上限は解像度に依存してもっと小さな値になる(最大でも32766まで?Vitaの解像度だと32698というとても微妙な数値になるはず)。正確に書くとよく分かんなくなるので32767としてます。

つまり、端末上の座標を-32767から32767にマッピングしている、もしくは縦横65535の解像度に読み替えたと言ってもよろしい。
注意する点は、とりうる値の範囲が-32767から32767なので真ん中が原点になります。
他も同様の変換が行われて、結果、端末の画面全体でそれに読み替えたものと、エミュレータ画面部分でそれに読み替えたものの2種類を算出しています。
途中で唐突にvp->xとvp->yを引いている箇所がありますが、エミュレータ画面部分を起点にして(端末画面の左上に移動させたと考えてもよい)算出しているということです。

まとめておくと、

  • 座標の範囲をX,Yとも「-32767から32767」に拡張する。

  • 画面中央を原点(0, 0)とし、X軸を右方向に、Y軸を下方向にとる。
    言い換えると画面左上が(-32767, -32767)、右下が(32767, 32767)となるように座標変換する。

  • 端末の画面全体をこれに当てはめた場合と、エミュレータ画面部分をこれに当てはめた場合の2通りで算出する。

(先にこちらを書いたので前段と言い回しがあってないところがあるけどご愛嬌)

タッチ座標が「-32767から32767」という少々奇妙な形で「pointer」に格納されるようだというところまではわかりました。
ところでmemmoveってなんだったんだろ?

   if (keyup && motion_ptr < MAX_TOUCH)
   {
      if (action == AMOTION_EVENT_ACTION_UP && ENABLE_TOUCH_SCREEN_MOUSE)
      {
         /* If touchscreen was pressed for less than 200ms
          * then register time stamp of a quick tap */
         if ((AMotionEvent_getEventTime(event)-AMotionEvent_getDownTime(event))/1000000 < 200)
            android->quick_tap_time = AMotionEvent_getEventTime(event);
         android->mouse_l = 0;
      }

      memmove(android->pointer + motion_ptr,
            android->pointer + motion_ptr + 1,
            (MAX_TOUCH - motion_ptr - 1) * sizeof(struct input_pointer));
      if (android->pointer_count > 0)
         android->pointer_count--;
   }

「keyup」とか「AMOTION_EVENT_ACTION_UP」とか、どうもキーを離したときに何かやる様子。
ここは厳密に確認したわけではないですけど離した時でピンとくるのはたぶんあれ。不要になった情報をリストから消してるんだろうな。
そもそもタッチ座標は全部更新するし、おそらく今回の改造にはこの箇所はたいして重要では無さげなので一旦このへんで忘れます。まずかったらまた考え直そう。

タッチ座標を参照してるとこ

今度は「pointer」を参照してる箇所を追ってみると「android_input_state」だけがヒット。

static int16_t android_input_state(
      void *data,
      const input_device_driver_t *joypad,
      const input_device_driver_t *sec_joypad,
      rarch_joypad_info_t *joypad_info,
      const retro_keybind_set *binds,
      bool keyboard_mapping_blocked,
      unsigned port,
      unsigned device,
      unsigned idx,
      unsigned id)

...

      case RETRO_DEVICE_POINTER:
      case RARCH_DEVICE_POINTER_SCREEN:
         switch (id)
         {
            case RETRO_DEVICE_ID_POINTER_X:
               if (device == RARCH_DEVICE_POINTER_SCREEN)
                  return android->pointer[idx].full_x;
               return android->pointer[idx].x;
            case RETRO_DEVICE_ID_POINTER_Y:
               if (device == RARCH_DEVICE_POINTER_SCREEN)
                  return android->pointer[idx].full_y;
               return android->pointer[idx].y;
            case RETRO_DEVICE_ID_POINTER_PRESSED:
               if (device == RARCH_DEVICE_POINTER_SCREEN)
                  return (idx < android->pointer_count) &&
                     (android->pointer[idx].full_x != -0x8000) &&
                     (android->pointer[idx].full_y != -0x8000);
               return (idx < android->pointer_count) &&
                  (android->pointer[idx].x != -0x8000) &&
                  (android->pointer[idx].y != -0x8000);
            case RETRO_DEVICE_ID_POINTER_COUNT:
               return android->pointer_count;
            case RARCH_DEVICE_ID_POINTER_BACK:
            {
               const struct retro_keybind *keyptr = 
                  &input_autoconf_binds[0][RARCH_MENU_TOGGLE];
               if (keyptr->joykey == 0)
                  return ANDROID_KEYBOARD_INPUT_PRESSED(AKEYCODE_BACK);
            }
         }
         break;

関数の名前からすると何が入力されているか返す関数みたい。
なるほどここならタッチ座標を参照するのも納得ですな。
一通り眺めてみると、条件が込み入ってはいますが、問い合わせの種類に応じて先ほど格納した値を返しているだけのようです。
他にも何か所タッチしたとかそういうのも返してるのかな。
いずれにせよここは普通に真似すれば良さげ。

Vita版の該当箇所を探す

これはあっさり見つかります。
Android版と同じフォルダに「input/drivers/psp_input.c」があって、次の2つがAndroid版で見てきた関数と対応する関数のようですね。

static void vita_input_poll(void *data)

static int16_t vita_input_state(
      void *data,
      const input_device_driver_t *joypad,
      const input_device_driver_t *sec_joypad,
      rarch_joypad_info_t *joypad_info,
      const retro_keybind_set *binds,
      bool keyboard_mapping_blocked,
      unsigned port,
      unsigned device,
      unsigned idx,
      unsigned id)

予習終わり。
いよいよいじってみるぞ。

次回予告

いじってみると言ったそばから唐突に次回予告です。
いろいろ確認してたら思ったよりだいぶ長いので前編と後編に分けることに。
後編では今度こそVita版をいじる冒険の旅へといざ出発!

この記事が気に入ったらサポートをしてみませんか?