Blender V4.3
bpy_app_timers.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_timer.h"
10
11#include <Python.h>
12
13#include "bpy_app_timers.hh"
14
16
17static double handle_returned_value(PyObject *function, PyObject *ret)
18{
19 if (ret == nullptr) {
20 PyErr_PrintEx(0);
21 PyErr_Clear();
22 return -1;
23 }
24
25 if (ret == Py_None) {
26 return -1;
27 }
28
29 double value = PyFloat_AsDouble(ret);
30 if (value == -1.0f && PyErr_Occurred()) {
31 PyErr_Clear();
32 printf("Error: 'bpy.app.timers' callback ");
33 PyObject_Print(function, stdout, Py_PRINT_RAW);
34 printf(" did not return None or float.\n");
35 return -1;
36 }
37
38 if (value < 0.0) {
39 value = 0.0;
40 }
41
42 return value;
43}
44
45static double py_timer_execute(uintptr_t /*uuid*/, void *user_data)
46{
47 PyObject *function = static_cast<PyObject *>(user_data);
48
49 PyGILState_STATE gilstate;
50 gilstate = PyGILState_Ensure();
51
52 PyObject *py_ret = PyObject_CallObject(function, nullptr);
53 const double ret = handle_returned_value(function, py_ret);
54
55 PyGILState_Release(gilstate);
56
57 return ret;
58}
59
60static void py_timer_free(uintptr_t /*uuid*/, void *user_data)
61{
62 PyObject *function = static_cast<PyObject *>(user_data);
63
64 PyGILState_STATE gilstate;
65 gilstate = PyGILState_Ensure();
66
67 Py_DECREF(function);
68
69 PyGILState_Release(gilstate);
70}
71
73 /* Wrap. */
74 bpy_app_timers_register_doc,
75 ".. function:: register(function, first_interval=0, persistent=False)\n"
76 "\n"
77 " Add a new function that will be called after the specified amount of seconds.\n"
78 " The function gets no arguments and is expected to return either None or a float.\n"
79 " If ``None`` is returned, the timer will be unregistered.\n"
80 " A returned number specifies the delay until the function is called again.\n"
81 " ``functools.partial`` can be used to assign some parameters.\n"
82 "\n"
83 " :arg function: The function that should called.\n"
84 " :type function: Callable[[], float | None]\n"
85 " :arg first_interval: Seconds until the callback should be called the first time.\n"
86 " :type first_interval: float\n"
87 " :arg persistent: Don't remove timer when a new file is loaded.\n"
88 " :type persistent: bool\n");
89static PyObject *bpy_app_timers_register(PyObject * /*self*/, PyObject *args, PyObject *kw)
90{
91 PyObject *function;
92 double first_interval = 0;
93 int persistent = false;
94
95 static const char *_keywords[] = {"function", "first_interval", "persistent", nullptr};
96 static _PyArg_Parser _parser = {
98 "O" /* `function` */
99 "|$" /* Optional keyword only arguments. */
100 "d" /* `first_interval` */
101 "p" /* `persistent` */
102 ":register",
103 _keywords,
104 nullptr,
105 };
106 if (!_PyArg_ParseTupleAndKeywordsFast(
107 args, kw, &_parser, &function, &first_interval, &persistent))
108 {
109 return nullptr;
110 }
111
112 if (!PyCallable_Check(function)) {
113 PyErr_SetString(PyExc_TypeError, "function is not callable");
114 return nullptr;
115 }
116
117 Py_INCREF(function);
119 intptr_t(function), py_timer_execute, function, py_timer_free, first_interval, persistent);
120 Py_RETURN_NONE;
121}
122
124 /* Wrap. */
125 bpy_app_timers_unregister_doc,
126 ".. function:: unregister(function)\n"
127 "\n"
128 " Unregister timer.\n"
129 "\n"
130 " :arg function: Function to unregister.\n"
131 " :type function: Callable[[], float | None]\n");
132static PyObject *bpy_app_timers_unregister(PyObject * /*self*/, PyObject *function)
133{
134 if (!BLI_timer_unregister(intptr_t(function))) {
135 PyErr_SetString(PyExc_ValueError, "Error: function is not registered");
136 return nullptr;
137 }
138 Py_RETURN_NONE;
139}
140
142 /* Wrap. */
143 bpy_app_timers_is_registered_doc,
144 ".. function:: is_registered(function)\n"
145 "\n"
146 " Check if this function is registered as a timer.\n"
147 "\n"
148 " :arg function: Function to check.\n"
149 " :type function: Callable[[], float | None]\n"
150 " :return: True when this function is registered, otherwise False.\n"
151 " :rtype: bool\n");
152static PyObject *bpy_app_timers_is_registered(PyObject * /*self*/, PyObject *function)
153{
154 const bool ret = BLI_timer_is_registered(intptr_t(function));
155 return PyBool_FromLong(ret);
156}
157
158#if (defined(__GNUC__) && !defined(__clang__))
159# pragma GCC diagnostic push
160# pragma GCC diagnostic ignored "-Wcast-function-type"
161#endif
162
163static PyMethodDef M_AppTimers_methods[] = {
164 {"register",
165 (PyCFunction)bpy_app_timers_register,
166 METH_VARARGS | METH_KEYWORDS,
167 bpy_app_timers_register_doc},
168 {"unregister", (PyCFunction)bpy_app_timers_unregister, METH_O, bpy_app_timers_unregister_doc},
169 {"is_registered",
170 (PyCFunction)bpy_app_timers_is_registered,
171 METH_O,
172 bpy_app_timers_is_registered_doc},
173 {nullptr, nullptr, 0, nullptr},
174};
175
176#if (defined(__GNUC__) && !defined(__clang__))
177# pragma GCC diagnostic pop
178#endif
179
180static PyModuleDef M_AppTimers_module_def = {
181 /*m_base*/ PyModuleDef_HEAD_INIT,
182 /*m_name*/ "bpy.app.timers",
183 /*m_doc*/ nullptr,
184 /*m_size*/ 0,
185 /*m_methods*/ M_AppTimers_methods,
186 /*m_slots*/ nullptr,
187 /*m_traverse*/ nullptr,
188 /*m_clear*/ nullptr,
189 /*m_free*/ nullptr,
190};
191
193{
194 PyObject *sys_modules = PyImport_GetModuleDict();
195 PyObject *mod = PyModule_Create(&M_AppTimers_module_def);
196 PyDict_SetItem(sys_modules, PyModule_GetNameObject(mod), mod);
197 return mod;
198}
bool BLI_timer_is_registered(uintptr_t uuid)
Definition BLI_timer.c:73
void BLI_timer_register(uintptr_t uuid, BLI_timer_func func, void *user_data, BLI_timer_data_free user_data_free, double first_interval, bool persistent)
Definition BLI_timer.c:34
bool BLI_timer_unregister(uintptr_t uuid)
Definition BLI_timer.c:61
PyObject * BPY_app_timers_module()
static double handle_returned_value(PyObject *function, PyObject *ret)
static PyObject * bpy_app_timers_unregister(PyObject *, PyObject *function)
PyDoc_STRVAR(bpy_app_timers_register_doc, ".. function:: register(function, first_interval=0, persistent=False)\n" "\n" " Add a new function that will be called after the specified amount of seconds.\n" " The function gets no arguments and is expected to return either None or a float.\n" " If ``None`` is returned, the timer will be unregistered.\n" " A returned number specifies the delay until the function is called again.\n" " ``functools.partial`` can be used to assign some parameters.\n" "\n" " :arg function: The function that should called.\n" " :type function: Callable[[], float | None]\n" " :arg first_interval: Seconds until the callback should be called the first time.\n" " :type first_interval: float\n" " :arg persistent: Don't remove timer when a new file is loaded.\n" " :type persistent: bool\n")
static PyObject * bpy_app_timers_register(PyObject *, PyObject *args, PyObject *kw)
static double py_timer_execute(uintptr_t, void *user_data)
static PyModuleDef M_AppTimers_module_def
static PyObject * bpy_app_timers_is_registered(PyObject *, PyObject *function)
static void py_timer_free(uintptr_t, void *user_data)
static PyMethodDef M_AppTimers_methods[]
#define printf
header-only compatibility defines.
#define PY_ARG_PARSER_HEAD_COMPAT()
return ret
_W64 unsigned int uintptr_t
Definition stdint.h:119
_W64 int intptr_t
Definition stdint.h:118
ccl_device_inline int mod(int x, int m)
Definition util/math.h:520