Open22

整理

nikogolinikogoli

わかったこと/わからないことを整理するところ

いつか調べる
  • RNA_id_pointer_createRNA_pointer_create が処理の中心
    → 具体的になにをやっているのか? 2つの違いは?

  • コールバック関数の処理は、結局どのような意味を持つのか?
    取得した情報は result に代入される → operator などは、context じゃなくて result を利用している?

StructRNA の作成について
  • RNA_Imageなどの RNA_xx 系は、StructRNAとして定義はされているが、具体的な設定を行うコードは見つからない
  • Code Doc. やらの記述を見ると、preprocessing において自動的に作成されるように見える
    RNA_def_xx()のなかでは、様々なrna_def_yy()を実行し、プロパティが設定される
  • 確認のためには自分でビルドしてみる?
    問題は、ビルド結果に対して「何を調べればいいのか」がわからないこと
Code Doc. の記述

RNA の説明のとこ

Structs and their properties are defined in rna_*.c files in the makesrna module, and the definitions are generated as part of the build system. Errors will be printed during build and will help debugging.
...
Next in the RNA_def_* function the struct must be defined. RNA_def_struct defines the struct itself. We must define an identifier which is a unique name and used for identifying the struct in code, and a name which is a human readable name.
The system will automatically try to find a corresponding DNA struct with the same name as the identifier. If there is a corresponding DNA struct but it has a different name, RNA_def_struct_sdna can be used to pass the right name, this is important for defining properties more automatic later.

いま:

  • コールバックを呼んだ側が、結果をどのように処理して最終的な context を作っているのか?
    ctx_data_getのなかで各階層のコールバックが呼ばれているっぽい
    CTX_data_dir_get_exの中では 空文字列でコールバックを呼んでいる?
  • ID is 何? dna とどう違うの?
    → 識別符号がついたデータ(データブロック:最初単位のデータ?)があって、その構造というか、関連情報の持ち方が DNA って感じに見える

any data-block that has an ID (data that can be linked in and accessed from bpy.data)

nikogolinikogoli

https://zenn.dev/nikogoli/scraps/c355b8cdab930a

コメントとか

疑問

  • UI context とは 何か? どうやって要素を取得しているのか?
    予想:Buttons の context / set_context_pointer による 一時的・局所的なもの (通常は無)
    確認:実際に取得しているコードを見つける・・・どこ?

  • member が空文字列かどうかによって、context の要素が Noneになるのはおかしい
    予想:? 微妙な理由しか思いつかない
    確認:member を与えてコールバックを呼んでいるとこを見る・・・ bContext ?

その他

  • User Pref. や Main の context ってなに?
  • wm の一番上は、screen なのか window なのか この2つの関係は?
nikogolinikogoli

基本情報

https://developer.blender.org/diffusion/B/browse/master/source/blender/editors/screen/screen_context.c

StructRNA の定義 コメント削除版
source/blender/makesrna/intern/rna_internal_types.h$497
struct StructRNA {
  ContainerRNA cont;
  const char *identifier;
  void *py_type;
  void *blender_type;
  int flag;
  const EnumPropertyItem *prop_tag_defines;
  const char *name;
  const char *description;
  const char *translation_context;
  int icon;
  PropertyRNA *nameproperty;
  PropertyRNA *iteratorproperty;
  struct StructRNA *base;
  struct StructRNA *nested;
  StructRefineFunc refine;
  StructPathFunc path;
  StructRegisterFunc reg;
  StructUnregisterFunc unreg;
  StructInstanceFunc instance;
  IDPropertiesFunc idproperties;
  ListBase functions;
};
PointerRNA の定義
source/blender/makesrna/RNA_types.h$49
typedef struct PointerRNA {
  struct ID *owner_id;
  struct StructRNA *type;
  void *data;
} PointerRNA;
ID の定義 コメント削除版
source/blender/makesdna/DNA_ID.h$273
typedef struct ID {
  void *next, *prev;
  struct ID *newid;
  struct Library *lib;
  struct AssetMetaData *asset_data;
  char name[66];
  short flag;
  int tag;
  int us;
  int icon_id;
  int recalc;
  int recalc_up_to_undo_push;
  int recalc_after_undo_push;
  unsigned int session_uuid;
  IDProperty *properties;
  IDOverrideLibrary *override_library;
  struct ID *orig_id;
  void *py_instance;
  void *_pad1;
} ID;
bContextDataResult の定義
source/blender/blenkernel/intern/context.c
struct bContextDataResult {
  PointerRNA ptr;
  ListBase list;
  const char **dir;
  short type; /* 0: normal, 1: seq */
};
nikogolinikogoli
画像/UVエディタのコールバック関数 image_context のコード
master/source/blender/editors/space_image/space_image.c
const char *image_context_dir[] = {"edit_image", "edit_mask", NULL};

static int /*eContextResult*/ image_context(const bContext *C,
                                            const char *member,
                                            bContextDataResult *result)
{
  SpaceImage *sima = CTX_wm_space_image(C);

  if (CTX_data_dir(member)) {
    CTX_data_dir_set(result, image_context_dir);
  }
  else if (CTX_data_equals(member, "edit_image")) {
    CTX_data_id_pointer_set(result, (ID *)ED_space_image(sima));
    return CTX_RESULT_OK;
  }
  else if (CTX_data_equals(member, "edit_mask")) {
    Mask *mask = ED_space_image_get_mask(sima);
    if (mask) {
      CTX_data_id_pointer_set(result, &mask->id);
    }
    return CTX_RESULT_OK;
  }
  return CTX_RESULT_MEMBER_NOT_FOUND;
}
image_context() に関連する関数の関係図っぽいもの

結局、処理の中心的なポイントは2つ

  1. CTX_wm_space_image で SpaceImageEditor を bContext から取得する
    → SpaceImageEditor が手に入れば、image や mask はそのメンバなのでデータの取得は簡単
  2. RNA_id_pointer_create で必要な情報をresult.ptr以下に代入する
    調べた結果
nikogolinikogoli
CTX_wm_space_image() に関連する関数の関係図っぽいもの

処理の中心的なポイント

  1. BPY_context_member_getで bContext から Area の RNAポインタを取得する (result に代入)
  2. Area の space が SpaceImageEditor であるかどうかを確認する
nikogolinikogoli
BPY_context_member_get() に関連する関数の関係図っぽいもの

処理の中心的なポイント

  1. RNA_pointer_createで必要な情報をresult.ptr以下に代入する
    RNA_id_pointer_createではなく RNA_pointer_createであることの意味は?
nikogolinikogoli

CTX_data_id_pointer_set

とりあえずこのコードを追ってみた
source/blender/editors/space_image/space_image.c の一部
SpaceImage *sima = CTX_wm_space_image(C);
...
else if (CTX_data_equals(member, "edit_image")) {
  CTX_data_id_pointer_set(result, (ID *)ED_space_image(sima));
  return CTX_RESULT_OK;
}
  1. CTX_data_equals(member, "edit_image")member "edit_image"の一致判定
  2. ED_space_image(sima):Space からメンバの imageを取得 (型はstruct Image *)
  3. (ID *) image:image へのポインタ値を ID へのポインタ値にキャスト
  4. CTX_data_id_pointer_set(result, &ID)RNA_id_pointer_create(&ID, &result->ptr)
  5. RNA_id_pointer_create(&ID, &result->ptr):情報がいい感じに result に代入される
  6. return CTX_RESULT_OK:1 を返す

わからないこと:結局なんの意味があったのか

"edit_image" は、一致判定のあとは一切使われない(= result には代入されない)
→ コールバックを呼んだ側が result の中身と "edit_image" を 組み合わせて context に設定?

nikogolinikogoli
CTX_data_equals の定義

STREQは文字列比較らしい

source/blender/blenkernel/intern/context.c$623
bool CTX_data_equals(const char *member, const char *str)
{
  return (STREQ(member, str));
}
ED_space_image の定義
source/blender/editors/space_image/image_edit.c$55
Image *ED_space_image(SpaceImage *sima)
{
  return sima->image;
}
CTX_data_id_pointer_set の定義
source/blender/blenkernel/intern/context.c$633
void CTX_data_id_pointer_set(bContextDataResult *result, ID *id)
{
  RNA_id_pointer_create(id, &result->ptr);
}
RNA_id_pointer_create の定義
source/blender/makesrna/intern/rna_access.c$122
void RNA_id_pointer_create(ID *id, PointerRNA *r_ptr)
{
  StructRNA *type, *idtype = NULL;

  if (id) {
    PointerRNA tmp = {NULL};
    tmp.data = id;
    idtype = rna_ID_refine(&tmp);

    while (idtype->refine) {
      type = idtype->refine(&tmp);

      if (type == idtype) {
        break;
      }
      idtype = type;
    }
  }

  r_ptr->owner_id = id;
  r_ptr->type = idtype;
  r_ptr->data = id;
}
nikogolinikogoli

RNA_id_pointer_create(&ID, &result->ptr)について考える

  • 基本情報:StructRNA / PointerRNA / ID / bContextDataResult の定義
状態確認 その1
  • id(&ID): Space から取得した Image へのポインタ値 (ID へのポインタ値に変換されている)
  • r_ptr(&result -> ptr): bContextDataResult の メンバで PointerRNA 構造体
開始 ~ idtype = rna_ID_refine ...
  1. &StructRNA として typeidtype を定義し、NULL を代入
  2. id (つまり&ID) はあるので判定を通過
  3. PointerRNA として tmp を定義し、先頭要素に NULL を代入して全体を初期化(たぶん)
  4. temp.data (汎用ポインタ型)に id を代入
  5. idtyperna_ID_refine(&tmp) の返り値である RNA_Image (StructRNA型) のアドレス値が代入される
状態確認 その2
  • id (&ID):Space から取得した Image へのポインタ値 (ID へのポインタ値に変換されている)

  • r_ptr (&result->ptr):bContextDataResult の メンバで PointerRNA 構造体

  • temptemp.dataに &IDが代入され、それ以外は(たぶん)初期化された PointerRNA

  • type:StructRNA へのポインタ値で、値は NULL

  • idtype:StructRNA へのポインタ値で、値は &RNA_Image

  • RNA_Image:(Build 時において作成されると予想している) Image 用の初期設定の StructRNA

  • while ループ:RNA_Image.refine は NULL のはずなので、ループは実行されない
最後の3行
  1. result.ptrowner_id に、Space から取得した Image へのポインタ値を代入する
  2. result.ptrtype に、StructRNA へのポインタ値 &RNA_Imageを代入する
  3. result.ptrdata に、Space から取得した Image へのポインタ値を代入する
結果として実現する状態
  • Space (sima) から取得した imageに基づいて
  • 3つのメンバ owner_id, type, data の値が設定された PointerRNA へのポインタ値が
  • resultのメンバptrに代入されている
nikogolinikogoli

rna_ID_refine(&tmp) について考える

5行目:rna_ID_refine(&tmp) の中での処理
  • &ID:Space から取得した Image 構造体へのポインタ値 (構造体 ID へのポインタ値に変換されている)
  • temptemp.dataに &IDが代入され、それ以外は(たぶん)初期化された PointerRNA

  1. &tmp ->data経由で IDへの?ポインタ値を取得し、IDへのポインタ値に再キャストしてidに代入
  2. id->name)Image.nameを取得し、GS() によって *(&short name)と2回型変換することでnameを short 型にする
    → この変換で、Image.nameの最初の2文字(たぶん) "IM" を取り出している (下部参照)
  3. 取りだした2文字(name)を ID_Type にキャストし、ID_code_to_RNA_type(name)を実行して2文字を列挙型ID_Typeの各要素と比較する
  4. "IM" はID_IMと一致するので、&RNA_Image (StructRNA型のアドレス値)が返される
データの名前とタイプの判定

Code Doc. の ID Datablocks のページの説明

The name of the datablock (pointer->id.name) encodes the type of the datablock. For example, a mesh that is presented in the Blender user interface with the name "Suzanne" is actually named "MESuzanne" in the datablock. This can be used to cast an ID *id to the correct type. A function GS(name) is available to take such a name and return a constant that indicates the ID data type: if (GS(id->name) == ID_SC) … can be used to test whether the ID datablock is a Scene.

(訳) データブロックの名前(pointer->id.name)は、データブロックのタイプに変換される。たとえば、Blender の UI に「Suzanne」という名前で表示されるメッシュは、データブロックでは "MESuzanne" という名前になっている。このデータ名の性質は、ID *idとして提供されるデータを適切な type にキャストするために利用できる。その手段である関数 GS(name) は、上記のような名前を引数に取り ID データの type を示す定数を返す関数であり、例えば、 ("SC"のようなものである ID_SCを利用して) if (GS(id->name) == ID_SC)という処理を行うことで、ID データの type が Scene であるかどうかを判定できる。

ID_Type の定義部分にも、以下のようなコメントがある

ID from database.

Written to #BHead.code (for file IO)
and the first 2 bytes of #ID.name (for runtime checks, see #GS macro).

nikogolinikogoli
na_ID_refine の定義
source/blender/makesrna/intern/rna_ID.c$446
StructRNA *rna_ID_refine(PointerRNA *ptr)
{
  ID *id = (ID *)ptr->data;

  return ID_code_to_RNA_type(GS(id->name));
}
ID_code_to_RNA_typeの定義 省略あり
source/blender/makesrna/intern/rna_ID.c$342
StructRNA *ID_code_to_RNA_type(short idcode)
{
  switch ((ID_Type)idcode) {
    case ID_AC:
      return &RNA_Action;
    ...
    case ID_IM:
      return &RNA_Image;
    ...
  }

  return &RNA_ID;
}
GS() の定義
source/blender/makesdna/DNA_ID.h$460
#define GS(a) \
  (CHECK_TYPE_ANY(a, char *, const char *, char[66], const char[66]), \
   (ID_Type)(*((const short *)(a))))

CHECK_TYPE_ANYの定義

nikogolinikogoli

メンバとしての refine と、デフォルトとしての StructRNA の作成 (未整理)

RNA_def_image の定義
source/blender/makesrna/intern/rna_image.c$1173
void RNA_def_image(BlenderRNA *brna)
{
  rna_def_render_slot(brna);
  rna_def_udim_tile(brna);
  rna_def_image(brna);
  rna_def_imageuser(brna);
  rna_def_image_packed_files(brna);
}
RNA_def_space の定義 (一部省略)
source/blender/makesrna/intern/rna_space.c$7174
void RNA_def_space(BlenderRNA *brna)
{
  rna_def_space(brna);
  rna_def_space_image(brna);
  rna_def_space_sequencer(brna);
  ...
}
rna_def_space の定義 (一部省略)
source/blender/makesrna/intern/rna_space.c$3040
static void rna_def_space(BlenderRNA *brna)
{
  StructRNA *srna;
  PropertyRNA *prop;

  srna = RNA_def_struct(brna, "Space", NULL);
  RNA_def_struct_sdna(srna, "SpaceLink");
  RNA_def_struct_ui_text(srna, "Space", "Space data for a screen area");
  RNA_def_struct_refine_func(srna, "rna_Space_refine");

  prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
  RNA_def_property_enum_sdna(prop, NULL, "spacetype");
  RNA_def_property_enum_items(prop, rna_enum_space_type_items);
  RNA_def_property_clear_flag(prop, PROP_EDITABLE);
  RNA_def_property_ui_text(prop, "Type", "Space data type");
  ...
}
BlenderRNA の定義
source/blender/makesrna/intern/rna_internal_types.h$572
struct BlenderRNA {
  ListBase structs;
  /* A map of structs: {StructRNA.identifier -> StructRNA}
   * These are ensured to have unique names (with STRUCT_PUBLIC_NAMESPACE enabled). */
  struct GHash *structs_map;
  /* Needed because types with an empty identifier aren't included in 'structs_map'. */
  unsigned int structs_len;
};

RNA_def_struct の定義
source/blender/makesrna/intern/rna_define.c$1047
StructRNA *RNA_def_struct(BlenderRNA *brna, const char *identifier, const char *from)
{
  StructRNA *srnafrom = NULL;

  /* only use RNA_def_struct() while pre-processing, otherwise use RNA_def_struct_ptr() */
  BLI_assert(DefRNA.preprocess);

  if (from) {
    /* find struct to derive from */
    /* Inline RNA_struct_find(...) because it wont link from here. */
    srnafrom = BLI_ghash_lookup(brna->structs_map, from);
    if (!srnafrom) {
      CLOG_ERROR(&LOG, "struct %s not found to define %s.", from, identifier);
      DefRNA.error = true;
    }
  }

  return RNA_def_struct_ptr(brna, identifier, srnafrom);
}

RNA_def_struct_ptrの定義srnafrom = NULLの場合は、identifierを設定しつつデフォルトのプロパティを設定した StructRNA を返すっぽい


RNA_def_struct_refine_func
source/blender/makesrna/intern/rna_define.c$1167
void RNA_def_struct_refine_func(StructRNA *srna, const char *refine)
{
  if (!DefRNA.preprocess) {
    CLOG_ERROR(&LOG, "only during preprocessing.");
    return;
  }

  if (refine) {
    srna->refine = (StructRefineFunc)refine;
  }
}
StructRefineFunc の定義
source/blender/makesrna/intern/rna_internal_types.h$58
typedef struct StructRNA *(*StructRefineFunc)(struct PointerRNA *ptr);

PointerRNA へのポインタ値を引数にとって StructRNA へのポインタへの値を返す関数ポインタ

rna_Space_refine の定義 (一部省略)
source/blender/makesrna/intern/rna_space.c$539
static StructRNA *rna_Space_refine(struct PointerRNA *ptr)
{
  SpaceLink *space = (SpaceLink *)ptr->data;

  switch ((eSpace_Type)space->spacetype) {
    case SPACE_VIEW3D:
      return &RNA_SpaceView3D;
    case SPACE_GRAPH:
      return &RNA_SpaceGraphEditor;
    case SPACE_OUTLINER:
      return &RNA_SpaceOutliner;
    ...
      /* Currently no type info. */
    case SPACE_SCRIPT:
    case SPACE_EMPTY:
    case SPACE_TOPBAR:
    case SPACE_STATUSBAR:
      break;
  }

  return &RNA_Space;
}
nikogolinikogoli

Build において、RNA_def_space() によってRNA_Spaceが作成されるとする

  1. RNA_def_space(&BlenderRNA)の中で、rna_def_space(&BlenderRNA)などが実行される
  2. rna_def_space(&BlenderRNA)の中で、Space 用の StrcutRNA が BlenderRNA から作成される
  3. この StrcutRNA に対し、RNA_def_struct_refine_func(StrcutRNA, "rna_Space_refine")が実行される
  4. RNA_def_struct_refine_funcの中で、文字列が StructRefineFunc 型(の関数ポインタ型?)にキャストされ、StrcutRNA.refine = rna_Space_refineとして代入される
    (ほんとに? 型変換だけでこんなことできるの?)
  5. その他のあれこれが行われる


もし、idtype = rna_ID_refine(&tmp)によって idtype に &RNA_Space が代入された場合

  1. idtype->refineが存在するので判定を通過
  2. idtype->refine(&tmp)が実行されると、type = rna_Space_refine(&PointerRNA)が実行される
  3. SpaceLink *space = (SpaceLink *)ptr->dataによって、PointerRNA の data メンバである ID が SpaceLink へのポインタにキャストされる
    → (この場合は)結局、引っ張ってきた Space のデータの SpaceLink を取得することになる
  4. SpaceLink のメンバspacetypeを使って判定を行い、該当するサブタイプの Space の PointerRNA &RNA_SpaceView3Dが返されることになり、これにidtypeを更新する
  5. 一致する case がない場合はベースのタイプが返されるので、idtype->refineが存在する場合、最終的に必ずtype == idtypeとなり、 while ループが終わる

要するにrefineはサブタイプを持つ構造体において、ベースからサブタイプを絞るためのもの[1]

  • rna_ID_refine:データ名の接頭辞を使って StructRNA を取ってきて、ベースタイプを規定
  • refine:データのタイプを使って StructRNA を取ってきて、サブタイプを規定
        ↓
    サブタイプがなければrefineは必要がないので、idtype->refine は False になりうる
        ↓
    Image:サブタイプがないのでidtype->refineは初期値 NULL のまま設定されない
脚注
  1. Struct 定義でのrefine部分のコメント:"function to give the more specific type" ↩︎

nikogolinikogoli
CTX_wm_space_image の定義
source/blender/blenkernel/intern/context.c
struct SpaceImage *CTX_wm_space_image(const bContext *C)
{
  ScrArea *area = CTX_wm_area(C);
  if (area && area->spacetype == SPACE_IMAGE) {
    return area->spacedata.first;
  }
  return NULL;
}
CTX_wm_area の定義
source/blender/blenkernel/intern/context.c
ScrArea *CTX_wm_area(const bContext *C)
{
  return ctx_wm_python_context_get(C, "area", &RNA_Area, C->wm.area);
}
ctx_wm_python_context_get の定義
source/blender/blenkernel/intern/context.c$256
static void *ctx_wm_python_context_get(const bContext *C,
                                       const char *member,
                                       const StructRNA *member_type,
                                       void *fall_through)
{
#ifdef WITH_PYTHON
  if (UNLIKELY(C && CTX_py_dict_get(C))) {
    bContextDataResult result;
    memset(&result, 0, sizeof(bContextDataResult));
    BPY_context_member_get((bContext *)C, member, &result);

    if (result.ptr.data) {
      if (RNA_struct_is_a(result.ptr.type, member_type)) {
        return result.ptr.data;
      }

      CLOG_WARN(&LOG,
                "PyContext '%s' is a '%s', expected a '%s'",
                member,
                RNA_struct_identifier(result.ptr.type),
                RNA_struct_identifier(member_type));
    }
  }
#else
  UNUSED_VARS(C, member, member_type);
#endif

  /* don't allow UI context access from non-main threads */
  if (!BLI_thread_is_main()) {
    return NULL;
  }

  return fall_through;
}

BPY_context_member_get の定義 一部省略
source/blender/python/intern/bpy_interface.c$660
int BPY_context_member_get(bContext *C, const char *member, bContextDataResult *result)
{
  PyObject *pyctx;
  PyObject *item;
  PointerRNA *ptr = NULL;
  bool done = false;

  pyctx = (PyObject *)CTX_py_dict_get(C);
  item = PyDict_GetItemString(pyctx, member);

  if (item == NULL) {
    /* pass */
  }
  else if (item == Py_None) {
    done = true;
  }
  else if (BPy_StructRNA_Check(item)) {
    ptr = &(((BPy_StructRNA *)item)->ptr);
    CTX_data_pointer_set(result, ptr->owner_id, ptr->type, ptr->data);
    CTX_data_type_set(result, CTX_DATA_TYPE_POINTER);
    done = true;
  }
  else if (PySequence_Check(item)) {
    PyObject *seq_fast = PySequence_Fast(item, "bpy_context_get sequence conversion");
    if (seq_fast == NULL) {
      PyErr_Print();
      PyErr_Clear();
    }
    else {
      const int len = PySequence_Fast_GET_SIZE(seq_fast);
      PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast);
      int i;

      for (i = 0; i < len; i++) {
        PyObject *list_item = seq_fast_items[i];

        if (BPy_StructRNA_Check(list_item)) {
          ptr = &(((BPy_StructRNA *)list_item)->ptr);
          CTX_data_list_add(result, ptr->owner_id, ptr->type, ptr->data);
        }
        else {
          CLOG_INFO(BPY_LOG_CONTEXT,
                    1,
                    "'%s' list item not a valid type in sequence type '%s'",
                    member,
                    Py_TYPE(item)->tp_name);
        }
      }
      Py_DECREF(seq_fast);
      CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
      done = true;
    }
  }
nikogolinikogoli

ctx_wm_python_context_get(C, "area", &RNA_Area, C->wm.area)

  1. CTX_py_dict_get(C)C->data.py_contextの有無の判定
    (UNLIKELY なので普通は NULL ではないらしい)
  2. bContextDataResult 型の result を作成し、memset で初期化
  3. BPY_context_member_get((bContext *)C, member, &result):?
  4. result.ptr.dataの有無を判定・・・通過した場合を考える
  5. RNA_struct_is_a(result.ptr.type, member_type)result.ptr.typeが &RNA_Area と一致するか判定
  6. 判定を通過すれば、result.ptr.dataを返して終了
  7. もし、いろいろな判定に失敗した場合は、C->wm.areaを返して終了
nikogolinikogoli
CTX_py_dict_get の定義
source/blender/blenkernel/intern/context.c$224
void *CTX_py_dict_get(const bContext *C)
{
  return C->data.py_context;
}
RNA_struct_is_a の定義
source/blender/makesrna/intern/rna_access.c$844
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
{
  const StructRNA *base;

  if (srna == &RNA_AnyType) {
    return true;
  }

  if (!type) {
    return false;
  }

  /* ptr->type is always maximally refined */
  for (base = type; base; base = base->base) {
    if (base == srna) {
      return true;
    }
  }

  return false;
}
BPY_context_member_get の定義
source/blender/python/intern/bpy_interface.c$660
int BPY_context_member_get(bContext *C, const char *member, bContextDataResult *result)
{
  PyGILState_STATE gilstate;
  const bool use_gil = !PyC_IsInterpreterActive();

  PyObject *pyctx;
  PyObject *item;
  PointerRNA *ptr = NULL;
  bool done = false;

  if (use_gil) {
    gilstate = PyGILState_Ensure();
  }

  pyctx = (PyObject *)CTX_py_dict_get(C);
  item = PyDict_GetItemString(pyctx, member);

  if (item == NULL) {
    /* pass */
  }
  else if (item == Py_None) {
    done = true;
  }
  else if (BPy_StructRNA_Check(item)) {
    ptr = &(((BPy_StructRNA *)item)->ptr);

    // result->ptr = ((BPy_StructRNA *)item)->ptr;
    CTX_data_pointer_set(result, ptr->owner_id, ptr->type, ptr->data);
    CTX_data_type_set(result, CTX_DATA_TYPE_POINTER);
    done = true;
  }
  else if (PySequence_Check(item)) {
    PyObject *seq_fast = PySequence_Fast(item, "bpy_context_get sequence conversion");
    if (seq_fast == NULL) {
      PyErr_Print();
      PyErr_Clear();
    }
    else {
      const int len = PySequence_Fast_GET_SIZE(seq_fast);
      PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast);
      int i;

      for (i = 0; i < len; i++) {
        PyObject *list_item = seq_fast_items[i];

        if (BPy_StructRNA_Check(list_item)) {
#if 0
          CollectionPointerLink *link = MEM_callocN(sizeof(CollectionPointerLink),
                                                    "bpy_context_get");
          link->ptr = ((BPy_StructRNA *)item)->ptr;
          BLI_addtail(&result->list, link);
#endif
          ptr = &(((BPy_StructRNA *)list_item)->ptr);
          CTX_data_list_add(result, ptr->owner_id, ptr->type, ptr->data);
        }
        else {
          CLOG_INFO(BPY_LOG_CONTEXT,
                    1,
                    "'%s' list item not a valid type in sequence type '%s'",
                    member,
                    Py_TYPE(item)->tp_name);
        }
      }
      Py_DECREF(seq_fast);
      CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
      done = true;
    }
  }

  if (done == false) {
    if (item) {
      CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not a valid type", member);
    }
    else {
      CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not found\n", member);
    }
  }
  else {
    CLOG_INFO(BPY_LOG_CONTEXT, 2, "'%s' found", member);
  }

  if (use_gil) {
    PyGILState_Release(gilstate);
  }

  return done;
}
nikogolinikogoli

BPY_context_member_get((bContext *)C, member, &result) を考える

  • C:コールバック呼び出し側から与えられた bContext 構造体へのポインタ値
  • member:"area"
  • resultctx_wm_python_context_getが作成した、初期状態の bContextDataResult

  1. pyctxC->data.py_context(PyObject へのポインタ型)を代入
  2. PyDict_GetItemString:pyctxから、キー"area"で値を取り出し、item(PyObject へのポインタ型)に代入
  3. itemのチェック:NULL やPy_Noneならば、メッセージを出すなりして終了
  4. BPy_StructRNA_Check(item)のチェック:pyrna_struct_Typeの type/subtype であるかどうかをチェック
    なにもわからないが、適切な対象で非リストなものがチェックを通過するっぽい

通過した場合

  1. あれこれ変換しているが、結局item.ptrのアドレス値を、ptr(PointerRNA へのポインタ型)に代入する
  2. CTX_data_pointer_setRNA_pointer_create(ptr->owner_id, ptr->type, ptr->data, &result->ptr)
  3. 結局のところ、item.ptrowner_idtypedataを、そのままresult->ptrに代入している
  4. さらにresult->ptr->type->refineが NULL でないか、つまりサブタイプがあるかを確認し、サブタイプがあるものならrefine(ptr)を実行してptr->typeをサブタイプに変更

RNA_id_pointer_create との差異:rna_ID_refine() するかどうか

つまり、データ名からベースのタイプの情報を規定するための StructRNA が必要かどうか
  ↓
「元データ」が、ID、つまり StructRNA でないならrna_ID_refine()が必要だが、「元データ」が StructRNA ならそのまま流用でき、サブタイプのチェックのみが必要ってこと?

nikogolinikogoli
細かい定義の情報
source/blender/python/intern/bpy_rna.h$80
#define BPy_StructRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_struct_Type))
nikogolinikogoli
CTX_data_pointer_set の定義 と RNA_pointer_createの定義(コメント省略)
source/blender/blenkernel/intern/context.c$638
void CTX_data_pointer_set(bContextDataResult *result, ID *id, StructRNA *type, void *data)
{
  RNA_pointer_create(id, type, data, &result->ptr);
}
source/blender/makesrna/intern/rna_access.c$146
void RNA_pointer_create(ID *id, StructRNA *type, void *data, PointerRNA *r_ptr)
{
  r_ptr->owner_id = id;
  r_ptr->type = type;
  r_ptr->data = data;

  if (data) {
    while (r_ptr->type && r_ptr->type->refine) {
      StructRNA *rtype = r_ptr->type->refine(r_ptr);

      if (rtype == r_ptr->type) {
        break;
      }
      r_ptr->type = rtype;
    }
  }
}
nikogolinikogoli

◇ コールバックが呼ばれるまで

なにか → CTX_data_active_objectとか → ctx_data_pointer_get →
 → member で指定して ctx_data_get → 指定した member で各階層のコールバックが呼ばれる