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.txt22
-rw-r--r--source/blender/python/intern/bpy.c8
-rw-r--r--source/blender/python/intern/bpy_app.c10
-rw-r--r--source/blender/python/intern/bpy_app_build_options.c21
-rw-r--r--source/blender/python/intern/bpy_app_handlers.c8
-rw-r--r--source/blender/python/intern/bpy_app_icons.c183
-rw-r--r--source/blender/python/intern/bpy_app_icons.h30
-rw-r--r--source/blender/python/intern/bpy_driver.c46
-rw-r--r--source/blender/python/intern/bpy_driver.h7
-rw-r--r--source/blender/python/intern/bpy_interface.c5
-rw-r--r--source/blender/python/intern/bpy_intern_string.c44
-rw-r--r--source/blender/python/intern/bpy_intern_string.h21
-rw-r--r--source/blender/python/intern/bpy_library_load.c13
-rw-r--r--source/blender/python/intern/bpy_manipulator_wrap.c236
-rw-r--r--source/blender/python/intern/bpy_manipulator_wrap.h36
-rw-r--r--source/blender/python/intern/bpy_msgbus.c400
-rw-r--r--source/blender/python/intern/bpy_msgbus.h30
-rw-r--r--source/blender/python/intern/bpy_props.h2
-rw-r--r--source/blender/python/intern/bpy_rna.c133
-rw-r--r--source/blender/python/intern/bpy_rna.h4
-rw-r--r--source/blender/python/intern/bpy_rna_anim.c6
-rw-r--r--source/blender/python/intern/bpy_rna_callback.c2
-rw-r--r--source/blender/python/intern/bpy_rna_manipulator.c565
-rw-r--r--source/blender/python/intern/bpy_rna_manipulator.h32
-rw-r--r--source/blender/python/intern/gpu.c253
-rw-r--r--source/blender/python/intern/gpu.h2
-rw-r--r--source/blender/python/intern/gpu_offscreen.c34
-rw-r--r--source/blender/python/intern/gpu_py_matrix.c552
-rw-r--r--source/blender/python/intern/gpu_py_select.c92
29 files changed, 2420 insertions, 377 deletions
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index ae16bd4a145..b3f4b4977bf 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -52,6 +52,7 @@ 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
@@ -65,6 +66,8 @@ set(SRC
bpy_intern_string.c
bpy_library_load.c
bpy_library_write.c
+ bpy_manipulator_wrap.c
+ bpy_msgbus.c
bpy_operator.c
bpy_operator_wrap.c
bpy_path.c
@@ -75,11 +78,14 @@ set(SRC
bpy_rna_callback.c
bpy_rna_driver.c
bpy_rna_id_collection.c
+ bpy_rna_manipulator.c
bpy_traceback.c
bpy_utils_previews.c
bpy_utils_units.c
gpu.c
gpu_offscreen.c
+ gpu_py_matrix.c
+ gpu_py_select.c
stubs.c
bpy.h
@@ -88,6 +94,7 @@ 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
@@ -98,6 +105,8 @@ set(SRC
bpy_driver.h
bpy_intern_string.h
bpy_library.h
+ bpy_manipulator_wrap.h
+ bpy_msgbus.h
bpy_operator.h
bpy_operator_wrap.h
bpy_path.h
@@ -107,6 +116,7 @@ set(SRC
bpy_rna_callback.h
bpy_rna_driver.h
bpy_rna_id_collection.h
+ bpy_rna_manipulator.h
bpy_traceback.h
bpy_utils_previews.h
bpy_utils_units.h
@@ -172,10 +182,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()
@@ -184,10 +190,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()
@@ -298,10 +300,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..751625e378b 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_manipulator.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_manipulator_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 66c64b580f3..03662102bbc 100644
--- a/source/blender/python/intern/bpy_app.c
+++ b/source/blender/python/intern/bpy_app.c
@@ -47,6 +47,9 @@
#include "bpy_app_handlers.h"
#include "bpy_driver.h"
+/* modules */
+#include "bpy_app_icons.h"
+
#include "BLI_utildefines.h"
#include "BKE_appdir.h"
@@ -117,6 +120,9 @@ 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"},
{NULL},
};
@@ -129,6 +135,7 @@ PyDoc_STRVAR(bpy_app_doc,
" :maxdepth: 1\n"
"\n"
" bpy.app.handlers.rst\n"
+" bpy.app.icons.rst\n"
" bpy.app.translations.rst\n"
);
@@ -210,6 +217,9 @@ static PyObject *make_app_info(void)
SetObjItem(BPY_app_handlers_struct());
SetObjItem(BPY_app_translations_struct());
+ /* modules */
+ SetObjItem(BPY_app_icons_module());
+
#undef SetIntItem
#undef SetStrItem
#undef SetBytesItem
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 90aa22de5bf..948a78d5794 100644
--- a/source/blender/python/intern/bpy_app_handlers.c
+++ b/source/blender/python/intern/bpy_app_handlers.c
@@ -59,14 +59,6 @@ static PyStructSequence_Field app_cb_info_fields[] = {
{(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 *)"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"},
/* sets the permanent tag */
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..4bd059dd295
--- /dev/null
+++ b/source/blender/python/intern/bpy_app_icons.c
@@ -0,0 +1,183 @@
+/*
+ * ***** 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 range: File path.\n"
+" :type range: 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 *mod = PyModule_Create(&M_AppIcons_module_def);
+
+ 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_driver.c b/source/blender/python/intern/bpy_driver.c
index bc81eb8ee24..b8c71d4e054 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
@@ -383,8 +385,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;
@@ -400,7 +406,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;
@@ -440,50 +446,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 */
@@ -557,7 +563,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);
}
}
}
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_interface.c b/source/blender/python/intern/bpy_interface.c
index c84765e9a47..0d7b0c92a90 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -75,6 +75,7 @@
#include "../generic/blf_py_api.h"
#include "../generic/idprop_py_api.h"
#include "../generic/imbuf_py_api.h"
+#include "../gawain/gwn_py_api.h"
#include "../bmesh/bmesh_py_api.h"
#include "../mathutils/mathutils.h"
@@ -218,6 +219,7 @@ static struct _inittab bpy_internal_modules[] = {
{"mathutils.kdtree", PyInit_mathutils_kdtree},
#endif
{"_bpy_path", BPyInit__bpy_path},
+ {"_gawain", BPyInit_gawain},
{"bgl", BPyInit_bgl},
{"blf", BPyInit_blf},
{"imbuf", BPyInit_imbuf},
@@ -537,7 +539,8 @@ static bool python_script_exec(
if (py_dict) {
#ifdef PYMODULE_CLEAR_WORKAROUND
- PyModuleObject *mmod = (PyModuleObject *)PyDict_GetItemString(PyThreadState_GET()->interp->modules, "__main__");
+ PyModuleObject *mmod = (PyModuleObject *)PyDict_GetItem(
+ PyThreadState_GET()->interp->modules, 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..6911e985e93 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___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_frame;
PyObject *bpy_intern_str_order;
-PyObject *bpy_intern_str_attr;
+PyObject *bpy_intern_str_properties;
+PyObject *bpy_intern_str_register;
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_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___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_frame, "frame");
BPY_INTERN_STR(bpy_intern_str_order, "order");
- BPY_INTERN_STR(bpy_intern_str_attr, "attr");
+ 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_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_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..998c312c321 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___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_frame;
extern PyObject *bpy_intern_str_order;
-extern PyObject *bpy_intern_str_attr;
+extern PyObject *bpy_intern_str_properties;
+extern PyObject *bpy_intern_str_register;
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_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..d60c44e702a 100644
--- a/source/blender/python/intern/bpy_library_load.c
+++ b/source/blender/python/intern/bpy_library_load.c
@@ -38,8 +38,6 @@
#include "BLI_linklist.h"
#include "BLI_path_util.h"
-#include "BLO_readfile.h"
-
#include "BKE_main.h"
#include "BKE_library.h"
#include "BKE_idcode.h"
@@ -48,6 +46,8 @@
#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);
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_manipulator_wrap.c b/source/blender/python/intern/bpy_manipulator_wrap.c
new file mode 100644
index 00000000000..9df4e81ec55
--- /dev/null
+++ b/source/blender/python/intern/bpy_manipulator_wrap.c
@@ -0,0 +1,236 @@
+/*
+ * ***** 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_manipulator_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_manipulator_wrap.h" /* own include */
+
+/* we may want to add, but not now */
+
+/* -------------------------------------------------------------------- */
+
+/** \name Manipulator
+ * \{ */
+
+
+static bool bpy_manipulatortype_target_property_def(
+ wmManipulatorType *wt, 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_manipulatortype_target_property_def(wt, params.id, params.type, params.array_length);
+ Py_DECREF(empty_tuple);
+ return true;
+
+fail:
+ Py_DECREF(empty_tuple);
+ return false;
+}
+
+static void manipulator_properties_init(wmManipulatorType *wt)
+{
+ PyTypeObject *py_class = wt->ext.data;
+ RNA_struct_blender_type_set(wt->ext.srna, wt);
+
+ /* 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(wt->srna, wt->idname);
+
+ if (pyrna_deferred_register_class(wt->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_manipulatortype_target_property_def(wt, items[i])) {
+ PyErr_Print();
+ PyErr_Clear();
+ break;
+ }
+ }
+
+ Py_DECREF(bl_target_properties_fast);
+ }
+ }
+}
+
+void BPY_RNA_manipulator_wrapper(wmManipulatorType *wt, void *userdata)
+{
+ /* take care not to overwrite anything set in
+ * WM_manipulatormaptype_group_link_ptr before opfunc() is called */
+ StructRNA *srna = wt->srna;
+ *wt = *((wmManipulatorType *)userdata);
+ wt->srna = srna; /* restore */
+
+ /* don't do translations here yet */
+#if 0
+ /* Use i18n context from ext.srna if possible (py manipulatorgroups). */
+ if (wt->ext.srna) {
+ RNA_def_struct_translation_context(wt->srna, RNA_struct_translation_context(wt->ext.srna));
+ }
+#endif
+
+ wt->struct_size = sizeof(wmManipulator);
+
+ manipulator_properties_init(wt);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Manipulator Group
+ * \{ */
+
+static void manipulatorgroup_properties_init(wmManipulatorGroupType *wgt)
+{
+#ifdef USE_SRNA
+ PyTypeObject *py_class = wgt->ext.data;
+#endif
+ RNA_struct_blender_type_set(wgt->ext.srna, wgt);
+
+#ifdef USE_SRNA
+ /* 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(wgt->srna, wgt->idname);
+
+ if (pyrna_deferred_register_class(wgt->srna, py_class) != 0) {
+ PyErr_Print(); /* failed to register operator props */
+ PyErr_Clear();
+ }
+#endif
+}
+
+void BPY_RNA_manipulatorgroup_wrapper(wmManipulatorGroupType *wgt, void *userdata)
+{
+ /* take care not to overwrite anything set in
+ * WM_manipulatormaptype_group_link_ptr before opfunc() is called */
+#ifdef USE_SRNA
+ StructRNA *srna = wgt->srna;
+#endif
+ *wgt = *((wmManipulatorGroupType *)userdata);
+#ifdef USE_SRNA
+ wgt->srna = srna; /* restore */
+#endif
+
+#ifdef USE_SRNA
+ /* Use i18n context from ext.srna if possible (py manipulatorgroups). */
+ if (wgt->ext.srna) {
+ RNA_def_struct_translation_context(wgt->srna, RNA_struct_translation_context(wgt->ext.srna));
+ }
+#endif
+
+ manipulatorgroup_properties_init(wgt);
+}
+
+/** \} */
+
diff --git a/source/blender/python/intern/bpy_manipulator_wrap.h b/source/blender/python/intern/bpy_manipulator_wrap.h
new file mode 100644
index 00000000000..3f739e26059
--- /dev/null
+++ b/source/blender/python/intern/bpy_manipulator_wrap.h
@@ -0,0 +1,36 @@
+/*
+ * ***** 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_manipulator_wrap.h
+ * \ingroup pythonintern
+ */
+
+#ifndef __BPY_MANIPULATOR_WRAP_H__
+#define __BPY_MANIPULATOR_WRAP_H__
+
+struct wmManipulatorType;
+struct wmManipulatorGroupType;
+
+/* exposed to rna/wm api */
+void BPY_RNA_manipulator_wrapper(struct wmManipulatorType *wt, void *userdata);
+void BPY_RNA_manipulatorgroup_wrapper(struct wmManipulatorGroupType *wgt, void *userdata);
+
+#endif /* __BPY_MANIPULATOR_WRAP_H__ */
+
diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c
new file mode 100644
index 00000000000..941d6b760dc
--- /dev/null
+++ b/source/blender/python/intern/bpy_msgbus.c
@@ -0,0 +1,400 @@
+/*
+ * ***** 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_manipulator_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.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 8c47ba3c2c5..be0e9771a0a 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);
@@ -1849,19 +1849,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_Manipulator)) {
+ 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/ManipulatorProperties 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_ManipulatorProperties) {
+ 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 +3509,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"
@@ -5164,6 +5235,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},
@@ -7645,7 +7718,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_Manipulator));
// 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);
@@ -8303,6 +8377,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 {
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..7012e04628c 100644
--- a/source/blender/python/intern/bpy_rna_anim.c
+++ b/source/blender/python/intern/bpy_rna_anim.c
@@ -233,6 +233,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 +255,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, options);
}
else {
BKE_reportf(&reports, RPT_ERROR, "Could not resolve path (%s)", path_full);
@@ -268,13 +269,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);
+ result = insert_keyframe(G_MAIN, depsgraph, &reports, id, NULL, group_name, path_full, index, cfra, keytype, options);
MEM_freeN((void *)path_full);
if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1)
diff --git a/source/blender/python/intern/bpy_rna_callback.c b/source/blender/python/intern/bpy_rna_callback.c
index fbdcc03ddd0..7adb1d40fcc 100644
--- a/source/blender/python/intern/bpy_rna_callback.c
+++ b/source/blender/python/intern/bpy_rna_callback.c
@@ -171,9 +171,7 @@ 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_SpaceClipEditor) return SPACE_CLIP;
diff --git a/source/blender/python/intern/bpy_rna_manipulator.c b/source/blender/python/intern/bpy_rna_manipulator.c
new file mode 100644
index 00000000000..950f7f98be0
--- /dev/null
+++ b/source/blender/python/intern/bpy_rna_manipulator.c
@@ -0,0 +1,565 @@
+/*
+ * ***** 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_manipulator.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_manipulator.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 Manipulator Target Property Define API
+ * \{ */
+
+enum {
+ BPY_MANIPULATOR_FN_SLOT_GET = 0,
+ BPY_MANIPULATOR_FN_SLOT_SET,
+ BPY_MANIPULATOR_FN_SLOT_RANGE_GET,
+};
+#define BPY_MANIPULATOR_FN_SLOT_LEN (BPY_MANIPULATOR_FN_SLOT_RANGE_GET + 1)
+
+struct BPyManipulatorHandlerUserData {
+
+ PyObject *fn_slots[BPY_MANIPULATOR_FN_SLOT_LEN];
+};
+
+static void py_rna_manipulator_handler_get_cb(
+ const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop,
+ void *value_p)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ struct BPyManipulatorHandlerUserData *data = mpr_prop->custom_func.user_data;
+ PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_MANIPULATOR_FN_SLOT_GET], NULL);
+ if (ret == NULL) {
+ goto fail;
+ }
+
+ if (mpr_prop->type->data_type == PROP_FLOAT) {
+ float *value = value_p;
+ if (mpr_prop->type->array_length == 1) {
+ if ((*value = PyFloat_AsDouble(ret)) == -1.0f && PyErr_Occurred()) {
+ goto fail;
+ }
+ }
+ else {
+ if (PyC_AsArray(value, ret, mpr_prop->type->array_length, &PyFloat_Type, false,
+ "Manipulator 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_manipulator_handler_set_cb(
+ const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop,
+ const void *value_p)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ struct BPyManipulatorHandlerUserData *data = mpr_prop->custom_func.user_data;
+
+ PyObject *args = PyTuple_New(1);
+
+ if (mpr_prop->type->data_type == PROP_FLOAT) {
+ const float *value = value_p;
+ PyObject *py_value;
+ if (mpr_prop->type->array_length == 1) {
+ py_value = PyFloat_FromDouble(*value);
+ }
+ else {
+ py_value = PyC_Tuple_PackArray_F32(value, mpr_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_MANIPULATOR_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_manipulator_handler_range_get_cb(
+ const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop,
+ void *value_p)
+{
+ struct BPyManipulatorHandlerUserData *data = mpr_prop->custom_func.user_data;
+
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_MANIPULATOR_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 (mpr_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_manipulator_handler_free_cb(
+ const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop)
+{
+ struct BPyManipulatorHandlerUserData *data = mpr_prop->custom_func.user_data;
+
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+ for (int i = 0; i < BPY_MANIPULATOR_FN_SLOT_LEN; i++) {
+ Py_XDECREF(data->fn_slots[i]);
+ }
+ PyGILState_Release(gilstate);
+
+ MEM_freeN(data);
+
+}
+
+PyDoc_STRVAR(bpy_manipulator_target_set_handler_doc,
+".. method:: target_set_handler(target, get, set, range=None):\n"
+"\n"
+" Assigns callbacks to a manipulators 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 manipulators that use a range.\n"
+" :type range: callable\n"
+);
+static PyObject *bpy_manipulator_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_MANIPULATOR_FN_SLOT_LEN];
+ } params = {
+ .self = NULL,
+ .target = NULL,
+ .py_fn_slots = {NULL},
+ };
+
+ /* Note: this is a counter-part to functions:
+ * 'Manipulator.target_set_prop & target_set_operator'
+ * (see: rna_wm_manipulator_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_MANIPULATOR_FN_SLOT_GET],
+ &params.py_fn_slots[BPY_MANIPULATOR_FN_SLOT_SET],
+ &params.py_fn_slots[BPY_MANIPULATOR_FN_SLOT_RANGE_GET]))
+ {
+ goto fail;
+ }
+
+ wmManipulator *mpr = ((BPy_StructRNA *)params.self)->ptr.data;
+
+ const wmManipulatorPropertyType *mpr_prop_type =
+ WM_manipulatortype_target_property_find(mpr->type, params.target);
+ if (mpr_prop_type == NULL) {
+ PyErr_Format(PyExc_ValueError,
+ "Manipulator target property '%s.%s' not found",
+ mpr->type->idname, params.target);
+ goto fail;
+ }
+
+ {
+ const int slots_required = 2;
+ const int slots_start = 2;
+ for (int i = 0; i < BPY_MANIPULATOR_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 BPyManipulatorHandlerUserData *data = MEM_callocN(sizeof(*data), __func__);
+
+ for (int i = 0; i < BPY_MANIPULATOR_FN_SLOT_LEN; i++) {
+ data->fn_slots[i] = params.py_fn_slots[i];
+ Py_XINCREF(params.py_fn_slots[i]);
+ }
+
+ WM_manipulator_target_property_def_func_ptr(
+ mpr, mpr_prop_type,
+ &(const struct wmManipulatorPropertyFnParams) {
+ .value_get_fn = py_rna_manipulator_handler_get_cb,
+ .value_set_fn = py_rna_manipulator_handler_set_cb,
+ .range_get_fn = py_rna_manipulator_handler_range_get_cb,
+ .free_fn = py_rna_manipulator_handler_free_cb,
+ .user_data = data,
+ });
+
+ PyGILState_Release(gilstate);
+
+ Py_RETURN_NONE;
+
+fail:
+ PyGILState_Release(gilstate);
+ return NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Manipulator Target Property Access API
+ * \{ */
+
+PyDoc_STRVAR(bpy_manipulator_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_manipulator_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;
+ }
+
+ wmManipulator *mpr = ((BPy_StructRNA *)params.self)->ptr.data;
+
+ wmManipulatorProperty *mpr_prop =
+ WM_manipulator_target_property_find(mpr, params.target);
+ if (mpr_prop == NULL) {
+ PyErr_Format(PyExc_ValueError,
+ "Manipulator target property '%s.%s' not found",
+ mpr->type->idname, params.target);
+ goto fail;
+ }
+
+ const int array_len = WM_manipulator_target_property_array_length(mpr, mpr_prop);
+ switch (mpr_prop->type->data_type) {
+ case PROP_FLOAT:
+ {
+ if (array_len != 0) {
+ float *value = BLI_array_alloca(value, array_len);
+ WM_manipulator_target_property_value_get_array(mpr, mpr_prop, value);
+ return PyC_Tuple_PackArray_F32(value, array_len);
+ }
+ else {
+ float value = WM_manipulator_target_property_value_get(mpr, mpr_prop);
+ return PyFloat_FromDouble(value);
+ }
+ break;
+ }
+ default:
+ {
+ PyErr_SetString(PyExc_RuntimeError, "Not yet supported type");
+ goto fail;
+ }
+ }
+
+fail:
+ return NULL;
+}
+
+PyDoc_STRVAR(bpy_manipulator_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_manipulator_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;
+ }
+
+ wmManipulator *mpr = ((BPy_StructRNA *)params.self)->ptr.data;
+
+ wmManipulatorProperty *mpr_prop =
+ WM_manipulator_target_property_find(mpr, params.target);
+ if (mpr_prop == NULL) {
+ PyErr_Format(PyExc_ValueError,
+ "Manipulator target property '%s.%s' not found",
+ mpr->type->idname, params.target);
+ goto fail;
+ }
+
+ const int array_len = WM_manipulator_target_property_array_length(mpr, mpr_prop);
+ switch (mpr_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, mpr_prop->type->array_length, &PyFloat_Type, false,
+ "Manipulator target property array") == -1)
+ {
+ goto fail;
+ }
+ WM_manipulator_target_property_value_set_array(BPy_GetContext(), mpr, mpr_prop, value);
+ }
+ else {
+ float value;
+ if ((value = PyFloat_AsDouble(params.value)) == -1.0f && PyErr_Occurred()) {
+ goto fail;
+ }
+ WM_manipulator_target_property_value_set(BPy_GetContext(), mpr, mpr_prop, value);
+ }
+ Py_RETURN_NONE;
+ }
+ default:
+ {
+ PyErr_SetString(PyExc_RuntimeError, "Not yet supported type");
+ goto fail;
+ }
+ }
+
+fail:
+ return NULL;
+}
+
+
+PyDoc_STRVAR(bpy_manipulator_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"
+" :Get the range for this target property"
+" :return: The range of this property (min, max).\n"
+" :rtype: tuple pair.\n"
+);
+static PyObject *bpy_manipulator_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;
+ }
+
+ wmManipulator *mpr = ((BPy_StructRNA *)params.self)->ptr.data;
+
+ wmManipulatorProperty *mpr_prop =
+ WM_manipulator_target_property_find(mpr, params.target);
+ if (mpr_prop == NULL) {
+ PyErr_Format(PyExc_ValueError,
+ "Manipulator target property '%s.%s' not found",
+ mpr->type->idname, params.target);
+ goto fail;
+ }
+
+ switch (mpr_prop->type->data_type) {
+ case PROP_FLOAT:
+ {
+ float range[2];
+ WM_manipulator_target_property_range_get(mpr, mpr_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_manipulator_module(PyObject *mod_par)
+{
+ static PyMethodDef method_def_array[] = {
+ /* Manipulator Target Property Define API */
+ {"target_set_handler", (PyCFunction)bpy_manipulator_target_set_handler,
+ METH_VARARGS | METH_KEYWORDS, bpy_manipulator_target_set_handler_doc},
+ /* Manipulator Target Property Access API */
+ {"target_get_value", (PyCFunction)bpy_manipulator_target_get_value,
+ METH_VARARGS | METH_KEYWORDS, bpy_manipulator_target_get_value_doc},
+ {"target_set_value", (PyCFunction)bpy_manipulator_target_set_value,
+ METH_VARARGS | METH_KEYWORDS, bpy_manipulator_target_set_value_doc},
+ {"target_get_range", (PyCFunction)bpy_manipulator_target_get_range,
+ METH_VARARGS | METH_KEYWORDS, bpy_manipulator_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_manipulator_%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/bpy_rna_manipulator.h b/source/blender/python/intern/bpy_rna_manipulator.h
new file mode 100644
index 00000000000..b6f3a2e651d
--- /dev/null
+++ b/source/blender/python/intern/bpy_rna_manipulator.h
@@ -0,0 +1,32 @@
+/*
+ * ***** 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.
+ *
+ * Contributor(s): Bastien Montagne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_rna_manipulator.h
+ * \ingroup pythonintern
+ */
+
+#ifndef __BPY_RNA_MANIPULATOR_H__
+#define __BPY_RNA_MANIPULATOR_H__
+
+int BPY_rna_manipulator_module(PyObject *);
+
+#endif /* __BPY_RNA_MANIPULATOR_H__ */
diff --git a/source/blender/python/intern/gpu.c b/source/blender/python/intern/gpu.c
index ece27915c9c..e77470c070e 100644
--- a/source/blender/python/intern/gpu.c
+++ b/source/blender/python/intern/gpu.c
@@ -55,7 +55,7 @@
#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."
+"This module provides access to GPU offscreen rendering, matrix stacks and selection."
);
static struct PyModuleDef gpumodule = {
PyModuleDef_HEAD_INIT,
@@ -74,251 +74,10 @@ static PyObject *PyInit_gpu(void)
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 */
@@ -330,13 +89,19 @@ PyObject *GPU_initPython(void)
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);
+ PyModule_AddObject(module, "matrix", (submodule = BPyInit_gpu_matrix()));
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+ Py_INCREF(submodule);
+
+ PyModule_AddObject(module, "select", (submodule = BPyInit_gpu_select()));
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+ Py_INCREF(submodule);
+
PyDict_SetItem(PyImport_GetModuleDict(), PyModule_GetNameObject(module), module);
return module;
}
diff --git a/source/blender/python/intern/gpu.h b/source/blender/python/intern/gpu.h
index 0da44a4eb87..92841db9027 100644
--- a/source/blender/python/intern/gpu.h
+++ b/source/blender/python/intern/gpu.h
@@ -37,5 +37,7 @@
PyObject *GPU_initPython(void);
PyObject *BPyInit_gpu_offscreen(void);
+PyObject *BPyInit_gpu_matrix(void);
+PyObject *BPyInit_gpu_select(void);
#endif /* __GPU_H__ */
diff --git a/source/blender/python/intern/gpu_offscreen.c b/source/blender/python/intern/gpu_offscreen.c
index 3b9b3c70ead..f0bc4b79296 100644
--- a/source/blender/python/intern/gpu_offscreen.c
+++ b/source/blender/python/intern/gpu_offscreen.c
@@ -40,8 +40,8 @@
#include "ED_screen.h"
-#include "GPU_compositing.h"
#include "GPU_framebuffer.h"
+#include "GPU_texture.h"
#include "../mathutils/mathutils.h"
@@ -92,7 +92,8 @@ 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));
+ GPUTexture *texture = GPU_offscreen_color_texture(self->ofs);
+ return PyLong_FromLong(GPU_texture_opengl_bindcode(texture));
}
PyDoc_STRVAR(pygpu_offscreen_bind_doc,
@@ -165,13 +166,15 @@ PyDoc_STRVAR(pygpu_offscreen_draw_view3d_doc,
);
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};
+ /* TODO: This doesn't work currently because of missing depsgraph. */
+#if 0
+ static const char *kwlist[] = {"scene", "view_layer", "view3d", "region", "projection_matrix", "modelview_matrix", NULL};
MatrixObject *py_mat_modelview, *py_mat_projection;
- PyObject *py_scene, *py_region, *py_view3d;
+ PyObject *py_scene, *py_view_layer, *py_region, *py_view3d;
- struct Main *bmain = G_MAIN; /* XXX UGLY! */
Scene *scene;
+ ViewLayer *view_layer;
View3D *v3d;
ARegion *ar;
GPUFX *fx;
@@ -181,13 +184,14 @@ static PyObject *pygpu_offscreen_draw_view3d(BPy_GPUOffScreen *self, PyObject *a
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
if (!PyArg_ParseTupleAndKeywords(
- args, kwds, "OOOO&O&:draw_view3d", (char **)(kwlist),
- &py_scene, &py_view3d, &py_region,
+ args, kwds, "OOOOO&O&:draw_view3d", (char **)(kwlist),
+ &py_scene, &py_view_layer, &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"))))
+ (!(scene = PyC_RNA_AsPointer(py_scene, "Scene")) ||
+ !(view_layer = PyC_RNA_AsPointer(py_view_layer, "ViewLayer")) ||
+ !(v3d = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) ||
+ !(ar = PyC_RNA_AsPointer(py_region, "Region"))))
{
return NULL;
}
@@ -198,14 +202,12 @@ static PyObject *pygpu_offscreen_draw_view3d(BPy_GPUOffScreen *self, PyObject *a
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),
+ scene, view_layer, 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,
@@ -218,6 +220,10 @@ static PyObject *pygpu_offscreen_draw_view3d(BPy_GPUOffScreen *self, PyObject *a
MEM_freeN(rv3d_mats);
Py_RETURN_NONE;
+#else
+ UNUSED_VARS(self, args, kwds);
+#endif
+ return NULL;
}
PyDoc_STRVAR(pygpu_offscreen_free_doc,
@@ -352,7 +358,7 @@ static PyObject *pygpu_offscreen_new(PyObject *UNUSED(self), PyObject *args, PyO
return NULL;
}
- ofs = GPU_offscreen_create(width, height, samples, err_out);
+ ofs = GPU_offscreen_create(width, height, samples, true, false, err_out);
if (ofs == NULL) {
PyErr_Format(PyExc_RuntimeError,
diff --git a/source/blender/python/intern/gpu_py_matrix.c b/source/blender/python/intern/gpu_py_matrix.c
new file mode 100644
index 00000000000..68b08dfb324
--- /dev/null
+++ b/source/blender/python/intern/gpu_py_matrix.c
@@ -0,0 +1,552 @@
+/*
+ * ***** 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/gpu_py_matrix.c
+ * \ingroup pythonintern
+ *
+ * This file defines the gpu.matrix stack API.
+ *
+ * \warning While these functions attempt to ensure correct stack usage.
+ * Mixing Python and C functions may still crash on invalid use.
+ */
+
+#include <Python.h>
+
+
+#include "BLI_utildefines.h"
+
+#include "../mathutils/mathutils.h"
+
+#include "../generic/py_capi_utils.h"
+
+#include "gpu.h"
+
+#define USE_GPU_PY_MATRIX_API
+#include "GPU_matrix.h"
+#undef USE_GPU_PY_MATRIX_API
+
+/* -------------------------------------------------------------------- */
+/** \name Helper Functions
+ * \{ */
+
+static bool pygpu_stack_is_push_model_view_ok_or_error(void)
+{
+ if (GPU_matrix_stack_level_get_model_view() >= GPU_PY_MATRIX_STACK_LEN) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Maximum model-view stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
+ return false;
+ }
+ return true;
+}
+
+static bool pygpu_stack_is_push_projection_ok_or_error(void)
+{
+ if (GPU_matrix_stack_level_get_projection() >= GPU_PY_MATRIX_STACK_LEN) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Maximum projection stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
+ return false;
+ }
+ return true;
+}
+
+static bool pygpu_stack_is_pop_model_view_ok_or_error(void)
+{
+ if (GPU_matrix_stack_level_get_model_view() == 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Minimum model-view stack depth reached");
+ return false;
+ }
+ return true;
+}
+
+static bool pygpu_stack_is_pop_projection_ok_or_error(void)
+{
+ if (GPU_matrix_stack_level_get_projection() == 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Minimum projection stack depth reached");
+ return false;
+ }
+ return true;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Manage Stack
+ * \{ */
+
+PyDoc_STRVAR(pygpu_matrix_push_doc,
+"push()\n"
+"\n"
+" Add to the model-view matrix stack.\n"
+);
+static PyObject *pygpu_matrix_push(PyObject *UNUSED(self))
+{
+ if (!pygpu_stack_is_push_model_view_ok_or_error()) {
+ return NULL;
+ }
+ gpuPushMatrix();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_matrix_pop_doc,
+"pop()\n"
+"\n"
+" Remove the last model-view matrix from the stack.\n"
+);
+static PyObject *pygpu_matrix_pop(PyObject *UNUSED(self))
+{
+ if (!pygpu_stack_is_pop_model_view_ok_or_error()) {
+ return NULL;
+ }
+ gpuPopMatrix();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_matrix_push_projection_doc,
+"push_projection()\n"
+"\n"
+" Add to the projection matrix stack.\n"
+);
+static PyObject *pygpu_matrix_push_projection(PyObject *UNUSED(self))
+{
+ if (!pygpu_stack_is_push_projection_ok_or_error()) {
+ return NULL;
+ }
+ gpuPushProjectionMatrix();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_matrix_pop_projection_doc,
+"pop_projection()\n"
+"\n"
+" Remove the last projection matrix from the stack.\n"
+);
+static PyObject *pygpu_matrix_pop_projection(PyObject *UNUSED(self))
+{
+ if (!pygpu_stack_is_pop_projection_ok_or_error()) {
+ return NULL;
+ }
+ gpuPopProjectionMatrix();
+ Py_RETURN_NONE;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Stack (Context Manager)
+ *
+ * Safer alternative to ensure balanced push/pop calls.
+ *
+ * \{ */
+
+typedef struct {
+ PyObject_HEAD /* required python macro */
+ int type;
+ int level;
+} BPy_GPU_MatrixStackContext;
+
+enum {
+ PYGPU_MATRIX_TYPE_MODEL_VIEW = 1,
+ PYGPU_MATRIX_TYPE_PROJECTION = 2,
+};
+
+static PyObject *pygpu_matrix_stack_context_enter(BPy_GPU_MatrixStackContext *self);
+static PyObject *pygpu_matrix_stack_context_exit(BPy_GPU_MatrixStackContext *self, PyObject *args);
+
+static PyMethodDef pygpu_matrix_stack_context_methods[] = {
+ {"__enter__", (PyCFunction)pygpu_matrix_stack_context_enter, METH_NOARGS},
+ {"__exit__", (PyCFunction)pygpu_matrix_stack_context_exit, METH_VARARGS},
+ {NULL}
+};
+
+static PyTypeObject pygpu_matrix_stack_context_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "GPUMatrixStackContext",
+ .tp_basicsize = sizeof(BPy_GPU_MatrixStackContext),
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_methods = pygpu_matrix_stack_context_methods,
+};
+
+static PyObject *pygpu_matrix_stack_context_enter(BPy_GPU_MatrixStackContext *self)
+{
+ /* sanity - should never happen */
+ if (self->level != -1) {
+ PyErr_SetString(PyExc_RuntimeError, "Already in use");
+ return NULL;
+ }
+
+ if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
+ if (!pygpu_stack_is_push_model_view_ok_or_error()) {
+ return NULL;
+ }
+ gpuPushMatrix();
+ self->level = GPU_matrix_stack_level_get_model_view();
+ }
+ else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
+ if (!pygpu_stack_is_push_projection_ok_or_error()) {
+ return NULL;
+ }
+ gpuPushProjectionMatrix();
+ self->level = GPU_matrix_stack_level_get_projection();
+ }
+ else {
+ BLI_assert(0);
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *pygpu_matrix_stack_context_exit(BPy_GPU_MatrixStackContext *self, PyObject *UNUSED(args))
+{
+ /* sanity - should never happen */
+ if (self->level == -1) {
+ fprintf(stderr, "Not yet in use\n");
+ goto finally;
+ }
+
+ if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
+ const int level = GPU_matrix_stack_level_get_model_view();
+ if (level != self->level) {
+ fprintf(stderr, "Level push/pop mismatch, expected %d, got %d\n", self->level, level);
+ }
+ if (level != 0) {
+ gpuPopMatrix();
+ }
+ }
+ else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
+ const int level = GPU_matrix_stack_level_get_projection();
+ if (level != self->level) {
+ fprintf(stderr, "Level push/pop mismatch, expected %d, got %d", self->level, level);
+ }
+ if (level != 0) {
+ gpuPopProjectionMatrix();
+ }
+ }
+ else {
+ BLI_assert(0);
+ }
+finally:
+ Py_RETURN_NONE;
+}
+
+static PyObject *pygpu_matrix_push_pop_impl(int type)
+{
+ BPy_GPU_MatrixStackContext *ret = PyObject_New(BPy_GPU_MatrixStackContext, &pygpu_matrix_stack_context_Type);
+ ret->type = type;
+ ret->level = -1;
+ return (PyObject *)ret;
+}
+
+PyDoc_STRVAR(pygpu_matrix_push_pop_doc,
+"push_pop()\n"
+"\n"
+" Context manager to ensure balanced push/pop calls, even in the case of an error.\n"
+);
+static PyObject *pygpu_matrix_push_pop(PyObject *UNUSED(self))
+{
+ return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_MODEL_VIEW);
+}
+
+PyDoc_STRVAR(pygpu_matrix_push_pop_projection_doc,
+"push_pop_projection()\n"
+"\n"
+" Context manager to ensure balanced push/pop calls, even in the case of an error.\n"
+);
+static PyObject *pygpu_matrix_push_pop_projection(PyObject *UNUSED(self))
+{
+ return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_PROJECTION);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Manipulate State
+ * \{ */
+
+PyDoc_STRVAR(pygpu_matrix_multiply_matrix_doc,
+"multiply_matrix(matrix)\n"
+"\n"
+" Multiply the current stack matrix.\n"
+"\n"
+" :param matrix: A 4x4 matrix.\n"
+" :type matrix: :class:`mathutils.Matrix`\n"
+);
+static PyObject *pygpu_matrix_multiply_matrix(PyObject *UNUSED(self), PyObject *value)
+{
+ MatrixObject *pymat;
+ if (!Matrix_Parse4x4(value, &pymat)) {
+ return NULL;
+ }
+ gpuMultMatrix(pymat->matrix);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_matrix_scale_doc,
+"scale(scale)\n"
+"\n"
+" Scale the current stack matrix.\n"
+"\n"
+" :param scale: Scale the current stack matrix.\n"
+" :type scale: sequence of 2 or 3 floats\n"
+);
+static PyObject *pygpu_matrix_scale(PyObject *UNUSED(self), PyObject *value)
+{
+ float scale[3];
+ int len;
+ if ((len = mathutils_array_parse(scale, 2, 3, value, "gpu.matrix.scale(): invalid vector arg")) == -1) {
+ return NULL;
+ }
+ if (len == 2) {
+ gpuScale2fv(scale);
+ }
+ else {
+ gpuScale3fv(scale);
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_matrix_scale_uniform_doc,
+"scale_uniform(scale)\n"
+"\n"
+" :param scale: Scale the current stack matrix.\n"
+" :type scale: sequence of 2 or 3 floats\n"
+);
+static PyObject *pygpu_matrix_scale_uniform(PyObject *UNUSED(self), PyObject *value)
+{
+ float scalar;
+ if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) {
+ PyErr_Format(PyExc_TypeError,
+ "expected a number, not %.200s",
+ Py_TYPE(value)->tp_name);
+ return NULL;
+ }
+ gpuScaleUniform(scalar);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_matrix_translate_doc,
+"translate(offset)\n"
+"\n"
+" Scale the current stack matrix.\n"
+"\n"
+" :param offset: Translate the current stack matrix.\n"
+" :type offset: sequence of 2 or 3 floats\n"
+);
+static PyObject *pygpu_matrix_translate(PyObject *UNUSED(self), PyObject *value)
+{
+ float offset[3];
+ int len;
+ if ((len = mathutils_array_parse(offset, 2, 3, value, "gpu.matrix.translate(): invalid vector arg")) == -1) {
+ return NULL;
+ }
+ if (len == 2) {
+ gpuTranslate2fv(offset);
+ }
+ else {
+ gpuTranslate3fv(offset);
+ }
+ Py_RETURN_NONE;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Write State
+ * \{ */
+
+PyDoc_STRVAR(pygpu_matrix_reset_doc,
+"reset()\n"
+"\n"
+" Empty stack and set to identity.\n"
+);
+static PyObject *pygpu_matrix_reset(PyObject *UNUSED(self))
+{
+ gpuMatrixReset();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_matrix_load_identity_doc,
+"load_identity()\n"
+"\n"
+" Empty stack and set to identity.\n"
+);
+static PyObject *pygpu_matrix_load_identity(PyObject *UNUSED(self))
+{
+ gpuLoadIdentity();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_matrix_load_matrix_doc,
+"load_matrix(matrix)\n"
+"\n"
+" Load a matrix into the stack.\n"
+"\n"
+" :param matrix: A 4x4 matrix.\n"
+" :type matrix: :class:`mathutils.Matrix`\n"
+);
+static PyObject *pygpu_matrix_load_matrix(PyObject *UNUSED(self), PyObject *value)
+{
+ MatrixObject *pymat;
+ if (!Matrix_Parse4x4(value, &pymat)) {
+ return NULL;
+ }
+ gpuLoadMatrix(pymat->matrix);
+ Py_RETURN_NONE;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Read State
+ * \{ */
+
+PyDoc_STRVAR(pygpu_matrix_get_projection_matrix_doc,
+"get_projection_matrix()\n"
+"\n"
+" Return a copy of the projection matrix.\n"
+"\n"
+" :return: A 4x4 projection matrix.\n"
+" :rtype: :class:`mathutils.Matrix`\n"
+);
+static PyObject *pygpu_matrix_get_projection_matrix(PyObject *UNUSED(self))
+{
+ float matrix[4][4];
+ gpuGetModelViewMatrix(matrix);
+ return Matrix_CreatePyObject(&matrix[0][0], 4, 4, NULL);
+}
+
+
+PyDoc_STRVAR(pygpu_matrix_get_modal_view_matrix_doc,
+"get_view_matrix()\n"
+"\n"
+" Return a copy of the view matrix.\n"
+"\n"
+" :return: A 4x4 view matrix.\n"
+" :rtype: :class:`mathutils.Matrix`\n"
+);
+static PyObject *pygpu_matrix_get_modal_view_matrix(PyObject *UNUSED(self))
+{
+ float matrix[4][4];
+ gpuGetProjectionMatrix(matrix);
+ return Matrix_CreatePyObject(&matrix[0][0], 4, 4, NULL);
+}
+
+PyDoc_STRVAR(pygpu_matrix_get_normal_matrix_doc,
+"get_normal_matrix()\n"
+"\n"
+" Return a copy of the normal matrix.\n"
+"\n"
+" :return: A 3x3 normal matrix.\n"
+" :rtype: :class:`mathutils.Matrix`\n"
+);
+static PyObject *pygpu_matrix_get_normal_matrix(PyObject *UNUSED(self))
+{
+ float matrix[3][3];
+ gpuGetNormalMatrix(matrix);
+ return Matrix_CreatePyObject(&matrix[0][0], 3, 3, NULL);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+static struct PyMethodDef BPy_GPU_matrix_methods[] = {
+ /* Manage Stack */
+ {"push", (PyCFunction)pygpu_matrix_push,
+ METH_NOARGS, pygpu_matrix_push_doc},
+ {"pop", (PyCFunction)pygpu_matrix_pop,
+ METH_NOARGS, pygpu_matrix_pop_doc},
+
+ {"push_projection", (PyCFunction)pygpu_matrix_push_projection,
+ METH_NOARGS, pygpu_matrix_push_projection_doc},
+ {"pop_projection", (PyCFunction)pygpu_matrix_pop_projection,
+ METH_NOARGS, pygpu_matrix_pop_projection_doc},
+
+ /* Stack (Context Manager) */
+ {"push_pop", (PyCFunction)pygpu_matrix_push_pop,
+ METH_NOARGS, pygpu_matrix_push_pop_doc},
+ {"push_pop_projection", (PyCFunction)pygpu_matrix_push_pop_projection,
+ METH_NOARGS, pygpu_matrix_push_pop_projection_doc},
+
+ /* Manipulate State */
+ {"multiply_matrix", (PyCFunction)pygpu_matrix_multiply_matrix,
+ METH_O, pygpu_matrix_multiply_matrix_doc},
+ {"scale", (PyCFunction)pygpu_matrix_scale,
+ METH_O, pygpu_matrix_scale_doc},
+ {"scale_uniform", (PyCFunction)pygpu_matrix_scale_uniform,
+ METH_O, pygpu_matrix_scale_uniform_doc},
+ {"translate", (PyCFunction)pygpu_matrix_translate,
+ METH_O, pygpu_matrix_translate_doc},
+
+ /* TODO */
+#if 0
+ {"rotate", (PyCFunction)pygpu_matrix_rotate,
+ METH_O, pygpu_matrix_rotate_doc},
+ {"rotate_axis", (PyCFunction)pygpu_matrix_rotate_axis,
+ METH_O, pygpu_matrix_rotate_axis_doc},
+ {"look_at", (PyCFunction)pygpu_matrix_look_at,
+ METH_O, pygpu_matrix_look_at_doc},
+#endif
+
+ /* Write State */
+ {"reset", (PyCFunction)pygpu_matrix_reset,
+ METH_NOARGS, pygpu_matrix_reset_doc},
+ {"load_identity", (PyCFunction)pygpu_matrix_load_identity,
+ METH_NOARGS, pygpu_matrix_load_identity_doc},
+ {"load_matrix", (PyCFunction)pygpu_matrix_load_matrix,
+ METH_O, pygpu_matrix_load_matrix_doc},
+
+ /* Read State */
+ {"get_projection_matrix", (PyCFunction)pygpu_matrix_get_projection_matrix,
+ METH_NOARGS, pygpu_matrix_get_projection_matrix_doc},
+ {"get_model_view_matrix", (PyCFunction)pygpu_matrix_get_modal_view_matrix,
+ METH_NOARGS, pygpu_matrix_get_modal_view_matrix_doc},
+ {"get_normal_matrix", (PyCFunction)pygpu_matrix_get_normal_matrix,
+ METH_NOARGS, pygpu_matrix_get_normal_matrix_doc},
+
+ {NULL, NULL, 0, NULL}
+};
+
+PyDoc_STRVAR(BPy_GPU_matrix_doc,
+"This module provides access to the matrix stack."
+);
+static PyModuleDef BPy_GPU_matrix_module_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "gpu.matrix",
+ .m_doc = BPy_GPU_matrix_doc,
+ .m_methods = BPy_GPU_matrix_methods,
+};
+
+PyObject *BPyInit_gpu_matrix(void)
+{
+ PyObject *submodule;
+
+ submodule = PyModule_Create(&BPy_GPU_matrix_module_def);
+
+ if (PyType_Ready(&pygpu_matrix_stack_context_Type) < 0) {
+ return NULL;
+ }
+
+ return submodule;
+}
+
+/** \} */
diff --git a/source/blender/python/intern/gpu_py_select.c b/source/blender/python/intern/gpu_py_select.c
new file mode 100644
index 00000000000..f570c4cdae2
--- /dev/null
+++ b/source/blender/python/intern/gpu_py_select.c
@@ -0,0 +1,92 @@
+/*
+ * ***** 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/gpu_py_select.c
+ * \ingroup pythonintern
+ *
+ * This file defines the gpu.select API.
+ *
+ * \note Currently only used for manipulator selection,
+ * will need to add begin/end and a way to access the hits.
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "../generic/py_capi_utils.h"
+
+#include "gpu.h"
+
+#include "GPU_select.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Methods
+ * \{ */
+
+PyDoc_STRVAR(pygpu_select_load_id_doc,
+"load_id(id)\n"
+"\n"
+" Set the selection ID.\n"
+"\n"
+" :param id: Number (32-bit unsigned int).\n"
+" :type select: int\n"
+);
+static PyObject *pygpu_select_load_id(PyObject *UNUSED(self), PyObject *value)
+{
+ uint id;
+ if ((id = PyC_Long_AsU32(value)) == (uint)-1) {
+ return NULL;
+ }
+ GPU_select_load_id(id);
+ Py_RETURN_NONE;
+}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+static struct PyMethodDef BPy_GPU_select_methods[] = {
+ /* Manage Stack */
+ {"load_id", (PyCFunction)pygpu_select_load_id, METH_O, pygpu_select_load_id_doc},
+ {NULL, NULL, 0, NULL}
+};
+
+PyDoc_STRVAR(BPy_GPU_select_doc,
+"This module provides access to selection."
+);
+static PyModuleDef BPy_GPU_select_module_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "gpu.select",
+ .m_doc = BPy_GPU_select_doc,
+ .m_methods = BPy_GPU_select_methods,
+};
+
+PyObject *BPyInit_gpu_select(void)
+{
+ PyObject *submodule;
+
+ submodule = PyModule_Create(&BPy_GPU_select_module_def);
+
+ return submodule;
+}
+
+/** \} */