Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/python/intern')
-rw-r--r--source/blender/python/intern/CMakeLists.txt25
-rw-r--r--source/blender/python/intern/bpy.c8
-rw-r--r--source/blender/python/intern/bpy_app.c48
-rw-r--r--source/blender/python/intern/bpy_app_build_options.c21
-rw-r--r--source/blender/python/intern/bpy_app_handlers.c49
-rw-r--r--source/blender/python/intern/bpy_app_icons.c187
-rw-r--r--source/blender/python/intern/bpy_app_icons.h30
-rw-r--r--source/blender/python/intern/bpy_app_timers.c199
-rw-r--r--source/blender/python/intern/bpy_app_timers.h30
-rw-r--r--source/blender/python/intern/bpy_driver.c50
-rw-r--r--source/blender/python/intern/bpy_driver.h7
-rw-r--r--source/blender/python/intern/bpy_gizmo_wrap.c228
-rw-r--r--source/blender/python/intern/bpy_gizmo_wrap.h35
-rw-r--r--source/blender/python/intern/bpy_interface.c11
-rw-r--r--source/blender/python/intern/bpy_intern_string.c48
-rw-r--r--source/blender/python/intern/bpy_intern_string.h23
-rw-r--r--source/blender/python/intern/bpy_library_load.c19
-rw-r--r--source/blender/python/intern/bpy_library_write.c3
-rw-r--r--source/blender/python/intern/bpy_msgbus.c399
-rw-r--r--source/blender/python/intern/bpy_msgbus.h30
-rw-r--r--source/blender/python/intern/bpy_props.c14
-rw-r--r--source/blender/python/intern/bpy_props.h2
-rw-r--r--source/blender/python/intern/bpy_rna.c302
-rw-r--r--source/blender/python/intern/bpy_rna.h4
-rw-r--r--source/blender/python/intern/bpy_rna_anim.c188
-rw-r--r--source/blender/python/intern/bpy_rna_callback.c197
-rw-r--r--source/blender/python/intern/bpy_rna_gizmo.c564
-rw-r--r--source/blender/python/intern/bpy_rna_gizmo.h (renamed from source/blender/python/intern/gpu.h)21
-rw-r--r--source/blender/python/intern/bpy_rna_id_collection.c84
-rw-r--r--source/blender/python/intern/bpy_utils_units.c1
-rw-r--r--source/blender/python/intern/gpu.c342
-rw-r--r--source/blender/python/intern/gpu_offscreen.c408
32 files changed, 2510 insertions, 1067 deletions
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 357cb175769..bd7306cddf2 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -52,11 +52,13 @@ set(SRC
bpy_app_build_options.c
bpy_app_ffmpeg.c
bpy_app_handlers.c
+ bpy_app_icons.c
bpy_app_ocio.c
bpy_app_oiio.c
bpy_app_opensubdiv.c
bpy_app_openvdb.c
bpy_app_sdl.c
+ bpy_app_timers.c
bpy_app_translations.c
bpy_capi_utils.c
bpy_driver.c
@@ -65,6 +67,8 @@ set(SRC
bpy_intern_string.c
bpy_library_load.c
bpy_library_write.c
+ bpy_gizmo_wrap.c
+ bpy_msgbus.c
bpy_operator.c
bpy_operator_wrap.c
bpy_path.c
@@ -75,11 +79,10 @@ set(SRC
bpy_rna_callback.c
bpy_rna_driver.c
bpy_rna_id_collection.c
+ bpy_rna_gizmo.c
bpy_traceback.c
bpy_utils_previews.c
bpy_utils_units.c
- gpu.c
- gpu_offscreen.c
stubs.c
bpy.h
@@ -88,16 +91,20 @@ set(SRC
bpy_app_build_options.h
bpy_app_ffmpeg.h
bpy_app_handlers.h
+ bpy_app_icons.h
bpy_app_ocio.h
bpy_app_oiio.h
bpy_app_opensubdiv.h
bpy_app_openvdb.h
bpy_app_sdl.h
+ bpy_app_timers.h
bpy_app_translations.h
bpy_capi_utils.h
bpy_driver.h
bpy_intern_string.h
bpy_library.h
+ bpy_gizmo_wrap.h
+ bpy_msgbus.h
bpy_operator.h
bpy_operator_wrap.h
bpy_path.h
@@ -107,10 +114,10 @@ set(SRC
bpy_rna_callback.h
bpy_rna_driver.h
bpy_rna_id_collection.h
+ bpy_rna_gizmo.h
bpy_traceback.h
bpy_utils_previews.h
bpy_utils_units.h
- gpu.h
../BPY_extern.h
../BPY_extern_clog.h
)
@@ -176,10 +183,6 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
-if(WITH_GAMEENGINE)
- add_definitions(-DWITH_GAMEENGINE)
-endif()
-
if(WITH_IMAGE_CINEON)
add_definitions(-DWITH_CINEON)
endif()
@@ -188,10 +191,6 @@ if(WITH_IMAGE_DDS)
add_definitions(-DWITH_DDS)
endif()
-if(WITH_IMAGE_FRAMESERVER)
- add_definitions(-DWITH_FRAMESERVER)
-endif()
-
if(WITH_IMAGE_HDR)
add_definitions(-DWITH_HDR)
endif()
@@ -302,10 +301,6 @@ if(WITH_OPENSUBDIV)
)
endif()
-if(WITH_PLAYER)
- add_definitions(-DWITH_PLAYER)
-endif()
-
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_python "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c
index 82f710e4acf..07ad8274f11 100644
--- a/source/blender/python/intern/bpy.c
+++ b/source/blender/python/intern/bpy.c
@@ -46,6 +46,7 @@
#include "bpy_rna.h"
#include "bpy_app.h"
#include "bpy_rna_id_collection.h"
+#include "bpy_rna_gizmo.h"
#include "bpy_props.h"
#include "bpy_library.h"
#include "bpy_operator.h"
@@ -57,6 +58,7 @@
/* external util modules */
#include "../generic/idprop_py_api.h"
+#include "bpy_msgbus.h"
#ifdef WITH_FREESTYLE
# include "BPy_Freestyle.h"
@@ -338,6 +340,8 @@ void BPy_init_modules(void)
BPY_rna_id_collection_module(mod);
+ BPY_rna_gizmo_module(mod);
+
bpy_import_test("bpy_types");
PyModule_AddObject(mod, "data", BPY_rna_module()); /* imports bpy_types by running this */
bpy_import_test("bpy_types");
@@ -347,6 +351,7 @@ void BPy_init_modules(void)
PyModule_AddObject(mod, "app", BPY_app_struct());
PyModule_AddObject(mod, "_utils_units", BPY_utils_units());
PyModule_AddObject(mod, "_utils_previews", BPY_utils_previews_module());
+ PyModule_AddObject(mod, "msgbus", BPY_msgbus_module());
/* bpy context */
RNA_pointer_create(NULL, &RNA_Context, (void *)BPy_GetContext(), &ctx_ptr);
@@ -371,6 +376,9 @@ void BPy_init_modules(void)
PyModule_AddObject(mod, meth_bpy_register_class.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_register_class, NULL));
PyModule_AddObject(mod, meth_bpy_unregister_class.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_unregister_class, NULL));
+ PyModule_AddObject(mod, meth_bpy_owner_id_get.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_owner_id_get, NULL));
+ PyModule_AddObject(mod, meth_bpy_owner_id_set.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_owner_id_set, NULL));
+
/* add our own modules dir, this is a python package */
bpy_package_py = bpy_import_test("bpy");
}
diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c
index 928e14c09bf..fe8eb768b3a 100644
--- a/source/blender/python/intern/bpy_app.c
+++ b/source/blender/python/intern/bpy_app.c
@@ -47,11 +47,16 @@
#include "bpy_app_handlers.h"
#include "bpy_driver.h"
+/* modules */
+#include "bpy_app_icons.h"
+#include "bpy_app_timers.h"
+
#include "BLI_utildefines.h"
#include "BKE_appdir.h"
#include "BKE_blender_version.h"
#include "BKE_global.h"
+#include "BKE_library_override.h"
#include "DNA_ID.h"
@@ -117,6 +122,10 @@ static PyStructSequence_Field app_info_fields[] = {
{(char *)"build_options", (char *)"A set containing most important enabled optional build features"},
{(char *)"handlers", (char *)"Application handler callbacks"},
{(char *)"translations", (char *)"Application and addons internationalization API"},
+
+ /* Modules (not struct sequence). */
+ {(char *)"icons", (char *)"Manage custom icons"},
+ {(char *)"timers", (char *)"Manage timers"},
{NULL},
};
@@ -129,6 +138,8 @@ PyDoc_STRVAR(bpy_app_doc,
" :maxdepth: 1\n"
"\n"
" bpy.app.handlers.rst\n"
+" bpy.app.icons.rst\n"
+" bpy.app.timers.rst\n"
" bpy.app.translations.rst\n"
);
@@ -210,6 +221,10 @@ static PyObject *make_app_info(void)
SetObjItem(BPY_app_handlers_struct());
SetObjItem(BPY_app_translations_struct());
+ /* modules */
+ SetObjItem(BPY_app_icons_module());
+ SetObjItem(BPY_app_timers_module());
+
#undef SetIntItem
#undef SetStrItem
#undef SetBytesItem
@@ -282,7 +297,7 @@ static PyObject *bpy_app_binary_path_python_get(PyObject *self, void *UNUSED(clo
}
PyDoc_STRVAR(bpy_app_debug_value_doc,
-"Int, number which can be set to non-zero values for testing purposes"
+"Short, number which can be set to non-zero values for testing purposes"
);
static PyObject *bpy_app_debug_value_get(PyObject *UNUSED(self), void *UNUSED(closure))
{
@@ -291,10 +306,12 @@ static PyObject *bpy_app_debug_value_get(PyObject *UNUSED(self), void *UNUSED(cl
static int bpy_app_debug_value_set(PyObject *UNUSED(self), PyObject *value, void *UNUSED(closure))
{
- int param = PyC_Long_AsI32(value);
+ short param = PyC_Long_AsI16(value);
if (param == -1 && PyErr_Occurred()) {
- PyErr_SetString(PyExc_TypeError, "bpy.app.debug_value can only be set to a whole number");
+ PyC_Err_SetString_Prefix(
+ PyExc_TypeError,
+ "bpy.app.debug_value can only be set to a whole number");
return -1;
}
@@ -348,6 +365,29 @@ static PyObject *bpy_app_autoexec_fail_message_get(PyObject *UNUSED(self), void
}
+PyDoc_STRVAR(bpy_app_use_static_override_doc,
+"Boolean, whether static override is exposed in UI or not."
+);
+static PyObject *bpy_app_use_static_override_get(PyObject *UNUSED(self), void *UNUSED(closure))
+{
+ return PyBool_FromLong((long)BKE_override_static_is_enabled());
+}
+
+static int bpy_app_use_static_override_set(PyObject *UNUSED(self), PyObject *value, void *UNUSED(closure))
+{
+ const int param = PyC_Long_AsBool(value);
+
+ if (param == -1 && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError, "bpy.app.use_static_override must be a boolean");
+ return -1;
+ }
+
+ BKE_override_static_enable((const bool)param);
+
+ return 0;
+}
+
+
static PyGetSetDef bpy_app_getsets[] = {
{(char *)"debug", bpy_app_debug_get, bpy_app_debug_set, (char *)bpy_app_debug_doc, (void *)G_DEBUG},
{(char *)"debug_ffmpeg", bpy_app_debug_get, bpy_app_debug_set, (char *)bpy_app_debug_doc, (void *)G_DEBUG_FFMPEG},
@@ -366,6 +406,8 @@ static PyGetSetDef bpy_app_getsets[] = {
{(char *)"debug_gpumem", bpy_app_debug_get, bpy_app_debug_set, (char *)bpy_app_debug_doc, (void *)G_DEBUG_GPU_MEM},
{(char *)"debug_io", bpy_app_debug_get, bpy_app_debug_set, (char *)bpy_app_debug_doc, (void *)G_DEBUG_IO},
+ {(char *)"use_static_override", bpy_app_use_static_override_get, bpy_app_use_static_override_set, (char *)bpy_app_use_static_override_doc, NULL},
+
{(char *)"binary_path_python", bpy_app_binary_path_python_get, NULL, (char *)bpy_app_binary_path_python_doc, NULL},
{(char *)"debug_value", bpy_app_debug_value_get, bpy_app_debug_value_set, (char *)bpy_app_debug_value_doc, NULL},
diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c
index d5c325e4317..70c52b8860c 100644
--- a/source/blender/python/intern/bpy_app_build_options.c
+++ b/source/blender/python/intern/bpy_app_build_options.c
@@ -42,10 +42,8 @@ static PyStructSequence_Field app_builtopts_info_fields[] = {
{(char *)"cycles", NULL},
{(char *)"cycles_osl", NULL},
{(char *)"freestyle", NULL},
- {(char *)"gameengine", NULL},
{(char *)"image_cineon", NULL},
{(char *)"image_dds", NULL},
- {(char *)"image_frameserver", NULL},
{(char *)"image_hdr", NULL},
{(char *)"image_openexr", NULL},
{(char *)"image_openjpeg", NULL},
@@ -64,7 +62,6 @@ static PyStructSequence_Field app_builtopts_info_fields[] = {
{(char *)"mod_smoke", NULL},
{(char *)"collada", NULL},
{(char *)"opencolorio", NULL},
- {(char *)"player", NULL},
{(char *)"openmp", NULL},
{(char *)"openvdb", NULL},
{(char *)"alembic", NULL},
@@ -140,12 +137,6 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
-#ifdef WITH_GAMEENGINE
- SetObjIncref(Py_True);
-#else
- SetObjIncref(Py_False);
-#endif
-
#ifdef WITH_CINEON
SetObjIncref(Py_True);
#else
@@ -158,12 +149,6 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
-#ifdef WITH_FRAMESERVER
- SetObjIncref(Py_True);
-#else
- SetObjIncref(Py_False);
-#endif
-
#ifdef WITH_HDR
SetObjIncref(Py_True);
#else
@@ -272,12 +257,6 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
-#ifdef WITH_PLAYER
- SetObjIncref(Py_True);
-#else
- SetObjIncref(Py_False);
-#endif
-
#ifdef _OPENMP
SetObjIncref(Py_True);
#else
diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c
index 8453808d6ec..d89b3bd01f5 100644
--- a/source/blender/python/intern/bpy_app_handlers.c
+++ b/source/blender/python/intern/bpy_app_handlers.c
@@ -46,35 +46,30 @@ void bpy_app_generic_callback(struct Main *main, struct ID *id, void *arg);
static PyTypeObject BlenderAppCbType;
static PyStructSequence_Field app_cb_info_fields[] = {
- {(char *)"frame_change_pre", (char *)"on frame change for playback and rendering (before)"},
- {(char *)"frame_change_post", (char *)"on frame change for playback and rendering (after)"},
- {(char *)"render_pre", (char *)"on render (before)"},
- {(char *)"render_post", (char *)"on render (after)"},
- {(char *)"render_write", (char *)"on writing a render frame (directly after the frame is written)"},
- {(char *)"render_stats", (char *)"on printing render statistics"},
- {(char *)"render_init", (char *)"on initialization of a render job"},
- {(char *)"render_complete", (char *)"on completion of render job"},
- {(char *)"render_cancel", (char *)"on canceling a render job"},
- {(char *)"load_pre", (char *)"on loading a new blend file (before)"},
- {(char *)"load_post", (char *)"on loading a new blend file (after)"},
- {(char *)"save_pre", (char *)"on saving a blend file (before)"},
- {(char *)"save_post", (char *)"on saving a blend file (after)"},
- {(char *)"undo_pre", (char *)"on loading an undo step (before)"},
- {(char *)"undo_post", (char *)"on loading an undo step (after)"},
- {(char *)"redo_pre", (char *)"on loading a redo step (before)"},
- {(char *)"redo_post", (char *)"on loading a redo step (after)"},
- {(char *)"scene_update_pre", (char *)"on every scene data update. Does not imply that anything changed in the "
- "scene, just that the dependency graph is about to be reevaluated, and the "
- "scene is about to be updated by Blender's animation system."},
- {(char *)"scene_update_post", (char *)"on every scene data update. Does not imply that anything changed in the "
- "scene, just that the dependency graph was reevaluated, and the scene was "
- "possibly updated by Blender's animation system."},
- {(char *)"game_pre", (char *)"on starting the game engine"},
- {(char *)"game_post", (char *)"on ending the game engine"},
- {(char *)"version_update", (char *)"on ending the versioning code"},
+ {(char *)"frame_change_pre", (char *)"on frame change for playback and rendering (before)"},
+ {(char *)"frame_change_post", (char *)"on frame change for playback and rendering (after)"},
+ {(char *)"render_pre", (char *)"on render (before)"},
+ {(char *)"render_post", (char *)"on render (after)"},
+ {(char *)"render_write", (char *)"on writing a render frame (directly after the frame is written)"},
+ {(char *)"render_stats", (char *)"on printing render statistics"},
+ {(char *)"render_init", (char *)"on initialization of a render job"},
+ {(char *)"render_complete", (char *)"on completion of render job"},
+ {(char *)"render_cancel", (char *)"on canceling a render job"},
+ {(char *)"load_pre", (char *)"on loading a new blend file (before)"},
+ {(char *)"load_post", (char *)"on loading a new blend file (after)"},
+ {(char *)"save_pre", (char *)"on saving a blend file (before)"},
+ {(char *)"save_post", (char *)"on saving a blend file (after)"},
+ {(char *)"undo_pre", (char *)"on loading an undo step (before)"},
+ {(char *)"undo_post", (char *)"on loading an undo step (after)"},
+ {(char *)"redo_pre", (char *)"on loading a redo step (before)"},
+ {(char *)"redo_post", (char *)"on loading a redo step (after)"},
+ {(char *)"depsgraph_update_pre", (char *)"on depsgraph update (pre)"},
+ {(char *)"depsgraph_update_post", (char *)"on depsgraph update (post)"},
+ {(char *)"version_update", (char *)"on ending the versioning code"},
+ {(char *)"load_factory_startup_post", (char *)"on loading factory startup (after)"},
/* sets the permanent tag */
-# define APP_CB_OTHER_FIELDS 1
+#define APP_CB_OTHER_FIELDS 1
{(char *)"persistent", (char *)"Function decorator for callback functions not to be removed when loading new files"},
{NULL}
diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c
new file mode 100644
index 00000000000..12315a6e112
--- /dev/null
+++ b/source/blender/python/intern/bpy_app_icons.c
@@ -0,0 +1,187 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_app_icons.c
+ * \ingroup pythonintern
+ *
+ * Runtime defined icons.
+ */
+
+#include <Python.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+
+#include "BKE_icons.h"
+
+#include "../generic/py_capi_utils.h"
+
+#include "bpy_app_icons.h"
+
+/* We may want to load direct from file. */
+PyDoc_STRVAR(bpy_app_icons_new_triangles_doc,
+".. function:: new_triangles(range, coords, colors)"
+"\n"
+" Create a new icon from triangle geometry.\n"
+"\n"
+" :arg range: Pair of ints.\n"
+" :type range: tuple.\n"
+" :arg coords: Sequence of bytes (6 floats for one triangle) for (X, Y) coordinates.\n"
+" :type coords: byte sequence.\n"
+" :arg colors: Sequence of ints (12 for one triangles) for RGBA.\n"
+" :type colors: byte sequence.\n"
+" :return: Unique icon value (pass to interface ``icon_value`` argument).\n"
+" :rtype: int\n"
+);
+static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ /* bytes */
+ uchar coords_range[2];
+ PyObject *py_coords, *py_colors;
+
+ static const char *_keywords[] = {"range", "coords", "colors", NULL};
+ static _PyArg_Parser _parser = {"(BB)SS:new_triangles", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kw, &_parser,
+ &coords_range[0], &coords_range[1], &py_coords, &py_colors))
+ {
+ return NULL;
+ }
+
+ const int coords_len = PyBytes_GET_SIZE(py_coords);
+ const int tris_len = coords_len / 6;
+ if (tris_len * 6 != coords_len) {
+ PyErr_SetString(PyExc_ValueError, "coords must be multiple of 6");
+ return NULL;
+ }
+ if (PyBytes_GET_SIZE(py_colors) != 2 * coords_len) {
+ PyErr_SetString(PyExc_ValueError, "colors must be twice size of coords");
+ return NULL;
+ }
+
+ int coords_size = sizeof(uchar[2]) * tris_len * 3;
+ int colors_size = sizeof(uchar[4]) * tris_len * 3;
+ uchar (*coords)[2] = MEM_mallocN(coords_size, __func__);
+ uchar (*colors)[4] = MEM_mallocN(colors_size, __func__);
+
+ memcpy(coords, PyBytes_AS_STRING(py_coords), coords_size);
+ memcpy(colors, PyBytes_AS_STRING(py_colors), colors_size);
+
+ struct Icon_Geom *geom = MEM_mallocN(sizeof(*geom), __func__);
+ geom->coords_len = tris_len;
+ geom->coords_range[0] = coords_range[0];
+ geom->coords_range[1] = coords_range[1];
+ geom->coords = coords;
+ geom->colors = colors;
+ geom->icon_id = 0;
+ int icon_id = BKE_icon_geom_ensure(geom);
+ return PyLong_FromLong(icon_id);
+}
+
+PyDoc_STRVAR(bpy_app_icons_new_triangles_from_file_doc,
+".. function:: new_triangles_from_file(filename)"
+"\n"
+" Create a new icon from triangle geometry.\n"
+"\n"
+" :arg filename: File path.\n"
+" :type filename: string.\n"
+" :return: Unique icon value (pass to interface ``icon_value`` argument).\n"
+" :rtype: int\n"
+);
+static PyObject *bpy_app_icons_new_triangles_from_file(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ /* bytes */
+ char *filename;
+
+ static const char *_keywords[] = {"filename", NULL};
+ static _PyArg_Parser _parser = {"s:new_triangles_from_file", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kw, &_parser,
+ &filename))
+ {
+ return NULL;
+ }
+
+ struct Icon_Geom *geom = BKE_icon_geom_from_file(filename);
+ if (geom == NULL) {
+ PyErr_SetString(PyExc_ValueError, "Unable to load from file");
+ return NULL;
+ }
+ int icon_id = BKE_icon_geom_ensure(geom);
+ return PyLong_FromLong(icon_id);
+}
+
+PyDoc_STRVAR(bpy_app_icons_release_doc,
+".. function:: release(icon_id)"
+"\n"
+" Release the icon.\n"
+);
+static PyObject *bpy_app_icons_release(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ int icon_id;
+ static const char *_keywords[] = {"icon_id", NULL};
+ static _PyArg_Parser _parser = {"i:release", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kw, &_parser,
+ &icon_id))
+ {
+ return NULL;
+ }
+
+ if (!BKE_icon_delete_unmanaged(icon_id)) {
+ PyErr_SetString(PyExc_ValueError, "invalid icon_id");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef M_AppIcons_methods[] = {
+ {"new_triangles", (PyCFunction)bpy_app_icons_new_triangles,
+ METH_VARARGS | METH_KEYWORDS, bpy_app_icons_new_triangles_doc},
+ {"new_triangles_from_file", (PyCFunction)bpy_app_icons_new_triangles_from_file,
+ METH_VARARGS | METH_KEYWORDS, bpy_app_icons_new_triangles_from_file_doc},
+ {"release", (PyCFunction)bpy_app_icons_release,
+ METH_VARARGS | METH_KEYWORDS, bpy_app_icons_release_doc},
+ {NULL, NULL, 0, NULL}
+};
+
+static struct PyModuleDef M_AppIcons_module_def = {
+ PyModuleDef_HEAD_INIT,
+ "bpy.app.icons", /* m_name */
+ NULL, /* m_doc */
+ 0, /* m_size */
+ M_AppIcons_methods, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyObject *BPY_app_icons_module(void)
+{
+ PyObject *sys_modules = PyImport_GetModuleDict();
+
+ PyObject *mod = PyModule_Create(&M_AppIcons_module_def);
+
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(mod), mod);
+
+ return mod;
+}
diff --git a/source/blender/python/intern/bpy_app_icons.h b/source/blender/python/intern/bpy_app_icons.h
new file mode 100644
index 00000000000..309c7ed222f
--- /dev/null
+++ b/source/blender/python/intern/bpy_app_icons.h
@@ -0,0 +1,30 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_app_icons.h
+ * \ingroup pythonintern
+ */
+
+#ifndef __BPY_APP_ICONS_H__
+#define __BPY_APP_ICONS_H__
+
+PyObject *BPY_app_icons_module(void);
+
+#endif /* __BPY_APP_ICONS_H__ */
diff --git a/source/blender/python/intern/bpy_app_timers.c b/source/blender/python/intern/bpy_app_timers.c
new file mode 100644
index 00000000000..cd0e57200ec
--- /dev/null
+++ b/source/blender/python/intern/bpy_app_timers.c
@@ -0,0 +1,199 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_app_timers.c
+ * \ingroup pythonintern
+ */
+
+#include <Python.h>
+#include "BLI_utildefines.h"
+#include "BLI_timer.h"
+#include "PIL_time.h"
+
+#include "BPY_extern.h"
+#include "bpy_app_timers.h"
+
+#include "../generic/py_capi_utils.h"
+#include "../generic/python_utildefines.h"
+
+
+static double handle_returned_value(PyObject *function, PyObject *ret)
+{
+ if (ret == NULL) {
+ PyErr_PrintEx(0);
+ PyErr_Clear();
+ return -1;
+ }
+
+ if (ret == Py_None) {
+ return -1;
+ }
+
+ double value = PyFloat_AsDouble(ret);
+ if (value == -1.0f && PyErr_Occurred()) {
+ PyErr_Clear();
+ printf("Error: 'bpy.app.timers' callback ");
+ PyObject_Print(function, stdout, Py_PRINT_RAW);
+ printf(" did not return None or float.\n");
+ return -1;
+ }
+
+ if (value < 0.0) {
+ value = 0.0;
+ }
+
+ return value;
+}
+
+static double py_timer_execute(uintptr_t UNUSED(uuid), void *user_data)
+{
+ PyObject *function = user_data;
+
+ PyGILState_STATE gilstate;
+ gilstate = PyGILState_Ensure();
+
+ PyObject *py_ret = PyObject_CallObject(function, NULL);
+ double ret = handle_returned_value(function, py_ret);
+
+ PyGILState_Release(gilstate);
+
+ return ret;
+}
+
+static void py_timer_free(uintptr_t UNUSED(uuid), void *user_data)
+{
+ PyObject *function = user_data;
+
+ PyGILState_STATE gilstate;
+ gilstate = PyGILState_Ensure();
+
+ Py_DECREF(function);
+
+ PyGILState_Release(gilstate);
+}
+
+
+PyDoc_STRVAR(bpy_app_timers_register_doc,
+".. function:: register(function, first_interval=0, persistent=False)\n"
+"\n"
+" Add a new function that will be called after the specified amount of seconds.\n"
+" The function gets no arguments and is expected to return either None or a float.\n"
+" If ``None`` is returned, the timer will be unregistered.\n"
+" A returned number specifies the delay until the function is called again.\n"
+" ``functools.partial`` can be used to assign some parameters.\n"
+"\n"
+" :arg function: The function that should called.\n"
+" :type function: Callable[[], Union[float, None]]\n"
+" :arg first_interval: Seconds until the callback should be called the first time.\n"
+" :type first_interval: float\n"
+" :arg persistent: Don't remove timer when a new file is loaded.\n"
+" :type persistent: bool\n"
+);
+static PyObject *bpy_app_timers_register(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ PyObject *function;
+ double first_interval = 0;
+ int persistent = false;
+
+ static const char *_keywords[] = {"function", "first_interval", "persistent", NULL};
+ static _PyArg_Parser _parser = {"O|$dp:register", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kw, &_parser,
+ &function, &first_interval, &persistent))
+ {
+ return NULL;
+ }
+
+ if (!PyCallable_Check(function)) {
+ PyErr_SetString(PyExc_TypeError, "function is not callable");
+ return NULL;
+ }
+
+ Py_INCREF(function);
+ BLI_timer_register(
+ (intptr_t)function,
+ py_timer_execute, function, py_timer_free,
+ first_interval, persistent);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(bpy_app_timers_unregister_doc,
+".. function:: unregister(function)\n"
+"\n"
+" Unregister timer.\n"
+"\n"
+" :arg function: Function to unregister.\n"
+" :type function: function\n"
+);
+static PyObject *bpy_app_timers_unregister(PyObject *UNUSED(self), PyObject *function)
+{
+ if (!BLI_timer_unregister((intptr_t)function)) {
+ PyErr_SetString(PyExc_ValueError, "Error: function is not registered");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(bpy_app_timers_is_registered_doc,
+".. function:: is_registered(function)\n"
+"\n"
+" Check if this function is registered as a timer.\n"
+"\n"
+" :arg function: Function to check.\n"
+" :type function: int\n"
+" :return: True when this function is registered, otherwise False.\n"
+" :rtype: bool\n"
+);
+static PyObject *bpy_app_timers_is_registered(PyObject *UNUSED(self), PyObject *function)
+{
+ bool ret = BLI_timer_is_registered((intptr_t)function);
+ return PyBool_FromLong(ret);
+}
+
+
+static struct PyMethodDef M_AppTimers_methods[] = {
+ {"register", (PyCFunction)bpy_app_timers_register,
+ METH_VARARGS | METH_KEYWORDS, bpy_app_timers_register_doc},
+ {"unregister", (PyCFunction)bpy_app_timers_unregister,
+ METH_O, bpy_app_timers_unregister_doc},
+ {"is_registered", (PyCFunction)bpy_app_timers_is_registered,
+ METH_O, bpy_app_timers_is_registered_doc},
+ {NULL, NULL, 0, NULL}
+};
+
+static struct PyModuleDef M_AppTimers_module_def = {
+ PyModuleDef_HEAD_INIT,
+ "bpy.app.timers", /* m_name */
+ NULL, /* m_doc */
+ 0, /* m_size */
+ M_AppTimers_methods, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyObject *BPY_app_timers_module(void)
+{
+ PyObject *sys_modules = PyImport_GetModuleDict();
+ PyObject *mod = PyModule_Create(&M_AppTimers_module_def);
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(mod), mod);
+ return mod;
+}
diff --git a/source/blender/python/intern/bpy_app_timers.h b/source/blender/python/intern/bpy_app_timers.h
new file mode 100644
index 00000000000..ddf0e258e67
--- /dev/null
+++ b/source/blender/python/intern/bpy_app_timers.h
@@ -0,0 +1,30 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_app_timers.h
+ * \ingroup pythonintern
+ */
+
+#ifndef __BPY_APP_TIMERS_H__
+#define __BPY_APP_TIMERS_H__
+
+PyObject *BPY_app_timers_module(void);
+
+#endif /* __BPY_APP_TIMERS_H__ */
diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c
index e7608e93f58..f098a28d679 100644
--- a/source/blender/python/intern/bpy_driver.c
+++ b/source/blender/python/intern/bpy_driver.c
@@ -48,6 +48,8 @@
#include "bpy_driver.h"
+#include "BPY_extern.h"
+
extern void BPY_update_rna_module(void);
#define USE_RNA_AS_PYOBJECT
@@ -381,8 +383,12 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, PyObject *d
* (new)note: checking if python is running is not threadsafe [#28114]
* now release the GIL on python operator execution instead, using
* PyEval_SaveThread() / PyEval_RestoreThread() so we don't lock up blender.
+ *
+ * For copy-on-write we always cache expressions and write errors in the
+ * original driver, otherwise these would get freed while editing. Due to
+ * the GIL this is thread-safe.
*/
-float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, const float evaltime)
+float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, ChannelDriver *driver_orig, const float evaltime)
{
PyObject *driver_vars = NULL;
PyObject *retval = NULL;
@@ -398,7 +404,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, c
int i;
/* get the py expression to be evaluated */
- expr = driver->expression;
+ expr = driver_orig->expression;
if (expr[0] == '\0')
return 0.0f;
@@ -438,50 +444,50 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, c
/* update global namespace */
bpy_pydriver_namespace_update_frame(evaltime);
- if (driver->flag & DRIVER_FLAG_USE_SELF) {
+ if (driver_orig->flag & DRIVER_FLAG_USE_SELF) {
bpy_pydriver_namespace_update_self(anim_rna);
}
else {
bpy_pydriver_namespace_clear_self();
}
- if (driver->expr_comp == NULL)
- driver->flag |= DRIVER_FLAG_RECOMPILE;
+ if (driver_orig->expr_comp == NULL)
+ driver_orig->flag |= DRIVER_FLAG_RECOMPILE;
/* compile the expression first if it hasn't been compiled or needs to be rebuilt */
- if (driver->flag & DRIVER_FLAG_RECOMPILE) {
- Py_XDECREF(driver->expr_comp);
- driver->expr_comp = PyTuple_New(2);
+ if (driver_orig->flag & DRIVER_FLAG_RECOMPILE) {
+ Py_XDECREF(driver_orig->expr_comp);
+ driver_orig->expr_comp = PyTuple_New(2);
expr_code = Py_CompileString(expr, "<bpy driver>", Py_eval_input);
- PyTuple_SET_ITEM(((PyObject *)driver->expr_comp), 0, expr_code);
+ PyTuple_SET_ITEM(((PyObject *)driver_orig->expr_comp), 0, expr_code);
- driver->flag &= ~DRIVER_FLAG_RECOMPILE;
- driver->flag |= DRIVER_FLAG_RENAMEVAR; /* maybe this can be removed but for now best keep until were sure */
+ driver_orig->flag &= ~DRIVER_FLAG_RECOMPILE;
+ driver_orig->flag |= DRIVER_FLAG_RENAMEVAR; /* maybe this can be removed but for now best keep until were sure */
#ifdef USE_BYTECODE_WHITELIST
is_recompile = true;
#endif
}
else {
- expr_code = PyTuple_GET_ITEM(((PyObject *)driver->expr_comp), 0);
+ expr_code = PyTuple_GET_ITEM(((PyObject *)driver_orig->expr_comp), 0);
}
- if (driver->flag & DRIVER_FLAG_RENAMEVAR) {
+ if (driver_orig->flag & DRIVER_FLAG_RENAMEVAR) {
/* may not be set */
- expr_vars = PyTuple_GET_ITEM(((PyObject *)driver->expr_comp), 1);
+ expr_vars = PyTuple_GET_ITEM(((PyObject *)driver_orig->expr_comp), 1);
Py_XDECREF(expr_vars);
- expr_vars = PyTuple_New(BLI_listbase_count(&driver->variables));
- PyTuple_SET_ITEM(((PyObject *)driver->expr_comp), 1, expr_vars);
+ expr_vars = PyTuple_New(BLI_listbase_count(&driver_orig->variables));
+ PyTuple_SET_ITEM(((PyObject *)driver_orig->expr_comp), 1, expr_vars);
- for (dvar = driver->variables.first, i = 0; dvar; dvar = dvar->next) {
+ for (dvar = driver_orig->variables.first, i = 0; dvar; dvar = dvar->next) {
PyTuple_SET_ITEM(expr_vars, i++, PyUnicode_FromString(dvar->name));
}
- driver->flag &= ~DRIVER_FLAG_RENAMEVAR;
+ driver_orig->flag &= ~DRIVER_FLAG_RENAMEVAR;
}
else {
- expr_vars = PyTuple_GET_ITEM(((PyObject *)driver->expr_comp), 1);
+ expr_vars = PyTuple_GET_ITEM(((PyObject *)driver_orig->expr_comp), 1);
}
/* add target values to a dict that will be used as '__locals__' dict */
@@ -543,7 +549,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, c
}
#ifdef USE_BYTECODE_WHITELIST
- if (is_recompile) {
+ if (is_recompile && expr_code) {
if (!(G.f & G_SCRIPT_AUTOEXEC)) {
if (!bpy_driver_secure_bytecode_validate(
expr_code, (PyObject *[]){
@@ -555,7 +561,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, c
{
Py_DECREF(expr_code);
expr_code = NULL;
- PyTuple_SET_ITEM(((PyObject *)driver->expr_comp), 0, NULL);
+ PyTuple_SET_ITEM(((PyObject *)driver_orig->expr_comp), 0, NULL);
}
}
}
@@ -595,7 +601,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, c
return (float)result;
}
else {
- fprintf(stderr, "\tBPY_driver_eval() - driver '%s' evaluates to '%f'\n", dvar->name, result);
+ fprintf(stderr, "\tBPY_driver_eval() - driver '%s' evaluates to '%f'\n", driver->expression, result);
return 0.0f;
}
}
diff --git a/source/blender/python/intern/bpy_driver.h b/source/blender/python/intern/bpy_driver.h
index 017a6fe89c5..971cbdf901f 100644
--- a/source/blender/python/intern/bpy_driver.h
+++ b/source/blender/python/intern/bpy_driver.h
@@ -27,14 +27,7 @@
#ifndef __BPY_DRIVER_H__
#define __BPY_DRIVER_H__
-struct ChannelDriver;
-struct PathResolvedRNA;
-
int bpy_pydriver_create_dict(void);
extern PyObject *bpy_pydriver_Dict;
-/* externals */
-float BPY_driver_exec(struct PathResolvedRNA *anim_rna, struct ChannelDriver *driver, const float evaltime);
-void BPY_driver_reset(void);
-
#endif /* __BPY_DRIVER_H__ */
diff --git a/source/blender/python/intern/bpy_gizmo_wrap.c b/source/blender/python/intern/bpy_gizmo_wrap.c
new file mode 100644
index 00000000000..411822ee4da
--- /dev/null
+++ b/source/blender/python/intern/bpy_gizmo_wrap.c
@@ -0,0 +1,228 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_gizmo_wrap.c
+ * \ingroup pythonintern
+ *
+ * This file is so Python can define widget-group's that C can call into.
+ * The generic callback functions for Python widget-group are defines in
+ * 'rna_wm.c', some calling into functions here to do python specific
+ * functionality.
+ *
+ * \note This follows 'bpy_operator_wrap.c' very closely.
+ * Keep in sync unless there is good reason not to!
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "bpy_rna.h"
+#include "bpy_intern_string.h"
+#include "bpy_gizmo_wrap.h" /* own include */
+
+/* we may want to add, but not now */
+
+/* -------------------------------------------------------------------- */
+
+/** \name Gizmo
+ * \{ */
+
+
+static bool bpy_gizmotype_target_property_def(
+ wmGizmoType *gzt, PyObject *item)
+{
+ /* Note: names based on 'rna_rna.c' */
+ PyObject *empty_tuple = PyTuple_New(0);
+
+ struct {
+ char *id;
+ char *type_id; int type;
+ int array_length;
+ } params = {
+ .id = NULL, /* not optional */
+ .type = PROP_FLOAT,
+ .type_id = NULL,
+ .array_length = 1,
+ };
+
+ static const char * const _keywords[] = {"id", "type", "array_length", NULL};
+ static _PyArg_Parser _parser = {"|$ssi:register_class", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ empty_tuple, item,
+ &_parser,
+ &params.id,
+ &params.type_id,
+ &params.array_length))
+ {
+ goto fail;
+ }
+
+ if (params.id == NULL) {
+ PyErr_SetString(PyExc_ValueError, "'id' argument not given");
+ goto fail;
+ }
+
+ if ((params.type_id != NULL) &&
+ pyrna_enum_value_from_id(
+ rna_enum_property_type_items, params.type_id, &params.type, "'type' enum value") == -1)
+ {
+ goto fail;
+ }
+ else {
+ params.type = rna_enum_property_type_items[params.type].value;
+ }
+
+ if ((params.array_length < 1 || params.array_length > RNA_MAX_ARRAY_LENGTH)) {
+ PyErr_SetString(PyExc_ValueError, "'array_length' out of range");
+ goto fail;
+ }
+
+ WM_gizmotype_target_property_def(gzt, params.id, params.type, params.array_length);
+ Py_DECREF(empty_tuple);
+ return true;
+
+fail:
+ Py_DECREF(empty_tuple);
+ return false;
+}
+
+static void gizmo_properties_init(wmGizmoType *gzt)
+{
+ PyTypeObject *py_class = gzt->ext.data;
+ RNA_struct_blender_type_set(gzt->ext.srna, gzt);
+
+ /* only call this so pyrna_deferred_register_class gives a useful error
+ * WM_operatortype_append_ptr will call RNA_def_struct_identifier
+ * later */
+ RNA_def_struct_identifier_no_struct_map(gzt->srna, gzt->idname);
+
+ if (pyrna_deferred_register_class(gzt->srna, py_class) != 0) {
+ PyErr_Print(); /* failed to register operator props */
+ PyErr_Clear();
+ }
+
+ /* Extract target property definitions from 'bl_target_properties' */
+ {
+ /* picky developers will notice that 'bl_targets' won't work with inheritance
+ * get direct from the dict to avoid raising a load of attribute errors (yes this isnt ideal) - campbell */
+ PyObject *py_class_dict = py_class->tp_dict;
+ PyObject *bl_target_properties = PyDict_GetItem(py_class_dict, bpy_intern_str_bl_target_properties);
+
+ /* Some widgets may only exist to activate operators. */
+ if (bl_target_properties != NULL) {
+ PyObject *bl_target_properties_fast;
+ if (!(bl_target_properties_fast = PySequence_Fast(
+ bl_target_properties, "bl_target_properties sequence")))
+ {
+ /* PySequence_Fast sets the error */
+ PyErr_Print();
+ PyErr_Clear();
+ return;
+ }
+
+ const uint items_len = PySequence_Fast_GET_SIZE(bl_target_properties_fast);
+ PyObject **items = PySequence_Fast_ITEMS(bl_target_properties_fast);
+
+ for (uint i = 0; i < items_len; i++) {
+ if (!bpy_gizmotype_target_property_def(gzt, items[i])) {
+ PyErr_Print();
+ PyErr_Clear();
+ break;
+ }
+ }
+
+ Py_DECREF(bl_target_properties_fast);
+ }
+ }
+}
+
+void BPY_RNA_gizmo_wrapper(wmGizmoType *gzt, void *userdata)
+{
+ /* take care not to overwrite anything set in
+ * WM_gizmomaptype_group_link_ptr before opfunc() is called */
+ StructRNA *srna = gzt->srna;
+ *gzt = *((wmGizmoType *)userdata);
+ gzt->srna = srna; /* restore */
+
+ /* don't do translations here yet */
+#if 0
+ /* Use i18n context from ext.srna if possible (py gizmogroups). */
+ if (gt->ext.srna) {
+ RNA_def_struct_translation_context(gt->srna, RNA_struct_translation_context(gt->ext.srna));
+ }
+#endif
+
+ gzt->struct_size = sizeof(wmGizmo);
+
+ gizmo_properties_init(gzt);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Gizmo Group
+ * \{ */
+
+static void gizmogroup_properties_init(wmGizmoGroupType *gzgt)
+{
+ PyTypeObject *py_class = gzgt->ext.data;
+ RNA_struct_blender_type_set(gzgt->ext.srna, gzgt);
+
+ /* only call this so pyrna_deferred_register_class gives a useful error
+ * WM_operatortype_append_ptr will call RNA_def_struct_identifier
+ * later */
+ RNA_def_struct_identifier_no_struct_map(gzgt->srna, gzgt->idname);
+
+ if (pyrna_deferred_register_class(gzgt->srna, py_class) != 0) {
+ PyErr_Print(); /* failed to register operator props */
+ PyErr_Clear();
+ }
+}
+
+void BPY_RNA_gizmogroup_wrapper(wmGizmoGroupType *gzgt, void *userdata)
+{
+ /* take care not to overwrite anything set in
+ * WM_gizmomaptype_group_link_ptr before opfunc() is called */
+ StructRNA *srna = gzgt->srna;
+ *gzgt = *((wmGizmoGroupType *)userdata);
+ gzgt->srna = srna; /* restore */
+
+ /* don't do translations here yet */
+#if 0
+ /* Use i18n context from ext.srna if possible (py gizmogroups). */
+ if (gzgt->ext.srna) {
+ RNA_def_struct_translation_context(gzgt->srna, RNA_struct_translation_context(gzgt->ext.srna));
+ }
+#endif
+
+ gizmogroup_properties_init(gzgt);
+}
+
+/** \} */
diff --git a/source/blender/python/intern/bpy_gizmo_wrap.h b/source/blender/python/intern/bpy_gizmo_wrap.h
new file mode 100644
index 00000000000..96f15312a4e
--- /dev/null
+++ b/source/blender/python/intern/bpy_gizmo_wrap.h
@@ -0,0 +1,35 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_gizmo_wrap.h
+ * \ingroup pythonintern
+ */
+
+#ifndef __BPY_GIZMO_WRAP_H__
+#define __BPY_GIZMO_WRAP_H__
+
+struct wmGizmoType;
+struct wmGizmoGroupType;
+
+/* exposed to rna/wm api */
+void BPY_RNA_gizmo_wrapper(struct wmGizmoType *gzt, void *userdata);
+void BPY_RNA_gizmogroup_wrapper(struct wmGizmoGroupType *gzgt, void *userdata);
+
+#endif /* __BPY_GIZMO_WRAP_H__ */
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 1ae3106aa76..e17e7562f2a 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -46,7 +46,6 @@
#include "RNA_types.h"
#include "bpy.h"
-#include "gpu.h"
#include "bpy_rna.h"
#include "bpy_path.h"
#include "bpy_capi_utils.h"
@@ -59,9 +58,9 @@
#include "BKE_appdir.h"
#include "BKE_context.h"
-#include "BKE_text.h"
-#include "BKE_main.h"
#include "BKE_global.h" /* only for script checking */
+#include "BKE_main.h"
+#include "BKE_text.h"
#include "CCL_api.h"
@@ -75,6 +74,7 @@
#include "../generic/blf_py_api.h"
#include "../generic/idprop_py_api.h"
#include "../generic/imbuf_py_api.h"
+#include "../gpu/gpu_py_api.h"
#include "../bmesh/bmesh_py_api.h"
#include "../mathutils/mathutils.h"
@@ -233,7 +233,7 @@ static struct _inittab bpy_internal_modules[] = {
#ifdef WITH_CYCLES
{"_cycles", CCL_initPython},
#endif
- {"gpu", GPU_initPython},
+ {"gpu", BPyInit_gpu},
{"idprop", BPyInit_idprop},
{NULL, NULL}
};
@@ -537,7 +537,8 @@ static bool python_script_exec(
if (py_dict) {
#ifdef PYMODULE_CLEAR_WORKAROUND
- PyModuleObject *mmod = (PyModuleObject *)PyDict_GetItemString(PyImport_GetModuleDict(), "__main__");
+ PyModuleObject *mmod = (PyModuleObject *)PyDict_GetItem(
+ PyImport_GetModuleDict(), bpy_intern_str___main__);
PyObject *dict_back = mmod->md_dict;
/* freeing the module will clear the namespace,
* gives problems running classes defined in this namespace being used later. */
diff --git a/source/blender/python/intern/bpy_intern_string.c b/source/blender/python/intern/bpy_intern_string.c
index ac0100fa75d..41276963fc9 100644
--- a/source/blender/python/intern/bpy_intern_string.c
+++ b/source/blender/python/intern/bpy_intern_string.c
@@ -34,21 +34,24 @@
#include "BLI_utildefines.h"
-static PyObject *bpy_intern_str_arr[13];
+static PyObject *bpy_intern_str_arr[16];
-PyObject *bpy_intern_str_register;
-PyObject *bpy_intern_str_unregister;
-PyObject *bpy_intern_str_bl_rna;
+PyObject *bpy_intern_str___annotations__;
+PyObject *bpy_intern_str___doc__;
+PyObject *bpy_intern_str___main__;
+PyObject *bpy_intern_str___module__;
+PyObject *bpy_intern_str___name__;
+PyObject *bpy_intern_str___slots__;
+PyObject *bpy_intern_str_attr;
PyObject *bpy_intern_str_bl_property;
+PyObject *bpy_intern_str_bl_rna;
+PyObject *bpy_intern_str_bl_target_properties;
PyObject *bpy_intern_str_bpy_types;
-PyObject *bpy_intern_str_order;
-PyObject *bpy_intern_str_attr;
-PyObject *bpy_intern_str_self;
PyObject *bpy_intern_str_frame;
-PyObject *bpy_intern_str___slots__;
-PyObject *bpy_intern_str___name__;
-PyObject *bpy_intern_str___doc__;
-PyObject *bpy_intern_str___module__;
+PyObject *bpy_intern_str_properties;
+PyObject *bpy_intern_str_register;
+PyObject *bpy_intern_str_self;
+PyObject *bpy_intern_str_unregister;
void bpy_intern_string_init(void)
{
@@ -57,19 +60,22 @@ void bpy_intern_string_init(void)
#define BPY_INTERN_STR(var, str) \
{ var = bpy_intern_str_arr[i++] = PyUnicode_FromString(str); } (void)0
- BPY_INTERN_STR(bpy_intern_str_register, "register");
- BPY_INTERN_STR(bpy_intern_str_unregister, "unregister");
- BPY_INTERN_STR(bpy_intern_str_bl_rna, "bl_rna");
+ BPY_INTERN_STR(bpy_intern_str___annotations__, "__annotations__");
+ BPY_INTERN_STR(bpy_intern_str___doc__, "__doc__");
+ BPY_INTERN_STR(bpy_intern_str___main__, "__main__");
+ BPY_INTERN_STR(bpy_intern_str___module__, "__module__");
+ BPY_INTERN_STR(bpy_intern_str___name__, "__name__");
+ BPY_INTERN_STR(bpy_intern_str___slots__, "__slots__");
+ BPY_INTERN_STR(bpy_intern_str_attr, "attr");
BPY_INTERN_STR(bpy_intern_str_bl_property, "bl_property");
+ BPY_INTERN_STR(bpy_intern_str_bl_rna, "bl_rna");
+ BPY_INTERN_STR(bpy_intern_str_bl_target_properties, "bl_target_properties");
BPY_INTERN_STR(bpy_intern_str_bpy_types, "bpy.types");
- BPY_INTERN_STR(bpy_intern_str_order, "order");
- BPY_INTERN_STR(bpy_intern_str_attr, "attr");
- BPY_INTERN_STR(bpy_intern_str_self, "self");
BPY_INTERN_STR(bpy_intern_str_frame, "frame");
- BPY_INTERN_STR(bpy_intern_str___slots__, "__slots__");
- BPY_INTERN_STR(bpy_intern_str___name__, "__name__");
- BPY_INTERN_STR(bpy_intern_str___doc__, "__doc__");
- BPY_INTERN_STR(bpy_intern_str___module__, "__module__");
+ BPY_INTERN_STR(bpy_intern_str_properties, "properties");
+ BPY_INTERN_STR(bpy_intern_str_register, "register");
+ BPY_INTERN_STR(bpy_intern_str_self, "self");
+ BPY_INTERN_STR(bpy_intern_str_unregister, "unregister");
#undef BPY_INTERN_STR
diff --git a/source/blender/python/intern/bpy_intern_string.h b/source/blender/python/intern/bpy_intern_string.h
index 394e84d89bd..41cd58f9c3d 100644
--- a/source/blender/python/intern/bpy_intern_string.h
+++ b/source/blender/python/intern/bpy_intern_string.h
@@ -30,18 +30,21 @@
void bpy_intern_string_init(void);
void bpy_intern_string_exit(void);
-extern PyObject *bpy_intern_str_register;
-extern PyObject *bpy_intern_str_unregister;
-extern PyObject *bpy_intern_str_bl_rna;
+extern PyObject *bpy_intern_str___annotations__;
+extern PyObject *bpy_intern_str___doc__;
+extern PyObject *bpy_intern_str___main__;
+extern PyObject *bpy_intern_str___module__;
+extern PyObject *bpy_intern_str___name__;
+extern PyObject *bpy_intern_str___slots__;
+extern PyObject *bpy_intern_str_attr;
extern PyObject *bpy_intern_str_bl_property;
+extern PyObject *bpy_intern_str_bl_rna;
+extern PyObject *bpy_intern_str_bl_target_properties;
extern PyObject *bpy_intern_str_bpy_types;
-extern PyObject *bpy_intern_str_order;
-extern PyObject *bpy_intern_str_attr;
-extern PyObject *bpy_intern_str_self;
extern PyObject *bpy_intern_str_frame;
-extern PyObject *bpy_intern_str___slots__;
-extern PyObject *bpy_intern_str___name__;
-extern PyObject *bpy_intern_str___doc__;
-extern PyObject *bpy_intern_str___module__;
+extern PyObject *bpy_intern_str_properties;
+extern PyObject *bpy_intern_str_register;
+extern PyObject *bpy_intern_str_self;
+extern PyObject *bpy_intern_str_unregister;
#endif /* __BPY_INTERN_STRING_H__ */
diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c
index e66b4e834dc..5c64a65fb58 100644
--- a/source/blender/python/intern/bpy_library_load.c
+++ b/source/blender/python/intern/bpy_library_load.c
@@ -38,16 +38,16 @@
#include "BLI_linklist.h"
#include "BLI_path_util.h"
-#include "BLO_readfile.h"
-
-#include "BKE_main.h"
-#include "BKE_library.h"
+#include "BKE_context.h"
#include "BKE_idcode.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
#include "BKE_report.h"
-#include "BKE_context.h"
#include "DNA_space_types.h" /* FILE_LINK, FILE_RELPATH */
+#include "BLO_readfile.h"
+
#include "bpy_capi_utils.h"
#include "bpy_library.h"
@@ -332,6 +332,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
Main *bmain = CTX_data_main(BPy_GetContext());
Main *mainl = NULL;
int err = 0;
+ const bool do_append = ((self->flag & FILE_LINK) == 0);
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
@@ -341,7 +342,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
{
int idcode_step = 0, idcode;
while ((idcode = BKE_idcode_iter_step(&idcode_step))) {
- if (BKE_idcode_is_linkable(idcode)) {
+ if (BKE_idcode_is_linkable(idcode) && (idcode != ID_WS || do_append)) {
const char *name_plural = BKE_idcode_to_name_plural(idcode);
PyObject *ls = PyDict_GetItemString(self->dict, name_plural);
// printf("lib: %s\n", name_plural);
@@ -402,7 +403,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
}
else {
Library *lib = mainl->curlib; /* newly added lib, assign before append end */
- BLO_library_link_end(mainl, &(self->blo_handle), self->flag, NULL, NULL);
+ BLO_library_link_end(mainl, &(self->blo_handle), self->flag, NULL, NULL, NULL, NULL);
BLO_blendhandle_close(self->blo_handle);
self->blo_handle = NULL;
@@ -414,7 +415,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
BKE_main_lib_objects_recalc_all(bmain);
/* append, rather than linking */
- if ((self->flag & FILE_LINK) == 0) {
+ if (do_append) {
BKE_library_make_local(bmain, lib, old_to_new_ids, true, false);
}
}
@@ -427,7 +428,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args))
{
int idcode_step = 0, idcode;
while ((idcode = BKE_idcode_iter_step(&idcode_step))) {
- if (BKE_idcode_is_linkable(idcode)) {
+ if (BKE_idcode_is_linkable(idcode) && (idcode != ID_WS || do_append)) {
const char *name_plural = BKE_idcode_to_name_plural(idcode);
PyObject *ls = PyDict_GetItemString(self->dict, name_plural);
if (ls && PyList_Check(ls)) {
diff --git a/source/blender/python/intern/bpy_library_write.c b/source/blender/python/intern/bpy_library_write.c
index 76b7ccf72fa..69dcfdb9455 100644
--- a/source/blender/python/intern/bpy_library_write.c
+++ b/source/blender/python/intern/bpy_library_write.c
@@ -34,7 +34,6 @@
#include "BLI_string.h"
#include "BLI_path_util.h"
-#include "BKE_library.h"
#include "BKE_blendfile.h"
#include "BKE_global.h"
#include "BKE_main.h"
@@ -135,7 +134,7 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject
if (!pyrna_id_FromPyObject(key, &id_store->id)) {
PyErr_Format(PyExc_TypeError,
- "Expected and ID type, not %.200s",
+ "Expected an ID type, not %.200s",
Py_TYPE(key)->tp_name);
ret = NULL;
goto finally;
diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c
new file mode 100644
index 00000000000..101cc8b41a3
--- /dev/null
+++ b/source/blender/python/intern/bpy_msgbus.c
@@ -0,0 +1,399 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_msgbus.c
+ * \ingroup pythonintern
+ * This file defines '_bpy_msgbus' module, exposed as 'bpy.msgbus'.
+ */
+
+#include <Python.h>
+
+#include "../generic/python_utildefines.h"
+#include "../generic/py_capi_utils.h"
+#include "../mathutils/mathutils.h"
+
+#include "BLI_utildefines.h"
+
+#include "BKE_context.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "WM_message.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "bpy_capi_utils.h"
+#include "bpy_rna.h"
+#include "bpy_intern_string.h"
+#include "bpy_gizmo_wrap.h" /* own include */
+
+
+#include "bpy_msgbus.h" /* own include */
+
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Utils
+ * \{ */
+
+#define BPY_MSGBUS_RNA_MSGKEY_DOC \
+" :arg key: Represents the type of data being subscribed to\n" \
+"\n" \
+" Arguments include\n" \
+" - :class:`bpy.types.Property` instance.\n" \
+" - :class:`bpy.types.Struct` type.\n" \
+" - (:class:`bpy.types.Struct`, str) type and property name.\n" \
+" :type key: Muliple\n"
+
+/**
+ * There are multiple ways we can get RNA from Python,
+ * it's also possible to register a type instead of an instance.
+ *
+ * This function handles converting Python to RNA subscription information.
+ *
+ * \param py_sub: See #BPY_MSGBUS_RNA_MSGKEY_DOC for description.
+ * \param msg_key_params: Message key with all members zeroed out.
+ * \return -1 on failure, 0 on success.
+ */
+static int py_msgbus_rna_key_from_py(
+ PyObject *py_sub,
+ wmMsgParams_RNA *msg_key_params,
+ const char *error_prefix)
+{
+
+ /* Allow common case, object rotation, location - etc. */
+ if (BaseMathObject_CheckExact(py_sub)) {
+ BaseMathObject *py_sub_math = (BaseMathObject *)py_sub;
+ if (py_sub_math->cb_user == NULL) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "%s: math argument has no owner",
+ error_prefix);
+ return -1;
+ }
+ py_sub = py_sub_math->cb_user;
+ /* Common case will use BPy_PropertyRNA_Check below. */
+ }
+
+ if (BPy_PropertyRNA_Check(py_sub)) {
+ BPy_PropertyRNA *data_prop = (BPy_PropertyRNA *)py_sub;
+ PYRNA_PROP_CHECK_INT(data_prop);
+ msg_key_params->ptr = data_prop->ptr;
+ msg_key_params->prop = data_prop->prop;
+ }
+ else if (BPy_StructRNA_Check(py_sub)) {
+ /* note, this isn't typically used since we don't edit structs directly. */
+ BPy_StructRNA *data_srna = (BPy_StructRNA *)py_sub;
+ PYRNA_STRUCT_CHECK_INT(data_srna);
+ msg_key_params->ptr = data_srna->ptr;
+ }
+ /* TODO - property / type, not instance. */
+ else if (PyType_Check(py_sub)) {
+ StructRNA *data_type = pyrna_struct_as_srna(py_sub, false, error_prefix);
+ if (data_type == NULL) {
+ return -1;
+ }
+ msg_key_params->ptr.type = data_type;
+ }
+ else if (PyTuple_CheckExact(py_sub)) {
+ if (PyTuple_GET_SIZE(py_sub) == 2) {
+ PyObject *data_type_py = PyTuple_GET_ITEM(py_sub, 0);
+ PyObject *data_prop_py = PyTuple_GET_ITEM(py_sub, 1);
+ StructRNA *data_type = pyrna_struct_as_srna(data_type_py, false, error_prefix);
+ if (data_type == NULL) {
+ return -1;
+ }
+ if (!PyUnicode_CheckExact(data_prop_py)) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "%s: expected property to be a string",
+ error_prefix);
+ return -1;
+ }
+ PointerRNA data_type_ptr = { .type = data_type, };
+ const char *data_prop_str = _PyUnicode_AsString(data_prop_py);
+ PropertyRNA *data_prop = RNA_struct_find_property(&data_type_ptr, data_prop_str);
+
+ if (data_prop == NULL) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "%s: struct %.200s does not contain property %.200s",
+ error_prefix,
+ RNA_struct_identifier(data_type),
+ data_prop_str);
+ return -1;
+ }
+
+ msg_key_params->ptr.type = data_type;
+ msg_key_params->prop = data_prop;
+ }
+ else {
+ PyErr_Format(
+ PyExc_ValueError,
+ "%s: Expected a pair (type, property_id)",
+ error_prefix);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Callbacks
+ * \{ */
+
+#define BPY_MSGBUS_USER_DATA_LEN 2
+
+/* Follow wmMsgNotifyFn spec */
+static void bpy_msgbus_notify(
+ bContext *C, wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val)
+{
+ PyGILState_STATE gilstate;
+ bpy_context_set(C, &gilstate);
+
+ PyObject *user_data = msg_val->user_data;
+ BLI_assert(PyTuple_GET_SIZE(user_data) == BPY_MSGBUS_USER_DATA_LEN);
+
+ PyObject *callback_args = PyTuple_GET_ITEM(user_data, 0);
+ PyObject *callback_notify = PyTuple_GET_ITEM(user_data, 1);
+
+ const bool is_write_ok = pyrna_write_check();
+ if (!is_write_ok) {
+ pyrna_write_set(true);
+ }
+
+ PyObject *ret = PyObject_CallObject(callback_notify, callback_args);
+
+ if (ret == NULL) {
+ PyC_Err_PrintWithFunc(callback_notify);
+ }
+ else {
+ if (ret != Py_None) {
+ PyErr_SetString(PyExc_ValueError, "the return value must be None");
+ PyC_Err_PrintWithFunc(callback_notify);
+ }
+ Py_DECREF(ret);
+ }
+
+ bpy_context_clear(C, &gilstate);
+
+ if (!is_write_ok) {
+ pyrna_write_set(false);
+ }
+}
+
+/* Follow wmMsgSubscribeValueFreeDataFn spec */
+static void bpy_msgbus_subscribe_value_free_data(
+ struct wmMsgSubscribeKey *UNUSED(msg_key), struct wmMsgSubscribeValue *msg_val)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+ Py_DECREF(msg_val->owner);
+ Py_DECREF(msg_val->user_data);
+ PyGILState_Release(gilstate);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public Message Bus API
+ * \{ */
+
+PyDoc_STRVAR(bpy_msgbus_subscribe_rna_doc,
+".. function:: subscribe_rna(data, owner, args, notify)\n"
+"\n"
+BPY_MSGBUS_RNA_MSGKEY_DOC
+" :arg owner: Handle for this subscription (compared by identity).\n"
+" :type owner: Any type.\n"
+"\n"
+" Returns a new vector int property definition.\n"
+);
+static PyObject *bpy_msgbus_subscribe_rna(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ const char *error_prefix = "subscribe_rna";
+ PyObject *py_sub = NULL;
+ PyObject *py_owner = NULL;
+ PyObject *callback_args = NULL;
+ PyObject *callback_notify = NULL;
+
+ enum {
+ IS_PERSISTENT = (1 << 0),
+ };
+ PyObject *py_options = NULL;
+ EnumPropertyItem py_options_enum[] = {
+ {IS_PERSISTENT, "PERSISTENT", 0, ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+ int options = 0;
+
+ static const char *_keywords[] = {
+ "key",
+ "owner",
+ "args",
+ "notify",
+ "options",
+ NULL,
+ };
+ static _PyArg_Parser _parser = {"$OOO!OO!:subscribe_rna", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kw, &_parser,
+ &py_sub, &py_owner,
+ &PyTuple_Type, &callback_args,
+ &callback_notify,
+ &PySet_Type, &py_options))
+ {
+ return NULL;
+ }
+
+ if (py_options &&
+ (pyrna_set_to_enum_bitfield(py_options_enum, py_options, &options, error_prefix)) == -1)
+ {
+ return NULL;
+ }
+
+ /* Note: we may want to have a way to pass this in. */
+ bContext *C = (bContext *)BPy_GetContext();
+ struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+ wmMsgParams_RNA msg_key_params = {{{0}}};
+
+ wmMsgSubscribeValue msg_val_params = {0};
+
+ if (py_msgbus_rna_key_from_py(py_sub, &msg_key_params, error_prefix) == -1) {
+ return NULL;
+ }
+
+ if (!PyFunction_Check(callback_notify)) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "notify expects a function, found %.200s",
+ Py_TYPE(callback_notify)->tp_name);
+ return NULL;
+ }
+
+ if (options != 0) {
+ if (options & IS_PERSISTENT) {
+ msg_val_params.is_persistent = true;
+ }
+ }
+
+ /* owner can be anything. */
+ {
+ msg_val_params.owner = py_owner;
+ Py_INCREF(py_owner);
+ }
+
+ {
+ PyObject *user_data = PyTuple_New(2);
+ PyTuple_SET_ITEMS(
+ user_data,
+ Py_INCREF_RET(callback_args),
+ Py_INCREF_RET(callback_notify));
+ msg_val_params.user_data = user_data;
+ }
+
+ msg_val_params.notify = bpy_msgbus_notify;
+ msg_val_params.free_data = bpy_msgbus_subscribe_value_free_data;
+
+ WM_msg_subscribe_rna_params(mbus, &msg_key_params, &msg_val_params, __func__);
+
+ WM_msg_dump(mbus, __func__);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(bpy_msgbus_publish_rna_doc,
+".. function:: publish_rna(data, owner, args, notify)\n"
+"\n"
+BPY_MSGBUS_RNA_MSGKEY_DOC
+"\n"
+" Notify subscribers of changes to this property\n"
+" (this typically doesn't need to be called explicitly since changes will automatically publish updates).\n"
+" In some cases it may be useful to publish changes explicitly using more general keys.\n"
+);
+static PyObject *bpy_msgbus_publish_rna(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ const char *error_prefix = "publish_rna";
+ PyObject *py_sub = NULL;
+
+ static const char *_keywords[] = {
+ "key",
+ NULL,
+ };
+ static _PyArg_Parser _parser = {"$O:publish_rna", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kw, &_parser,
+ &py_sub))
+ {
+ return NULL;
+ }
+
+ /* Note: we may want to have a way to pass this in. */
+ bContext *C = (bContext *)BPy_GetContext();
+ struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+ wmMsgParams_RNA msg_key_params = {{{0}}};
+
+ if (py_msgbus_rna_key_from_py(py_sub, &msg_key_params, error_prefix) == -1) {
+ return NULL;
+ }
+
+ WM_msg_publish_rna_params(mbus, &msg_key_params);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(bpy_msgbus_clear_by_owner_doc,
+".. function:: clear_by_owner(owner)\n"
+"\n"
+" Clear all subscribers using this owner.\n"
+);
+static PyObject *bpy_msgbus_clear_by_owner(PyObject *UNUSED(self), PyObject *py_owner)
+{
+ bContext *C = (bContext *)BPy_GetContext();
+ struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+ WM_msgbus_clear_by_owner(mbus, py_owner);
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef BPy_msgbus_methods[] = {
+ {"subscribe_rna", (PyCFunction)bpy_msgbus_subscribe_rna, METH_VARARGS | METH_KEYWORDS, bpy_msgbus_subscribe_rna_doc},
+ {"publish_rna", (PyCFunction)bpy_msgbus_publish_rna, METH_VARARGS | METH_KEYWORDS, bpy_msgbus_publish_rna_doc},
+ {"clear_by_owner", (PyCFunction)bpy_msgbus_clear_by_owner, METH_O, bpy_msgbus_clear_by_owner_doc},
+ {NULL, NULL, 0, NULL}
+};
+
+static struct PyModuleDef _bpy_msgbus_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "msgbus",
+ .m_methods = BPy_msgbus_methods,
+};
+
+
+PyObject *BPY_msgbus_module(void)
+{
+ PyObject *submodule;
+
+ submodule = PyModule_Create(&_bpy_msgbus_def);
+
+ return submodule;
+}
+
+/** \} */
diff --git a/source/blender/python/intern/bpy_msgbus.h b/source/blender/python/intern/bpy_msgbus.h
new file mode 100644
index 00000000000..97b20e9b926
--- /dev/null
+++ b/source/blender/python/intern/bpy_msgbus.h
@@ -0,0 +1,30 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_msgbus.h
+ * \ingroup pythonintern
+ */
+
+#ifndef __BPY_MSGBUS_H__
+#define __BPY_MSGBUS_H__
+
+PyObject *BPY_msgbus_module(void);
+
+#endif /* __BPY_MSGBUS_H__ */
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index 0db2fc189c1..b7242680dca 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -1905,7 +1905,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, PyObject *ge
#define BPY_PROPDEF_UNIT_DOC \
" :arg unit: Enumerator in ['NONE', 'LENGTH', 'AREA', 'VOLUME', 'ROTATION', 'TIME', 'VELOCITY', 'ACCELERATION', 'MASS', 'CAMERA'].\n" \
-" :type unit: string\n" \
+" :type unit: string\n" \
#define BPY_PROPDEF_NUM_MIN_DOC \
" :arg min: Hard minimum, trying to assign a value below will silently assign this minimum instead.\n" \
@@ -3083,9 +3083,10 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
return NULL;
if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) {
- PyErr_Format(PyExc_TypeError,
- "CollectionProperty(...) expected an RNA type derived from %.200s",
- RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup));
+ PyErr_Format(
+ PyExc_TypeError,
+ "CollectionProperty(...) expected an RNA type derived from %.200s",
+ RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup));
return NULL;
}
@@ -3203,11 +3204,6 @@ PyObject *BPY_rna_props(void)
submodule = PyModule_Create(&props_module);
PyDict_SetItemString(PyImport_GetModuleDict(), props_module.m_name, submodule);
- /* INCREF since its its assumed that all these functions return the
- * module with a new ref like PyDict_New, since they are passed to
- * PyModule_AddObject which steals a ref */
- Py_INCREF(submodule);
-
/* api needs the PyObjects internally */
submodule_dict = PyModule_GetDict(submodule);
diff --git a/source/blender/python/intern/bpy_props.h b/source/blender/python/intern/bpy_props.h
index 614c1b4b708..fa2594f94d2 100644
--- a/source/blender/python/intern/bpy_props.h
+++ b/source/blender/python/intern/bpy_props.h
@@ -34,6 +34,6 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw);
PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw);
StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix);
-#define PYRNA_STACK_ARRAY 32
+#define PYRNA_STACK_ARRAY RNA_STACK_ARRAY
#endif
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index b36a6e06651..f25f007033f 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -315,7 +315,7 @@ static bool rna_id_write_error(PointerRNA *ptr, PyObject *key)
ID *id = ptr->id.data;
if (id) {
const short idcode = GS(id->name);
- if (!ELEM(idcode, ID_WM, ID_SCR)) { /* may need more added here */
+ if (!ELEM(idcode, ID_WM, ID_SCR, ID_WS)) { /* may need more added here */
const char *idtype = BKE_idcode_to_name(idcode);
const char *pyname;
if (key && PyUnicode_Check(key)) pyname = _PyUnicode_AsString(key);
@@ -1401,53 +1401,39 @@ static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val)
ret = PyUnicode_FromString(identifier);
}
else {
+ /* Static, no need to free. */
const EnumPropertyItem *enum_item;
- bool free;
+ bool free_dummy;
+ RNA_property_enum_items_ex(NULL, ptr, prop, true, &enum_item, NULL, &free_dummy);
+ BLI_assert(!free_dummy);
- /* don't throw error here, can't trust blender 100% to give the
- * right values, python code should not generate error for that */
- RNA_property_enum_items(BPy_GetContext(), ptr, prop, &enum_item, NULL, &free);
- if (enum_item && enum_item->identifier) {
- ret = PyUnicode_FromString(enum_item->identifier);
- }
- else {
- if (free) {
- MEM_freeN((void *)enum_item);
- }
- RNA_property_enum_items(NULL, ptr, prop, &enum_item, NULL, &free);
-
- /* Do not print warning in case of DummyRNA_NULL_items, this one will never match any value... */
- if (enum_item != DummyRNA_NULL_items) {
- const char *ptr_name = RNA_struct_name_get_alloc(ptr, NULL, 0, NULL);
+ /* Do not print warning in case of DummyRNA_NULL_items, this one will never match any value... */
+ if (enum_item != DummyRNA_NULL_items) {
+ const char *ptr_name = RNA_struct_name_get_alloc(ptr, NULL, 0, NULL);
- /* prefer not fail silently in case of api errors, maybe disable it later */
- CLOG_WARN(BPY_LOG_RNA,
- "current value '%d' "
- "matches no enum in '%s', '%s', '%s'",
- val, RNA_struct_identifier(ptr->type),
- ptr_name, RNA_property_identifier(prop));
+ /* prefer not fail silently in case of api errors, maybe disable it later */
+ CLOG_WARN(BPY_LOG_RNA,
+ "current value '%d' "
+ "matches no enum in '%s', '%s', '%s'",
+ val, RNA_struct_identifier(ptr->type),
+ ptr_name, RNA_property_identifier(prop));
#if 0 /* gives python decoding errors while generating docs :( */
- char error_str[256];
- BLI_snprintf(error_str, sizeof(error_str),
- "RNA Warning: Current value \"%d\" "
- "matches no enum in '%s', '%s', '%s'",
- val, RNA_struct_identifier(ptr->type),
- ptr_name, RNA_property_identifier(prop));
-
- PyErr_Warn(PyExc_RuntimeWarning, error_str);
+ char error_str[256];
+ BLI_snprintf(error_str, sizeof(error_str),
+ "RNA Warning: Current value \"%d\" "
+ "matches no enum in '%s', '%s', '%s'",
+ val, RNA_struct_identifier(ptr->type),
+ ptr_name, RNA_property_identifier(prop));
+
+ PyErr_Warn(PyExc_RuntimeWarning, error_str);
#endif
- if (ptr_name)
- MEM_freeN((void *)ptr_name);
- }
-
- ret = PyUnicode_FromString("");
+ if (ptr_name)
+ MEM_freeN((void *)ptr_name);
}
- if (free) {
- MEM_freeN((void *)enum_item);
- }
+ ret = PyUnicode_FromString("");
#if 0
PyErr_Format(PyExc_AttributeError,
"RNA Error: Current value \"%d\" matches no enum", val);
@@ -1729,7 +1715,25 @@ static int pyrna_py_to_prop(
const int subtype = RNA_property_subtype(prop);
const char *param;
- if (subtype == PROP_BYTESTRING) {
+ if (value == Py_None) {
+ if ((RNA_property_flag(prop) & PROP_NEVER_NULL) == 0) {
+ if (data) {
+ *((char **)data) = (char *)NULL;
+ }
+ else {
+ RNA_property_string_set(ptr, prop, NULL);
+ }
+ }
+ else {
+ PyC_Err_Format_Prefix(
+ PyExc_TypeError,
+ "%.200s %.200s.%.200s doesn't support None from string types",
+ error_prefix, RNA_struct_identifier(ptr->type),
+ RNA_property_identifier(prop));
+ return -1;
+ }
+ }
+ else if (subtype == PROP_BYTESTRING) {
/* Byte String */
@@ -1849,19 +1853,28 @@ static int pyrna_py_to_prop(
* class mixing if this causes problems in the future it should be removed.
*/
if ((ptr_type == &RNA_AnyType) &&
- (BPy_StructRNA_Check(value)) &&
- (RNA_struct_is_a(((BPy_StructRNA *)value)->ptr.type, &RNA_Operator)))
+ (BPy_StructRNA_Check(value)))
{
- value = PyObject_GetAttrString(value, "properties");
- value_new = value;
+ const StructRNA *base_type =
+ RNA_struct_base_child_of(((const BPy_StructRNA *)value)->ptr.type, NULL);
+ if (ELEM(base_type, &RNA_Operator, &RNA_Gizmo)) {
+ value = PyObject_GetAttr(value, bpy_intern_str_properties);
+ value_new = value;
+ }
}
-
- /* if property is an OperatorProperties pointer and value is a map,
+ /* if property is an OperatorProperties/GizmoProperties pointer and value is a map,
* forward back to pyrna_pydict_to_props */
- if (RNA_struct_is_a(ptr_type, &RNA_OperatorProperties) && PyDict_Check(value)) {
- PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
- return pyrna_pydict_to_props(&opptr, value, false, error_prefix);
+ if (PyDict_Check(value)) {
+ const StructRNA *base_type = RNA_struct_base_child_of(ptr_type, NULL);
+ if (base_type == &RNA_OperatorProperties) {
+ PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
+ return pyrna_pydict_to_props(&opptr, value, false, error_prefix);
+ }
+ else if (base_type == &RNA_GizmoProperties) {
+ PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
+ return pyrna_pydict_to_props(&opptr, value, false, error_prefix);
+ }
}
/* another exception, allow to pass a collection as an RNA property */
@@ -3500,6 +3513,68 @@ static PyObject *pyrna_struct_is_property_readonly(BPy_StructRNA *self, PyObject
return PyBool_FromLong(!RNA_property_editable(&self->ptr, prop));
}
+
+PyDoc_STRVAR(pyrna_struct_is_property_overridable_static_doc,
+".. method:: is_property_overridable_static(property)\n"
+"\n"
+" Check if a property is statically overridable.\n"
+"\n"
+" :return: True when the property is statically overridable.\n"
+" :rtype: boolean\n"
+);
+static PyObject *pyrna_struct_is_property_overridable_static(BPy_StructRNA *self, PyObject *args)
+{
+ PropertyRNA *prop;
+ const char *name;
+
+ PYRNA_STRUCT_CHECK_OBJ(self);
+
+ if (!PyArg_ParseTuple(args, "s:is_property_overridable_static", &name)) {
+ return NULL;
+ }
+
+ if ((prop = RNA_struct_find_property(&self->ptr, name)) == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s.is_property_overridable_static(\"%.200s\") not found",
+ RNA_struct_identifier(self->ptr.type), name);
+ return NULL;
+ }
+
+ return PyBool_FromLong((long)RNA_property_overridable_get(&self->ptr, prop));
+}
+
+PyDoc_STRVAR(pyrna_struct_property_overridable_static_set_doc,
+".. method:: property_overridable_static_set(property)\n"
+"\n"
+" Define a property as statically overridable or not (only for custom properties!).\n"
+"\n"
+" :return: True when the overridable status of the property was successfully set.\n"
+" :rtype: boolean\n"
+);
+static PyObject *pyrna_struct_property_overridable_static_set(BPy_StructRNA *self, PyObject *args)
+{
+ PropertyRNA *prop;
+ const char *name;
+ int is_overridable;
+
+ PYRNA_STRUCT_CHECK_OBJ(self);
+
+ if (!PyArg_ParseTuple(args, "sp:property_overridable_static_set", &name, &is_overridable)) {
+ return NULL;
+ }
+
+ if ((prop = RNA_struct_find_property(&self->ptr, name)) == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s.property_overridable_static_set(\"%.200s\") not found",
+ RNA_struct_identifier(self->ptr.type), name);
+ return NULL;
+ }
+
+ return PyBool_FromLong((long)RNA_property_overridable_static_set(&self->ptr, prop, (bool)is_overridable));
+}
+
+
+
PyDoc_STRVAR(pyrna_struct_path_resolve_doc,
".. method:: path_resolve(path, coerce=True)\n"
"\n"
@@ -5172,6 +5247,8 @@ static struct PyMethodDef pyrna_struct_methods[] = {
{"property_unset", (PyCFunction)pyrna_struct_property_unset, METH_VARARGS, pyrna_struct_property_unset_doc},
{"is_property_hidden", (PyCFunction)pyrna_struct_is_property_hidden, METH_VARARGS, pyrna_struct_is_property_hidden_doc},
{"is_property_readonly", (PyCFunction)pyrna_struct_is_property_readonly, METH_VARARGS, pyrna_struct_is_property_readonly_doc},
+ {"is_property_overridable_static", (PyCFunction)pyrna_struct_is_property_overridable_static, METH_VARARGS, pyrna_struct_is_property_overridable_static_doc},
+ {"property_overridable_static_set", (PyCFunction)pyrna_struct_property_overridable_static_set, METH_VARARGS, pyrna_struct_property_overridable_static_set_doc},
{"path_resolve", (PyCFunction)pyrna_struct_path_resolve, METH_VARARGS, pyrna_struct_path_resolve_doc},
{"path_from_id", (PyCFunction)pyrna_struct_path_from_id, METH_VARARGS, pyrna_struct_path_from_id_doc},
{"type_recast", (PyCFunction)pyrna_struct_type_recast, METH_NOARGS, pyrna_struct_type_recast_doc},
@@ -5581,6 +5658,17 @@ static PyObject *pyrna_func_call(BPy_FunctionRNA *self, PyObject *args, PyObject
item = NULL;
if (i < pyargs_len) {
+ /* New in 2.8x, optional arguments must be keywords. */
+ if (UNLIKELY((flag_parameter & PARM_REQUIRED) == 0)) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s.%.200s(): required parameter \"%.200s\" to be a keyword argument!",
+ RNA_struct_identifier(self_ptr->type),
+ RNA_function_identifier(self_func),
+ RNA_property_identifier(parm));
+ err = -1;
+ break;
+ }
+
item = PyTuple_GET_ITEM(args, i);
kw_arg = false;
}
@@ -7353,29 +7441,40 @@ static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item
static int pyrna_deferred_register_props(StructRNA *srna, PyObject *class_dict)
{
+ PyObject *annotations_dict;
PyObject *item, *key;
- PyObject *order;
Py_ssize_t pos = 0;
int ret = 0;
/* in both cases PyDict_CheckExact(class_dict) will be true even
* though Operators have a metaclass dict namespace */
+ if ((annotations_dict = PyDict_GetItem(class_dict, bpy_intern_str___annotations__)) &&
+ PyDict_CheckExact(annotations_dict))
+ {
+ while (PyDict_Next(annotations_dict, &pos, &key, &item)) {
+ ret = deferred_register_prop(srna, key, item);
- if ((order = PyDict_GetItem(class_dict, bpy_intern_str_order)) && PyList_CheckExact(order)) {
- for (pos = 0; pos < PyList_GET_SIZE(order); pos++) {
- key = PyList_GET_ITEM(order, pos);
- /* however unlikely its possible
- * fails in py 3.3 beta with __qualname__ */
- if ((item = PyDict_GetItem(class_dict, key))) {
- ret = deferred_register_prop(srna, key, item);
- if (ret != 0) {
- break;
- }
+ if (ret != 0) {
+ break;
}
}
}
- else {
+
+ {
+ /* This block can be removed once 2.8x is released and annotations are in use. */
+ bool has_warning = false;
while (PyDict_Next(class_dict, &pos, &key, &item)) {
+ if (pyrna_is_deferred_prop(item)) {
+ if (!has_warning) {
+ printf("Warning: class %.200s "
+ "contains a properties which should be an annotation!\n",
+ RNA_struct_identifier(srna));
+ PyC_LineSpit();
+ has_warning = true;
+ }
+ printf(" make annotation: %.200s.%.200s\n",
+ RNA_struct_identifier(srna), _PyUnicode_AsString(key));
+ }
ret = deferred_register_prop(srna, key, item);
if (ret != 0)
@@ -7658,7 +7757,8 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
PyGILState_STATE gilstate;
#ifdef USE_PEDANTIC_WRITE
- const bool is_readonly_init = !RNA_struct_is_a(ptr->type, &RNA_Operator);
+ const bool is_readonly_init = !(RNA_struct_is_a(ptr->type, &RNA_Operator) ||
+ RNA_struct_is_a(ptr->type, &RNA_Gizmo));
// const char *func_id = RNA_function_identifier(func); /* UNUSED */
/* testing, for correctness, not operator and not draw function */
const bool is_readonly = !(RNA_function_flag(func) & FUNC_ALLOW_WRITE);
@@ -8316,6 +8416,43 @@ static PyObject *pyrna_unregister_class(PyObject *UNUSED(self), PyObject *py_cla
Py_RETURN_NONE;
}
+/* Access to 'owner_id' internal global. */
+
+static PyObject *pyrna_bl_owner_id_get(PyObject *UNUSED(self))
+{
+ const char *name = RNA_struct_state_owner_get();
+ if (name) {
+ return PyUnicode_FromString(name);
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *pyrna_bl_owner_id_set(PyObject *UNUSED(self), PyObject *value)
+{
+ const char *name;
+ if (value == Py_None) {
+ name = NULL;
+ }
+ else if (PyUnicode_Check(value)) {
+ name = _PyUnicode_AsString(value);
+ }
+ else {
+ PyErr_Format(PyExc_ValueError,
+ "owner_set(...): "
+ "expected None or a string, not '%.200s'", Py_TYPE(value)->tp_name);
+ return NULL;
+ }
+ RNA_struct_state_owner_set(name);
+ Py_RETURN_NONE;
+}
+
+PyMethodDef meth_bpy_owner_id_get = {
+ "_bl_owner_id_get", (PyCFunction)pyrna_bl_owner_id_get, METH_NOARGS, NULL,
+};
+PyMethodDef meth_bpy_owner_id_set = {
+ "_bl_owner_id_set", (PyCFunction)pyrna_bl_owner_id_set, METH_O, NULL,
+};
+
/* currently this is fairly limited, we would need to make some way to split up
* pyrna_callback_classmethod_... if we want more than one callback per type */
typedef struct BPyRNA_CallBack {
@@ -8323,9 +8460,46 @@ typedef struct BPyRNA_CallBack {
StructRNA *bpy_srna;
} PyRNA_CallBack;
+PyDoc_STRVAR(pyrna_draw_handler_add_doc,
+".. method:: draw_handler_add(callback, args, region_type, draw_type)\n"
+"\n"
+" Add a new draw handler to this space type.\n"
+" It will be called every time the specified region in the space type will be drawn.\n"
+" Note: All arguments are positional only for now.\n"
+"\n"
+" :param callback:\n"
+" A function that will be called when the region is drawn.\n"
+" It gets the specified arguments as input.\n"
+" :type callback: function\n"
+" :param args: Arguments that will be passed to the callback.\n"
+" :type args: tuple\n"
+" :param region_type: The region type the callback draws in; usually `'WINDOW'`. (:class:`bpy.types.Region.type`)\n"
+" :type region_type: str\n"
+" :param draw_type: Usually `POST_PIXEL` for 2D drawing and `POST_VIEW` for 3D drawing. In some cases `PRE_VIEW` can be used.\n"
+" :type draw_type: str\n"
+" :return: Handler that can be removed later on.\n"
+" :rtype: object"
+);
+
+PyDoc_STRVAR(pyrna_draw_handler_remove_doc,
+".. method:: draw_handler_remove(handler, region_type)\n"
+"\n"
+" Remove a draw handler that was added previously.\n"
+"\n"
+" :param handler: The draw handler that should be removed.\n"
+" :type handler: object\n"
+" :param region_type: Region type the callback was added to.\n"
+" :type region_type: str\n"
+);
+
static struct BPyRNA_CallBack pyrna_cb_methods[] = {
- {{"draw_handler_add", (PyCFunction)pyrna_callback_classmethod_add, METH_VARARGS | METH_STATIC, ""}, &RNA_Space},
- {{"draw_handler_remove", (PyCFunction)pyrna_callback_classmethod_remove, METH_VARARGS | METH_STATIC, ""}, &RNA_Space},
+ {{"draw_handler_add", (PyCFunction)pyrna_callback_classmethod_add,
+ METH_VARARGS | METH_STATIC, pyrna_draw_handler_add_doc}, &RNA_Space},
+ {{"draw_handler_remove", (PyCFunction)pyrna_callback_classmethod_remove,
+ METH_VARARGS | METH_STATIC, pyrna_draw_handler_remove_doc}, &RNA_Space},
+
+ {{"draw_cursor_add", (PyCFunction)pyrna_callback_classmethod_add, METH_VARARGS | METH_STATIC, ""}, &RNA_WindowManager},
+ {{"draw_cursor_remove", (PyCFunction)pyrna_callback_classmethod_remove, METH_VARARGS | METH_STATIC, ""}, &RNA_WindowManager},
{{NULL, NULL, 0, NULL}, NULL}
};
diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h
index f666294666e..32a63acde40 100644
--- a/source/blender/python/intern/bpy_rna.h
+++ b/source/blender/python/intern/bpy_rna.h
@@ -225,4 +225,8 @@ int pyrna_prop_validity_check(BPy_PropertyRNA *self);
extern PyMethodDef meth_bpy_register_class;
extern PyMethodDef meth_bpy_unregister_class;
+/* bpy.utils._bl_owner_(get/set) */
+extern PyMethodDef meth_bpy_owner_id_set;
+extern PyMethodDef meth_bpy_owner_id_get;
+
#endif
diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c
index 73809e96560..2727b81e1cf 100644
--- a/source/blender/python/intern/bpy_rna_anim.c
+++ b/source/blender/python/intern/bpy_rna_anim.c
@@ -33,6 +33,7 @@
#include "BLI_utildefines.h"
#include "BLI_string.h"
+#include "BLI_string_utils.h"
#include "DNA_scene_types.h"
#include "DNA_anim_types.h"
@@ -61,9 +62,9 @@
#include "../generic/python_utildefines.h"
/* for keyframes and drivers */
-static int pyrna_struct_anim_args_parse(
+static int pyrna_struct_anim_args_parse_ex(
PointerRNA *ptr, const char *error_prefix, const char *path,
- const char **path_full, int *index)
+ const char **r_path_full, int *r_index, bool *r_path_no_validate)
{
const bool is_idbase = RNA_struct_is_ID(ptr->type);
PropertyRNA *prop;
@@ -78,11 +79,11 @@ static int pyrna_struct_anim_args_parse(
/* full paths can only be given from ID base */
if (is_idbase) {
- int r_index = -1;
- if (RNA_path_resolve_property_full(ptr, path, &r_ptr, &prop, &r_index) == false) {
+ int path_index = -1;
+ if (RNA_path_resolve_property_full(ptr, path, &r_ptr, &prop, &path_index) == false) {
prop = NULL;
}
- else if (r_index != -1) {
+ else if (path_index != -1) {
PyErr_Format(PyExc_ValueError,
"%.200s path includes index, must be a separate argument",
error_prefix, path);
@@ -101,86 +102,167 @@ static int pyrna_struct_anim_args_parse(
}
if (prop == NULL) {
+ if (r_path_no_validate) {
+ *r_path_no_validate = true;
+ return -1;
+ }
PyErr_Format(PyExc_TypeError,
"%.200s property \"%s\" not found",
error_prefix, path);
return -1;
}
- if (!RNA_property_animateable(&r_ptr, prop)) {
- PyErr_Format(PyExc_TypeError,
- "%.200s property \"%s\" not animatable",
- error_prefix, path);
- return -1;
+ if (r_path_no_validate) {
+ /* Don't touch the index. */
}
+ else {
+ if (!RNA_property_animateable(&r_ptr, prop)) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s property \"%s\" not animatable",
+ error_prefix, path);
+ return -1;
+ }
- if (RNA_property_array_check(prop) == 0) {
- if ((*index) == -1) {
- *index = 0;
+ if (RNA_property_array_check(prop) == 0) {
+ if ((*r_index) == -1) {
+ *r_index = 0;
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s index %d was given while property \"%s\" is not an array",
+ error_prefix, *r_index, path);
+ return -1;
+ }
}
else {
- PyErr_Format(PyExc_TypeError,
- "%.200s index %d was given while property \"%s\" is not an array",
- error_prefix, *index, path);
- return -1;
+ int array_len = RNA_property_array_length(&r_ptr, prop);
+ if ((*r_index) < -1 || (*r_index) >= array_len) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s index out of range \"%s\", given %d, array length is %d",
+ error_prefix, path, *r_index, array_len);
+ return -1;
+ }
}
}
+
+ if (is_idbase) {
+ *r_path_full = BLI_strdup(path);
+ }
else {
- int array_len = RNA_property_array_length(&r_ptr, prop);
- if ((*index) < -1 || (*index) >= array_len) {
+ *r_path_full = RNA_path_from_ID_to_property(&r_ptr, prop);
+
+ if (*r_path_full == NULL) {
PyErr_Format(PyExc_TypeError,
- "%.200s index out of range \"%s\", given %d, array length is %d",
- error_prefix, path, *index, array_len);
+ "%.200s could not make path to \"%s\"",
+ error_prefix, path);
return -1;
}
}
+ return 0;
+}
+
+static int pyrna_struct_anim_args_parse(
+ PointerRNA *ptr, const char *error_prefix, const char *path,
+ const char **r_path_full, int *r_index)
+{
+ return pyrna_struct_anim_args_parse_ex(ptr, error_prefix, path, r_path_full, r_index, NULL);
+}
+
+/**
+ * Unlike #pyrna_struct_anim_args_parse \a r_path_full may be copied from \a path.
+ */
+static int pyrna_struct_anim_args_parse_no_resolve(
+ PointerRNA *ptr, const char *error_prefix, const char *path,
+ const char **r_path_full)
+{
+ const bool is_idbase = RNA_struct_is_ID(ptr->type);
if (is_idbase) {
- *path_full = BLI_strdup(path);
+ *r_path_full = path;
+ return 0;
}
else {
- *path_full = RNA_path_from_ID_to_property(&r_ptr, prop);
-
- if (*path_full == NULL) {
+ char *path_prefix = RNA_path_from_ID_to_struct(ptr);
+ if (path_prefix == NULL) {
PyErr_Format(PyExc_TypeError,
- "%.200s could not make path to \"%s\"",
- error_prefix, path);
+ "%.200s could not make path for type %s",
+ error_prefix, RNA_struct_identifier(ptr->type));
return -1;
}
+
+ if (*path == '[') {
+ *r_path_full = BLI_string_joinN(path_prefix, path);
+ }
+ else {
+ *r_path_full = BLI_string_join_by_sep_charN('.', path_prefix, path);
+ }
+ MEM_freeN(path_prefix);
}
+ return 0;
+}
+static int pyrna_struct_anim_args_parse_no_resolve_fallback(
+ PointerRNA *ptr, const char *error_prefix, const char *path,
+ const char **r_path_full, int *r_index)
+{
+ bool path_unresolved = false;
+ if (pyrna_struct_anim_args_parse_ex(
+ ptr, error_prefix, path,
+ r_path_full, r_index, &path_unresolved) == -1)
+ {
+ if (path_unresolved == true) {
+ if (pyrna_struct_anim_args_parse_no_resolve(
+ ptr, error_prefix, path, r_path_full) == -1)
+ {
+ return -1;
+ }
+ }
+ else {
+ return -1;
+ }
+ }
return 0;
}
/* internal use for insert and delete */
static int pyrna_struct_keyframe_parse(
PointerRNA *ptr, PyObject *args, PyObject *kw, const char *parse_str, const char *error_prefix,
- const char **path_full, int *index, float *cfra, const char **group_name, int *options) /* return values */
+ /* return values */
+ const char **r_path_full, int *r_index, float *r_cfra, const char **r_group_name, int *r_options)
{
static const char *kwlist[] = {"data_path", "index", "frame", "group", "options", NULL};
PyObject *pyoptions = NULL;
const char *path;
/* note, parse_str MUST start with 's|ifsO!' */
- if (!PyArg_ParseTupleAndKeywords(args, kw, parse_str, (char **)kwlist, &path, index, cfra, group_name,
- &PySet_Type, &pyoptions))
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, parse_str, (char **)kwlist, &path, r_index, r_cfra, r_group_name,
+ &PySet_Type, &pyoptions))
{
return -1;
}
- if (pyrna_struct_anim_args_parse(ptr, error_prefix, path, path_full, index) == -1)
+ if (pyrna_struct_anim_args_parse(
+ ptr, error_prefix, path,
+ r_path_full, r_index) == -1)
+ {
return -1;
+ }
- if (*cfra == FLT_MAX)
- *cfra = CTX_data_scene(BPy_GetContext())->r.cfra;
+ if (*r_cfra == FLT_MAX) {
+ *r_cfra = CTX_data_scene(BPy_GetContext())->r.cfra;
+ }
/* flag may be null (no option currently for remove keyframes e.g.). */
- if (options) {
- if (pyoptions && (pyrna_set_to_enum_bitfield(rna_enum_keying_flag_items, pyoptions, options, error_prefix) == -1)) {
+ if (r_options) {
+ if (pyoptions &&
+ (pyrna_set_to_enum_bitfield(
+ rna_enum_keying_flag_items, pyoptions, r_options, error_prefix) == -1))
+ {
return -1;
}
- *options |= INSERTKEY_NO_USERPREF;
+ *r_options |= INSERTKEY_NO_USERPREF;
}
return 0; /* success */
@@ -222,9 +304,10 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
PYRNA_STRUCT_CHECK_OBJ(self);
- if (pyrna_struct_keyframe_parse(&self->ptr, args, kw,
- "s|ifsO!:bpy_struct.keyframe_insert()", "bpy_struct.keyframe_insert()",
- &path_full, &index, &cfra, &group_name, &options) == -1)
+ if (pyrna_struct_keyframe_parse(
+ &self->ptr, args, kw,
+ "s|ifsO!:bpy_struct.keyframe_insert()", "bpy_struct.keyframe_insert()",
+ &path_full, &index, &cfra, &group_name, &options) == -1)
{
return NULL;
}
@@ -233,6 +316,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
* strips themselves. These are stored separately or else the properties will
* not have any effect.
*/
+ struct Depsgraph *depsgraph = CTX_data_depsgraph(BPy_GetContext());
ReportList reports;
short result = 0;
@@ -254,7 +338,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
NlaStrip *strip = (NlaStrip *)ptr.data;
FCurve *fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), index);
- result = insert_keyframe_direct(&reports, ptr, prop, fcu, cfra, keytype, options);
+ result = insert_keyframe_direct(depsgraph, &reports, ptr, prop, fcu, cfra, keytype, NULL, options);
}
else {
BKE_reportf(&reports, RPT_ERROR, "Could not resolve path (%s)", path_full);
@@ -268,13 +352,14 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
}
else {
ID *id = self->ptr.id.data;
+ struct Depsgraph *depsgraph = CTX_data_depsgraph(BPy_GetContext());
ReportList reports;
short result;
BKE_reports_init(&reports, RPT_STORE);
- BLI_assert(BKE_id_is_in_gobal_main(id));
- result = insert_keyframe(G_MAIN, &reports, id, NULL, group_name, path_full, index, cfra, keytype, options);
+ BLI_assert(BKE_id_is_in_global_main(id));
+ result = insert_keyframe(G_MAIN, depsgraph, &reports, id, NULL, group_name, path_full, index, cfra, keytype, NULL, options);
MEM_freeN((void *)path_full);
if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1)
@@ -384,7 +469,7 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb
BKE_reports_init(&reports, RPT_STORE);
- result = delete_keyframe(&reports, (ID *)self->ptr.id.data, NULL, group_name, path_full, index, cfra, 0);
+ result = delete_keyframe(G.main, &reports, (ID *)self->ptr.id.data, NULL, group_name, path_full, index, cfra, 0);
MEM_freeN((void *)path_full);
if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1)
@@ -417,7 +502,10 @@ PyObject *pyrna_struct_driver_add(BPy_StructRNA *self, PyObject *args)
if (!PyArg_ParseTuple(args, "s|i:driver_add", &path, &index))
return NULL;
- if (pyrna_struct_anim_args_parse(&self->ptr, "bpy_struct.driver_add():", path, &path_full, &index) == -1) {
+ if (pyrna_struct_anim_args_parse(
+ &self->ptr, "bpy_struct.driver_add():", path,
+ &path_full, &index) == -1)
+ {
return NULL;
}
else {
@@ -488,10 +576,14 @@ PyObject *pyrna_struct_driver_remove(BPy_StructRNA *self, PyObject *args)
PYRNA_STRUCT_CHECK_OBJ(self);
- if (!PyArg_ParseTuple(args, "s|i:driver_remove", &path, &index))
+ if (!PyArg_ParseTuple(args, "s|i:driver_remove", &path, &index)) {
return NULL;
+ }
- if (pyrna_struct_anim_args_parse(&self->ptr, "bpy_struct.driver_remove():", path, &path_full, &index) == -1) {
+ if (pyrna_struct_anim_args_parse_no_resolve_fallback(
+ &self->ptr, "bpy_struct.driver_remove():", path,
+ &path_full, &index) == -1)
+ {
return NULL;
}
else {
@@ -502,7 +594,9 @@ PyObject *pyrna_struct_driver_remove(BPy_StructRNA *self, PyObject *args)
result = ANIM_remove_driver(&reports, (ID *)self->ptr.id.data, path_full, index, 0);
- MEM_freeN((void *)path_full);
+ if (path != path_full) {
+ MEM_freeN((void *)path_full);
+ }
if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1)
return NULL;
diff --git a/source/blender/python/intern/bpy_rna_callback.c b/source/blender/python/intern/bpy_rna_callback.c
index fbdcc03ddd0..ed29aa795e6 100644
--- a/source/blender/python/intern/bpy_rna_callback.c
+++ b/source/blender/python/intern/bpy_rna_callback.c
@@ -33,6 +33,7 @@
#include "RNA_types.h"
#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
#include "bpy_rna.h"
#include "bpy_rna_callback.h"
@@ -47,8 +48,12 @@
#include "BKE_context.h"
#include "BKE_screen.h"
+#include "WM_api.h"
+
#include "ED_space_api.h"
+#include "../generic/python_utildefines.h"
+
/* use this to stop other capsules from being mis-used */
#define RNA_CAPSULE_ID "RNA_HANDLE"
#define RNA_CAPSULE_ID_INVALID "RNA_HANDLE_REMOVED"
@@ -82,6 +87,53 @@ static void cb_region_draw(const bContext *C, ARegion *UNUSED(ar), void *customd
bpy_context_clear((bContext *)C, &gilstate);
}
+/* We could make generic utility */
+static PyObject *PyC_Tuple_CopySized(PyObject *src, int len_dst)
+{
+ PyObject *dst = PyTuple_New(len_dst);
+ int len_src = PyTuple_GET_SIZE(src);
+ BLI_assert(len_src <= len_dst);
+ for (int i = 0; i < len_src; i++) {
+ PyObject *item = PyTuple_GET_ITEM(src, i);
+ PyTuple_SET_ITEM(dst, i, item);
+ Py_INCREF(item);
+ }
+ return dst;
+}
+
+static void cb_wm_cursor_draw(bContext *C, int x, int y, void *customdata)
+{
+ PyObject *cb_func, *cb_args, *result;
+ PyGILState_STATE gilstate;
+
+ bpy_context_set((bContext *)C, &gilstate);
+
+ cb_func = PyTuple_GET_ITEM((PyObject *)customdata, 1);
+ cb_args = PyTuple_GET_ITEM((PyObject *)customdata, 2);
+
+ const int cb_args_len = PyTuple_GET_SIZE(cb_args);
+
+ PyObject *cb_args_xy = PyTuple_New(2);
+ PyTuple_SET_ITEMS(cb_args_xy, PyLong_FromLong(x), PyLong_FromLong(y));
+
+ PyObject *cb_args_with_xy = PyC_Tuple_CopySized(cb_args, cb_args_len + 1);
+ PyTuple_SET_ITEM(cb_args_with_xy, cb_args_len, cb_args_xy);
+
+ result = PyObject_CallObject(cb_func, cb_args_with_xy);
+
+ Py_DECREF(cb_args_with_xy);
+
+ if (result) {
+ Py_DECREF(result);
+ }
+ else {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ bpy_context_clear((bContext *)C, &gilstate);
+}
+
#if 0
PyObject *pyrna_callback_add(BPy_StructRNA *self, PyObject *args)
{
@@ -171,11 +223,9 @@ static eSpace_Type rna_Space_refine_reverse(StructRNA *srna)
if (srna == &RNA_SpaceTextEditor) return SPACE_TEXT;
if (srna == &RNA_SpaceDopeSheetEditor) return SPACE_ACTION;
if (srna == &RNA_SpaceNLA) return SPACE_NLA;
- if (srna == &RNA_SpaceTimeline) return SPACE_TIME;
if (srna == &RNA_SpaceNodeEditor) return SPACE_NODE;
- if (srna == &RNA_SpaceLogicEditor) return SPACE_LOGIC;
if (srna == &RNA_SpaceConsole) return SPACE_CONSOLE;
- if (srna == &RNA_SpaceUserPreferences) return SPACE_USERPREF;
+ if (srna == &RNA_SpacePreferences) return SPACE_USERPREF;
if (srna == &RNA_SpaceClipEditor) return SPACE_CLIP;
return SPACE_EMPTY;
}
@@ -185,10 +235,6 @@ PyObject *pyrna_callback_classmethod_add(PyObject *UNUSED(self), PyObject *args)
void *handle;
PyObject *cls;
PyObject *cb_func, *cb_args;
- const char *cb_regiontype_str;
- const char *cb_event_str;
- int cb_event;
- int cb_regiontype;
StructRNA *srna;
if (PyTuple_GET_SIZE(args) < 2) {
@@ -207,23 +253,78 @@ PyObject *pyrna_callback_classmethod_add(PyObject *UNUSED(self), PyObject *args)
}
/* class specific callbacks */
- if (RNA_struct_is_a(srna, &RNA_Space)) {
- if (!PyArg_ParseTuple(args, "OOO!ss:Space.draw_handler_add",
- &cls, &cb_func, /* already assigned, no matter */
- &PyTuple_Type, &cb_args, &cb_regiontype_str, &cb_event_str))
+
+ if (srna == &RNA_WindowManager) {
+ const char *error_prefix = "WindowManager.draw_cursor_add";
+ struct {
+ const char *space_type_str;
+ const char *region_type_str;
+
+ int space_type;
+ int region_type;
+ } params = {
+ .space_type_str = NULL,
+ .region_type_str = NULL,
+ .space_type = SPACE_TYPE_ANY,
+ .region_type = RGN_TYPE_ANY,
+ };
+
+ if (!PyArg_ParseTuple(
+ args, "OOO!|ss:WindowManager.draw_cursor_add",
+ &cls, &cb_func, /* already assigned, no matter */
+ &PyTuple_Type, &cb_args, &params.space_type_str, &params.region_type_str))
+ {
+ return NULL;
+ }
+
+ if (params.space_type_str && pyrna_enum_value_from_id(
+ rna_enum_space_type_items, params.space_type_str,
+ &params.space_type, error_prefix) == -1)
+ {
+ return NULL;
+ }
+ else if (params.region_type_str && pyrna_enum_value_from_id(
+ rna_enum_region_type_items, params.region_type_str,
+ &params.region_type, error_prefix) == -1)
+ {
+ return NULL;
+ }
+
+ bContext *C = BPy_GetContext();
+ struct wmWindowManager *wm = CTX_wm_manager(C);
+ handle = WM_paint_cursor_activate(
+ wm,
+ params.space_type, params.region_type,
+ NULL, cb_wm_cursor_draw, (void *)args);
+ }
+ else if (RNA_struct_is_a(srna, &RNA_Space)) {
+ const char *error_prefix = "Space.draw_handler_add";
+ struct {
+ const char *region_type_str;
+ const char *event_str;
+
+ int region_type;
+ int event;
+ } params;
+
+ if (!PyArg_ParseTuple(
+ args, "OOO!ss:Space.draw_handler_add",
+ &cls, &cb_func, /* already assigned, no matter */
+ &PyTuple_Type, &cb_args,
+ &params.region_type_str, &params.event_str))
{
return NULL;
}
if (pyrna_enum_value_from_id(
- region_draw_mode_items, cb_event_str,
- &cb_event, "bpy_struct.callback_add()") == -1)
+ region_draw_mode_items, params.event_str,
+ &params.event, error_prefix) == -1)
{
return NULL;
}
else if (pyrna_enum_value_from_id(
- rna_enum_region_type_items, cb_regiontype_str,
- &cb_regiontype, "bpy_struct.callback_add()") == -1)
+ rna_enum_region_type_items, params.region_type_str,
+ &params.region_type, error_prefix) == -1)
{
return NULL;
}
@@ -235,13 +336,12 @@ PyObject *pyrna_callback_classmethod_add(PyObject *UNUSED(self), PyObject *args)
}
else {
SpaceType *st = BKE_spacetype_from_id(spaceid);
- ARegionType *art = BKE_regiontype_from_id(st, cb_regiontype);
+ ARegionType *art = BKE_regiontype_from_id(st, params.region_type);
if (art == NULL) {
- PyErr_Format(PyExc_TypeError, "region type '%.200s' not in space", cb_regiontype_str);
+ PyErr_Format(PyExc_TypeError, "region type '%.200s' not in space", params.region_type_str);
return NULL;
}
- handle = ED_region_draw_cb_activate(art, cb_region_draw, (void *)args, cb_event);
- Py_INCREF(args);
+ handle = ED_region_draw_cb_activate(art, cb_region_draw, (void *)args, params.event);
}
}
}
@@ -250,7 +350,14 @@ PyObject *pyrna_callback_classmethod_add(PyObject *UNUSED(self), PyObject *args)
return NULL;
}
- return PyCapsule_New((void *)handle, RNA_CAPSULE_ID, NULL);
+ PyObject *ret = PyCapsule_New((void *)handle, RNA_CAPSULE_ID, NULL);
+
+ /* Store 'args' in context as well as the handler custom-data,
+ * because the handle may be freed by Blender (new file, new window... etc) */
+ PyCapsule_SetContext(ret, args);
+ Py_INCREF(args);
+
+ return ret;
}
PyObject *pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *args)
@@ -258,10 +365,8 @@ PyObject *pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *ar
PyObject *cls;
PyObject *py_handle;
void *handle;
- void *customdata;
StructRNA *srna;
- const char *cb_regiontype_str;
- int cb_regiontype;
+ bool capsule_clear = false;
if (PyTuple_GET_SIZE(args) < 2) {
PyErr_SetString(PyExc_ValueError, "callback_remove(handler): expected at least 2 args");
@@ -278,21 +383,39 @@ PyObject *pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *ar
PyErr_SetString(PyExc_ValueError, "callback_remove(handler): NULL handler given, invalid or already removed");
return NULL;
}
+ PyObject *handle_args = PyCapsule_GetContext(py_handle);
- if (RNA_struct_is_a(srna, &RNA_Space)) {
- if (!PyArg_ParseTuple(args, "OO!s:Space.draw_handler_remove",
- &cls, &PyCapsule_Type, &py_handle, /* already assigned, no matter */
- &cb_regiontype_str))
+ if (srna == &RNA_WindowManager) {
+ if (!PyArg_ParseTuple(
+ args, "OO!:WindowManager.draw_cursor_remove",
+ &cls, &PyCapsule_Type, &py_handle))
+ {
+ return NULL;
+ }
+ bContext *C = BPy_GetContext();
+ struct wmWindowManager *wm = CTX_wm_manager(C);
+ WM_paint_cursor_end(wm, handle);
+ capsule_clear = true;
+ }
+ else if (RNA_struct_is_a(srna, &RNA_Space)) {
+ const char *error_prefix = "Space.draw_handler_remove";
+ struct {
+ const char *region_type_str;
+
+ int region_type;
+ } params;
+
+ if (!PyArg_ParseTuple(
+ args, "OO!s:Space.draw_handler_remove",
+ &cls, &PyCapsule_Type, &py_handle, /* already assigned, no matter */
+ &params.region_type_str))
{
return NULL;
}
-
- customdata = ED_region_draw_cb_customdata(handle);
- Py_DECREF((PyObject *)customdata);
if (pyrna_enum_value_from_id(
- rna_enum_region_type_items, cb_regiontype_str,
- &cb_regiontype, "bpy_struct.callback_remove()") == -1)
+ rna_enum_region_type_items, params.region_type_str,
+ &params.region_type, error_prefix) == -1)
{
return NULL;
}
@@ -304,12 +427,13 @@ PyObject *pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *ar
}
else {
SpaceType *st = BKE_spacetype_from_id(spaceid);
- ARegionType *art = BKE_regiontype_from_id(st, cb_regiontype);
+ ARegionType *art = BKE_regiontype_from_id(st, params.region_type);
if (art == NULL) {
- PyErr_Format(PyExc_TypeError, "region type '%.200s' not in space", cb_regiontype_str);
+ PyErr_Format(PyExc_TypeError, "region type '%.200s' not in space", params.region_type_str);
return NULL;
}
ED_region_draw_cb_exit(art, handle);
+ capsule_clear = true;
}
}
}
@@ -319,7 +443,10 @@ PyObject *pyrna_callback_classmethod_remove(PyObject *UNUSED(self), PyObject *ar
}
/* don't allow reuse */
- PyCapsule_SetName(py_handle, RNA_CAPSULE_ID_INVALID);
+ if (capsule_clear) {
+ Py_DECREF(handle_args);
+ PyCapsule_SetName(py_handle, RNA_CAPSULE_ID_INVALID);
+ }
Py_RETURN_NONE;
}
diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c
new file mode 100644
index 00000000000..8189431dfc9
--- /dev/null
+++ b/source/blender/python/intern/bpy_rna_gizmo.c
@@ -0,0 +1,564 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_rna_gizmo.c
+ * \ingroup pythonintern
+ *
+ * .
+ */
+
+#include <Python.h>
+#include <stddef.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_alloca.h"
+
+#include "BKE_main.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "bpy_capi_utils.h"
+#include "bpy_rna_gizmo.h"
+
+#include "../generic/py_capi_utils.h"
+#include "../generic/python_utildefines.h"
+
+#include "RNA_access.h"
+#include "RNA_types.h"
+#include "RNA_enum_types.h"
+
+#include "bpy_rna.h"
+
+
+/* -------------------------------------------------------------------- */
+/** \name Gizmo Target Property Define API
+ * \{ */
+
+enum {
+ BPY_GIZMO_FN_SLOT_GET = 0,
+ BPY_GIZMO_FN_SLOT_SET,
+ BPY_GIZMO_FN_SLOT_RANGE_GET,
+};
+#define BPY_GIZMO_FN_SLOT_LEN (BPY_GIZMO_FN_SLOT_RANGE_GET + 1)
+
+struct BPyGizmoHandlerUserData {
+
+ PyObject *fn_slots[BPY_GIZMO_FN_SLOT_LEN];
+};
+
+static void py_rna_gizmo_handler_get_cb(
+ const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop,
+ void *value_p)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data;
+ PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_GET], NULL);
+ if (ret == NULL) {
+ goto fail;
+ }
+
+ if (gz_prop->type->data_type == PROP_FLOAT) {
+ float *value = value_p;
+ if (gz_prop->type->array_length == 1) {
+ if ((*value = PyFloat_AsDouble(ret)) == -1.0f && PyErr_Occurred()) {
+ goto fail;
+ }
+ }
+ else {
+ if (PyC_AsArray(value, ret, gz_prop->type->array_length, &PyFloat_Type, false,
+ "Gizmo get callback: ") == -1)
+ {
+ goto fail;
+ }
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_AttributeError, "internal error, unsupported type");
+ goto fail;
+ }
+
+ Py_DECREF(ret);
+
+ PyGILState_Release(gilstate);
+ return;
+
+fail:
+ PyErr_Print();
+ PyErr_Clear();
+
+ PyGILState_Release(gilstate);
+}
+
+static void py_rna_gizmo_handler_set_cb(
+ const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop,
+ const void *value_p)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data;
+
+ PyObject *args = PyTuple_New(1);
+
+ if (gz_prop->type->data_type == PROP_FLOAT) {
+ const float *value = value_p;
+ PyObject *py_value;
+ if (gz_prop->type->array_length == 1) {
+ py_value = PyFloat_FromDouble(*value);
+ }
+ else {
+ py_value = PyC_Tuple_PackArray_F32(value, gz_prop->type->array_length);
+ }
+ if (py_value == NULL) {
+ goto fail;
+ }
+ PyTuple_SET_ITEM(args, 0, py_value);
+ }
+ else {
+ PyErr_SetString(PyExc_AttributeError, "internal error, unsupported type");
+ goto fail;
+ }
+
+ PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_SET], args);
+ if (ret == NULL) {
+ goto fail;
+ }
+ Py_DECREF(ret);
+
+ PyGILState_Release(gilstate);
+ return;
+
+fail:
+ PyErr_Print();
+ PyErr_Clear();
+
+ Py_DECREF(args);
+
+ PyGILState_Release(gilstate);
+}
+
+static void py_rna_gizmo_handler_range_get_cb(
+ const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop,
+ void *value_p)
+{
+ struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data;
+
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_RANGE_GET], NULL);
+ if (ret == NULL) {
+ goto fail;
+ }
+
+ if (!PyTuple_Check(ret)) {
+ PyErr_Format(PyExc_TypeError,
+ "Expected a tuple, not %.200s",
+ Py_TYPE(ret)->tp_name);
+ goto fail;
+ }
+
+ if (PyTuple_GET_SIZE(ret) != 2) {
+ PyErr_Format(PyExc_TypeError,
+ "Expected a tuple of size 2, not %d",
+ PyTuple_GET_SIZE(ret));
+ goto fail;
+ }
+
+ if (gz_prop->type->data_type == PROP_FLOAT) {
+ float range[2];
+ for (int i = 0; i < 2; i++) {
+ if (((range[i] = PyFloat_AsDouble(PyTuple_GET_ITEM(ret, i))) == -1.0f && PyErr_Occurred()) == 0) {
+ /* pass */
+ }
+ else {
+ goto fail;
+ }
+ }
+ memcpy(value_p, range, sizeof(range));
+ }
+ else {
+ PyErr_SetString(PyExc_AttributeError, "internal error, unsupported type");
+ goto fail;
+ }
+
+ Py_DECREF(ret);
+ PyGILState_Release(gilstate);
+ return;
+
+fail:
+ Py_XDECREF(ret);
+
+ PyErr_Print();
+ PyErr_Clear();
+
+ PyGILState_Release(gilstate);
+}
+
+static void py_rna_gizmo_handler_free_cb(
+ const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop)
+{
+ struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data;
+
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+ for (int i = 0; i < BPY_GIZMO_FN_SLOT_LEN; i++) {
+ Py_XDECREF(data->fn_slots[i]);
+ }
+ PyGILState_Release(gilstate);
+
+ MEM_freeN(data);
+
+}
+
+PyDoc_STRVAR(bpy_gizmo_target_set_handler_doc,
+".. method:: target_set_handler(target, get, set, range=None):\n"
+"\n"
+" Assigns callbacks to a gizmos property.\n"
+"\n"
+" :arg get: Function that returns the value for this property (single value or sequence).\n"
+" :type get: callable\n"
+" :arg set: Function that takes a single value argument and applies it.\n"
+" :type set: callable\n"
+" :arg range: Function that returns a (min, max) tuple for gizmos that use a range.\n"
+" :type range: callable\n"
+);
+static PyObject *bpy_gizmo_target_set_handler(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ struct {
+ PyObject *self;
+ char *target;
+ PyObject *py_fn_slots[BPY_GIZMO_FN_SLOT_LEN];
+ } params = {
+ .self = NULL,
+ .target = NULL,
+ .py_fn_slots = {NULL},
+ };
+
+ /* Note: this is a counter-part to functions:
+ * 'Gizmo.target_set_prop & target_set_operator'
+ * (see: rna_wm_gizmo_api.c). conventions should match. */
+ static const char * const _keywords[] = {"self", "target", "get", "set", "range", NULL};
+ static _PyArg_Parser _parser = {"Os|$OOO:target_set_handler", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kw, &_parser,
+ &params.self,
+ &params.target,
+ &params.py_fn_slots[BPY_GIZMO_FN_SLOT_GET],
+ &params.py_fn_slots[BPY_GIZMO_FN_SLOT_SET],
+ &params.py_fn_slots[BPY_GIZMO_FN_SLOT_RANGE_GET]))
+ {
+ goto fail;
+ }
+
+ wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data;
+
+ const wmGizmoPropertyType *gz_prop_type =
+ WM_gizmotype_target_property_find(gz->type, params.target);
+ if (gz_prop_type == NULL) {
+ PyErr_Format(PyExc_ValueError,
+ "Gizmo target property '%s.%s' not found",
+ gz->type->idname, params.target);
+ goto fail;
+ }
+
+ {
+ const int slots_required = 2;
+ const int slots_start = 2;
+ for (int i = 0; i < BPY_GIZMO_FN_SLOT_LEN; i++) {
+ if (params.py_fn_slots[i] == NULL) {
+ if (i < slots_required) {
+ PyErr_Format(PyExc_ValueError, "Argument '%s' not given", _keywords[slots_start + i]);
+ goto fail;
+ }
+ }
+ else if (!PyCallable_Check(params.py_fn_slots[i])) {
+ PyErr_Format(PyExc_ValueError, "Argument '%s' not callable", _keywords[slots_start + i]);
+ goto fail;
+ }
+ }
+ }
+
+ struct BPyGizmoHandlerUserData *data = MEM_callocN(sizeof(*data), __func__);
+
+ for (int i = 0; i < BPY_GIZMO_FN_SLOT_LEN; i++) {
+ data->fn_slots[i] = params.py_fn_slots[i];
+ Py_XINCREF(params.py_fn_slots[i]);
+ }
+
+ WM_gizmo_target_property_def_func_ptr(
+ gz, gz_prop_type,
+ &(const struct wmGizmoPropertyFnParams) {
+ .value_get_fn = py_rna_gizmo_handler_get_cb,
+ .value_set_fn = py_rna_gizmo_handler_set_cb,
+ .range_get_fn = py_rna_gizmo_handler_range_get_cb,
+ .free_fn = py_rna_gizmo_handler_free_cb,
+ .user_data = data,
+ });
+
+ PyGILState_Release(gilstate);
+
+ Py_RETURN_NONE;
+
+fail:
+ PyGILState_Release(gilstate);
+ return NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Gizmo Target Property Access API
+ * \{ */
+
+PyDoc_STRVAR(bpy_gizmo_target_get_value_doc,
+".. method:: target_get_value(target):\n"
+"\n"
+" Get the value of this target property.\n"
+"\n"
+" :arg target: Target property name.\n"
+" :type target: string\n"
+" :return: The value of the target property.\n"
+" :rtype: Single value or array based on the target type\n"
+);
+static PyObject *bpy_gizmo_target_get_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ struct {
+ PyObject *self;
+ char *target;
+ } params = {
+ .self = NULL,
+ .target = NULL,
+ };
+
+ static const char * const _keywords[] = {"self", "target", NULL};
+ static _PyArg_Parser _parser = {"Os:target_get_value", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kw, &_parser,
+ &params.self,
+ &params.target))
+ {
+ goto fail;
+ }
+
+ wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data;
+
+ wmGizmoProperty *gz_prop =
+ WM_gizmo_target_property_find(gz, params.target);
+ if (gz_prop == NULL) {
+ PyErr_Format(PyExc_ValueError,
+ "Gizmo target property '%s.%s' not found",
+ gz->type->idname, params.target);
+ goto fail;
+ }
+
+ const int array_len = WM_gizmo_target_property_array_length(gz, gz_prop);
+ switch (gz_prop->type->data_type) {
+ case PROP_FLOAT:
+ {
+ if (array_len != 0) {
+ float *value = BLI_array_alloca(value, array_len);
+ WM_gizmo_target_property_float_get_array(gz, gz_prop, value);
+ return PyC_Tuple_PackArray_F32(value, array_len);
+ }
+ else {
+ float value = WM_gizmo_target_property_float_get(gz, gz_prop);
+ return PyFloat_FromDouble(value);
+ }
+ break;
+ }
+ default:
+ {
+ PyErr_SetString(PyExc_RuntimeError, "Not yet supported type");
+ goto fail;
+ }
+ }
+
+fail:
+ return NULL;
+}
+
+PyDoc_STRVAR(bpy_gizmo_target_set_value_doc,
+".. method:: target_set_value(target):\n"
+"\n"
+" Set the value of this target property.\n"
+"\n"
+" :arg target: Target property name.\n"
+" :type target: string\n"
+);
+static PyObject *bpy_gizmo_target_set_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ struct {
+ PyObject *self;
+ char *target;
+ PyObject *value;
+ } params = {
+ .self = NULL,
+ .target = NULL,
+ .value = NULL,
+ };
+
+ static const char * const _keywords[] = {"self", "target", "value", NULL};
+ static _PyArg_Parser _parser = {"OsO:target_set_value", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kw, &_parser,
+ &params.self,
+ &params.target,
+ &params.value))
+ {
+ goto fail;
+ }
+
+ wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data;
+
+ wmGizmoProperty *gz_prop =
+ WM_gizmo_target_property_find(gz, params.target);
+ if (gz_prop == NULL) {
+ PyErr_Format(PyExc_ValueError,
+ "Gizmo target property '%s.%s' not found",
+ gz->type->idname, params.target);
+ goto fail;
+ }
+
+ const int array_len = WM_gizmo_target_property_array_length(gz, gz_prop);
+ switch (gz_prop->type->data_type) {
+ case PROP_FLOAT:
+ {
+ if (array_len != 0) {
+ float *value = BLI_array_alloca(value, array_len);
+ if (PyC_AsArray(value, params.value, gz_prop->type->array_length, &PyFloat_Type, false,
+ "Gizmo target property array") == -1)
+ {
+ goto fail;
+ }
+ WM_gizmo_target_property_float_set_array(BPy_GetContext(), gz, gz_prop, value);
+ }
+ else {
+ float value;
+ if ((value = PyFloat_AsDouble(params.value)) == -1.0f && PyErr_Occurred()) {
+ goto fail;
+ }
+ WM_gizmo_target_property_float_set(BPy_GetContext(), gz, gz_prop, value);
+ }
+ Py_RETURN_NONE;
+ }
+ default:
+ {
+ PyErr_SetString(PyExc_RuntimeError, "Not yet supported type");
+ goto fail;
+ }
+ }
+
+fail:
+ return NULL;
+}
+
+
+PyDoc_STRVAR(bpy_gizmo_target_get_range_doc,
+".. method:: target_get_range(target):\n"
+"\n"
+" Get the range for this target property.\n"
+"\n"
+" :arg target: Target property name.\n"
+" :return: The range of this property (min, max).\n"
+" :rtype: tuple pair.\n"
+);
+static PyObject *bpy_gizmo_target_get_range(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ struct {
+ PyObject *self;
+ char *target;
+ } params = {
+ .self = NULL,
+ .target = NULL,
+ };
+
+ static const char * const _keywords[] = {"self", "target", NULL};
+ static _PyArg_Parser _parser = {"Os:target_get_range", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kw, &_parser,
+ &params.self,
+ &params.target))
+ {
+ goto fail;
+ }
+
+ wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data;
+
+ wmGizmoProperty *gz_prop =
+ WM_gizmo_target_property_find(gz, params.target);
+ if (gz_prop == NULL) {
+ PyErr_Format(PyExc_ValueError,
+ "Gizmo target property '%s.%s' not found",
+ gz->type->idname, params.target);
+ goto fail;
+ }
+
+ switch (gz_prop->type->data_type) {
+ case PROP_FLOAT:
+ {
+ float range[2];
+ WM_gizmo_target_property_float_range_get(gz, gz_prop, range);
+ return PyC_Tuple_PackArray_F32(range, 2);
+ }
+ default:
+ {
+ PyErr_SetString(PyExc_RuntimeError, "Not yet supported type");
+ goto fail;
+ }
+ }
+
+fail:
+ return NULL;
+}
+
+/** \} */
+
+int BPY_rna_gizmo_module(PyObject *mod_par)
+{
+ static PyMethodDef method_def_array[] = {
+ /* Gizmo Target Property Define API */
+ {"target_set_handler", (PyCFunction)bpy_gizmo_target_set_handler,
+ METH_VARARGS | METH_KEYWORDS, bpy_gizmo_target_set_handler_doc},
+ /* Gizmo Target Property Access API */
+ {"target_get_value", (PyCFunction)bpy_gizmo_target_get_value,
+ METH_VARARGS | METH_KEYWORDS, bpy_gizmo_target_get_value_doc},
+ {"target_set_value", (PyCFunction)bpy_gizmo_target_set_value,
+ METH_VARARGS | METH_KEYWORDS, bpy_gizmo_target_set_value_doc},
+ {"target_get_range", (PyCFunction)bpy_gizmo_target_get_range,
+ METH_VARARGS | METH_KEYWORDS, bpy_gizmo_target_get_range_doc},
+ /* no sentinel needed. */
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(method_def_array); i++) {
+ PyMethodDef *m = &method_def_array[i];
+ PyObject *func = PyCFunction_New(m, NULL);
+ PyObject *func_inst = PyInstanceMethod_New(func);
+ char name_prefix[128];
+ PyOS_snprintf(name_prefix, sizeof(name_prefix), "_rna_gizmo_%s", m->ml_name);
+ /* TODO, return a type that binds nearly to a method. */
+ PyModule_AddObject(mod_par, name_prefix, func_inst);
+ }
+
+ return 0;
+}
diff --git a/source/blender/python/intern/gpu.h b/source/blender/python/intern/bpy_rna_gizmo.h
index 0da44a4eb87..68d8c1e052a 100644
--- a/source/blender/python/intern/gpu.h
+++ b/source/blender/python/intern/bpy_rna_gizmo.h
@@ -15,27 +15,18 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2005 Blender Foundation.
- * All rights reserved.
- *
- * The Original Code is: all of this file.
- *
- * Contributor(s): Benoit Bolsee.
+ * Contributor(s): Bastien Montagne
*
* ***** END GPL LICENSE BLOCK *****
*/
-/** \file blender/python/intern/gpu.h
+/** \file blender/python/intern/bpy_rna_gizmo.h
* \ingroup pythonintern
- *
- * Initializes the gpu Python module.
*/
-#ifndef __GPU_H__
-#define __GPU_H__
-
-PyObject *GPU_initPython(void);
+#ifndef __BPY_RNA_GIZMO_H__
+#define __BPY_RNA_GIZMO_H__
-PyObject *BPyInit_gpu_offscreen(void);
+int BPY_rna_gizmo_module(PyObject *);
-#endif /* __GPU_H__ */
+#endif /* __BPY_RNA_GIZMO_H__ */
diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c
index 4806c2266ba..3fc12d4cc54 100644
--- a/source/blender/python/intern/bpy_rna_id_collection.c
+++ b/source/blender/python/intern/bpy_rna_id_collection.c
@@ -35,9 +35,9 @@
#include "BLI_bitmap.h"
#include "BKE_global.h"
-#include "BKE_main.h"
#include "BKE_library.h"
#include "BKE_library_query.h"
+#include "BKE_main.h"
#include "DNA_ID.h"
/* Those folowing are only to support hack of not listing some internal 'backward' pointers in generated user_map... */
@@ -97,11 +97,11 @@ static int foreach_libblock_id_user_map_callback(
}
if ((GS(self_id->name) == ID_OB) && (id_p == (ID **)&((Object *)self_id)->proxy_from)) {
- /* We skip proxy_from here, since it some internal pointer which is not irrelevant info for py/API level. */
+ /* We skip proxy_from here, since it's some internal pointer which is not relevant info for py/API level. */
return IDWALK_RET_NOP;
}
else if ((GS(self_id->name) == ID_KE) && (id_p == (ID **)&((Key *)self_id)->from)) {
- /* We skip from here, since it some internal pointer which is not irrelevant info for py/API level. */
+ /* We skip from here, since it's some internal pointer which is not relevant info for py/API level. */
return IDWALK_RET_NOP;
}
@@ -290,6 +290,79 @@ error:
}
+PyDoc_STRVAR(bpy_batch_remove_doc,
+".. method:: batch_remove(ids=(id1, id2, ...))\n"
+"\n"
+" Remove (delete) several IDs at once.\n"
+"\n"
+" WARNING: Considered experimental feature currently.\n"
+"\n"
+" Note that this function is quicker than individual calls to :func:`remove()` (from :class:`bpy.types.BlendData`\n"
+" ID collections), but less safe/versatile (it can break Blender, e.g. by removing all scenes...).\n"
+"\n"
+" :arg ids: Iterables of IDs (types can be mixed).\n"
+" :type subset: sequence\n"
+);
+static PyObject *bpy_batch_remove(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
+{
+#if 0 /* If someone knows how to get a proper 'self' in that case... */
+ BPy_StructRNA *pyrna = (BPy_StructRNA *)self;
+ Main *bmain = pyrna->ptr.data;
+#else
+ Main *bmain = G_MAIN; /* XXX Ugly, but should work! */
+#endif
+
+ PyObject *ids = NULL;
+
+ PyObject *ret = NULL;
+
+ static const char *_keywords[] = {"ids", NULL};
+ static _PyArg_Parser _parser = {"O:user_map", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kwds, &_parser,
+ &ids))
+ {
+ return ret;
+ }
+
+ if (ids) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+ PyObject *ids_fast = PySequence_Fast(ids, "batch_remove");
+ if (ids_fast == NULL) {
+ goto error;
+ }
+
+ PyObject **ids_array = PySequence_Fast_ITEMS(ids_fast);
+ Py_ssize_t ids_len = PySequence_Fast_GET_SIZE(ids_fast);
+
+ for (; ids_len; ids_array++, ids_len--) {
+ ID *id;
+ if (!pyrna_id_FromPyObject(*ids_array, &id)) {
+ PyErr_Format(PyExc_TypeError,
+ "Expected an ID type, not %.200s",
+ Py_TYPE(*ids_array)->tp_name);
+ Py_DECREF(ids_fast);
+ goto error;
+ }
+
+ id->tag |= LIB_TAG_DOIT;
+ }
+ Py_DECREF(ids_fast);
+
+ BKE_id_multi_tagged_delete(bmain);
+ }
+ else {
+ goto error;
+ }
+
+ Py_INCREF(Py_None);
+ ret = Py_None;
+
+error:
+ return ret;
+}
+
int BPY_rna_id_collection_module(PyObject *mod_par)
{
static PyMethodDef user_map = {
@@ -297,5 +370,10 @@ int BPY_rna_id_collection_module(PyObject *mod_par)
PyModule_AddObject(mod_par, "_rna_id_collection_user_map", PyCFunction_New(&user_map, NULL));
+ static PyMethodDef batch_remove = {
+ "batch_remove", (PyCFunction)bpy_batch_remove, METH_VARARGS | METH_KEYWORDS, bpy_batch_remove_doc};
+
+ PyModule_AddObject(mod_par, "_rna_id_collection_batch_remove", PyCFunction_New(&batch_remove, NULL));
+
return 0;
}
diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c
index cbd8b67d1ce..1d1108e1af0 100644
--- a/source/blender/python/intern/bpy_utils_units.c
+++ b/source/blender/python/intern/bpy_utils_units.c
@@ -323,7 +323,6 @@ PyObject *BPY_utils_units(void)
submodule = PyModule_Create(&bpyunits_module);
PyDict_SetItemString(PyImport_GetModuleDict(), bpyunits_module.m_name, submodule);
- Py_INCREF(submodule);
/* Finalize our unit systems and types structseq definitions! */
diff --git a/source/blender/python/intern/gpu.c b/source/blender/python/intern/gpu.c
deleted file mode 100644
index d902b6838f4..00000000000
--- a/source/blender/python/intern/gpu.c
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2006 Blender Foundation.
- * All rights reserved.
- *
- * The Original Code is: all of this file.
- *
- * Contributor(s): Benoit Bolsee.
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/python/intern/gpu.c
- * \ingroup pythonintern
- *
- * This file defines the 'gpu' module, used to get GLSL shader code and data
- * from blender materials.
- */
-
-#include <Python.h>
-
-#include "DNA_scene_types.h"
-#include "DNA_material_types.h"
-#include "DNA_ID.h"
-#include "DNA_customdata_types.h"
-
-#include "BLI_listbase.h"
-#include "BLI_utildefines.h"
-
-#include "RNA_access.h"
-
-#include "bpy_rna.h"
-
-#include "../generic/py_capi_utils.h"
-
-#include "GPU_material.h"
-
-#include "gpu.h"
-
-#define PY_MODULE_ADD_CONSTANT(module, name) PyModule_AddIntConstant(module, # name, name)
-
-PyDoc_STRVAR(M_gpu_doc,
-"This module provides access to the GLSL shader and Offscreen rendering functionalities."
-);
-static struct PyModuleDef gpumodule = {
- PyModuleDef_HEAD_INIT,
- "gpu", /* name of module */
- M_gpu_doc, /* module documentation */
- -1, /* size of per-interpreter state of the module,
- * or -1 if the module keeps state in global variables. */
- NULL, NULL, NULL, NULL, NULL
-};
-
-static PyObject *PyInit_gpu(void)
-{
- PyObject *m;
-
- m = PyModule_Create(&gpumodule);
- if (m == NULL)
- return NULL;
-
-
- /* Take care to update docs when editing: 'doc/python_api/rst/gpu.rst' */
-
-
- /* -------------------------------------------------------------------- */
- /* GPUDynamicType */
-
- /* device constant groups */
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_GROUP_MISC);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_GROUP_LAMP);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_GROUP_OBJECT);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_GROUP_SAMPLER);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_GROUP_MIST);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_GROUP_WORLD);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_GROUP_MAT);
-
- /* device constants */
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_NONE);
- /* GPU_DYNAMIC_GROUP_OBJECT */
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_OBJECT_VIEWMAT);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_OBJECT_MAT);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_OBJECT_VIEWIMAT);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_OBJECT_IMAT);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_OBJECT_LOCTOVIEWMAT);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_OBJECT_LOCTOVIEWIMAT);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_OBJECT_COLOR);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_OBJECT_AUTOBUMPSCALE);
- /* GPU_DYNAMIC_GROUP_LAMP */
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_DYNVEC);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_DYNCO);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_DYNIMAT);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_DYNPERSMAT);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_DYNENERGY);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_DYNCOL);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_ATT1);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_ATT2);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_DISTANCE);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_SPOTSIZE);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_SPOTBLEND);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_LAMP_SPOTSCALE);
- /* GPU_DYNAMIC_GROUP_SAMPLER */
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_SAMPLER_2DBUFFER);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_SAMPLER_2DIMAGE);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_SAMPLER_2DSHADOW);
- /* GPU_DYNAMIC_GROUP_MIST */
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MIST_ENABLE);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MIST_START);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MIST_DISTANCE);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MIST_INTENSITY);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MIST_TYPE);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MIST_COLOR);
- /* GPU_DYNAMIC_GROUP_WORLD */
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_HORIZON_COLOR);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_AMBIENT_COLOR);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_ZENITH_COLOR);
- /* GPU_DYNAMIC_GROUP_MAT */
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MAT_DIFFRGB);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MAT_REF);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MAT_SPECRGB);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MAT_SPEC);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MAT_HARD);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MAT_EMIT);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MAT_AMB);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MAT_ALPHA);
- PY_MODULE_ADD_CONSTANT(m, GPU_DYNAMIC_MAT_MIR);
-
-
- /* -------------------------------------------------------------------- */
- /* GPUDataType */
-
- PY_MODULE_ADD_CONSTANT(m, GPU_DATA_1I);
- PY_MODULE_ADD_CONSTANT(m, GPU_DATA_1F);
- PY_MODULE_ADD_CONSTANT(m, GPU_DATA_2F);
- PY_MODULE_ADD_CONSTANT(m, GPU_DATA_3F);
- PY_MODULE_ADD_CONSTANT(m, GPU_DATA_4F);
- PY_MODULE_ADD_CONSTANT(m, GPU_DATA_9F);
- PY_MODULE_ADD_CONSTANT(m, GPU_DATA_16F);
- PY_MODULE_ADD_CONSTANT(m, GPU_DATA_4UB);
-
-
- /* -------------------------------------------------------------------- */
- /* CustomDataType
- *
- * Intentionally only include the subset used by the GPU API.
- */
- PY_MODULE_ADD_CONSTANT(m, CD_MTFACE);
- PY_MODULE_ADD_CONSTANT(m, CD_ORCO);
- PY_MODULE_ADD_CONSTANT(m, CD_TANGENT);
- PY_MODULE_ADD_CONSTANT(m, CD_MCOL);
- return m;
-}
-
-#define PY_DICT_ADD_STRING(d, s, f) \
- val = PyUnicode_FromString(s->f); \
- PyDict_SetItemString(d, # f, val); \
- Py_DECREF(val)
-
-#define PY_DICT_ADD_LONG(d, s, f) \
- val = PyLong_FromLong(s->f); \
- PyDict_SetItemString(d, # f, val); \
- Py_DECREF(val)
-
-#define PY_DICT_ADD_ID(d, s, f) \
- RNA_id_pointer_create((struct ID *)s->f, &tptr); \
- val = pyrna_struct_CreatePyObject(&tptr); \
- PyDict_SetItemString(d, # f, val); \
- Py_DECREF(val)
-
-#if 0 /* UNUSED */
-#define PY_OBJ_ADD_ID(d, s, f) \
- val = PyUnicode_FromString(&s->f->id.name[2]); \
- PyObject_SetAttrString(d, # f, val); \
- Py_DECREF(val)
-
-#define PY_OBJ_ADD_LONG(d, s, f) \
- val = PyLong_FromLong(s->f); \
- PyObject_SetAttrString(d, # f, val); \
- Py_DECREF(val)
-
-#define PY_OBJ_ADD_STRING(d, s, f) \
- val = PyUnicode_FromString(s->f); \
- PyObject_SetAttrString(d, # f, val); \
- Py_DECREF(val)
-#endif
-
-PyDoc_STRVAR(GPU_export_shader_doc,
-"export_shader(scene, material)\n"
-"\n"
-" Returns the GLSL shader that produces the visual effect of material in scene.\n"
-"\n"
-" :return: Dictionary defining the shader, uniforms and attributes.\n"
-" :rtype: Dict"
-);
-static PyObject *GPU_export_shader(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
-{
- PyObject *pyscene;
- PyObject *pymat;
- PyObject *result;
- PyObject *dict;
- PyObject *val;
- PyObject *seq;
-
- int i;
- Scene *scene;
- PointerRNA tptr;
- Material *material;
- GPUShaderExport *shader;
- GPUInputUniform *uniform;
- GPUInputAttribute *attribute;
-
- static const char *_keywords[] = {"scene", "material", NULL};
- static _PyArg_Parser _parser = {"OO:export_shader", _keywords, 0};
- if (!_PyArg_ParseTupleAndKeywordsFast(
- args, kw, &_parser,
- &pyscene, &pymat))
- {
- return NULL;
- }
- scene = (Scene *)PyC_RNA_AsPointer(pyscene, "Scene");
- if (scene == NULL) {
- return NULL;
- }
-
- material = (Material *)PyC_RNA_AsPointer(pymat, "Material");
- if (material == NULL) {
- return NULL;
- }
-
- /* we can call our internal function at last: */
- shader = GPU_shader_export(scene, material);
- if (!shader) {
- PyErr_SetString(PyExc_RuntimeError, "cannot export shader");
- return NULL;
- }
- /* build a dictionary */
- result = PyDict_New();
- if (shader->fragment) {
- PY_DICT_ADD_STRING(result, shader, fragment);
- }
- if (shader->vertex) {
- PY_DICT_ADD_STRING(result, shader, vertex);
- }
- seq = PyList_New(BLI_listbase_count(&shader->uniforms));
- for (i = 0, uniform = shader->uniforms.first; uniform; uniform = uniform->next, i++) {
- dict = PyDict_New();
- PY_DICT_ADD_STRING(dict, uniform, varname);
- PY_DICT_ADD_LONG(dict, uniform, datatype);
- PY_DICT_ADD_LONG(dict, uniform, type);
- if (uniform->lamp) {
- PY_DICT_ADD_ID(dict, uniform, lamp);
- }
- if (uniform->material) {
- PY_DICT_ADD_ID(dict, uniform, material);
- }
- if (uniform->image) {
- PY_DICT_ADD_ID(dict, uniform, image);
- }
- if (uniform->type == GPU_DYNAMIC_SAMPLER_2DBUFFER ||
- uniform->type == GPU_DYNAMIC_SAMPLER_2DIMAGE ||
- uniform->type == GPU_DYNAMIC_SAMPLER_2DSHADOW)
- {
- PY_DICT_ADD_LONG(dict, uniform, texnumber);
- }
- if (uniform->texpixels) {
- val = PyByteArray_FromStringAndSize((const char *)uniform->texpixels, uniform->texsize * 4);
- PyDict_SetItemString(dict, "texpixels", val);
- Py_DECREF(val);
- PY_DICT_ADD_LONG(dict, uniform, texsize);
- }
- PyList_SET_ITEM(seq, i, dict);
- }
- PyDict_SetItemString(result, "uniforms", seq);
- Py_DECREF(seq);
-
- seq = PyList_New(BLI_listbase_count(&shader->attributes));
- for (i = 0, attribute = shader->attributes.first; attribute; attribute = attribute->next, i++) {
- dict = PyDict_New();
- PY_DICT_ADD_STRING(dict, attribute, varname);
- PY_DICT_ADD_LONG(dict, attribute, datatype);
- PY_DICT_ADD_LONG(dict, attribute, type);
- PY_DICT_ADD_LONG(dict, attribute, number);
- if (attribute->name) {
- if (attribute->name[0] != 0) {
- PY_DICT_ADD_STRING(dict, attribute, name);
- }
- else {
- val = PyLong_FromLong(0);
- PyDict_SetItemString(dict, "name", val);
- Py_DECREF(val);
- }
- }
- PyList_SET_ITEM(seq, i, dict);
- }
- PyDict_SetItemString(result, "attributes", seq);
- Py_DECREF(seq);
-
- GPU_free_shader_export(shader);
-
- return result;
-}
-
-static PyMethodDef meth_export_shader[] = {
- {"export_shader", (PyCFunction)GPU_export_shader, METH_VARARGS | METH_KEYWORDS, GPU_export_shader_doc}
-};
-
-/* -------------------------------------------------------------------- */
-/* Initialize Module */
-
-PyObject *GPU_initPython(void)
-{
- PyObject *module;
- PyObject *submodule;
- PyObject *sys_modules = PyImport_GetModuleDict();
-
- module = PyInit_gpu();
-
- PyModule_AddObject(module, "export_shader", (PyObject *)PyCFunction_New(meth_export_shader, NULL));
-
- /* gpu.offscreen */
- PyModule_AddObject(module, "offscreen", (submodule = BPyInit_gpu_offscreen()));
- PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
- Py_INCREF(submodule);
-
- PyDict_SetItem(sys_modules, PyModule_GetNameObject(module), module);
- return module;
-}
diff --git a/source/blender/python/intern/gpu_offscreen.c b/source/blender/python/intern/gpu_offscreen.c
deleted file mode 100644
index 3b9b3c70ead..00000000000
--- a/source/blender/python/intern/gpu_offscreen.c
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright 2015, Blender Foundation.
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/python/intern/gpu_offscreen.c
- * \ingroup pythonintern
- *
- * This file defines the offscreen functionalities of the 'gpu' module
- * used for off-screen OpenGL rendering.
- */
-
-#include <Python.h>
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_utildefines.h"
-
-#include "WM_types.h"
-
-#include "BKE_global.h"
-#include "BKE_library.h"
-
-#include "ED_screen.h"
-
-#include "GPU_compositing.h"
-#include "GPU_framebuffer.h"
-
-#include "../mathutils/mathutils.h"
-
-#include "../generic/py_capi_utils.h"
-
-#include "gpu.h"
-
-#include "ED_view3d.h"
-
-/* -------------------------------------------------------------------- */
-/* GPU Offscreen PyObject */
-
-typedef struct {
- PyObject_HEAD
- GPUOffScreen *ofs;
-} BPy_GPUOffScreen;
-
-static int bpy_gpu_offscreen_valid_check(BPy_GPUOffScreen *py_gpu_ofs)
-{
- if (UNLIKELY(py_gpu_ofs->ofs == NULL)) {
- PyErr_SetString(PyExc_ReferenceError, "GPU offscreen was freed, no further access is valid");
- return -1;
- }
- return 0;
-}
-
-#define BPY_GPU_OFFSCREEN_CHECK_OBJ(pygpu) { \
- if (UNLIKELY(bpy_gpu_offscreen_valid_check(pygpu) == -1)) { \
- return NULL; \
- } \
-} ((void)0)
-
-PyDoc_STRVAR(pygpu_offscreen_width_doc, "Texture width.\n\n:type: int");
-static PyObject *pygpu_offscreen_width_get(BPy_GPUOffScreen *self, void *UNUSED(type))
-{
- BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
- return PyLong_FromLong(GPU_offscreen_width(self->ofs));
-}
-
-PyDoc_STRVAR(pygpu_offscreen_height_doc, "Texture height.\n\n:type: int");
-static PyObject *pygpu_offscreen_height_get(BPy_GPUOffScreen *self, void *UNUSED(type))
-{
- BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
- return PyLong_FromLong(GPU_offscreen_height(self->ofs));
-}
-
-PyDoc_STRVAR(pygpu_offscreen_color_texture_doc, "Color texture.\n\n:type: int");
-static PyObject *pygpu_offscreen_color_texture_get(BPy_GPUOffScreen *self, void *UNUSED(type))
-{
- BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
- return PyLong_FromLong(GPU_offscreen_color_texture(self->ofs));
-}
-
-PyDoc_STRVAR(pygpu_offscreen_bind_doc,
-"bind(save=True)\n"
-"\n"
-" Bind the offscreen object.\n"
-"\n"
-" :param save: save OpenGL current states.\n"
-" :type save: bool\n"
-);
-static PyObject *pygpu_offscreen_bind(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds)
-{
- static const char *kwlist[] = {"save", NULL};
- bool save = true;
-
- BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
-
- if (!PyArg_ParseTupleAndKeywords(
- args, kwds, "|O&:bind", (char **)(kwlist),
- PyC_ParseBool, &save))
- {
- return NULL;
- }
-
- GPU_offscreen_bind(self->ofs, save);
- Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(pygpu_offscreen_unbind_doc,
-"unbind(restore=True)\n"
-"\n"
-" Unbind the offscreen object.\n"
-"\n"
-" :param restore: restore OpenGL previous states.\n"
-" :type restore: bool\n"
-);
-static PyObject *pygpu_offscreen_unbind(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds)
-{
- static const char *kwlist[] = {"restore", NULL};
- bool restore = true;
-
- BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
-
- if (!PyArg_ParseTupleAndKeywords(
- args, kwds, "|O&:unbind", (char **)(kwlist),
- PyC_ParseBool, &restore))
- {
- return NULL;
- }
-
- GPU_offscreen_unbind(self->ofs, restore);
- Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(pygpu_offscreen_draw_view3d_doc,
-"draw_view3d(scene, view3d, region, modelview_matrix, projection_matrix)\n"
-"\n"
-" Draw the 3d viewport in the offscreen object.\n"
-"\n"
-" :param scene: Scene to draw.\n"
-" :type scene: :class:`bpy.types.Scene`\n"
-" :param view3d: 3D View to get the drawing settings from.\n"
-" :type view3d: :class:`bpy.types.SpaceView3D`\n"
-" :param region: Region of the 3D View.\n"
-" :type region: :class:`bpy.types.Region`\n"
-" :param modelview_matrix: ModelView Matrix.\n"
-" :type modelview_matrix: :class:`mathutils.Matrix`\n"
-" :param projection_matrix: Projection Matrix.\n"
-" :type projection_matrix: :class:`mathutils.Matrix`\n"
-);
-static PyObject *pygpu_offscreen_draw_view3d(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds)
-{
- static const char *kwlist[] = {"scene", "view3d", "region", "projection_matrix", "modelview_matrix", NULL};
-
- MatrixObject *py_mat_modelview, *py_mat_projection;
- PyObject *py_scene, *py_region, *py_view3d;
-
- struct Main *bmain = G_MAIN; /* XXX UGLY! */
- Scene *scene;
- View3D *v3d;
- ARegion *ar;
- GPUFX *fx;
- GPUFXSettings fx_settings;
- struct RV3DMatrixStore *rv3d_mats;
-
- BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
-
- if (!PyArg_ParseTupleAndKeywords(
- args, kwds, "OOOO&O&:draw_view3d", (char **)(kwlist),
- &py_scene, &py_view3d, &py_region,
- Matrix_Parse4x4, &py_mat_projection,
- Matrix_Parse4x4, &py_mat_modelview) ||
- (!(scene = PyC_RNA_AsPointer(py_scene, "Scene")) ||
- !(v3d = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) ||
- !(ar = PyC_RNA_AsPointer(py_region, "Region"))))
- {
- return NULL;
- }
-
- BLI_assert(BKE_id_is_in_gobal_main(&scene->id));
-
- fx = GPU_fx_compositor_create();
-
- fx_settings = v3d->fx_settings; /* full copy */
-
- ED_view3d_draw_offscreen_init(bmain, scene, v3d);
-
- rv3d_mats = ED_view3d_mats_rv3d_backup(ar->regiondata);
-
- GPU_offscreen_bind(self->ofs, true); /* bind */
-
- ED_view3d_draw_offscreen(
- bmain, scene, v3d, ar, GPU_offscreen_width(self->ofs), GPU_offscreen_height(self->ofs),
- (float(*)[4])py_mat_modelview->matrix, (float(*)[4])py_mat_projection->matrix,
- false, true, true, "",
- fx, &fx_settings,
- self->ofs);
-
- GPU_fx_compositor_destroy(fx);
- GPU_offscreen_unbind(self->ofs, true); /* unbind */
-
- ED_view3d_mats_rv3d_restore(ar->regiondata, rv3d_mats);
- MEM_freeN(rv3d_mats);
-
- Py_RETURN_NONE;
-}
-
-PyDoc_STRVAR(pygpu_offscreen_free_doc,
-"free()\n"
-"\n"
-" Free the offscreen object\n"
-" The framebuffer, texture and render objects will no longer be accessible.\n"
-);
-static PyObject *pygpu_offscreen_free(BPy_GPUOffScreen *self)
-{
- BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
-
- GPU_offscreen_free(self->ofs);
- self->ofs = NULL;
- Py_RETURN_NONE;
-}
-
-static void BPy_GPUOffScreen__tp_dealloc(BPy_GPUOffScreen *self)
-{
- if (self->ofs)
- GPU_offscreen_free(self->ofs);
- Py_TYPE(self)->tp_free((PyObject *)self);
-}
-
-static PyGetSetDef bpy_gpu_offscreen_getseters[] = {
- {(char *)"color_texture", (getter)pygpu_offscreen_color_texture_get, (setter)NULL, pygpu_offscreen_color_texture_doc, NULL},
- {(char *)"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL},
- {(char *)"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL},
- {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
-};
-
-static struct PyMethodDef bpy_gpu_offscreen_methods[] = {
- {"bind", (PyCFunction)pygpu_offscreen_bind, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_bind_doc},
- {"unbind", (PyCFunction)pygpu_offscreen_unbind, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_unbind_doc},
- {"draw_view3d", (PyCFunction)pygpu_offscreen_draw_view3d, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_draw_view3d_doc},
- {"free", (PyCFunction)pygpu_offscreen_free, METH_NOARGS, pygpu_offscreen_free_doc},
- {NULL, NULL, 0, NULL}
-};
-
-PyDoc_STRVAR(py_gpu_offscreen_doc,
-".. class:: GPUOffscreen"
-"\n"
-" This object gives access to off screen buffers.\n"
-);
-static PyTypeObject BPy_GPUOffScreen_Type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "GPUOffScreen", /* tp_name */
- sizeof(BPy_GPUOffScreen), /* tp_basicsize */
- 0, /* tp_itemsize */
- /* methods */
- (destructor)BPy_GPUOffScreen__tp_dealloc, /* tp_dealloc */
- NULL, /* tp_print */
- NULL, /* tp_getattr */
- NULL, /* tp_setattr */
- NULL, /* tp_compare */
- NULL, /* tp_repr */
- NULL, /* tp_as_number */
- NULL, /* tp_as_sequence */
- NULL, /* tp_as_mapping */
- NULL, /* tp_hash */
- NULL, /* tp_call */
- NULL, /* tp_str */
- NULL, /* tp_getattro */
- NULL, /* tp_setattro */
- NULL, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
- py_gpu_offscreen_doc, /* Documentation string */
- NULL, /* tp_traverse */
- NULL, /* tp_clear */
- NULL, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- NULL, /* tp_iter */
- NULL, /* tp_iternext */
- bpy_gpu_offscreen_methods, /* tp_methods */
- NULL, /* tp_members */
- bpy_gpu_offscreen_getseters, /* tp_getset */
- NULL, /* tp_base */
- NULL, /* tp_dict */
- NULL, /* tp_descr_get */
- NULL, /* tp_descr_set */
- 0, /* tp_dictoffset */
- 0, /* tp_init */
- NULL, /* tp_alloc */
- NULL, /* tp_new */
- (freefunc)0, /* tp_free */
- NULL, /* tp_is_gc */
- NULL, /* tp_bases */
- NULL, /* tp_mro */
- NULL, /* tp_cache */
- NULL, /* tp_subclasses */
- NULL, /* tp_weaklist */
- (destructor) NULL /* tp_del */
-};
-
-/* -------------------------------------------------------------------- */
-/* GPU offscreen methods */
-
-static PyObject *BPy_GPU_OffScreen_CreatePyObject(GPUOffScreen *ofs)
-{
- BPy_GPUOffScreen *self;
- self = PyObject_New(BPy_GPUOffScreen, &BPy_GPUOffScreen_Type);
- self->ofs = ofs;
- return (PyObject *)self;
-}
-
-PyDoc_STRVAR(pygpu_offscreen_new_doc,
-"new(width, height, samples=0)\n"
-"\n"
-" Return a GPUOffScreen.\n"
-"\n"
-" :param width: Horizontal dimension of the buffer.\n"
-" :type width: int`\n"
-" :param height: Vertical dimension of the buffer.\n"
-" :type height: int`\n"
-" :param samples: OpenGL samples to use for MSAA or zero to disable.\n"
-" :type samples: int\n"
-" :return: Newly created off-screen buffer.\n"
-" :rtype: :class:`gpu.GPUOffscreen`\n"
-);
-static PyObject *pygpu_offscreen_new(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
-{
- static const char *kwlist[] = {"width", "height", "samples", NULL};
-
- GPUOffScreen *ofs;
- int width, height, samples = 0;
- char err_out[256];
-
- if (!PyArg_ParseTupleAndKeywords(
- args, kwds, "ii|i:new", (char **)(kwlist),
- &width, &height, &samples))
- {
- return NULL;
- }
-
- ofs = GPU_offscreen_create(width, height, samples, err_out);
-
- if (ofs == NULL) {
- PyErr_Format(PyExc_RuntimeError,
- "gpu.offscreen.new(...) failed with '%s'",
- err_out[0] ? err_out : "unknown error");
- return NULL;
- }
-
- return BPy_GPU_OffScreen_CreatePyObject(ofs);
-}
-
-static struct PyMethodDef BPy_GPU_offscreen_methods[] = {
- {"new", (PyCFunction)pygpu_offscreen_new, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_new_doc},
- {NULL, NULL, 0, NULL}
-};
-
-PyDoc_STRVAR(BPy_GPU_offscreen_doc,
-"This module provides access to offscreen rendering functions."
-);
-static PyModuleDef BPy_GPU_offscreen_module_def = {
- PyModuleDef_HEAD_INIT,
- "gpu.offscreen", /* m_name */
- BPy_GPU_offscreen_doc, /* m_doc */
- 0, /* m_size */
- BPy_GPU_offscreen_methods, /* m_methods */
- NULL, /* m_reload */
- NULL, /* m_traverse */
- NULL, /* m_clear */
- NULL, /* m_free */
-};
-
-PyObject *BPyInit_gpu_offscreen(void)
-{
- PyObject *submodule;
-
- /* Register the 'GPUOffscreen' class */
- if (PyType_Ready(&BPy_GPUOffScreen_Type)) {
- return NULL;
- }
-
- submodule = PyModule_Create(&BPy_GPU_offscreen_module_def);
-
-#define MODULE_TYPE_ADD(s, t) \
- PyModule_AddObject(s, t.tp_name, (PyObject *)&t); Py_INCREF((PyObject *)&t)
-
- MODULE_TYPE_ADD(submodule, BPy_GPUOffScreen_Type);
-
-#undef MODULE_TYPE_ADD
-
- return submodule;
-}
-
-#undef BPY_GPU_OFFSCREEN_CHECK_OBJ