Blender  V3.3
bpy_app_timers.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "BLI_timer.h"
8 #include "BLI_utildefines.h"
9 #include "PIL_time.h"
10 #include <Python.h>
11 
12 #include "BPY_extern.h"
13 #include "bpy_app_timers.h"
14 
15 #include "../generic/py_capi_utils.h"
16 #include "../generic/python_utildefines.h"
17 
18 static double handle_returned_value(PyObject *function, PyObject *ret)
19 {
20  if (ret == NULL) {
21  PyErr_PrintEx(0);
22  PyErr_Clear();
23  return -1;
24  }
25 
26  if (ret == Py_None) {
27  return -1;
28  }
29 
30  double value = PyFloat_AsDouble(ret);
31  if (value == -1.0f && PyErr_Occurred()) {
32  PyErr_Clear();
33  printf("Error: 'bpy.app.timers' callback ");
34  PyObject_Print(function, stdout, Py_PRINT_RAW);
35  printf(" did not return None or float.\n");
36  return -1;
37  }
38 
39  if (value < 0.0) {
40  value = 0.0;
41  }
42 
43  return value;
44 }
45 
46 static double py_timer_execute(uintptr_t UNUSED(uuid), void *user_data)
47 {
48  PyObject *function = user_data;
49 
50  PyGILState_STATE gilstate;
51  gilstate = PyGILState_Ensure();
52 
53  PyObject *py_ret = PyObject_CallObject(function, NULL);
54  const double ret = handle_returned_value(function, py_ret);
55 
56  PyGILState_Release(gilstate);
57 
58  return ret;
59 }
60 
61 static void py_timer_free(uintptr_t UNUSED(uuid), void *user_data)
62 {
63  PyObject *function = user_data;
64 
65  PyGILState_STATE gilstate;
66  gilstate = PyGILState_Ensure();
67 
68  Py_DECREF(function);
69 
70  PyGILState_Release(gilstate);
71 }
72 
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[[], Union[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");
89 static PyObject *bpy_app_timers_register(PyObject *UNUSED(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", NULL};
96  static _PyArg_Parser _parser = {
97  "O" /* `function` */
98  "|$" /* Optional keyword only arguments. */
99  "d" /* `first_interval` */
100  "p" /* `persistent` */
101  ":register",
102  _keywords,
103  0,
104  };
105  if (!_PyArg_ParseTupleAndKeywordsFast(
106  args, kw, &_parser, &function, &first_interval, &persistent)) {
107  return NULL;
108  }
109 
110  if (!PyCallable_Check(function)) {
111  PyErr_SetString(PyExc_TypeError, "function is not callable");
112  return NULL;
113  }
114 
115  Py_INCREF(function);
117  (intptr_t)function, py_timer_execute, function, py_timer_free, first_interval, persistent);
118  Py_RETURN_NONE;
119 }
120 
121 PyDoc_STRVAR(bpy_app_timers_unregister_doc,
122  ".. function:: unregister(function)\n"
123  "\n"
124  " Unregister timer.\n"
125  "\n"
126  " :arg function: Function to unregister.\n"
127  " :type function: function\n");
128 static PyObject *bpy_app_timers_unregister(PyObject *UNUSED(self), PyObject *function)
129 {
130  if (!BLI_timer_unregister((intptr_t)function)) {
131  PyErr_SetString(PyExc_ValueError, "Error: function is not registered");
132  return NULL;
133  }
134  Py_RETURN_NONE;
135 }
136 
137 PyDoc_STRVAR(bpy_app_timers_is_registered_doc,
138  ".. function:: is_registered(function)\n"
139  "\n"
140  " Check if this function is registered as a timer.\n"
141  "\n"
142  " :arg function: Function to check.\n"
143  " :type function: int\n"
144  " :return: True when this function is registered, otherwise False.\n"
145  " :rtype: bool\n");
146 static PyObject *bpy_app_timers_is_registered(PyObject *UNUSED(self), PyObject *function)
147 {
148  const bool ret = BLI_timer_is_registered((intptr_t)function);
149  return PyBool_FromLong(ret);
150 }
151 
152 static struct PyMethodDef M_AppTimers_methods[] = {
153  {"register",
154  (PyCFunction)bpy_app_timers_register,
155  METH_VARARGS | METH_KEYWORDS,
156  bpy_app_timers_register_doc},
157  {"unregister", (PyCFunction)bpy_app_timers_unregister, METH_O, bpy_app_timers_unregister_doc},
158  {"is_registered",
159  (PyCFunction)bpy_app_timers_is_registered,
160  METH_O,
161  bpy_app_timers_is_registered_doc},
162  {NULL, NULL, 0, NULL},
163 };
164 
165 static struct PyModuleDef M_AppTimers_module_def = {
166  PyModuleDef_HEAD_INIT,
167  "bpy.app.timers", /* m_name */
168  NULL, /* m_doc */
169  0, /* m_size */
170  M_AppTimers_methods, /* m_methods */
171  NULL, /* m_reload */
172  NULL, /* m_traverse */
173  NULL, /* m_clear */
174  NULL, /* m_free */
175 };
176 
177 PyObject *BPY_app_timers_module(void)
178 {
179  PyObject *sys_modules = PyImport_GetModuleDict();
180  PyObject *mod = PyModule_Create(&M_AppTimers_module_def);
181  PyDict_SetItem(sys_modules, PyModule_GetNameObject(mod), mod);
182  return mod;
183 }
bool BLI_timer_is_registered(uintptr_t uuid)
Definition: BLI_timer.c:72
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:33
bool BLI_timer_unregister(uintptr_t uuid)
Definition: BLI_timer.c:60
#define UNUSED(x)
Platform independent time functions.
static struct PyMethodDef M_AppTimers_methods[]
static struct PyModuleDef M_AppTimers_module_def
PyDoc_STRVAR(bpy_app_timers_register_doc, ".. function:: register(function, first_interval=0, persistent=False)\n" "\n" " Add a new function that will be called after the specified amount of seconds.\n" " The function gets no arguments and is expected to return either None or a float.\n" " If ``None`` is returned, the timer will be unregistered.\n" " A returned number specifies the delay until the function is called again.\n" " ``functools.partial`` can be used to assign some parameters.\n" "\n" " :arg function: The function that should called.\n" " :type function: Callable[[], Union[float, None]]\n" " :arg first_interval: Seconds until the callback should be called the first time.\n" " :type first_interval: float\n" " :arg persistent: Don't remove timer when a new file is loaded.\n" " :type persistent: bool\n")
static double handle_returned_value(PyObject *function, PyObject *ret)
static PyObject * bpy_app_timers_is_registered(PyObject *UNUSED(self), PyObject *function)
static PyObject * bpy_app_timers_unregister(PyObject *UNUSED(self), PyObject *function)
PyObject * BPY_app_timers_module(void)
static PyObject * bpy_app_timers_register(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
static void py_timer_free(uintptr_t UNUSED(uuid), void *user_data)
static double py_timer_execute(uintptr_t UNUSED(uuid), void *user_data)
void * user_data
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:490