main sync #3

Merged
Patrick Busch merged 318 commits from blender/blender:main into main 2023-03-17 15:52:21 +01:00
8 changed files with 218 additions and 28 deletions
Showing only changes of commit 46be42f6b1 - Show all commits

View File

@ -83,8 +83,10 @@ typedef enum {
BKE_CB_EVT_RENDER_CANCEL, BKE_CB_EVT_RENDER_CANCEL,
BKE_CB_EVT_LOAD_PRE, BKE_CB_EVT_LOAD_PRE,
BKE_CB_EVT_LOAD_POST, BKE_CB_EVT_LOAD_POST,
BKE_CB_EVT_LOAD_POST_FAIL,
BKE_CB_EVT_SAVE_PRE, BKE_CB_EVT_SAVE_PRE,
BKE_CB_EVT_SAVE_POST, BKE_CB_EVT_SAVE_POST,
BKE_CB_EVT_SAVE_POST_FAIL,
BKE_CB_EVT_UNDO_PRE, BKE_CB_EVT_UNDO_PRE,
BKE_CB_EVT_UNDO_POST, BKE_CB_EVT_UNDO_POST,
BKE_CB_EVT_REDO_PRE, BKE_CB_EVT_REDO_PRE,
@ -123,6 +125,7 @@ void BKE_callback_exec_id_depsgraph(struct Main *bmain,
struct ID *id, struct ID *id,
struct Depsgraph *depsgraph, struct Depsgraph *depsgraph,
eCbEvent evt); eCbEvent evt);
void BKE_callback_exec_string(struct Main *bmain, eCbEvent evt, const char *str);
void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt); void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt);
void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt); void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt);

View File

@ -69,6 +69,18 @@ void BKE_callback_exec_id_depsgraph(struct Main *bmain,
BKE_callback_exec(bmain, pointers, 2, evt); BKE_callback_exec(bmain, pointers, 2, evt);
} }
void BKE_callback_exec_string(struct Main *bmain, eCbEvent evt, const char *str)
{
PointerRNA str_ptr;
PrimitiveStringRNA data = {};
data.value = str;
RNA_pointer_create(NULL, &RNA_PrimitiveString, &data, &str_ptr);
PointerRNA *pointers[1] = {&str_ptr};
BKE_callback_exec(bmain, pointers, 1, evt);
}
void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt) void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt)
{ {
ASSERT_CALLBACKS_INITIALIZED(); ASSERT_CALLBACKS_INITIALIZED();

View File

@ -774,6 +774,24 @@ typedef struct ExtensionRNA {
StructFreeFunc free; StructFreeFunc free;
} ExtensionRNA; } ExtensionRNA;
/* Primitive types. */
typedef struct PrimitiveStringRNA {
const char *value;
} PrimitiveStringRNA;
typedef struct PrimitiveIntRNA {
int value;
} PrimitiveIntRNA;
typedef struct PrimitiveFloatRNA {
float value;
} PrimitiveFloatRNA;
typedef struct PrimitiveBooleanRNA {
bool value;
} PrimitiveBooleanRNA;
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -2775,6 +2775,52 @@ bool rna_property_override_apply_default(Main *bmain,
# undef RNA_PROPERTY_GET_SINGLE # undef RNA_PROPERTY_GET_SINGLE
# undef RNA_PROPERTY_SET_SINGLE # undef RNA_PROPERTY_SET_SINGLE
/** \} */
/* -------------------------------------------------------------------- */
/** \name Primitive Values
* \{ */
/* Primitive String. */
static void rna_PrimitiveString_value_get(PointerRNA *ptr, char *result)
{
const PrimitiveStringRNA *data = ptr->data;
strcpy(result, data->value ? data->value : "");
}
static int rna_PrimitiveString_value_length(PointerRNA *ptr)
{
const PrimitiveStringRNA *data = ptr->data;
return data->value ? strlen(data->value) : 0;
}
/* Primitive Int. */
static int rna_PrimitiveInt_value_get(PointerRNA *ptr)
{
const PrimitiveIntRNA *data = ptr->data;
return data->value;
}
/* Primitive Float. */
static float rna_PrimitiveFloat_value_get(PointerRNA *ptr)
{
const PrimitiveFloatRNA *data = ptr->data;
return data->value;
}
/* Primitive Boolean. */
static bool rna_PrimitiveBoolean_value_get(PointerRNA *ptr)
{
const PrimitiveBooleanRNA *data = ptr->data;
return data->value;
}
/** \} */
#else #else
static void rna_def_struct(BlenderRNA *brna) static void rna_def_struct(BlenderRNA *brna)
@ -3367,6 +3413,40 @@ static void rna_def_pointer_property(StructRNA *srna, PropertyType type)
RNA_def_property_ui_text(prop, "Pointer Type", "Fixed pointer type, empty if variable type"); RNA_def_property_ui_text(prop, "Pointer Type", "Fixed pointer type, empty if variable type");
} }
static void rna_def_rna_primitive(BlenderRNA *brna)
{
/* Primitive Values, use when passing #PointerRNA is used for primitive types.
* For the rare cases we want to pass a value as RNA which wraps a primitive data. */
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "PrimitiveString", NULL);
RNA_def_struct_ui_text(srna, "String Value", "RNA wrapped string");
prop = RNA_def_property(srna, "value", PROP_STRING, PROP_BYTESTRING);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_string_funcs(
prop, "rna_PrimitiveString_value_get", "rna_PrimitiveString_value_length", NULL);
srna = RNA_def_struct(brna, "PrimitiveInt", NULL);
RNA_def_struct_ui_text(srna, "Primitive Int", "RNA wrapped int");
prop = RNA_def_property(srna, "value", PROP_INT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_int_funcs(prop, "rna_PrimitiveInt_value_get", NULL, NULL);
srna = RNA_def_struct(brna, "PrimitiveFloat", NULL);
RNA_def_struct_ui_text(srna, "Primitive Float", "RNA wrapped float");
prop = RNA_def_property(srna, "value", PROP_FLOAT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_float_funcs(prop, "rna_PrimitiveFloat_value_get", NULL, NULL);
srna = RNA_def_struct(brna, "PrimitiveBoolean", NULL);
RNA_def_struct_ui_text(srna, "Primitive Boolean", "RNA wrapped boolean");
prop = RNA_def_property(srna, "value", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_funcs(prop, "rna_PrimitiveBoolean_value_get", NULL);
}
void RNA_def_rna(BlenderRNA *brna) void RNA_def_rna(BlenderRNA *brna)
{ {
StructRNA *srna; StructRNA *srna;
@ -3451,6 +3531,8 @@ void RNA_def_rna(BlenderRNA *brna)
# endif # endif
RNA_def_property_ui_text(prop, "Structs", ""); RNA_def_property_ui_text(prop, "Structs", "");
rna_def_rna_primitive(brna);
} }
#endif #endif

View File

@ -14,7 +14,9 @@
#include "BKE_callbacks.h" #include "BKE_callbacks.h"
#include "RNA_access.h" #include "RNA_access.h"
#include "RNA_prototypes.h"
#include "RNA_types.h" #include "RNA_types.h"
#include "bpy_app_handlers.h" #include "bpy_app_handlers.h"
#include "bpy_rna.h" #include "bpy_rna.h"
@ -29,6 +31,13 @@ void bpy_app_generic_callback(struct Main *main,
static PyTypeObject BlenderAppCbType; static PyTypeObject BlenderAppCbType;
#define FILEPATH_SAVE_ARG \
"Accepts one argument: " \
"the file being saved, an empty string for the startup-file."
#define FILEPATH_LOAD_ARG \
"Accepts one argument: " \
"the file being loaded, an empty string for the startup-file."
/** /**
* See `BKE_callbacks.h` #eCbEvent declaration for the policy on naming. * See `BKE_callbacks.h` #eCbEvent declaration for the policy on naming.
*/ */
@ -50,10 +59,15 @@ static PyStructSequence_Field app_cb_info_fields[] = {
{"render_init", "on initialization of a render job"}, {"render_init", "on initialization of a render job"},
{"render_complete", "on completion of render job"}, {"render_complete", "on completion of render job"},
{"render_cancel", "on canceling a render job"}, {"render_cancel", "on canceling a render job"},
{"load_pre", "on loading a new blend file (before)"},
{"load_post", "on loading a new blend file (after)"}, {"load_pre", "on loading a new blend file (before)." FILEPATH_LOAD_ARG},
{"save_pre", "on saving a blend file (before)"}, {"load_post", "on loading a new blend file (after). " FILEPATH_LOAD_ARG},
{"save_post", "on saving a blend file (after)"}, {"load_post_fail", "on failure to load a new blend file (after). " FILEPATH_LOAD_ARG},
{"save_pre", "on saving a blend file (before). " FILEPATH_SAVE_ARG},
{"save_post", "on saving a blend file (after). " FILEPATH_SAVE_ARG},
{"save_post_fail", "on failure to save a blend file (after). " FILEPATH_SAVE_ARG},
{"undo_pre", "on loading an undo step (before)"}, {"undo_pre", "on loading an undo step (before)"},
{"undo_post", "on loading an undo step (after)"}, {"undo_post", "on loading an undo step (after)"},
{"redo_pre", "on loading a redo step (before)"}, {"redo_pre", "on loading a redo step (before)"},
@ -344,7 +358,8 @@ void bpy_app_generic_callback(struct Main *UNUSED(main),
/* setup arguments */ /* setup arguments */
for (int i = 0; i < pointers_num; ++i) { for (int i = 0; i < pointers_num; ++i) {
PyTuple_SET_ITEM(args_all, i, pyrna_struct_CreatePyObject(pointers[i])); PyTuple_SET_ITEM(
args_all, i, pyrna_struct_CreatePyObject_with_primitive_support(pointers[i]));
} }
for (int i = pointers_num; i < num_arguments; ++i) { for (int i = pointers_num; i < num_arguments; ++i) {
PyTuple_SET_ITEM(args_all, i, Py_INCREF_RET(Py_None)); PyTuple_SET_ITEM(args_all, i, Py_INCREF_RET(Py_None));
@ -354,7 +369,8 @@ void bpy_app_generic_callback(struct Main *UNUSED(main),
PyTuple_SET_ITEM(args_single, 0, Py_INCREF_RET(Py_None)); PyTuple_SET_ITEM(args_single, 0, Py_INCREF_RET(Py_None));
} }
else { else {
PyTuple_SET_ITEM(args_single, 0, pyrna_struct_CreatePyObject(pointers[0])); PyTuple_SET_ITEM(
args_single, 0, pyrna_struct_CreatePyObject_with_primitive_support(pointers[0]));
} }
/* Iterate the list and run the callbacks /* Iterate the list and run the callbacks

View File

@ -7407,6 +7407,27 @@ PyObject *pyrna_struct_CreatePyObject(PointerRNA *ptr)
return (PyObject *)pyrna; return (PyObject *)pyrna;
} }
PyObject *pyrna_struct_CreatePyObject_with_primitive_support(PointerRNA *ptr)
{
if (ptr->type == &RNA_PrimitiveString) {
const PrimitiveStringRNA *data = ptr->data;
return PyC_UnicodeFromBytes(data->value);
}
if (ptr->type == &RNA_PrimitiveInt) {
const PrimitiveIntRNA *data = ptr->data;
return PyLong_FromLong(data->value);
}
if (ptr->type == &RNA_PrimitiveFloat) {
const PrimitiveFloatRNA *data = ptr->data;
return PyFloat_FromDouble(data->value);
}
if (ptr->type == &RNA_PrimitiveBoolean) {
const PrimitiveBooleanRNA *data = ptr->data;
return PyBool_FromLong(data->value);
}
return pyrna_struct_CreatePyObject(ptr);
}
PyObject *pyrna_prop_CreatePyObject(PointerRNA *ptr, PropertyRNA *prop) PyObject *pyrna_prop_CreatePyObject(PointerRNA *ptr, PropertyRNA *prop)
{ {
BPy_PropertyRNA *pyrna; BPy_PropertyRNA *pyrna;

View File

@ -173,6 +173,7 @@ void BPY_update_rna_module(void);
// PyObject *BPY_rna_doc(void); // PyObject *BPY_rna_doc(void);
PyObject *BPY_rna_types(void); PyObject *BPY_rna_types(void);
PyObject *pyrna_struct_CreatePyObject_with_primitive_support(PointerRNA *ptr);
PyObject *pyrna_struct_CreatePyObject(PointerRNA *ptr); PyObject *pyrna_struct_CreatePyObject(PointerRNA *ptr);
PyObject *pyrna_prop_CreatePyObject(PointerRNA *ptr, PropertyRNA *prop); PyObject *pyrna_prop_CreatePyObject(PointerRNA *ptr, PropertyRNA *prop);

View File

@ -632,10 +632,9 @@ void wm_file_read_report(bContext *C, Main *bmain)
* \note In the case of #WM_file_read the file may fail to load. * \note In the case of #WM_file_read the file may fail to load.
* Change here shouldn't cause user-visible changes in that case. * Change here shouldn't cause user-visible changes in that case.
*/ */
static void wm_file_read_pre(bContext *C, bool use_data, bool /*use_userdef*/) static void wm_file_read_pre(bool use_data, bool /*use_userdef*/)
{ {
if (use_data) { if (use_data) {
BKE_callback_exec_null(CTX_data_main(C), BKE_CB_EVT_LOAD_PRE);
BLI_timer_on_file_load(); BLI_timer_on_file_load();
} }
@ -656,6 +655,10 @@ struct wmFileReadPost_Params {
uint is_startup_file : 1; uint is_startup_file : 1;
uint is_factory_startup : 1; uint is_factory_startup : 1;
uint reset_app_template : 1; uint reset_app_template : 1;
/* Used by #wm_homefile_read_post */
uint success : 1;
uint is_alloc : 1;
}; };
/** /**
@ -747,7 +750,6 @@ static void wm_file_read_post(bContext *C, const struct wmFileReadPost_Params *p
if (use_data) { if (use_data) {
/* important to do before nullptr'ing the context */ /* important to do before nullptr'ing the context */
BKE_callback_exec_null(bmain, BKE_CB_EVT_VERSION_UPDATE); BKE_callback_exec_null(bmain, BKE_CB_EVT_VERSION_UPDATE);
BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_POST);
if (is_factory_startup) { if (is_factory_startup) {
BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST); BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST);
} }
@ -944,6 +946,10 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
const bool use_data = true; const bool use_data = true;
const bool use_userdef = false; const bool use_userdef = false;
/* NOTE: either #BKE_CB_EVT_LOAD_POST or #BKE_CB_EVT_LOAD_POST_FAIL must run.
* Runs at the end of this function, don't return beforehand. */
BKE_callback_exec_string(CTX_data_main(C), BKE_CB_EVT_LOAD_PRE, filepath);
/* so we can get the error message */ /* so we can get the error message */
errno = 0; errno = 0;
@ -968,7 +974,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
bf_reports.duration.whole = PIL_check_seconds_timer(); bf_reports.duration.whole = PIL_check_seconds_timer();
struct BlendFileData *bfd = BKE_blendfile_read(filepath, &params, &bf_reports); struct BlendFileData *bfd = BKE_blendfile_read(filepath, &params, &bf_reports);
if (bfd != nullptr) { if (bfd != nullptr) {
wm_file_read_pre(C, use_data, use_userdef); wm_file_read_pre(use_data, use_userdef);
/* Put aside screens to match with persistent windows later, /* Put aside screens to match with persistent windows later,
* also exit screens and editors. */ * also exit screens and editors. */
@ -1004,6 +1010,8 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
read_file_post_params.is_startup_file = false; read_file_post_params.is_startup_file = false;
read_file_post_params.is_factory_startup = false; read_file_post_params.is_factory_startup = false;
read_file_post_params.reset_app_template = false; read_file_post_params.reset_app_template = false;
read_file_post_params.success = true;
read_file_post_params.is_alloc = false;
wm_file_read_post(C, &read_file_post_params); wm_file_read_post(C, &read_file_post_params);
bf_reports.duration.whole = PIL_check_seconds_timer() - bf_reports.duration.whole; bf_reports.duration.whole = PIL_check_seconds_timer() - bf_reports.duration.whole;
@ -1048,7 +1056,11 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
WM_cursor_wait(false); WM_cursor_wait(false);
BLI_assert(BKE_main_namemap_validate(CTX_data_main(C))); Main *bmain = CTX_data_main(C);
BKE_callback_exec_string(
bmain, success ? BKE_CB_EVT_LOAD_POST : BKE_CB_EVT_LOAD_POST_FAIL, filepath);
BLI_assert(BKE_main_namemap_validate(bmain));
return success; return success;
} }
@ -1178,10 +1190,16 @@ void wm_homefile_read_ex(bContext *C,
#endif /* WITH_PYTHON */ #endif /* WITH_PYTHON */
} }
if (use_data) {
/* NOTE: either #BKE_CB_EVT_LOAD_POST or #BKE_CB_EVT_LOAD_POST_FAIL must run.
* This runs from #wm_homefile_read_post. */
BKE_callback_exec_string(CTX_data_main(C), BKE_CB_EVT_LOAD_PRE, "");
}
/* For regular file loading this only runs after the file is successfully read. /* For regular file loading this only runs after the file is successfully read.
* In the case of the startup file, the in-memory startup file is used as a fallback * In the case of the startup file, the in-memory startup file is used as a fallback
* so we know this will work if all else fails. */ * so we know this will work if all else fails. */
wm_file_read_pre(C, use_data, use_userdef); wm_file_read_pre(use_data, use_userdef);
if (use_data) { if (use_data) {
/* put aside screens to match with persistent windows later */ /* put aside screens to match with persistent windows later */
@ -1392,10 +1410,14 @@ void wm_homefile_read_ex(bContext *C,
params_file_read_post.is_factory_startup = is_factory_startup; params_file_read_post.is_factory_startup = is_factory_startup;
params_file_read_post.reset_app_template = reset_app_template; params_file_read_post.reset_app_template = reset_app_template;
params_file_read_post.success = success;
params_file_read_post.is_alloc = false;
if (r_params_file_read_post == nullptr) { if (r_params_file_read_post == nullptr) {
wm_file_read_post(C, &params_file_read_post); wm_homefile_read_post(C, &params_file_read_post);
} }
else { else {
params_file_read_post.is_alloc = true;
*r_params_file_read_post = static_cast<wmFileReadPost_Params *>( *r_params_file_read_post = static_cast<wmFileReadPost_Params *>(
MEM_mallocN(sizeof(wmFileReadPost_Params), __func__)); MEM_mallocN(sizeof(wmFileReadPost_Params), __func__));
**r_params_file_read_post = params_file_read_post; **r_params_file_read_post = params_file_read_post;
@ -1417,8 +1439,18 @@ void wm_homefile_read_post(struct bContext *C,
const struct wmFileReadPost_Params *params_file_read_post) const struct wmFileReadPost_Params *params_file_read_post)
{ {
wm_file_read_post(C, params_file_read_post); wm_file_read_post(C, params_file_read_post);
if (params_file_read_post->use_data) {
BKE_callback_exec_string(CTX_data_main(C),
params_file_read_post->success ? BKE_CB_EVT_LOAD_POST :
BKE_CB_EVT_LOAD_POST_FAIL,
"");
}
if (params_file_read_post->is_alloc) {
MEM_freeN((void *)params_file_read_post); MEM_freeN((void *)params_file_read_post);
} }
}
/** \} */ /** \} */
@ -1820,7 +1852,10 @@ static bool wm_file_write(bContext *C,
/* Call pre-save callbacks before writing preview, /* Call pre-save callbacks before writing preview,
* that way you can generate custom file thumbnail. */ * that way you can generate custom file thumbnail. */
BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE);
/* NOTE: either #BKE_CB_EVT_SAVE_POST or #BKE_CB_EVT_SAVE_POST_FAIL must run.
* Runs at the end of this function, don't return beforehand. */
BKE_callback_exec_string(bmain, BKE_CB_EVT_SAVE_PRE, filepath);
ED_assets_pre_save(bmain); ED_assets_pre_save(bmain);
/* Enforce full override check/generation on file save. */ /* Enforce full override check/generation on file save. */
@ -1906,8 +1941,6 @@ static bool wm_file_write(bContext *C,
wm_history_file_update(); wm_history_file_update();
} }
BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_POST);
/* run this function after because the file can't be written before the blend is */ /* run this function after because the file can't be written before the blend is */
if (ibuf_thumb) { if (ibuf_thumb) {
IMB_thumb_delete(filepath, THB_FAIL); /* without this a failed thumb overrides */ IMB_thumb_delete(filepath, THB_FAIL); /* without this a failed thumb overrides */
@ -1921,6 +1954,8 @@ static bool wm_file_write(bContext *C,
ok = true; ok = true;
} }
BKE_callback_exec_string(bmain, ok ? BKE_CB_EVT_SAVE_POST : BKE_CB_EVT_SAVE_POST_FAIL, filepath);
if (ibuf_thumb) { if (ibuf_thumb) {
IMB_freeImBuf(ibuf_thumb); IMB_freeImBuf(ibuf_thumb);
} }
@ -2154,7 +2189,9 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED; return OPERATOR_CANCELLED;
} }
BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE); /* NOTE: either #BKE_CB_EVT_SAVE_POST or #BKE_CB_EVT_SAVE_POST_FAIL must run.
* Runs at the end of this function, don't return beforehand. */
BKE_callback_exec_string(bmain, BKE_CB_EVT_SAVE_PRE, "");
ED_assets_pre_save(bmain); ED_assets_pre_save(bmain);
/* check current window and close it if temp */ /* check current window and close it if temp */
@ -2181,18 +2218,18 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
blend_write_params.remap_mode = BLO_WRITE_PATH_REMAP_ABSOLUTE; blend_write_params.remap_mode = BLO_WRITE_PATH_REMAP_ABSOLUTE;
/* Don't apply any path changes to the current blend file. */ /* Don't apply any path changes to the current blend file. */
blend_write_params.use_save_as_copy = true; blend_write_params.use_save_as_copy = true;
if (BLO_write_file(bmain, filepath, fileflags, &blend_write_params, op->reports) == 0) { const bool ok = BLO_write_file(bmain, filepath, fileflags, &blend_write_params, op->reports);
printf("fail\n");
return OPERATOR_CANCELLED;
}
BKE_callback_exec_string(bmain, ok ? BKE_CB_EVT_SAVE_POST : BKE_CB_EVT_SAVE_POST_FAIL, "");
if (ok) {
printf("ok\n"); printf("ok\n");
BKE_report(op->reports, RPT_INFO, "Startup file saved"); BKE_report(op->reports, RPT_INFO, "Startup file saved");
BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_POST);
return OPERATOR_FINISHED; return OPERATOR_FINISHED;
} }
printf("fail\n");
return OPERATOR_CANCELLED;
}
void WM_OT_save_homefile(wmOperatorType *ot) void WM_OT_save_homefile(wmOperatorType *ot)
{ {